@uniweb/build 1.8.0 → 1.8.1
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.
- package/package.json +6 -6
- package/src/uwx/zip.js +39 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/build",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "Build tooling for the Uniweb Component Web Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
"js-yaml": "^4.1.0",
|
|
57
57
|
"sharp": "^0.35.3",
|
|
58
58
|
"yaml": "^2.5.0",
|
|
59
|
-
"@uniweb/content-writer": "~1.3.0",
|
|
60
59
|
"@uniweb/schemas": "~1.6.5",
|
|
61
|
-
"@uniweb/content-reader": "~1.5.4",
|
|
62
|
-
"@uniweb/projections": "~1.1.6",
|
|
63
60
|
"@uniweb/semantic-parser": "~1.12.2",
|
|
61
|
+
"@uniweb/projections": "~1.1.6",
|
|
62
|
+
"@uniweb/content-writer": "~1.3.0",
|
|
63
|
+
"@uniweb/content-reader": "~1.5.4",
|
|
64
64
|
"@uniweb/theming": "~1.2.1"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
"@tailwindcss/vite": "^4.0.0",
|
|
74
74
|
"@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
|
|
75
75
|
"vite-plugin-svgr": "^4.0.0",
|
|
76
|
-
"@uniweb/
|
|
77
|
-
"@uniweb/
|
|
76
|
+
"@uniweb/core": "~1.4.0",
|
|
77
|
+
"@uniweb/runtime": "~1.1.15"
|
|
78
78
|
},
|
|
79
79
|
"peerDependenciesMeta": {
|
|
80
80
|
"vite": {
|
package/src/uwx/zip.js
CHANGED
|
@@ -1,29 +1,40 @@
|
|
|
1
1
|
// Minimal, zero-dependency ZIP writer/reader for .uwx containers.
|
|
2
2
|
//
|
|
3
|
-
// DESIGN DECISION: our WRITER (`createZip`)
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
3
|
+
// DESIGN DECISION: our WRITER (`createZip`) Deflates (compression method 8)
|
|
4
|
+
// every entry that Deflate makes smaller, and Stores (method 0) the rest — an
|
|
5
|
+
// empty or tiny one. A .uwx travels as a request body, and a backend measures
|
|
6
|
+
// a request by its size AS SENT, so the container's own compression is what
|
|
7
|
+
// carries a large site: a 3.6 MB page tree Deflates to about 0.5 MB. It is the
|
|
8
|
+
// container's job and not the transport's — the backend reads the ZIP and
|
|
9
|
+
// decodes no HTTP `Content-Encoding` (backend, 2026-10-23). ⛔ Until 2026-10-23
|
|
10
|
+
// this writer emitted Stored only ("compression is an optimization, not part of
|
|
11
|
+
// the contract"), and a documentation site's push was refused `413` for its
|
|
12
|
+
// size. The READER (`readZip`) inflates both methods, mixed freely in one
|
|
13
|
+
// archive — the backend's pull `.uwx` Deflates its entities and Stores its
|
|
14
|
+
// `manifest.json`, and the framework must read what the backend produces.
|
|
10
15
|
//
|
|
11
16
|
// No Zip64: per-record JSON files are far below 4 GiB and entry counts far
|
|
12
17
|
// below 65535. The format is otherwise the classic APPNOTE layout, all
|
|
13
18
|
// multi-byte fields little-endian.
|
|
14
19
|
//
|
|
15
|
-
// Determinism: mod time/date are fixed to 0
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
20
|
+
// Determinism: mod time/date are fixed to 0 and Deflate runs at one fixed
|
|
21
|
+
// level, so a given input gives the same bytes under the same zlib. The .uwx
|
|
22
|
+
// content key is `manifest.package_sha256` (computed over the manifest, NOT the
|
|
23
|
+
// ZIP bytes), so ZIP framing — compression included — never participates in
|
|
24
|
+
// dedupe.
|
|
19
25
|
|
|
20
26
|
import { crc32 } from './crc32.js'
|
|
21
|
-
import { inflateRawSync } from 'node:zlib'
|
|
27
|
+
import { deflateRawSync, inflateRawSync } from 'node:zlib'
|
|
22
28
|
|
|
23
29
|
const LOCAL_SIG = 0x04034b50
|
|
24
30
|
const CENTRAL_SIG = 0x02014b50
|
|
25
31
|
const EOCD_SIG = 0x06054b50
|
|
26
|
-
const VERSION = 20 // 2.0 — the minimum that supports Stored with a CRC
|
|
32
|
+
const VERSION = 20 // 2.0 — the minimum that supports Deflate, and Stored with a CRC
|
|
33
|
+
const STORED = 0
|
|
34
|
+
const DEFLATE = 8
|
|
35
|
+
// Measured 2026-10-23 on a 3.6 MB page tree: level 9 → 533 KB in 38 ms, the
|
|
36
|
+
// default 6 → 540 KB in 25 ms. A push is measured compressed, so take the bytes.
|
|
37
|
+
const DEFLATE_LEVEL = 9
|
|
27
38
|
|
|
28
39
|
/**
|
|
29
40
|
* @param {{name: string, data: Buffer}[]} files
|
|
@@ -38,31 +49,34 @@ export function createZip(files) {
|
|
|
38
49
|
const nameBuf = Buffer.from(file.name, 'utf8')
|
|
39
50
|
const data = file.data
|
|
40
51
|
const crc = crc32(data)
|
|
52
|
+
const deflated = deflateRawSync(data, { level: DEFLATE_LEVEL })
|
|
53
|
+
const method = deflated.length < data.length ? DEFLATE : STORED
|
|
54
|
+
const body = method === DEFLATE ? deflated : data
|
|
41
55
|
|
|
42
56
|
const local = Buffer.alloc(30)
|
|
43
57
|
local.writeUInt32LE(LOCAL_SIG, 0)
|
|
44
58
|
local.writeUInt16LE(VERSION, 4)
|
|
45
59
|
local.writeUInt16LE(0, 6) // general purpose bit flag
|
|
46
|
-
local.writeUInt16LE(
|
|
60
|
+
local.writeUInt16LE(method, 8) // compression method: 8 = Deflate, 0 = Stored
|
|
47
61
|
local.writeUInt16LE(0, 10) // mod time (fixed)
|
|
48
62
|
local.writeUInt16LE(0, 12) // mod date (fixed)
|
|
49
|
-
local.writeUInt32LE(crc, 14)
|
|
50
|
-
local.writeUInt32LE(
|
|
51
|
-
local.writeUInt32LE(data.length, 22)
|
|
63
|
+
local.writeUInt32LE(crc, 14) // over the uncompressed data
|
|
64
|
+
local.writeUInt32LE(body.length, 18) // compressed size
|
|
65
|
+
local.writeUInt32LE(data.length, 22) // uncompressed size
|
|
52
66
|
local.writeUInt16LE(nameBuf.length, 26)
|
|
53
67
|
local.writeUInt16LE(0, 28) // extra field length
|
|
54
|
-
localParts.push(local, nameBuf,
|
|
68
|
+
localParts.push(local, nameBuf, body)
|
|
55
69
|
|
|
56
70
|
const central = Buffer.alloc(46)
|
|
57
71
|
central.writeUInt32LE(CENTRAL_SIG, 0)
|
|
58
72
|
central.writeUInt16LE(VERSION, 4) // version made by
|
|
59
73
|
central.writeUInt16LE(VERSION, 6) // version needed
|
|
60
74
|
central.writeUInt16LE(0, 8)
|
|
61
|
-
central.writeUInt16LE(
|
|
75
|
+
central.writeUInt16LE(method, 10)
|
|
62
76
|
central.writeUInt16LE(0, 12)
|
|
63
77
|
central.writeUInt16LE(0, 14)
|
|
64
78
|
central.writeUInt32LE(crc, 16)
|
|
65
|
-
central.writeUInt32LE(
|
|
79
|
+
central.writeUInt32LE(body.length, 20)
|
|
66
80
|
central.writeUInt32LE(data.length, 24)
|
|
67
81
|
central.writeUInt16LE(nameBuf.length, 28)
|
|
68
82
|
central.writeUInt16LE(0, 30) // extra
|
|
@@ -73,7 +87,7 @@ export function createZip(files) {
|
|
|
73
87
|
central.writeUInt32LE(offset, 42) // local header offset
|
|
74
88
|
centralParts.push(central, nameBuf)
|
|
75
89
|
|
|
76
|
-
offset += local.length + nameBuf.length +
|
|
90
|
+
offset += local.length + nameBuf.length + body.length
|
|
77
91
|
}
|
|
78
92
|
|
|
79
93
|
const centralDir = Buffer.concat(centralParts)
|
|
@@ -91,9 +105,10 @@ export function createZip(files) {
|
|
|
91
105
|
}
|
|
92
106
|
|
|
93
107
|
/**
|
|
94
|
-
* Reader for `.uwx` containers. Handles Stored (method 0
|
|
95
|
-
*
|
|
96
|
-
* throws. Not otherwise a general-purpose unzip (no Zip64, no
|
|
108
|
+
* Reader for `.uwx` containers. Handles Stored (method 0) and Deflate (method 8),
|
|
109
|
+
* mixed freely — both are what our writer and the backend's pull `.uwx` emit;
|
|
110
|
+
* any other method throws. Not otherwise a general-purpose unzip (no Zip64, no
|
|
111
|
+
* encryption).
|
|
97
112
|
*
|
|
98
113
|
* @param {Buffer} buf
|
|
99
114
|
* @returns {Map<string, Buffer>} name -> uncompressed data
|