@bcts/lifehash-cli 1.0.0-alpha.17

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 ADDED
@@ -0,0 +1,48 @@
1
+ Copyright © 2025 Blockchain Commons, LLC
2
+ Copyright © 2025-2026 Leonardo Amoroso Custodio
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ Subject to the terms and conditions of this license, each copyright holder and
15
+ contributor hereby grants to those receiving rights under this license a
16
+ perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
17
+ (except for failure to satisfy the conditions of this license) patent license to
18
+ make, have made, use, offer to sell, sell, import, and otherwise transfer this
19
+ software, where such license applies only to those patent claims, already
20
+ acquired or hereafter acquired, licensable by such copyright holder or
21
+ contributor that are necessarily infringed by:
22
+
23
+ (a) their Contribution(s) (the licensed copyrights of copyright holders and
24
+ non-copyrightable additions of contributors, in source or binary form)
25
+ alone; or
26
+
27
+ (b) combination of their Contribution(s) with the work of authorship to
28
+ which such Contribution(s) was added by such copyright holder or
29
+ contributor, if, at the time the Contribution is added, such addition causes
30
+ such combination to be necessarily infringed. The patent license shall not
31
+ apply to any other combinations which include the Contribution.
32
+
33
+ Except as expressly stated above, no rights or licenses from any copyright
34
+ holder or contributor is granted under this license, whether expressly, by
35
+ implication, estoppel or otherwise.
36
+
37
+ DISCLAIMER
38
+
39
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
40
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
41
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
43
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
44
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
45
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
46
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
47
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
48
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # Blockchain Commons LifeHash CLI (TypeScript)
2
+
3
+ > Disclaimer: This package is under active development and APIs may change.
4
+
5
+ ## Introduction
6
+
7
+ `@bcts/lifehash-cli` is a command line tool for generating [LifeHash](https://github.com/BlockchainCommons/bc-lifehash) images as PNG files. LifeHash is a beautiful method of hash visualization based on Conway's Game of Life.
8
+
9
+ Features:
10
+ - Generate LifeHash PNG images from any input string
11
+ - Support for all 5 LifeHash versions (version1, version2, detailed, fiducial, grayscaleFiducial)
12
+ - Configurable module (pixel) size for image scaling
13
+ - Automatic random input generation if none provided
14
+ - Cross-platform (Node.js/Bun)
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ # Install globally
20
+ bun add -g @bcts/lifehash-cli
21
+
22
+ # Or run directly with bunx
23
+ bunx @bcts/lifehash-cli --help
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ### Generate a LifeHash
29
+
30
+ ```bash
31
+ # Generate a version2 (default), 32x32 pixel LifeHash from the string "Hello"
32
+ lifehash Hello
33
+
34
+ # Output: Hello.png
35
+ ```
36
+
37
+ ### Specify Version
38
+
39
+ ```bash
40
+ # Generate a version1 LifeHash (deprecated HSB colors)
41
+ lifehash -v version1 Hello
42
+
43
+ # Generate a detailed (64x64) LifeHash
44
+ lifehash -v detailed Hello
45
+
46
+ # Generate a fiducial LifeHash (for machine vision)
47
+ lifehash -v fiducial Hello
48
+
49
+ # Generate a grayscale fiducial LifeHash
50
+ lifehash -v grayscaleFiducial Hello
51
+ ```
52
+
53
+ ### Module Size (Scaling)
54
+
55
+ ```bash
56
+ # Generate a 256x256 image (32x32 with module size 8)
57
+ lifehash -m 8 Hello
58
+
59
+ # Generate a 512x512 detailed image (64x64 with module size 8)
60
+ lifehash -v detailed -m 8 Hello
61
+ ```
62
+
63
+ ### Output Path
64
+
65
+ ```bash
66
+ # Save to a specific directory
67
+ lifehash -p ./output Hello
68
+
69
+ # Output: ./output/Hello.png
70
+ ```
71
+
72
+ ### Random Input
73
+
74
+ ```bash
75
+ # Generate with a random input of the form "XXX-XXX"
76
+ lifehash
77
+
78
+ # Output: ABC-DEF.png (random letters)
79
+ ```
80
+
81
+ ## Command Line Reference
82
+
83
+ ```
84
+ Usage: lifehash [options] [input]
85
+
86
+ Arguments:
87
+ input Input string to hash (default: random XXX-XXX)
88
+
89
+ Options:
90
+ -v, --version <version> LifeHash version: version1, version2, detailed, fiducial, grayscaleFiducial (default: "version2")
91
+ -m, --module <size> Size of each module ("pixel") (default: 1)
92
+ -p, --path <path> Output directory path (default: current directory)
93
+ -h, --help Display help
94
+ -V, --version Display version number
95
+ ```
96
+
97
+ ## LifeHash Versions
98
+
99
+ | Version | Size | Description |
100
+ |---------|------|-------------|
101
+ | `version1` | 32x32 | Original version using HSB color space (deprecated) |
102
+ | `version2` | 32x32 | Default version using CMYK-safe colors |
103
+ | `detailed` | 64x64 | Higher resolution version |
104
+ | `fiducial` | 64x64 | Optimized for machine vision applications |
105
+ | `grayscaleFiducial` | 64x64 | Grayscale version for machine vision |
106
+
107
+ ## C++ Reference Implementation
108
+
109
+ This TypeScript implementation is based on [bc-lifehash-cli](https://github.com/BlockchainCommons/bc-lifehash-cli).
package/dist/index.cjs ADDED
@@ -0,0 +1,317 @@
1
+ let fs = require("fs");
2
+ let pngjs = require("pngjs");
3
+ let commander = require("commander");
4
+ let _bcts_lifehash = require("@bcts/lifehash");
5
+
6
+ //#region src/png-writer.ts
7
+ /**
8
+ * PNG writer for LifeHash images.
9
+ *
10
+ * Ported from bc-lifehash-cli C++ implementation (png-writer.hpp).
11
+ * Uses pngjs instead of libpng.
12
+ *
13
+ * @module
14
+ */
15
+ /**
16
+ * Writes a LifeHash image to a PNG file.
17
+ *
18
+ * Port of `write_image()` from lifehash.cpp lines 174-186 and
19
+ * PNGWriter class from png-writer.hpp.
20
+ *
21
+ * @param image - The LifeHash image to write
22
+ * @param filename - The output filename
23
+ * @category PNG Encoding
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * import { makeFromUtf8 } from "@bcts/lifehash";
28
+ *
29
+ * const image = makeFromUtf8("Hello");
30
+ * writeImage(image, "Hello.png");
31
+ * ```
32
+ */
33
+ function writeImage(image, filename) {
34
+ const png = new pngjs.PNG({
35
+ width: image.width,
36
+ height: image.height,
37
+ colorType: 2,
38
+ bitDepth: 8,
39
+ inputColorType: 2,
40
+ inputHasAlpha: false
41
+ });
42
+ for (let y = 0; y < image.height; y++) for (let x = 0; x < image.width; x++) {
43
+ const srcOffset = (y * image.width + x) * 3;
44
+ const dstOffset = (y * image.width + x) * 4;
45
+ png.data[dstOffset] = image.colors[srcOffset];
46
+ png.data[dstOffset + 1] = image.colors[srcOffset + 1];
47
+ png.data[dstOffset + 2] = image.colors[srcOffset + 2];
48
+ png.data[dstOffset + 3] = 255;
49
+ }
50
+ (0, fs.writeFileSync)(filename, pngjs.PNG.sync.write(png));
51
+ }
52
+ /**
53
+ * Generates a PNG buffer from a LifeHash image without writing to disk.
54
+ *
55
+ * @param image - The LifeHash image to encode
56
+ * @returns A Buffer containing the PNG data
57
+ * @category PNG Encoding
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * import { makeFromUtf8 } from "@bcts/lifehash";
62
+ *
63
+ * const image = makeFromUtf8("Hello");
64
+ * const pngBuffer = generatePNG(image);
65
+ * ```
66
+ */
67
+ function generatePNG(image) {
68
+ const png = new pngjs.PNG({
69
+ width: image.width,
70
+ height: image.height,
71
+ colorType: 2,
72
+ bitDepth: 8,
73
+ inputColorType: 2,
74
+ inputHasAlpha: false
75
+ });
76
+ for (let y = 0; y < image.height; y++) for (let x = 0; x < image.width; x++) {
77
+ const srcOffset = (y * image.width + x) * 3;
78
+ const dstOffset = (y * image.width + x) * 4;
79
+ png.data[dstOffset] = image.colors[srcOffset];
80
+ png.data[dstOffset + 1] = image.colors[srcOffset + 1];
81
+ png.data[dstOffset + 2] = image.colors[srcOffset + 2];
82
+ png.data[dstOffset + 3] = 255;
83
+ }
84
+ return pngjs.PNG.sync.write(png);
85
+ }
86
+
87
+ //#endregion
88
+ //#region src/utils.ts
89
+ /**
90
+ * Utility functions for the LifeHash CLI.
91
+ *
92
+ * Ported from bc-lifehash-cli C++ implementation.
93
+ *
94
+ * @module
95
+ */
96
+ /**
97
+ * Appends a path component to a path, handling trailing slashes correctly.
98
+ *
99
+ * Port of `appending_path_component()` from lifehash.cpp lines 18-24.
100
+ *
101
+ * @param path - The base path
102
+ * @param component - The component to append
103
+ * @returns The combined path
104
+ * @category Utilities
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * appendingPathComponent("", "file.png") // => "file.png"
109
+ * appendingPathComponent("/tmp/", "file.png") // => "/tmp/file.png"
110
+ * appendingPathComponent("/tmp", "file.png") // => "/tmp/file.png"
111
+ * ```
112
+ */
113
+ function appendingPathComponent(path, component) {
114
+ if (path === "") return component;
115
+ if (path.endsWith("/")) return `${path}${component}`;
116
+ return `${path}/${component}`;
117
+ }
118
+ /**
119
+ * Selects a random element from an array.
120
+ *
121
+ * Port of `random_element()` template from lifehash.cpp lines 38-50.
122
+ *
123
+ * @param array - The array to select from
124
+ * @returns A random element from the array
125
+ * @category Utilities
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * randomElement(["A", "B", "C"]) // => "A" or "B" or "C"
130
+ * ```
131
+ */
132
+ function randomElement(array) {
133
+ return array[Math.floor(Math.random() * array.length)];
134
+ }
135
+ /**
136
+ * Generates a random input string in "XXX-XXX" format where X is a random uppercase letter.
137
+ *
138
+ * Port of `make_random_input()` from lifehash.cpp lines 52-57.
139
+ *
140
+ * @returns A random string in "XXX-XXX" format
141
+ * @category Utilities
142
+ *
143
+ * @example
144
+ * ```typescript
145
+ * makeRandomInput() // => "ABC-DEF" (random letters)
146
+ * ```
147
+ */
148
+ function makeRandomInput() {
149
+ const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
150
+ const letter = () => randomElement(letters);
151
+ const cluster = () => `${letter()}${letter()}${letter()}`;
152
+ return `${cluster()}-${cluster()}`;
153
+ }
154
+
155
+ //#endregion
156
+ //#region src/main.ts
157
+ /**
158
+ * LifeHash CLI - Command line tool for generating LifeHash PNG images.
159
+ *
160
+ * Ported from bc-lifehash-cli C++ implementation (lifehash.cpp).
161
+ *
162
+ * @module
163
+ */
164
+ /**
165
+ * Parses a version string to the Version enum.
166
+ *
167
+ * Port of version parsing logic from lifehash.cpp lines 130-145.
168
+ *
169
+ * @param versionString - The version string from CLI
170
+ * @returns The corresponding Version enum value
171
+ * @throws Error if the version string is invalid
172
+ * @category CLI
173
+ *
174
+ * @example
175
+ * ```typescript
176
+ * parseVersion("version2") // => Version.version2
177
+ * parseVersion("detailed") // => Version.detailed
178
+ * parseVersion("invalid") // throws Error
179
+ * ```
180
+ */
181
+ function parseVersion(versionString) {
182
+ switch (versionString) {
183
+ case "version1": return _bcts_lifehash.Version.version1;
184
+ case "version2": return _bcts_lifehash.Version.version2;
185
+ case "detailed": return _bcts_lifehash.Version.detailed;
186
+ case "fiducial": return _bcts_lifehash.Version.fiducial;
187
+ case "grayscaleFiducial": return _bcts_lifehash.Version.grayscale_fiducial;
188
+ default: throw new Error("Invalid version.");
189
+ }
190
+ }
191
+ /**
192
+ * Main execution function that generates a LifeHash image.
193
+ *
194
+ * Port of `run()` from lifehash.cpp lines 188-192.
195
+ *
196
+ * @param input - Input string to hash (or empty for random)
197
+ * @param options - CLI options
198
+ * @category CLI
199
+ *
200
+ * @example
201
+ * ```typescript
202
+ * run("Hello", { version: "version2", module: "1", path: "." });
203
+ * // Generates Hello.png in current directory
204
+ * ```
205
+ */
206
+ function run(input, options) {
207
+ const version = parseVersion(options.version);
208
+ const moduleSize = parseInt(options.module, 10);
209
+ if (moduleSize < 1 || isNaN(moduleSize)) throw new Error("Illegal value.");
210
+ const actualInput = input !== "" ? input : makeRandomInput();
211
+ const outputFilename = `${actualInput}.png`;
212
+ const outputFile = appendingPathComponent(options.path, outputFilename);
213
+ writeImage((0, _bcts_lifehash.makeFromUtf8)(actualInput, version, moduleSize), outputFile);
214
+ }
215
+ /**
216
+ * CLI entry point.
217
+ *
218
+ * Port of `main()` from lifehash.cpp lines 194-206.
219
+ *
220
+ * @category CLI
221
+ */
222
+ function main() {
223
+ const program = new commander.Command();
224
+ program.name("lifehash").description("Generate LifeHash PNG images from input strings").argument("[input]", "Input string to hash (default: random XXX-XXX)").option("-v, --version <version>", "LifeHash version: version1, version2, detailed, fiducial, grayscaleFiducial", "version2").option("-m, --module <size>", "Size of each module (\"pixel\")", "1").option("-p, --path <path>", "Output directory path", "").action((input, options) => {
225
+ try {
226
+ run(input ?? "", options);
227
+ } catch (error) {
228
+ if (error instanceof Error) {
229
+ console.log(`\u{1F928} ${error.message}`);
230
+ console.log();
231
+ program.help();
232
+ }
233
+ process.exit(1);
234
+ }
235
+ });
236
+ program.parse();
237
+ }
238
+ if (typeof process !== "undefined" && typeof process.argv[1] === "string" && (process.argv[1].endsWith("/main.mjs") || process.argv[1].endsWith("/main.js") || process.argv[1].includes("lifehash-cli"))) main();
239
+
240
+ //#endregion
241
+ //#region src/index.ts
242
+ /**
243
+ * @bcts/lifehash-cli - Command line tool for generating LifeHash PNG images.
244
+ *
245
+ * This package provides both a CLI tool and a programmatic API for generating
246
+ * LifeHash visual hash images as PNG files.
247
+ *
248
+ * @packageDocumentation
249
+ * @module @bcts/lifehash-cli
250
+ *
251
+ * @example CLI Usage
252
+ * ```bash
253
+ * # Generate a LifeHash from a string
254
+ * lifehash Hello
255
+ *
256
+ * # Generate with specific version and module size
257
+ * lifehash -v detailed -m 8 Hello
258
+ *
259
+ * # Generate with random input
260
+ * lifehash
261
+ * ```
262
+ *
263
+ * @example Programmatic Usage
264
+ * ```typescript
265
+ * import { generateLifeHash } from "@bcts/lifehash-cli";
266
+ * import { writeFileSync } from "fs";
267
+ *
268
+ * // Generate a PNG buffer
269
+ * const pngBuffer = generateLifeHash("Hello", {
270
+ * version: "version2",
271
+ * moduleSize: 1,
272
+ * });
273
+ *
274
+ * // Write to file
275
+ * writeFileSync("Hello.png", pngBuffer);
276
+ * ```
277
+ */
278
+ /**
279
+ * Generates a LifeHash PNG buffer from an input string.
280
+ *
281
+ * This is the main programmatic API for generating LifeHash images.
282
+ *
283
+ * @param input - The input string to hash
284
+ * @param options - Generation options
285
+ * @returns A Buffer containing the PNG data
286
+ * @category Image Generation
287
+ *
288
+ * @example
289
+ * ```typescript
290
+ * import { generateLifeHash } from "@bcts/lifehash-cli";
291
+ * import { writeFileSync } from "fs";
292
+ *
293
+ * const pngBuffer = generateLifeHash("Hello", {
294
+ * version: "version2",
295
+ * moduleSize: 1,
296
+ * });
297
+ *
298
+ * writeFileSync("Hello.png", pngBuffer);
299
+ * ```
300
+ */
301
+ function generateLifeHash(input, options = {}) {
302
+ const { version = "version2", moduleSize = 1 } = options;
303
+ return generatePNG((0, _bcts_lifehash.makeFromUtf8)(input, parseVersion(version), moduleSize));
304
+ }
305
+
306
+ //#endregion
307
+ exports.Version = _bcts_lifehash.Version;
308
+ exports.appendingPathComponent = appendingPathComponent;
309
+ exports.generateLifeHash = generateLifeHash;
310
+ exports.generatePNG = generatePNG;
311
+ exports.makeFromUtf8 = _bcts_lifehash.makeFromUtf8;
312
+ exports.makeRandomInput = makeRandomInput;
313
+ exports.parseVersion = parseVersion;
314
+ exports.randomElement = randomElement;
315
+ exports.run = run;
316
+ exports.writeImage = writeImage;
317
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["PNG","Version","Command"],"sources":["../src/png-writer.ts","../src/utils.ts","../src/main.ts","../src/index.ts"],"sourcesContent":["/**\n * PNG writer for LifeHash images.\n *\n * Ported from bc-lifehash-cli C++ implementation (png-writer.hpp).\n * Uses pngjs instead of libpng.\n *\n * @module\n */\n\nimport { writeFileSync } from \"fs\";\nimport { PNG } from \"pngjs\";\nimport type { Image } from \"@bcts/lifehash\";\n\n/**\n * Writes a LifeHash image to a PNG file.\n *\n * Port of `write_image()` from lifehash.cpp lines 174-186 and\n * PNGWriter class from png-writer.hpp.\n *\n * @param image - The LifeHash image to write\n * @param filename - The output filename\n * @category PNG Encoding\n *\n * @example\n * ```typescript\n * import { makeFromUtf8 } from \"@bcts/lifehash\";\n *\n * const image = makeFromUtf8(\"Hello\");\n * writeImage(image, \"Hello.png\");\n * ```\n */\nexport function writeImage(image: Image, filename: string): void {\n const png = new PNG({\n width: image.width,\n height: image.height,\n colorType: 2, // RGB\n bitDepth: 8,\n inputColorType: 2,\n inputHasAlpha: false,\n });\n\n // Copy RGB data from image to PNG\n // PNG expects RGBA, so we need to add alpha channel\n for (let y = 0; y < image.height; y++) {\n for (let x = 0; x < image.width; x++) {\n const srcOffset = (y * image.width + x) * 3;\n const dstOffset = (y * image.width + x) * 4;\n\n // Copy RGB from source\n png.data[dstOffset] = image.colors[srcOffset]; // R\n png.data[dstOffset + 1] = image.colors[srcOffset + 1]; // G\n png.data[dstOffset + 2] = image.colors[srcOffset + 2]; // B\n png.data[dstOffset + 3] = 255; // A (fully opaque)\n }\n }\n\n // Write to file\n const buffer = PNG.sync.write(png);\n writeFileSync(filename, buffer);\n}\n\n/**\n * Generates a PNG buffer from a LifeHash image without writing to disk.\n *\n * @param image - The LifeHash image to encode\n * @returns A Buffer containing the PNG data\n * @category PNG Encoding\n *\n * @example\n * ```typescript\n * import { makeFromUtf8 } from \"@bcts/lifehash\";\n *\n * const image = makeFromUtf8(\"Hello\");\n * const pngBuffer = generatePNG(image);\n * ```\n */\nexport function generatePNG(image: Image): Buffer {\n const png = new PNG({\n width: image.width,\n height: image.height,\n colorType: 2,\n bitDepth: 8,\n inputColorType: 2,\n inputHasAlpha: false,\n });\n\n // Copy RGB data from image to PNG\n for (let y = 0; y < image.height; y++) {\n for (let x = 0; x < image.width; x++) {\n const srcOffset = (y * image.width + x) * 3;\n const dstOffset = (y * image.width + x) * 4;\n\n png.data[dstOffset] = image.colors[srcOffset];\n png.data[dstOffset + 1] = image.colors[srcOffset + 1];\n png.data[dstOffset + 2] = image.colors[srcOffset + 2];\n png.data[dstOffset + 3] = 255;\n }\n }\n\n return PNG.sync.write(png);\n}\n","/**\n * Utility functions for the LifeHash CLI.\n *\n * Ported from bc-lifehash-cli C++ implementation.\n *\n * @module\n */\n\n/**\n * Appends a path component to a path, handling trailing slashes correctly.\n *\n * Port of `appending_path_component()` from lifehash.cpp lines 18-24.\n *\n * @param path - The base path\n * @param component - The component to append\n * @returns The combined path\n * @category Utilities\n *\n * @example\n * ```typescript\n * appendingPathComponent(\"\", \"file.png\") // => \"file.png\"\n * appendingPathComponent(\"/tmp/\", \"file.png\") // => \"/tmp/file.png\"\n * appendingPathComponent(\"/tmp\", \"file.png\") // => \"/tmp/file.png\"\n * ```\n */\nexport function appendingPathComponent(path: string, component: string): string {\n if (path === \"\") {\n return component;\n }\n if (path.endsWith(\"/\")) {\n return `${path}${component}`;\n }\n return `${path}/${component}`;\n}\n\n/**\n * Selects a random element from an array.\n *\n * Port of `random_element()` template from lifehash.cpp lines 38-50.\n *\n * @param array - The array to select from\n * @returns A random element from the array\n * @category Utilities\n *\n * @example\n * ```typescript\n * randomElement([\"A\", \"B\", \"C\"]) // => \"A\" or \"B\" or \"C\"\n * ```\n */\nexport function randomElement<T>(array: T[]): T {\n const index = Math.floor(Math.random() * array.length);\n return array[index];\n}\n\n/**\n * Generates a random input string in \"XXX-XXX\" format where X is a random uppercase letter.\n *\n * Port of `make_random_input()` from lifehash.cpp lines 52-57.\n *\n * @returns A random string in \"XXX-XXX\" format\n * @category Utilities\n *\n * @example\n * ```typescript\n * makeRandomInput() // => \"ABC-DEF\" (random letters)\n * ```\n */\nexport function makeRandomInput(): string {\n const letters = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\".split(\"\");\n\n const letter = (): string => randomElement(letters);\n const cluster = (): string => `${letter()}${letter()}${letter()}`;\n\n return `${cluster()}-${cluster()}`;\n}\n","/**\n * LifeHash CLI - Command line tool for generating LifeHash PNG images.\n *\n * Ported from bc-lifehash-cli C++ implementation (lifehash.cpp).\n *\n * @module\n */\n\nimport { Command } from \"commander\";\nimport { makeFromUtf8, Version } from \"@bcts/lifehash\";\nimport { writeImage } from \"./png-writer\";\nimport { appendingPathComponent, makeRandomInput } from \"./utils\";\n\n/**\n * CLI options interface.\n *\n * Port of Parameters struct from lifehash.cpp lines 86-91.\n *\n * @category CLI\n */\nexport interface CliOptions {\n /** LifeHash version to generate */\n version: string;\n /** Size of each module (\"pixel\") */\n module: string;\n /** Output directory path */\n path: string;\n}\n\n/**\n * Parses a version string to the Version enum.\n *\n * Port of version parsing logic from lifehash.cpp lines 130-145.\n *\n * @param versionString - The version string from CLI\n * @returns The corresponding Version enum value\n * @throws Error if the version string is invalid\n * @category CLI\n *\n * @example\n * ```typescript\n * parseVersion(\"version2\") // => Version.version2\n * parseVersion(\"detailed\") // => Version.detailed\n * parseVersion(\"invalid\") // throws Error\n * ```\n */\nexport function parseVersion(versionString: string): Version {\n switch (versionString) {\n case \"version1\":\n return Version.version1;\n case \"version2\":\n return Version.version2;\n case \"detailed\":\n return Version.detailed;\n case \"fiducial\":\n return Version.fiducial;\n case \"grayscaleFiducial\":\n return Version.grayscale_fiducial;\n default:\n throw new Error(\"Invalid version.\");\n }\n}\n\n/**\n * Main execution function that generates a LifeHash image.\n *\n * Port of `run()` from lifehash.cpp lines 188-192.\n *\n * @param input - Input string to hash (or empty for random)\n * @param options - CLI options\n * @category CLI\n *\n * @example\n * ```typescript\n * run(\"Hello\", { version: \"version2\", module: \"1\", path: \".\" });\n * // Generates Hello.png in current directory\n * ```\n */\nexport function run(input: string, options: CliOptions): void {\n // Parse parameters (matching Parameters constructor from C++)\n const version = parseVersion(options.version);\n const moduleSize = parseInt(options.module, 10);\n\n if (moduleSize < 1 || isNaN(moduleSize)) {\n throw new Error(\"Illegal value.\");\n }\n\n // Use random input if none provided\n const actualInput = input !== \"\" ? input : makeRandomInput();\n\n // Generate output filename\n const outputFilename = `${actualInput}.png`;\n const outputFile = appendingPathComponent(options.path, outputFilename);\n\n // Generate LifeHash image\n const image = makeFromUtf8(actualInput, version, moduleSize);\n\n // Write to PNG file\n writeImage(image, outputFile);\n}\n\n/**\n * CLI entry point.\n *\n * Port of `main()` from lifehash.cpp lines 194-206.\n *\n * @category CLI\n */\nexport function main(): void {\n const program = new Command();\n\n program\n .name(\"lifehash\")\n .description(\"Generate LifeHash PNG images from input strings\")\n .argument(\"[input]\", \"Input string to hash (default: random XXX-XXX)\")\n .option(\n \"-v, --version <version>\",\n \"LifeHash version: version1, version2, detailed, fiducial, grayscaleFiducial\",\n \"version2\",\n )\n .option(\"-m, --module <size>\", 'Size of each module (\"pixel\")', \"1\")\n .option(\"-p, --path <path>\", \"Output directory path\", \"\")\n .action((input: string | undefined, options: CliOptions) => {\n try {\n run(input ?? \"\", options);\n } catch (error) {\n if (error instanceof Error) {\n console.log(`\\u{1F928} ${error.message}`);\n console.log();\n program.help();\n }\n process.exit(1);\n }\n });\n\n program.parse();\n}\n\n// Run CLI when executed directly (not when imported as a module)\n// Check if this file is the main entry point\nconst isMainModule =\n typeof process !== \"undefined\" &&\n typeof process.argv[1] === \"string\" &&\n (process.argv[1].endsWith(\"/main.mjs\") ||\n process.argv[1].endsWith(\"/main.js\") ||\n process.argv[1].includes(\"lifehash-cli\"));\n\nif (isMainModule) {\n main();\n}\n","/**\n * @bcts/lifehash-cli - Command line tool for generating LifeHash PNG images.\n *\n * This package provides both a CLI tool and a programmatic API for generating\n * LifeHash visual hash images as PNG files.\n *\n * @packageDocumentation\n * @module @bcts/lifehash-cli\n *\n * @example CLI Usage\n * ```bash\n * # Generate a LifeHash from a string\n * lifehash Hello\n *\n * # Generate with specific version and module size\n * lifehash -v detailed -m 8 Hello\n *\n * # Generate with random input\n * lifehash\n * ```\n *\n * @example Programmatic Usage\n * ```typescript\n * import { generateLifeHash } from \"@bcts/lifehash-cli\";\n * import { writeFileSync } from \"fs\";\n *\n * // Generate a PNG buffer\n * const pngBuffer = generateLifeHash(\"Hello\", {\n * version: \"version2\",\n * moduleSize: 1,\n * });\n *\n * // Write to file\n * writeFileSync(\"Hello.png\", pngBuffer);\n * ```\n */\n\nimport { generatePNG, writeImage } from \"./png-writer\";\nimport { parseVersion, run, type CliOptions } from \"./main\";\nimport { makeFromUtf8, Version, type Image } from \"@bcts/lifehash\";\n\n// PNG encoding exports\nexport { writeImage, generatePNG };\n\n// Utility exports\nexport { appendingPathComponent, randomElement, makeRandomInput } from \"./utils\";\n\n// CLI exports\nexport { parseVersion, run, type CliOptions };\n\n// Re-export @bcts/lifehash types for convenience\nexport { Version, type Image };\n\n// Re-export makeFromUtf8 for programmatic usage\nexport { makeFromUtf8 };\n\n/**\n * Options for generating a LifeHash image.\n *\n * @category Image Generation\n */\nexport interface GenerateOptions {\n /**\n * LifeHash version to generate.\n * @default \"version2\"\n */\n version?: \"version1\" | \"version2\" | \"detailed\" | \"fiducial\" | \"grayscaleFiducial\";\n /**\n * Size of each module (\"pixel\").\n * @default 1\n */\n moduleSize?: number;\n}\n\n/**\n * Generates a LifeHash PNG buffer from an input string.\n *\n * This is the main programmatic API for generating LifeHash images.\n *\n * @param input - The input string to hash\n * @param options - Generation options\n * @returns A Buffer containing the PNG data\n * @category Image Generation\n *\n * @example\n * ```typescript\n * import { generateLifeHash } from \"@bcts/lifehash-cli\";\n * import { writeFileSync } from \"fs\";\n *\n * const pngBuffer = generateLifeHash(\"Hello\", {\n * version: \"version2\",\n * moduleSize: 1,\n * });\n *\n * writeFileSync(\"Hello.png\", pngBuffer);\n * ```\n */\nexport function generateLifeHash(input: string, options: GenerateOptions = {}): Buffer {\n const { version = \"version2\", moduleSize = 1 } = options;\n\n const versionEnum = parseVersion(version);\n const image = makeFromUtf8(input, versionEnum, moduleSize);\n return generatePNG(image);\n}\n"],"mappings":"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"}