@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.
Files changed (185) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/Dockerfile +3 -0
  3. package/README.md +148 -11
  4. package/package.json +3 -1
  5. package/packages/claude-code/package.json +1 -1
  6. package/packages/claude-code/src/main.ts +4 -4
  7. package/packages/claude-code/src/wrapper.ts +3 -2
  8. package/packages/cli/dist/hub-cli.js +19334 -7849
  9. package/packages/cli/package.json +11 -0
  10. package/packages/cli/src/acting.ts +70 -0
  11. package/packages/cli/src/args.ts +91 -0
  12. package/packages/cli/src/bootstrap/adapters/archive-stream.ts +276 -0
  13. package/packages/cli/src/bootstrap/adapters/claude/README.md +63 -0
  14. package/packages/cli/src/bootstrap/adapters/claude/adapter.ts +250 -0
  15. package/packages/cli/src/bootstrap/adapters/claude/index.ts +42 -0
  16. package/packages/cli/src/bootstrap/adapters/claude/node.ts +99 -0
  17. package/packages/cli/src/bootstrap/adapters/claude/openpgp.ts +221 -0
  18. package/packages/cli/src/bootstrap/adapters/claude/ports.ts +44 -0
  19. package/packages/cli/src/bootstrap/adapters/claude/source.ts +160 -0
  20. package/packages/cli/src/bootstrap/adapters/claude/verify.ts +165 -0
  21. package/packages/cli/src/bootstrap/adapters/codex/archive.ts +103 -0
  22. package/packages/cli/src/bootstrap/adapters/codex/index.ts +60 -0
  23. package/packages/cli/src/bootstrap/adapters/codex/plan.ts +129 -0
  24. package/packages/cli/src/bootstrap/adapters/codex/sigstore.ts +298 -0
  25. package/packages/cli/src/bootstrap/adapters/codex/source.ts +102 -0
  26. package/packages/cli/src/bootstrap/adapters/codex/verify.ts +298 -0
  27. package/packages/cli/src/bootstrap/adapters/inflate.ts +433 -0
  28. package/packages/cli/src/bootstrap/adapters/node-services/control.ts +392 -0
  29. package/packages/cli/src/bootstrap/adapters/node-services/index.ts +23 -0
  30. package/packages/cli/src/bootstrap/adapters/node-services/launch.ts +223 -0
  31. package/packages/cli/src/bootstrap/adapters/node-services/plan.ts +222 -0
  32. package/packages/cli/src/bootstrap/adapters/node-services/render.ts +183 -0
  33. package/packages/cli/src/bootstrap/adapters/node-services/validate.ts +197 -0
  34. package/packages/cli/src/bootstrap/adapters/opencode/README.md +204 -0
  35. package/packages/cli/src/bootstrap/adapters/opencode/apply.ts +205 -0
  36. package/packages/cli/src/bootstrap/adapters/opencode/archive.ts +91 -0
  37. package/packages/cli/src/bootstrap/adapters/opencode/config.ts +212 -0
  38. package/packages/cli/src/bootstrap/adapters/opencode/index.ts +81 -0
  39. package/packages/cli/src/bootstrap/adapters/opencode/plan.ts +266 -0
  40. package/packages/cli/src/bootstrap/adapters/opencode/verify.ts +208 -0
  41. package/packages/cli/src/bootstrap/adapters/readiness.ts +19 -0
  42. package/packages/cli/src/bootstrap/ansible/index.ts +2 -0
  43. package/packages/cli/src/bootstrap/ansible/render.ts +279 -0
  44. package/packages/cli/src/bootstrap/contracts/actions.ts +470 -0
  45. package/packages/cli/src/bootstrap/contracts/artifact.ts +323 -0
  46. package/packages/cli/src/bootstrap/contracts/canonical.ts +54 -0
  47. package/packages/cli/src/bootstrap/contracts/credentials.ts +108 -0
  48. package/packages/cli/src/bootstrap/contracts/discovery.ts +215 -0
  49. package/packages/cli/src/bootstrap/contracts/envelope.ts +143 -0
  50. package/packages/cli/src/bootstrap/contracts/exec.ts +74 -0
  51. package/packages/cli/src/bootstrap/contracts/harness.ts +202 -0
  52. package/packages/cli/src/bootstrap/contracts/index.ts +28 -0
  53. package/packages/cli/src/bootstrap/contracts/journal.ts +198 -0
  54. package/packages/cli/src/bootstrap/contracts/paths.ts +91 -0
  55. package/packages/cli/src/bootstrap/contracts/plan.ts +155 -0
  56. package/packages/cli/src/bootstrap/contracts/platform.ts +62 -0
  57. package/packages/cli/src/bootstrap/contracts/preview.ts +190 -0
  58. package/packages/cli/src/bootstrap/contracts/profile.ts +105 -0
  59. package/packages/cli/src/bootstrap/contracts/secrets.ts +52 -0
  60. package/packages/cli/src/bootstrap/contracts/validate.ts +212 -0
  61. package/packages/cli/src/bootstrap/discovery/discover.ts +146 -0
  62. package/packages/cli/src/bootstrap/discovery/executables.ts +134 -0
  63. package/packages/cli/src/bootstrap/discovery/host.ts +96 -0
  64. package/packages/cli/src/bootstrap/discovery/index.ts +6 -0
  65. package/packages/cli/src/bootstrap/discovery/node-probe.ts +97 -0
  66. package/packages/cli/src/bootstrap/discovery/probe.ts +41 -0
  67. package/packages/cli/src/bootstrap/discovery/util.ts +13 -0
  68. package/packages/cli/src/bootstrap/enrollment/enroll.ts +226 -0
  69. package/packages/cli/src/bootstrap/enrollment/index.ts +7 -0
  70. package/packages/cli/src/bootstrap/enrollment/policy.ts +54 -0
  71. package/packages/cli/src/bootstrap/enrollment/result.ts +63 -0
  72. package/packages/cli/src/bootstrap/enrollment/status.ts +119 -0
  73. package/packages/cli/src/bootstrap/enrollment/store.ts +173 -0
  74. package/packages/cli/src/bootstrap/preview/index.ts +5 -0
  75. package/packages/cli/src/bootstrap/preview/links.ts +31 -0
  76. package/packages/cli/src/bootstrap/preview/plan.ts +242 -0
  77. package/packages/cli/src/bootstrap/preview/preview.ts +104 -0
  78. package/packages/cli/src/bootstrap/preview/render.ts +30 -0
  79. package/packages/cli/src/bootstrap/profiles/audit.ts +87 -0
  80. package/packages/cli/src/bootstrap/profiles/catalog.ts +49 -0
  81. package/packages/cli/src/bootstrap/profiles/desired.ts +96 -0
  82. package/packages/cli/src/bootstrap/profiles/drift.ts +286 -0
  83. package/packages/cli/src/bootstrap/profiles/history.ts +263 -0
  84. package/packages/cli/src/bootstrap/profiles/index.ts +11 -0
  85. package/packages/cli/src/bootstrap/profiles/upgrade.ts +276 -0
  86. package/packages/cli/src/bootstrap/profiles/versions.ts +50 -0
  87. package/packages/cli/src/bootstrap/release/README.md +115 -0
  88. package/packages/cli/src/bootstrap/release/assets.ts +39 -0
  89. package/packages/cli/src/bootstrap/release/cli.ts +332 -0
  90. package/packages/cli/src/bootstrap/release/harness-pins.json +94 -0
  91. package/packages/cli/src/bootstrap/release/manifest.ts +118 -0
  92. package/packages/cli/src/bootstrap/release/pins.ts +192 -0
  93. package/packages/cli/src/bootstrap/release/text-assets.d.ts +9 -0
  94. package/packages/cli/src/bootstrap/release/trust-root.json +13 -0
  95. package/packages/cli/src/bootstrap/release/trust-root.ts +39 -0
  96. package/packages/cli/src/bootstrap/release/verify.ts +88 -0
  97. package/packages/cli/src/bootstrap/transaction/builtin.ts +312 -0
  98. package/packages/cli/src/bootstrap/transaction/engine.ts +994 -0
  99. package/packages/cli/src/bootstrap/transaction/executor.ts +112 -0
  100. package/packages/cli/src/bootstrap/transaction/fs.ts +194 -0
  101. package/packages/cli/src/bootstrap/transaction/index.ts +12 -0
  102. package/packages/cli/src/bootstrap/transaction/json-merge.ts +403 -0
  103. package/packages/cli/src/bootstrap/transaction/lock.ts +132 -0
  104. package/packages/cli/src/bootstrap/transaction/resolve.ts +48 -0
  105. package/packages/cli/src/bootstrap/transaction/result.ts +60 -0
  106. package/packages/cli/src/bootstrap/transaction/state.ts +88 -0
  107. package/packages/cli/src/bootstrap/transaction/store.ts +189 -0
  108. package/packages/cli/src/bootstrap/transaction/stream.ts +254 -0
  109. package/packages/cli/src/cli.ts +814 -0
  110. package/packages/cli/src/credentials.ts +75 -0
  111. package/packages/cli/src/decisions.ts +106 -0
  112. package/packages/cli/src/format.ts +185 -0
  113. package/packages/cli/src/grants.ts +105 -0
  114. package/packages/cli/src/http.ts +86 -0
  115. package/packages/cli/src/launches.ts +157 -0
  116. package/packages/cli/src/login.ts +160 -0
  117. package/packages/cli/src/main.ts +63 -0
  118. package/packages/cli/src/mcp.ts +879 -0
  119. package/packages/cli/src/messaging.ts +108 -0
  120. package/packages/cli/src/node.ts +279 -0
  121. package/packages/cli/src/openshell.ts +166 -0
  122. package/packages/cli/src/setup/commands.ts +809 -0
  123. package/packages/cli/src/setup/contributors.ts +171 -0
  124. package/packages/cli/src/setup/dispatch.ts +74 -0
  125. package/packages/cli/src/setup/launch-driver.ts +109 -0
  126. package/packages/cli/src/setup/runtime.ts +456 -0
  127. package/packages/cli/src/setup/spoke-probe.ts +120 -0
  128. package/packages/cli/src/setup/state.ts +244 -0
  129. package/packages/cli/src/setup/user-bin.ts +39 -0
  130. package/packages/cli/src/spoke-services.ts +109 -0
  131. package/packages/cli/src/watch.ts +28 -0
  132. package/packages/codex/package.json +1 -1
  133. package/packages/codex/src/main.ts +3 -2
  134. package/packages/codex/src/spoke.ts +4 -1
  135. package/packages/codex/src/wrapper.ts +7 -3
  136. package/packages/node/package.json +1 -1
  137. package/packages/node/src/harnesses.ts +39 -4
  138. package/packages/node/src/run.ts +1 -1
  139. package/packages/preset/package.json +1 -1
  140. package/packages/protocol/package.json +1 -1
  141. package/packages/runtime-openshell/package.json +1 -1
  142. package/packages/runtime-sdk/package.json +1 -1
  143. package/packages/sdk/package.json +1 -1
  144. package/packages/sdk/src/client.ts +55 -4
  145. package/packages/sdk/src/errors.ts +1 -0
  146. package/packages/sdk/src/sse.ts +4 -1
  147. package/packages/sdk/src/types.ts +59 -0
  148. package/packages/spoke-sdk/package.json +1 -1
  149. package/packages/spoke-sdk/src/enroll.ts +26 -4
  150. package/src/central/agent/app-tokens.ts +27 -17
  151. package/src/central/agent/archive-inactive.ts +66 -0
  152. package/src/central/agent/directory-routes.ts +155 -12
  153. package/src/central/agent/directory.ts +1 -1
  154. package/src/central/agent/errors.ts +2 -0
  155. package/src/central/agent/event-cursor.ts +49 -0
  156. package/src/central/agent/event-writers.ts +181 -0
  157. package/src/central/agent/events-routes.ts +148 -0
  158. package/src/central/agent/events.ts +277 -0
  159. package/src/central/agent/names.ts +1 -1
  160. package/src/central/agent/read-routes.ts +32 -36
  161. package/src/central/agent/routes.ts +14 -11
  162. package/src/central/agent/watch.ts +65 -174
  163. package/src/central/agent/webhook-routes.ts +10 -2
  164. package/src/central/agent/webhooks.ts +49 -5
  165. package/src/central/auth.ts +127 -1
  166. package/src/central/backend/index.ts +3 -0
  167. package/src/central/backend/types.ts +2 -0
  168. package/src/central/http.ts +115 -32
  169. package/src/central/mcp-http.ts +116 -0
  170. package/src/central/store.ts +53 -0
  171. package/src/config.ts +70 -7
  172. package/src/index.ts +90 -91
  173. package/src/spoke/launchers.ts +109 -3
  174. package/web/dist/apple-touch-icon.png +0 -0
  175. package/web/dist/assets/index-DWnMhXk9.js +20 -0
  176. package/web/dist/assets/index-DmUXEnw9.css +1 -0
  177. package/web/dist/favicon.ico +0 -0
  178. package/web/dist/icon-192.png +0 -0
  179. package/web/dist/icon-512.png +0 -0
  180. package/web/dist/icon-maskable-512.png +0 -0
  181. package/web/dist/icon.svg +5 -5
  182. package/web/dist/index.html +10 -4
  183. package/web/dist/manifest.webmanifest +2 -2
  184. package/web/dist/assets/index-D9zMbxSg.css +0 -1
  185. 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
+ }