@twardoch/namzy 1.0.19 → 1.0.23
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/dist/cli.js +19 -16
- package/dist/index.d.ts +7 -8
- package/dist/index.js +15 -21
- package/dist/mangle.d.ts +4 -14
- package/dist/mangle.js +55 -64
- package/dist/web.d.ts +1 -1
- package/dist/web.js +1 -1
- package/dist/wordlist.d.ts +3 -2
- package/dist/wordlist.js +286 -172
- package/package.json +1 -1
- package/src/cli.ts +20 -18
- package/src/index.ts +17 -25
- package/src/mangle.ts +57 -66
- package/src/web.ts +1 -1
- package/src/wordlist.ts +288 -172
package/dist/cli.js
CHANGED
|
@@ -2,22 +2,20 @@
|
|
|
2
2
|
// this_file: src/cli.ts
|
|
3
3
|
import { generate } from "./index.js";
|
|
4
4
|
const HELP = `
|
|
5
|
-
namzy — generate
|
|
5
|
+
namzy — generate compact, memorable, unique names
|
|
6
6
|
|
|
7
7
|
Usage:
|
|
8
8
|
namzy [options]
|
|
9
9
|
|
|
10
10
|
Options:
|
|
11
11
|
--count <N> Number of names to generate (default: 1)
|
|
12
|
+
--seed <N> Integer seed (default: current timestamp)
|
|
12
13
|
--help Show this help
|
|
13
|
-
|
|
14
|
-
Examples:
|
|
15
|
-
namzy
|
|
16
|
-
namzy --count 5
|
|
17
14
|
`.trimStart();
|
|
18
15
|
function parseArgs(argv) {
|
|
19
16
|
const args = argv.slice(2);
|
|
20
17
|
let count = 1;
|
|
18
|
+
let seed;
|
|
21
19
|
for (let i = 0; i < args.length; i++) {
|
|
22
20
|
const arg = args[i];
|
|
23
21
|
switch (arg) {
|
|
@@ -29,28 +27,33 @@ function parseArgs(argv) {
|
|
|
29
27
|
case "--count": {
|
|
30
28
|
const n = parseInt(args[++i], 10);
|
|
31
29
|
if (Number.isNaN(n) || n < 1) {
|
|
32
|
-
process.stderr.write(
|
|
30
|
+
process.stderr.write("--count must be a positive integer\n");
|
|
33
31
|
process.exit(1);
|
|
34
32
|
}
|
|
35
33
|
count = n;
|
|
36
34
|
break;
|
|
37
35
|
}
|
|
36
|
+
case "--seed": {
|
|
37
|
+
const n = parseInt(args[++i], 10);
|
|
38
|
+
if (Number.isNaN(n)) {
|
|
39
|
+
process.stderr.write("--seed must be an integer\n");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
seed = n;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
38
45
|
default:
|
|
39
46
|
process.stderr.write(`Unknown flag: ${arg}\nRun namzy --help for usage.\n`);
|
|
40
47
|
process.exit(1);
|
|
41
48
|
}
|
|
42
49
|
}
|
|
43
|
-
return { count };
|
|
50
|
+
return { count, seed };
|
|
44
51
|
}
|
|
45
|
-
|
|
46
|
-
const { count } = parseArgs(process.argv);
|
|
47
|
-
const base = Date.now();
|
|
52
|
+
function main() {
|
|
53
|
+
const { count, seed } = parseArgs(process.argv);
|
|
54
|
+
const base = seed ?? Date.now();
|
|
48
55
|
for (let i = 0; i < count; i++) {
|
|
49
|
-
|
|
50
|
-
process.stdout.write(`${name}\n`);
|
|
56
|
+
process.stdout.write(`${generate({ seed: base + i * 2654435761 })}\n`);
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
|
-
main()
|
|
54
|
-
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
55
|
-
process.exit(1);
|
|
56
|
-
});
|
|
59
|
+
main();
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { applyRotation, buildName, mulberry32 } from "./mangle.js";
|
|
2
2
|
export interface NamzyOptions {
|
|
3
3
|
seed?: number;
|
|
4
4
|
}
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
export {
|
|
11
|
-
export { activeRotationMask, joinClean, mangle, mulberry32 };
|
|
5
|
+
/** Generate one namzy name. Default seed is the current timestamp. */
|
|
6
|
+
export declare function generate(opts?: NamzyOptions): string;
|
|
7
|
+
/** Generate `count` names. Distinct seeds derived from the base seed. */
|
|
8
|
+
export declare function generateMany(count: number, opts?: NamzyOptions): string[];
|
|
9
|
+
export { STEMS, ROTATIONS } from "./wordlist.js";
|
|
10
|
+
export { applyRotation, buildName, mulberry32 };
|
package/dist/index.js
CHANGED
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
// this_file: src/index.ts
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
function
|
|
5
|
-
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
6
|
-
}
|
|
7
|
-
function pick(arr, rng) {
|
|
8
|
-
return arr[Math.floor(rng() * arr.length)];
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Generate a single fused, mangled namzy name.
|
|
12
|
-
* Two raw words → junction-cleaned fusion → consonant rotation → capitalize.
|
|
13
|
-
*/
|
|
14
|
-
export async function generate(opts) {
|
|
2
|
+
import { applyRotation, buildName, mulberry32 } from "./mangle.js";
|
|
3
|
+
/** Generate one namzy name. Default seed is the current timestamp. */
|
|
4
|
+
export function generate(opts) {
|
|
15
5
|
const seed = opts?.seed ?? Date.now();
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
6
|
+
return buildName(mulberry32(seed));
|
|
7
|
+
}
|
|
8
|
+
/** Generate `count` names. Distinct seeds derived from the base seed. */
|
|
9
|
+
export function generateMany(count, opts) {
|
|
10
|
+
const base = opts?.seed ?? Date.now();
|
|
11
|
+
const out = [];
|
|
12
|
+
for (let i = 0; i < count; i++) {
|
|
13
|
+
out.push(generate({ seed: base + i * 2654435761 }));
|
|
14
|
+
}
|
|
15
|
+
return out;
|
|
22
16
|
}
|
|
23
|
-
export {
|
|
24
|
-
export {
|
|
17
|
+
export { STEMS, ROTATIONS } from "./wordlist.js";
|
|
18
|
+
export { applyRotation, buildName, mulberry32 };
|
package/dist/mangle.d.ts
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
/** Simple seeded mulberry32 RNG — returns a function that yields [0,1) floats */
|
|
2
1
|
export declare function mulberry32(seed: number): () => number;
|
|
3
|
-
|
|
4
|
-
export declare
|
|
5
|
-
/**
|
|
6
|
-
export declare function
|
|
7
|
-
/** Consonant rotation. Case-preserving. Defaults to all rules for direct helper use. */
|
|
8
|
-
export declare function mangle(s: string, activeMask?: number): string;
|
|
9
|
-
/**
|
|
10
|
-
* Fuse two lowercase words at a clean junction.
|
|
11
|
-
* Drops the first char of `b` while it duplicates the tail of `a`,
|
|
12
|
-
* or while the seam is vowel-vowel. Max two trims.
|
|
13
|
-
*/
|
|
14
|
-
export declare function joinClean(a: string, b: string): string;
|
|
15
|
-
export {};
|
|
2
|
+
/** Apply one rotation at a randomly chosen matching position. */
|
|
3
|
+
export declare function applyRotation(s: string, rng: () => number): string;
|
|
4
|
+
/** Build one name with junction validation + one rotation. */
|
|
5
|
+
export declare function buildName(rng: () => number): string;
|
package/dist/mangle.js
CHANGED
|
@@ -1,85 +1,76 @@
|
|
|
1
1
|
// this_file: src/mangle.ts
|
|
2
|
-
|
|
2
|
+
import { BAD_SEAMS, ROTATIONS, STEMS } from "./wordlist.js";
|
|
3
|
+
const MAX_LEN = 12;
|
|
4
|
+
const MAX_TRIES = 8;
|
|
3
5
|
export function mulberry32(seed) {
|
|
4
6
|
let s = seed >>> 0;
|
|
5
7
|
return () => {
|
|
6
|
-
s
|
|
8
|
+
s = (s + 0x6d2b79f5) >>> 0;
|
|
7
9
|
let t = Math.imul(s ^ (s >>> 15), 1 | s);
|
|
8
10
|
t ^= t + Math.imul(t ^ (t >>> 7), 61 | t);
|
|
9
11
|
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
10
12
|
};
|
|
11
13
|
}
|
|
12
|
-
|
|
13
|
-
[
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
["v", "f"],
|
|
20
|
-
["w", "u"],
|
|
21
|
-
["b", "p"],
|
|
22
|
-
["p", "b"],
|
|
23
|
-
];
|
|
24
|
-
const ALL_ROTATIONS = (1 << ROTATION_RULES.length) - 1;
|
|
25
|
-
function rotationFor(lower, activeMask) {
|
|
26
|
-
for (let i = 0; i < ROTATION_RULES.length; i++) {
|
|
27
|
-
if ((activeMask & (1 << i)) === 0) {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
const [from, to] = ROTATION_RULES[i];
|
|
31
|
-
if (lower === from) {
|
|
32
|
-
return to;
|
|
33
|
-
}
|
|
14
|
+
function pick(arr, rng) {
|
|
15
|
+
return arr[Math.floor(rng() * arr.length)];
|
|
16
|
+
}
|
|
17
|
+
function hasTripleLetter(s) {
|
|
18
|
+
for (let i = 2; i < s.length; i++) {
|
|
19
|
+
if (s[i] === s[i - 1] && s[i] === s[i - 2])
|
|
20
|
+
return true;
|
|
34
21
|
}
|
|
35
|
-
return
|
|
22
|
+
return false;
|
|
36
23
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
24
|
+
function junctionUgly(compound, junction) {
|
|
25
|
+
// Inspect the 4-char window centered on the junction.
|
|
26
|
+
const start = Math.max(0, junction - 2);
|
|
27
|
+
const end = Math.min(compound.length, junction + 2);
|
|
28
|
+
const win = compound.slice(start, end);
|
|
29
|
+
for (const seam of BAD_SEAMS) {
|
|
30
|
+
if (win.includes(seam))
|
|
31
|
+
return true;
|
|
44
32
|
}
|
|
45
|
-
return
|
|
33
|
+
return false;
|
|
46
34
|
}
|
|
47
|
-
/**
|
|
48
|
-
export function
|
|
49
|
-
|
|
50
|
-
for (
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
out += ch === lower ? repl : repl.toUpperCase();
|
|
35
|
+
/** Apply one rotation at a randomly chosen matching position. */
|
|
36
|
+
export function applyRotation(s, rng) {
|
|
37
|
+
const matches = [];
|
|
38
|
+
for (let i = 0; i < s.length; i++) {
|
|
39
|
+
for (const [src, dst] of ROTATIONS) {
|
|
40
|
+
if (s.slice(i, i + src.length) === src) {
|
|
41
|
+
matches.push({ i, src, dst });
|
|
42
|
+
}
|
|
58
43
|
}
|
|
59
44
|
}
|
|
60
|
-
|
|
45
|
+
if (matches.length === 0)
|
|
46
|
+
return s;
|
|
47
|
+
const m = matches[Math.floor(rng() * matches.length)];
|
|
48
|
+
return s.slice(0, m.i) + m.dst + s.slice(m.i + m.src.length);
|
|
61
49
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
tail = tail.slice(1);
|
|
50
|
+
function capitalize(s) {
|
|
51
|
+
return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
|
|
52
|
+
}
|
|
53
|
+
/** Build one name with junction validation + one rotation. */
|
|
54
|
+
export function buildName(rng) {
|
|
55
|
+
let best = "";
|
|
56
|
+
for (let attempt = 0; attempt < MAX_TRIES; attempt++) {
|
|
57
|
+
const a = pick(STEMS, rng);
|
|
58
|
+
const b = pick(STEMS, rng);
|
|
59
|
+
const compound = a + b;
|
|
60
|
+
if (compound.length > MAX_LEN) {
|
|
61
|
+
best = best || compound;
|
|
62
|
+
continue;
|
|
76
63
|
}
|
|
77
|
-
|
|
78
|
-
|
|
64
|
+
if (hasTripleLetter(compound)) {
|
|
65
|
+
best = best || compound;
|
|
66
|
+
continue;
|
|
79
67
|
}
|
|
80
|
-
|
|
81
|
-
|
|
68
|
+
if (junctionUgly(compound, a.length)) {
|
|
69
|
+
best = best || compound;
|
|
70
|
+
continue;
|
|
82
71
|
}
|
|
72
|
+
return capitalize(applyRotation(compound, rng));
|
|
83
73
|
}
|
|
84
|
-
|
|
74
|
+
// All attempts had some flaw; rotate and ship the first candidate anyway.
|
|
75
|
+
return capitalize(applyRotation(best, rng));
|
|
85
76
|
}
|
package/dist/web.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { generate,
|
|
1
|
+
export { generate, generateMany, applyRotation, buildName, mulberry32, STEMS, ROTATIONS } from "./index.js";
|
|
2
2
|
export type { NamzyOptions } from "./index.js";
|
package/dist/web.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// this_file: src/web.ts
|
|
2
2
|
// Browser entry point — exposes namzy.generate() on window.namzy.
|
|
3
|
-
export { generate,
|
|
3
|
+
export { generate, generateMany, applyRotation, buildName, mulberry32, STEMS, ROTATIONS } from "./index.js";
|
package/dist/wordlist.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
export declare const
|
|
1
|
+
export declare const STEMS: readonly string[];
|
|
2
|
+
export declare const ROTATIONS: readonly (readonly [string, string])[];
|
|
3
|
+
export declare const BAD_SEAMS: readonly string[];
|