mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
feat(desktop): show download progress and diagnostics on splash (#3273)
This commit is contained in:
1 parent
795e190bda
commit
0ef1cd14c3
12 files changed
+667
-18
No files matched your search
@@ -14,7 +14,8 @@ import type {ModulePlanItem} from '@electron/main/ModuleUpdatePlanner';
|
||||
|
||||
const MODULE_PACKAGE_HASH_MISMATCH_RETRIES = 1;
|
||||
const MODULE_PACKAGE_HEADER_TIMEOUT_MS = 30000;
|
||||
const MODULE_PACKAGE_STALL_TIMEOUT_MS = 60000;
|
||||
export const MODULE_PACKAGE_STALL_TIMEOUT_MS = 30000;
|
||||
const MODULE_PACKAGE_PROGRESS_INTERVAL_MS = 250;
|
||||
const TRANSIENT_HTTP_STATUS_FLOOR = 500;
|
||||
const TRANSIENT_PACKAGE_HTTP_STATUSES: ReadonlySet<number> = new Set([408, 425, 429]);
|
||||
const CAUSE_CHAIN_LIMIT = 8;
|
||||
@@ -37,6 +38,8 @@ interface ModulePackageInstallProgress {
|
||||
readonly total: number;
|
||||
readonly moduleName: string;
|
||||
readonly progress: number;
|
||||
readonly receivedBytes: number;
|
||||
readonly totalBytes: number;
|
||||
}
|
||||
|
||||
export const ModulePackageInstallerReportType = Object.freeze({
|
||||
@@ -77,6 +80,7 @@ interface ModulePackageInstallerOptions {
|
||||
readonly packageOrigin: string;
|
||||
readonly fetch: typeof globalThis.fetch;
|
||||
readonly onProgress: (progress: ModulePackageInstallProgress) => void;
|
||||
readonly now?: () => number;
|
||||
readonly report: (report: ModulePackageInstallerReport) => void;
|
||||
}
|
||||
|
||||
@@ -92,6 +96,17 @@ export class ModulePackageFetchError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export class ModulePackageStallError extends ModulePackageFetchError {
|
||||
public constructor(moduleName: string, stallMs: number) {
|
||||
super(moduleName, null, `package download stalled for ${stallMs}ms`);
|
||||
this.name = 'ModulePackageStallError';
|
||||
}
|
||||
}
|
||||
|
||||
export function isModulePackageStall(error: unknown): boolean {
|
||||
return findCause(error, ModulePackageStallError) != null;
|
||||
}
|
||||
|
||||
class ModulePackageUrlPolicyError extends Error {
|
||||
public readonly module: string;
|
||||
|
||||
@@ -168,6 +183,7 @@ export class ModulePackageInstaller {
|
||||
private readonly packageOrigin: string;
|
||||
private readonly fetchImplementation: typeof globalThis.fetch;
|
||||
private readonly onProgress: (progress: ModulePackageInstallProgress) => void;
|
||||
private readonly now: () => number;
|
||||
private readonly reporter: (report: ModulePackageInstallerReport) => void;
|
||||
|
||||
public constructor(options: ModulePackageInstallerOptions) {
|
||||
@@ -175,6 +191,7 @@ export class ModulePackageInstaller {
|
||||
this.packageOrigin = options.packageOrigin;
|
||||
this.fetchImplementation = options.fetch;
|
||||
this.onProgress = options.onProgress;
|
||||
this.now = options.now ?? Date.now;
|
||||
this.reporter = options.report;
|
||||
}
|
||||
|
||||
@@ -192,6 +209,8 @@ export class ModulePackageInstaller {
|
||||
total,
|
||||
moduleName: item.module,
|
||||
progress: Math.round((current / total) * 100),
|
||||
receivedBytes: item.entry.bytes,
|
||||
totalBytes: item.entry.bytes,
|
||||
});
|
||||
return;
|
||||
} catch (error) {
|
||||
@@ -238,6 +257,8 @@ export class ModulePackageInstaller {
|
||||
total,
|
||||
moduleName: item.module,
|
||||
progress: item.entry.bytes === 0 ? 0 : Math.floor((offset / item.entry.bytes) * 100),
|
||||
receivedBytes: offset,
|
||||
totalBytes: item.entry.bytes,
|
||||
});
|
||||
if (offset > 0 && offset === item.entry.bytes) {
|
||||
return {offset, chunks: []};
|
||||
@@ -339,18 +360,13 @@ export class ModulePackageInstaller {
|
||||
let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;
|
||||
let received = start;
|
||||
let reported = item.entry.bytes === 0 ? 0 : Math.floor((start / item.entry.bytes) * 100);
|
||||
let reportedAt = this.now();
|
||||
let completed = false;
|
||||
let transferFailure: unknown = null;
|
||||
let stall: NodeJS.Timeout | null = null;
|
||||
const arm = (): void => {
|
||||
stall = setTimeout(() => {
|
||||
controller.abort(
|
||||
new ModulePackageFetchError(
|
||||
item.module,
|
||||
null,
|
||||
`package download stalled for ${MODULE_PACKAGE_STALL_TIMEOUT_MS}ms`,
|
||||
),
|
||||
);
|
||||
controller.abort(new ModulePackageStallError(item.module, MODULE_PACKAGE_STALL_TIMEOUT_MS));
|
||||
}, MODULE_PACKAGE_STALL_TIMEOUT_MS);
|
||||
};
|
||||
const disarm = (): void => {
|
||||
@@ -378,14 +394,18 @@ export class ModulePackageInstaller {
|
||||
);
|
||||
}
|
||||
const percent = item.entry.bytes === 0 ? 100 : Math.floor((received / item.entry.bytes) * 100);
|
||||
if (percent > reported) {
|
||||
const at = this.now();
|
||||
if (percent > reported || at - reportedAt >= MODULE_PACKAGE_PROGRESS_INTERVAL_MS) {
|
||||
reported = percent;
|
||||
reportedAt = at;
|
||||
this.onProgress({
|
||||
phase: ModulePackageInstallPhase.DOWNLOADING,
|
||||
current,
|
||||
total,
|
||||
moduleName: item.module,
|
||||
progress: percent,
|
||||
receivedBytes: received,
|
||||
totalBytes: item.entry.bytes,
|
||||
});
|
||||
}
|
||||
yield chunk.value;
|
||||
|
||||
Reference in new issue
Block a user