text-compress 2.0.0
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/LICENSE +21 -0
- package/README.md +186 -0
- package/dist/api/folder.d.ts +38 -0
- package/dist/api/folder.d.ts.map +1 -0
- package/dist/api/folder.js +50 -0
- package/dist/api/folder.js.map +1 -0
- package/dist/api/text.d.ts +30 -0
- package/dist/api/text.d.ts.map +1 -0
- package/dist/api/text.js +38 -0
- package/dist/api/text.js.map +1 -0
- package/dist/archive/collect.d.ts +26 -0
- package/dist/archive/collect.d.ts.map +1 -0
- package/dist/archive/collect.js +38 -0
- package/dist/archive/collect.js.map +1 -0
- package/dist/archive/format.d.ts +62 -0
- package/dist/archive/format.d.ts.map +1 -0
- package/dist/archive/format.js +143 -0
- package/dist/archive/format.js.map +1 -0
- package/dist/archive/types.d.ts +25 -0
- package/dist/archive/types.d.ts.map +1 -0
- package/dist/archive/types.js +10 -0
- package/dist/archive/types.js.map +1 -0
- package/dist/archive/unpack.d.ts +21 -0
- package/dist/archive/unpack.d.ts.map +1 -0
- package/dist/archive/unpack.js +41 -0
- package/dist/archive/unpack.js.map +1 -0
- package/dist/cli/analytics.d.ts +10 -0
- package/dist/cli/analytics.d.ts.map +1 -0
- package/dist/cli/analytics.js +22 -0
- package/dist/cli/analytics.js.map +1 -0
- package/dist/cli/args.d.ts +43 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +152 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/commands/compress.d.ts +9 -0
- package/dist/cli/commands/compress.d.ts.map +1 -0
- package/dist/cli/commands/compress.js +62 -0
- package/dist/cli/commands/compress.js.map +1 -0
- package/dist/cli/commands/decompress.d.ts +9 -0
- package/dist/cli/commands/decompress.d.ts.map +1 -0
- package/dist/cli/commands/decompress.js +69 -0
- package/dist/cli/commands/decompress.js.map +1 -0
- package/dist/cli/detect.d.ts +25 -0
- package/dist/cli/detect.d.ts.map +1 -0
- package/dist/cli/detect.js +78 -0
- package/dist/cli/detect.js.map +1 -0
- package/dist/cli/main.d.ts +8 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +74 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/output.d.ts +13 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +26 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/paths.d.ts +13 -0
- package/dist/cli/paths.d.ts.map +1 -0
- package/dist/cli/paths.js +24 -0
- package/dist/cli/paths.js.map +1 -0
- package/dist/cli/usage.d.ts +8 -0
- package/dist/cli/usage.d.ts.map +1 -0
- package/dist/cli/usage.js +73 -0
- package/dist/cli/usage.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -0
- package/dist/compression/brotli.d.ts +43 -0
- package/dist/compression/brotli.d.ts.map +1 -0
- package/dist/compression/brotli.js +56 -0
- package/dist/compression/brotli.js.map +1 -0
- package/dist/crypto/password.d.ts +22 -0
- package/dist/crypto/password.d.ts.map +1 -0
- package/dist/crypto/password.js +62 -0
- package/dist/crypto/password.js.map +1 -0
- package/dist/encoding/base64.d.ts +17 -0
- package/dist/encoding/base64.d.ts.map +1 -0
- package/dist/encoding/base64.js +21 -0
- package/dist/encoding/base64.js.map +1 -0
- package/dist/encoding/index.d.ts +29 -0
- package/dist/encoding/index.d.ts.map +1 -0
- package/dist/encoding/index.js +47 -0
- package/dist/encoding/index.js.map +1 -0
- package/dist/encoding/z85.d.ts +64 -0
- package/dist/encoding/z85.d.ts.map +1 -0
- package/dist/encoding/z85.js +99 -0
- package/dist/encoding/z85.js.map +1 -0
- package/dist/fs/gitignore.d.ts +30 -0
- package/dist/fs/gitignore.d.ts.map +1 -0
- package/dist/fs/gitignore.js +104 -0
- package/dist/fs/gitignore.js.map +1 -0
- package/dist/fs/paths.d.ts +22 -0
- package/dist/fs/paths.d.ts.map +1 -0
- package/dist/fs/paths.js +55 -0
- package/dist/fs/paths.js.map +1 -0
- package/dist/fs/walk.d.ts +22 -0
- package/dist/fs/walk.d.ts.map +1 -0
- package/dist/fs/walk.js +49 -0
- package/dist/fs/walk.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/payload/tags.d.ts +60 -0
- package/dist/payload/tags.d.ts.map +1 -0
- package/dist/payload/tags.js +78 -0
- package/dist/payload/tags.js.map +1 -0
- package/dist/split/parts.d.ts +130 -0
- package/dist/split/parts.d.ts.map +1 -0
- package/dist/split/parts.js +290 -0
- package/dist/split/parts.js.map +1 -0
- package/dist/streaming/folder.d.ts +49 -0
- package/dist/streaming/folder.d.ts.map +1 -0
- package/dist/streaming/folder.js +220 -0
- package/dist/streaming/folder.js.map +1 -0
- package/dist/streaming.d.ts +16 -0
- package/dist/streaming.d.ts.map +1 -0
- package/dist/streaming.js +243 -0
- package/dist/streaming.js.map +1 -0
- package/dist/types.d.ts +18 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 text-compress contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# text-compress
|
|
2
|
+
|
|
3
|
+
Brotli-compress text or entire folder trees into pasteable base64 or Z85 strings — ideal for chat, email, or code.
|
|
4
|
+
|
|
5
|
+
**v2** ships as [`text-compress`](https://www.npmjs.com/package/text-compress). The original CLI [`@startdoing/tc`](https://www.npmjs.com/package/@startdoing/tc) stays frozen at **v1.0.4**.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Auto-detect** — pass a path; plain files compress, valid payloads decompress (no subcommand)
|
|
10
|
+
- **Max-quality Brotli** via Node.js `zlib`
|
|
11
|
+
- **Base64 (default)** — paste-safe (`A-Za-z0-9+/=`)
|
|
12
|
+
- **Z85 base85** — ~8% smaller; punctuation-safe for code blocks
|
|
13
|
+
- **Folder archives** — pack a directory tree into one string
|
|
14
|
+
- **Password protection** — AES-256-GCM (`-p` / `--password`)
|
|
15
|
+
- **v2 split parts** — self-describing parts; shuffled names, merged files, any sibling as entry
|
|
16
|
+
- **CLI + library** — terminal or `import from "text-compress"`
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install -g text-compress
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or run without installing:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx text-compress ./notes.md
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Local development:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
git clone <repo-url>
|
|
34
|
+
cd text-compress
|
|
35
|
+
git checkout v2
|
|
36
|
+
npm install
|
|
37
|
+
npm run build
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## CLI
|
|
41
|
+
|
|
42
|
+
No `compress` / `decompress` subcommand — the CLI picks the operation from the input.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Compress a file
|
|
46
|
+
text-compress ./notes.md
|
|
47
|
+
|
|
48
|
+
# Compress with password
|
|
49
|
+
text-compress ./notes.md -p "hello-world"
|
|
50
|
+
|
|
51
|
+
# Decompress (auto-detected from valid compressed output)
|
|
52
|
+
text-compress ./notes.txt -p "hello-world"
|
|
53
|
+
|
|
54
|
+
# Compress a folder
|
|
55
|
+
text-compress ./my-project
|
|
56
|
+
|
|
57
|
+
# Base85 encoding (~8% smaller)
|
|
58
|
+
text-compress ./notes.md -e 85
|
|
59
|
+
|
|
60
|
+
# Split large output
|
|
61
|
+
text-compress ./large-file.txt -s 4000
|
|
62
|
+
|
|
63
|
+
# Inline text
|
|
64
|
+
text-compress -t "hello world" -o output.txt
|
|
65
|
+
|
|
66
|
+
# Force mode when auto-detect is wrong
|
|
67
|
+
text-compress --compress ./looks-compressed.txt
|
|
68
|
+
text-compress --decompress ./plain.md # errors if not valid payload
|
|
69
|
+
|
|
70
|
+
# Split set — pass any sibling
|
|
71
|
+
text-compress ./output.7.txt
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
During development:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm run dev -- ./notes.md
|
|
78
|
+
npm run dev -- ./notes.txt -p "secret"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Defaults
|
|
82
|
+
|
|
83
|
+
| | Output when `-o` omitted |
|
|
84
|
+
|--|--|
|
|
85
|
+
| Compress file/text | `<input>.txt` |
|
|
86
|
+
| Compress folder | `<folder-name>.txt` |
|
|
87
|
+
| Decompress text | `<input>.de.txt` |
|
|
88
|
+
| Decompress folder | `<input>.de/` |
|
|
89
|
+
|
|
90
|
+
## Library
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import {
|
|
94
|
+
compress,
|
|
95
|
+
decompress,
|
|
96
|
+
compressFolder,
|
|
97
|
+
decompressToPath,
|
|
98
|
+
} from "text-compress";
|
|
99
|
+
|
|
100
|
+
const encoded = compress("hello world");
|
|
101
|
+
const restored = decompress(encoded);
|
|
102
|
+
|
|
103
|
+
const locked = compress("hello world", 64, "my secret");
|
|
104
|
+
const unlocked = decompress(locked, 64, "my secret");
|
|
105
|
+
|
|
106
|
+
const { encoded: folderBlob } = compressFolder("./my-project");
|
|
107
|
+
decompressToPath(folderBlob, "./restored-project");
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
| Function | Description |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `compress(text, encoding?, password?)` | UTF-8 text → encoded string (`64` or `85`) |
|
|
113
|
+
| `decompress(encoded, encoding?, password?)` | Encoded text payload → string |
|
|
114
|
+
| `compressFolder(dirPath, encoding?, password?)` | Folder → `{ encoded, fileCount, ... }` |
|
|
115
|
+
| `decompressToPath(encoded, destDir, encoding?, password?)` | Unpack folder archive |
|
|
116
|
+
| `decompressPayload(encoded, encoding?, password?)` | Low-level `{ tag, data }` |
|
|
117
|
+
|
|
118
|
+
## Encoding
|
|
119
|
+
|
|
120
|
+
| Value | Format | Use when |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `64` (default) | Base64 | Paste anywhere |
|
|
123
|
+
| `85` | Z85 | Slightly smaller; paste in code blocks |
|
|
124
|
+
|
|
125
|
+
Use the same encoding for compress and decompress. When `-e` is omitted on decompress, the CLI tries both.
|
|
126
|
+
|
|
127
|
+
## Password protection
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
text-compress ./notes.md -p "my secret" -o locked.txt
|
|
131
|
+
text-compress ./locked.txt -p "my secret" -o notes.md
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Password-protected payloads error without `-p` (they are **not** silently re-compressed).
|
|
135
|
+
|
|
136
|
+
## v2 split output
|
|
137
|
+
|
|
138
|
+
Large outputs split into numbered files (`output.1.txt`, `output.02.txt`, …). Each part embeds order in a `TCP\x02` header.
|
|
139
|
+
|
|
140
|
+
On decompress:
|
|
141
|
+
|
|
142
|
+
- Pass **any** sibling file
|
|
143
|
+
- Discovery uses the basename **prefix** (before the first `.`)
|
|
144
|
+
- Extension ignored; invalid siblings skipped
|
|
145
|
+
- Parts can be shuffled or merged into fewer files
|
|
146
|
+
|
|
147
|
+
**Not compatible** with v1 split format (raw concatenation without headers).
|
|
148
|
+
|
|
149
|
+
## Migration from @startdoing/tc v1
|
|
150
|
+
|
|
151
|
+
| v1 (`@startdoing/tc@1.0.4`) | v2 (`text-compress`) |
|
|
152
|
+
|---|---|
|
|
153
|
+
| `tc compress notes.md` | `text-compress notes.md` |
|
|
154
|
+
| `tc decompress out.txt` | `text-compress out.txt` |
|
|
155
|
+
| `import from "@startdoing/tc"` | `import from "text-compress"` |
|
|
156
|
+
| Raw split parts | Headered split parts |
|
|
157
|
+
|
|
158
|
+
## Development
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npm install
|
|
162
|
+
npm test
|
|
163
|
+
npm run build
|
|
164
|
+
npm run check
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) and [docs/LEARNING.md](docs/LEARNING.md).
|
|
168
|
+
|
|
169
|
+
## Changelog
|
|
170
|
+
|
|
171
|
+
### v2.0.0 — `text-compress` (2026-07-08)
|
|
172
|
+
|
|
173
|
+
- New npm package **`text-compress`** (v1 remains `@startdoing/tc@1.0.4`)
|
|
174
|
+
- Auto-detect compress vs decompress from input
|
|
175
|
+
- Shorter CLI: `text-compress ./file.md` (no subcommand)
|
|
176
|
+
- Self-describing split format (`TCP\x02` headers)
|
|
177
|
+
- Prefix-based split discovery; skip invalid siblings
|
|
178
|
+
- Force flags: `--compress` / `--decompress`
|
|
179
|
+
|
|
180
|
+
### v1.x
|
|
181
|
+
|
|
182
|
+
See git tag / branch `main` and [@startdoing/tc on npm](https://www.npmjs.com/package/@startdoing/tc).
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
MIT
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module api/folder
|
|
3
|
+
*
|
|
4
|
+
* High-level API for folder compression and decompression.
|
|
5
|
+
*
|
|
6
|
+
* ## In-memory path (`compressFolder`)
|
|
7
|
+
*
|
|
8
|
+
* Walks the tree, builds a binary archive in RAM, compresses, and encodes.
|
|
9
|
+
* Suitable for small-to-medium folders.
|
|
10
|
+
*
|
|
11
|
+
* ## Decompression (`decompressToPath`)
|
|
12
|
+
*
|
|
13
|
+
* Decodes, decompresses, checks the folder tag, and unpacks to disk.
|
|
14
|
+
*/
|
|
15
|
+
import type { Encoding } from "../types.js";
|
|
16
|
+
/**
|
|
17
|
+
* Pack a directory tree into a single encoded string (in-memory).
|
|
18
|
+
*
|
|
19
|
+
* @returns Encoded blob plus statistics about the source folder.
|
|
20
|
+
*/
|
|
21
|
+
export declare function compressFolder(dirPath: string, encoding?: Encoding, password?: string): {
|
|
22
|
+
encoded: string;
|
|
23
|
+
fileCount: number;
|
|
24
|
+
dirCount: number;
|
|
25
|
+
originalBytes: number;
|
|
26
|
+
archiveBytes: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Decode and unpack a folder archive to a destination directory.
|
|
30
|
+
*
|
|
31
|
+
* @throws If the payload is text, not a folder archive.
|
|
32
|
+
*/
|
|
33
|
+
export declare function decompressToPath(encoded: string, destDir: string, encoding?: Encoding, password?: string): {
|
|
34
|
+
files: number;
|
|
35
|
+
dirs: number;
|
|
36
|
+
bytes: number;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=folder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder.d.ts","sourceRoot":"","sources":["../../src/api/folder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,QAAa,EAAE,QAAQ,CAAC,EAAE,MAAM;;;;;;EAazF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,QAAa,EACvB,QAAQ,CAAC,EAAE,MAAM;;;;EAOlB"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module api/folder
|
|
3
|
+
*
|
|
4
|
+
* High-level API for folder compression and decompression.
|
|
5
|
+
*
|
|
6
|
+
* ## In-memory path (`compressFolder`)
|
|
7
|
+
*
|
|
8
|
+
* Walks the tree, builds a binary archive in RAM, compresses, and encodes.
|
|
9
|
+
* Suitable for small-to-medium folders.
|
|
10
|
+
*
|
|
11
|
+
* ## Decompression (`decompressToPath`)
|
|
12
|
+
*
|
|
13
|
+
* Decodes, decompresses, checks the folder tag, and unpacks to disk.
|
|
14
|
+
*/
|
|
15
|
+
import { collectEntries } from "../archive/collect.js";
|
|
16
|
+
import { serializeArchive } from "../archive/format.js";
|
|
17
|
+
import { unpackDirectory } from "../archive/unpack.js";
|
|
18
|
+
import { compressTaggedPayload, decompressPayload, TAG_FOLDER } from "../payload/tags.js";
|
|
19
|
+
/**
|
|
20
|
+
* Pack a directory tree into a single encoded string (in-memory).
|
|
21
|
+
*
|
|
22
|
+
* @returns Encoded blob plus statistics about the source folder.
|
|
23
|
+
*/
|
|
24
|
+
export function compressFolder(dirPath, encoding = 64, password) {
|
|
25
|
+
const entries = collectEntries(dirPath);
|
|
26
|
+
const archive = serializeArchive(entries);
|
|
27
|
+
const encoded = compressTaggedPayload(TAG_FOLDER, archive, encoding, password);
|
|
28
|
+
const files = entries.filter((e) => e.type === "f");
|
|
29
|
+
const originalBytes = files.reduce((sum, e) => sum + (e.content?.length ?? 0), 0);
|
|
30
|
+
return {
|
|
31
|
+
encoded,
|
|
32
|
+
fileCount: files.length,
|
|
33
|
+
dirCount: entries.length - files.length,
|
|
34
|
+
originalBytes,
|
|
35
|
+
archiveBytes: archive.length,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Decode and unpack a folder archive to a destination directory.
|
|
40
|
+
*
|
|
41
|
+
* @throws If the payload is text, not a folder archive.
|
|
42
|
+
*/
|
|
43
|
+
export function decompressToPath(encoded, destDir, encoding = 64, password) {
|
|
44
|
+
const { tag, data } = decompressPayload(encoded, encoding, password);
|
|
45
|
+
if (tag !== TAG_FOLDER) {
|
|
46
|
+
throw new Error("This payload is compressed text, not a folder. Use decompress.");
|
|
47
|
+
}
|
|
48
|
+
return unpackDirectory(data, destDir);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=folder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder.js","sourceRoot":"","sources":["../../src/api/folder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAGzF;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,QAAQ,GAAa,EAAE,EAAE,QAAiB;IACxF,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;IACvC,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACzC,MAAM,OAAO,GAAG,qBAAqB,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAC9E,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAA;IACnD,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACjF,OAAO;QACL,OAAO;QACP,SAAS,EAAE,KAAK,CAAC,MAAM;QACvB,QAAQ,EAAE,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM;QACvC,aAAa;QACb,YAAY,EAAE,OAAO,CAAC,MAAM;KAC7B,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,OAAe,EACf,QAAQ,GAAa,EAAE,EACvB,QAAiB;IAEjB,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,iBAAiB,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;IACpE,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAA;IACnF,CAAC;IACD,OAAO,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;AACvC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module api/text
|
|
3
|
+
*
|
|
4
|
+
* High-level API for compressing and decompressing UTF-8 text strings.
|
|
5
|
+
*
|
|
6
|
+
* ## Pipeline
|
|
7
|
+
*
|
|
8
|
+
* ```
|
|
9
|
+
* compress: text → UTF-8 bytes → tag → Brotli → Base64/Z85
|
|
10
|
+
* decompress: Base64/Z85 → Brotli → tag check → UTF-8 string
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Text and folder payloads share the same outer encoding but are
|
|
14
|
+
* distinguished by the leading tag byte after decompression.
|
|
15
|
+
*/
|
|
16
|
+
import type { Encoding } from "../types.js";
|
|
17
|
+
/**
|
|
18
|
+
* Compress a UTF-8 string to a pasteable encoded blob.
|
|
19
|
+
*
|
|
20
|
+
* @param text - Input string (any Unicode code points).
|
|
21
|
+
* @param encoding - `64` (Base64) or `85` (Z85); default Base64.
|
|
22
|
+
*/
|
|
23
|
+
export declare function compress(text: string, encoding?: Encoding, password?: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Decompress an encoded text payload back to a UTF-8 string.
|
|
26
|
+
*
|
|
27
|
+
* @throws If the payload is a folder archive (wrong tag).
|
|
28
|
+
*/
|
|
29
|
+
export declare function decompress(encoded: string, encoding?: Encoding, password?: string): string;
|
|
30
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/api/text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,QAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzF;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,QAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAM9F"}
|
package/dist/api/text.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module api/text
|
|
3
|
+
*
|
|
4
|
+
* High-level API for compressing and decompressing UTF-8 text strings.
|
|
5
|
+
*
|
|
6
|
+
* ## Pipeline
|
|
7
|
+
*
|
|
8
|
+
* ```
|
|
9
|
+
* compress: text → UTF-8 bytes → tag → Brotli → Base64/Z85
|
|
10
|
+
* decompress: Base64/Z85 → Brotli → tag check → UTF-8 string
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Text and folder payloads share the same outer encoding but are
|
|
14
|
+
* distinguished by the leading tag byte after decompression.
|
|
15
|
+
*/
|
|
16
|
+
import { compressTaggedPayload, decompressPayload, TAG_TEXT } from "../payload/tags.js";
|
|
17
|
+
/**
|
|
18
|
+
* Compress a UTF-8 string to a pasteable encoded blob.
|
|
19
|
+
*
|
|
20
|
+
* @param text - Input string (any Unicode code points).
|
|
21
|
+
* @param encoding - `64` (Base64) or `85` (Z85); default Base64.
|
|
22
|
+
*/
|
|
23
|
+
export function compress(text, encoding = 64, password) {
|
|
24
|
+
return compressTaggedPayload(TAG_TEXT, Buffer.from(text, "utf-8"), encoding, password);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Decompress an encoded text payload back to a UTF-8 string.
|
|
28
|
+
*
|
|
29
|
+
* @throws If the payload is a folder archive (wrong tag).
|
|
30
|
+
*/
|
|
31
|
+
export function decompress(encoded, encoding = 64, password) {
|
|
32
|
+
const raw = decompressPayload(encoded, encoding, password);
|
|
33
|
+
if (raw.tag !== TAG_TEXT) {
|
|
34
|
+
throw new Error("This payload is a compressed folder, not text. Use decompressToPath.");
|
|
35
|
+
}
|
|
36
|
+
return raw.data.toString("utf-8");
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/api/text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAGvF;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,QAAQ,GAAa,EAAE,EAAE,QAAiB;IAC/E,OAAO,qBAAqB,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;AACxF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,QAAQ,GAAa,EAAE,EAAE,QAAiB;IACpF,MAAM,GAAG,GAAG,iBAAiB,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAC1D,IAAI,GAAG,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAA;IACzF,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;AACnC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module archive/collect
|
|
3
|
+
*
|
|
4
|
+
* In-memory directory tree walker that produces a flat {@link ArchiveEntry}
|
|
5
|
+
* list suitable for {@link serializeArchive}.
|
|
6
|
+
*
|
|
7
|
+
* ## Traversal algorithm
|
|
8
|
+
*
|
|
9
|
+
* Depth-first pre-order walk with sorted children:
|
|
10
|
+
*
|
|
11
|
+
* 1. Emit a directory marker for every non-root directory.
|
|
12
|
+
* 2. For each child (sorted by `localeCompare`), recurse into subdirs or
|
|
13
|
+
* read file content into memory.
|
|
14
|
+
* 3. Skip paths excluded by `.gitignore` rules (outside → inside).
|
|
15
|
+
*
|
|
16
|
+
* This path loads every file into RAM — use the streaming builder in
|
|
17
|
+
* `streaming/folder.ts` for large directory trees.
|
|
18
|
+
*/
|
|
19
|
+
import type { ArchiveEntry } from "./types.js";
|
|
20
|
+
/**
|
|
21
|
+
* Walk a directory tree and collect all entries into a flat array.
|
|
22
|
+
*
|
|
23
|
+
* @param rootDir - Absolute path to the folder root.
|
|
24
|
+
*/
|
|
25
|
+
export declare function collectEntries(rootDir: string): ArchiveEntry[];
|
|
26
|
+
//# sourceMappingURL=collect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/archive/collect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CAa9D"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module archive/collect
|
|
3
|
+
*
|
|
4
|
+
* In-memory directory tree walker that produces a flat {@link ArchiveEntry}
|
|
5
|
+
* list suitable for {@link serializeArchive}.
|
|
6
|
+
*
|
|
7
|
+
* ## Traversal algorithm
|
|
8
|
+
*
|
|
9
|
+
* Depth-first pre-order walk with sorted children:
|
|
10
|
+
*
|
|
11
|
+
* 1. Emit a directory marker for every non-root directory.
|
|
12
|
+
* 2. For each child (sorted by `localeCompare`), recurse into subdirs or
|
|
13
|
+
* read file content into memory.
|
|
14
|
+
* 3. Skip paths excluded by `.gitignore` rules (outside → inside).
|
|
15
|
+
*
|
|
16
|
+
* This path loads every file into RAM — use the streaming builder in
|
|
17
|
+
* `streaming/folder.ts` for large directory trees.
|
|
18
|
+
*/
|
|
19
|
+
import { readFileSync } from "node:fs";
|
|
20
|
+
import { walkDirectory } from "../fs/walk.js";
|
|
21
|
+
/**
|
|
22
|
+
* Walk a directory tree and collect all entries into a flat array.
|
|
23
|
+
*
|
|
24
|
+
* @param rootDir - Absolute path to the folder root.
|
|
25
|
+
*/
|
|
26
|
+
export function collectEntries(rootDir) {
|
|
27
|
+
const entries = [];
|
|
28
|
+
walkDirectory(rootDir, {
|
|
29
|
+
onDirectory: (_absDir, relDir) => {
|
|
30
|
+
entries.push({ type: "d", relPath: relDir });
|
|
31
|
+
},
|
|
32
|
+
onFile: (abs, rel) => {
|
|
33
|
+
entries.push({ type: "f", relPath: rel, content: readFileSync(abs) });
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
return entries;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=collect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect.js","sourceRoot":"","sources":["../../src/archive/collect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAG7C;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,MAAM,OAAO,GAAmB,EAAE,CAAA;IAElC,aAAa,CAAC,OAAO,EAAE;QACrB,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC/B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;QAC9C,CAAC;QACD,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACnB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACvE,CAAC;KACF,CAAC,CAAA;IAEF,OAAO,OAAO,CAAA;AAChB,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module archive/format
|
|
3
|
+
*
|
|
4
|
+
* Binary serialization and deserialization for folder archives.
|
|
5
|
+
*
|
|
6
|
+
* ## Wire format
|
|
7
|
+
*
|
|
8
|
+
* A flat sequence of length-prefixed entries (little-endian u32 lengths):
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* directory: [0x44] [pathLen: u32le] [path utf-8 bytes]
|
|
12
|
+
* file: [0x46] [pathLen: u32le] [path utf-8] [contentLen: u32le] [content bytes]
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Paths are POSIX-style (forward slashes) and relative to the archive root.
|
|
16
|
+
* File metadata (permissions, timestamps, symlinks) is intentionally dropped
|
|
17
|
+
* to keep the format minimal and deterministic.
|
|
18
|
+
*
|
|
19
|
+
* ## Design choices
|
|
20
|
+
*
|
|
21
|
+
* - **Flat list vs tree** — A pre-order walk produces a flat list that is
|
|
22
|
+
* trivial to stream to disk without building an in-memory tree.
|
|
23
|
+
* - **Little-endian u32** — Matches Node's `writeUInt32LE`; portable across
|
|
24
|
+
* all platforms this library targets.
|
|
25
|
+
* - **Sorted children** — `collectEntries` sorts directory entries by name
|
|
26
|
+
* so the same folder always produces the same byte sequence (deterministic
|
|
27
|
+
* output aids testing and deduplication).
|
|
28
|
+
*
|
|
29
|
+
* ## Security
|
|
30
|
+
*
|
|
31
|
+
* `deserializeArchive` rejects paths containing `..` or starting with `/`
|
|
32
|
+
* to prevent zip-slip style directory traversal on unpack.
|
|
33
|
+
*/
|
|
34
|
+
import { type ArchiveEntry } from "./types.js";
|
|
35
|
+
/**
|
|
36
|
+
* Serialize an array of archive entries into a single binary buffer.
|
|
37
|
+
*
|
|
38
|
+
* @param entries - Flat list from `collectEntries` or manual construction.
|
|
39
|
+
*/
|
|
40
|
+
export declare function serializeArchive(entries: ArchiveEntry[]): Buffer;
|
|
41
|
+
/**
|
|
42
|
+
* Parse a binary archive buffer back into entry objects.
|
|
43
|
+
*
|
|
44
|
+
* @throws On unknown type bytes, truncated data, or unsafe paths.
|
|
45
|
+
*/
|
|
46
|
+
export declare function deserializeArchive(buffer: Buffer): ArchiveEntry[];
|
|
47
|
+
/**
|
|
48
|
+
* Write a single directory entry to an open file descriptor.
|
|
49
|
+
*
|
|
50
|
+
* Used by the streaming archive builder to avoid buffering the entire
|
|
51
|
+
* archive in memory.
|
|
52
|
+
*/
|
|
53
|
+
export declare function writeDirEntry(fd: number, relPath: string): void;
|
|
54
|
+
/**
|
|
55
|
+
* Write a single file entry to an open file descriptor, streaming content
|
|
56
|
+
* from disk in chunks.
|
|
57
|
+
*
|
|
58
|
+
* @param copyChunkSize - Read buffer size for streaming file content.
|
|
59
|
+
* @returns Number of content bytes written.
|
|
60
|
+
*/
|
|
61
|
+
export declare function writeFileEntry(fd: number, relPath: string, filePath: string, copyChunkSize: number): number;
|
|
62
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/archive/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAGH,OAAO,EAAE,KAAK,YAAY,EAAyB,MAAM,YAAY,CAAA;AAErE;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAiBhE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,CA4BjE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAO/D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACpB,MAAM,CA6BR"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module archive/format
|
|
3
|
+
*
|
|
4
|
+
* Binary serialization and deserialization for folder archives.
|
|
5
|
+
*
|
|
6
|
+
* ## Wire format
|
|
7
|
+
*
|
|
8
|
+
* A flat sequence of length-prefixed entries (little-endian u32 lengths):
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* directory: [0x44] [pathLen: u32le] [path utf-8 bytes]
|
|
12
|
+
* file: [0x46] [pathLen: u32le] [path utf-8] [contentLen: u32le] [content bytes]
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Paths are POSIX-style (forward slashes) and relative to the archive root.
|
|
16
|
+
* File metadata (permissions, timestamps, symlinks) is intentionally dropped
|
|
17
|
+
* to keep the format minimal and deterministic.
|
|
18
|
+
*
|
|
19
|
+
* ## Design choices
|
|
20
|
+
*
|
|
21
|
+
* - **Flat list vs tree** — A pre-order walk produces a flat list that is
|
|
22
|
+
* trivial to stream to disk without building an in-memory tree.
|
|
23
|
+
* - **Little-endian u32** — Matches Node's `writeUInt32LE`; portable across
|
|
24
|
+
* all platforms this library targets.
|
|
25
|
+
* - **Sorted children** — `collectEntries` sorts directory entries by name
|
|
26
|
+
* so the same folder always produces the same byte sequence (deterministic
|
|
27
|
+
* output aids testing and deduplication).
|
|
28
|
+
*
|
|
29
|
+
* ## Security
|
|
30
|
+
*
|
|
31
|
+
* `deserializeArchive` rejects paths containing `..` or starting with `/`
|
|
32
|
+
* to prevent zip-slip style directory traversal on unpack.
|
|
33
|
+
*/
|
|
34
|
+
import { closeSync, openSync, readSync, statSync, writeSync } from "node:fs";
|
|
35
|
+
import { ENTRY_DIR, ENTRY_FILE } from "./types.js";
|
|
36
|
+
/**
|
|
37
|
+
* Serialize an array of archive entries into a single binary buffer.
|
|
38
|
+
*
|
|
39
|
+
* @param entries - Flat list from `collectEntries` or manual construction.
|
|
40
|
+
*/
|
|
41
|
+
export function serializeArchive(entries) {
|
|
42
|
+
const chunks = [];
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
const pathBuf = Buffer.from(entry.relPath, "utf-8");
|
|
45
|
+
const pathLenBuf = Buffer.alloc(4);
|
|
46
|
+
pathLenBuf.writeUInt32LE(pathBuf.length, 0);
|
|
47
|
+
if (entry.type === "d") {
|
|
48
|
+
chunks.push(Buffer.from([ENTRY_DIR]), pathLenBuf, pathBuf);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
const content = entry.content ?? Buffer.alloc(0);
|
|
52
|
+
const contentLenBuf = Buffer.alloc(4);
|
|
53
|
+
contentLenBuf.writeUInt32LE(content.length, 0);
|
|
54
|
+
chunks.push(Buffer.from([ENTRY_FILE]), pathLenBuf, pathBuf, contentLenBuf, content);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return Buffer.concat(chunks);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Parse a binary archive buffer back into entry objects.
|
|
61
|
+
*
|
|
62
|
+
* @throws On unknown type bytes, truncated data, or unsafe paths.
|
|
63
|
+
*/
|
|
64
|
+
export function deserializeArchive(buffer) {
|
|
65
|
+
const entries = [];
|
|
66
|
+
let offset = 0;
|
|
67
|
+
while (offset < buffer.length) {
|
|
68
|
+
const type = buffer[offset];
|
|
69
|
+
offset += 1;
|
|
70
|
+
const pathLen = buffer.readUInt32LE(offset);
|
|
71
|
+
offset += 4;
|
|
72
|
+
const relPath = buffer.subarray(offset, offset + pathLen).toString("utf-8");
|
|
73
|
+
offset += pathLen;
|
|
74
|
+
if (relPath.startsWith("/") || relPath.split("/").includes("..")) {
|
|
75
|
+
throw new Error(`Unsafe path in archive: "${relPath}"`);
|
|
76
|
+
}
|
|
77
|
+
if (type === ENTRY_DIR) {
|
|
78
|
+
entries.push({ type: "d", relPath });
|
|
79
|
+
}
|
|
80
|
+
else if (type === ENTRY_FILE) {
|
|
81
|
+
const contentLen = buffer.readUInt32LE(offset);
|
|
82
|
+
offset += 4;
|
|
83
|
+
const content = buffer.subarray(offset, offset + contentLen);
|
|
84
|
+
offset += contentLen;
|
|
85
|
+
entries.push({ type: "f", relPath, content });
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
throw new Error(`Corrupt archive: unknown entry type byte 0x${type.toString(16)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return entries;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Write a single directory entry to an open file descriptor.
|
|
95
|
+
*
|
|
96
|
+
* Used by the streaming archive builder to avoid buffering the entire
|
|
97
|
+
* archive in memory.
|
|
98
|
+
*/
|
|
99
|
+
export function writeDirEntry(fd, relPath) {
|
|
100
|
+
const pathBuf = Buffer.from(relPath, "utf-8");
|
|
101
|
+
const pathLenBuf = Buffer.alloc(4);
|
|
102
|
+
pathLenBuf.writeUInt32LE(pathBuf.length, 0);
|
|
103
|
+
writeSync(fd, Buffer.from([ENTRY_DIR]));
|
|
104
|
+
writeSync(fd, pathLenBuf);
|
|
105
|
+
writeSync(fd, pathBuf);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Write a single file entry to an open file descriptor, streaming content
|
|
109
|
+
* from disk in chunks.
|
|
110
|
+
*
|
|
111
|
+
* @param copyChunkSize - Read buffer size for streaming file content.
|
|
112
|
+
* @returns Number of content bytes written.
|
|
113
|
+
*/
|
|
114
|
+
export function writeFileEntry(fd, relPath, filePath, copyChunkSize) {
|
|
115
|
+
const pathBuf = Buffer.from(relPath, "utf-8");
|
|
116
|
+
const pathLenBuf = Buffer.alloc(4);
|
|
117
|
+
pathLenBuf.writeUInt32LE(pathBuf.length, 0);
|
|
118
|
+
const contentLen = statSync(filePath).size;
|
|
119
|
+
const contentLenBuf = Buffer.alloc(4);
|
|
120
|
+
contentLenBuf.writeUInt32LE(contentLen, 0);
|
|
121
|
+
writeSync(fd, Buffer.from([ENTRY_FILE]));
|
|
122
|
+
writeSync(fd, pathLenBuf);
|
|
123
|
+
writeSync(fd, pathBuf);
|
|
124
|
+
writeSync(fd, contentLenBuf);
|
|
125
|
+
const srcFd = openSync(filePath, "r");
|
|
126
|
+
try {
|
|
127
|
+
const buf = Buffer.alloc(copyChunkSize);
|
|
128
|
+
let copied = 0;
|
|
129
|
+
while (copied < contentLen) {
|
|
130
|
+
const toRead = Math.min(buf.length, contentLen - copied);
|
|
131
|
+
const n = readSync(srcFd, buf, 0, toRead, copied);
|
|
132
|
+
if (n <= 0)
|
|
133
|
+
break;
|
|
134
|
+
writeSync(fd, buf, 0, n);
|
|
135
|
+
copied += n;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
closeSync(srcFd);
|
|
140
|
+
}
|
|
141
|
+
return contentLen;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/archive/format.ts"],"names":[],"mappings":"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"}
|