bs58ify 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.husky/pre-commit +3 -0
- package/.lintstagedrc.js +5 -0
- package/README.md +40 -0
- package/dist/cli.js +3082 -0
- package/docs/superpowers/plans/2026-06-22-bs58ify-success-messages.md +74 -0
- package/docs/superpowers/specs/2026-06-22-bs58ify-cli-design.md +37 -0
- package/docs/superpowers/specs/2026-06-22-bs58ify-success-messages-design.md +16 -0
- package/oxfmt.config.ts +11 -0
- package/oxlint.config.ts +12 -0
- package/package.json +40 -9
- package/rolldown.config.ts +11 -0
- package/src/cli.ts +46 -0
- package/src/conversion.ts +18 -0
- package/tests/cli.test.ts +64 -0
- package/tests/conversion.test.ts +15 -0
- package/tsconfig.json +28 -0
- package/index.js +0 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# bs58ify Success Messages Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Print a confirmation after each successful `bs58ify` conversion writes its output file.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Keep the existing conversion and write operations unchanged. Each Commander action writes an exact stdout message only after `writeFile` resolves, and process-level CLI tests capture the built binary’s output.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript, Commander, Node.js `fs/promises`, Bun test runner.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
### Task 1: Verify and emit command success messages
|
|
14
|
+
|
|
15
|
+
**Files:**
|
|
16
|
+
- Modify: `tests/cli.test.ts`
|
|
17
|
+
- Modify: `src/cli.ts`
|
|
18
|
+
|
|
19
|
+
- [ ] **Step 1: Write failing built-CLI output tests**
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
test('reports successful encode output', async () => {
|
|
23
|
+
const directory = await mkdtemp(join(tmpdir(), 'bs58ify-'));
|
|
24
|
+
const output = join(directory, 'encoded.txt');
|
|
25
|
+
const result = await Bun.$`node dist/cli.js encode '[1,2,3,4]' ${output}`.quiet();
|
|
26
|
+
|
|
27
|
+
expect(result.stdout.toString()).toBe(`Encoded Base58 value written to ${output}\n`);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('reports successful decode output', async () => {
|
|
31
|
+
const directory = await mkdtemp(join(tmpdir(), 'bs58ify-'));
|
|
32
|
+
const output = join(directory, 'decoded.json');
|
|
33
|
+
const result = await Bun.$`node dist/cli.js decode 2VfUX ${output}`.quiet();
|
|
34
|
+
|
|
35
|
+
expect(result.stdout.toString()).toBe(`Decoded JSON byte array written to ${output}\n`);
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- [ ] **Step 2: Verify the new tests fail**
|
|
40
|
+
|
|
41
|
+
Run: `bun test tests/cli.test.ts`
|
|
42
|
+
|
|
43
|
+
Expected: FAIL because the built commands write no standard-output confirmation.
|
|
44
|
+
|
|
45
|
+
- [ ] **Step 3: Add output after successful writes**
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
await writeFile(outputPath, encodeByteArray(input));
|
|
49
|
+
console.log(`Encoded Base58 value written to ${outputPath}`);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
await writeFile(outputPath, decodeBase58(input));
|
|
54
|
+
console.log(`Decoded JSON byte array written to ${outputPath}`);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- [ ] **Step 4: Verify the focused CLI suite passes**
|
|
58
|
+
|
|
59
|
+
Run: `bun test tests/cli.test.ts`
|
|
60
|
+
|
|
61
|
+
Expected: PASS, including the exact encode and decode confirmation messages.
|
|
62
|
+
|
|
63
|
+
- [ ] **Step 5: Verify the full project**
|
|
64
|
+
|
|
65
|
+
Run: `bun test && bun run typecheck && bun run build`
|
|
66
|
+
|
|
67
|
+
Expected: all commands exit 0.
|
|
68
|
+
|
|
69
|
+
- [ ] **Step 6: Commit the implementation**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git add src/cli.ts tests/cli.test.ts
|
|
73
|
+
git commit -m "feat: report successful CLI conversions"
|
|
74
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# bs58ify CLI Design
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Replace the two positional-argument scripts with a distributable Commander CLI named `bs58ify`. The package will be published under the existing user-owned `bs58ify` npm package.
|
|
6
|
+
|
|
7
|
+
## CLI contract
|
|
8
|
+
|
|
9
|
+
The compiled executable exposes two required subcommands:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
bs58ify encode <uint8array-json> <output-filepath>
|
|
13
|
+
bs58ify decode <base58-private-key> <output-filepath>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- `encode` parses a JSON array of byte values, Base58-encodes it, and writes the resulting text to `output-filepath`.
|
|
17
|
+
- `decode` Base58-decodes its input, serializes the bytes as a JSON array, and writes it to `output-filepath`.
|
|
18
|
+
- Commander owns command parsing, help output, required arguments, and non-zero failure behavior for invalid invocations.
|
|
19
|
+
- Invalid JSON or Base58 input must cause a non-zero command failure without reporting success.
|
|
20
|
+
|
|
21
|
+
## Architecture
|
|
22
|
+
|
|
23
|
+
Conversion logic moves into a small reusable module containing pure encode/decode functions. A CLI entry module owns only Commander configuration, conversion invocation, and file writes. This permits unit tests to exercise conversion semantics without spawning a process, while command tests validate the public CLI behavior.
|
|
24
|
+
|
|
25
|
+
The current root-level `bs58.ts` and `uint8array.ts` scripts are replaced by the unified CLI entry point and supporting module under `src/`.
|
|
26
|
+
|
|
27
|
+
## Packaging and build
|
|
28
|
+
|
|
29
|
+
- Set the package name to `bs58ify`.
|
|
30
|
+
- Add `commander` as a runtime dependency and `rolldown` as a development dependency.
|
|
31
|
+
- Add a `build` script that bundles the CLI to `dist/cli.js` for Node.js, preserving a `#!/usr/bin/env node` shebang.
|
|
32
|
+
- Add a `bin` mapping from `bs58ify` to `dist/cli.js`.
|
|
33
|
+
- Keep Bun as the package manager and test runner.
|
|
34
|
+
|
|
35
|
+
## Documentation and verification
|
|
36
|
+
|
|
37
|
+
The README will document installation, building locally, and both subcommands. Tests cover valid encode/decode conversions, malformed conversion inputs, CLI help, successful file output, and failure exit status. Verification runs the Bun test suite, TypeScript type checking, the Rolldown build, and the built `dist/cli.js` executable.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# bs58ify Success Messages Design
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Provide clear confirmation after each successful file conversion without exposing converted values in terminal output.
|
|
6
|
+
|
|
7
|
+
## Behavior
|
|
8
|
+
|
|
9
|
+
- After `encode` finishes writing its output file, it writes `Encoded Base58 value written to <output>` to standard output.
|
|
10
|
+
- After `decode` finishes writing its output file, it writes `Decoded JSON byte array written to <output>` to standard output.
|
|
11
|
+
- A message is emitted only after `writeFile` resolves successfully.
|
|
12
|
+
- Failed conversions or file writes retain their existing non-zero failure behavior and emit no success message.
|
|
13
|
+
|
|
14
|
+
## Verification
|
|
15
|
+
|
|
16
|
+
CLI tests will capture standard output for each successful subcommand and assert the exact message. Existing error-path and built-binary tests remain in place.
|
package/oxfmt.config.ts
ADDED
package/oxlint.config.ts
ADDED
package/package.json
CHANGED
|
@@ -1,11 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bs58ify",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A CLI for converting between Base58 strings and JSON byte arrays.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"bs58ify": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "rolldown src/cli.ts --config rolldown.config.ts",
|
|
11
|
+
"test": "bun test",
|
|
12
|
+
"typecheck": "tsc --noEmit",
|
|
13
|
+
"lint": "oxlint",
|
|
14
|
+
"lint:fix": "oxlint --fix",
|
|
15
|
+
"fmt": "oxfmt --check",
|
|
16
|
+
"fmt:fix": "oxfmt --write",
|
|
17
|
+
"publish:npm": "bun publish --registry https://registry.npmjs.org/",
|
|
18
|
+
"prepare": "husky"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/bun": "latest",
|
|
22
|
+
"bs58": "^6.0.0",
|
|
23
|
+
"husky": "^9.1.7",
|
|
24
|
+
"lint-staged": "^17.0.8",
|
|
25
|
+
"oxfmt": "^0.55.0",
|
|
26
|
+
"oxlint": "^1.70.0",
|
|
27
|
+
"rolldown": "^1.1.2"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"typescript": "^5"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"commander": "^15.0.0"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"bs58",
|
|
37
|
+
"base58",
|
|
38
|
+
"cli",
|
|
39
|
+
"command-line",
|
|
40
|
+
"utility"
|
|
41
|
+
]
|
|
42
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
import { decodeBase58, encodeByteArray } from "./conversion";
|
|
7
|
+
|
|
8
|
+
export function createProgram(): Command {
|
|
9
|
+
const program = new Command()
|
|
10
|
+
.name("bs58ify")
|
|
11
|
+
.description("Encode and decode Base58 byte arrays.");
|
|
12
|
+
|
|
13
|
+
return program
|
|
14
|
+
.action(() => {
|
|
15
|
+
program.outputHelp();
|
|
16
|
+
})
|
|
17
|
+
.addCommand(
|
|
18
|
+
new Command("encode")
|
|
19
|
+
.description("Encode a JSON byte array as Base58.")
|
|
20
|
+
.argument("<uint8array>", "JSON array of bytes")
|
|
21
|
+
.argument("<output>", "file to write the Base58 result to")
|
|
22
|
+
.action(async (input: string, outputPath: string) => {
|
|
23
|
+
await writeFile(outputPath, encodeByteArray(input));
|
|
24
|
+
console.log(`Encoded Base58 value written to ${outputPath}`);
|
|
25
|
+
}),
|
|
26
|
+
)
|
|
27
|
+
.addCommand(
|
|
28
|
+
new Command("decode")
|
|
29
|
+
.description("Decode Base58 as a JSON byte array.")
|
|
30
|
+
.argument("<base58>", "Base58-encoded value")
|
|
31
|
+
.argument("<output>", "file to write the JSON result to")
|
|
32
|
+
.action(async (input: string, outputPath: string) => {
|
|
33
|
+
await writeFile(outputPath, decodeBase58(input));
|
|
34
|
+
console.log(`Decoded JSON byte array written to ${outputPath}`);
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
40
|
+
createProgram()
|
|
41
|
+
.parseAsync()
|
|
42
|
+
.catch((error: unknown) => {
|
|
43
|
+
console.error(error instanceof Error ? error.message : error);
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import bs58 from "bs58";
|
|
2
|
+
|
|
3
|
+
export function encodeByteArray(input: string): string {
|
|
4
|
+
const values: unknown = JSON.parse(input);
|
|
5
|
+
|
|
6
|
+
if (
|
|
7
|
+
!Array.isArray(values) ||
|
|
8
|
+
values.some((value) => !Number.isInteger(value) || value < 0 || value > 255)
|
|
9
|
+
) {
|
|
10
|
+
throw new Error("Input must be a JSON array of bytes.");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return bs58.encode(new Uint8Array(values));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function decodeBase58(input: string): string {
|
|
17
|
+
return JSON.stringify(Array.from(bs58.decode(input)));
|
|
18
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { mkdtemp, readFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
import { createProgram } from "../src/cli";
|
|
7
|
+
|
|
8
|
+
test("registers encode and decode commands", () => {
|
|
9
|
+
expect(createProgram().commands.map((command) => command.name())).toEqual(["encode", "decode"]);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("writes encoded output", async () => {
|
|
13
|
+
const directory = await mkdtemp(join(tmpdir(), "bs58ify-"));
|
|
14
|
+
const output = join(directory, "encoded.txt");
|
|
15
|
+
|
|
16
|
+
await createProgram().parseAsync(["node", "bs58ify", "encode", "[1,2,3,4]", output]);
|
|
17
|
+
|
|
18
|
+
expect(await readFile(output, "utf8")).toBe("2VfUX");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("rejects malformed command input", async () => {
|
|
22
|
+
await expect(
|
|
23
|
+
createProgram().parseAsync(["node", "bs58ify", "encode", "not-json", "out.txt"]),
|
|
24
|
+
).rejects.toThrow();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("reports successful encode output", async () => {
|
|
28
|
+
const directory = await mkdtemp(join(tmpdir(), "bs58ify-"));
|
|
29
|
+
const output = join(directory, "encoded.txt");
|
|
30
|
+
await Bun.$`bun run build`.quiet();
|
|
31
|
+
|
|
32
|
+
const result = await Bun.$`node dist/cli.js encode '[1,2,3,4]' ${output}`.quiet();
|
|
33
|
+
|
|
34
|
+
expect(result.stdout.toString()).toBe(`Encoded Base58 value written to ${output}\n`);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("reports successful decode output", async () => {
|
|
38
|
+
const directory = await mkdtemp(join(tmpdir(), "bs58ify-"));
|
|
39
|
+
const output = join(directory, "decoded.json");
|
|
40
|
+
await Bun.$`bun run build`.quiet();
|
|
41
|
+
|
|
42
|
+
const result = await Bun.$`node dist/cli.js decode 2VfUX ${output}`.quiet();
|
|
43
|
+
|
|
44
|
+
expect(result.stdout.toString()).toBe(`Decoded JSON byte array written to ${output}\n`);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("builds a CLI with a Node shebang", async () => {
|
|
48
|
+
const packageJson = await Bun.file("package.json").json();
|
|
49
|
+
expect(packageJson.scripts.build).toBe("rolldown src/cli.ts --config rolldown.config.ts");
|
|
50
|
+
expect(await Bun.file("rolldown.config.ts").exists()).toBe(true);
|
|
51
|
+
|
|
52
|
+
const result = await Bun.$`bun run build`.quiet().nothrow();
|
|
53
|
+
|
|
54
|
+
expect(result.exitCode).toBe(0);
|
|
55
|
+
expect((await Bun.file("dist/cli.js").text()).startsWith("#!/usr/bin/env node")).toBe(true);
|
|
56
|
+
|
|
57
|
+
const help = await Bun.$`node dist/cli.js --help`.quiet();
|
|
58
|
+
expect(help.stdout.toString()).toContain("encode");
|
|
59
|
+
expect(help.stdout.toString()).toContain("decode");
|
|
60
|
+
|
|
61
|
+
const noArguments = await Bun.$`node dist/cli.js`.quiet().nothrow();
|
|
62
|
+
expect(noArguments.exitCode).toBe(0);
|
|
63
|
+
expect(noArguments.stdout.toString()).toContain("Usage: bs58ify");
|
|
64
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { decodeBase58, encodeByteArray } from "../src/conversion";
|
|
4
|
+
|
|
5
|
+
test("encodes a JSON byte array as Base58", () => {
|
|
6
|
+
expect(encodeByteArray("[1,2,3,4]")).toBe("2VfUX");
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test("decodes Base58 into a JSON byte array", () => {
|
|
10
|
+
expect(decodeBase58("2VfUX")).toBe("[1,2,3,4]");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("rejects a JSON array containing an invalid byte", () => {
|
|
14
|
+
expect(() => encodeByteArray("[256]")).toThrow("Input must be a JSON array of bytes.");
|
|
15
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Environment setup & latest features
|
|
4
|
+
"lib": ["esnext"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
|
|
11
|
+
// Bundler mode
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
// Best practices
|
|
18
|
+
"strict": true,
|
|
19
|
+
"skipLibCheck": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
"noUncheckedIndexedAccess": true,
|
|
22
|
+
|
|
23
|
+
// Some stricter flags (disabled by default)
|
|
24
|
+
"noUnusedLocals": false,
|
|
25
|
+
"noUnusedParameters": false,
|
|
26
|
+
"noPropertyAccessFromIndexSignature": false
|
|
27
|
+
}
|
|
28
|
+
}
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
// Placeholder
|