feat: open multi-part packages in browser
This commit is contained in:
@@ -1,6 +1,33 @@
|
||||
import type { SourceKind } from './worker/onenote.client.js';
|
||||
|
||||
export const MAX_SOURCE_FILE_BYTES = 512 * 1024 * 1024;
|
||||
export const MAX_SOURCE_PARTS = 256;
|
||||
|
||||
export interface SourceFileDescriptor {
|
||||
name: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
interface SourceFileSelectionFailure {
|
||||
ok: false;
|
||||
title: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type SourceFileSelection<T extends SourceFileDescriptor> =
|
||||
| {
|
||||
ok: true;
|
||||
kind: 'one';
|
||||
source: T;
|
||||
cabinetParts: [];
|
||||
}
|
||||
| {
|
||||
ok: true;
|
||||
kind: 'onepkg';
|
||||
source: T;
|
||||
cabinetParts: T[];
|
||||
}
|
||||
| SourceFileSelectionFailure;
|
||||
|
||||
export function sourceKindFromFileName(fileName: string): SourceKind | null {
|
||||
const normalized = fileName.toLocaleLowerCase('en-US');
|
||||
@@ -8,3 +35,101 @@ export function sourceKindFromFileName(fileName: string): SourceKind | null {
|
||||
if (normalized.endsWith('.one')) return 'one';
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isCabinetPartFileName(fileName: string): boolean {
|
||||
return fileName.toLocaleLowerCase('en-US').endsWith('.cab');
|
||||
}
|
||||
|
||||
export function selectSourceFiles<T extends SourceFileDescriptor>(
|
||||
files: readonly T[]
|
||||
): SourceFileSelection<T> {
|
||||
if (files.length === 0) {
|
||||
return invalidSelection();
|
||||
}
|
||||
if (files.length > MAX_SOURCE_PARTS) {
|
||||
return {
|
||||
ok: false,
|
||||
title: 'Too many package parts',
|
||||
message: `Choose at most ${MAX_SOURCE_PARTS} files for one cabinet set.`,
|
||||
};
|
||||
}
|
||||
|
||||
const sections: T[] = [];
|
||||
const packages: T[] = [];
|
||||
const cabinetParts: T[] = [];
|
||||
for (const file of files) {
|
||||
const kind = sourceKindFromFileName(file.name);
|
||||
if (kind === 'one') sections.push(file);
|
||||
else if (kind === 'onepkg') packages.push(file);
|
||||
else if (isCabinetPartFileName(file.name)) cabinetParts.push(file);
|
||||
else {
|
||||
return {
|
||||
ok: false,
|
||||
title: 'Unsupported file name',
|
||||
message:
|
||||
'Choose one .one file, or one .onepkg file together with its .cab parts.',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const validSection =
|
||||
sections.length === 1 && packages.length === 0 && cabinetParts.length === 0;
|
||||
const validPackage = sections.length === 0 && packages.length === 1;
|
||||
if (!validSection && !validPackage) return invalidSelection();
|
||||
|
||||
const seenNames = new Set<string>();
|
||||
let totalSize = 0;
|
||||
for (const file of files) {
|
||||
const nameKey = file.name.normalize('NFC').toLocaleLowerCase('en-US');
|
||||
if (seenNames.has(nameKey)) {
|
||||
return {
|
||||
ok: false,
|
||||
title: 'Duplicate file name',
|
||||
message: `The selection contains more than one file named ${file.name}.`,
|
||||
};
|
||||
}
|
||||
seenNames.add(nameKey);
|
||||
if (!Number.isSafeInteger(file.size) || file.size < 0) {
|
||||
return {
|
||||
ok: false,
|
||||
title: 'Invalid file size',
|
||||
message: `The browser reported an invalid size for ${file.name}.`,
|
||||
};
|
||||
}
|
||||
if (file.size > MAX_SOURCE_FILE_BYTES) {
|
||||
return {
|
||||
ok: false,
|
||||
title: 'File is too large',
|
||||
message: `${file.name} exceeds the current 512 MiB safety limit.`,
|
||||
};
|
||||
}
|
||||
totalSize += file.size;
|
||||
if (!Number.isSafeInteger(totalSize) || totalSize > MAX_SOURCE_FILE_BYTES) {
|
||||
return {
|
||||
ok: false,
|
||||
title: 'Package set is too large',
|
||||
message:
|
||||
'The selected .onepkg and .cab files exceed the combined 512 MiB safety limit.',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (validSection) {
|
||||
return { ok: true, kind: 'one', source: sections[0]!, cabinetParts: [] };
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
kind: 'onepkg',
|
||||
source: packages[0]!,
|
||||
cabinetParts,
|
||||
};
|
||||
}
|
||||
|
||||
function invalidSelection(): SourceFileSelectionFailure {
|
||||
return {
|
||||
ok: false,
|
||||
title: 'Choose one OneNote source',
|
||||
message:
|
||||
'Choose one .one file, or one .onepkg file together with its .cab parts.',
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user