Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
bb1b1a4
test(odf.js): pin ods writer cell-run, cell-value, and canonicaliser …
Mearman Sep 12, 2026
6101ca6
test(odf.js): fix invalid fixture values in canonical* unit tests
Mearman Sep 12, 2026
7f31922
test(odf.js): pin regular-polygon/rounded-rect geometry and per-vecto…
Mearman Sep 12, 2026
555d518
test(odf.js): pin the shared canonicalise.ts helpers directly, and dr…
Mearman Sep 13, 2026
0bb6b8c
test(odf.js): cover parseOdfAngleDeg, isLengthUnit and expandExponent…
Mearman Sep 13, 2026
4bf7a94
test(odf.js): add the first direct unit coverage for typed/shared/lis…
Mearman Sep 13, 2026
7462ad8
test(odf.js): add the first direct unit coverage for typed/shared/exp…
Mearman Sep 13, 2026
233fd27
chore(odf.js): raise the mutation break threshold to the re-measured …
Mearman Sep 13, 2026
e58f164
test(odf.js): give the BOM check its own testable function in parsePa…
Mearman Sep 13, 2026
df1a600
test(odf.js): add the first direct unit coverage for typed/shared/for…
Mearman Sep 13, 2026
ffcd2d7
test(odf.js): kill every mutant in test-support/zip.ts's LE integer r…
Mearman Sep 13, 2026
86ee96d
test(odf.js): kill every mutant in test-support/document-tree.ts's he…
Mearman Sep 13, 2026
269cd9f
test(odf.js): add the first direct unit coverage for xml/build.ts
Mearman Sep 13, 2026
131146a
test(odf.js): kill xml/build.ts's remaining survivors
Mearman Sep 13, 2026
67db12b
test(odf.js): add the first direct unit coverage for image/sniff.ts
Mearman Sep 13, 2026
eedab8e
test(odf.js): add the first direct unit coverage for util/base64.ts
Mearman Sep 13, 2026
e931725
test(odf.js): cover columnLettersToIndex and TableCursor's own error …
Mearman Sep 13, 2026
b98cff3
test(odf.js): pin columnLettersToIndex's uppercase-only guard against…
Mearman Sep 13, 2026
08e6bf1
test(odf.js): kill typed/shared/text.ts's remaining survivors
Mearman Sep 13, 2026
440bb94
test(odf.js): add the first direct unit coverage for package-io/scaff…
Mearman Sep 13, 2026
b8cf35b
test(odf.js): add the first direct unit coverage for typed/shared/bor…
Mearman Sep 13, 2026
68e0368
test(odf.js): kill typed/shared/list.ts's remaining survivors
Mearman Sep 13, 2026
3574f7b
test(odf.js): kill typed/shared/transform.ts's remaining survivors
Mearman Sep 13, 2026
f144de9
test(odf.js): kill the last two typed/shared/transform.ts survivors
Mearman Sep 13, 2026
b611fa3
test(odf.js): kill typed/shared/metadata.ts's remaining survivors
Mearman Sep 13, 2026
8b9ef6b
test(odf.js): kill ooo1/ns.ts's remaining survivors
Mearman Sep 13, 2026
2d39be4
test(odf.js): add the first direct unit coverage for typed/draw/embed…
Mearman Sep 13, 2026
a7eed51
test(odf.js): kill typed/odp/read.ts's remaining survivors
Mearman Sep 13, 2026
9b91768
fix(odf.js): supply the required frame field in embedded-write's own …
Mearman Sep 13, 2026
bcfe781
test(odf.js): kill typed/shared/cascade.ts's remaining survivors
Mearman Sep 13, 2026
cb5647d
test(odf.js): kill typed/shared/masterpage.ts's remaining survivors
Mearman Sep 13, 2026
af16469
test(odf.js): kill typed/shared/units.ts's remaining survivors
Mearman Sep 13, 2026
795b7b6
test(odf.js): add the first direct unit coverage for typed/shared/ima…
Mearman Sep 13, 2026
f2a60c6
test(odf.js): kill typed/odb/subdocument.ts's remaining survivor
Mearman Sep 13, 2026
be4696e
test(odf.js): kill typed/odg/read.ts's remaining survivors
Mearman Sep 13, 2026
9350f92
test(odf.js): kill typed/odm/write.ts's remaining survivors
Mearman Sep 13, 2026
19f9a2e
test(odf.js): kill typed/shared/expression.ts's remaining survivor
Mearman Sep 13, 2026
f5f414a
test(odf.js): add the first direct unit coverage for package-io/write.ts
Mearman Sep 13, 2026
0081169
test(odf.js): kill styles/serialize.ts's remaining survivor
Mearman Sep 13, 2026
b06b647
test(odf.js): kill typed/formula/read.ts's remaining survivor
Mearman Sep 13, 2026
f3b83a6
test(odf.js): kill typed/odm/read.ts's remaining survivor
Mearman Sep 13, 2026
c844d8d
test(odf.js): kill util/base64.ts's remaining bytesToBase64 survivors
Mearman Sep 13, 2026
f20e574
test(odf.js): kill package-io/write.ts's remaining survivor
Mearman Sep 13, 2026
095155c
test(odf.js): pin that only the mimetype entry is stored uncompressed
Mearman Sep 13, 2026
82f29be
test(odf.js): kill typed/odm/read.ts's remaining tag-check survivor
Mearman Sep 13, 2026
7f0bb50
test(odf.js): kill styles/span.ts's redundant-guard and undercovered …
Mearman Sep 13, 2026
4e24d76
test(odf.js): kill styles/registry.ts's redundant-guard and undercove…
Mearman Sep 13, 2026
47fbbe4
test(odf.js): kill typed/formula/write.ts's remaining survivors
Mearman Sep 13, 2026
72d6e95
test(odf.js): kill typed/odg/write.ts's remaining survivors
Mearman Sep 13, 2026
5799c6e
test(odf.js): kill typed/draw/embedded.ts's remaining survivors
Mearman Sep 13, 2026
9fde337
test(odf.js): reach 100% mutation coverage on typed/odb/report.ts
Mearman Sep 14, 2026
4c1a0c7
test(odf.js): reach 100% mutation coverage on typed/ods/conditional-f…
Mearman Sep 14, 2026
c5f9add
test(odf.js): kill readCondition's remaining rank-branch survivors
Mearman Sep 14, 2026
44992cb
test(odf.js): reach 100% mutation coverage on typed/ods/data-validati…
Mearman Sep 14, 2026
5194462
refactor(odf.js): simplify insertOdfConstructMarkers and export isEmb…
Mearman Sep 14, 2026
449f360
test(odf.js): add direct unit coverage for typed/shared/constructs.ts
Mearman Sep 14, 2026
2fd90a1
test(odf.js): fix ContentControlDescriptor fixture's controlType value
Mearman Sep 14, 2026
2609935
test(odf.js): kill typed/shared/constructs.ts's remaining survivors
Mearman Sep 14, 2026
a961e2b
test(odf.js): cover typed/shared/constructs.ts's remaining no-coverag…
Mearman Sep 14, 2026
c4995c5
docs(odf.js): note shared/constructs.ts's zero survivors in the break…
Mearman Sep 14, 2026
5327d44
test(odf.js): cover formatServerDatabaseUrl's missing db:type branch
Mearman Sep 14, 2026
09b7871
refactor(odf.js): remove two unobservable branches from draw/shapes.ts
Mearman Sep 14, 2026
b7dfc5e
test(odf.js): kill typed/draw/shapes.ts's remaining survivors
Mearman Sep 14, 2026
d9b7312
test(odf.js): cover readOdbInventory's remaining connection/component…
Mearman Sep 14, 2026
1060552
refactor(odf.js): simplify transform.ts's colon check and drop a redu…
Mearman Sep 14, 2026
5b5a7d4
test(odf.js): cover transform.ts's carriesNoLength, href, and reverse…
Mearman Sep 14, 2026
a0252c6
refactor(odf.js): remove reverse-direction branches the caller's own …
Mearman Sep 14, 2026
06ca1ab
test(odf.js): cover transform.ts's classAttributeName and remaining r…
Mearman Sep 14, 2026
0fca8fb
docs(odf.js): note ooo1/transform.ts's zero survivors in the breakThr…
Mearman Sep 14, 2026
025e5b4
fix(odf.js): clamp skipExpression's index advances to text.length
Mearman Sep 14, 2026
e789a68
test(odf.js): cover sheetCellStyle's decoration-present and no-decora…
Mearman Sep 14, 2026
b46c5a7
test(odf.js): cover data validation message and interning branches
Mearman Sep 14, 2026
54c8eb5
test(odf.js): cover unsupportedConditionalFormatReason's refusal bran…
Mearman Sep 14, 2026
1dac393
refactor(odf.js): compute the used range's max row/column via Math.max
Mearman Sep 14, 2026
a62bc59
test(odf.js): cover dataBar's unset showValue and an empty conditiona…
Mearman Sep 14, 2026
2cb427c
test(odf.js): recompute readOdbInventory fresh per test and cover its…
Mearman Sep 14, 2026
3944cb0
test(odf.js): cover typed/shared/table.ts's read/write decoration and…
Mearman Sep 14, 2026
af69ce0
test(odf.js): add direct unit coverage for canonicalRun
Mearman Sep 14, 2026
e630e32
test(odf.js): add direct XML-structure coverage for odb/write.ts
Mearman Sep 14, 2026
3f55383
test(odf.js): add the first direct unit coverage for typed/draw/write…
Mearman Sep 14, 2026
fc2ae49
test(odf.js): cover paragraph.ts's note/annotation sequential minting
Mearman Sep 14, 2026
27b692c
test(odf.js): add the first direct unit coverage for ooo1/properties.ts
Mearman Sep 14, 2026
62897b7
test(odf.js): add the first direct unit coverage for model/node.ts
Mearman Sep 14, 2026
719099d
test(odf.js): add the first direct unit coverage for xml/parse.ts
Mearman Sep 14, 2026
cbd9e97
test(odf.js): add the first direct unit coverage for typed/draw/write…
Mearman Sep 15, 2026
f3d4872
refactor(odf.js): share one file-entry parse between readManifest and…
Mearman Sep 15, 2026
314b7bc
fix(odf.js): drop parseLineDecoration's hardcoded no-op companion pla…
Mearman Sep 15, 2026
cf0a0cc
test(odf.js): cover propertyTypesForContainer and splitStylePropertie…
Mearman Sep 15, 2026
80b802e
test(odf.js): cover ods write's used-range extension and validation d…
Mearman Sep 15, 2026
4d18484
test(odf.js): use toStrictEqual for canonicalRun/canonicalCell's own …
Mearman Sep 15, 2026
410ac0d
test(odf.js): use toStrictEqual throughout the ods canonical* helpers…
Mearman Sep 15, 2026
0ab2112
fix(odf.js): materialise a cell for an embedded-object-only anchor po…
Mearman Sep 15, 2026
8296296
test(odf.js): cover writeSheetEmbeddedObjectFrame's own sequential z-…
Mearman Sep 15, 2026
f4f6b38
test(odf.js): kill isXmlNode's unrecognised-type and typeof survivors
Mearman Sep 15, 2026
279d478
test(odf.js): kill write-vectors.ts's stroke, height, and rotation su…
Mearman Sep 15, 2026
a558114
test(odf.js): kill odb write's xlink:type and version-fallback survivors
Mearman Sep 15, 2026
38f2391
test(odf.js): kill canonicalise.ts's undefined-key and coverage survi…
Mearman Sep 15, 2026
5634b16
docs(odf.js): raise the mutation break threshold to the re-measured f…
Mearman Sep 15, 2026
d41caf6
docs(odf.js): correct the mutation gap numbers to the CI-confirmed me…
Mearman Sep 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/odf.js/eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export default tseslint.config(
preferReadonlyParams: "off",
}),
{
// fast-xml-parser@5 deprecates the whole XMLBuilder class, not one of its options, and ships no replacement of its own -- it points at a separate `fast-xml-builder` package that is not a declared dependency here. Swapping it is a real dependency decision with round-trip fidelity to re-verify (this builder is what keeps XML byte-faithful), so it is tracked rather than guessed at inside a tooling change. Scoped to the one module that constructs the builder.
files: ["src/xml/build.ts"],
// fast-xml-parser@5 deprecates the whole XMLBuilder class, not one of its options, and ships no replacement of its own it points at a separate `fast-xml-builder` package that is not a declared dependency here. Swapping it is a real dependency decision with round-trip fidelity to re-verify (this builder is what keeps XML byte-faithful), so it is tracked rather than guessed at inside a tooling change. Scoped to the one module that constructs the builder, plus its own test file, which necessarily references the identical deprecated class to reach BUILDER's shared prototype.
files: ["src/xml/build.ts", "src/xml/build.test.ts"],
rules: { "@typescript-eslint/no-deprecated": "off" },
},
);
84 changes: 84 additions & 0 deletions packages/odf.js/src/image/sniff.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { describe, expect, it } from "vitest";
import { sniffImageFormat } from "./sniff";

function bytesOf(values: number[]): Uint8Array<ArrayBuffer> {
return new Uint8Array(values);
}

function asciiBytes(text: string): number[] {
return Array.from(text, (c) => c.charCodeAt(0));
}

describe("sniffImageFormat", () => {
it("detects a PNG from its 8-byte magic signature", () => {
expect(
sniffImageFormat(
bytesOf([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0xff]),
),
).toBe("png");
});

it("detects a JPEG from its 3-byte magic signature", () => {
expect(sniffImageFormat(bytesOf([0xff, 0xd8, 0xff, 0xe0]))).toBe("jpeg");
});

it("detects a GIF87a header", () => {
expect(sniffImageFormat(bytesOf(asciiBytes("GIF87a").concat([0x00])))).toBe(
"gif",
);
});

it("detects a GIF89a header", () => {
expect(sniffImageFormat(bytesOf(asciiBytes("GIF89a").concat([0x00])))).toBe(
"gif",
);
});

it("returns undefined for bytes shorter than every signature it checks", () => {
expect(sniffImageFormat(bytesOf([0x89, 0x50]))).toBeUndefined();
});

it("returns undefined for an empty byte array", () => {
expect(sniffImageFormat(bytesOf([]))).toBeUndefined();
});

it("detects SVG from an XML prolog, with no '<svg' tag present in the sniffed window", () => {
const bytes = bytesOf(asciiBytes('<?xml version="1.0"?><notsvg/>'));
expect(sniffImageFormat(bytes)).toBe("svg");
});

it("detects SVG from a bare '<svg' root tag, with no XML prolog", () => {
const bytes = bytesOf(
asciiBytes('<svg xmlns="http://www.w3.org/2000/svg"></svg>'),
);
expect(sniffImageFormat(bytes)).toBe("svg");
});

it("skips leading whitespace before the '<?xml' prolog", () => {
const bytes = bytesOf(asciiBytes(' <?xml version="1.0"?><svg/>'));
expect(sniffImageFormat(bytes)).toBe("svg");
});

it("returns undefined for text that starts with neither '<?xml' nor '<svg'", () => {
const bytes = bytesOf(asciiBytes("<html><body/></html>"));
expect(sniffImageFormat(bytes)).toBeUndefined();
});

it("never finds a root element hidden behind more leading whitespace than the sniff window covers", () => {
// The sniff window is capped at a fixed size specifically so a caller can't be made to scan an unboundedly large file — a real SVG's root element always appears well within it (see sniff.ts's own comment), so padding past the window with plain spaces before the real tag is exactly the case the cap is meant to give up on, not a bug to work around.
const paddingLength = 2000;
const bytes = bytesOf([
...Array<number>(paddingLength).fill(0x20),
...asciiBytes("<?xml?>"),
]);
expect(bytes.length).toBeGreaterThan(1024);
expect(sniffImageFormat(bytes)).toBeUndefined();
});

it("does not match bytes that merely end with, rather than start with, an SVG marker", () => {
const bytes = bytesOf(
asciiBytes("<html>embeds a literal <?xml tag</html>"),
);
expect(sniffImageFormat(bytes)).toBeUndefined();
});
});
4 changes: 1 addition & 3 deletions packages/odf.js/src/image/sniff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ const GIF89A_SIGNATURE: readonly number[] = [
0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
];

// No separate "bytes too short" guard: when bytes.length < signature.length, some index i in the loop below reads past the end of bytes, and an out-of-bounds array read is `undefined` in JS -- which is never strictly equal to signature[i] (always a real 0-255 byte value), so the loop's own mismatch check already returns false for every too-short input. A dedicated length guard would only ever produce a result the loop already produces on its own.
function startsWith(
bytes: Uint8Array<ArrayBuffer>,
signature: readonly number[],
): boolean {
if (bytes.length < signature.length) {
return false;
}
for (let i = 0; i < signature.length; i++) {
if (bytes[i] !== signature[i]) {
return false;
Expand Down
Loading