single-file-core 1.6.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/core/helper.js +5 -2
  2. package/modules/css-fonts-minifier.js +34 -13
  3. package/modules/template-formatter.js +8 -2
  4. package/package.json +10 -2
  5. package/.github/workflows/publish.yml +0 -88
  6. package/.github/workflows/test.yml +0 -31
  7. package/.github/workflows/vendor.yml +0 -39
  8. package/css-tree-build/README.md +0 -39
  9. package/css-tree-build/build.js +0 -66
  10. package/css-tree-build/package-lock.json +0 -511
  11. package/css-tree-build/package.json +0 -13
  12. package/deno.lock +0 -19
  13. package/doc/assets/singlefile-archive-byte-map.svg +0 -276
  14. package/doc/assets/singlefile-archive-lenses.svg +0 -245
  15. package/doc/singlefile-archive.md +0 -1968
  16. package/eslint.config.mjs +0 -74
  17. package/test/capture/README.md +0 -74
  18. package/test/capture/canonical-link.js +0 -50
  19. package/test/capture/common.js +0 -73
  20. package/test/capture/deferred-content-options.js +0 -71
  21. package/test/capture/dom.js +0 -26
  22. package/test/capture/maff-metadata.js +0 -205
  23. package/test/capture/resource-cap.js +0 -79
  24. package/test/capture/script-uri-sanitization.js +0 -82
  25. package/test/fixtures/README.md +0 -18
  26. package/test/fixtures/classic-digest.html +0 -5
  27. package/test/fixtures/generate-fixtures.sh +0 -31
  28. package/test/fixtures/multi-page-dedup.zip.html +0 -70
  29. package/test/fixtures/multi-page.zip.html +0 -26
  30. package/test/fixtures/single-page.zip.html +0 -11
  31. package/test/fixtures/site/multi-page/alpha.html +0 -2
  32. package/test/fixtures/site/multi-page/beta.html +0 -2
  33. package/test/fixtures/site/multi-page/index.html +0 -2
  34. package/test/fixtures/site/multi-page/sub/gamma.html +0 -2
  35. package/test/fixtures/site/multi-page-dedup/index.html +0 -2
  36. package/test/fixtures/site/multi-page-dedup/logo.png +0 -0
  37. package/test/fixtures/site/multi-page-dedup/one.html +0 -2
  38. package/test/fixtures/site/multi-page-dedup/shared.css +0 -3
  39. package/test/fixtures/site/multi-page-dedup/two.html +0 -2
  40. package/test/run.js +0 -109
  41. package/test/sfz-harness/README.md +0 -87
  42. package/test/sfz-harness/adopted-stylesheets-hook.js +0 -240
  43. package/test/sfz-harness/byte-map.js +0 -137
  44. package/test/sfz-harness/charset-round-trip.js +0 -161
  45. package/test/sfz-harness/check-determinism.js +0 -57
  46. package/test/sfz-harness/common.js +0 -154
  47. package/test/sfz-harness/content-type-sniffing.js +0 -83
  48. package/test/sfz-harness/css-fonts-minifier.js +0 -235
  49. package/test/sfz-harness/css-property-filter.js +0 -113
  50. package/test/sfz-harness/dom-stub.js +0 -13
  51. package/test/sfz-harness/entry-compression.js +0 -77
  52. package/test/sfz-harness/filename-characters.js +0 -55
  53. package/test/sfz-harness/filename-max-length.js +0 -69
  54. package/test/sfz-harness/font-face-composite.js +0 -135
  55. package/test/sfz-harness/format-rules.js +0 -617
  56. package/test/sfz-harness/gen-e2e-page.js +0 -14
  57. package/test/sfz-harness/inlined-functions.js +0 -82
  58. package/test/sfz-harness/option-wiring.js +0 -60
  59. package/test/sfz-harness/pages-archive.js +0 -318
  60. package/test/sfz-harness/pages-router.js +0 -143
  61. package/test/sfz-harness/relocation-cost.js +0 -94
  62. package/test/sfz-harness/search-triggers.js +0 -35
  63. package/test/sfz-harness/smoke.js +0 -10
  64. package/test/sfz-harness/stored-trigger.js +0 -95
  65. package/test/sfz-harness/trigger-seeds.json +0 -120
  66. package/test/sfz-harness/zip64.js +0 -77
  67. package/zip-build/README.md +0 -56
  68. package/zip-build/lib/fflate-streams.js +0 -47
  69. package/zip-build/lib/fflate.js +0 -2692
  70. package/zip-build/lib/zip-vendor-worker.js +0 -3
  71. package/zip-build/lib/zip-vendor.js +0 -9
  72. package/zip-build/lib/zip.js +0 -25
  73. package/zip-build/package-lock.json +0 -665
  74. package/zip-build/package.json +0 -15
  75. package/zip-build/reserved-property-names.json +0 -10630
  76. package/zip-build/rollup.config.js +0 -61
@@ -1,94 +0,0 @@
1
- // §5.2 of doc/singlefile-archive.md quantifies what relocating the extra-data element costs. The
2
- // figures it carried until 2026-09-12 came from two live captures and one fixture, and two of the
3
- // three did not reconstruct: the paragraph subtracted the 17 bytes of terminator and end tags from
4
- // the reservation without subtracting the element, which the appended placement also carries, so
5
- // it over-counted by the whole element. This pins the corrected arithmetic. The cost is the
6
- // reservation margin alone, it is positive on every rung whenever an element exists, and the only
7
- // way to make relocation save bytes is to have no element to relocate.
8
- import { makePageData, makeOptions, runProcess, freezeDate } from "./common.js";
9
-
10
- const DECODER = new TextDecoder("windows-1252");
11
- const OPEN_TAG = "<sfz-extra-data>";
12
- const CLOSE_TAG = "</sfz-extra-data>";
13
- const END_TAGS_LENGTH = "</body></html>".length;
14
-
15
- let failed = false;
16
-
17
- function check(label, actual, expected) {
18
- const ok = actual === expected;
19
- console.log(`${ok ? "PASS" : "FAIL"} ${label}: ${actual}${ok ? "" : " (expected " + expected + ")"}`);
20
- failed ||= !ok;
21
- }
22
-
23
- function reservationSize(length) {
24
- return Math.ceil(length * 1.01) + 32;
25
- }
26
-
27
- function inspect(bytes) {
28
- const text = DECODER.decode(bytes);
29
- const open = text.indexOf(OPEN_TAG);
30
- const firstHeader = text.indexOf("PK\x03\x04");
31
- return {
32
- total: bytes.length,
33
- element: open == -1 ? 0 : text.indexOf(CLOSE_TAG, open) + CLOSE_TAG.length - open,
34
- relocated: open != -1 && open < firstHeader
35
- };
36
- }
37
-
38
- async function build(seed, targetLength, overrides) {
39
- const restoreDate = freezeDate();
40
- const appended = inspect((await runProcess(makePageData(seed, targetLength), makeOptions(overrides))).bytes);
41
- const relocated = inspect((await runProcess(makePageData(seed, targetLength), makeOptions({ ...overrides, preventAppendedData: true }))).bytes);
42
- restoreDate();
43
- return { appended, relocated, cost: relocated.total - appended.total };
44
- }
45
-
46
- // the element changes length between the two passes once the reservation is large enough to move
47
- // the central-directory offsets it encodes, and then the reservation comes from the first pass
48
- // while the element written into it comes from the second. These fixtures stay below that, which
49
- // is what lets the identity be asserted exactly rather than within a tolerance.
50
- for (const [label, seed, targetLength, closeTagLength] of [
51
- ["comment rung", 1, 64 * 1024, "-->".length],
52
- ["comment rung, larger", 2, 147 * 1024, "-->".length]
53
- ]) {
54
- const { appended, relocated, cost } = await build(seed, targetLength, {});
55
- check(`${label}: the element keeps its length`, relocated.element, appended.element);
56
- check(`${label}: relocates`, relocated.relocated, true);
57
- check(`${label}: cost`, cost,
58
- reservationSize(appended.element) - appended.element - closeTagLength - END_TAGS_LENGTH);
59
- check(`${label}: cost is positive`, cost > 0, true);
60
- }
61
-
62
- // the rung sets the constant, because it is the closing tag the relocated placement stops emitting
63
- for (const [label, closeTag] of [
64
- ["script rung", "</script>"],
65
- ["svg CDATA rung", "]]></svg>"],
66
- ["plaintext rung", "</plaintext>"]
67
- ]) {
68
- const startTag = { "</script>": "<script type=sfz-data>", "]]></svg>": "<svg><![CDATA[", "</plaintext>": "<plaintext>" }[closeTag];
69
- const { appended, cost } = await build(3, 64 * 1024, { extractDataFromPageTags: [startTag, closeTag] });
70
- check(`${label}: cost`, cost,
71
- reservationSize(appended.element) - appended.element - closeTag.length - END_TAGS_LENGTH);
72
- check(`${label}: cost is positive`, cost > 0, true);
73
- }
74
-
75
- // with no element there is nothing to reserve, so the appended run is dropped and nothing replaces
76
- // it. This is the only case in which suppressing the run makes the file smaller.
77
- {
78
- const { appended, cost } = await build(4, 64 * 1024, { extractDataFromPage: false });
79
- check("extraction disabled: no element", appended.element, 0);
80
- check("extraction disabled: cost", cost, -("-->".length + END_TAGS_LENGTH));
81
- }
82
-
83
- // the budget triggers relocation once the element no longer fits beside the terminator and the end
84
- // tags, so the boundary is exactly maxAppendedDataLength - 3 - 14
85
- {
86
- const { appended } = await build(5, 64 * 1024, {});
87
- const fits = appended.element + "-->".length + END_TAGS_LENGTH;
88
- const atBoundary = await build(5, 64 * 1024, { maxAppendedDataLength: fits });
89
- const belowBoundary = await build(5, 64 * 1024, { maxAppendedDataLength: fits - 1 });
90
- check("an element that exactly fits the budget stays appended", atBoundary.appended.relocated, false);
91
- check("one byte less of budget relocates it", belowBoundary.appended.relocated, true);
92
- }
93
-
94
- Deno.exit(failed ? 1 : 0);
@@ -1,35 +0,0 @@
1
- import { makePageData, makeOptions, runProcess, freezeDate } from "./common.js";
2
-
3
- const startSeed = Number(Deno.args[0] ?? 1);
4
- const seedCount = Number(Deno.args[1] ?? 100);
5
- const contentLength = Number(Deno.args[2] ?? 2 * 1024 * 1024);
6
-
7
- const found = [];
8
- const restoreDate = freezeDate();
9
- const startTime = performance.now();
10
- try {
11
- for (let seed = startSeed; seed < startSeed + seedCount; seed++) {
12
- const options = makeOptions();
13
- const result = await runProcess(makePageData(seed, contentLength), options);
14
- if (result.fallbackTag || result.extractionDisabled) {
15
- found.push({ seed, fallbackTag: result.fallbackTag, pageSize: result.bytes.length });
16
- console.log(`seed ${seed}: TRIGGER, fell back to ${result.fallbackTag}`);
17
- }
18
- }
19
- } finally {
20
- restoreDate();
21
- }
22
- const seconds = (performance.now() - startTime) / 1000;
23
- console.log(`${found.length} trigger(s) in ${seedCount} attempts (content ${contentLength} bytes, ${seconds.toFixed(1)}s)`);
24
- if (found.length) {
25
- const path = new URL("trigger-seeds.json", import.meta.url).pathname;
26
- let existing = { contentLength, seeds: [] };
27
- try {
28
- existing = JSON.parse(Deno.readTextFileSync(path));
29
- } catch {
30
- // first run
31
- }
32
- const seeds = [...existing.seeds, ...found.filter(entry => !existing.seeds.some(known => known.seed == entry.seed))];
33
- Deno.writeTextFileSync(path, JSON.stringify({ contentLength, seeds }, null, "\t") + "\n");
34
- console.log(`saved to trigger-seeds.json (${seeds.length} total)`);
35
- }
@@ -1,10 +0,0 @@
1
- import { makePageData, makeOptions, runProcess } from "./common.js";
2
-
3
- const options = makeOptions();
4
- const pageData = makePageData(1, 256 * 1024);
5
- const result = await runProcess(pageData, options);
6
- console.log("page size:", result.bytes.length);
7
- console.log("fallback tag:", result.fallbackTag);
8
- console.log("extraction disabled:", result.extractionDisabled);
9
- const head = new TextDecoder().decode(result.bytes.subarray(0, 200));
10
- console.log("head:", JSON.stringify(head));
@@ -1,95 +0,0 @@
1
- import { makePageData, makeOptions, runProcess, mulberry32 } from "./common.js";
2
-
3
- function storedResource(name, literals) {
4
- const rand = mulberry32(0xbeef);
5
- const bytes = new Uint8Array(Math.max(4096, 256 + literals.length * 512)).map(() => (rand() * 256) | 0);
6
- const encoder = new TextEncoder();
7
- let offset = 256;
8
- for (const literal of literals) {
9
- bytes.set(encoder.encode(literal), offset);
10
- offset += 512;
11
- }
12
- return { name, extension: ".jpg", content: bytes };
13
- }
14
-
15
- let failed = false;
16
-
17
- function check(label, actual, expected) {
18
- const ok = actual === expected;
19
- console.log(`${ok ? "PASS" : "FAIL"} ${label}: ${actual}${ok ? "" : " (expected " + expected + ")"}`);
20
- failed ||= !ok;
21
- }
22
-
23
- {
24
- const options = makeOptions();
25
- const pageData = makePageData(1, 64 * 1024);
26
- pageData.resources.images.push(storedResource("photo.jpg", ["-->"]));
27
- const result = await runProcess(pageData, options);
28
- check("stored '-->' falls back to", result.fallbackTag, "<script type=sfz-data>");
29
- check("stored '-->' keeps extraction", result.extractionDisabled, false);
30
- }
31
-
32
- {
33
- const options = makeOptions();
34
- const pageData = makePageData(2, 64 * 1024);
35
- pageData.resources.images.push(storedResource("photo.jpg",
36
- ["-->", "</script>", "</style>", "</noframes>", "</noembed>", "</iframe>", "</xmp>", "]]>"]));
37
- const result = await runProcess(pageData, options);
38
- check("all closers exhaust to", result.fallbackTag, "<plaintext>");
39
- check("all closers keep extraction", result.extractionDisabled, false);
40
- }
41
-
42
- {
43
- const options = makeOptions();
44
- const pageData = makePageData(3, 64 * 1024);
45
- pageData.resources.images.push(storedResource("photo.jpg", ["</xmp>"]));
46
- const result = await runProcess(pageData, options);
47
- check("stored '</xmp>' alone stays on comment path", result.fallbackTag, null);
48
- }
49
-
50
- // the rung patterns are matched over the archive BYTES rather than a decoded string, so these
51
- // four pin the parts of that match a byte scan is easy to get wrong: closers are matched
52
- // case-insensitively, a closer only counts when a tag-name terminator follows it, and the
53
- // comment closer is '-->' or '--!>' and nothing else in between
54
-
55
- {
56
- const options = makeOptions();
57
- const pageData = makePageData(4, 64 * 1024);
58
- pageData.resources.images.push(storedResource("photo.jpg", ["-->", "</SCRIPT>"]));
59
- const result = await runProcess(pageData, options);
60
- check("an upper-case closer counts, so '</SCRIPT>' escalates to", result.fallbackTag, "<style type=sfz-data>");
61
- }
62
-
63
- {
64
- const options = makeOptions();
65
- const pageData = makePageData(5, 64 * 1024);
66
- pageData.resources.images.push(storedResource("photo.jpg", ["-->", "</script\tsrc"]));
67
- const result = await runProcess(pageData, options);
68
- check("a tab terminates a closer, so '</script\\t' escalates to", result.fallbackTag, "<style type=sfz-data>");
69
- }
70
-
71
- {
72
- const options = makeOptions();
73
- const pageData = makePageData(6, 64 * 1024);
74
- pageData.resources.images.push(storedResource("photo.jpg", ["-->", "</scriptx"]));
75
- const result = await runProcess(pageData, options);
76
- check("'</scriptx' is not a closer, so it stops at", result.fallbackTag, "<script type=sfz-data>");
77
- }
78
-
79
- {
80
- const options = makeOptions();
81
- const pageData = makePageData(7, 64 * 1024);
82
- pageData.resources.images.push(storedResource("photo.jpg", ["--!>"]));
83
- const result = await runProcess(pageData, options);
84
- check("'--!>' closes a comment, so it falls back to", result.fallbackTag, "<script type=sfz-data>");
85
- }
86
-
87
- {
88
- const options = makeOptions();
89
- const pageData = makePageData(8, 64 * 1024);
90
- pageData.resources.images.push(storedResource("photo.jpg", ["--?>"]));
91
- const result = await runProcess(pageData, options);
92
- check("'--?>' does not close a comment", result.fallbackTag, null);
93
- }
94
-
95
- Deno.exit(failed ? 1 : 0);
@@ -1,120 +0,0 @@
1
- {
2
- "contentLength": 2097152,
3
- "seeds": [
4
- {
5
- "seed": 68,
6
- "fallbackTag": "<script type=sfz-data>",
7
- "pageSize": 723106
8
- },
9
- {
10
- "seed": 154,
11
- "fallbackTag": "<script type=sfz-data>",
12
- "pageSize": 724555
13
- },
14
- {
15
- "seed": 176,
16
- "fallbackTag": "<script type=sfz-data>",
17
- "pageSize": 724734
18
- },
19
- {
20
- "seed": 184,
21
- "fallbackTag": "<script type=sfz-data>",
22
- "pageSize": 724449
23
- },
24
- {
25
- "seed": 212,
26
- "fallbackTag": "<script type=sfz-data>",
27
- "pageSize": 723683
28
- },
29
- {
30
- "seed": 223,
31
- "fallbackTag": "<script type=sfz-data>",
32
- "pageSize": 724277
33
- },
34
- {
35
- "seed": 224,
36
- "fallbackTag": "<script type=sfz-data>",
37
- "pageSize": 724265
38
- },
39
- {
40
- "seed": 232,
41
- "fallbackTag": "<script type=sfz-data>",
42
- "pageSize": 723856
43
- },
44
- {
45
- "seed": 256,
46
- "fallbackTag": "<script type=sfz-data>",
47
- "pageSize": 722938
48
- },
49
- {
50
- "seed": 282,
51
- "fallbackTag": "<script type=sfz-data>",
52
- "pageSize": 724570
53
- },
54
- {
55
- "seed": 288,
56
- "fallbackTag": "<script type=sfz-data>",
57
- "pageSize": 725625
58
- },
59
- {
60
- "seed": 343,
61
- "fallbackTag": "<script type=sfz-data>",
62
- "pageSize": 724582
63
- },
64
- {
65
- "seed": 351,
66
- "fallbackTag": "<script type=sfz-data>",
67
- "pageSize": 724787
68
- },
69
- {
70
- "seed": 372,
71
- "fallbackTag": "<script type=sfz-data>",
72
- "pageSize": 723867
73
- },
74
- {
75
- "seed": 379,
76
- "fallbackTag": "<script type=sfz-data>",
77
- "pageSize": 725136
78
- },
79
- {
80
- "seed": 383,
81
- "fallbackTag": "<script type=sfz-data>",
82
- "pageSize": 725078
83
- },
84
- {
85
- "seed": 404,
86
- "fallbackTag": "<script type=sfz-data>",
87
- "pageSize": 723429
88
- },
89
- {
90
- "seed": 426,
91
- "fallbackTag": "<script type=sfz-data>",
92
- "pageSize": 724508
93
- },
94
- {
95
- "seed": 441,
96
- "fallbackTag": "<script type=sfz-data>",
97
- "pageSize": 724645
98
- },
99
- {
100
- "seed": 445,
101
- "fallbackTag": "<script type=sfz-data>",
102
- "pageSize": 724563
103
- },
104
- {
105
- "seed": 475,
106
- "fallbackTag": "<script type=sfz-data>",
107
- "pageSize": 723429
108
- },
109
- {
110
- "seed": 495,
111
- "fallbackTag": "<script type=sfz-data>",
112
- "pageSize": 723247
113
- },
114
- {
115
- "seed": 500,
116
- "fallbackTag": "<script type=sfz-data>",
117
- "pageSize": 724039
118
- }
119
- ]
120
- }
@@ -1,77 +0,0 @@
1
- // §5.7 states that the page.pdf central-record injection applies its accounting to the zip64
2
- // end of central directory record when one is present, and claimed the combination "has been
3
- // verified on a forced-zip64 build". No such build existed here: the writer has no zip64 lever
4
- // and §8.3 described the specimen only in prose, so nothing checked the branch. It runs only past
5
- // 4 GiB or 65535 entries, which a saved page never reaches, so a defect in it would be invisible.
6
- //
7
- // zipWriter.options is a live reference to the object the ZipWriter was constructed with, so the
8
- // writeEntries callback can force zip64 on without any production change.
9
- import "./dom-stub.js";
10
- import { createArchive } from "../../processors/compression/compression.js";
11
- import { TextReader, ZipReader, BlobReader } from "../../vendor/zip/zip.js";
12
- import { FIXED_DATE, blobBytes } from "./common.js";
13
-
14
- const ZIP64_EOCD_SIGNATURE = 0x06064b50;
15
- const ZIP64_LOCATOR_SIGNATURE = 0x07064b50;
16
- const EOCD_SIGNATURE = 0x06054b50;
17
-
18
- let failed = false;
19
-
20
- function check(label, actual, expected) {
21
- const ok = actual === expected;
22
- console.log(`${ok ? "PASS" : "FAIL"} ${label}: ${actual}${ok ? "" : " (expected " + expected + ")"}`);
23
- failed ||= !ok;
24
- }
25
-
26
- const PDF = new TextEncoder().encode("%PDF-1.4\n1 0 obj\n<< /X (zip64) >>\nendobj\ntrailer\n<<>>\n%%EOF\n");
27
- const pageData = { doctype: "<!DOCTYPE html>", content: "<html><body>zip64</body></html>", title: "zip64" };
28
- const archiveOptions = { url: "https://example.com/", selfExtractingArchive: true, embeddedPdf: PDF };
29
-
30
- const blob = await createArchive(pageData, archiveOptions, "/* zip script stub */", async zipWriter => {
31
- zipWriter.options.zip64 = true;
32
- await zipWriter.add("index.html", new TextReader("<html><body>zip64</body></html>"));
33
- await zipWriter.add("manifest.json", new TextReader("{}"));
34
- }, FIXED_DATE);
35
- const bytes = await blobBytes(blob);
36
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
37
-
38
- // locate the three records from the end, the way a reader does
39
- let eocd = -1;
40
- for (let index = bytes.length - 22; index >= 0 && eocd == -1; index--) {
41
- if (view.getUint32(index, true) == EOCD_SIGNATURE) {
42
- eocd = index;
43
- }
44
- }
45
- check("the archive ends with an EOCD record", eocd != -1, true);
46
- const locator = eocd - 20;
47
- check("a zip64 locator precedes the EOCD", view.getUint32(locator, true), ZIP64_LOCATOR_SIGNATURE);
48
- const zip64 = Number(view.getBigUint64(locator + 8, true));
49
- check("the locator points at the zip64 record", view.getUint32(zip64, true), ZIP64_EOCD_SIGNATURE);
50
-
51
- // §5.7: the EOCD's saturated fields stay at their sentinels, and the writer saturates all of
52
- // them once zip64 is emitted rather than only the one that overflowed
53
- check("the EOCD entry count is a sentinel", view.getUint16(eocd + 8, true), 0xFFFF);
54
- check("the EOCD total count is a sentinel", view.getUint16(eocd + 10, true), 0xFFFF);
55
- check("the EOCD directory size is a sentinel", view.getUint32(eocd + 12, true), 0xFFFFFFFF);
56
- check("the EOCD directory offset is a sentinel", view.getUint32(eocd + 16, true), 0xFFFFFFFF);
57
-
58
- // the injection's accounting lands in the zip64 record: three entries, not the writer's two
59
- check("the zip64 record counts the injected record on this disk", Number(view.getBigUint64(zip64 + 24, true)), 3);
60
- check("the zip64 record counts it in the total", Number(view.getBigUint64(zip64 + 32, true)), 3);
61
-
62
- // the directory begins at the injected record, which sits ahead of the writer's own directory
63
- const directoryOffset = Number(view.getBigUint64(zip64 + 48, true));
64
- const directorySize = Number(view.getBigUint64(zip64 + 40, true));
65
- check("the zip64 directory offset points at the injected record",
66
- view.getUint32(directoryOffset, true), 0x02014b50);
67
- check("the first record in the directory is page.pdf",
68
- new TextDecoder().decode(bytes.subarray(directoryOffset + 46, directoryOffset + 54)), "page.pdf");
69
- check("the directory size covers every record", directoryOffset + directorySize, zip64);
70
-
71
- // and the result is still an archive every reader can read
72
- const reader = new ZipReader(new BlobReader(new Blob([bytes])));
73
- const entries = await reader.getEntries();
74
- await reader.close();
75
- check("a reader lists all three entries", entries.map(entry => entry.filename).join(","), "page.pdf,index.html,manifest.json");
76
-
77
- Deno.exit(failed ? 1 : 0);
@@ -1,56 +0,0 @@
1
- # Rebuilding `vendor/zip`
2
-
3
- The three files in [`../vendor/zip`](../vendor/zip) are custom builds of
4
- [@zip.js/zip.js](https://www.npmjs.com/package/@zip.js/zip.js) with
5
- [fflate](https://www.npmjs.com/package/fflate) as `DecompressionStream`
6
- fallback:
7
-
8
- - `zip.min.js` — extraction bundle used by self-extracting pages
9
- (`ZipReader`, `HttpRangeReader`, blob/text/data-URI readers and writers,
10
- no web workers, fflate fallback for browsers without
11
- `DecompressionStream`)
12
- - `zip.js` — creation bundle used when saving pages (full zip-core plus
13
- `deflateRaw`/`inflateRaw`)
14
- - `z-worker.js` — web worker script for the creation bundle
15
-
16
- This directory rebuilds them byte-for-byte from their sources. All
17
- dependencies are pinned to exact versions by `package.json` and
18
- `package-lock.json`, and the build embeds no timestamps, so the output is
19
- reproducible on any platform with a recent Node.js version:
20
-
21
- ```sh
22
- cd zip-build
23
- npm ci
24
- npm run build
25
- git diff --exit-code ../vendor/zip
26
- ```
27
-
28
- An empty diff means the checked-in files match the sources exactly. The
29
- GitHub Actions workflow `.github/workflows/vendor.yml` runs the same steps
30
- on every push.
31
-
32
- ## Files
33
-
34
- - `lib/zip.js`, `lib/zip-vendor.js`, `lib/zip-vendor-worker.js` — bundle
35
- entry points; they import the zip.js sources from the pinned npm package
36
- - `lib/fflate.js` — vendored [fflate](https://github.com/101arrowz/fflate)
37
- 0.8.3 (MIT)
38
- - `lib/fflate-streams.js` — `CompressionStream`/`DecompressionStream`
39
- adapter over fflate
40
- - `reserved-property-names.json` — property names protected from terser
41
- property mangling; generated by `reserved-property-names.js` in the
42
- [zip.js repository](https://github.com/gildas-lormeau/zip.js) at the
43
- pinned version (the script is not part of the npm package)
44
-
45
- ## Updating zip.js
46
-
47
- 1. Bump `@zip.js/zip.js` in `package.json` and run `npm install`.
48
- 2. Regenerate `reserved-property-names.json` from a checkout of the zip.js
49
- repository at the matching tag, with its dev dependencies installed:
50
-
51
- ```sh
52
- node --input-type=module -e "import { getReservedPropertyNames } from './reserved-property-names.js'; console.log(JSON.stringify([...getReservedPropertyNames()], null, '\t'))" > path/to/single-file-core/zip-build/reserved-property-names.json
53
- ```
54
-
55
- 3. Run `npm run build` and commit the regenerated files in `../vendor/zip`
56
- together with the changes in this directory.
@@ -1,47 +0,0 @@
1
- /* global TransformStream */
2
-
3
- import { Deflate, Inflate } from "./fflate.js";
4
-
5
- const FORMAT_DEFLATE_RAW = "deflate-raw";
6
-
7
- class FflateStream extends TransformStream {
8
- constructor(codec) {
9
- super({
10
- start(controller) {
11
- codec.ondata = chunk => {
12
- if (chunk.length) {
13
- controller.enqueue(chunk);
14
- }
15
- };
16
- },
17
- transform(chunk) {
18
- codec.push(chunk);
19
- },
20
- flush() {
21
- codec.push(new Uint8Array(0), true);
22
- }
23
- });
24
- }
25
- }
26
-
27
- class CompressionStreamFallback extends FflateStream {
28
- constructor(format, { level } = {}) {
29
- checkFormat(format);
30
- super(new Deflate(level === undefined ? {} : { level }));
31
- }
32
- }
33
-
34
- class DecompressionStreamFallback extends FflateStream {
35
- constructor(format) {
36
- checkFormat(format);
37
- super(new Inflate());
38
- }
39
- }
40
-
41
- function checkFormat(format) {
42
- if (format != FORMAT_DEFLATE_RAW) {
43
- throw new TypeError("Unsupported compression format: " + format);
44
- }
45
- }
46
-
47
- export { CompressionStreamFallback, DecompressionStreamFallback };