zstdify-cli 1.0.2 → 1.1.1

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/README.md CHANGED
@@ -5,8 +5,18 @@
5
5
  [![Tests][tests-badge]][tests-url]
6
6
  [![Coverage][coverage-badge]][coverage-url]
7
7
 
8
+ ![Zstdify Logo](/logo.webp)
9
+
8
10
  Command-line tool for compressing and decompressing files with zstd. Built on the [zstdify](https://www.npmjs.com/package/zstdify) package (pure TypeScript, no native dependencies).
9
11
 
12
+ ## Features
13
+
14
+ - **Pure JS/TS zstd CLI**: No native addon dependency, portable across Node.js environments.
15
+ - **Compression + extraction workflows**: Simple file-to-file commands with level/checksum controls.
16
+ - **Interop-focused**: Files produced by `zstdify` are decoded by the official `zstd` CLI, and `zstd` output is decoded by `zstdify`.
17
+ - **Robust command UX**: Clear subcommands, aliases, and actionable error messages.
18
+ - **Optional dictionary support**: Train dictionaries from samples and use them with `compress`/`extract` when needed.
19
+
10
20
  ## Installation
11
21
 
12
22
  ```sh
@@ -19,6 +29,7 @@ pnpm add -g zstdify-cli
19
29
  | ------- | ----------- |
20
30
  | `zstdify compress <input> <output>` | Compress a file with zstd |
21
31
  | `zstdify extract <input> <output>` | Decompress a zstd-compressed file |
32
+ | `zstdify dict train <output> --input <path>...` | Train a dictionary from sample files/directories |
22
33
 
23
34
  Aliases: `compress` / `c`, `extract` / `x`.
24
35
 
@@ -26,6 +37,23 @@ Aliases: `compress` / `c`, `extract` / `x`.
26
37
 
27
38
  - `--level`, `-l` — Compression level (0=raw, 1+=RLE, 2+=compressed blocks)
28
39
  - `--checksum` — Add content checksum to the frame
40
+ - `--dict` — Dictionary file path to use for compression
41
+ - `--dictID` — Dictionary ID to store in the frame header
42
+ - `--noDictId` — Do not write dictID in frame header
43
+
44
+ ### Options (extract)
45
+
46
+ - `--dict` — Dictionary file path for dictionary-compressed input
47
+ - `--dictID` — Expected dictionary ID for validation
48
+
49
+ ### Options (dict train)
50
+
51
+ - `--recursive` — Recursively collect files from input directories
52
+ - `--maxSamples` — Maximum number of sample files to load
53
+ - `--algorithm` — `fastcover`, `cover`, or `legacy`
54
+ - `--maxdict` — Maximum dictionary size in bytes
55
+ - `--dictID` — Optional dictionary ID metadata setting
56
+ - Advanced tuning knobs: `--k`, `--d`, `--steps`, `--split`, `--f`, `--accel`, `--selectivity`, `--shrink`
29
57
 
30
58
  ## Examples
31
59
 
@@ -33,8 +61,32 @@ Aliases: `compress` / `c`, `extract` / `x`.
33
61
  zstdify compress input.txt output.zst
34
62
  zstdify compress input.txt output.zst --level 2
35
63
  zstdify extract output.zst restored.txt
64
+ zstdify dict train my.dict --input samples/ --recursive --maxdict 2048
65
+ zstdify compress input.txt output.zst --dict my.dict --dictID 42
66
+ zstdify extract output.zst restored.txt --dict my.dict --dictID 42
36
67
  ```
37
68
 
69
+ ## How we validate
70
+
71
+ CLI behavior is covered by automated tests (`pnpm vitest`, including `packages/cli-tests`):
72
+
73
+ - **CLI round-trip**: `zstdify compress` -> `zstdify extract` restores original file bytes.
74
+ - **Core flags and aliases**: Compression levels, checksums, and command aliases are exercised.
75
+ - **Differential interop with official zstd CLI**:
76
+ - `zstd` output is extracted by `zstdify-cli`.
77
+ - `zstdify-cli` output is decompressed by `zstd`.
78
+ - These checks run across standard (non-dictionary) workflows.
79
+ - **Dictionary interop coverage**:
80
+ - `zstd -D dict` compressed streams are extracted by `zstdify-cli --dict`.
81
+ - `zstdify-cli --dict` compressed streams are decompressed by `zstd -D dict`.
82
+ - Coverage includes both zstd-trained and zstdify-trained dictionaries.
83
+ - **Error paths**: Missing files and invalid inputs produce non-zero exits and actionable messages.
84
+
85
+ ## Acknowledgements
86
+
87
+ This project is made possible by the original [zstd](https://github.com/facebook/zstd) project by Meta and its contributors.
88
+ The monorepo, project, and CLI structure were bootstrapped from [hdrify](https://github.com/bhouston/hdrify), which made this project much easier to build.
89
+
38
90
  # License
39
91
 
40
92
  MIT
@@ -6,4 +6,10 @@ export declare const command: import("yargs-file-commands").DefineCommandResult<
6
6
  level: number | undefined;
7
7
  } & {
8
8
  checksum: boolean;
9
+ } & {
10
+ dict: string | undefined;
11
+ } & {
12
+ dictID: number | undefined;
13
+ } & {
14
+ noDictId: boolean;
9
15
  }>;
@@ -26,16 +26,40 @@ export const command = defineCommand({
26
26
  describe: 'Add content checksum to the frame',
27
27
  type: 'boolean',
28
28
  default: false,
29
+ })
30
+ .option('dict', {
31
+ describe: 'Dictionary file path to use for compression',
32
+ type: 'string',
33
+ })
34
+ .option('dictID', {
35
+ describe: 'Dictionary ID to store in frame header',
36
+ type: 'number',
37
+ })
38
+ .option('noDictId', {
39
+ describe: "Don't write dictID into frame header",
40
+ type: 'boolean',
41
+ default: false,
29
42
  }),
30
43
  handler: async (argv) => {
31
- const { input, output, level, checksum } = argv;
44
+ const { input, output, level, checksum, dict, dictID, noDictId } = argv;
32
45
  if (!fs.existsSync(input)) {
33
46
  console.error(`Error: Input file not found: ${input}`);
34
47
  process.exit(1);
35
48
  }
49
+ if (dict && !fs.existsSync(dict)) {
50
+ console.error(`Error: Dictionary file not found: ${dict}`);
51
+ process.exit(1);
52
+ }
36
53
  try {
37
54
  const inputBuf = fs.readFileSync(input);
38
55
  const data = new Uint8Array(inputBuf.buffer, inputBuf.byteOffset, inputBuf.byteLength);
56
+ const dictionary = dict
57
+ ? (() => {
58
+ const dictBuf = fs.readFileSync(dict);
59
+ const bytes = new Uint8Array(dictBuf.buffer, dictBuf.byteOffset, dictBuf.byteLength);
60
+ return dictID !== undefined ? { bytes, id: dictID } : bytes;
61
+ })()
62
+ : undefined;
39
63
  const outDir = path.dirname(output);
40
64
  if (outDir) {
41
65
  fs.mkdirSync(outDir, { recursive: true });
@@ -43,6 +67,8 @@ export const command = defineCommand({
43
67
  const result = compress(data, {
44
68
  level,
45
69
  checksum,
70
+ dictionary,
71
+ noDictId,
46
72
  });
47
73
  fs.writeFileSync(output, result);
48
74
  console.log(`Compressed ${input} -> ${output}`);
@@ -1 +1 @@
1
- {"version":3,"file":"compress.js","sourceRoot":"","sources":["../../src/commands/compress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;IACnC,OAAO,EAAE,2BAA2B;IACpC,QAAQ,EAAE,2BAA2B;IACrC,OAAO,EAAE,CAAC,GAAG,CAAC;IACd,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,UAAU,CAAC,OAAO,EAAE;QACnB,QAAQ,EAAE,iBAAiB;QAC3B,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;KACnB,CAAC;SACD,UAAU,CAAC,QAAQ,EAAE;QACpB,QAAQ,EAAE,gCAAgC;QAC1C,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;KACnB,CAAC;SACD,MAAM,CAAC,OAAO,EAAE;QACf,QAAQ,EAAE,yDAAyD;QACnE,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,GAAG;KACX,CAAC;SACD,MAAM,CAAC,UAAU,EAAE;QAClB,QAAQ,EAAE,mCAAmC;QAC7C,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,KAAK;KACf,CAAC;IACN,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;QACvB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;QAEhD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,KAAK,CAAC,gCAAgC,KAAK,EAAE,CAAC,CAAC;YACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YACxC,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;YAEvF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACpC,IAAI,MAAM,EAAE,CAAC;gBACX,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5C,CAAC;YAED,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE;gBAC5B,KAAK;gBACL,QAAQ;aACT,CAAC,CAAC;YACH,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,OAAO,MAAM,EAAE,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAC3F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IAAA,CACF;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"compress.js","sourceRoot":"","sources":["../../src/commands/compress.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ export declare const command: import("yargs-file-commands").DefineCommandResult<{}, {
2
+ output: string;
3
+ } & {
4
+ input: string[];
5
+ } & {
6
+ recursive: boolean;
7
+ } & {
8
+ maxSamples: number;
9
+ } & {
10
+ algorithm: string;
11
+ } & {
12
+ maxdict: number | undefined;
13
+ } & {
14
+ dictID: number | undefined;
15
+ } & {
16
+ k: number | undefined;
17
+ } & {
18
+ d: number | undefined;
19
+ } & {
20
+ steps: number | undefined;
21
+ } & {
22
+ split: number | undefined;
23
+ } & {
24
+ f: number | undefined;
25
+ } & {
26
+ accel: number | undefined;
27
+ } & {
28
+ selectivity: number | undefined;
29
+ } & {
30
+ shrink: unknown;
31
+ }>;
@@ -0,0 +1,129 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { defineCommand } from 'yargs-file-commands';
4
+ import { generateDictionary } from 'zstdify';
5
+ function collectFiles(inputPath, recursive, out) {
6
+ const stat = fs.statSync(inputPath);
7
+ if (stat.isFile()) {
8
+ out.push(inputPath);
9
+ return;
10
+ }
11
+ if (!stat.isDirectory()) {
12
+ return;
13
+ }
14
+ const entries = fs.readdirSync(inputPath, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
15
+ for (const entry of entries) {
16
+ const fullPath = path.join(inputPath, entry.name);
17
+ if (entry.isFile()) {
18
+ out.push(fullPath);
19
+ }
20
+ else if (entry.isDirectory() && recursive) {
21
+ collectFiles(fullPath, recursive, out);
22
+ }
23
+ }
24
+ }
25
+ export const command = defineCommand({
26
+ command: 'dict train <output>',
27
+ describe: 'Train and write a dictionary from sample files',
28
+ builder: (yargs) => yargs
29
+ .positional('output', {
30
+ describe: 'Output dictionary file path',
31
+ type: 'string',
32
+ demandOption: true,
33
+ })
34
+ .option('input', {
35
+ describe: 'Input sample file or directory path (repeat for multiple)',
36
+ type: 'string',
37
+ array: true,
38
+ alias: 'i',
39
+ demandOption: true,
40
+ })
41
+ .option('recursive', {
42
+ describe: 'Recursively read files inside input directories',
43
+ type: 'boolean',
44
+ default: false,
45
+ })
46
+ .option('maxSamples', {
47
+ describe: 'Maximum number of sample files to load',
48
+ type: 'number',
49
+ default: 10_000,
50
+ })
51
+ .option('algorithm', {
52
+ describe: 'Training algorithm style',
53
+ choices: ['fastcover', 'cover', 'legacy'],
54
+ default: 'fastcover',
55
+ })
56
+ .option('maxdict', {
57
+ describe: 'Maximum dictionary size in bytes',
58
+ type: 'number',
59
+ })
60
+ .option('dictID', {
61
+ describe: 'Dictionary ID value (for metadata parity with zstd)',
62
+ type: 'number',
63
+ })
64
+ .option('k', { type: 'number', describe: 'Candidate segment size' })
65
+ .option('d', { type: 'number', describe: 'Distance step between candidate probes' })
66
+ .option('steps', { type: 'number', describe: 'Score refinement passes' })
67
+ .option('split', { type: 'number', describe: 'Percent of each sample to use (1-100)' })
68
+ .option('f', { type: 'number', describe: 'fastcover-style score multiplier' })
69
+ .option('accel', { type: 'number', describe: 'Probe stride accelerator (1-10)' })
70
+ .option('selectivity', { type: 'number', describe: 'legacy-style density control (1-10)' })
71
+ .option('shrink', {
72
+ describe: 'Optional shrink pass, true or numeric factor',
73
+ }),
74
+ handler: async (argv) => {
75
+ const { output, input, recursive, maxSamples, algorithm, maxdict, dictID, k, d, steps, split, f, accel, selectivity, shrink, } = argv;
76
+ const inputPaths = input ?? [];
77
+ const files = [];
78
+ for (const inputPath of inputPaths) {
79
+ if (!fs.existsSync(inputPath)) {
80
+ console.error(`Error: Input path not found: ${inputPath}`);
81
+ process.exit(1);
82
+ }
83
+ collectFiles(inputPath, recursive, files);
84
+ if (files.length >= maxSamples)
85
+ break;
86
+ }
87
+ if (files.length === 0) {
88
+ console.error('Error: No sample files found for dictionary training');
89
+ process.exit(1);
90
+ }
91
+ const selectedFiles = files.slice(0, maxSamples);
92
+ try {
93
+ const samples = selectedFiles
94
+ .map((filePath) => {
95
+ const buf = fs.readFileSync(filePath);
96
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
97
+ })
98
+ .filter((sample) => sample.length > 0);
99
+ if (samples.length === 0) {
100
+ console.error('Error: No non-empty sample files found');
101
+ process.exit(1);
102
+ }
103
+ const dictionary = generateDictionary(samples, {
104
+ algorithm: algorithm,
105
+ maxDictSize: maxdict,
106
+ dictId: dictID,
107
+ k,
108
+ d,
109
+ steps,
110
+ split,
111
+ f,
112
+ accel,
113
+ selectivity,
114
+ shrink: typeof shrink === 'string' ? Number(shrink) : shrink,
115
+ });
116
+ const outDir = path.dirname(output);
117
+ if (outDir) {
118
+ fs.mkdirSync(outDir, { recursive: true });
119
+ }
120
+ fs.writeFileSync(output, dictionary);
121
+ console.log(`Trained dictionary (${dictionary.length} bytes) -> ${output}`);
122
+ }
123
+ catch (error) {
124
+ console.error(`Error during dictionary training:`, error instanceof Error ? error.message : error);
125
+ process.exit(1);
126
+ }
127
+ },
128
+ });
129
+ //# sourceMappingURL=dict.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dict.js","sourceRoot":"","sources":["../../src/commands/dict.ts"],"names":[],"mappings":"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"}
@@ -2,4 +2,8 @@ export declare const command: import("yargs-file-commands").DefineCommandResult<
2
2
  input: string;
3
3
  } & {
4
4
  output: string;
5
+ } & {
6
+ dict: string | undefined;
7
+ } & {
8
+ dictID: number | undefined;
5
9
  }>;
@@ -16,21 +16,40 @@ export const command = defineCommand({
16
16
  describe: 'Output file path',
17
17
  type: 'string',
18
18
  demandOption: true,
19
+ })
20
+ .option('dict', {
21
+ describe: 'Dictionary file path for dictionary-compressed input',
22
+ type: 'string',
23
+ })
24
+ .option('dictID', {
25
+ describe: 'Expected dictionary ID for validation',
26
+ type: 'number',
19
27
  }),
20
28
  handler: async (argv) => {
21
- const { input, output } = argv;
29
+ const { input, output, dict, dictID } = argv;
22
30
  if (!fs.existsSync(input)) {
23
31
  console.error(`Error: Input file not found: ${input}`);
24
32
  process.exit(1);
25
33
  }
34
+ if (dict && !fs.existsSync(dict)) {
35
+ console.error(`Error: Dictionary file not found: ${dict}`);
36
+ process.exit(1);
37
+ }
26
38
  try {
27
39
  const inputBuf = fs.readFileSync(input);
28
40
  const data = new Uint8Array(inputBuf.buffer, inputBuf.byteOffset, inputBuf.byteLength);
41
+ const dictionary = dict
42
+ ? (() => {
43
+ const dictBuf = fs.readFileSync(dict);
44
+ const bytes = new Uint8Array(dictBuf.buffer, dictBuf.byteOffset, dictBuf.byteLength);
45
+ return dictID !== undefined ? { bytes, id: dictID } : bytes;
46
+ })()
47
+ : undefined;
29
48
  const outDir = path.dirname(output);
30
49
  if (outDir) {
31
50
  fs.mkdirSync(outDir, { recursive: true });
32
51
  }
33
- const result = decompress(data);
52
+ const result = decompress(data, dictionary ? { dictionary } : undefined);
34
53
  fs.writeFileSync(output, result);
35
54
  console.log(`Decompressed ${input} -> ${output}`);
36
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/commands/extract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;IACnC,OAAO,EAAE,0BAA0B;IACnC,QAAQ,EAAE,mCAAmC;IAC7C,OAAO,EAAE,CAAC,GAAG,CAAC;IACd,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,UAAU,CAAC,OAAO,EAAE;QACnB,QAAQ,EAAE,4BAA4B;QACtC,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;KACnB,CAAC;SACD,UAAU,CAAC,QAAQ,EAAE;QACpB,QAAQ,EAAE,kBAAkB;QAC5B,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;KACnB,CAAC;IACN,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;QACvB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAE/B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,KAAK,CAAC,gCAAgC,KAAK,EAAE,CAAC,CAAC;YACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YACxC,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;YAEvF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACpC,IAAI,MAAM,EAAE,CAAC;gBACX,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5C,CAAC;YAED,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;YAChC,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,OAAO,MAAM,EAAE,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAC7F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IAAA,CACF;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/commands/extract.ts"],"names":[],"mappings":"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"}
package/logo.webp ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zstdify-cli",
3
- "version": "1.0.2",
3
+ "version": "1.1.1",
4
4
  "type": "module",
5
5
  "description": "CLI for zstd compression and decompression using zstdify",
6
6
  "license": "MIT",
@@ -17,7 +17,7 @@
17
17
  "dependencies": {
18
18
  "yargs": "^17.7.2",
19
19
  "yargs-file-commands": "^1.2.2",
20
- "zstdify": "^1.0.2"
20
+ "zstdify": "^1.1.1"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "^24",