@bigdreamsweb3/wordbin 1.1.8 → 1.3.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/CONTRIBUTING.md +18 -18
- package/README.md +33 -31
- package/dist/builder-vFphFQMU.js.map +1 -1
- package/dist/cli.mjs +3 -3
- package/dist/cli.mjs.map +1 -1
- package/dist/core/format-detection.d.ts +5 -0
- package/dist/core/helpers.d.ts +1 -0
- package/dist/core/index.d.ts +3 -32
- package/dist/data/dict-v1-bip39.json +2054 -0
- package/dist/index.mjs +233 -179
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.ts +1 -3
- package/package.json +2 -1
- package/dist/core/binary-payload.d.ts +0 -6
- package/dist/core/comp/latin1-compressor.d.ts +0 -9
- package/dist/core/comp/onebyte-encoder.d.ts +0 -2
- package/dist/data/wordbin-v1-bip39.json +0 -6150
package/CONTRIBUTING.md
CHANGED
|
@@ -45,7 +45,7 @@ If you find a bug:
|
|
|
45
45
|
- Expected vs actual behaviour
|
|
46
46
|
- Node.js version (`node -v`)
|
|
47
47
|
- Dictionary version used (v1 / v2 / custom)
|
|
48
|
-
- The payload format involved (hex / base58 / base64 /
|
|
48
|
+
- The payload format involved (hex / base58 / base64 / bytes)
|
|
49
49
|
- A minimal code snippet or failing test case
|
|
50
50
|
|
|
51
51
|
### Suggesting Features or Improvements
|
|
@@ -64,7 +64,7 @@ For larger architectural changes — new payload formats, new dictionary structu
|
|
|
64
64
|
1. **Fork** the repository and **clone** your fork
|
|
65
65
|
2. Create a focused branch:
|
|
66
66
|
```bash
|
|
67
|
-
git checkout -b fix/decode-
|
|
67
|
+
git checkout -b fix/decode-format-detection
|
|
68
68
|
# or
|
|
69
69
|
git checkout -b feat/add-top-20k-dictionary
|
|
70
70
|
```
|
|
@@ -76,10 +76,10 @@ For larger architectural changes — new payload formats, new dictionary structu
|
|
|
76
76
|
```
|
|
77
77
|
6. Commit with clear, semantic messages:
|
|
78
78
|
```
|
|
79
|
-
fix: correct
|
|
79
|
+
fix: correct format detection in detectAndConvert
|
|
80
80
|
feat: add partialScan fallback for non-WordBin payloads
|
|
81
81
|
docs: update decode API in README
|
|
82
|
-
test: add round-trip cases for base58 and
|
|
82
|
+
test: add round-trip cases for base58 and base64 formats
|
|
83
83
|
```
|
|
84
84
|
7. Push your branch and open a pull request against `main`
|
|
85
85
|
|
|
@@ -180,26 +180,26 @@ Set any flag to `false` to skip that suite entirely. Skipped suites are reported
|
|
|
180
180
|
|
|
181
181
|
| Suite | What it tests |
|
|
182
182
|
| ---------------------- | -------------------------------------------------------------------------------------------- |
|
|
183
|
-
| **Encode only** | All payload formats (hex, base58, base64
|
|
183
|
+
| **Encode only** | All payload formats (hex, base58, base64), compression ratio, format validity |
|
|
184
184
|
| **Decode only** | Known hex payload, known base64 payload, non-WordBin fallback |
|
|
185
|
-
| **Encode then decode** | Full round-trip for hex, base58, base64,
|
|
185
|
+
| **Encode then decode** | Full round-trip for hex, base58, base64, and raw `Uint8Array` |
|
|
186
186
|
| **Non-WordBin decode** | 4 foreign payloads — verifies `isWordBin: false`, `notice` is set, `text` is always a string |
|
|
187
187
|
|
|
188
188
|
---
|
|
189
189
|
|
|
190
190
|
## Where Things Live
|
|
191
191
|
|
|
192
|
-
| What you want to change
|
|
193
|
-
|
|
|
194
|
-
| Encode / decode logic
|
|
195
|
-
| Payload format detection (hex / base58 / base64
|
|
196
|
-
| `DecodeResult` type or `PayloadFormat` union
|
|
197
|
-
| Partial scan / best-effort fallback
|
|
198
|
-
| Dictionary loading and versioning
|
|
199
|
-
| Building a dictionary from a wordlist
|
|
200
|
-
| Buffer utilities (varint, hex, utf8)
|
|
201
|
-
| Shared constants (LITERAL byte value)
|
|
202
|
-
| Tests
|
|
192
|
+
| What you want to change | File |
|
|
193
|
+
| ------------------------------------------------ | ------------------------------------ |
|
|
194
|
+
| Encode / decode logic | `src/core/wordbin.ts` |
|
|
195
|
+
| Payload format detection (hex / base58 / base64) | `detectAndConvert()` in `wordbin.ts` |
|
|
196
|
+
| `DecodeResult` type or `PayloadFormat` union | top of `wordbin.ts` |
|
|
197
|
+
| Partial scan / best-effort fallback | `partialScan()` in `wordbin.ts` |
|
|
198
|
+
| Dictionary loading and versioning | `src/dict/dictionary-loader.ts` |
|
|
199
|
+
| Building a dictionary from a wordlist | `src/dict/builder.ts` |
|
|
200
|
+
| Buffer utilities (varint, hex, utf8) | `src/utils/buffer.ts` |
|
|
201
|
+
| Shared constants (LITERAL byte value) | `src/constants.ts` |
|
|
202
|
+
| Tests | `test/test.spec.ts` |
|
|
203
203
|
|
|
204
204
|
---
|
|
205
205
|
|
|
@@ -209,7 +209,7 @@ These improvements would have the biggest impact right now:
|
|
|
209
209
|
|
|
210
210
|
- **Case-insensitive dictionaries** — normalise to lowercase during dictionary building so `"Hello"` and `"hello"` encode identically
|
|
211
211
|
- **Smaller curated dictionaries** — top 10k–50k common English words, programming keywords, domain-specific lists (DeFi, medical, etc.)
|
|
212
|
-
-
|
|
212
|
+
- Note: Bin21 removed — hex is primary payload. Remove any Bin21-specific tests or docs.
|
|
213
213
|
- **Performance benchmarks** — encode/decode throughput across dictionary sizes and phrase lengths
|
|
214
214
|
- **Browser demo** — a minimal CodeSandbox or static HTML page showing encode/decode live
|
|
215
215
|
- **CLI enhancements** — progress bars, `--lowercase` flag, `--output-dir`, improved help text
|
package/README.md
CHANGED
|
@@ -22,7 +22,6 @@ Saved : 38 bytes — 47% of original size
|
|
|
22
22
|
Hex : 0108c424409e363270f7d64deba55e2e11ba716eba59926de2f50282599fc5afd1a8
|
|
23
23
|
Base58 : 2MepGpLHGPPmnrdzjmpqet2XFQ2YGMSpQoDXDex7toUBdZ
|
|
24
24
|
Base64 : AQjEJECeNjJw99ZN66VeLhG6cW66WZJt4vUCglmfxa/RqA==
|
|
25
|
-
Bin21 : ☺◄Ä$@ž6rp÷ÖMë¥^.►ºqnºY™mâõ☻™Å¯Ñ¨
|
|
26
25
|
|
|
27
26
|
Decoded: "stock ridge avoid school honey trap wait wheel worry face differ wedding" ✓
|
|
28
27
|
```
|
|
@@ -34,7 +33,7 @@ Decoded: "stock ridge avoid school honey trap wait wheel worry face differ weddi
|
|
|
34
33
|
- **40–70% size reduction** on typical short phrases
|
|
35
34
|
- **Deterministic** — same input + same dictionary = same output, every time
|
|
36
35
|
- **Lossless** — decode is always a perfect round-trip
|
|
37
|
-
- **Universal decoder** — accepts hex, Base58, Base64,
|
|
36
|
+
- **Universal decoder** — accepts hex, Base58, Base64, or raw bytes; format is auto-detected
|
|
38
37
|
- **Resilient** — non-WordBin payloads are never rejected; partial word extraction is attempted before falling back gracefully
|
|
39
38
|
- **No runtime dependencies** — works in Node.js and the browser
|
|
40
39
|
- **Flexible dictionaries** — BIP-39 (v1, bundled), large English (v2), or custom wordlists
|
|
@@ -58,28 +57,29 @@ import { WordBin } from "@bigdreamsweb3/wordbin";
|
|
|
58
57
|
|
|
59
58
|
const wb = await WordBin.create();
|
|
60
59
|
|
|
61
|
-
const phrase =
|
|
62
|
-
"stock ridge avoid school honey trap wait wheel worry face differ wedding";
|
|
60
|
+
const phrase = "the quick brown fox jumps over thirteen lazy dogs";
|
|
63
61
|
|
|
64
62
|
// ── Encode ────────────────────────────────────────────────────────────────────
|
|
65
63
|
const encoded = await wb.encode(phrase);
|
|
66
64
|
|
|
65
|
+
console.log(encoded.dictVersion); // 2
|
|
67
66
|
console.log(encoded.hexPayload); // standard hex string
|
|
68
67
|
console.log(encoded.base58Payload); // Base58 string
|
|
69
68
|
console.log(encoded.base64Payload); // Base64 string
|
|
70
|
-
console.log(encoded.payload); //
|
|
71
|
-
console.log(encoded.encodedBytes); //
|
|
72
|
-
console.log(encoded.originalBytes); //
|
|
73
|
-
console.log(encoded.
|
|
69
|
+
console.log(encoded.payload); // hex payload (primary)
|
|
70
|
+
console.log(encoded.encodedBytes); // 24
|
|
71
|
+
console.log(encoded.originalBytes); // 49
|
|
72
|
+
console.log(encoded.bytesSaved); // 25
|
|
73
|
+
console.log(encoded.ratioPercent); // 48.98
|
|
74
74
|
|
|
75
75
|
// ── Decode — pass any format, it's auto-detected ──────────────────────────────
|
|
76
76
|
const r1 = await wb.decode(encoded.hexPayload); // DetectedFormat: "hex"
|
|
77
77
|
const r2 = await wb.decode(encoded.base58Payload); // DetectedFormat: "base58"
|
|
78
78
|
const r3 = await wb.decode(encoded.base64Payload); // DetectedFormat: "base64"
|
|
79
|
-
const r4 = await wb.decode(encoded.payload); // DetectedFormat: "
|
|
79
|
+
const r4 = await wb.decode(encoded.payload); // DetectedFormat: "hex"
|
|
80
80
|
const r5 = await wb.decode(encoded.encoded); // DetectedFormat: "bytes" (Uint8Array)
|
|
81
81
|
|
|
82
|
-
console.log(r1.text); // "
|
|
82
|
+
console.log(r1.text); // "the quick brown fox jumps over..."
|
|
83
83
|
console.log(r1.isWordBin); // true
|
|
84
84
|
```
|
|
85
85
|
|
|
@@ -101,15 +101,14 @@ console.log(r1.isWordBin); // true
|
|
|
101
101
|
|
|
102
102
|
WordBin produces four interchangeable representations of the same encoded bytes. Pass any of them to `decode()` — the format is detected automatically.
|
|
103
103
|
|
|
104
|
-
| Format | Field | Description | Size
|
|
105
|
-
| ---------- | --------------- | -------------------------------------- |
|
|
106
|
-
| **Hex** | `hexPayload` | Lowercase hex, 2 chars per byte | 2× raw
|
|
107
|
-
| **Base58** | `base58Payload` | URL-safe, no ambiguous chars (0/O/I/l) | ~1.4× raw
|
|
108
|
-
| **Base64** | `base64Payload` | Standard Base64 with `=` padding | ~1.33× raw
|
|
109
|
-
| **
|
|
110
|
-
| **Bytes** | `encoded` | Raw `Uint8Array` | 1× raw |
|
|
104
|
+
| Format | Field | Description | Size |
|
|
105
|
+
| ---------- | --------------- | -------------------------------------- | ---------- |
|
|
106
|
+
| **Hex** | `hexPayload` | Lowercase hex, 2 chars per byte | 2× raw |
|
|
107
|
+
| **Base58** | `base58Payload` | URL-safe, no ambiguous chars (0/O/I/l) | ~1.4× raw |
|
|
108
|
+
| **Base64** | `base64Payload` | Standard Base64 with `=` padding | ~1.33× raw |
|
|
109
|
+
| **Bytes** | `encoded` | Raw `Uint8Array` | 1× raw |
|
|
111
110
|
|
|
112
|
-
>
|
|
111
|
+
> `payload` is now hex (lowercase, two characters per byte). A 34-byte payload is a 68-character hex string.
|
|
113
112
|
|
|
114
113
|
---
|
|
115
114
|
|
|
@@ -121,7 +120,7 @@ WordBin produces four interchangeable representations of the same encoded bytes.
|
|
|
121
120
|
interface DecodeResult {
|
|
122
121
|
text: string; // decoded words, or best-effort extraction
|
|
123
122
|
isWordBin: boolean; // true = valid WordBin payload, perfectly decoded
|
|
124
|
-
detectedFormat: PayloadFormat; // "hex" | "base58" | "base64" | "
|
|
123
|
+
detectedFormat: PayloadFormat; // "hex" | "base58" | "base64" | "bytes"
|
|
125
124
|
notice?: string; // present when payload is not a valid WordBin stream
|
|
126
125
|
rawSegments?: string[]; // unmatched bytes shown as [0xXX], non-WordBin only
|
|
127
126
|
}
|
|
@@ -133,7 +132,7 @@ interface DecodeResult {
|
|
|
133
132
|
Payload received
|
|
134
133
|
│
|
|
135
134
|
▼
|
|
136
|
-
Format detection ──── hex / base58 / base64 /
|
|
135
|
+
Format detection ──── hex / base58 / base64 / bytes
|
|
137
136
|
│
|
|
138
137
|
▼
|
|
139
138
|
Strict WordBin parse (all installed dictionary versions)
|
|
@@ -150,11 +149,14 @@ Strict WordBin parse (all installed dictionary versions)
|
|
|
150
149
|
|
|
151
150
|
```ts
|
|
152
151
|
// Non-WordBin payload — still handled gracefully
|
|
153
|
-
const result = await wb.decode("
|
|
152
|
+
const result = await wb.decode("68656c6c6f20776f726c6421"); // Plain "hello world!" as hex
|
|
154
153
|
|
|
154
|
+
// console.log("Input payload :", foreignHex);
|
|
155
|
+
console.log(result.text); // hello world[raw:!]
|
|
156
|
+
console.log(result.detectedFormat); // hex
|
|
155
157
|
console.log(result.isWordBin); // false
|
|
156
158
|
console.log(result.notice); // "This does not appear to be a valid WordBin payload..."
|
|
157
|
-
console.log(result.rawSegments); // [
|
|
159
|
+
console.log(result.rawSegments); // [ '[raw:!]' ] — unmatched bytes
|
|
158
160
|
```
|
|
159
161
|
|
|
160
162
|
---
|
|
@@ -164,7 +166,7 @@ console.log(result.rawSegments); // ["[0x48]", "[0x65]", ...] — unmatched byte
|
|
|
164
166
|
1. **Version header** — first byte identifies the dictionary version (`0x01` for v1)
|
|
165
167
|
2. **Dictionary lookup** — each word in the phrase is replaced by its compact binary ID (1–4 bytes)
|
|
166
168
|
3. **Literal fallback** — words not in the dictionary are stored as `varint length + UTF-8 bytes`
|
|
167
|
-
4. **Payload representations** — the raw bytes are encoded into hex, Base58,
|
|
169
|
+
4. **Payload representations** — the raw bytes are encoded into hex, Base58, and Base64
|
|
168
170
|
|
|
169
171
|
Payloads are **self-describing** (the version byte is embedded) and **fully lossless**.
|
|
170
172
|
|
|
@@ -329,14 +331,14 @@ The `./data/` directory is gitignored. Built dictionaries are loaded automatical
|
|
|
329
331
|
|
|
330
332
|
The most common contribution points:
|
|
331
333
|
|
|
332
|
-
| What you want to change
|
|
333
|
-
|
|
|
334
|
-
| Encode / decode logic
|
|
335
|
-
| Format detection (hex/base58/base64
|
|
336
|
-
| Dictionary loading / versioning
|
|
337
|
-
| Dictionary building from a wordlist
|
|
338
|
-
| Buffer utilities (varint, hex, utf8)
|
|
339
|
-
| Add a new payload format
|
|
334
|
+
| What you want to change | Where to look |
|
|
335
|
+
| ------------------------------------ | -------------------------------------------------------- |
|
|
336
|
+
| Encode / decode logic | `src/core/wordbin.ts` |
|
|
337
|
+
| Format detection (hex/base58/base64) | `detectAndConvert()` in `wordbin.ts` |
|
|
338
|
+
| Dictionary loading / versioning | `src/dict/dictionary-loader.ts` |
|
|
339
|
+
| Dictionary building from a wordlist | `src/dict/builder.ts` |
|
|
340
|
+
| Buffer utilities (varint, hex, utf8) | `src/utils/buffer.ts` |
|
|
341
|
+
| Add a new payload format | `PayloadFormat` type + `detectAndConvert()` + `encode()` |
|
|
340
342
|
|
|
341
343
|
### Submitting a pull request
|
|
342
344
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder-vFphFQMU.js","sources":["../src/core/tiers.ts","../src/core/id.ts","../src/utils/buffer.ts","../src/dict/builder.ts"],"sourcesContent":["export function getIdByteLength(wordLength: number): number {\r\n if (wordLength <= 4) return 2;\r\n if (wordLength <= 9) return 3;\r\n return 4;\r\n}\r\n\r\nexport function getWrapByteLength(wordLength: number): number {\r\n if (wordLength <= 4) return 2;\r\n if (wordLength <= 9) return 3;\r\n return 4;\r\n}\r\n\r\nexport async function getTextEncoder(): Promise<TextEncoder> {\r\n if (typeof TextEncoder !== \"undefined\") return new TextEncoder();\r\n const { TextEncoder: NodeTextEncoder } = await import(\"node:util\");\r\n // @ts-ignore Node typings\r\n return new NodeTextEncoder();\r\n}\r\n\r\nexport async function wrapBase64(data: string): Promise<Uint8Array> {\r\n const normalized = data.trim().toLowerCase();\r\n if (!normalized) throw new Error(\"Cannot generate ID for empty string\");\r\n\r\n const encoder = await getTextEncoder();\r\n const result = encoder.encode(normalized);\r\n\r\n // Browser + Node compatible SHA-256\r\n let hash: ArrayBuffer;\r\n const anyCrypto: any = (globalThis as any).crypto;\r\n if (anyCrypto && anyCrypto.subtle) {\r\n hash = await anyCrypto.subtle.digest(\"SHA-256\", result);\r\n } else {\r\n const { createHash } = await import(\"node:crypto\");\r\n hash = createHash(\"sha256\").update(Buffer.from(result)).digest().buffer;\r\n }\r\n\r\n const hashBytes = new Uint8Array(hash);\r\n const size = getWrapByteLength(normalized.length);\r\n return hashBytes.slice(0, size);\r\n}\r\n","
|
|
1
|
+
{"version":3,"file":"builder-vFphFQMU.js","sources":["../src/core/tiers.ts","../src/core/id.ts","../src/utils/buffer.ts","../src/dict/builder.ts"],"sourcesContent":["export function getIdByteLength(wordLength: number): number {\r\n if (wordLength <= 4) return 2;\r\n if (wordLength <= 9) return 3;\r\n return 4;\r\n}\r\n\r\nexport function getWrapByteLength(wordLength: number): number {\r\n if (wordLength <= 4) return 2;\r\n if (wordLength <= 9) return 3;\r\n return 4;\r\n}\r\n\r\nexport async function getTextEncoder(): Promise<TextEncoder> {\r\n if (typeof TextEncoder !== \"undefined\") return new TextEncoder();\r\n const { TextEncoder: NodeTextEncoder } = await import(\"node:util\");\r\n // @ts-ignore Node typings\r\n return new NodeTextEncoder();\r\n}\r\n\r\nexport async function wrapBase64(data: string): Promise<Uint8Array> {\r\n const normalized = data.trim().toLowerCase();\r\n if (!normalized) throw new Error(\"Cannot generate ID for empty string\");\r\n\r\n const encoder = await getTextEncoder();\r\n const result = encoder.encode(normalized);\r\n\r\n // Browser + Node compatible SHA-256\r\n let hash: ArrayBuffer;\r\n const anyCrypto: any = (globalThis as any).crypto;\r\n if (anyCrypto && anyCrypto.subtle) {\r\n hash = await anyCrypto.subtle.digest(\"SHA-256\", result);\r\n } else {\r\n const { createHash } = await import(\"node:crypto\");\r\n hash = createHash(\"sha256\").update(Buffer.from(result)).digest().buffer;\r\n }\r\n\r\n const hashBytes = new Uint8Array(hash);\r\n const size = getWrapByteLength(normalized.length);\r\n return hashBytes.slice(0, size);\r\n}\r\n","// File: src/core/id.ts\r\n\r\nimport { getIdByteLength } from './tiers.js'\r\n\r\n/**\r\n * Deterministic word \t ID generator\r\n * Same output on browser and node (when using compatible input)\r\n */\r\nexport async function generateWordId(word: string): Promise<Uint8Array> {\r\n const normalized = word.trim().toLowerCase()\r\n if (!normalized) throw new Error('Cannot generate ID for empty string')\r\n\r\n const encoder = await getTextEncoder()\r\n const data = encoder.encode(normalized)\r\n\r\n // Browser + Node compatible SHA-256\r\n let hash: ArrayBuffer\r\n const anyCrypto: any = (globalThis as any).crypto\r\n if (anyCrypto && anyCrypto.subtle) {\r\n hash = await anyCrypto.subtle.digest('SHA-256', data)\r\n } else {\r\n const { createHash } = await import('node:crypto')\r\n hash = createHash('sha256').update(Buffer.from(data)).digest().buffer\r\n }\r\n\r\n const hashBytes = new Uint8Array(hash)\r\n const size = getIdByteLength(normalized.length)\r\n return hashBytes.slice(0, size)\r\n}\r\n\r\nasync function getTextEncoder(): Promise<TextEncoder> {\r\n if (typeof TextEncoder !== 'undefined') return new TextEncoder()\r\n const { TextEncoder: NodeTextEncoder } = await import('node:util')\r\n // @ts-ignore Node typings\r\n return new NodeTextEncoder()\r\n}\r\n","export function toHex(bytes: Uint8Array): string {\r\n return Array.from(bytes)\r\n .map((b) => b.toString(16).padStart(2, '0'))\r\n .join('')\r\n}\r\n\r\nexport function fromHex(hex: string): Uint8Array {\r\n if (hex.length % 2 !== 0) throw new Error('Invalid hex string length')\r\n const bytes = new Uint8Array(hex.length / 2)\r\n for (let i = 0; i < hex.length; i += 2) {\r\n bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16)\r\n }\r\n return bytes\r\n}\r\n\r\nexport function toBase64(bytes: Uint8Array): string {\r\n const b64 = (globalThis as any).btoa\r\n if (typeof b64 === 'function') {\r\n return b64(String.fromCharCode(...bytes))\r\n }\r\n // Node fallback\r\n return Buffer.from(bytes).toString('base64')\r\n}\r\n\r\nexport function fromBase64(base64: string): Uint8Array {\r\n const at = (globalThis as any).atob\r\n if (typeof at === 'function') {\r\n const binary = at(base64)\r\n const out = new Uint8Array(binary.length)\r\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i)\r\n return out\r\n }\r\n // Node fallback\r\n return new Uint8Array(Buffer.from(base64, 'base64'))\r\n}\r\n\r\n// UTF-8 helpers\r\nexport function utf8Encode(str: string): Uint8Array {\r\n if (typeof TextEncoder !== 'undefined') return new TextEncoder().encode(str)\r\n // Node fallback\r\n return new Uint8Array(Buffer.from(str, 'utf8'))\r\n}\r\n\r\nexport function utf8Decode(bytes: Uint8Array): string {\r\n if (typeof TextDecoder !== 'undefined') return new TextDecoder().decode(bytes)\r\n // Node fallback\r\n return Buffer.from(bytes).toString('utf8')\r\n}\r\n\r\n// Varint (LEB128 7-bit groups) helpers\r\nexport function encodeVarint(n: number): Uint8Array {\r\n if (n < 0) throw new Error('Varint cannot encode negative numbers')\r\n const out: number[] = []\r\n do {\r\n let byte = n & 0x7f\r\n n >>>= 7\r\n if (n !== 0) byte |= 0x80\r\n out.push(byte)\r\n } while (n !== 0)\r\n return new Uint8Array(out)\r\n}\r\n\r\nexport function decodeVarint(bytes: Uint8Array, offset: number): { value: number; bytesRead: number } {\r\n let result = 0\r\n let shift = 0\r\n let pos = offset\r\n while (pos < bytes.length) {\r\n const byte = bytes[pos++]\r\n result |= (byte & 0x7f) << shift\r\n if ((byte & 0x80) === 0) {\r\n return { value: result, bytesRead: pos - offset }\r\n }\r\n shift += 7\r\n if (shift > 35) throw new Error('Varint too large')\r\n }\r\n throw new Error('Truncated varint')\r\n}\r\n","// File: src\\dict\\builder.ts\r\n\r\nimport type { WordBinDictionary } from \"../types\";\r\nimport { generateWordId } from \"../core/id.js\";\r\nimport { toHex } from \"../utils/buffer.js\";\r\n\r\nexport interface BuildDictionaryOptions {\r\n /**\r\n * Dictionary version number (used in header and for format compatibility)\r\n * @default 1\r\n */\r\n version?: number;\r\n\r\n /**\r\n * Human-readable description of this dictionary\r\n * @default \"WordBin dictionary v${version}\"\r\n */\r\n description?: string;\r\n\r\n /**\r\n * Optional: custom prefix or identifier for this dictionary build\r\n * (can be used in logs, filenames, etc.)\r\n */\r\n name?: string;\r\n}\r\n\r\nexport async function buildDictionary(\r\n words: string[],\r\n options: BuildDictionaryOptions = {},\r\n): Promise<WordBinDictionary> {\r\n const { version = 1, description = `WordBin dictionary v${version}` } =\r\n options;\r\n\r\n const map: Record<string, string[]> = {};\r\n\r\n const normalizedWords = words\r\n .map((w) => w.trim().toLowerCase())\r\n .filter((w) => w);\r\n\r\n await Promise.all(\r\n normalizedWords.map(async (word) => {\r\n let attempt = 0;\r\n let key: string;\r\n\r\n while (true) {\r\n const id = await generateWordId(\r\n attempt === 0 ? word : `${word}:${attempt}`,\r\n );\r\n\r\n key = toHex(id);\r\n\r\n // If no collision, break\r\n if (!map[key]) {\r\n map[key] = [word];\r\n break;\r\n }\r\n\r\n // Collision detected → try again\r\n attempt++;\r\n }\r\n }),\r\n );\r\n\r\n Object.values(map).forEach((collisions) => {\r\n collisions.sort((a, b) => a.localeCompare(b));\r\n });\r\n\r\n return {\r\n version,\r\n description,\r\n words: map,\r\n };\r\n}\r\n"],"names":[],"mappings":"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"}
|
package/dist/cli.mjs
CHANGED
|
@@ -57,7 +57,7 @@ async function buildV1() {
|
|
|
57
57
|
1,
|
|
58
58
|
"WordBin dictionary v1 – BIP-39 English (2048 words)",
|
|
59
59
|
words,
|
|
60
|
-
"
|
|
60
|
+
"dict-v1-bip39.json"
|
|
61
61
|
);
|
|
62
62
|
}
|
|
63
63
|
async function buildV2() {
|
|
@@ -68,7 +68,7 @@ async function buildV2() {
|
|
|
68
68
|
2,
|
|
69
69
|
`WordBin dictionary v2 – dwyl/english-words (${words.length} words)`,
|
|
70
70
|
words,
|
|
71
|
-
"
|
|
71
|
+
"dict-v2-dwyl.json"
|
|
72
72
|
);
|
|
73
73
|
}
|
|
74
74
|
async function buildCustom(source) {
|
|
@@ -82,7 +82,7 @@ async function buildCustom(source) {
|
|
|
82
82
|
}
|
|
83
83
|
const version = 3;
|
|
84
84
|
const desc = `WordBin custom dictionary v${version} – from ${source} (${words.length} words)`;
|
|
85
|
-
await saveDict(version, desc, words, `
|
|
85
|
+
await saveDict(version, desc, words, `dict-v${version}-custom.json`);
|
|
86
86
|
}
|
|
87
87
|
async function interactiveMode() {
|
|
88
88
|
console.log("Interactive Dictionary Builder");
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\r\n\r\nimport { writeFile, mkdir, readFile } from \"node:fs/promises\";\r\nimport { resolve } from \"node:path\";\r\nimport { createInterface } from \"node:readline\";\r\nimport { stdin as input, stdout as output } from \"node:process\";\r\nimport { wordlists } from \"bip39\";\r\nimport { buildDictionary } from \"./index\";\r\n\r\nconst rl = createInterface({ input, output });\r\n\r\nconst help = `\r\nWordBin CLI – Dictionary Builder\r\n\r\nUsage:\r\n npx wordbin build [options]\r\n\r\nOptions:\r\n --version <num> Build specific version (1 or 2)\r\n --all Build all versions\r\n --custom <source> Build from custom URL or local file path\r\n --help Show this help\r\n\r\nIf no options provided, enters interactive mode.\r\n`;\r\n\r\nconst args = process.argv.slice(2);\r\nconst cmd = args[0];\r\n\r\nasync function prompt(question: string): Promise<string> {\r\n return new Promise((resolve) => {\r\n rl.question(question, resolve);\r\n });\r\n}\r\n\r\nasync function saveDict(\r\n version: number,\r\n desc: string,\r\n words: string[],\r\n filename: string,\r\n) {\r\n const dict = await buildDictionary(words, {\r\n version,\r\n description: desc,\r\n });\r\n\r\n const outDir = resolve(process.cwd(), \"data\");\r\n await mkdir(outDir, { recursive: true });\r\n\r\n const outPath = resolve(outDir, filename);\r\n await writeFile(outPath, JSON.stringify(dict, null, 2), \"utf8\");\r\n\r\n console.log(`Saved ${filename} (${words.length} words) to ${outPath}`);\r\n}\r\n\r\nasync function fetchWordsFromUrl(url: string) {\r\n const res = await fetch(url);\r\n if (!res.ok) throw new Error(`Failed to fetch: ${res.status}`);\r\n\r\n const text = await res.text();\r\n return text\r\n .split(\"\\n\")\r\n .map((w) => w.trim())\r\n .filter(Boolean);\r\n}\r\n\r\nasync function readWordsFromFile(path: string) {\r\n const text = await readFile(path, \"utf8\");\r\n return text\r\n .split(\"\\n\")\r\n .map((w) => w.trim())\r\n .filter(Boolean);\r\n}\r\n\r\nasync function buildV1() {\r\n const words = wordlists.english;\r\n\r\n if (!Array.isArray(words) || words.length !== 2048) {\r\n throw new Error(\"Invalid BIP-39 wordlist\");\r\n }\r\n\r\n await saveDict(\r\n 1,\r\n \"WordBin dictionary v1 – BIP-39 English (2048 words)\",\r\n words,\r\n \"
|
|
1
|
+
{"version":3,"file":"cli.mjs","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\r\n\r\nimport { writeFile, mkdir, readFile } from \"node:fs/promises\";\r\nimport { resolve } from \"node:path\";\r\nimport { createInterface } from \"node:readline\";\r\nimport { stdin as input, stdout as output } from \"node:process\";\r\nimport { wordlists } from \"bip39\";\r\nimport { buildDictionary } from \"./index\";\r\n\r\nconst rl = createInterface({ input, output });\r\n\r\nconst help = `\r\nWordBin CLI – Dictionary Builder\r\n\r\nUsage:\r\n npx wordbin build [options]\r\n\r\nOptions:\r\n --version <num> Build specific version (1 or 2)\r\n --all Build all versions\r\n --custom <source> Build from custom URL or local file path\r\n --help Show this help\r\n\r\nIf no options provided, enters interactive mode.\r\n`;\r\n\r\nconst args = process.argv.slice(2);\r\nconst cmd = args[0];\r\n\r\nasync function prompt(question: string): Promise<string> {\r\n return new Promise((resolve) => {\r\n rl.question(question, resolve);\r\n });\r\n}\r\n\r\nasync function saveDict(\r\n version: number,\r\n desc: string,\r\n words: string[],\r\n filename: string,\r\n) {\r\n const dict = await buildDictionary(words, {\r\n version,\r\n description: desc,\r\n });\r\n\r\n const outDir = resolve(process.cwd(), \"data\");\r\n await mkdir(outDir, { recursive: true });\r\n\r\n const outPath = resolve(outDir, filename);\r\n await writeFile(outPath, JSON.stringify(dict, null, 2), \"utf8\");\r\n\r\n console.log(`Saved ${filename} (${words.length} words) to ${outPath}`);\r\n}\r\n\r\nasync function fetchWordsFromUrl(url: string) {\r\n const res = await fetch(url);\r\n if (!res.ok) throw new Error(`Failed to fetch: ${res.status}`);\r\n\r\n const text = await res.text();\r\n return text\r\n .split(\"\\n\")\r\n .map((w) => w.trim())\r\n .filter(Boolean);\r\n}\r\n\r\nasync function readWordsFromFile(path: string) {\r\n const text = await readFile(path, \"utf8\");\r\n return text\r\n .split(\"\\n\")\r\n .map((w) => w.trim())\r\n .filter(Boolean);\r\n}\r\n\r\nasync function buildV1() {\r\n const words = wordlists.english;\r\n\r\n if (!Array.isArray(words) || words.length !== 2048) {\r\n throw new Error(\"Invalid BIP-39 wordlist\");\r\n }\r\n\r\n await saveDict(\r\n 1,\r\n \"WordBin dictionary v1 – BIP-39 English (2048 words)\",\r\n words,\r\n \"dict-v1-bip39.json\",\r\n );\r\n}\r\n\r\nasync function buildV2() {\r\n console.log(\"Downloading dwyl/english-words...\");\r\n\r\n const url =\r\n \"https://raw.githubusercontent.com/dwyl/english-words/master/words.txt\";\r\n\r\n const words = await fetchWordsFromUrl(url);\r\n\r\n await saveDict(\r\n 2,\r\n `WordBin dictionary v2 – dwyl/english-words (${words.length} words)`,\r\n words,\r\n \"dict-v2-dwyl.json\",\r\n );\r\n}\r\n\r\nasync function buildCustom(source: string) {\r\n let words: string[];\r\n\r\n if (source.startsWith(\"http://\") || source.startsWith(\"https://\")) {\r\n console.log(`Fetching from URL: ${source}`);\r\n words = await fetchWordsFromUrl(source);\r\n } else {\r\n console.log(`Reading from local file: ${source}`);\r\n words = await readWordsFromFile(source);\r\n }\r\n\r\n const version = 3;\r\n const desc = `WordBin custom dictionary v${version} – from ${source} (${words.length} words)`;\r\n\r\n await saveDict(version, desc, words, `dict-v${version}-custom.json`);\r\n}\r\n\r\nasync function interactiveMode() {\r\n console.log(\"Interactive Dictionary Builder\");\r\n console.log(\"Options: 1 (BIP-39), 2 (dwyl large), all, custom, or q to quit\");\r\n\r\n const choice =\r\n (await prompt(\"Choose dictionary to build (default: 2): \")) || \"2\";\r\n\r\n if (choice === \"q\") {\r\n rl.close();\r\n return;\r\n }\r\n\r\n if (choice === \"1\") await buildV1();\r\n else if (choice === \"2\") await buildV2();\r\n else if (choice === \"all\") {\r\n await buildV1();\r\n await buildV2();\r\n } else if (choice === \"custom\") {\r\n const source = await prompt(\"Enter URL or local file path: \");\r\n if (source) await buildCustom(source);\r\n } else {\r\n console.error(\"Invalid choice\");\r\n }\r\n\r\n rl.close();\r\n}\r\n\r\nasync function main() {\r\n if (cmd === \"--help\" || cmd === \"-h\") {\r\n console.log(help);\r\n process.exit(0);\r\n }\r\n\r\n if (cmd === \"build\") {\r\n if (args.length === 1) {\r\n await interactiveMode();\r\n return;\r\n }\r\n\r\n const option = args[1];\r\n\r\n if (option === \"--version\") {\r\n const ver = parseInt(args[2]);\r\n if (ver === 1) await buildV1();\r\n else if (ver === 2) await buildV2();\r\n else console.error(\"Invalid version\");\r\n } else if (option === \"--all\") {\r\n await buildV1();\r\n await buildV2();\r\n } else if (option === \"--custom\") {\r\n const source = args[2];\r\n if (!source) {\r\n console.error(\"Provide URL or file path\");\r\n process.exit(1);\r\n }\r\n await buildCustom(source);\r\n } else {\r\n console.log(help);\r\n }\r\n\r\n return;\r\n }\r\n\r\n console.log(help);\r\n process.exit(1);\r\n}\r\n\r\nmain().catch((err) => {\r\n console.error(\"Error:\", err);\r\n process.exit(1);\r\n});\r\n"],"names":["input","output","resolve"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function bytesToHex(bytes: Uint8Array): string;
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,21 +1,10 @@
|
|
|
1
1
|
import { EncodeResult, WordBinDictionary } from '../types.js';
|
|
2
|
-
|
|
2
|
+
import { PayloadFormat } from './format-detection.js';
|
|
3
3
|
export interface DecodeResult {
|
|
4
|
-
/** The decoded text — words for WordBin payloads, best-effort for others. */
|
|
5
4
|
text: string;
|
|
6
|
-
/** True only when the payload was a valid, fully-parsed WordBin stream. */
|
|
7
5
|
isWordBin: boolean;
|
|
8
|
-
/** Auto-detected wire format of the input. */
|
|
9
6
|
detectedFormat: PayloadFormat;
|
|
10
|
-
/**
|
|
11
|
-
* Human-readable notice when the payload is not a valid WordBin stream.
|
|
12
|
-
* Includes information about what the decoder did as a fallback.
|
|
13
|
-
*/
|
|
14
7
|
notice?: string;
|
|
15
|
-
/**
|
|
16
|
-
* Present when partial scanning was used (non-WordBin payloads).
|
|
17
|
-
* Lists raw byte sequences that had no dictionary match, in order.
|
|
18
|
-
*/
|
|
19
8
|
rawSegments?: string[];
|
|
20
9
|
}
|
|
21
10
|
export declare class WordBin {
|
|
@@ -30,29 +19,11 @@ export declare class WordBin {
|
|
|
30
19
|
debug?: boolean;
|
|
31
20
|
}): Promise<WordBin>;
|
|
32
21
|
private getMapsForVersion;
|
|
22
|
+
private tryRecoverWordsFromHex;
|
|
23
|
+
private validateDecodedWords;
|
|
33
24
|
encode(text: string | EncodeResult | Uint8Array, options?: {
|
|
34
25
|
dictVersion?: number;
|
|
35
26
|
}): Promise<EncodeResult>;
|
|
36
|
-
/**
|
|
37
|
-
* Decodes any supported payload format back to human-readable text.
|
|
38
|
-
*
|
|
39
|
-
* For valid WordBin payloads: returns the exact original words.
|
|
40
|
-
* For non-WordBin payloads: scans byte-by-byte, extracts dictionary words
|
|
41
|
-
* wherever possible, and preserves unrecognised
|
|
42
|
-
* bytes as "[0xXX]" markers.
|
|
43
|
-
*/
|
|
44
27
|
decode(payload: Uint8Array | string): Promise<DecodeResult>;
|
|
45
|
-
/**
|
|
46
|
-
* O(n) longest-match-first decode. Returns null if any byte has no match.
|
|
47
|
-
* This is the fast path; tryDecode is used as a backtracking fallback.
|
|
48
|
-
*/
|
|
49
28
|
private greedyDecode;
|
|
50
|
-
/**
|
|
51
|
-
* Scans through the buffer extracting any recognised dictionary words.
|
|
52
|
-
* Unrecognised bytes are collected as raw segments and rendered as [0xXX].
|
|
53
|
-
* Always consumes the entire buffer — never returns null.
|
|
54
|
-
*/
|
|
55
|
-
private partialScan;
|
|
56
|
-
private tryDecode;
|
|
57
29
|
}
|
|
58
|
-
export {};
|