release: prepare OneNote Tools 0.2.0

This commit is contained in:
2026-07-22 20:17:09 +02:00
parent edb19bfcc8
commit 41cffdc5d9
19 changed files with 423 additions and 234 deletions

View File

@@ -1,5 +1,34 @@
# Changelog
## 0.2.0 - 2026-07-22
- Open unfragmented FSSHTTPB package-store `.one` sections through the same
bounded page/content model as desktop revision stores.
- Interpret desktop and FSSHTTPB `.onetoc2` stores into authored section-group,
section, page, and subpage trees, with identity fallback and deterministic
handling of missing, duplicate, cyclic, and unlisted entries.
- Add complete-set CAB extraction for multi-cabinet packages, including split
CFDATA blocks, spanning files, codec continuity, link validation, aggregate
limits, and browser selection of one `.onepkg` plus its `.cab` companions.
- Add native MSZIP and Quantum decoding alongside uncompressed and LZX CAB
support.
- Parse and render structured outlines, rich-text formatting and safe links,
tables, signature/header-validated PNG/JPEG images, decoded ink, layout hints,
and declared embedded attachments.
- Add local structured JSON, text, Markdown, inert HTML, and deterministic
static notebook ZIP exports with diagnostics, unsupported-content summaries,
normalized resource names, and bounded output.
- Add independent parser, image-preview, display, TOC, cabinet-set, and export
resource budgets. Oversized images remain explicitly downloadable but are
not copied or decoded for automatic preview.
- Verify the implementation against the redistributable OneNote golden corpus,
a licensed five-part libmspack fixture, adversarial malformed inputs, and two
private `.onepkg` files without committing their bytes.
Known unsupported areas remain fragmented FSSHTTPB data-element reconstruction,
equations, audio/video, pixel-perfect page placement, and untested OneNote
producer variants.
## 0.1.0 - 2026-07-22
- Add a standalone React/Vite reader integrated with Toolbox SDK 0.1.1,
@@ -8,21 +37,17 @@
- Add a worker-hosted native TypeScript parser for a desktop OneNote
revision-store subset: bounded file-node and object-space traversal,
property sets, page metadata, titles, timestamps, levels, and plain text.
- Add bounded FSSHTTPB packaging, storage-index/cell/revision/object-group
parsing and open unfragmented package-store `.one` sections through the same
content model.
- Add in-memory CAB enumeration and extraction for uncompressed, MSZIP,
Quantum, and LZX folders, including checksums, strict paths, explicit
resource limits, and cooperative cancellation between data blocks.
- Add in-memory CAB enumeration and extraction for uncompressed and LZX
folders, including checksums, strict paths, explicit resource limits, and
cooperative cancellation between data blocks.
- Open contained `.one` sections independently and expose package entries,
section/page navigation, partial failures, and structured diagnostics.
- Verify a Joplin `.one` golden with three pages and a Joplin LZX `0x1203`
`.onepkg` golden with seven entries, five sections, and seven pages. Also
verify rust-cab uncompressed, MSZIP, and LZX `0x1303` fixtures plus a
generated two-block MSZIP dictionary-chain fixture and pinned Quantum
single-/multi-frame payloads.
verify rust-cab uncompressed and LZX `0x1303` fixtures.
- Add deterministic static release packaging, SHA-256 sidecars, pinned-source
provenance, fixture licensing, and security/support documentation.
Known unsupported areas include fragmented FSSHTTPB data-element
reconstruction and untested OneNote producer variants.
Known unsupported areas are FSSHTTP-packaged `.one`, MSZIP and Quantum CAB
extraction, multi-cabinet archives, `.onetoc2` hierarchy/order interpretation,
visual content, attachments, layout/format fidelity, links, and exports.

10
LICENSE
View File

@@ -1,4 +1,8 @@
SPDX-License-Identifier: MPL-2.0
SPDX-License-Identifier: MPL-2.0 AND LGPL-2.1-only
OneNote Tools is licensed under the Mozilla Public License, version 2.0.
The complete, unmodified licence text is provided in LICENSES/MPL-2.0.txt.
OneNote Tools is primarily licensed under the Mozilla Public License, version
2.0. The adapted multi-cabinet implementation in
src/onenote/onepkg/cabinet-set.ts is LGPL-2.1-only. Complete, unmodified licence
texts are provided in LICENSES/MPL-2.0.txt and
LICENSES/libmspack-LGPL-2.1.txt. Other adapted and third-party material remains
under the terms recorded in THIRD_PARTY_NOTICES.md.

View File

@@ -1,53 +1,61 @@
# OneNote Tools
OneNote Tools is a standalone, local-first browser reader for a tested subset
of Microsoft OneNote `.one` sections and CAB-based `.onepkg` packages. The
parser is native TypeScript, runs in a dedicated module worker, and never
uploads or persists the selected notebook.
OneNote Tools is a standalone, local-first browser reader and exporter for a
tested subset of Microsoft OneNote `.one` sections and CAB-based `.onepkg`
packages. Its native TypeScript parser runs in a dedicated module worker; the
selected notebook is neither uploaded nor persisted.
It is also an independent application in the add·ideas Toolbox contract: the
It is also an independent application in the add·ideas Toolbox contract. The
source uses the Toolbox SDK shell and manifest, builds with Vite `base: './'`,
and is delivered to Toolbox Portal as an already-built, checksummed static ZIP.
and is delivered to Toolbox Portal as a checksummed static ZIP.
## What version 0.1.0 can open
## What version 0.2.0 supports
- Desktop revision-store `.one` sections from the tested OneNote family. The
reader exposes section/page names, stable page IDs, created and updated
timestamps when present, page level, and readable plain text.
- Unfragmented FSSHTTPB-packaged `.one` sections matching the tested Joplin
family. Package cells, revision chains, object groups, and resolved object
and page-space references feed the same bounded content reader.
- Single-cabinet `.onepkg` files whose CAB folders are uncompressed, MSZIP,
Quantum, or LZX compressed. Each contained supported `.one` section is
parsed independently, so one unsupported section does not hide sections that
did parse.
- Package entries, safe normalized paths, uncompressed sizes, compression
metadata, section/page navigation, and structured diagnostics.
- Desktop revision-store and unfragmented FSSHTTPB package-store `.one`
sections from the tested producer families.
- Complete single- or multi-cabinet `.onepkg` sets using uncompressed, MSZIP,
Quantum, or LZX CAB folders. Split packages can be selected as one `.onepkg`
file together with all of its `.cab` companions, in any selection order.
- Desktop and FSSHTTPB `.onetoc2` stores inside packages. Section groups,
authored section order, recycle-bin filtering, pages, and subpages are
presented as a notebook tree; unresolved entries retain a deterministic
fallback rather than hiding readable sections.
- Structured page content: outlines and layout hints, rich-text runs, safe
links, tables, PNG/JPEG images, decoded ink strokes, and declared embedded
attachments. Resource bytes remain lazy until previewed, explicitly
downloaded, or included in an explicit export.
- Local exports as structured JSON, plain text, Markdown, inert semantic HTML,
or a deterministic static notebook ZIP. Exports retain parser diagnostics
and unsupported-content summaries; they do not create or modify OneNote
files.
The golden tests parse a real Joplin desktop `.one` file with three pages, an
unfragmented 17,197-byte Joplin FSSHTTPB `.one` file with one page, and a real
Joplin `.onepkg` with seven entries, five sections, and seven pages. LZX
`0x1203` and `0x1303`, uncompressed CAB, one- and two-block MSZIP data, and
single- and multi-frame Quantum data are covered by fixtures. The MSZIP and
Quantum fixtures are CAB-level evidence, not a claim about a particular
OneNote `.onepkg` producer.
The redistributable golden corpus covers desktop and unfragmented FSSHTTPB
sections, a real LZX `.onepkg`, all four CAB compression modes, a five-part
spanning CAB set, TOC hierarchy behavior, rich content, resources, and every
export format. The generic MSZIP, Quantum, and multi-cabinet fixtures establish
CAB behavior; they are not claims about every OneNote producer.
This is a structural reader, not a full-fidelity OneNote renderer. Images,
tables, ink, embedded files, layout, formatting, links, and exports are not
rendered. Fragmented FSSHTTPB data elements and some producer variants remain
unsupported. See
[format support](docs/FORMAT_SUPPORT.md) for the precise matrix.
This remains a deliberately bounded compatibility subset. Fragmented FSSHTTPB
data-element reconstruction, equations, audio/video, and untested producer
variants are not supported. Visual placement is approximate rather than a
pixel-perfect OneNote layout. See [format support](docs/FORMAT_SUPPORT.md) for
the precise matrix.
There is no Rust/Wasm runtime, server, proxy, upload, telemetry, or notebook
storage. All parsing and CAB/LZX/MSZIP/Quantum decompression is ordinary
browser JavaScript produced from the TypeScript source.
There is no Rust/Wasm runtime, notebook-processing application backend, upload
proxy, telemetry, or notebook storage. All parsing and CAB decompression is
ordinary browser JavaScript produced from the TypeScript source.
## Use
Run the static build or open it through Toolbox Portal, then drop or choose one
local `.one` or `.onepkg` file. Use **Clear file** to terminate the current
worker and release application references. Browser garbage collection does not
provide a secure-erasure guarantee.
Run the static build or open it through Toolbox Portal, then:
- choose one local `.one` file; or
- choose one `.onepkg` file and, for a split package, all linked `.cab` parts
in the same file-picker action.
Use **Clear file** to terminate the current worker and release application
references. Browser garbage collection does not provide a secure-erasure
guarantee.
## Development
@@ -72,7 +80,7 @@ supplied through the SDK's `?toolbox=` context mechanism. The app uses the SDK
```sh
npm run release:artifact
(cd release && sha256sum -c onenote-tools-0.1.0.sha256)
(cd release && sha256sum -c onenote-tools-0.2.0.sha256)
```
The ZIP root contains the built static app and manifest plus the changelog,
@@ -95,7 +103,7 @@ into the portal React tree.
## Licence
OneNote Tools is licensed under MPL-2.0. The complete licence is in
[`LICENSES/MPL-2.0.txt`](LICENSES/MPL-2.0.txt). Adapted and third-party material
remains under the terms recorded in
OneNote Tools is primarily MPL-2.0; the adapted multi-cabinet module is
LGPL-2.1-only. The complete licence texts are in [`LICENSES/`](LICENSES/), and
adapted/third-party material remains under the terms recorded in
[`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md).

View File

@@ -1,8 +1,8 @@
# Corresponding source
The corresponding source for OneNote Tools 0.1.0 is the `v0.1.0` tag:
The corresponding source for OneNote Tools 0.2.0 is the `v0.2.0` tag:
https://git.add-ideas.de/zemion/onenote-tools/src/tag/v0.1.0
https://git.add-ideas.de/zemion/onenote-tools/src/tag/v0.2.0
Build the release from that tag with Node.js 22 or newer:

View File

@@ -28,6 +28,17 @@ modules, pinned at `04a6db2aa7bd487a89c559631d79d1b384139f50`. Its adapted
modules carry provenance headers and its licence is in
`LICENSES/compcol-MIT.txt`.
Multi-cabinet chain construction, split CFDATA handling, and spanning-file
semantics adapt libmspack's LGPL-2.1-only CAB decompressor, pinned at
`55d501976171397ccd5d5a7a1ca7da065b1d9a06`. The adapted module carries its
copyright/provenance header and the complete licence is in
`LICENSES/libmspack-LGPL-2.1.txt`.
`one2html` (MIT), pinned at
`59930ad309004030f812790c6749efe4265bb6bd`, was inspected as a behavioral
reference for rich-content rendering. No one2html source, template, stylesheet,
binary, or runtime dependency is shipped in this application.
MSZIP raw DEFLATE decoding uses fflate 0.8.3 (MIT). Its complete installed
licence text is included in the generated runtime licence bundle.
@@ -49,5 +60,6 @@ The encoded fixture bytes are excluded from `dist` and the release ZIP. The
release retains the fixture provenance document and AGPL text so these notices
remain complete.
The remaining npm packages are build and test tooling and are not shipped as
runtime modules in the static application.
Vite and the remaining npm packages are build/test tooling rather than runtime
modules. Vite's optional modulepreload runtime polyfill is explicitly disabled
for this baseline-browser build.

View File

@@ -1,20 +1,27 @@
# Format support
## Version 0.1.0 support matrix
## Version 0.2.0 support matrix
| Input or feature | Status | Tested evidence and boundary |
| ---------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------- |
| Desktop revision-store `.one` | Supported subset | Real 34,904-byte Joplin golden; three pages |
| FSSHTTPB-packaged `.one` | Supported subset | Licensed 17,197-byte Joplin golden; one page; unfragmented data elements |
| -------------------------------- | -------------------- | ---------------------------------------------------------------------------------------------------- |
| Desktop revision-store `.one` | Supported subset | Real 34,904-byte Joplin golden; three pages and rich content |
| FSSHTTPB package-store `.one` | Supported subset | Licensed 17,197-byte Joplin golden; one page; unfragmented data elements |
| Unknown/legacy `.one` variants | Unsupported | File type and format GUIDs are validated before traversal |
| `.onepkg`, single CAB, LZX | Supported subset | Real Joplin package: LZX `0x1203`, three CFDATA blocks, seven entries, five sections, seven pages |
| CAB LZX `0x1303` | Supported extraction | Byte-for-byte rust-cab MIT unit fixture, two text entries |
| CAB without compression | Supported extraction | Byte-for-byte rust-cab MIT unit fixture |
| CAB MSZIP | Supported extraction | rust-cab single-block fixture plus a generated two-block dictionary-chain fixture |
| CAB Quantum | Supported extraction | Pinned compcol qtm.txt payload, generated one-folder CAB, and two-frame state/sentinel tests |
| Multi-cabinet CAB / spanning files | Unsupported | Header flags and spanning folder indexes are rejected |
| `.onetoc2` package entries | Listed only | Files are identified, but notebook hierarchy and section order are not interpreted |
| Standalone `.onetoc2` | Unsupported in the app | The picker accepts only `.one` and `.onepkg` |
| `.onepkg`, one CAB | Supported subset | Real Joplin LZX package; generated/pinned none, MSZIP, Quantum, and LZX codec fixtures |
| `.onepkg`, complete CAB set | Supported extraction | Licensed five-part libmspack set plus split-block, spanning-file, ordering, and malformed-set tests |
| Missing/inconsistent CAB parts | Rejected | Set ID, indexes, previous/next names, compression, split blocks, and spanning records must agree |
| CAB without compression | Supported extraction | Byte-for-byte rust-cab fixture |
| CAB MSZIP | Supported extraction | rust-cab single block plus generated two-block dictionary-chain fixture |
| CAB Quantum | Supported extraction | Pinned compcol payloads and generated CAB integration fixture |
| CAB LZX | Supported extraction | Real OneNote `0x1203` package and rust-cab `0x1303` fixture |
| `.onetoc2` inside a package | Supported subset | Desktop golden/hierarchy tests; FSSHTTPB dispatch uses a mocked shared-store seam, not a real golden |
| Standalone `.onetoc2` in the app | Not selectable | The picker accepts `.one`, or `.onepkg` together with `.cab` companions |
| Rich text and layout hints | Supported subset | Run formatting, paragraph properties, safe links, and bounded layout metadata |
| Tables | Supported subset | Rows, cells, nested blocks, column widths, locks, borders, and cell backgrounds |
| Images | Supported subset | Lazy, signature/header-validated PNG and JPEG resources; unsafe images stay downloadable only |
| Ink | Supported subset | Bounded delta decoding, strokes, colors, width/height, transparency, and SVG display/export |
| Embedded attachments | Supported subset | `ATTACHMENT_DECLARATION`/file-data resources remain inert until explicit download |
| Export | Supported subset | JSON, text, Markdown, inert HTML, and deterministic static notebook ZIP |
“Supported subset” is intentionally narrower than “all files with this
extension.” A successful open requires the tested desktop or FSSHTTPB OneStore
@@ -22,6 +29,8 @@ structures and content graph nodes. Malformed offsets, counts, references, CAB
checksums, paths, compressed data, and unsupported variants produce bounded
diagnostics or a controlled failure.
## OneStore and package boundaries
FSSHTTPB support covers the OneStore packaging envelope, Storage Index and
Storage Manifest, header/data-root cells, bounded revision chains, Object Group
partitions, resolved object/page-space references, and available Object Data
@@ -31,49 +40,68 @@ the store adapter rejects packages containing fragment records with a
controlled `UNSUPPORTED_FORMAT` error. Excluded object data cannot be
reconstructed from a standalone file and is likewise unsupported.
For a split package, the browser must receive the `.onepkg` and every linked
`.cab` companion in one selection. The extractor validates a complete,
uniquely indexed cabinet chain before decoding. It reassembles split CFDATA
blocks, preserves codec state across cabinets, and reconciles the CAB
`continued from previous`, `continued to next`, and `continued both` file
records. Partial sets are not opened heuristically.
The default LZX limit accepts CAB window sizes through 8 MiB, but compatibility
claims are fixture-backed only for window bits 18 (`0x1203`) and 19 (`0x1303`).
MSZIP validates the `CK` prefix, limits each compressed block to 32 KiB + 12,
decodes into a fixed-size output buffer, requires the declared output length,
and carries at most 32 KiB of history between blocks in one folder. The MSZIP
tests establish CAB extraction behavior; no redistributable MSZIP-compressed
OneNote export is currently present, so they do not establish producer-specific
`.onepkg` compatibility.
and carries at most 32 KiB of history between blocks in one logical folder.
Quantum validates level/window metadata, bounds its window to 2 MiB by default,
requires full 32 KiB intermediate frames, preserves its arithmetic models and
LZ window across frames, and injects exactly one `0xFF` CAB block sentinel.
Trailer padding is limited to the documented 0..4 zero bytes; match, frame,
output, operation, and cancellation bounds are enforced. Its tests likewise
establish CAB extraction behavior only: no redistributable Quantum-compressed
OneNote export is currently present.
LZ window across frames/cabinets, and injects exactly one `0xFF` CAB block
sentinel. Trailer, match, frame, output, operation, and cancellation bounds are
enforced.
Trailing CAB data is ignored with a warning. Reserved CAB areas are bounded and
skipped. CAB names are decoded as strict UTF-8; unmarked valid UTF-8 receives a
warning, while invalid non-ASCII legacy-code-page names are rejected rather
than guessed.
## Extracted OneNote data
## Notebook and page model
The supported section paths currently return:
Supported `.onetoc2` stores provide authored section order, nested section
groups, display names, identities, and colors. Exact normalized paths take
precedence; same-directory file identity is a guarded fallback. Recycle-bin
entries are omitted, duplicate/cyclic references are diagnosed, and readable
sections not reachable from a TOC are appended deterministically. Page levels
are interpreted as a page/subpage tree.
- section display name;
- page ID, title, created/updated timestamp when available, and page level;
- visible Unicode/extended-ASCII rich text as plain text and a text preview;
- diagnostics tied to an offset, structure, or page where available.
The supported section path returns:
It does not preserve visual position, rich-text formatting, list formatting,
tables, images, ink, equations, embedded files, audio/video, active links,
page versions/conflicts, or exports. Unsupported content objects remain visible
as diagnostics instead of being silently presented as parsed.
- section/page identities, names, timestamps, page levels, and diagnostics;
- outlines and recursively nested content blocks;
- rich-text runs with bold, italic, underline, strikeout, super/subscript,
font, size, foreground/highlight color, paragraph alignment/spacing, and
allow-listed `http`, `https`, and `mailto` links;
- tables, lazy PNG/JPEG images, embedded-file declarations, and decoded ink;
- bounded layout hints such as offsets, maximum dimensions, and alignment.
Within a supported `.onepkg`, each `.one` entry is parsed independently.
Malformed or unsupported sections are marked failed/unsupported while other
sections remain available. Package navigation is temporary normalized-path
order until `.onetoc2` interpretation is implemented.
This is not a pixel-perfect OneNote renderer. Equations, audio/video, page
versions/conflicts, and producer-specific structures outside the golden corpus
are not represented. Encountered unsupported object kinds remain visible as
diagnostics. Active notebook HTML/SVG is never injected.
## Export
All exports are created inside the existing local parser worker. They include a
versioned parser/support manifest, the readable page hierarchy, diagnostics,
and unsupported-content summaries. The static ZIP contains inert semantic HTML
plus JSON, text, Markdown, warnings, and bounded resources. Resource names are
normalized, potentially active attachment suffixes are neutralized, and only
validated image types may be embedded for automatic display.
Export is a representation of the parser-supported subset, not a round trip:
it neither emits `.one`/`.onepkg` nor promises full OneNote visual fidelity.
## Test fixtures
The exact hashes, source revisions, licences, expected page names, and CAB
The exact hashes, source revisions, licences, expected content, and CAB
structures are recorded in
[`tests/fixtures/README.md`](../tests/fixtures/README.md). Tests use no private
notebooks and do not treat a synthetic archive as proof of Microsoft OneNote

View File

@@ -4,31 +4,41 @@ The desktop `.one` reader adapts selected structures from the pinned
`onenote.rs` implementation. It is not a line-for-line or complete port.
| TypeScript module | Upstream path(s) | Revision | Port scope and deviations |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `src/onenote/onestore/file-node.ts` | `onestore/desktop/file_structure/file_node_list_fragment.rs`, `file_node/*`, chunk references | `onenote.rs@5138a39a3f4e72b840932f9872fecde52fa9da60` | Selected; immutable byte input, strict range checks, fragment/child cycle detection, and depth/count limits |
| `src/onenote/onestore/property-set.ts` | `onestore/shared/{object_prop_set,prop_set,property,object_stream_header}.rs` | same | Substantial; bounded vectors/counts/nesting and serializable discriminated unions |
| `src/onenote/onestore/desktop-store.ts` | `onestore/desktop/{file_node,file_structure,objects}/*` | same | Selected desktop section path; explicit missing-reference errors; file-data/attachment payloads skipped with diagnostics |
| `src/onenote/onestore/desktop-store.ts` | `onestore/desktop/{file_node,file_structure,objects}/*` | same | Selected desktop section/TOC paths; explicit missing-reference errors; bounded file-data references |
| `src/onenote/onestore/file-data.ts` | `onestore/desktop/{file_node/shared,objects/file_data_store}.rs` | same | Bounded zero-copy file-data payload ranges, checked declarations, and explicit external/invalid availability |
| `src/onenote/onestore/types.ts` | `onestore/desktop/common/*`, `onestore/shared/*`, and desktop object modules | same | Serializable structural records used across desktop and FSSHTTPB adapters |
| `src/onenote/fsshttpb/*` | `fsshttpb/{data,data_element,packaging}.rs` | same | Native bounded package/header/data-element records, checked compact integers, zero-copy BLOB ranges; fragment records parsed but not assembled |
| `src/onenote/onestore/fsshttp-store.ts` | `onestore/fsshttpb/{header,mapping_table,object,object_space,revision,revision_role}.rs` | same | Unfragmented package-store adapter; bounded/cycle-checked revision chains, newest-revision precedence, resolved positional reference arrays |
| `src/onenote/one/{constants,section}.ts` | `one/property*`, `one/property_set/*`, `onenote/{section,page,page_series,outline,rich_text}.rs` | same | Page metadata, titles, and plain text only; bounded graph traversal; visual/embedded content diagnosed |
| `src/onenote/one/{constants,section,content}.ts` | `one/property*`, `one/property_set/*`, `onenote/{section,page,page_series,outline,rich_text,table,image,embedded_file,ink}.rs` | same | Page metadata plus bounded recursive rich text, tables, resources, layout hints, and decoded ink in an inert browser DTO |
| `src/onenote/one/property-access.ts` | `one/property/{simple,object_reference}.rs` | same | Typed property access and resolved/compact reference validation without coercion |
| `src/onenote/one/section-content.ts` | `onenote/{section,page_series,page}.rs` | same | Worker-retained page/content/resource model with bounded diagnostics and timestamps |
| `src/onenote/parser/parse-toc.ts` | `one/property_set/toc_container.rs` and desktop/FSSHTTP OneStore readers | same | Desktop/FSSHTTP TOC content, names, identities, ordering, colors, nesting, and bounded diagnostics |
The browser-native package path has separate MIT provenance:
The browser-native package path has separate upstream provenance:
| TypeScript module | Upstream path(s) | Revision and selected licence | Port scope and deviations |
| ------------------------------------------------------------------------ | ------------------------------------------------------ | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `src/onenote/onepkg/{cabinet,checksum}.ts` | rust-cab `src/{cabinet,folder,file,ctype,checksum}.rs` | `c5839f5fdfa4c4e7cc9b22f570c79c96af0560e2`, MIT | CAB reading/checksum adapted for immutable browser bytes, strict normalized paths, checked ranges/checksums, whole-folder extraction, explicit limits, and cancellation/yield hooks; writers are not ported |
| `src/onenote/onepkg/cabinet-set.ts` | libmspack `mspack/cabd.c` | `55d501976171397ccd5d5a7a1ca7da065b1d9a06`, LGPL-2.1-only | Complete named-set validation, split CFDATA reassembly, codec continuity, spanning-file reconciliation, aggregate limits, and immutable browser inputs |
| `src/onenote/onepkg/mszip/decoder.ts` | rust-cab `src/mszip.rs`; fflate raw DEFLATE | rust-cab revision above; fflate 0.8.3, MIT | `CK` framing and persistent 32 KiB folder history follow rust-cab; raw DEFLATE uses fflate with a caller-owned bounded buffer and strict declared-size checks |
| `src/onenote/onepkg/quantum/{bit-reader,budget,decoder,model,tables}.ts` | compcol `src/quantum/{bits,decoder,model,tables}.rs` | `04a6db2aa7bd487a89c559631d79d1b384139f50`, MIT | Native TypeScript arithmetic/model/LZ decoder; fixed CAB output, 2 MiB maximum window, strict match/frame/trailer checks, two-block lookahead, operation budget, and cancellation checkpoints |
| `src/onenote/onepkg/lzx/{bit-reader,huffman,decoder}.ts` | lzxd 0.2.5 `src/{bitstream,tree,block,window,lib}.rs` | `4748e43594e3e30cff2ace3a6ad7a376c9816fdd`, MIT option of MIT OR Apache-2.0 | Directly translated core with typed arrays, CAB CFDATA boundaries, persistent folder state, strict malformed-stream errors, and operation/window/cancellation limits; delta reference data is omitted |
The bounded binary readers, format detection, parser limits/error model, path
policy, worker protocol/DTOs, package-to-section boundary, and application UI
are original TypeScript implementations guided by the normative specifications
and project requirements.
policy, package TOC-to-section hierarchy, worker protocol/DTOs, safe image
inspection, export serializers/ZIP writer, and React application UI are original
TypeScript implementations guided by the normative specifications and project
requirements. The pinned `one2html` renderer informed expected visual semantics
for rich text, tables, images, attachments, and ink; its Rust/templates/CSS were
not copied or shipped.
Files adapted from `onenote.rs` carry an MPL-2.0 notice, SPDX identifier,
upstream path/revision, and deviations. Files adapted from rust-cab, compcol,
or lzxd carry their MIT provenance, copyright notice, exact revision, and deviations.
or lzxd carry their MIT provenance, copyright notice, exact revision, and
deviations. The multi-cabinet module carries libmspack's LGPL-2.1-only notice.
Licence texts are reproduced in `LICENSES/` and included in the release ZIP.
Focused tests cover successful pinned fixtures plus malformed bounds,

View File

@@ -1,18 +1,20 @@
# Specifications and pinned references
Version 0.1.0 records these immutable implementation revisions:
Version 0.2.0 records these immutable implementation revisions:
| Repository | Exact revision | Use |
| ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------- |
| ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------- |
| [`toolbox-sdk`](https://git.add-ideas.de/zemion/toolbox-sdk/src/commit/5e206baf42dfae5a00f2743fb2cc501bb12d9b7a) | `5e206baf42dfae5a00f2743fb2cc501bb12d9b7a` | App contract, React shell, manifest schema/testkit, and nested-path behavior |
| [`toolbox-portal`](https://git.add-ideas.de/zemion/toolbox-portal/src/commit/5d2e466ad61e36a0e2be2a2d8459b1a127f53f11) | `5d2e466ad61e36a0e2be2a2d8459b1a127f53f11` | Reviewed ZIP shape, exact checksum lock, and independent-app assembly model |
| [`toolbox-portal`](https://git.add-ideas.de/zemion/toolbox-portal/src/commit/d46f1b61fb85a18299b6b8af2148dcd991ef6b05) | `d46f1b61fb85a18299b6b8af2148dcd991ef6b05` | Reviewed ZIP shape, exact checksum lock, independent-app assembly, and nginx deployment model |
| [`onenote.rs`](https://github.com/msiemens/onenote.rs/tree/5138a39a3f4e72b840932f9872fecde52fa9da60) | `5138a39a3f4e72b840932f9872fecde52fa9da60` | Selected desktop/FSSHTTPB OneStore and MS-ONE structures adapted into TypeScript |
| [`rust-cab`](https://github.com/mdsteele/rust-cab/tree/c5839f5fdfa4c4e7cc9b22f570c79c96af0560e2) | `c5839f5fdfa4c4e7cc9b22f570c79c96af0560e2` | CAB structures, checksum, enumeration/extraction, MSZIP history, and unit fixtures |
| [`compcol`](https://github.com/KarpelesLab/compcol/tree/04a6db2aa7bd487a89c559631d79d1b384139f50) | `04a6db2aa7bd487a89c559631d79d1b384139f50` | Quantum bit reader, arithmetic models, LZ window, CAB sentinel behavior, and tests |
| [`lzxd` 0.2.5](https://github.com/Lonami/lzxd/tree/4748e43594e3e30cff2ace3a6ad7a376c9816fdd) | `4748e43594e3e30cff2ace3a6ad7a376c9816fdd` | LZX bitstream, Huffman, block/window, match, and E8 logic adapted into TypeScript |
| [`libmspack`](https://github.com/kyz/libmspack/tree/55d501976171397ccd5d5a7a1ca7da065b1d9a06) | `55d501976171397ccd5d5a7a1ca7da065b1d9a06` | Multi-cabinet chain, split CFDATA, spanning-file semantics, and licensed fixtures |
| [`one2html`](https://github.com/msiemens/one2html/tree/59930ad309004030f812790c6749efe4265bb6bd) | `59930ad309004030f812790c6749efe4265bb6bd` | Behavioral reference for rich text, tables, images, attachments, ink, and HTML rendering |
The SDK and Portal revisions were inspected from the local repositories. The
four parser references were cloned and reviewed at the hashes above; a branch
The SDK and Portal revisions were inspected from the local repositories. Every
parser/renderer reference was cloned and reviewed at the hash above; a branch
name or package version alone is not used as provenance. Source-level mappings
and behavioural deviations are in [PORTING.md](PORTING.md).
@@ -50,5 +52,6 @@ The Microsoft documents are linked rather than copied. Their published pages
can change independently; the source references and golden bytes remain pinned
for reproducibility.
`one2html` is deliberately not pinned or inspected because visual HTML
rendering is outside this release.
The `one2html` renderer is a behavioral reference only: no Rust, Askama
template, stylesheet, or generated HTML is shipped or executed by this
application.

View File

@@ -2,35 +2,42 @@
## Trust boundary and local processing
Selected `.one` and `.onepkg` files are untrusted. The main thread checks the
source size, reads one file into an `ArrayBuffer`, transfers it to a dedicated
module worker, and receives serializable data-transfer objects. CAB entries are
extracted only into memory; archive paths are never written to the filesystem.
Selected `.one`, `.onepkg`, and companion `.cab` files are untrusted. The main
thread validates the selection and combined size, reads the files into
`ArrayBuffer`s, and transfers them to a dedicated module worker. CAB entries
are extracted only into memory; archive paths are never written to the
filesystem.
The worker is a fault-containment and responsiveness boundary, not a security
sandbox. A successful open keeps its worker session alive for page requests.
Clearing the file, choosing another file, or unmounting the app terminates that
worker; a failed open also terminates it. JavaScript garbage collection does
not promise immediate release or secure erasure.
sandbox. A successful open keeps its session alive for page/resource/export
requests. Clearing the file, choosing another file, or unmounting the app
terminates that worker; a failed open also terminates it. JavaScript garbage
collection does not promise immediate release or secure erasure.
The application does not:
- upload or transmit notebook bytes;
- use a backend, proxy, telemetry, analytics, remote fonts, or external content
fetching;
- use a notebook-processing application backend, upload proxy, telemetry,
analytics, remote fonts, or notebook-referenced external content fetching;
the optional same-origin Toolbox catalogue/manifest is application chrome
and receives no notebook data;
- persist source or derived content in LocalStorage, IndexedDB, Cache Storage,
cookies, or a service worker;
- execute notebook content or inject notebook HTML/SVG;
- use WebAssembly, a Rust runtime, or a native helper.
## Active default limits
## Active parser defaults
The main thread rejects a source larger than 512 MiB before reading it. The
OneStore section parsers then apply these independent defaults:
The main thread accepts one `.one`, or one `.onepkg` plus its `.cab`
companions. It permits at most 256 selected parts, 512 MiB per file, and 512 MiB
combined before reading.
The OneStore parser applies these independent defaults per section/store:
| Resource | Default limit |
| ------------------------------------- | -------------------: |
| ------------------------------------------------ | -------------------------: |
| Section bytes | 512 MiB |
| Transaction fragments / entries | 16,384 / 1,000,000 |
| File-node list fragments / nesting | 16,384 / 64 |
| File nodes | 1,000,000 |
| Objects | 500,000 |
@@ -38,26 +45,39 @@ OneStore section parsers then apply these independent defaults:
| Stream IDs | 1,000,000 |
| FSSHTTPB revision-chain entries | 16,384 |
| One property vector | 64 MiB |
| Object references | 1,000,000 |
| Desktop object-reference vector | 1,000,000 per vector |
| FSSHTTPB ExGUID/CellID references | 1,000,000 aggregate |
| Nested PropertySets per ObjectPropSet | 1,000,000 aggregate |
| File-data objects / one payload / total payloads | 10,000 / 256 MiB / 512 MiB |
| Content-graph depth | 256 |
| Pages per section | 100,000 |
| Diagnostics per section | 10,000 |
| Extracted text per section | 2,000,000 characters |
| Content blocks / text runs / table cells | 100,000 each |
| Decoded ink points | 100,000 |
| Pages | 10,000 |
| Diagnostics | 10,000 |
| Retained rich/extracted text | 2,000,000 UTF-16 units |
The CAB/package path applies these defaults:
Images are automatically previewable only when a bounded PNG IHDR or JPEG
marker/SOF inspection succeeds. The compressed payload must be at most 32 MiB,
each dimension at most 16,384 pixels, and the image at most 67,108,864 pixels.
JPEG inspection reads at most 1 MiB of markers and 4,096 marker records. Images
outside those limits stay inert and are available only through an explicit
download action.
The CAB/package path applies these defaults across a complete cabinet set:
| Resource | Default limit |
| ------------------------------- | -------------: |
| Cabinet input | 512 MiB |
| Folders / files | 1,024 / 10,000 |
| CFDATA blocks | 131,072 |
| ------------------------------------------- | -----------------: |
| One cabinet / complete set input | 512 MiB / 512 MiB |
| Cabinet parts | 256 |
| Folder segments / files | 1,024 / 10,000 |
| CFDATA fragments | 131,072 |
| CAB name bytes | 255 |
| Path characters / path segments | 4,096 / 64 |
| TOC construction operations / diagnostics | 1,000,000 / 10,000 |
| One extracted file | 256 MiB |
| One folder output | 512 MiB |
| Total folder output | 512 MiB |
| Total extracted entry bytes | 512 MiB |
| Folder compression ratio | 200:1 |
| One logical folder output | 512 MiB |
| Total folder output / extracted entry bytes | 512 MiB / 512 MiB |
| Logical-folder compression ratio | 200:1 |
| One MSZIP compressed block | 32 KiB + 12 B |
| Quantum window | 2 MiB |
| LZX window | 8 MiB |
@@ -65,56 +85,89 @@ The CAB/package path applies these defaults:
A CFDATA block may declare at most 32 KiB of output. Counts and offsets use
checked arithmetic and bounded readers. The CAB parser verifies supplied block
checksums, rejects overlapping/out-of-range data, rejects absolute/traversal,
empty, control-character, and case-insensitive duplicate paths, and refuses
multi-cabinet/spanning entries.
checksums; rejects overlapping/out-of-range data, traversal/absolute/control or
case-insensitive duplicate paths; and validates a complete cabinet set before
decoding. Set IDs, indexes, links, compression, split blocks, and spanning-file
records must agree. Reserve areas are private to each cabinet/folder and are
validated locally rather than compared across parts.
MSZIP checks its `CK` framing and raw DEFLATE data, writes into a fixed caller
buffer with one overflow-detection byte, requires the exact declared output
size, and retains no more than 32 KiB of history. LZX checks its window, Huffman
trees, block sizes, match ranges, output size, and operation budget. CAB
Quantum limits its window, model scans, arithmetic renormalisation, matches,
frame/output boundaries, operation count, and trailer padding. It queues at
most the current and next compressed block so a synthetic `0xFF` sentinel can
be consumed without buffering a complete folder. CAB extraction checks
cancellation during Quantum work and yields between data blocks so the worker
can process termination. Desktop OneStore traversal checks fragment/reference
cycles, nesting, object/property/reference counts, and byte ranges. FSSHTTPB
parsing bounds every declared stream body and compact integer, rejects invalid
end markers and non-zero package tails, checks mapping/reference counts and
revision cycles, and retains BLOB payloads as bounded source ranges. Fragment
records are range-checked, but packages containing them are rejected because
MSZIP checks `CK` framing and raw DEFLATE data, writes into a fixed caller
buffer with overflow detection, requires the declared output size, and keeps at
most 32 KiB of logical-folder history. LZX and Quantum validate their windows,
trees/models, blocks/frames, matches, output, trailer, and operation budgets.
Codec state is preserved across continued cabinet segments. Extraction checks
cancellation between data blocks and periodically yields to the worker loop.
Desktop OneStore traversal checks fragment/reference cycles, nesting,
object/property/reference counts, and byte ranges. FSSHTTPB parsing bounds every
declared stream body and compact integer, rejects invalid end markers and
non-zero package tails, checks aggregate reference/mapping counts and revision
cycles, and retains BLOB payloads as bounded source ranges. Fragment records
are range-checked, but packages containing them are rejected because
fragmented-element reconstruction is not implemented.
## Rendering and links
## Display and export budgets
DTO strings are rendered as React text nodes. Notebook HTML, active SVG,
scripts, attachment payloads, and automatic external navigation are not
rendered. Link activation is not implemented. If links are added, they require
explicit user action, scheme validation, and safe navigation attributes.
Parser DTOs are projected through a smaller page-display budget before React
creates nodes: 5,000 blocks, depth 64, 10,000 text runs, 500,000 displayed text
units, 2,000 table rows, 5,000 table cells, 256 columns, 10,000 ink strokes,
50,000 ink points, and 4,096 characters per metadata label. Exceeding a display
budget produces an explicit truncation notice. Newlines remain inside text
nodes instead of multiplying DOM elements.
## Known gaps and roadmap
Navigation and inspectors separately expose at most 250 page links, 500
notebook nodes at depth 16, 200 package entries per page, and 100 diagnostics
per page. Titles, previews, package paths, diagnostic fields, and panel labels
are length-bounded; pagination/truncation remains explicit in the UI.
- There is no wall-clock deadline. Synchronous OneStore `.one` parsing cannot
observe cooperative cancellation until control returns; terminating the
worker is the current hard stop.
- The package flow holds the source, decompressed folder buffers, extracted
entry slices, and parsed DTOs in memory at overlapping times. The byte/count
limits bound these individually but do not guarantee a low peak heap.
- Section text/page/diagnostic limits apply per contained section rather than
once to the complete package.
Export planning independently permits at most 10,000 sections, 100,000 pages,
1,000,000 blocks, depth 256, 100,000 diagnostics, 8,000,000 text units, and
500,000 ink points. One resource and all resources are capped at 64 MiB; one
standalone output or all uncompressed static-ZIP members are capped at 128 MiB.
Planning accounts for metadata and projected UTF-8 output before serialization,
then verifies actual encoded/member sizes. Resource views remain zero-copy until
the bounded serializer needs bytes.
## Rendering, links, and downloads
DTO strings are rendered as React text nodes. Notebook HTML, scripts, active
SVG, and attachment payloads are never injected. Only parsed ink coordinates
generate application-owned SVG path attributes. Hyperlinks require an explicit
click, are limited to `http`, `https`, or `mailto`, and use
`rel="noopener noreferrer"`.
Automatic image requests carry a distinct preview intent. The worker rejects a
non-image, unapproved media type, unsafe dimensions, or oversized payload before
allocating/copying its bytes to the main thread. Approved image bytes may cross
for automatic preview; attachment bytes and rejected image bytes cross only
after the user presses a download button.
Static export HTML is inert, contains no script or external fetch, escapes all
text/attributes, embeds only images that pass the same inspector, and
neutralizes potentially active attachment suffixes. Exports represent the
supported reader model; they do not write OneNote files.
## Known gaps
- There is no wall-clock deadline. Synchronous OneStore graph parsing cannot
observe cancellation until control returns; terminating the worker is the
current hard stop.
- Package opening can hold source cabinets, decompressed logical folders,
extracted entry views, and parsed DTOs at overlapping times. Byte/count limits
bound them, but do not guarantee a low peak heap.
- Section content limits apply per contained section rather than once to the
complete `.onepkg`; cabinet/TOC limits are aggregate for the package set.
- CAB checksums detect accidental corruption but do not authenticate a package.
- Format coverage is narrow and has only the documented golden files. New
producer/version claims require licensed regression fixtures and malformed
- Format coverage is limited to the documented golden files and structures.
New producer/version claims require licensed regression fixtures and malformed
variants.
- The worker boundary reduces UI impact but is not a substitute for browser and
operating-system isolation. Open especially sensitive files in a dedicated
browser profile if that risk matters.
Planned hardening includes an elapsed-time budget, cancellation checkpoints in
desktop OneStore graph traversal, aggregate package DTO/text budgets, lower
peak-memory extraction, expanded malformed-input/fuzz corpora, and additional
licensed OneNote export fixtures.
Planned hardening includes elapsed-time budgets, more cancellation checkpoints,
lower-peak-memory extraction, expanded malformed-input/fuzz corpora, fragmented
FSSHTTP reconstruction, and additional licensed OneNote producer fixtures.
## Fixtures and vulnerability reporting

6
package-lock.json generated
View File

@@ -1,13 +1,13 @@
{
"name": "onenote-tools",
"version": "0.1.0",
"version": "0.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "onenote-tools",
"version": "0.1.0",
"license": "MPL-2.0",
"version": "0.2.0",
"license": "MPL-2.0 AND LGPL-2.1-only",
"dependencies": {
"@add-ideas/toolbox-contract": "0.1.1",
"@add-ideas/toolbox-shell-react": "0.1.1",

View File

@@ -1,8 +1,8 @@
{
"name": "onenote-tools",
"version": "0.1.0",
"description": "A local-first browser workspace for native OneNote parsing.",
"license": "MPL-2.0",
"version": "0.2.0",
"description": "A local-first browser reader and exporter for OneNote files.",
"license": "MPL-2.0 AND LGPL-2.1-only",
"private": true,
"type": "module",
"engines": {

View File

@@ -3,11 +3,22 @@
"schemaVersion": 1,
"id": "de.add-ideas.onenote-tools",
"name": "OneNote Tools",
"description": "Local-first reader for tested desktop revision-store .one sections and LZX .onepkg packages; other OneNote formats are not implemented.",
"description": "Local-first reader and exporter for tested .one and .onepkg variants, with notebook trees, rich content, and split CAB package support.",
"entry": "./",
"icon": "./favicon.svg",
"categories": ["documents", "onenote"],
"tags": ["local", "read-only", "one", "onepkg", "cab", "lzx"],
"tags": [
"local",
"read-only",
"one",
"onepkg",
"cab",
"lzx",
"mszip",
"quantum",
"fsshttp",
"export"
],
"integration": {
"contextVersion": 1,
"launchModes": ["navigate", "new-tab"],
@@ -27,7 +38,7 @@
},
"source": {
"repository": "https://git.add-ideas.de/zemion/onenote-tools",
"license": "MPL-2.0"
"license": "MPL-2.0 AND LGPL-2.1-only"
},
"version": "0.1.0"
"version": "0.2.0"
}

View File

@@ -13,20 +13,27 @@ export function HelpDialog({ onClose }: HelpDialogProps) {
>
<h2 id="help-title">About OneNote Tools</h2>
<p>
Read the tested desktop revision-store .one subset and single-cabinet
LZX or uncompressed .onepkg packages with a native TypeScript parser.
Unsupported content remains visible as diagnostics instead of being
silently discarded.
Open tested desktop and unfragmented FSSHTTPB OneNote sections or
complete single- and multi-cabinet .onepkg sets with a native
TypeScript parser. For a split package, select its .onepkg and all
linked .cab files together.
</p>
<p>
FSSHTTP sections, MSZIP and Quantum packages, multi-cabinet archives,
notebook TOC ordering, visual fidelity, attachments, and export are
not supported in this release.
Notebook trees, rich text, safe links, tables, PNG/JPEG images, ink,
attachments, and local JSON, text, Markdown, HTML, or static ZIP
exports are supported within documented safety and format limits.
Encountered unsupported object kinds remain visible as diagnostics.
</p>
<p>
Nothing is uploaded, persisted, or sent to telemetry. Closing or
clearing releases application references and terminates the worker;
this is not a secure-erasure guarantee.
Fragmented FSSHTTPB data, equations, audio/video, exact OneNote page
placement, and untested producer variants remain unsupported. Exports
preserve readable content but do not create OneNote files.
</p>
<p>
Nothing is uploaded or sent to telemetry, and the app stores no
notebook data unless you explicitly download a resource or create an
export. Closing or clearing releases application references and
terminates the worker; this is not a secure-erasure guarantee.
</p>
<button type="button" onClick={onClose}>
Close

View File

@@ -529,6 +529,15 @@ p {
background: #f5eff8;
}
.render-limit-notice {
margin: 1rem 0 0;
border-left: 0.2rem solid #b18124;
padding: 0.7rem 0.85rem;
color: #66552f;
background: #fbf4e6;
font-size: 0.85rem;
}
.package-inspector,
.diagnostics {
padding: 1.25rem;

View File

@@ -1,11 +1,22 @@
{
"id": "de.add-ideas.onenote-tools",
"name": "OneNote Tools",
"description": "Local-first reader for tested desktop revision-store .one sections and LZX .onepkg packages; other OneNote formats are not implemented.",
"description": "Local-first reader and exporter for tested .one and .onepkg variants, with notebook trees, rich content, and split CAB package support.",
"entry": "./",
"icon": "./favicon.svg",
"categories": ["documents", "onenote"],
"tags": ["local", "read-only", "one", "onepkg", "cab", "lzx"],
"tags": [
"local",
"read-only",
"one",
"onepkg",
"cab",
"lzx",
"mszip",
"quantum",
"fsshttp",
"export"
],
"integration": {
"contextVersion": 1,
"launchModes": ["navigate", "new-tab"],
@@ -25,6 +36,6 @@
},
"source": {
"repository": "https://git.add-ideas.de/zemion/onenote-tools",
"license": "MPL-2.0"
"license": "MPL-2.0 AND LGPL-2.1-only"
}
}

View File

@@ -10,7 +10,8 @@ describe('toolbox manifest', () => {
});
it('does not advertise unimplemented format support', () => {
expect(toolboxApp.description).toContain('not implemented');
expect(toolboxApp.description).toContain('tested');
expect(toolboxApp.description).not.toContain('all OneNote');
expect(toolboxApp.privacy).toEqual(
expect.objectContaining({
processing: 'local',

View File

@@ -1 +1 @@
export const APP_VERSION = '0.1.0';
export const APP_VERSION = '0.2.0';

View File

@@ -17,7 +17,8 @@ and does not change the underlying file's licence.
`7c43e85e2169b4bc3236db73a24c8f20d5c284b74563c07baf1406887d20eb5e`.
- Golden expectation: desktop OneStore section with three pages — `Test`,
`Another page`, and `Page 3` — plus readable text/timestamps and diagnostics
for image, table, and ink content.
plus structured rich text, a PNG resource, a table, and 1,316 decoded ink
points across 11 ink blocks.
## `joplin-test.onepkg.base64`
@@ -33,7 +34,8 @@ and does not change the underlying file's licence.
and nested paths, five `.one` sections, and two `.onetoc2` entries.
- Parser expectation: all five sections parse, producing seven pages in total:
`Page 1`, `Page 2`, `Test`, `Test page`, `Testing…`, `A test`, and
`Tuesday, January 13, 2026`.
`Tuesday, January 13, 2026`; the contained TOCs provide interpreted notebook
ordering rather than normalized-path-only navigation.
Tests verify the package hash, all seven paths, the native `.one` magic for
every section, section parse statuses, and representative title/text output.

View File

@@ -5,6 +5,11 @@ import { defineConfig } from 'vite';
export default defineConfig({
base: './',
plugins: [react()],
build: {
// Baseline browsers support modulepreload natively. Avoid shipping Vite's
// injected runtime polyfill (and an otherwise unnecessary runtime notice).
modulePreload: { polyfill: false },
},
test: {
environment: 'jsdom',
setupFiles: './src/test/setup.ts',