feat: add safe OneNote export serializers
This commit is contained in:
178
src/onenote/export/zip.ts
Normal file
178
src/onenote/export/zip.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
import { strToU8, zipSync, type Zippable } from 'fflate';
|
||||
import { forceFilenameExtension, sanitizeExportFilename } from './naming.js';
|
||||
import { createOneNoteExportPlan, type OneNoteExportPlan } from './plan.js';
|
||||
import {
|
||||
serializeHtmlWithPlan,
|
||||
serializeMarkdownWithPlan,
|
||||
serializePlainTextWithPlan,
|
||||
serializeStructuredJsonWithPlan,
|
||||
} from './serializers.js';
|
||||
import type { OneNoteExportOptions, OneNoteExportSnapshot } from './types.js';
|
||||
|
||||
export interface OneNoteStaticNotebookManifest {
|
||||
schemaVersion: number;
|
||||
exportKind: 'onenote-tools-static-notebook';
|
||||
app: OneNoteExportSnapshot['app'];
|
||||
parser: OneNoteExportSnapshot['parser'];
|
||||
supportLevel: OneNoteExportSnapshot['supportLevel'];
|
||||
supportNotes: string[];
|
||||
source: OneNoteExportSnapshot['source'];
|
||||
notebookName: string;
|
||||
files: {
|
||||
viewer: 'index.html';
|
||||
structuredData: 'notebook.json';
|
||||
plainText: 'notebook.txt';
|
||||
markdown: 'notebook.md';
|
||||
warnings: 'warnings.json';
|
||||
};
|
||||
counts: {
|
||||
sections: number;
|
||||
pages: number;
|
||||
blocks: number;
|
||||
resources: number;
|
||||
diagnostics: number;
|
||||
exportWarnings: number;
|
||||
unsupportedContent: number;
|
||||
};
|
||||
sections: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
sourcePath?: string;
|
||||
pageCount: number;
|
||||
}>;
|
||||
resources: Array<{
|
||||
sectionId: string;
|
||||
id: string;
|
||||
kind: 'image' | 'attachment';
|
||||
path: string;
|
||||
filename: string;
|
||||
mediaType: string;
|
||||
size: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface OneNoteExportWarningsManifest {
|
||||
schemaVersion: number;
|
||||
app: OneNoteExportSnapshot['app'];
|
||||
parser: OneNoteExportSnapshot['parser'];
|
||||
supportLevel: OneNoteExportSnapshot['supportLevel'];
|
||||
source: OneNoteExportSnapshot['source'];
|
||||
diagnostics: OneNoteExportPlan['diagnostics'];
|
||||
exportWarnings: OneNoteExportPlan['warnings'];
|
||||
unsupportedContent: OneNoteExportPlan['unsupportedContent'];
|
||||
}
|
||||
|
||||
export interface OneNoteStaticNotebookZipArtifact {
|
||||
filename: string;
|
||||
mediaType: 'application/zip';
|
||||
bytes: Uint8Array;
|
||||
manifest: OneNoteStaticNotebookManifest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an application-owned static export. This never writes or claims to
|
||||
* write a .one/.onepkg file, and it never interprets source HTML or SVG.
|
||||
*/
|
||||
export function createOneNoteStaticNotebookZip(
|
||||
snapshot: OneNoteExportSnapshot,
|
||||
options: OneNoteExportOptions = {}
|
||||
): OneNoteStaticNotebookZipArtifact {
|
||||
const plan = createOneNoteExportPlan(snapshot, options);
|
||||
const manifest = createStaticManifest(snapshot, plan);
|
||||
const warnings: OneNoteExportWarningsManifest = {
|
||||
schemaVersion: snapshot.schemaVersion,
|
||||
app: snapshot.app,
|
||||
parser: snapshot.parser,
|
||||
supportLevel: snapshot.supportLevel,
|
||||
source: snapshot.source,
|
||||
diagnostics: plan.diagnostics,
|
||||
exportWarnings: plan.warnings,
|
||||
unsupportedContent: plan.unsupportedContent,
|
||||
};
|
||||
|
||||
const files: Zippable = Object.create(null) as Zippable;
|
||||
files['index.html'] = strToU8(serializeHtmlWithPlan(snapshot, plan, true));
|
||||
files['notebook.json'] = strToU8(
|
||||
serializeStructuredJsonWithPlan(snapshot, plan)
|
||||
);
|
||||
files['notebook.txt'] = strToU8(serializePlainTextWithPlan(snapshot, plan));
|
||||
files['notebook.md'] = strToU8(
|
||||
serializeMarkdownWithPlan(snapshot, plan, true)
|
||||
);
|
||||
files['manifest.json'] = jsonBytes(manifest);
|
||||
files['warnings.json'] = jsonBytes(warnings);
|
||||
for (const resource of plan.resources) files[resource.path] = resource.bytes;
|
||||
|
||||
const bytes = zipSync(files, {
|
||||
level: 6,
|
||||
mtime: new Date(1980, 0, 1, 0, 0, 0),
|
||||
os: 3,
|
||||
attrs: 0o644 << 16,
|
||||
});
|
||||
const safeName = sanitizeExportFilename(
|
||||
snapshot.notebookName,
|
||||
'onenote-export'
|
||||
);
|
||||
return {
|
||||
filename: forceFilenameExtension(safeName, 'zip'),
|
||||
mediaType: 'application/zip',
|
||||
bytes,
|
||||
manifest,
|
||||
};
|
||||
}
|
||||
|
||||
function createStaticManifest(
|
||||
snapshot: OneNoteExportSnapshot,
|
||||
plan: OneNoteExportPlan
|
||||
): OneNoteStaticNotebookManifest {
|
||||
return {
|
||||
schemaVersion: snapshot.schemaVersion,
|
||||
exportKind: 'onenote-tools-static-notebook',
|
||||
app: snapshot.app,
|
||||
parser: snapshot.parser,
|
||||
supportLevel: snapshot.supportLevel,
|
||||
supportNotes: snapshot.supportNotes,
|
||||
source: snapshot.source,
|
||||
notebookName: snapshot.notebookName,
|
||||
files: {
|
||||
viewer: 'index.html',
|
||||
structuredData: 'notebook.json',
|
||||
plainText: 'notebook.txt',
|
||||
markdown: 'notebook.md',
|
||||
warnings: 'warnings.json',
|
||||
},
|
||||
counts: {
|
||||
sections: snapshot.sections.length,
|
||||
pages: plan.pageCount,
|
||||
blocks: plan.blockCount,
|
||||
resources: plan.resources.length,
|
||||
diagnostics: plan.diagnostics.length,
|
||||
exportWarnings: plan.warnings.length,
|
||||
unsupportedContent: plan.unsupportedContent.length,
|
||||
},
|
||||
sections: snapshot.sections.map((section) => ({
|
||||
id: section.id,
|
||||
name: section.name,
|
||||
...(section.sourcePath ? { sourcePath: section.sourcePath } : {}),
|
||||
pageCount: section.pages.length,
|
||||
})),
|
||||
resources: plan.resources.map((resource) => ({
|
||||
sectionId: resource.sectionId,
|
||||
id: resource.id,
|
||||
kind: resource.kind,
|
||||
path: resource.path,
|
||||
filename: resource.filename,
|
||||
mediaType: resource.mediaType,
|
||||
size: resource.size,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
function jsonBytes(value: unknown): Uint8Array {
|
||||
return strToU8(`${JSON.stringify(value, null, 2)}\n`);
|
||||
}
|
||||
Reference in New Issue
Block a user