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.
Files changed (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +186 -0
  3. package/dist/api/folder.d.ts +38 -0
  4. package/dist/api/folder.d.ts.map +1 -0
  5. package/dist/api/folder.js +50 -0
  6. package/dist/api/folder.js.map +1 -0
  7. package/dist/api/text.d.ts +30 -0
  8. package/dist/api/text.d.ts.map +1 -0
  9. package/dist/api/text.js +38 -0
  10. package/dist/api/text.js.map +1 -0
  11. package/dist/archive/collect.d.ts +26 -0
  12. package/dist/archive/collect.d.ts.map +1 -0
  13. package/dist/archive/collect.js +38 -0
  14. package/dist/archive/collect.js.map +1 -0
  15. package/dist/archive/format.d.ts +62 -0
  16. package/dist/archive/format.d.ts.map +1 -0
  17. package/dist/archive/format.js +143 -0
  18. package/dist/archive/format.js.map +1 -0
  19. package/dist/archive/types.d.ts +25 -0
  20. package/dist/archive/types.d.ts.map +1 -0
  21. package/dist/archive/types.js +10 -0
  22. package/dist/archive/types.js.map +1 -0
  23. package/dist/archive/unpack.d.ts +21 -0
  24. package/dist/archive/unpack.d.ts.map +1 -0
  25. package/dist/archive/unpack.js +41 -0
  26. package/dist/archive/unpack.js.map +1 -0
  27. package/dist/cli/analytics.d.ts +10 -0
  28. package/dist/cli/analytics.d.ts.map +1 -0
  29. package/dist/cli/analytics.js +22 -0
  30. package/dist/cli/analytics.js.map +1 -0
  31. package/dist/cli/args.d.ts +43 -0
  32. package/dist/cli/args.d.ts.map +1 -0
  33. package/dist/cli/args.js +152 -0
  34. package/dist/cli/args.js.map +1 -0
  35. package/dist/cli/commands/compress.d.ts +9 -0
  36. package/dist/cli/commands/compress.d.ts.map +1 -0
  37. package/dist/cli/commands/compress.js +62 -0
  38. package/dist/cli/commands/compress.js.map +1 -0
  39. package/dist/cli/commands/decompress.d.ts +9 -0
  40. package/dist/cli/commands/decompress.d.ts.map +1 -0
  41. package/dist/cli/commands/decompress.js +69 -0
  42. package/dist/cli/commands/decompress.js.map +1 -0
  43. package/dist/cli/detect.d.ts +25 -0
  44. package/dist/cli/detect.d.ts.map +1 -0
  45. package/dist/cli/detect.js +78 -0
  46. package/dist/cli/detect.js.map +1 -0
  47. package/dist/cli/main.d.ts +8 -0
  48. package/dist/cli/main.d.ts.map +1 -0
  49. package/dist/cli/main.js +74 -0
  50. package/dist/cli/main.js.map +1 -0
  51. package/dist/cli/output.d.ts +13 -0
  52. package/dist/cli/output.d.ts.map +1 -0
  53. package/dist/cli/output.js +26 -0
  54. package/dist/cli/output.js.map +1 -0
  55. package/dist/cli/paths.d.ts +13 -0
  56. package/dist/cli/paths.d.ts.map +1 -0
  57. package/dist/cli/paths.js +24 -0
  58. package/dist/cli/paths.js.map +1 -0
  59. package/dist/cli/usage.d.ts +8 -0
  60. package/dist/cli/usage.d.ts.map +1 -0
  61. package/dist/cli/usage.js +73 -0
  62. package/dist/cli/usage.js.map +1 -0
  63. package/dist/cli.d.ts +3 -0
  64. package/dist/cli.d.ts.map +1 -0
  65. package/dist/cli.js +10 -0
  66. package/dist/cli.js.map +1 -0
  67. package/dist/compression/brotli.d.ts +43 -0
  68. package/dist/compression/brotli.d.ts.map +1 -0
  69. package/dist/compression/brotli.js +56 -0
  70. package/dist/compression/brotli.js.map +1 -0
  71. package/dist/crypto/password.d.ts +22 -0
  72. package/dist/crypto/password.d.ts.map +1 -0
  73. package/dist/crypto/password.js +62 -0
  74. package/dist/crypto/password.js.map +1 -0
  75. package/dist/encoding/base64.d.ts +17 -0
  76. package/dist/encoding/base64.d.ts.map +1 -0
  77. package/dist/encoding/base64.js +21 -0
  78. package/dist/encoding/base64.js.map +1 -0
  79. package/dist/encoding/index.d.ts +29 -0
  80. package/dist/encoding/index.d.ts.map +1 -0
  81. package/dist/encoding/index.js +47 -0
  82. package/dist/encoding/index.js.map +1 -0
  83. package/dist/encoding/z85.d.ts +64 -0
  84. package/dist/encoding/z85.d.ts.map +1 -0
  85. package/dist/encoding/z85.js +99 -0
  86. package/dist/encoding/z85.js.map +1 -0
  87. package/dist/fs/gitignore.d.ts +30 -0
  88. package/dist/fs/gitignore.d.ts.map +1 -0
  89. package/dist/fs/gitignore.js +104 -0
  90. package/dist/fs/gitignore.js.map +1 -0
  91. package/dist/fs/paths.d.ts +22 -0
  92. package/dist/fs/paths.d.ts.map +1 -0
  93. package/dist/fs/paths.js +55 -0
  94. package/dist/fs/paths.js.map +1 -0
  95. package/dist/fs/walk.d.ts +22 -0
  96. package/dist/fs/walk.d.ts.map +1 -0
  97. package/dist/fs/walk.js +49 -0
  98. package/dist/fs/walk.js.map +1 -0
  99. package/dist/index.d.ts +21 -0
  100. package/dist/index.d.ts.map +1 -0
  101. package/dist/index.js +25 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/payload/tags.d.ts +60 -0
  104. package/dist/payload/tags.d.ts.map +1 -0
  105. package/dist/payload/tags.js +78 -0
  106. package/dist/payload/tags.js.map +1 -0
  107. package/dist/split/parts.d.ts +130 -0
  108. package/dist/split/parts.d.ts.map +1 -0
  109. package/dist/split/parts.js +290 -0
  110. package/dist/split/parts.js.map +1 -0
  111. package/dist/streaming/folder.d.ts +49 -0
  112. package/dist/streaming/folder.d.ts.map +1 -0
  113. package/dist/streaming/folder.js +220 -0
  114. package/dist/streaming/folder.js.map +1 -0
  115. package/dist/streaming.d.ts +16 -0
  116. package/dist/streaming.d.ts.map +1 -0
  117. package/dist/streaming.js +243 -0
  118. package/dist/streaming.js.map +1 -0
  119. package/dist/types.d.ts +18 -0
  120. package/dist/types.d.ts.map +1 -0
  121. package/dist/types.js +11 -0
  122. package/dist/types.js.map +1 -0
  123. 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,8 @@
1
+ /**
2
+ * @module cli/main
3
+ *
4
+ * CLI entry logic — parses argv and auto-routes to compress or decompress.
5
+ */
6
+ /** Main CLI dispatcher. */
7
+ export declare function main(): void;
8
+ //# sourceMappingURL=main.d.ts.map
@@ -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"}
@@ -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,8 @@
1
+ /**
2
+ * @module cli/usage
3
+ *
4
+ * Help text for the `text-compress` CLI.
5
+ */
6
+ /** Print usage guide to stdout. */
7
+ export declare function printUsage(): void;
8
+ //# sourceMappingURL=usage.d.ts.map
@@ -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,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -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
@@ -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":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEvF,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;AACpC,MAAM,WAAW,GAAG,EAAE,CAAA;AACtB,MAAM,SAAS,GAAG,EAAE,CAAA;AACpB,MAAM,eAAe,GAAG,EAAE,CAAA;AAC1B,MAAM,UAAU,GAAG,EAAE,CAAA;AACrB,MAAM,QAAQ,GAAG,KAAK,CAAA;AACtB,MAAM,QAAQ,GAAG,CAAC,CAAA;AAClB,MAAM,QAAQ,GAAG,CAAC,CAAA;AAElB,+EAA+E;AAC/E,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACpF,CAAC;AAED,SAAS,SAAS,CAAC,QAAgB,EAAE,IAAY;IAC/C,OAAO,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;AAC1F,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAE,QAAgB;IAC/D,MAAM,IAAI,GAAG,WAAW,CAAC,WAAW,CAAC,CAAA;IACrC,MAAM,EAAE,GAAG,WAAW,CAAC,SAAS,CAAC,CAAA;IACjC,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;IACrC,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;IACrD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IAC3E,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAA;IACnC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAA;AAC7D,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,QAAgB;IAC1D,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAA;IAC5D,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;IAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC,CAAA;IACxD,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,WAAW,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC,CAAA;IAChF,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAC3B,MAAM,GAAG,WAAW,GAAG,SAAS,EAChC,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,eAAe,CACnD,CAAA;IACD,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,eAAe,CAAC,CAAA;IACpF,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;IACrC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;IACzD,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;IAC5B,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAA;IACtC,CAAC;AACH,CAAC"}
@@ -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"}