Skip to content

Commit bbaa23d

Browse files
committed
risk and total functions
- risk given bits and total - total given bits and risk - generator functions for risk and total - also remove small total from bits calc
1 parent c6b66bd commit bbaa23d

File tree

12 files changed

+348
-103
lines changed

12 files changed

+348
-103
lines changed

.vscode/launch.json

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,68 @@
77
"request": "launch",
88
"name": "Debug Active Spec",
99
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ava",
10-
"runtimeArgs": ["debug", "--break", "--serial", "${file}"],
10+
"runtimeArgs": [
11+
"debug",
12+
"--break",
13+
"--serial",
14+
"${file}"
15+
],
1116
"outputCapture": "std",
12-
"skipFiles": ["<node_internals>/**/*.js"],
17+
"skipFiles": [
18+
"<node_internals>/**/*.js"
19+
],
1320
"preLaunchTask": "npm: build",
14-
"smartStep": true
21+
"smartStep": true,
22+
"sourceMaps": true,
23+
"resolveSourceMapLocations": [
24+
"${workspaceFolder}/**",
25+
"!**/node_modules/**"
26+
]
1527
},
1628
{
1729
// Use this one if you're already running `yarn watch`
1830
"type": "node",
1931
"request": "launch",
2032
"name": "Debug Active Spec (no build)",
2133
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ava",
22-
"runtimeArgs": ["debug", "--break", "--serial", "${file}"],
34+
"runtimeArgs": [
35+
"debug",
36+
"--break",
37+
"--serial",
38+
"${file}"
39+
],
2340
"outputCapture": "std",
24-
"skipFiles": ["<node_internals>/**/*.js"],
25-
"smartStep": true
26-
}]
27-
}
41+
"skipFiles": [
42+
"<node_internals>/**/*.js"
43+
],
44+
"smartStep": true,
45+
"sourceMaps": true,
46+
"resolveSourceMapLocations": [
47+
"${workspaceFolder}/**",
48+
"!**/node_modules/**"
49+
]
50+
},
51+
{
52+
// Alternative: Use ts-node directly for more reliable debugging
53+
"type": "node",
54+
"request": "launch",
55+
"name": "Debug with ts-node",
56+
"program": "${workspaceFolder}/node_modules/.bin/ava",
57+
"args": [
58+
"${file}"
59+
],
60+
"runtimeArgs": [
61+
"-r",
62+
"ts-node/register"
63+
],
64+
"env": {
65+
"TS_NODE_PROJECT": "${workspaceFolder}/tsconfig.test.json"
66+
},
67+
"sourceMaps": true,
68+
"resolveSourceMapLocations": [
69+
"${workspaceFolder}/**",
70+
"!**/node_modules/**"
71+
]
72+
}
73+
]
74+
}

.vscode/settings.json

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,27 @@
66
"**/.nyc_output": true
77
},
88
"cSpell.words": [
9+
"aeiou",
910
"alphanum",
11+
"ATCG",
1012
"bitauth",
1113
"Crockford",
14+
"dîngøsky",
15+
"dingoskyme",
16+
"FFTFTTFFTFTTFFTT",
17+
"GACGGTCG",
18+
"insgkskn",
19+
"îøsîndøk",
20+
"kiyooodd",
21+
"KPGS",
1222
"libauth",
13-
"Puid"
23+
"PQIB",
24+
"Puid",
25+
"qbhujm",
26+
"TBHY",
27+
"TTACCCAC",
28+
"TTTTTFTTFFFFFTFF",
29+
"TWQZAA",
30+
"ydkîsnsd"
1431
]
1532
}

README.md

Lines changed: 105 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -54,65 +54,6 @@ Random string generation can be thought of as a _transformation_ of some random
5454

5555
> `puid-js` allows an intuitive, explicit specification of ID randomness
5656
57-
[TOC](#TOC)
58-
59-
### <a name="UUIDv4Migration"></a>Migrating from UUID v4
60-
61-
- UUID v4 has 122 bits of entropy (36 chars with hyphens; 32 hex chars without). Default `puid-js` IDs are ~132 bits in 22 URL/file-safe chars.
62-
63-
Replace uuidv4() one-off
64-
65-
```js
66-
// before
67-
import { v4 as uuidv4 } from 'uuid'
68-
const id = uuidv4()
69-
70-
// after
71-
import { generate, Chars } from 'puid-js'
72-
// ≈132 bits, 22 chars, URL/file-safe
73-
const id = generate({ chars: Chars.Safe64 })
74-
75-
// hex-like (32 chars, 128 bits)
76-
const hexId = generate({ bits: 128, chars: Chars.HexUpper })
77-
```
78-
79-
Use a generator in hot paths
80-
81-
```js
82-
import { puid, Chars } from 'puid-js'
83-
84-
// explicit bits (≈ UUID v4 or better)
85-
const { generator: id128 } = puid({ bits: 128, chars: Chars.Safe64 })
86-
87-
// or size by total/risk (10M IDs, 1e-12 repeat risk)
88-
const { generator: sized } = puid({ total: 1e7, risk: 1e12, chars: Chars.Safe64 })
89-
90-
const id = id128()
91-
```
92-
93-
Browser
94-
95-
```js
96-
import { generate, Chars } from 'puid-js/web'
97-
const id = generate({ chars: Chars.Safe64 })
98-
```
99-
100-
Error handling for generate()
101-
102-
```js
103-
try {
104-
generate({ total: 1000 }) // invalid: missing risk
105-
} catch (err) {
106-
// handle invalid config
107-
}
108-
```
109-
110-
Notes
111-
- If your DB/validators assume UUID format/length, update to accept generic ID strings. Default Safe64 is 22 chars; HexUpper at 128 bits is 32 chars.
112-
- Charset guidance: Safe64 (shortest URL/file-safe), Hex/HexUpper (compat), Safe32/WordSafe32 (human-friendlier).
113-
114-
And remember, you rarely need the 122-bytes of entropy provided by UUID, and you certainly never need the inefficiency of the string representation!
115-
11657
### <a name="Usage"></a>Usage
11758

11859
Creating a random ID generator using `puid-js` is as simple as:
@@ -277,16 +218,19 @@ The optional `PuidConfig` object has the following fields:
277218
- `chars`: `Chars.Safe64`
278219
- `entropyBytes`: `crypto.randomBytes`
279220

280-
#### PuidInfo
221+
#### `generator` API
222+
The `puid` generator function includes:
223+
- `info` field that displays generator configuration
224+
- `risk/1` function that approximates the `risk` of a repeat given a `total` number of IDs
225+
- `total/1` function that approximates the `total` possible IDs for a given `risk`
281226

282-
The `puid` generator function includes an `info` field that displays generator configuration:
283-
284-
- `bits`: ID entropy
285-
- `bitsPerChar`: Entropy bits per ID character
286-
- `chars`: Source characters
287-
- `charsName`: Name of pre-defined `Chars` or `custom`
288-
- `ere`: Entropy representation efficiency
289-
- `length`: ID string length
227+
- `info`
228+
- `bits`: ID entropy
229+
- `bitsPerChar`: Entropy bits per ID character
230+
- `chars`: Source characters
231+
- `charsName`: Name of pre-defined `Chars` or `custom`
232+
- `ere`: Entropy representation efficiency
233+
- `length`: ID string length
290234

291235
Example:
292236

@@ -309,6 +253,42 @@ Example:
309253
}
310254
```
311255

256+
- `risk`
257+
For the `generator`, an approximate `risk` given a number of `total` possible IDs.
258+
This is useful when specifying puid `bits` to inspect the `risk` for some `total`.
259+
260+
Example: (output is rounded). And remember, read this as '1 in `risk` chance of repeat'
261+
```js
262+
const { Chars, puid } = require('puid-js')
263+
const { generator: genId } = puid({ bits: 96, chars: Chars.Safe32 })
264+
265+
genId.risk(1e6)
266+
// => 2535303735760194600
267+
genId.risk(1e9)
268+
// => 2535301202992
269+
genId.risk(1e12)
270+
// => 2535301
271+
```
272+
273+
- `total`
274+
For the `generator`, an approximate number of `total` possible IDs for a given `risk`
275+
This is useful when specifying puid `bits` to inspect the possible `total` given some `risk`.
276+
277+
Example: (output is rounded).
278+
```js
279+
const { Chars, puid } = require('puid-js')
280+
const { generator: genId } = puid({ bits: 96, chars: Chars.Safe32 })
281+
282+
genId.total(1e9)
283+
// => 50351774552
284+
genId.total(1e12)
285+
// => 1592262910
286+
genId.total(1e15)
287+
// => 50351775
288+
```
289+
290+
291+
312292
### <a name="Chars"></a>Chars
313293

314294
There are 19 pre-defined character sets:
@@ -564,3 +544,60 @@ Hmmm. Looks like there are 500,000 IDs expected and the repeat risk is 1 in a tr
564544
```
565545

566546
[TOC](#TOC)
547+
548+
### <a name="UUIDv4Migration"></a>Migrating from UUID v4
549+
550+
- UUID v4 has 122 bits of entropy (36 chars with hyphens; 32 hex chars without). Default `puid-js` IDs are ~132 bits in 22 URL/file-safe chars.
551+
552+
Replace uuidv4() one-off
553+
554+
```js
555+
// before
556+
import { v4 as uuidv4 } from 'uuid'
557+
const id = uuidv4()
558+
559+
// after
560+
import { generate, Chars } from 'puid-js'
561+
// ≈132 bits, 22 chars, URL/file-safe
562+
const id = generate({ chars: Chars.Safe64 })
563+
564+
// hex-like (32 chars, 128 bits)
565+
const hexId = generate({ bits: 128, chars: Chars.HexUpper })
566+
```
567+
568+
Use a generator in hot paths
569+
570+
```js
571+
import { puid, Chars } from 'puid-js'
572+
573+
// explicit bits (≈ UUID v4 or better)
574+
const { generator: id128 } = puid({ bits: 128, chars: Chars.Safe64 })
575+
576+
// or size by total/risk (10M IDs, 1e-12 repeat risk)
577+
const { generator: sized } = puid({ total: 1e7, risk: 1e12, chars: Chars.Safe64 })
578+
579+
const id = id128()
580+
```
581+
582+
Browser
583+
584+
```js
585+
import { generate, Chars } from 'puid-js/web'
586+
const id = generate({ chars: Chars.Safe64 })
587+
```
588+
589+
Error handling for generate()
590+
591+
```js
592+
try {
593+
generate({ total: 1000 }) // invalid: missing risk
594+
} catch (err) {
595+
// handle invalid config
596+
}
597+
```
598+
599+
Notes
600+
- If your DB/validators assume UUID format/length, update to accept generic ID strings. Default Safe64 is 22 chars; HexUpper at 128 bits is 32 chars.
601+
- Charset guidance: Safe64 (shortest URL/file-safe), Hex/HexUpper (compat), Safe32/WordSafe32 (human-friendlier).
602+
603+
And remember, you rarely need the 122-bytes of entropy provided by UUID, and you certainly never need the inefficiency of the string representation!

src/generate.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import test from 'ava'
22

3-
import { generate, Chars } from './index'
43
import puid from './lib/puid'
54

5+
import { Chars, generate } from './index'
6+
67
// Convenience wrapper should produce a string and match the HOF-derived length
78
test('generate() default matches HOF length', (t) => {
89
const { generator } = puid()

src/lib/bits.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export default (puidLen: number, puidChars: string, entropyFunction: EntropyFunc
143143
const nBytesPerPuid = ceil(nBitsPerPuid / 8)
144144

145145
const bufferLen = nBytesPerPuid + 1
146-
146+
147147
let entropyOffset = 8 * bufferLen
148148
const entropyBuffer = new ArrayBuffer(bufferLen)
149149
const entropyBytes = new Uint8Array(entropyBuffer)
@@ -158,9 +158,7 @@ export default (puidLen: number, puidChars: string, entropyFunction: EntropyFunc
158158
entropyOffset = fillEntropy(entropyOffset, entropyBuffer, entropyFunction)
159159
const codes = new Array<number>(puidLen)
160160
for (let i = 0; i < puidLen; i++) {
161-
codes[i] = charsEncoder(
162-
valueAt(entropyOffset + i * nBitsPerChar, nBitsPerChar, entropyBytes)
163-
)
161+
codes[i] = charsEncoder(valueAt(entropyOffset + i * nBitsPerChar, nBitsPerChar, entropyBytes))
164162
}
165163
entropyOffset += nBitsPerPuid
166164
return String.fromCharCode(...codes)

0 commit comments

Comments
 (0)