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
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module cli/detect
|
|
3
|
+
*
|
|
4
|
+
* Detect whether input is a valid compressed payload for auto-routing.
|
|
5
|
+
*/
|
|
6
|
+
import { brotliDecompressSync } from "node:zlib";
|
|
7
|
+
import { decryptBuffer, isEncrypted } from "../crypto/password.js";
|
|
8
|
+
import { decodeBuffer } from "../encoding/index.js";
|
|
9
|
+
import { decompressPayload, TAG_FOLDER, TAG_TEXT } from "../payload/tags.js";
|
|
10
|
+
/** True when an error means the payload is locked, not that it is plain text. */
|
|
11
|
+
export function isPasswordRelatedError(err) {
|
|
12
|
+
const message = err.message ?? "";
|
|
13
|
+
return message.includes("password-protected") || message === "Invalid password.";
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Return whether trimmed encoded text is a valid compressed payload.
|
|
17
|
+
*
|
|
18
|
+
* When `-e` is omitted, both Base64 and Z85 are tried. Encrypted payloads
|
|
19
|
+
* without a password return `password-required` instead of `not-compressed`.
|
|
20
|
+
*/
|
|
21
|
+
export function detectCompressedPayload(encoded, encoding, password) {
|
|
22
|
+
const trimmed = encoded.trim();
|
|
23
|
+
if (trimmed.length === 0)
|
|
24
|
+
return "not-compressed";
|
|
25
|
+
const encodings = encoding ? [encoding] : [64, 85];
|
|
26
|
+
let sawEncryptedWithoutPassword = false;
|
|
27
|
+
for (const enc of encodings) {
|
|
28
|
+
let decoded;
|
|
29
|
+
try {
|
|
30
|
+
decoded = decodeBuffer(trimmed, enc);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (isEncrypted(decoded)) {
|
|
36
|
+
if (!password) {
|
|
37
|
+
sawEncryptedWithoutPassword = true;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
decoded = decryptBuffer(decoded, password);
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const raw = brotliDecompressSync(decoded);
|
|
44
|
+
const tag = raw[0];
|
|
45
|
+
if (tag === TAG_TEXT || tag === TAG_FOLDER) {
|
|
46
|
+
return "compressed";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch { }
|
|
50
|
+
}
|
|
51
|
+
if (sawEncryptedWithoutPassword)
|
|
52
|
+
return "password-required";
|
|
53
|
+
return "not-compressed";
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolve the best encoding for decompress after auto-detection.
|
|
57
|
+
*
|
|
58
|
+
* When `-e` is set, use it. Otherwise return the encoding that validates.
|
|
59
|
+
*/
|
|
60
|
+
export function resolveDetectedEncoding(encoded, encoding, password) {
|
|
61
|
+
if (encoding)
|
|
62
|
+
return encoding;
|
|
63
|
+
const trimmed = encoded.trim();
|
|
64
|
+
for (const enc of [64, 85]) {
|
|
65
|
+
if (detectCompressedPayload(trimmed, enc, password) === "compressed") {
|
|
66
|
+
return enc;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return 64;
|
|
70
|
+
}
|
|
71
|
+
/** Validate using the full decompress path (used when forcing decompress). */
|
|
72
|
+
export function assertDecompressible(encoded, encoding, password) {
|
|
73
|
+
const { tag } = decompressPayload(encoded.trim(), encoding, password);
|
|
74
|
+
if (tag !== TAG_TEXT && tag !== TAG_FOLDER) {
|
|
75
|
+
throw new Error(`Corrupt or unrecognized payload (tag 0x${tag.toString(16)}).`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/cli/detect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAyCH,2BAA2B;AAC3B,wBAAgB,IAAI,SAgCnB"}
|
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module cli/main
|
|
3
|
+
*
|
|
4
|
+
* CLI entry logic — parses argv and auto-routes to compress or decompress.
|
|
5
|
+
*/
|
|
6
|
+
import { readSplitInput } from "../split/parts.js";
|
|
7
|
+
import { parseArgs, resolveEncodingOptional, resolveInputArgs } from "./args.js";
|
|
8
|
+
import { runCompress } from "./commands/compress.js";
|
|
9
|
+
import { runDecompress } from "./commands/decompress.js";
|
|
10
|
+
import { detectCompressedPayload } from "./detect.js";
|
|
11
|
+
import { printUsage } from "./usage.js";
|
|
12
|
+
function wantsHelp(argv) {
|
|
13
|
+
return argv.length === 0 || argv.includes("-h") || argv.includes("--help");
|
|
14
|
+
}
|
|
15
|
+
/** Strip an optional legacy leading compress/decompress command. */
|
|
16
|
+
function normalizeArgv(argv) {
|
|
17
|
+
const [first, ...rest] = argv;
|
|
18
|
+
if (first === "compress" || first === "c")
|
|
19
|
+
return { argv: rest, forcedMode: "compress" };
|
|
20
|
+
if (first === "decompress" || first === "d")
|
|
21
|
+
return { argv: rest, forcedMode: "decompress" };
|
|
22
|
+
return { argv };
|
|
23
|
+
}
|
|
24
|
+
function readEncodedInput(args) {
|
|
25
|
+
if (args.file)
|
|
26
|
+
return readSplitInput(args.file).content;
|
|
27
|
+
if (args.text !== undefined)
|
|
28
|
+
return args.text;
|
|
29
|
+
throw new Error("No input provided. Pass a path, or use -t <text>.");
|
|
30
|
+
}
|
|
31
|
+
function resolveCliMode(args) {
|
|
32
|
+
if (args.mode === "compress")
|
|
33
|
+
return "compress";
|
|
34
|
+
if (args.mode === "decompress")
|
|
35
|
+
return "decompress";
|
|
36
|
+
if (args.dir)
|
|
37
|
+
return "compress";
|
|
38
|
+
const encoded = readEncodedInput(args);
|
|
39
|
+
const detection = detectCompressedPayload(encoded, resolveEncodingOptional(args), args.password);
|
|
40
|
+
if (detection === "password-required") {
|
|
41
|
+
throw new Error("This payload is password-protected. Pass -p/--password to decompress.");
|
|
42
|
+
}
|
|
43
|
+
if (detection === "compressed")
|
|
44
|
+
return "decompress";
|
|
45
|
+
return "compress";
|
|
46
|
+
}
|
|
47
|
+
/** Main CLI dispatcher. */
|
|
48
|
+
export function main() {
|
|
49
|
+
let argv = process.argv.slice(2);
|
|
50
|
+
if (wantsHelp(argv)) {
|
|
51
|
+
printUsage();
|
|
52
|
+
process.exit(argv.length === 0 ? 1 : 0);
|
|
53
|
+
}
|
|
54
|
+
const legacy = normalizeArgv(argv);
|
|
55
|
+
argv = legacy.argv;
|
|
56
|
+
const args = parseArgs(argv);
|
|
57
|
+
if (legacy.forcedMode && !args.mode) {
|
|
58
|
+
args.mode = legacy.forcedMode;
|
|
59
|
+
}
|
|
60
|
+
const run = async () => {
|
|
61
|
+
resolveInputArgs(args, args.mode === "decompress" ? "decompress" : "compress");
|
|
62
|
+
const mode = resolveCliMode(args);
|
|
63
|
+
if (mode === "decompress") {
|
|
64
|
+
runDecompress(args);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
await runCompress(args);
|
|
68
|
+
};
|
|
69
|
+
run().catch((err) => {
|
|
70
|
+
console.error(`Error: ${err.message}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module cli/output
|
|
3
|
+
*
|
|
4
|
+
* Write compressed output to disk, optionally split into numbered parts.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Write an encoded string to one file or multiple split part files.
|
|
8
|
+
*/
|
|
9
|
+
export declare function writeCompressedOutput(encoded: string, outputPath: string, explicitSplit?: number): {
|
|
10
|
+
paths: string[];
|
|
11
|
+
splitChunkSize?: number;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAe9C"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module cli/output
|
|
3
|
+
*
|
|
4
|
+
* Write compressed output to disk, optionally split into numbered parts.
|
|
5
|
+
*/
|
|
6
|
+
import { writeFileSync } from "node:fs";
|
|
7
|
+
import { formatSplitOutputPath, resolveSplitChunkSize, splitString, wrapSplitChunk, } from "../split/parts.js";
|
|
8
|
+
/**
|
|
9
|
+
* Write an encoded string to one file or multiple split part files.
|
|
10
|
+
*/
|
|
11
|
+
export function writeCompressedOutput(encoded, outputPath, explicitSplit) {
|
|
12
|
+
const splitChunkSize = resolveSplitChunkSize(encoded.length, explicitSplit);
|
|
13
|
+
if (splitChunkSize === undefined) {
|
|
14
|
+
writeFileSync(outputPath, encoded, "utf-8");
|
|
15
|
+
return { paths: [outputPath] };
|
|
16
|
+
}
|
|
17
|
+
const chunks = splitString(encoded, splitChunkSize);
|
|
18
|
+
const totalParts = chunks.length;
|
|
19
|
+
const paths = chunks.map((chunk, index) => {
|
|
20
|
+
const partPath = formatSplitOutputPath(outputPath, index + 1, totalParts);
|
|
21
|
+
writeFileSync(partPath, wrapSplitChunk(index + 1, totalParts, chunk), "utf-8");
|
|
22
|
+
return partPath;
|
|
23
|
+
});
|
|
24
|
+
return { paths, splitChunkSize };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACX,cAAc,GACf,MAAM,mBAAmB,CAAA;AAE1B;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAAe,EACf,UAAkB,EAClB,aAAsB;IAEtB,MAAM,cAAc,GAAG,qBAAqB,CAAC,OAAO,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IAC3E,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;QAC3C,OAAO,EAAE,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,CAAA;IAChC,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,EAAE,cAAc,CAAC,CAAA;IACnD,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAA;IAChC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACxC,MAAM,QAAQ,GAAG,qBAAqB,CAAC,UAAU,EAAE,KAAK,GAAG,CAAC,EAAE,UAAU,CAAC,CAAA;QACzE,aAAa,CAAC,QAAQ,EAAE,cAAc,CAAC,KAAK,GAAG,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,CAAA;QAC9E,OAAO,QAAQ,CAAA;IACjB,CAAC,CAAC,CAAA;IACF,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,CAAA;AAClC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module cli/paths
|
|
3
|
+
*
|
|
4
|
+
* Output path resolution for CLI commands.
|
|
5
|
+
*/
|
|
6
|
+
import type { Args } from "./args.js";
|
|
7
|
+
/**
|
|
8
|
+
* Derive an output path from input args or use an explicit `-o` value.
|
|
9
|
+
*
|
|
10
|
+
* Prevents accidentally overwriting the input file when defaults collide.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveOutputPath(args: Args, fallbackName: string, suffix: string): string;
|
|
13
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/cli/paths.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAErC;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAe1F"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module cli/paths
|
|
3
|
+
*
|
|
4
|
+
* Output path resolution for CLI commands.
|
|
5
|
+
*/
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
/**
|
|
8
|
+
* Derive an output path from input args or use an explicit `-o` value.
|
|
9
|
+
*
|
|
10
|
+
* Prevents accidentally overwriting the input file when defaults collide.
|
|
11
|
+
*/
|
|
12
|
+
export function resolveOutputPath(args, fallbackName, suffix) {
|
|
13
|
+
if (args.output)
|
|
14
|
+
return args.output;
|
|
15
|
+
const inputPath = args.dir ?? args.file;
|
|
16
|
+
const defaultPath = inputPath
|
|
17
|
+
? inputPath.replace(/\.[^/.]+$/, "").replace(/[/\\]+$/, "") + suffix
|
|
18
|
+
: fallbackName;
|
|
19
|
+
if (inputPath && resolve(defaultPath) === resolve(inputPath)) {
|
|
20
|
+
throw new Error(`Default output path "${defaultPath}" would overwrite the input. Specify -o <output path> explicitly.`);
|
|
21
|
+
}
|
|
22
|
+
return defaultPath;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/cli/paths.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGnC;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAU,EAAE,YAAoB,EAAE,MAAc;IAChF,IAAI,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC,MAAM,CAAA;IAEnC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAA;IACvC,MAAM,WAAW,GAAG,SAAS;QAC3B,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,GAAG,MAAM;QACpE,CAAC,CAAC,YAAY,CAAA;IAEhB,IAAI,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CACb,wBAAwB,WAAW,mEAAmE,CACvG,CAAA;IACH,CAAC;IAED,OAAO,WAAW,CAAA;AACpB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/cli/usage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,mCAAmC;AACnC,wBAAgB,UAAU,SAiEzB"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module cli/usage
|
|
3
|
+
*
|
|
4
|
+
* Help text for the `text-compress` CLI.
|
|
5
|
+
*/
|
|
6
|
+
/** Print usage guide to stdout. */
|
|
7
|
+
export function printUsage() {
|
|
8
|
+
console.log(`
|
|
9
|
+
text-compress - brotli (max quality) compress/decompress with base64 or base85 output
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
text-compress [path|options]
|
|
13
|
+
npx text-compress <path> [options]
|
|
14
|
+
|
|
15
|
+
Auto-detect:
|
|
16
|
+
Pass a file or folder path with no subcommand. Plain files and folders are
|
|
17
|
+
compressed; valid compressed payloads are decompressed automatically.
|
|
18
|
+
Folders are always compressed.
|
|
19
|
+
|
|
20
|
+
Options:
|
|
21
|
+
<path> Input path (auto-detects file vs folder)
|
|
22
|
+
-t, --text <string> Input given directly as a string
|
|
23
|
+
-f, --file <path> Input file (optional; same as passing <path>)
|
|
24
|
+
-d, --dir <path> Input folder (optional; same as passing <path>)
|
|
25
|
+
-C, --compress Always compress (even if input looks compressed)
|
|
26
|
+
-D, --decompress Always decompress
|
|
27
|
+
-o, --output <path> Output path (optional, see defaults below)
|
|
28
|
+
-s, --split <chars> Split compressed output into multiple files, each
|
|
29
|
+
at most this many characters (compress only).
|
|
30
|
+
If omitted, auto-splits at 30,000 characters when
|
|
31
|
+
the output is larger. Parts are named by inserting
|
|
32
|
+
.NNN before the extension, e.g. output.001.txt
|
|
33
|
+
-e, --encoding <64|85> Text encoding for the compressed output (default: 64)
|
|
34
|
+
64: standard base64 [A-Za-z0-9+/=] — safe to
|
|
35
|
+
paste literally anywhere (chat, email, etc.)
|
|
36
|
+
85: Z85 base85 — ~8% smaller, but uses extra
|
|
37
|
+
punctuation; only paste it somewhere that
|
|
38
|
+
preserves text verbatim (e.g. a code block)
|
|
39
|
+
-p, --password <string> Password-protect on compress, or unlock on decompress
|
|
40
|
+
-h, --help Show this usage guide
|
|
41
|
+
|
|
42
|
+
Split output (decompress):
|
|
43
|
+
Pass any one sibling file. All files sharing the same basename prefix
|
|
44
|
+
(segment before the first ".") are scanned; invalid siblings are skipped.
|
|
45
|
+
Part order comes from embedded headers, not filenames.
|
|
46
|
+
|
|
47
|
+
Defaults:
|
|
48
|
+
If -o is omitted, the output path is derived from the input's name:
|
|
49
|
+
compress (file/text): <input>.txt
|
|
50
|
+
compress (folder): <folder-name>.txt
|
|
51
|
+
decompress (text): <input>.de.txt
|
|
52
|
+
decompress (folder): <input>.de (recreated as a directory)
|
|
53
|
+
|
|
54
|
+
Examples:
|
|
55
|
+
text-compress notes.md
|
|
56
|
+
text-compress notes.md -p "my secret"
|
|
57
|
+
text-compress ./my-project
|
|
58
|
+
text-compress notes.md -e 85
|
|
59
|
+
text-compress notes.md -s 4000
|
|
60
|
+
text-compress output.txt
|
|
61
|
+
text-compress output.txt -p "my secret"
|
|
62
|
+
text-compress output.01.txt
|
|
63
|
+
text-compress -t "some text" -o output.txt
|
|
64
|
+
text-compress --compress notes.txt
|
|
65
|
+
npx text-compress ./somefile.md -p "hello-world"
|
|
66
|
+
|
|
67
|
+
Every run prints analytics (encoding, size, ratio, time taken) after
|
|
68
|
+
writing the output.
|
|
69
|
+
|
|
70
|
+
v1 CLI (@startdoing/tc) remains on npm at 1.0.4. v2 is published as text-compress.
|
|
71
|
+
`);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/cli/usage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,mCAAmC;AACnC,MAAM,UAAU,UAAU;IACxB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+Db,CAAC,CAAA;AACF,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI binary entry point (`text-compress` command).
|
|
4
|
+
*
|
|
5
|
+
* This thin file exists because `package.json` points `"bin"."text-compress"` at
|
|
6
|
+
* `dist/cli.js`. All logic lives under `src/cli/`.
|
|
7
|
+
*/
|
|
8
|
+
import { main } from "./cli/main.js";
|
|
9
|
+
main();
|
|
10
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAEpC,IAAI,EAAE,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module compression/brotli
|
|
3
|
+
*
|
|
4
|
+
* Brotli compression wrappers around Node.js `zlib`.
|
|
5
|
+
*
|
|
6
|
+
* ## Why Brotli?
|
|
7
|
+
*
|
|
8
|
+
* Brotli (RFC 7932) is a modern LZ77 + Huffman + context-modelling codec
|
|
9
|
+
* developed by Google. For text and source code it typically beats gzip and
|
|
10
|
+
* deflate by 15–25 % at comparable speed. We always use maximum quality
|
|
11
|
+
* because this tool optimises for smallest output, not speed.
|
|
12
|
+
*
|
|
13
|
+
* ## Parameters
|
|
14
|
+
*
|
|
15
|
+
* | Parameter | Value | Effect |
|
|
16
|
+
* |--------------------|--------------------|---------------------------------|
|
|
17
|
+
* | `QUALITY` | `BROTLI_MAX_QUALITY` (11) | Best ratio, slowest |
|
|
18
|
+
* | `LGWIN` | `BROTLI_MAX_WINDOW_BITS` | Largest LZ77 window |
|
|
19
|
+
* | `SIZE_HINT` | input byte length | Helps encoder pre-allocate |
|
|
20
|
+
*
|
|
21
|
+
* ## Sync vs streaming
|
|
22
|
+
*
|
|
23
|
+
* - `brotliCompress` — in-memory, used by the library API for text and
|
|
24
|
+
* small folder archives loaded entirely into RAM.
|
|
25
|
+
* - `createMaxQualityBrotliCompress` — streaming transform, used when
|
|
26
|
+
* compressing large folder archives without loading them into memory.
|
|
27
|
+
*
|
|
28
|
+
* @see https://github.com/google/brotli — reference implementation
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Compress a buffer synchronously (in-memory path).
|
|
32
|
+
*
|
|
33
|
+
* Suitable for text payloads and folder archives that fit in RAM.
|
|
34
|
+
*/
|
|
35
|
+
export declare function brotliCompress(input: Buffer): Buffer;
|
|
36
|
+
/**
|
|
37
|
+
* Create a streaming Brotli compressor transform.
|
|
38
|
+
*
|
|
39
|
+
* Used by the folder streaming pipeline: archive bytes flow through this
|
|
40
|
+
* transform into a file on disk without ever being fully buffered.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createMaxQualityBrotliCompress(sizeHint: number): import("node:zlib").BrotliCompress;
|
|
43
|
+
//# sourceMappingURL=brotli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brotli.d.ts","sourceRoot":"","sources":["../../src/compression/brotli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAaH;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,sCAE9D"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module compression/brotli
|
|
3
|
+
*
|
|
4
|
+
* Brotli compression wrappers around Node.js `zlib`.
|
|
5
|
+
*
|
|
6
|
+
* ## Why Brotli?
|
|
7
|
+
*
|
|
8
|
+
* Brotli (RFC 7932) is a modern LZ77 + Huffman + context-modelling codec
|
|
9
|
+
* developed by Google. For text and source code it typically beats gzip and
|
|
10
|
+
* deflate by 15–25 % at comparable speed. We always use maximum quality
|
|
11
|
+
* because this tool optimises for smallest output, not speed.
|
|
12
|
+
*
|
|
13
|
+
* ## Parameters
|
|
14
|
+
*
|
|
15
|
+
* | Parameter | Value | Effect |
|
|
16
|
+
* |--------------------|--------------------|---------------------------------|
|
|
17
|
+
* | `QUALITY` | `BROTLI_MAX_QUALITY` (11) | Best ratio, slowest |
|
|
18
|
+
* | `LGWIN` | `BROTLI_MAX_WINDOW_BITS` | Largest LZ77 window |
|
|
19
|
+
* | `SIZE_HINT` | input byte length | Helps encoder pre-allocate |
|
|
20
|
+
*
|
|
21
|
+
* ## Sync vs streaming
|
|
22
|
+
*
|
|
23
|
+
* - `brotliCompress` — in-memory, used by the library API for text and
|
|
24
|
+
* small folder archives loaded entirely into RAM.
|
|
25
|
+
* - `createMaxQualityBrotliCompress` — streaming transform, used when
|
|
26
|
+
* compressing large folder archives without loading them into memory.
|
|
27
|
+
*
|
|
28
|
+
* @see https://github.com/google/brotli — reference implementation
|
|
29
|
+
*/
|
|
30
|
+
import { brotliCompressSync, createBrotliCompress, constants as zlibConstants } from "node:zlib";
|
|
31
|
+
/** Shared Brotli parameters for maximum compression ratio. */
|
|
32
|
+
function maxQualityParams(sizeHint) {
|
|
33
|
+
return {
|
|
34
|
+
[zlibConstants.BROTLI_PARAM_QUALITY]: zlibConstants.BROTLI_MAX_QUALITY,
|
|
35
|
+
[zlibConstants.BROTLI_PARAM_LGWIN]: zlibConstants.BROTLI_MAX_WINDOW_BITS,
|
|
36
|
+
[zlibConstants.BROTLI_PARAM_SIZE_HINT]: sizeHint,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Compress a buffer synchronously (in-memory path).
|
|
41
|
+
*
|
|
42
|
+
* Suitable for text payloads and folder archives that fit in RAM.
|
|
43
|
+
*/
|
|
44
|
+
export function brotliCompress(input) {
|
|
45
|
+
return brotliCompressSync(input, { params: maxQualityParams(input.length) });
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create a streaming Brotli compressor transform.
|
|
49
|
+
*
|
|
50
|
+
* Used by the folder streaming pipeline: archive bytes flow through this
|
|
51
|
+
* transform into a file on disk without ever being fully buffered.
|
|
52
|
+
*/
|
|
53
|
+
export function createMaxQualityBrotliCompress(sizeHint) {
|
|
54
|
+
return createBrotliCompress({ params: maxQualityParams(sizeHint) });
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=brotli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brotli.js","sourceRoot":"","sources":["../../src/compression/brotli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,WAAW,CAAA;AAEhG,8DAA8D;AAC9D,SAAS,gBAAgB,CAAC,QAAgB;IACxC,OAAO;QACL,CAAC,aAAa,CAAC,oBAAoB,CAAC,EAAE,aAAa,CAAC,kBAAkB;QACtE,CAAC,aAAa,CAAC,kBAAkB,CAAC,EAAE,aAAa,CAAC,sBAAsB;QACxE,CAAC,aAAa,CAAC,sBAAsB,CAAC,EAAE,QAAQ;KACjD,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,kBAAkB,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAC9E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,8BAA8B,CAAC,QAAgB;IAC7D,OAAO,oBAAoB,CAAC,EAAE,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;AACrE,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module crypto/password
|
|
3
|
+
*
|
|
4
|
+
* Optional password protection for compressed payloads.
|
|
5
|
+
*
|
|
6
|
+
* When a password is set at compress time, the Brotli-compressed bytes are
|
|
7
|
+
* encrypted with AES-256-GCM before Base64/Z85 encoding. The wire format is:
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* [MAGIC: 4][salt: 16][iv: 12][authTag: 16][ciphertext…]
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Unencrypted payloads omit the magic prefix and remain compatible with
|
|
14
|
+
* earlier versions of the tool.
|
|
15
|
+
*/
|
|
16
|
+
/** True when decoded binary data starts with the password-protection magic. */
|
|
17
|
+
export declare function isEncrypted(data: Buffer): boolean;
|
|
18
|
+
/** Encrypt Brotli-compressed bytes with a user password. */
|
|
19
|
+
export declare function encryptBuffer(plaintext: Buffer, password: string): Buffer;
|
|
20
|
+
/** Decrypt password-protected bytes back to the original Brotli blob. */
|
|
21
|
+
export declare function decryptBuffer(data: Buffer, password: string): Buffer;
|
|
22
|
+
//# sourceMappingURL=password.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password.d.ts","sourceRoot":"","sources":["../../src/crypto/password.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAaH,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjD;AAMD,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQzE;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAoBpE"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module crypto/password
|
|
3
|
+
*
|
|
4
|
+
* Optional password protection for compressed payloads.
|
|
5
|
+
*
|
|
6
|
+
* When a password is set at compress time, the Brotli-compressed bytes are
|
|
7
|
+
* encrypted with AES-256-GCM before Base64/Z85 encoding. The wire format is:
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* [MAGIC: 4][salt: 16][iv: 12][authTag: 16][ciphertext…]
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Unencrypted payloads omit the magic prefix and remain compatible with
|
|
14
|
+
* earlier versions of the tool.
|
|
15
|
+
*/
|
|
16
|
+
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "node:crypto";
|
|
17
|
+
const MAGIC = Buffer.from("TCP\x01");
|
|
18
|
+
const SALT_LENGTH = 16;
|
|
19
|
+
const IV_LENGTH = 12;
|
|
20
|
+
const AUTH_TAG_LENGTH = 16;
|
|
21
|
+
const KEY_LENGTH = 32;
|
|
22
|
+
const SCRYPT_N = 16384;
|
|
23
|
+
const SCRYPT_R = 8;
|
|
24
|
+
const SCRYPT_P = 1;
|
|
25
|
+
/** True when decoded binary data starts with the password-protection magic. */
|
|
26
|
+
export function isEncrypted(data) {
|
|
27
|
+
return data.length >= MAGIC.length && data.subarray(0, MAGIC.length).equals(MAGIC);
|
|
28
|
+
}
|
|
29
|
+
function deriveKey(password, salt) {
|
|
30
|
+
return scryptSync(password, salt, KEY_LENGTH, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P });
|
|
31
|
+
}
|
|
32
|
+
/** Encrypt Brotli-compressed bytes with a user password. */
|
|
33
|
+
export function encryptBuffer(plaintext, password) {
|
|
34
|
+
const salt = randomBytes(SALT_LENGTH);
|
|
35
|
+
const iv = randomBytes(IV_LENGTH);
|
|
36
|
+
const key = deriveKey(password, salt);
|
|
37
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
38
|
+
const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
39
|
+
const authTag = cipher.getAuthTag();
|
|
40
|
+
return Buffer.concat([MAGIC, salt, iv, authTag, encrypted]);
|
|
41
|
+
}
|
|
42
|
+
/** Decrypt password-protected bytes back to the original Brotli blob. */
|
|
43
|
+
export function decryptBuffer(data, password) {
|
|
44
|
+
if (!isEncrypted(data)) {
|
|
45
|
+
throw new Error("This payload is not password-protected.");
|
|
46
|
+
}
|
|
47
|
+
const offset = MAGIC.length;
|
|
48
|
+
const salt = data.subarray(offset, offset + SALT_LENGTH);
|
|
49
|
+
const iv = data.subarray(offset + SALT_LENGTH, offset + SALT_LENGTH + IV_LENGTH);
|
|
50
|
+
const authTag = data.subarray(offset + SALT_LENGTH + IV_LENGTH, offset + SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);
|
|
51
|
+
const ciphertext = data.subarray(offset + SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);
|
|
52
|
+
const key = deriveKey(password, salt);
|
|
53
|
+
const decipher = createDecipheriv("aes-256-gcm", key, iv);
|
|
54
|
+
decipher.setAuthTag(authTag);
|
|
55
|
+
try {
|
|
56
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw new Error("Invalid password.");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=password.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password.js","sourceRoot":"","sources":["../../src/crypto/password.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module encoding/base64
|
|
3
|
+
*
|
|
4
|
+
* Thin wrapper around Node.js built-in Base64 encoding.
|
|
5
|
+
*
|
|
6
|
+
* Base64 maps every 3 bytes → 4 ASCII characters using a 64-character
|
|
7
|
+
* alphabet (`A–Z a–z 0–9 + /`) plus `=` padding. It expands data by
|
|
8
|
+
* ~33 % but is universally supported and paste-safe.
|
|
9
|
+
*
|
|
10
|
+
* We delegate to `Buffer.toString("base64")` rather than reimplementing
|
|
11
|
+
* the algorithm — Node's implementation is well-tested and SIMD-optimised.
|
|
12
|
+
*/
|
|
13
|
+
/** Encode raw bytes to a standard Base64 string. */
|
|
14
|
+
export declare function encodeBase64(buffer: Buffer): string;
|
|
15
|
+
/** Decode a Base64 string back to raw bytes. */
|
|
16
|
+
export declare function decodeBase64(str: string): Buffer;
|
|
17
|
+
//# sourceMappingURL=base64.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../../src/encoding/base64.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,oDAAoD;AACpD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,gDAAgD;AAChD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module encoding/base64
|
|
3
|
+
*
|
|
4
|
+
* Thin wrapper around Node.js built-in Base64 encoding.
|
|
5
|
+
*
|
|
6
|
+
* Base64 maps every 3 bytes → 4 ASCII characters using a 64-character
|
|
7
|
+
* alphabet (`A–Z a–z 0–9 + /`) plus `=` padding. It expands data by
|
|
8
|
+
* ~33 % but is universally supported and paste-safe.
|
|
9
|
+
*
|
|
10
|
+
* We delegate to `Buffer.toString("base64")` rather than reimplementing
|
|
11
|
+
* the algorithm — Node's implementation is well-tested and SIMD-optimised.
|
|
12
|
+
*/
|
|
13
|
+
/** Encode raw bytes to a standard Base64 string. */
|
|
14
|
+
export function encodeBase64(buffer) {
|
|
15
|
+
return buffer.toString("base64");
|
|
16
|
+
}
|
|
17
|
+
/** Decode a Base64 string back to raw bytes. */
|
|
18
|
+
export function decodeBase64(str) {
|
|
19
|
+
return Buffer.from(str, "base64");
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=base64.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base64.js","sourceRoot":"","sources":["../../src/encoding/base64.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,oDAAoD;AACpD,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;AAClC,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;AACnC,CAAC"}
|