272 lines
7.3 KiB
TypeScript
272 lines
7.3 KiB
TypeScript
import { OneNoteExportError } from '../onenote/export/index.js';
|
|
import type {
|
|
OneNotePackageDto,
|
|
OneNotePageDto,
|
|
OneNoteSectionDto,
|
|
} from '../onenote/model/dto.js';
|
|
import type { OneNoteResource } from '../onenote/one/content-model.js';
|
|
import { createWorkerExportArtifact } from './export-adapter.js';
|
|
import {
|
|
failureFromUnknown,
|
|
openPackageForWorker,
|
|
openPackageSetForWorker,
|
|
openSectionForWorker,
|
|
resourcePayload,
|
|
workerPageKey,
|
|
workerResourceKey,
|
|
} from './parser-adapter.js';
|
|
import type {
|
|
WorkerFailure,
|
|
WorkerRequest,
|
|
WorkerResponse,
|
|
} from './worker-protocol.js';
|
|
|
|
interface ModuleWorkerScope {
|
|
onmessage: ((event: MessageEvent<WorkerRequest>) => void) | null;
|
|
postMessage(response: WorkerResponse, transfer?: Transferable[]): void;
|
|
}
|
|
|
|
interface ParserSession {
|
|
source: OneNoteSectionDto | OneNotePackageDto;
|
|
pages: Map<string, OneNotePageDto>;
|
|
resources: Map<string, OneNoteResource>;
|
|
}
|
|
|
|
const workerScope = globalThis as unknown as ModuleWorkerScope;
|
|
const sessions = new Map<string, ParserSession>();
|
|
let sessionSequence = 0;
|
|
|
|
function createSession(
|
|
source: OneNoteSectionDto | OneNotePackageDto,
|
|
pages: Map<string, OneNotePageDto>,
|
|
resources: Map<string, OneNoteResource>
|
|
): string {
|
|
const id = `session-${++sessionSequence}`;
|
|
sessions.set(id, { source, pages, resources });
|
|
return id;
|
|
}
|
|
|
|
async function handleRequest(data: WorkerRequest): Promise<void> {
|
|
switch (data.type) {
|
|
case 'parse-one': {
|
|
try {
|
|
const parsed = openSectionForWorker(
|
|
new Uint8Array(data.bytes),
|
|
data.fileName
|
|
);
|
|
const sessionId = createSession(
|
|
parsed.section,
|
|
parsed.pages,
|
|
parsed.resources
|
|
);
|
|
workerScope.postMessage({
|
|
type: 'section',
|
|
requestId: data.requestId,
|
|
sessionId,
|
|
section: parsed.section,
|
|
});
|
|
} catch (error) {
|
|
workerScope.postMessage({
|
|
type: 'failure',
|
|
requestId: data.requestId,
|
|
error: failureFromUnknown(error),
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
case 'parse-onepkg':
|
|
try {
|
|
const parsed =
|
|
data.cabinetParts && data.cabinetParts.length > 0
|
|
? await openPackageSetForWorker(
|
|
[
|
|
{
|
|
fileName: data.fileName,
|
|
bytes: new Uint8Array(data.bytes),
|
|
},
|
|
...data.cabinetParts.map((part) => ({
|
|
fileName: part.fileName,
|
|
bytes: new Uint8Array(part.bytes),
|
|
})),
|
|
],
|
|
data.fileName
|
|
)
|
|
: await openPackageForWorker(
|
|
new Uint8Array(data.bytes),
|
|
data.fileName
|
|
);
|
|
const sessionId = createSession(
|
|
parsed.notebook,
|
|
parsed.pages,
|
|
parsed.resources
|
|
);
|
|
workerScope.postMessage({
|
|
type: 'package',
|
|
requestId: data.requestId,
|
|
sessionId,
|
|
notebook: parsed.notebook,
|
|
});
|
|
} catch (error) {
|
|
workerScope.postMessage({
|
|
type: 'failure',
|
|
requestId: data.requestId,
|
|
error: failureFromUnknown(error),
|
|
});
|
|
}
|
|
return;
|
|
case 'get-page': {
|
|
const session = sessions.get(data.sessionId);
|
|
if (!session) {
|
|
workerScope.postMessage({
|
|
type: 'failure',
|
|
requestId: data.requestId,
|
|
error: {
|
|
code: 'session-not-available',
|
|
message: 'The parser session is no longer available.',
|
|
recoverable: false,
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
const page = session.pages.get(
|
|
workerPageKey(data.pageId, data.sectionId)
|
|
);
|
|
if (!page) {
|
|
workerScope.postMessage({
|
|
type: 'failure',
|
|
requestId: data.requestId,
|
|
error: {
|
|
code: 'page-not-available',
|
|
message: 'The selected page is not available in this source.',
|
|
recoverable: true,
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
workerScope.postMessage({
|
|
type: 'page',
|
|
requestId: data.requestId,
|
|
sessionId: data.sessionId,
|
|
page,
|
|
});
|
|
return;
|
|
}
|
|
case 'get-resource': {
|
|
const session = sessions.get(data.sessionId);
|
|
if (!session) {
|
|
workerScope.postMessage({
|
|
type: 'failure',
|
|
requestId: data.requestId,
|
|
error: {
|
|
code: 'session-not-available',
|
|
message: 'The parser session is no longer available.',
|
|
recoverable: false,
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
const resource = session.resources.get(
|
|
workerResourceKey(data.resourceId, data.sectionId)
|
|
);
|
|
const payload = resource
|
|
? resourcePayload(resource, data.intent)
|
|
: undefined;
|
|
if (!payload) {
|
|
workerScope.postMessage({
|
|
type: 'failure',
|
|
requestId: data.requestId,
|
|
error: {
|
|
code: 'resource-not-available',
|
|
message:
|
|
data.intent === 'preview' && resource
|
|
? 'This image can be downloaded but is not approved for automatic preview.'
|
|
: 'The selected embedded resource is not available.',
|
|
recoverable: true,
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
workerScope.postMessage(
|
|
{
|
|
type: 'resource',
|
|
requestId: data.requestId,
|
|
sessionId: data.sessionId,
|
|
resource: payload,
|
|
},
|
|
[payload.bytes]
|
|
);
|
|
return;
|
|
}
|
|
case 'export-session': {
|
|
const session = sessions.get(data.sessionId);
|
|
if (!session) {
|
|
workerScope.postMessage({
|
|
type: 'failure',
|
|
requestId: data.requestId,
|
|
error: {
|
|
code: 'session-not-available',
|
|
message: 'The parser session is no longer available.',
|
|
recoverable: false,
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
try {
|
|
const artifact = createWorkerExportArtifact(
|
|
session.source,
|
|
session.pages,
|
|
session.resources,
|
|
data.format
|
|
);
|
|
workerScope.postMessage(
|
|
{
|
|
type: 'export-artifact',
|
|
requestId: data.requestId,
|
|
sessionId: data.sessionId,
|
|
artifact,
|
|
},
|
|
[artifact.bytes]
|
|
);
|
|
} catch (error) {
|
|
workerScope.postMessage({
|
|
type: 'failure',
|
|
requestId: data.requestId,
|
|
error: exportFailureFromUnknown(error),
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
case 'dispose':
|
|
sessions.delete(data.sessionId);
|
|
workerScope.postMessage({
|
|
type: 'disposed',
|
|
sessionId: data.sessionId,
|
|
});
|
|
}
|
|
}
|
|
|
|
function exportFailureFromUnknown(error: unknown): WorkerFailure {
|
|
if (error instanceof OneNoteExportError) {
|
|
return {
|
|
code:
|
|
error.code === 'limit-exceeded' ? 'limit-exceeded' : 'export-failed',
|
|
message: error.message,
|
|
recoverable: true,
|
|
structure: 'OneNote export',
|
|
};
|
|
}
|
|
return {
|
|
code: 'export-failed',
|
|
message:
|
|
error instanceof Error
|
|
? error.message
|
|
: 'The local notebook export could not be created.',
|
|
recoverable: true,
|
|
structure: 'OneNote export',
|
|
};
|
|
}
|
|
|
|
workerScope.onmessage = ({ data }) => {
|
|
void handleRequest(data);
|
|
};
|