@ekaone/entropy 0.0.1 → 0.1.1
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 +20 -20
- package/README.md +128 -130
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Eka Prasetia <ekaone3033@gmail.com> (https://prasetia.me)
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eka Prasetia <ekaone3033@gmail.com> (https://prasetia.me)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,131 +1,129 @@
|
|
|
1
|
-
# @ekaone/entropy
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
import {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
|
69
|
-
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
|
86
|
-
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
|
95
|
-
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
- [npm Package](https://www.npmjs.com/package/@ekaone/entropy)
|
|
130
|
-
- [GitHub Repository](https://github.com/ekaone/entropy)
|
|
1
|
+
# @ekaone/entropy
|
|
2
|
+
|
|
3
|
+
Primitive Shannon entropy measurement for strings.
|
|
4
|
+
|
|
5
|
+
Measures **randomness density** — best used for generated tokens, secrets, and API keys. Not intended for judging human-chosen passwords. Refer to [Shannon entropy](https://en.wikipedia.org/wiki/Entropy_(information_theory)) for details.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @ekaone/entropy
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
yarn add @ekaone/entropy
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @ekaone/entropy
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### Basic — pure measurement
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { calculate } from "@ekaone/entropy"
|
|
27
|
+
|
|
28
|
+
calculate("hsg3-3;gs")
|
|
29
|
+
// { entropy: 3.46, length: 9, unique: 8 }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### With `level` option
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
calculate("hsg3-3;gs", { level: true })
|
|
36
|
+
// { entropy: 3.46, length: 9, unique: 8, level: "medium" }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### With `charset` option
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
calculate("hsg3-3;gs", { level: true, charset: true })
|
|
43
|
+
// { entropy: 3.46, length: 9, unique: 8, level: "medium", charset: "mixed" }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Custom thresholds
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
calculate("hsg3-3;gs", {
|
|
50
|
+
level: { thresholds: { high: 5.0 } }
|
|
51
|
+
})
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Tree-shakeable primitives
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { shannonEntropy } from "@ekaone/entropy"
|
|
58
|
+
import { classifyLevel, DEFAULT_THRESHOLDS } from "@ekaone/entropy"
|
|
59
|
+
import { detectCharset } from "@ekaone/entropy"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## API
|
|
63
|
+
|
|
64
|
+
### `calculate(input, options?)`
|
|
65
|
+
|
|
66
|
+
| Field | Type | Description |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| `entropy` | `number` | Shannon bits per character |
|
|
69
|
+
| `length` | `number` | String length |
|
|
70
|
+
| `unique` | `number` | Count of distinct characters |
|
|
71
|
+
| `level` | `EntropyLevel` | Returned when `options.level` is set |
|
|
72
|
+
| `charset` | `CharSet` | Returned when `options.charset` is set |
|
|
73
|
+
|
|
74
|
+
### Options
|
|
75
|
+
|
|
76
|
+
| Option | Type | Description |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `level` | `boolean \| { thresholds? }` | Enable level classification |
|
|
79
|
+
| `charset` | `boolean` | Enable charset detection |
|
|
80
|
+
|
|
81
|
+
### Entropy levels (defaults)
|
|
82
|
+
|
|
83
|
+
| Level | Bits | Typical example |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `low` | < 2.5 | `"aaaa"`, `"1111"` |
|
|
86
|
+
| `medium` | 2.5–3.5 | `"hsg3-3;gs"` |
|
|
87
|
+
| `high` | 3.5–4.5 | random hex hashes |
|
|
88
|
+
| `critical` | ≥ 4.5 | API keys, JWT secrets |
|
|
89
|
+
|
|
90
|
+
### Charset detection
|
|
91
|
+
|
|
92
|
+
| Charset | Rule |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `numeric` | digits only |
|
|
95
|
+
| `alpha` | letters only |
|
|
96
|
+
| `hex` | `[0-9a-fA-F]` with both digits and letters, no letters outside `a-f` |
|
|
97
|
+
| `alphanumeric` | letters + digits, not purely hex |
|
|
98
|
+
| `mixed` | contains special characters |
|
|
99
|
+
|
|
100
|
+
> **Note:** `"deadbeef"` classifies as `alpha` (no digits). `"abc123"` classifies as `hex` (all chars within hex range). This is expected — see [charset ambiguity](#charset-ambiguity).
|
|
101
|
+
|
|
102
|
+
### Charset ambiguity
|
|
103
|
+
|
|
104
|
+
Strings using only `[0-9a-fA-F]` characters are inherently ambiguous. `@ekaone/entropy` resolves this by requiring hex strings to contain **both digits and letters within the `a-f` range**. Pure-letter strings always classify as `alpha` regardless of whether their characters fall within hex range.
|
|
105
|
+
|
|
106
|
+
## Building on top
|
|
107
|
+
|
|
108
|
+
This package is a primitive. Higher-level opinions live in consumer packages:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { calculate } from "@ekaone/entropy"
|
|
112
|
+
|
|
113
|
+
// your app defines "strong"
|
|
114
|
+
const { entropy, length } = calculate(token)
|
|
115
|
+
const isStrong = entropy >= 4.0 && length >= 16
|
|
116
|
+
|
|
117
|
+
// your UI defines the label
|
|
118
|
+
const score = Math.round((entropy / 6.55) * 100)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT © Eka Prasetia
|
|
124
|
+
|
|
125
|
+
## Links
|
|
126
|
+
|
|
127
|
+
- [npm Package](https://www.npmjs.com/package/@ekaone/entropy)
|
|
128
|
+
- [GitHub Repository](https://github.com/ekaone/entropy)
|
|
131
129
|
- [Issue Tracker](https://github.com/ekaone/entropy/issues)
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/calculate.ts","../src/level.ts","../src/charset.ts"],"sourcesContent":["/**\n * index.ts\n * @description Primitive Shannon entropy measurement for strings.\n * @author Eka Prasetia\n */\n\nimport { shannonEntropy } from \"./calculate\";\nimport { classifyLevel, resolveThresholds } from \"./level\";\nimport { detectCharset } from \"./charset\";\nimport type { EntropyOptions, EntropyResult } from \"./types\";\n\nexport function calculate(\n input: string,\n options?: EntropyOptions,\n): EntropyResult {\n const entropy = shannonEntropy(input);\n\n const result: EntropyResult = {\n entropy,\n length: input.length,\n unique: new Set(input).size,\n };\n\n if (options?.level) {\n const thresholds = resolveThresholds(\n typeof options.level === \"object\" ? options.level.thresholds : undefined,\n );\n result.level = classifyLevel(entropy, thresholds);\n }\n\n if (options?.charset) {\n result.charset = detectCharset(input);\n }\n\n return result;\n}\n\n// named exports for tree-shaking\nexport { shannonEntropy } from \"./calculate\";\nexport { classifyLevel, resolveThresholds, DEFAULT_THRESHOLDS } from \"./level\";\nexport { detectCharset } from \"./charset\";\nexport type {\n EntropyOptions,\n EntropyResult,\n EntropyLevel,\n CharSet,\n EntropyThresholds,\n} from \"./types\";\n","/**\
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/calculate.ts","../src/level.ts","../src/charset.ts"],"sourcesContent":["/**\n * index.ts\n * @description Primitive Shannon entropy measurement for strings.\n * @author Eka Prasetia\n */\n\nimport { shannonEntropy } from \"./calculate\";\nimport { classifyLevel, resolveThresholds } from \"./level\";\nimport { detectCharset } from \"./charset\";\nimport type { EntropyOptions, EntropyResult } from \"./types\";\n\nexport function calculate(\n input: string,\n options?: EntropyOptions,\n): EntropyResult {\n const entropy = shannonEntropy(input);\n\n const result: EntropyResult = {\n entropy,\n length: input.length,\n unique: new Set(input).size,\n };\n\n if (options?.level) {\n const thresholds = resolveThresholds(\n typeof options.level === \"object\" ? options.level.thresholds : undefined,\n );\n result.level = classifyLevel(entropy, thresholds);\n }\n\n if (options?.charset) {\n result.charset = detectCharset(input);\n }\n\n return result;\n}\n\n// named exports for tree-shaking\nexport { shannonEntropy } from \"./calculate\";\nexport { classifyLevel, resolveThresholds, DEFAULT_THRESHOLDS } from \"./level\";\nexport { detectCharset } from \"./charset\";\nexport type {\n EntropyOptions,\n EntropyResult,\n EntropyLevel,\n CharSet,\n EntropyThresholds,\n} from \"./types\";\n","/**\n * Calculates Shannon entropy of a string.\n * Returns bits of entropy per character (0 to ~6.55 for printable ASCII).\n *\n * Best used for measuring randomness density of generated strings,\n * tokens, secrets, and API keys — not human-chosen passwords.\n *\n * @see https://en.wikipedia.org/wiki/Entropy_(information_theory)\n */\nexport function shannonEntropy(input: string): number {\n if (input.length === 0) return 0;\n\n const freq = new Map<string, number>();\n\n for (const char of input) {\n freq.set(char, (freq.get(char) ?? 0) + 1);\n }\n\n const len = input.length;\n let entropy = 0;\n\n for (const count of freq.values()) {\n const p = count / len;\n entropy -= p * Math.log2(p);\n }\n\n return Math.round(entropy * 1000) / 1000; // 3 decimal precision\n}\n","import type { EntropyLevel, EntropyThresholds } from \"./types\";\n\nexport const DEFAULT_THRESHOLDS: EntropyThresholds = {\n low: 2.5,\n medium: 3.5,\n high: 4.5,\n // above 4.5 → \"critical\"\n};\n\nexport function classifyLevel(\n entropy: number,\n thresholds: EntropyThresholds = DEFAULT_THRESHOLDS,\n): EntropyLevel {\n if (entropy < thresholds.low) return \"low\";\n if (entropy < thresholds.medium) return \"medium\";\n if (entropy < thresholds.high) return \"high\";\n return \"critical\";\n}\n\nexport function resolveThresholds(\n overrides?: Partial<EntropyThresholds>,\n): EntropyThresholds {\n return { ...DEFAULT_THRESHOLDS, ...overrides };\n}\n","import type { CharSet } from \"./types\";\n\nconst ONLY_DIGITS = /^[0-9]+$/;\nconst ONLY_ALPHA = /^[a-zA-Z]+$/;\nconst ONLY_HEX = /^[0-9a-fA-F]+$/;\nconst ONLY_ALPHANUM = /^[a-zA-Z0-9]+$/;\nconst HAS_DIGIT = /[0-9]/;\nconst HAS_ALPHA = /[a-zA-Z]/;\nconst HAS_NON_HEX_LETTER = /[g-zG-Z]/; // letters outside hex range\n\n/**\n * Detects the character set family of a string.\n *\n * Priority:\n * 1. numeric — digits only\n * 2. alpha — letters only (checked before hex to avoid \"deadbeef\" → hex)\n * 3. hex — hex chars [0-9a-fA-F] AND has both a digit and a letter AND no letters outside a-f range\n * 4. alphanumeric — letters + digits, but NOT purely hex\n * 5. mixed — everything else (has special chars)\n */\nexport function detectCharset(input: string): CharSet {\n if (input.length === 0) return \"mixed\";\n\n if (ONLY_DIGITS.test(input)) return \"numeric\";\n if (ONLY_ALPHA.test(input)) return \"alpha\";\n\n // hex: must match hex pattern AND have both digits and letters AND no letters outside a-f range\n if (\n ONLY_HEX.test(input) &&\n HAS_DIGIT.test(input) &&\n HAS_ALPHA.test(input) &&\n !HAS_NON_HEX_LETTER.test(input)\n )\n return \"hex\";\n\n if (ONLY_ALPHANUM.test(input)) return \"alphanumeric\";\n\n return \"mixed\";\n}\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,wBAAAE,EAAA,cAAAC,EAAA,kBAAAC,EAAA,kBAAAC,EAAA,sBAAAC,EAAA,mBAAAC,IAAA,eAAAC,EAAAR,GCSO,SAASS,EAAeC,EAAuB,CATtD,IAAAC,EAUE,GAAID,EAAM,SAAW,EAAG,MAAO,GAE/B,IAAME,EAAO,IAAI,IAEjB,QAAWC,KAAQH,EACjBE,EAAK,IAAIC,IAAOF,EAAAC,EAAK,IAAIC,CAAI,IAAb,KAAAF,EAAkB,GAAK,CAAC,EAG1C,IAAMG,EAAMJ,EAAM,OACdK,EAAU,EAEd,QAAWC,KAASJ,EAAK,OAAO,EAAG,CACjC,IAAMK,EAAID,EAAQF,EAClBC,GAAWE,EAAI,KAAK,KAAKA,CAAC,CAC5B,CAEA,OAAO,KAAK,MAAMF,EAAU,GAAI,EAAI,GACtC,CCzBO,IAAMG,EAAwC,CACnD,IAAK,IACL,OAAQ,IACR,KAAM,GAER,EAEO,SAASC,EACdC,EACAC,EAAgCH,EAClB,CACd,OAAIE,EAAUC,EAAW,IAAY,MACjCD,EAAUC,EAAW,OAAe,SACpCD,EAAUC,EAAW,KAAa,OAC/B,UACT,CAEO,SAASC,EACdC,EACmB,CACnB,MAAO,CAAE,GAAGL,EAAoB,GAAGK,CAAU,CAC/C,CCrBA,IAAMC,EAAc,WACdC,EAAa,cACbC,EAAW,iBACXC,EAAgB,iBAChBC,EAAY,QACZC,EAAY,WACZC,EAAqB,WAYpB,SAASC,EAAcC,EAAwB,CACpD,OAAIA,EAAM,SAAW,EAAU,QAE3BR,EAAY,KAAKQ,CAAK,EAAU,UAChCP,EAAW,KAAKO,CAAK,EAAU,QAIjCN,EAAS,KAAKM,CAAK,GACnBJ,EAAU,KAAKI,CAAK,GACpBH,EAAU,KAAKG,CAAK,GACpB,CAACF,EAAmB,KAAKE,CAAK,EAEvB,MAELL,EAAc,KAAKK,CAAK,EAAU,eAE/B,OACT,CH3BO,SAASC,EACdC,EACAC,EACe,CACf,IAAMC,EAAUC,EAAeH,CAAK,EAE9BI,EAAwB,CAC5B,QAAAF,EACA,OAAQF,EAAM,OACd,OAAQ,IAAI,IAAIA,CAAK,EAAE,IACzB,EAEA,GAAIC,GAAA,MAAAA,EAAS,MAAO,CAClB,IAAMI,EAAaC,EACjB,OAAOL,EAAQ,OAAU,SAAWA,EAAQ,MAAM,WAAa,MACjE,EACAG,EAAO,MAAQG,EAAcL,EAASG,CAAU,CAClD,CAEA,OAAIJ,GAAA,MAAAA,EAAS,UACXG,EAAO,QAAUI,EAAcR,CAAK,GAG/BI,CACT","names":["index_exports","__export","DEFAULT_THRESHOLDS","calculate","classifyLevel","detectCharset","resolveThresholds","shannonEntropy","__toCommonJS","shannonEntropy","input","_a","freq","char","len","entropy","count","p","DEFAULT_THRESHOLDS","classifyLevel","entropy","thresholds","resolveThresholds","overrides","ONLY_DIGITS","ONLY_ALPHA","ONLY_HEX","ONLY_ALPHANUM","HAS_DIGIT","HAS_ALPHA","HAS_NON_HEX_LETTER","detectCharset","input","calculate","input","options","entropy","shannonEntropy","result","thresholds","resolveThresholds","classifyLevel","detectCharset"]}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/calculate.ts","../src/level.ts","../src/charset.ts","../src/index.ts"],"sourcesContent":["/**\
|
|
1
|
+
{"version":3,"sources":["../src/calculate.ts","../src/level.ts","../src/charset.ts","../src/index.ts"],"sourcesContent":["/**\n * Calculates Shannon entropy of a string.\n * Returns bits of entropy per character (0 to ~6.55 for printable ASCII).\n *\n * Best used for measuring randomness density of generated strings,\n * tokens, secrets, and API keys — not human-chosen passwords.\n *\n * @see https://en.wikipedia.org/wiki/Entropy_(information_theory)\n */\nexport function shannonEntropy(input: string): number {\n if (input.length === 0) return 0;\n\n const freq = new Map<string, number>();\n\n for (const char of input) {\n freq.set(char, (freq.get(char) ?? 0) + 1);\n }\n\n const len = input.length;\n let entropy = 0;\n\n for (const count of freq.values()) {\n const p = count / len;\n entropy -= p * Math.log2(p);\n }\n\n return Math.round(entropy * 1000) / 1000; // 3 decimal precision\n}\n","import type { EntropyLevel, EntropyThresholds } from \"./types\";\n\nexport const DEFAULT_THRESHOLDS: EntropyThresholds = {\n low: 2.5,\n medium: 3.5,\n high: 4.5,\n // above 4.5 → \"critical\"\n};\n\nexport function classifyLevel(\n entropy: number,\n thresholds: EntropyThresholds = DEFAULT_THRESHOLDS,\n): EntropyLevel {\n if (entropy < thresholds.low) return \"low\";\n if (entropy < thresholds.medium) return \"medium\";\n if (entropy < thresholds.high) return \"high\";\n return \"critical\";\n}\n\nexport function resolveThresholds(\n overrides?: Partial<EntropyThresholds>,\n): EntropyThresholds {\n return { ...DEFAULT_THRESHOLDS, ...overrides };\n}\n","import type { CharSet } from \"./types\";\n\nconst ONLY_DIGITS = /^[0-9]+$/;\nconst ONLY_ALPHA = /^[a-zA-Z]+$/;\nconst ONLY_HEX = /^[0-9a-fA-F]+$/;\nconst ONLY_ALPHANUM = /^[a-zA-Z0-9]+$/;\nconst HAS_DIGIT = /[0-9]/;\nconst HAS_ALPHA = /[a-zA-Z]/;\nconst HAS_NON_HEX_LETTER = /[g-zG-Z]/; // letters outside hex range\n\n/**\n * Detects the character set family of a string.\n *\n * Priority:\n * 1. numeric — digits only\n * 2. alpha — letters only (checked before hex to avoid \"deadbeef\" → hex)\n * 3. hex — hex chars [0-9a-fA-F] AND has both a digit and a letter AND no letters outside a-f range\n * 4. alphanumeric — letters + digits, but NOT purely hex\n * 5. mixed — everything else (has special chars)\n */\nexport function detectCharset(input: string): CharSet {\n if (input.length === 0) return \"mixed\";\n\n if (ONLY_DIGITS.test(input)) return \"numeric\";\n if (ONLY_ALPHA.test(input)) return \"alpha\";\n\n // hex: must match hex pattern AND have both digits and letters AND no letters outside a-f range\n if (\n ONLY_HEX.test(input) &&\n HAS_DIGIT.test(input) &&\n HAS_ALPHA.test(input) &&\n !HAS_NON_HEX_LETTER.test(input)\n )\n return \"hex\";\n\n if (ONLY_ALPHANUM.test(input)) return \"alphanumeric\";\n\n return \"mixed\";\n}\n","/**\n * index.ts\n * @description Primitive Shannon entropy measurement for strings.\n * @author Eka Prasetia\n */\n\nimport { shannonEntropy } from \"./calculate\";\nimport { classifyLevel, resolveThresholds } from \"./level\";\nimport { detectCharset } from \"./charset\";\nimport type { EntropyOptions, EntropyResult } from \"./types\";\n\nexport function calculate(\n input: string,\n options?: EntropyOptions,\n): EntropyResult {\n const entropy = shannonEntropy(input);\n\n const result: EntropyResult = {\n entropy,\n length: input.length,\n unique: new Set(input).size,\n };\n\n if (options?.level) {\n const thresholds = resolveThresholds(\n typeof options.level === \"object\" ? options.level.thresholds : undefined,\n );\n result.level = classifyLevel(entropy, thresholds);\n }\n\n if (options?.charset) {\n result.charset = detectCharset(input);\n }\n\n return result;\n}\n\n// named exports for tree-shaking\nexport { shannonEntropy } from \"./calculate\";\nexport { classifyLevel, resolveThresholds, DEFAULT_THRESHOLDS } from \"./level\";\nexport { detectCharset } from \"./charset\";\nexport type {\n EntropyOptions,\n EntropyResult,\n EntropyLevel,\n CharSet,\n EntropyThresholds,\n} from \"./types\";\n"],"mappings":"AASO,SAASA,EAAeC,EAAuB,CATtD,IAAAC,EAUE,GAAID,EAAM,SAAW,EAAG,MAAO,GAE/B,IAAME,EAAO,IAAI,IAEjB,QAAWC,KAAQH,EACjBE,EAAK,IAAIC,IAAOF,EAAAC,EAAK,IAAIC,CAAI,IAAb,KAAAF,EAAkB,GAAK,CAAC,EAG1C,IAAMG,EAAMJ,EAAM,OACdK,EAAU,EAEd,QAAWC,KAASJ,EAAK,OAAO,EAAG,CACjC,IAAMK,EAAID,EAAQF,EAClBC,GAAWE,EAAI,KAAK,KAAKA,CAAC,CAC5B,CAEA,OAAO,KAAK,MAAMF,EAAU,GAAI,EAAI,GACtC,CCzBO,IAAMG,EAAwC,CACnD,IAAK,IACL,OAAQ,IACR,KAAM,GAER,EAEO,SAASC,EACdC,EACAC,EAAgCH,EAClB,CACd,OAAIE,EAAUC,EAAW,IAAY,MACjCD,EAAUC,EAAW,OAAe,SACpCD,EAAUC,EAAW,KAAa,OAC/B,UACT,CAEO,SAASC,EACdC,EACmB,CACnB,MAAO,CAAE,GAAGL,EAAoB,GAAGK,CAAU,CAC/C,CCrBA,IAAMC,EAAc,WACdC,EAAa,cACbC,EAAW,iBACXC,EAAgB,iBAChBC,EAAY,QACZC,EAAY,WACZC,EAAqB,WAYpB,SAASC,EAAcC,EAAwB,CACpD,OAAIA,EAAM,SAAW,EAAU,QAE3BR,EAAY,KAAKQ,CAAK,EAAU,UAChCP,EAAW,KAAKO,CAAK,EAAU,QAIjCN,EAAS,KAAKM,CAAK,GACnBJ,EAAU,KAAKI,CAAK,GACpBH,EAAU,KAAKG,CAAK,GACpB,CAACF,EAAmB,KAAKE,CAAK,EAEvB,MAELL,EAAc,KAAKK,CAAK,EAAU,eAE/B,OACT,CC3BO,SAASC,EACdC,EACAC,EACe,CACf,IAAMC,EAAUC,EAAeH,CAAK,EAE9BI,EAAwB,CAC5B,QAAAF,EACA,OAAQF,EAAM,OACd,OAAQ,IAAI,IAAIA,CAAK,EAAE,IACzB,EAEA,GAAIC,GAAA,MAAAA,EAAS,MAAO,CAClB,IAAMI,EAAaC,EACjB,OAAOL,EAAQ,OAAU,SAAWA,EAAQ,MAAM,WAAa,MACjE,EACAG,EAAO,MAAQG,EAAcL,EAASG,CAAU,CAClD,CAEA,OAAIJ,GAAA,MAAAA,EAAS,UACXG,EAAO,QAAUI,EAAcR,CAAK,GAG/BI,CACT","names":["shannonEntropy","input","_a","freq","char","len","entropy","count","p","DEFAULT_THRESHOLDS","classifyLevel","entropy","thresholds","resolveThresholds","overrides","ONLY_DIGITS","ONLY_ALPHA","ONLY_HEX","ONLY_ALPHANUM","HAS_DIGIT","HAS_ALPHA","HAS_NON_HEX_LETTER","detectCharset","input","calculate","input","options","entropy","shannonEntropy","result","thresholds","resolveThresholds","classifyLevel","detectCharset"]}
|