@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 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
- > Under Active Development, not release yet
4
-
5
- Primitive Shannon entropy measurement for strings.
6
-
7
- 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.
8
-
9
- ## Installation
10
-
11
- ```bash
12
- npm install @ekaone/entropy
13
- ```
14
-
15
- ```bash
16
- yarn add @ekaone/entropy
17
- ```
18
-
19
- ```bash
20
- pnpm add @ekaone/entropy
21
- ```
22
-
23
- ## Usage
24
-
25
- ### Basic — pure measurement
26
-
27
- ```ts
28
- import { calculate } from "@ekaone/entropy"
29
-
30
- calculate("hsg3-3;gs")
31
- // { entropy: 3.46, length: 9, unique: 8 }
32
- ```
33
-
34
- ### With `level` option
35
-
36
- ```ts
37
- calculate("hsg3-3;gs", { level: true })
38
- // { entropy: 3.46, length: 9, unique: 8, level: "medium" }
39
- ```
40
-
41
- ### With `charset` option
42
-
43
- ```ts
44
- calculate("hsg3-3;gs", { level: true, charset: true })
45
- // { entropy: 3.46, length: 9, unique: 8, level: "medium", charset: "mixed" }
46
- ```
47
-
48
- ### Custom thresholds
49
-
50
- ```ts
51
- calculate("hsg3-3;gs", {
52
- level: { thresholds: { high: 5.0 } }
53
- })
54
- ```
55
-
56
- ### Tree-shakeable primitives
57
-
58
- ```ts
59
- import { shannonEntropy } from "@ekaone/entropy"
60
- import { classifyLevel, DEFAULT_THRESHOLDS } from "@ekaone/entropy"
61
- import { detectCharset } from "@ekaone/entropy"
62
- ```
63
-
64
- ## API
65
-
66
- ### `calculate(input, options?)`
67
-
68
- | Field | Type | Description |
69
- |---|---|---|
70
- | `entropy` | `number` | Shannon bits per character |
71
- | `length` | `number` | String length |
72
- | `unique` | `number` | Count of distinct characters |
73
- | `level` | `EntropyLevel` | Returned when `options.level` is set |
74
- | `charset` | `CharSet` | Returned when `options.charset` is set |
75
-
76
- ### Options
77
-
78
- | Option | Type | Description |
79
- |---|---|---|
80
- | `level` | `boolean \| { thresholds? }` | Enable level classification |
81
- | `charset` | `boolean` | Enable charset detection |
82
-
83
- ### Entropy levels (defaults)
84
-
85
- | Level | Bits | Typical example |
86
- |---|---|---|
87
- | `low` | < 2.5 | `"aaaa"`, `"1111"` |
88
- | `medium` | 2.5–3.5 | `"hsg3-3;gs"` |
89
- | `high` | 3.5–4.5 | random hex hashes |
90
- | `critical` | ≥ 4.5 | API keys, JWT secrets |
91
-
92
- ### Charset detection
93
-
94
- | Charset | Rule |
95
- |---|---|
96
- | `numeric` | digits only |
97
- | `alpha` | letters only |
98
- | `hex` | `[0-9a-fA-F]` with both digits and letters, no letters outside `a-f` |
99
- | `alphanumeric` | letters + digits, not purely hex |
100
- | `mixed` | contains special characters |
101
-
102
- > **Note:** `"deadbeef"` classifies as `alpha` (no digits). `"abc123"` classifies as `hex` (all chars within hex range). This is expected — see [charset ambiguity](#charset-ambiguity).
103
-
104
- ### Charset ambiguity
105
-
106
- 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.
107
-
108
- ## Building on top
109
-
110
- This package is a primitive. Higher-level opinions live in consumer packages:
111
-
112
- ```ts
113
- import { calculate } from "@ekaone/entropy"
114
-
115
- // your app defines "strong"
116
- const { entropy, length } = calculate(token)
117
- const isStrong = entropy >= 4.0 && length >= 16
118
-
119
- // your UI defines the label
120
- const score = Math.round((entropy / 6.55) * 100)
121
- ```
122
-
123
- ## License
124
-
125
- MIT © Eka Prasetia
126
-
127
- ## Links
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","/**\r\n * Calculates Shannon entropy of a string.\r\n * Returns bits of entropy per character (0 to ~6.55 for printable ASCII).\r\n *\r\n * Best used for measuring randomness density of generated strings,\r\n * tokens, secrets, and API keys — not human-chosen passwords.\r\n *\r\n * @see https://en.wikipedia.org/wiki/Entropy_(information_theory)\r\n */\r\nexport function shannonEntropy(input: string): number {\r\n if (input.length === 0) return 0;\r\n\r\n const freq = new Map<string, number>();\r\n\r\n for (const char of input) {\r\n freq.set(char, (freq.get(char) ?? 0) + 1);\r\n }\r\n\r\n const len = input.length;\r\n let entropy = 0;\r\n\r\n for (const count of freq.values()) {\r\n const p = count / len;\r\n entropy -= p * Math.log2(p);\r\n }\r\n\r\n return Math.round(entropy * 1000) / 1000; // 3 decimal precision\r\n}\r\n","import type { EntropyLevel, EntropyThresholds } from \"./types\";\r\n\r\nexport const DEFAULT_THRESHOLDS: EntropyThresholds = {\r\n low: 2.5,\r\n medium: 3.5,\r\n high: 4.5,\r\n // above 4.5 → \"critical\"\r\n};\r\n\r\nexport function classifyLevel(\r\n entropy: number,\r\n thresholds: EntropyThresholds = DEFAULT_THRESHOLDS,\r\n): EntropyLevel {\r\n if (entropy < thresholds.low) return \"low\";\r\n if (entropy < thresholds.medium) return \"medium\";\r\n if (entropy < thresholds.high) return \"high\";\r\n return \"critical\";\r\n}\r\n\r\nexport function resolveThresholds(\r\n overrides?: Partial<EntropyThresholds>,\r\n): EntropyThresholds {\r\n return { ...DEFAULT_THRESHOLDS, ...overrides };\r\n}\r\n","import type { CharSet } from \"./types\";\r\n\r\nconst ONLY_DIGITS = /^[0-9]+$/;\r\nconst ONLY_ALPHA = /^[a-zA-Z]+$/;\r\nconst ONLY_HEX = /^[0-9a-fA-F]+$/;\r\nconst ONLY_ALPHANUM = /^[a-zA-Z0-9]+$/;\r\nconst HAS_DIGIT = /[0-9]/;\r\nconst HAS_ALPHA = /[a-zA-Z]/;\r\nconst HAS_NON_HEX_LETTER = /[g-zG-Z]/; // letters outside hex range\r\n\r\n/**\r\n * Detects the character set family of a string.\r\n *\r\n * Priority:\r\n * 1. numeric — digits only\r\n * 2. alpha — letters only (checked before hex to avoid \"deadbeef\" → hex)\r\n * 3. hex — hex chars [0-9a-fA-F] AND has both a digit and a letter AND no letters outside a-f range\r\n * 4. alphanumeric — letters + digits, but NOT purely hex\r\n * 5. mixed — everything else (has special chars)\r\n */\r\nexport function detectCharset(input: string): CharSet {\r\n if (input.length === 0) return \"mixed\";\r\n\r\n if (ONLY_DIGITS.test(input)) return \"numeric\";\r\n if (ONLY_ALPHA.test(input)) return \"alpha\";\r\n\r\n // hex: must match hex pattern AND have both digits and letters AND no letters outside a-f range\r\n if (\r\n ONLY_HEX.test(input) &&\r\n HAS_DIGIT.test(input) &&\r\n HAS_ALPHA.test(input) &&\r\n !HAS_NON_HEX_LETTER.test(input)\r\n )\r\n return \"hex\";\r\n\r\n if (ONLY_ALPHANUM.test(input)) return \"alphanumeric\";\r\n\r\n return \"mixed\";\r\n}\r\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"]}
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"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/calculate.ts","../src/level.ts","../src/charset.ts","../src/index.ts"],"sourcesContent":["/**\r\n * Calculates Shannon entropy of a string.\r\n * Returns bits of entropy per character (0 to ~6.55 for printable ASCII).\r\n *\r\n * Best used for measuring randomness density of generated strings,\r\n * tokens, secrets, and API keys — not human-chosen passwords.\r\n *\r\n * @see https://en.wikipedia.org/wiki/Entropy_(information_theory)\r\n */\r\nexport function shannonEntropy(input: string): number {\r\n if (input.length === 0) return 0;\r\n\r\n const freq = new Map<string, number>();\r\n\r\n for (const char of input) {\r\n freq.set(char, (freq.get(char) ?? 0) + 1);\r\n }\r\n\r\n const len = input.length;\r\n let entropy = 0;\r\n\r\n for (const count of freq.values()) {\r\n const p = count / len;\r\n entropy -= p * Math.log2(p);\r\n }\r\n\r\n return Math.round(entropy * 1000) / 1000; // 3 decimal precision\r\n}\r\n","import type { EntropyLevel, EntropyThresholds } from \"./types\";\r\n\r\nexport const DEFAULT_THRESHOLDS: EntropyThresholds = {\r\n low: 2.5,\r\n medium: 3.5,\r\n high: 4.5,\r\n // above 4.5 → \"critical\"\r\n};\r\n\r\nexport function classifyLevel(\r\n entropy: number,\r\n thresholds: EntropyThresholds = DEFAULT_THRESHOLDS,\r\n): EntropyLevel {\r\n if (entropy < thresholds.low) return \"low\";\r\n if (entropy < thresholds.medium) return \"medium\";\r\n if (entropy < thresholds.high) return \"high\";\r\n return \"critical\";\r\n}\r\n\r\nexport function resolveThresholds(\r\n overrides?: Partial<EntropyThresholds>,\r\n): EntropyThresholds {\r\n return { ...DEFAULT_THRESHOLDS, ...overrides };\r\n}\r\n","import type { CharSet } from \"./types\";\r\n\r\nconst ONLY_DIGITS = /^[0-9]+$/;\r\nconst ONLY_ALPHA = /^[a-zA-Z]+$/;\r\nconst ONLY_HEX = /^[0-9a-fA-F]+$/;\r\nconst ONLY_ALPHANUM = /^[a-zA-Z0-9]+$/;\r\nconst HAS_DIGIT = /[0-9]/;\r\nconst HAS_ALPHA = /[a-zA-Z]/;\r\nconst HAS_NON_HEX_LETTER = /[g-zG-Z]/; // letters outside hex range\r\n\r\n/**\r\n * Detects the character set family of a string.\r\n *\r\n * Priority:\r\n * 1. numeric — digits only\r\n * 2. alpha — letters only (checked before hex to avoid \"deadbeef\" → hex)\r\n * 3. hex — hex chars [0-9a-fA-F] AND has both a digit and a letter AND no letters outside a-f range\r\n * 4. alphanumeric — letters + digits, but NOT purely hex\r\n * 5. mixed — everything else (has special chars)\r\n */\r\nexport function detectCharset(input: string): CharSet {\r\n if (input.length === 0) return \"mixed\";\r\n\r\n if (ONLY_DIGITS.test(input)) return \"numeric\";\r\n if (ONLY_ALPHA.test(input)) return \"alpha\";\r\n\r\n // hex: must match hex pattern AND have both digits and letters AND no letters outside a-f range\r\n if (\r\n ONLY_HEX.test(input) &&\r\n HAS_DIGIT.test(input) &&\r\n HAS_ALPHA.test(input) &&\r\n !HAS_NON_HEX_LETTER.test(input)\r\n )\r\n return \"hex\";\r\n\r\n if (ONLY_ALPHANUM.test(input)) return \"alphanumeric\";\r\n\r\n return \"mixed\";\r\n}\r\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"]}
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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekaone/entropy",
3
- "version": "0.0.1",
3
+ "version": "0.1.1",
4
4
  "description": "Primitive Shannon entropy measurement for strings.",
5
5
  "keywords": [
6
6
  "typescript",