@huynle/hub 0.7.1 → 0.7.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.
- package/CHANGELOG.md +70 -0
- package/Dockerfile +3 -0
- package/README.md +148 -11
- package/package.json +3 -1
- package/packages/claude-code/package.json +1 -1
- package/packages/claude-code/src/main.ts +4 -4
- package/packages/claude-code/src/wrapper.ts +3 -2
- package/packages/cli/dist/hub-cli.js +19334 -7849
- package/packages/cli/package.json +11 -0
- package/packages/cli/src/acting.ts +70 -0
- package/packages/cli/src/args.ts +91 -0
- package/packages/cli/src/bootstrap/adapters/archive-stream.ts +276 -0
- package/packages/cli/src/bootstrap/adapters/claude/README.md +63 -0
- package/packages/cli/src/bootstrap/adapters/claude/adapter.ts +250 -0
- package/packages/cli/src/bootstrap/adapters/claude/index.ts +42 -0
- package/packages/cli/src/bootstrap/adapters/claude/node.ts +99 -0
- package/packages/cli/src/bootstrap/adapters/claude/openpgp.ts +221 -0
- package/packages/cli/src/bootstrap/adapters/claude/ports.ts +44 -0
- package/packages/cli/src/bootstrap/adapters/claude/source.ts +160 -0
- package/packages/cli/src/bootstrap/adapters/claude/verify.ts +165 -0
- package/packages/cli/src/bootstrap/adapters/codex/archive.ts +103 -0
- package/packages/cli/src/bootstrap/adapters/codex/index.ts +60 -0
- package/packages/cli/src/bootstrap/adapters/codex/plan.ts +129 -0
- package/packages/cli/src/bootstrap/adapters/codex/sigstore.ts +298 -0
- package/packages/cli/src/bootstrap/adapters/codex/source.ts +102 -0
- package/packages/cli/src/bootstrap/adapters/codex/verify.ts +298 -0
- package/packages/cli/src/bootstrap/adapters/inflate.ts +433 -0
- package/packages/cli/src/bootstrap/adapters/node-services/control.ts +392 -0
- package/packages/cli/src/bootstrap/adapters/node-services/index.ts +23 -0
- package/packages/cli/src/bootstrap/adapters/node-services/launch.ts +223 -0
- package/packages/cli/src/bootstrap/adapters/node-services/plan.ts +222 -0
- package/packages/cli/src/bootstrap/adapters/node-services/render.ts +183 -0
- package/packages/cli/src/bootstrap/adapters/node-services/validate.ts +197 -0
- package/packages/cli/src/bootstrap/adapters/opencode/README.md +204 -0
- package/packages/cli/src/bootstrap/adapters/opencode/apply.ts +205 -0
- package/packages/cli/src/bootstrap/adapters/opencode/archive.ts +91 -0
- package/packages/cli/src/bootstrap/adapters/opencode/config.ts +212 -0
- package/packages/cli/src/bootstrap/adapters/opencode/index.ts +81 -0
- package/packages/cli/src/bootstrap/adapters/opencode/plan.ts +266 -0
- package/packages/cli/src/bootstrap/adapters/opencode/verify.ts +208 -0
- package/packages/cli/src/bootstrap/adapters/readiness.ts +19 -0
- package/packages/cli/src/bootstrap/ansible/index.ts +2 -0
- package/packages/cli/src/bootstrap/ansible/render.ts +279 -0
- package/packages/cli/src/bootstrap/contracts/actions.ts +470 -0
- package/packages/cli/src/bootstrap/contracts/artifact.ts +323 -0
- package/packages/cli/src/bootstrap/contracts/canonical.ts +54 -0
- package/packages/cli/src/bootstrap/contracts/credentials.ts +108 -0
- package/packages/cli/src/bootstrap/contracts/discovery.ts +215 -0
- package/packages/cli/src/bootstrap/contracts/envelope.ts +143 -0
- package/packages/cli/src/bootstrap/contracts/exec.ts +74 -0
- package/packages/cli/src/bootstrap/contracts/harness.ts +202 -0
- package/packages/cli/src/bootstrap/contracts/index.ts +28 -0
- package/packages/cli/src/bootstrap/contracts/journal.ts +198 -0
- package/packages/cli/src/bootstrap/contracts/paths.ts +91 -0
- package/packages/cli/src/bootstrap/contracts/plan.ts +155 -0
- package/packages/cli/src/bootstrap/contracts/platform.ts +62 -0
- package/packages/cli/src/bootstrap/contracts/preview.ts +190 -0
- package/packages/cli/src/bootstrap/contracts/profile.ts +105 -0
- package/packages/cli/src/bootstrap/contracts/secrets.ts +52 -0
- package/packages/cli/src/bootstrap/contracts/validate.ts +212 -0
- package/packages/cli/src/bootstrap/discovery/discover.ts +146 -0
- package/packages/cli/src/bootstrap/discovery/executables.ts +134 -0
- package/packages/cli/src/bootstrap/discovery/host.ts +96 -0
- package/packages/cli/src/bootstrap/discovery/index.ts +6 -0
- package/packages/cli/src/bootstrap/discovery/node-probe.ts +97 -0
- package/packages/cli/src/bootstrap/discovery/probe.ts +41 -0
- package/packages/cli/src/bootstrap/discovery/util.ts +13 -0
- package/packages/cli/src/bootstrap/enrollment/enroll.ts +226 -0
- package/packages/cli/src/bootstrap/enrollment/index.ts +7 -0
- package/packages/cli/src/bootstrap/enrollment/policy.ts +54 -0
- package/packages/cli/src/bootstrap/enrollment/result.ts +63 -0
- package/packages/cli/src/bootstrap/enrollment/status.ts +119 -0
- package/packages/cli/src/bootstrap/enrollment/store.ts +173 -0
- package/packages/cli/src/bootstrap/preview/index.ts +5 -0
- package/packages/cli/src/bootstrap/preview/links.ts +31 -0
- package/packages/cli/src/bootstrap/preview/plan.ts +242 -0
- package/packages/cli/src/bootstrap/preview/preview.ts +104 -0
- package/packages/cli/src/bootstrap/preview/render.ts +30 -0
- package/packages/cli/src/bootstrap/profiles/audit.ts +87 -0
- package/packages/cli/src/bootstrap/profiles/catalog.ts +49 -0
- package/packages/cli/src/bootstrap/profiles/desired.ts +96 -0
- package/packages/cli/src/bootstrap/profiles/drift.ts +286 -0
- package/packages/cli/src/bootstrap/profiles/history.ts +263 -0
- package/packages/cli/src/bootstrap/profiles/index.ts +11 -0
- package/packages/cli/src/bootstrap/profiles/upgrade.ts +276 -0
- package/packages/cli/src/bootstrap/profiles/versions.ts +50 -0
- package/packages/cli/src/bootstrap/release/README.md +115 -0
- package/packages/cli/src/bootstrap/release/assets.ts +39 -0
- package/packages/cli/src/bootstrap/release/cli.ts +332 -0
- package/packages/cli/src/bootstrap/release/harness-pins.json +94 -0
- package/packages/cli/src/bootstrap/release/manifest.ts +118 -0
- package/packages/cli/src/bootstrap/release/pins.ts +192 -0
- package/packages/cli/src/bootstrap/release/text-assets.d.ts +9 -0
- package/packages/cli/src/bootstrap/release/trust-root.json +13 -0
- package/packages/cli/src/bootstrap/release/trust-root.ts +39 -0
- package/packages/cli/src/bootstrap/release/verify.ts +88 -0
- package/packages/cli/src/bootstrap/transaction/builtin.ts +312 -0
- package/packages/cli/src/bootstrap/transaction/engine.ts +994 -0
- package/packages/cli/src/bootstrap/transaction/executor.ts +112 -0
- package/packages/cli/src/bootstrap/transaction/fs.ts +194 -0
- package/packages/cli/src/bootstrap/transaction/index.ts +12 -0
- package/packages/cli/src/bootstrap/transaction/json-merge.ts +403 -0
- package/packages/cli/src/bootstrap/transaction/lock.ts +132 -0
- package/packages/cli/src/bootstrap/transaction/resolve.ts +48 -0
- package/packages/cli/src/bootstrap/transaction/result.ts +60 -0
- package/packages/cli/src/bootstrap/transaction/state.ts +88 -0
- package/packages/cli/src/bootstrap/transaction/store.ts +189 -0
- package/packages/cli/src/bootstrap/transaction/stream.ts +254 -0
- package/packages/cli/src/cli.ts +814 -0
- package/packages/cli/src/credentials.ts +75 -0
- package/packages/cli/src/decisions.ts +106 -0
- package/packages/cli/src/format.ts +185 -0
- package/packages/cli/src/grants.ts +105 -0
- package/packages/cli/src/http.ts +86 -0
- package/packages/cli/src/launches.ts +157 -0
- package/packages/cli/src/login.ts +160 -0
- package/packages/cli/src/main.ts +63 -0
- package/packages/cli/src/mcp.ts +879 -0
- package/packages/cli/src/messaging.ts +108 -0
- package/packages/cli/src/node.ts +279 -0
- package/packages/cli/src/openshell.ts +166 -0
- package/packages/cli/src/setup/commands.ts +809 -0
- package/packages/cli/src/setup/contributors.ts +171 -0
- package/packages/cli/src/setup/dispatch.ts +74 -0
- package/packages/cli/src/setup/launch-driver.ts +109 -0
- package/packages/cli/src/setup/runtime.ts +456 -0
- package/packages/cli/src/setup/spoke-probe.ts +120 -0
- package/packages/cli/src/setup/state.ts +244 -0
- package/packages/cli/src/setup/user-bin.ts +39 -0
- package/packages/cli/src/spoke-services.ts +109 -0
- package/packages/cli/src/watch.ts +28 -0
- package/packages/codex/package.json +1 -1
- package/packages/codex/src/main.ts +3 -2
- package/packages/codex/src/spoke.ts +4 -1
- package/packages/codex/src/wrapper.ts +7 -3
- package/packages/node/package.json +1 -1
- package/packages/node/src/harnesses.ts +39 -4
- package/packages/node/src/run.ts +1 -1
- package/packages/preset/package.json +1 -1
- package/packages/protocol/package.json +1 -1
- package/packages/runtime-openshell/package.json +1 -1
- package/packages/runtime-sdk/package.json +1 -1
- package/packages/sdk/package.json +1 -1
- package/packages/sdk/src/client.ts +55 -4
- package/packages/sdk/src/errors.ts +1 -0
- package/packages/sdk/src/sse.ts +4 -1
- package/packages/sdk/src/types.ts +59 -0
- package/packages/spoke-sdk/package.json +1 -1
- package/packages/spoke-sdk/src/enroll.ts +26 -4
- package/src/central/agent/app-tokens.ts +27 -17
- package/src/central/agent/archive-inactive.ts +66 -0
- package/src/central/agent/directory-routes.ts +155 -12
- package/src/central/agent/directory.ts +1 -1
- package/src/central/agent/errors.ts +2 -0
- package/src/central/agent/event-cursor.ts +49 -0
- package/src/central/agent/event-writers.ts +181 -0
- package/src/central/agent/events-routes.ts +148 -0
- package/src/central/agent/events.ts +277 -0
- package/src/central/agent/names.ts +1 -1
- package/src/central/agent/read-routes.ts +32 -36
- package/src/central/agent/routes.ts +14 -11
- package/src/central/agent/watch.ts +65 -174
- package/src/central/agent/webhook-routes.ts +10 -2
- package/src/central/agent/webhooks.ts +49 -5
- package/src/central/auth.ts +127 -1
- package/src/central/backend/index.ts +3 -0
- package/src/central/backend/types.ts +2 -0
- package/src/central/http.ts +115 -32
- package/src/central/mcp-http.ts +116 -0
- package/src/central/store.ts +53 -0
- package/src/config.ts +70 -7
- package/src/index.ts +90 -91
- package/src/spoke/launchers.ts +109 -3
- package/web/dist/apple-touch-icon.png +0 -0
- package/web/dist/assets/index-DWnMhXk9.js +20 -0
- package/web/dist/assets/index-DmUXEnw9.css +1 -0
- package/web/dist/favicon.ico +0 -0
- package/web/dist/icon-192.png +0 -0
- package/web/dist/icon-512.png +0 -0
- package/web/dist/icon-maskable-512.png +0 -0
- package/web/dist/icon.svg +5 -5
- package/web/dist/index.html +10 -4
- package/web/dist/manifest.webmanifest +2 -2
- package/web/dist/assets/index-D9zMbxSg.css +0 -1
- package/web/dist/assets/index-db1z4duA.js +0 -20
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
// hub's own streaming DEFLATE decoder (RFC 1951), gzip reader (RFC 1952) and CRC-32, for the archive readers
|
|
2
|
+
// (archive-stream.ts). Why not node:zlib: its one-shot calls hold the whole output (the OOM this replaces), and with
|
|
3
|
+
// the real OpenCode 2.2.0 darwin zip Bun 1.3.5's zlib *streams* stalled for good (no output, no error) in about half
|
|
4
|
+
// the runs, even when the pump yielded to the event loop after every chunk; Node does not stall. A decoder of our own
|
|
5
|
+
// is deterministic on every runtime.
|
|
6
|
+
//
|
|
7
|
+
// It reads the compressed bytes synchronously from a ByteReader by offset (the staged download), 64 KiB at a time,
|
|
8
|
+
// and keeps only the 32 KiB window plus one output chunk: output comes in chunks of at most OUTPUT_BYTES (64 KiB),
|
|
9
|
+
// whatever the compression ratio, so a bomb never allocates more than that at once (the readers bound the total).
|
|
10
|
+
// Small chunks also keep the garbage young: with 1 MiB chunks the collector lagged by ~50 MB. Everything it
|
|
11
|
+
// refuses is an InflateError: a reserved block type, a stored length that does not match its complement, an
|
|
12
|
+
// over-subscribed or incomplete code, a distance before the start of the output, an invalid symbol, a truncated
|
|
13
|
+
// stream, a gzip member whose CRC-32 or size does not match its trailer.
|
|
14
|
+
import type { ByteReader } from "../transaction/stream.ts"
|
|
15
|
+
|
|
16
|
+
export class InflateError extends Error {
|
|
17
|
+
constructor(message: string) {
|
|
18
|
+
super(message)
|
|
19
|
+
this.name = "InflateError"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ---------- CRC-32 (IEEE 802.3, reflected, as gzip and zip use it), slice-by-4 ----------
|
|
24
|
+
|
|
25
|
+
const CRC = (() => {
|
|
26
|
+
const t = new Uint32Array(4 * 256)
|
|
27
|
+
for (let n = 0; n < 256; n++) {
|
|
28
|
+
let c = n
|
|
29
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1
|
|
30
|
+
t[n] = c >>> 0
|
|
31
|
+
}
|
|
32
|
+
for (let n = 0; n < 256; n++) for (let k = 1; k < 4; k++) t[k * 256 + n] = (t[(k - 1) * 256 + n]! >>> 8) ^ t[t[(k - 1) * 256 + n]! & 0xff]!
|
|
33
|
+
return t
|
|
34
|
+
})()
|
|
35
|
+
|
|
36
|
+
/** The CRC-32 of `data` continuing from `crc` (0 to start). */
|
|
37
|
+
export function crc32(crc: number, data: Uint8Array): number {
|
|
38
|
+
let c = ~crc >>> 0
|
|
39
|
+
let i = 0
|
|
40
|
+
const n = data.byteLength
|
|
41
|
+
for (; i + 4 <= n; i += 4) {
|
|
42
|
+
c ^= data[i]! | (data[i + 1]! << 8) | (data[i + 2]! << 16) | (data[i + 3]! << 24)
|
|
43
|
+
c = CRC[768 + (c & 0xff)]! ^ CRC[512 + ((c >>> 8) & 0xff)]! ^ CRC[256 + ((c >>> 16) & 0xff)]! ^ CRC[c >>> 24]!
|
|
44
|
+
}
|
|
45
|
+
for (; i < n; i++) c = CRC[(c ^ data[i]!) & 0xff]! ^ (c >>> 8)
|
|
46
|
+
return ~c >>> 0
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ---------- DEFLATE ----------
|
|
50
|
+
|
|
51
|
+
const WINDOW = 32 * 1024
|
|
52
|
+
/** The largest output chunk. */
|
|
53
|
+
export const OUTPUT_BYTES = 64 * 1024
|
|
54
|
+
const MAX_MATCH = 258
|
|
55
|
+
const INPUT_BYTES = 64 * 1024
|
|
56
|
+
const LBASE = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258]
|
|
57
|
+
const LEXT = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0]
|
|
58
|
+
const DBASE = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577]
|
|
59
|
+
const DEXT = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13]
|
|
60
|
+
const CLEN_ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]
|
|
61
|
+
|
|
62
|
+
/** A decoding table: entries (symbol << 4 | code length) indexed by the next `bits` input bits (LSB first). */
|
|
63
|
+
interface Table {
|
|
64
|
+
readonly entries: Uint16Array
|
|
65
|
+
readonly bits: number
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The canonical Huffman table for `lengths`. An over-subscribed set is refused; an incomplete one too, except (as
|
|
70
|
+
* zlib) a literal/length or distance code of at most one bit (a single code, or none: decoding it then fails).
|
|
71
|
+
*/
|
|
72
|
+
function table(lengths: ArrayLike<number>, allowIncomplete: boolean): Table {
|
|
73
|
+
const count = new Uint16Array(16)
|
|
74
|
+
let max = 0
|
|
75
|
+
for (let s = 0; s < lengths.length; s++) {
|
|
76
|
+
const l = lengths[s]!
|
|
77
|
+
count[l]!++
|
|
78
|
+
if (l > max) max = l
|
|
79
|
+
}
|
|
80
|
+
count[0] = 0
|
|
81
|
+
let left = 1
|
|
82
|
+
for (let l = 1; l <= 15; l++) {
|
|
83
|
+
left = (left << 1) - count[l]!
|
|
84
|
+
if (left < 0) throw new InflateError("an over-subscribed Huffman code")
|
|
85
|
+
}
|
|
86
|
+
if (left > 0 && !(allowIncomplete && max <= 1)) throw new InflateError("an incomplete Huffman code")
|
|
87
|
+
const bits = Math.max(1, max)
|
|
88
|
+
const entries = new Uint16Array(1 << bits)
|
|
89
|
+
const next = new Uint16Array(16)
|
|
90
|
+
for (let l = 1, code = 0; l <= 15; l++) {
|
|
91
|
+
code = (code + count[l - 1]!) << 1
|
|
92
|
+
next[l] = code
|
|
93
|
+
}
|
|
94
|
+
for (let s = 0; s < lengths.length; s++) {
|
|
95
|
+
const l = lengths[s]!
|
|
96
|
+
if (l === 0) continue
|
|
97
|
+
let code = next[l]!++
|
|
98
|
+
let rev = 0
|
|
99
|
+
for (let k = 0; k < l; k++) {
|
|
100
|
+
rev = (rev << 1) | (code & 1)
|
|
101
|
+
code >>>= 1
|
|
102
|
+
}
|
|
103
|
+
const entry = (s << 4) | l
|
|
104
|
+
for (let j = rev; j < entries.length; j += 1 << l) entries[j] = entry
|
|
105
|
+
}
|
|
106
|
+
return { entries, bits }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let fixed: { lit: Table; dist: Table } | undefined
|
|
110
|
+
function fixedTables() {
|
|
111
|
+
if (fixed) return fixed
|
|
112
|
+
const lit = new Uint8Array(288)
|
|
113
|
+
lit.fill(8, 0, 144)
|
|
114
|
+
lit.fill(9, 144, 256)
|
|
115
|
+
lit.fill(7, 256, 280)
|
|
116
|
+
lit.fill(8, 280, 288)
|
|
117
|
+
// The fixed distance code has 30 usable symbols of 32; 30 and 31 never occur in valid data.
|
|
118
|
+
fixed = { lit: table(lit, false), dist: table(new Uint8Array(32).fill(5), false) }
|
|
119
|
+
return fixed
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** LSB-first bits of `reader` from `start` to `end`, refilled synchronously 64 KiB at a time. */
|
|
123
|
+
class Bits {
|
|
124
|
+
/** The current input slice and the next byte in it (the block decoder reads them directly). */
|
|
125
|
+
buf: Uint8Array = new Uint8Array()
|
|
126
|
+
bp = 0
|
|
127
|
+
/** The absolute offset of buf[0]. */
|
|
128
|
+
private base: number
|
|
129
|
+
bitbuf = 0
|
|
130
|
+
bitcnt = 0
|
|
131
|
+
|
|
132
|
+
constructor(
|
|
133
|
+
private readonly reader: ByteReader,
|
|
134
|
+
start: number,
|
|
135
|
+
private readonly end: number,
|
|
136
|
+
) {
|
|
137
|
+
this.base = start
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** The next input byte, or -1 at the end of the input. */
|
|
141
|
+
byte(): number {
|
|
142
|
+
if (this.bp >= this.buf.byteLength) {
|
|
143
|
+
const at = this.base + this.buf.byteLength
|
|
144
|
+
if (at >= this.end) return -1
|
|
145
|
+
this.buf = this.reader.read(at, Math.min(INPUT_BYTES, this.end - at))
|
|
146
|
+
this.base = at
|
|
147
|
+
this.bp = 0
|
|
148
|
+
if (this.buf.byteLength === 0) return -1
|
|
149
|
+
}
|
|
150
|
+
return this.buf[this.bp++]!
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Fills the bit buffer to at least `n` bits (n <= 24) where the input allows; false if it ran out. */
|
|
154
|
+
fill(n: number): boolean {
|
|
155
|
+
while (this.bitcnt < n) {
|
|
156
|
+
let b: number
|
|
157
|
+
if (this.bp < this.buf.byteLength) b = this.buf[this.bp++]!
|
|
158
|
+
else if ((b = this.byte()) < 0) return false
|
|
159
|
+
this.bitbuf |= b << this.bitcnt
|
|
160
|
+
this.bitcnt += 8
|
|
161
|
+
}
|
|
162
|
+
return true
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
bits(n: number): number {
|
|
166
|
+
if (n === 0) return 0
|
|
167
|
+
if (this.bitcnt < n && !this.fill(n)) throw new InflateError("the compressed stream is truncated")
|
|
168
|
+
const v = this.bitbuf & ((1 << n) - 1)
|
|
169
|
+
this.bitbuf >>>= n
|
|
170
|
+
this.bitcnt -= n
|
|
171
|
+
return v
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Drops the bits up to the next byte boundary. */
|
|
175
|
+
align(): void {
|
|
176
|
+
const drop = this.bitcnt & 7
|
|
177
|
+
this.bitbuf >>>= drop
|
|
178
|
+
this.bitcnt -= drop
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** The absolute offset of the first byte not consumed (at a byte boundary). */
|
|
182
|
+
position(): number {
|
|
183
|
+
return this.base + this.bp - (this.bitcnt >> 3)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
decode(t: Table): number {
|
|
187
|
+
if (this.bitcnt < t.bits) this.fill(t.bits)
|
|
188
|
+
const e = t.entries[this.bitbuf & ((1 << t.bits) - 1)]!
|
|
189
|
+
const l = e & 15
|
|
190
|
+
if (l === 0) throw new InflateError("an invalid Huffman code")
|
|
191
|
+
if (l > this.bitcnt) throw new InflateError("the compressed stream is truncated")
|
|
192
|
+
this.bitbuf >>>= l
|
|
193
|
+
this.bitcnt -= l
|
|
194
|
+
return e >> 4
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* The raw DEFLATE stream at `start` (at most to `end`) of `reader`, decoded: `chunks` yields the output in pieces of
|
|
200
|
+
* at most OUTPUT_BYTES (copies the caller may keep); once it is done, `consumed()` is how many input bytes the stream
|
|
201
|
+
* took (what follows it is not read). InflateError for anything damaged.
|
|
202
|
+
*/
|
|
203
|
+
export function inflateRawChunks(reader: ByteReader, start: number, end: number): { readonly chunks: Generator<Uint8Array>; consumed(): number } {
|
|
204
|
+
const input = new Bits(reader, start, end)
|
|
205
|
+
let consumed = -1
|
|
206
|
+
function* chunks(): Generator<Uint8Array> {
|
|
207
|
+
const out = new Uint8Array(WINDOW + OUTPUT_BYTES)
|
|
208
|
+
let op = 0
|
|
209
|
+
let flushed = 0
|
|
210
|
+
let total = 0
|
|
211
|
+
const flushAt = OUTPUT_BYTES - MAX_MATCH
|
|
212
|
+
// Yields what is not yet out, then slides the window down when there is no room for another chunk.
|
|
213
|
+
const flush = function* (): Generator<Uint8Array> {
|
|
214
|
+
if (op > flushed) yield out.slice(flushed, op)
|
|
215
|
+
flushed = op
|
|
216
|
+
if (op > WINDOW) {
|
|
217
|
+
out.copyWithin(0, op - WINDOW, op)
|
|
218
|
+
op = flushed = WINDOW
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
let last = 0
|
|
222
|
+
while (!last) {
|
|
223
|
+
last = input.bits(1)
|
|
224
|
+
const type = input.bits(2)
|
|
225
|
+
if (type === 0) {
|
|
226
|
+
input.align()
|
|
227
|
+
const len = input.bits(16)
|
|
228
|
+
if ((input.bits(16) ^ 0xffff) !== len) throw new InflateError("a stored block's length does not match its complement")
|
|
229
|
+
for (let left = len; left > 0; left--) {
|
|
230
|
+
let b: number
|
|
231
|
+
if (input.bitcnt >= 8) b = input.bits(8)
|
|
232
|
+
else if ((b = input.byte()) < 0) throw new InflateError("the compressed stream is truncated")
|
|
233
|
+
out[op++] = b
|
|
234
|
+
total++
|
|
235
|
+
if (op - flushed >= flushAt) yield* flush()
|
|
236
|
+
}
|
|
237
|
+
continue
|
|
238
|
+
}
|
|
239
|
+
let lit: Table
|
|
240
|
+
let dist: Table
|
|
241
|
+
if (type === 1) ({ lit, dist } = fixedTables())
|
|
242
|
+
else if (type === 2) {
|
|
243
|
+
const hlit = input.bits(5) + 257
|
|
244
|
+
const hdist = input.bits(5) + 1
|
|
245
|
+
const hclen = input.bits(4) + 4
|
|
246
|
+
if (hlit > 286 || hdist > 30) throw new InflateError("too many length or distance codes")
|
|
247
|
+
const clens = new Uint8Array(19)
|
|
248
|
+
for (let i = 0; i < hclen; i++) clens[CLEN_ORDER[i]!] = input.bits(3)
|
|
249
|
+
const clen = table(clens, false)
|
|
250
|
+
const lengths = new Uint8Array(hlit + hdist)
|
|
251
|
+
for (let i = 0; i < lengths.length; ) {
|
|
252
|
+
const sym = input.decode(clen)
|
|
253
|
+
if (sym < 16) lengths[i++] = sym
|
|
254
|
+
else {
|
|
255
|
+
let repeat: number
|
|
256
|
+
let value = 0
|
|
257
|
+
if (sym === 16) {
|
|
258
|
+
if (i === 0) throw new InflateError("a repeated length with no previous length")
|
|
259
|
+
value = lengths[i - 1]!
|
|
260
|
+
repeat = 3 + input.bits(2)
|
|
261
|
+
} else if (sym === 17) repeat = 3 + input.bits(3)
|
|
262
|
+
else repeat = 11 + input.bits(7)
|
|
263
|
+
if (i + repeat > lengths.length) throw new InflateError("code lengths run past their count")
|
|
264
|
+
lengths.fill(value, i, i + repeat)
|
|
265
|
+
i += repeat
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (lengths[256] === 0) throw new InflateError("no end-of-block code")
|
|
269
|
+
lit = table(lengths.subarray(0, hlit), true)
|
|
270
|
+
dist = table(lengths.subarray(hlit), true)
|
|
271
|
+
} else throw new InflateError("a reserved block type")
|
|
272
|
+
// The hot loop: the bit buffer lives in locals (written back at the end of the block), the tables are
|
|
273
|
+
// indexed directly, and input is taken straight from the current slice.
|
|
274
|
+
const litE = lit.entries
|
|
275
|
+
const litB = lit.bits
|
|
276
|
+
const litM = (1 << litB) - 1
|
|
277
|
+
const distE = dist.entries
|
|
278
|
+
const distB = dist.bits
|
|
279
|
+
const distM = (1 << distB) - 1
|
|
280
|
+
let bb = input.bitbuf
|
|
281
|
+
let bc = input.bitcnt
|
|
282
|
+
const truncated = () => new InflateError("the compressed stream is truncated")
|
|
283
|
+
for (;;) {
|
|
284
|
+
// Up to litB bits (fewer only at the very end of the input).
|
|
285
|
+
while (bc < litB) {
|
|
286
|
+
let b: number
|
|
287
|
+
if (input.bp < input.buf.byteLength) b = input.buf[input.bp++]!
|
|
288
|
+
else if ((b = input.byte()) < 0) break
|
|
289
|
+
bb |= b << bc
|
|
290
|
+
bc += 8
|
|
291
|
+
}
|
|
292
|
+
const e = litE[bb & litM]!
|
|
293
|
+
const l = e & 15
|
|
294
|
+
if (l === 0) throw new InflateError("an invalid Huffman code")
|
|
295
|
+
if (l > bc) throw truncated()
|
|
296
|
+
bb >>>= l
|
|
297
|
+
bc -= l
|
|
298
|
+
const sym = e >> 4
|
|
299
|
+
if (sym < 256) {
|
|
300
|
+
out[op++] = sym
|
|
301
|
+
total++
|
|
302
|
+
if (op - flushed >= flushAt) yield* flush()
|
|
303
|
+
continue
|
|
304
|
+
}
|
|
305
|
+
if (sym === 256) break
|
|
306
|
+
const li = sym - 257
|
|
307
|
+
if (li >= 29) throw new InflateError("an invalid length symbol")
|
|
308
|
+
let len = LBASE[li]!
|
|
309
|
+
let extra = LEXT[li]!
|
|
310
|
+
if (extra) {
|
|
311
|
+
while (bc < extra) {
|
|
312
|
+
let b: number
|
|
313
|
+
if (input.bp < input.buf.byteLength) b = input.buf[input.bp++]!
|
|
314
|
+
else if ((b = input.byte()) < 0) throw truncated()
|
|
315
|
+
bb |= b << bc
|
|
316
|
+
bc += 8
|
|
317
|
+
}
|
|
318
|
+
len += bb & ((1 << extra) - 1)
|
|
319
|
+
bb >>>= extra
|
|
320
|
+
bc -= extra
|
|
321
|
+
}
|
|
322
|
+
while (bc < distB) {
|
|
323
|
+
let b: number
|
|
324
|
+
if (input.bp < input.buf.byteLength) b = input.buf[input.bp++]!
|
|
325
|
+
else if ((b = input.byte()) < 0) break
|
|
326
|
+
bb |= b << bc
|
|
327
|
+
bc += 8
|
|
328
|
+
}
|
|
329
|
+
const de = distE[bb & distM]!
|
|
330
|
+
const dl = de & 15
|
|
331
|
+
if (dl === 0) throw new InflateError("an invalid Huffman code")
|
|
332
|
+
if (dl > bc) throw truncated()
|
|
333
|
+
bb >>>= dl
|
|
334
|
+
bc -= dl
|
|
335
|
+
const di = de >> 4
|
|
336
|
+
if (di >= 30) throw new InflateError("an invalid distance symbol")
|
|
337
|
+
let d = DBASE[di]!
|
|
338
|
+
extra = DEXT[di]!
|
|
339
|
+
if (extra) {
|
|
340
|
+
while (bc < extra) {
|
|
341
|
+
let b: number
|
|
342
|
+
if (input.bp < input.buf.byteLength) b = input.buf[input.bp++]!
|
|
343
|
+
else if ((b = input.byte()) < 0) throw truncated()
|
|
344
|
+
bb |= b << bc
|
|
345
|
+
bc += 8
|
|
346
|
+
}
|
|
347
|
+
d += bb & ((1 << extra) - 1)
|
|
348
|
+
bb >>>= extra
|
|
349
|
+
bc -= extra
|
|
350
|
+
}
|
|
351
|
+
if (d > total || d > WINDOW) throw new InflateError("a distance before the start of the output")
|
|
352
|
+
if (d >= len) out.copyWithin(op, op - d, op - d + len)
|
|
353
|
+
else for (let k = 0; k < len; k++) out[op + k] = out[op + k - d]!
|
|
354
|
+
op += len
|
|
355
|
+
total += len
|
|
356
|
+
if (op - flushed >= flushAt) yield* flush()
|
|
357
|
+
}
|
|
358
|
+
input.bitbuf = bb
|
|
359
|
+
input.bitcnt = bc
|
|
360
|
+
}
|
|
361
|
+
if (op > flushed) yield out.slice(flushed, op)
|
|
362
|
+
input.align()
|
|
363
|
+
consumed = input.position() - start
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
chunks: chunks(),
|
|
367
|
+
consumed: () => {
|
|
368
|
+
if (consumed < 0) throw new Error("the stream has not been decoded yet")
|
|
369
|
+
return consumed
|
|
370
|
+
},
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ---------- gzip ----------
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* The gzip stream in `reader`, decoded, member after member (concatenated members are one stream); each member's
|
|
378
|
+
* CRC-32 and size are checked against its trailer. Bytes after the last member that do not start another member are
|
|
379
|
+
* ignored, as gzip does. InflateError for anything damaged.
|
|
380
|
+
*/
|
|
381
|
+
export function* gunzipChunks(reader: ByteReader): Generator<Uint8Array> {
|
|
382
|
+
let at = 0
|
|
383
|
+
let members = 0
|
|
384
|
+
for (;;) {
|
|
385
|
+
const head = reader.read(at, 10)
|
|
386
|
+
if (head.byteLength < 2 || head[0] !== 0x1f || head[1] !== 0x8b) {
|
|
387
|
+
if (members === 0) throw new InflateError("not a gzip stream")
|
|
388
|
+
return
|
|
389
|
+
}
|
|
390
|
+
if (head.byteLength < 10) throw new InflateError("a truncated gzip header")
|
|
391
|
+
if (head[2] !== 8) throw new InflateError("a gzip member that is not deflate")
|
|
392
|
+
const flags = head[3]!
|
|
393
|
+
if (flags & 0xe0) throw new InflateError("a gzip header with reserved flags")
|
|
394
|
+
at += 10
|
|
395
|
+
if (flags & 0x04) {
|
|
396
|
+
const xlen = reader.read(at, 2)
|
|
397
|
+
if (xlen.byteLength < 2) throw new InflateError("a truncated gzip header")
|
|
398
|
+
at += 2 + (xlen[0]! | (xlen[1]! << 8))
|
|
399
|
+
}
|
|
400
|
+
for (const flag of [0x08, 0x10]) {
|
|
401
|
+
if (!(flags & flag)) continue
|
|
402
|
+
// A zero-terminated name or comment.
|
|
403
|
+
for (;;) {
|
|
404
|
+
const part = reader.read(at, 4096)
|
|
405
|
+
if (part.byteLength === 0) throw new InflateError("a truncated gzip header")
|
|
406
|
+
const nul = part.indexOf(0)
|
|
407
|
+
if (nul >= 0) {
|
|
408
|
+
at += nul + 1
|
|
409
|
+
break
|
|
410
|
+
}
|
|
411
|
+
at += part.byteLength
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (flags & 0x02) at += 2
|
|
415
|
+
if (at > reader.size) throw new InflateError("a truncated gzip header")
|
|
416
|
+
const member = inflateRawChunks(reader, at, reader.size)
|
|
417
|
+
let crc = 0
|
|
418
|
+
let size = 0
|
|
419
|
+
for (const chunk of member.chunks) {
|
|
420
|
+
crc = crc32(crc, chunk)
|
|
421
|
+
size += chunk.byteLength
|
|
422
|
+
yield chunk
|
|
423
|
+
}
|
|
424
|
+
at += member.consumed()
|
|
425
|
+
const trailer = reader.read(at, 8)
|
|
426
|
+
if (trailer.byteLength < 8) throw new InflateError("a truncated gzip trailer")
|
|
427
|
+
const view = new DataView(trailer.buffer, trailer.byteOffset, 8)
|
|
428
|
+
if (view.getUint32(0, true) !== crc) throw new InflateError("a gzip member whose CRC-32 does not match")
|
|
429
|
+
if (view.getUint32(4, true) !== size >>> 0) throw new InflateError("a gzip member whose size does not match")
|
|
430
|
+
at += 8
|
|
431
|
+
members++
|
|
432
|
+
}
|
|
433
|
+
}
|