@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 +48 -0
- package/README.md +109 -0
- package/dist/index.cjs +317 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +191 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +191 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +308 -0
- package/dist/index.mjs.map +1 -0
- package/dist/main.mjs +209 -0
- package/dist/main.mjs.map +1 -0
- package/package.json +92 -0
- package/src/index.ts +104 -0
- package/src/main.ts +150 -0
- package/src/png-writer.ts +101 -0
- package/src/utils.ts +75 -0
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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"}
|