hslid 0.1.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/README.md +65 -0
- package/dist/css/index.d.ts +16 -0
- package/dist/css/index.js +16 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +73 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# hsl-id
|
|
2
|
+
|
|
3
|
+
A collision-resistant ID generator that creates readable, color-based identifiers that convert to valid CSS `hsl()` / `hsla()` values.
|
|
4
|
+
|
|
5
|
+
* URL-safe
|
|
6
|
+
* Collisions are unlikely
|
|
7
|
+
* Relatively performant
|
|
8
|
+
* Converts to CSS
|
|
9
|
+
* Configurable
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Normal:
|
|
14
|
+
```ts
|
|
15
|
+
import hslid from "hsl-id";
|
|
16
|
+
|
|
17
|
+
const id = hslid();
|
|
18
|
+
|
|
19
|
+
console.log(id);
|
|
20
|
+
// h18342.s04821.l06342.a00912.A9F
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Custom:
|
|
24
|
+
```ts
|
|
25
|
+
import hslid from "hsl-id";
|
|
26
|
+
|
|
27
|
+
const id = hslid({ //A vivid color example
|
|
28
|
+
separator: "-",
|
|
29
|
+
precision: 5,
|
|
30
|
+
salt: false,
|
|
31
|
+
clamp: {
|
|
32
|
+
h: { min: 0, max: 36000 },
|
|
33
|
+
s: { min: 6000, max: 10000 },
|
|
34
|
+
l: { min: 4500, max: 6500 },
|
|
35
|
+
a: { min: 10000, max: 10000 },
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
console.log(id);
|
|
40
|
+
// h27442-s06468-l06167-a10000
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## API
|
|
44
|
+
```ts
|
|
45
|
+
// defaults (all optional)
|
|
46
|
+
hslid({
|
|
47
|
+
separator: '.', // String
|
|
48
|
+
precision: 5, // Number (Minimum: 3, pads each value of hsla to this length with leading zeroes.)
|
|
49
|
+
salt: true, // Boolean (Appends a random 3 character hexadecimal string to the end of the id.)
|
|
50
|
+
clamp: { // Object (Minimum and maximum values of range for each value of hsla.)
|
|
51
|
+
h: { min: 0, max: 36000 },
|
|
52
|
+
s: { min: 0, max: 10000 },
|
|
53
|
+
l: { min: 0, max: 10000 },
|
|
54
|
+
a: { min: 0, max: 10000 },
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Why
|
|
60
|
+
|
|
61
|
+
Mostly as a toy but should work for low-stakes applications or applications where security is not a priority, even though the collision probability is very low and generation is secure.
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts an hslid string to a CSS hsl/hsla string.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} hslid - The hslid string to convert.
|
|
5
|
+
* @param {boolean} [alpha=true] - Whether to include alpha in the output. (hsl vs hsla)
|
|
6
|
+
* @returns {string|null} - The converted CSS string or null if the input is invalid.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* hslToCSS("h01234.s01234.l01234.a01234.ABC");
|
|
10
|
+
* // "hsla(12.34, 12.34%, 12.34%, 12.34%)"
|
|
11
|
+
* @example
|
|
12
|
+
* hslToCSS("h01234.s01234.l01234", false);
|
|
13
|
+
* // "hsl(12.34, 12.34%, 12.34%)"
|
|
14
|
+
* */
|
|
15
|
+
declare const hslToCSS: (hslid: string, alpha?: boolean) => string | null;
|
|
16
|
+
export { hslToCSS };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/css/index.ts
|
|
2
|
+
var hslToCSS = (hslid, alpha = true) => {
|
|
3
|
+
const match = hslid.match(/^h(?<h>\d+).*?s(?<s>\d+).*?l(?<l>\d+).*?a(?<a>\d+)(?:.*?(?<salt>\w{3}))?$/);
|
|
4
|
+
if (!match?.groups)
|
|
5
|
+
return null;
|
|
6
|
+
const h = Number(match.groups.h) / 100;
|
|
7
|
+
const s = Number(match.groups.s) / 100;
|
|
8
|
+
const l = Number(match.groups.l) / 100;
|
|
9
|
+
const a = Number(match.groups.a) / 100;
|
|
10
|
+
if (!alpha)
|
|
11
|
+
return `hsl(${h}, ${s}%, ${l}%)`;
|
|
12
|
+
return `hsla(${h}, ${s}%, ${l}%, ${a})`;
|
|
13
|
+
};
|
|
14
|
+
export {
|
|
15
|
+
hslToCSS
|
|
16
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
type ClampRange = {
|
|
2
|
+
min: number;
|
|
3
|
+
max: number;
|
|
4
|
+
};
|
|
5
|
+
type PartialClamp = {
|
|
6
|
+
h?: Partial<ClampRange>;
|
|
7
|
+
s?: Partial<ClampRange>;
|
|
8
|
+
l?: Partial<ClampRange>;
|
|
9
|
+
a?: Partial<ClampRange>;
|
|
10
|
+
};
|
|
11
|
+
type HslInputOptions = Partial<Omit<HslOptions, "clamp"> & {
|
|
12
|
+
clamp?: PartialClamp;
|
|
13
|
+
}>;
|
|
14
|
+
interface HslOptions {
|
|
15
|
+
separator: string;
|
|
16
|
+
salt: boolean;
|
|
17
|
+
precision: number;
|
|
18
|
+
clamp: {
|
|
19
|
+
h: ClampRange;
|
|
20
|
+
s: ClampRange;
|
|
21
|
+
l: ClampRange;
|
|
22
|
+
a: ClampRange;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
declare const DEFAULT_OPTIONS: HslOptions;
|
|
26
|
+
/**
|
|
27
|
+
* Generates a random hsl id.
|
|
28
|
+
*
|
|
29
|
+
* @param {HslInputOptions} [options] - Options to generate the id.
|
|
30
|
+
* @param {string} [options.separator="."] - The separator to use between the hsla values.
|
|
31
|
+
* @param {boolean} [options.salt=true] - Whether to append a random salt to the end of the id.
|
|
32
|
+
* @param {number} [options.precision=5] - The padded length of the generated id.
|
|
33
|
+
* @param {PartialClamp} [options.clamp] - Custom clamp ranges (min/max) for each hsla value.
|
|
34
|
+
*/
|
|
35
|
+
declare const hslid: (options?: HslInputOptions) => string;
|
|
36
|
+
export { hslid as default, PartialClamp, HslOptions, HslInputOptions, DEFAULT_OPTIONS, ClampRange };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var SALT_DICT = "0123456789ABCDEF";
|
|
3
|
+
var SALT_LOOKUP = new Array(16).fill("").map((_, i) => SALT_DICT[i]);
|
|
4
|
+
var BUFFER_SIZE = 2048;
|
|
5
|
+
var BUFFER = new Uint32Array(BUFFER_SIZE);
|
|
6
|
+
var BUFFER_INDEX = BUFFER_SIZE;
|
|
7
|
+
var MAX_RANGE = Math.pow(2, 32);
|
|
8
|
+
var DEFAULT_CLAMP = {
|
|
9
|
+
h: { min: 0, max: 36000 },
|
|
10
|
+
s: { min: 0, max: 1e4 },
|
|
11
|
+
l: { min: 0, max: 1e4 },
|
|
12
|
+
a: { min: 0, max: 1e4 }
|
|
13
|
+
};
|
|
14
|
+
var DEFAULT_OPTIONS = {
|
|
15
|
+
clamp: structuredClone(DEFAULT_CLAMP),
|
|
16
|
+
precision: 5,
|
|
17
|
+
salt: true,
|
|
18
|
+
separator: "."
|
|
19
|
+
};
|
|
20
|
+
var mergeClamp = (clamp) => {
|
|
21
|
+
return {
|
|
22
|
+
h: { ...DEFAULT_CLAMP.h, ...clamp?.h },
|
|
23
|
+
s: { ...DEFAULT_CLAMP.s, ...clamp?.s },
|
|
24
|
+
l: { ...DEFAULT_CLAMP.l, ...clamp?.l },
|
|
25
|
+
a: { ...DEFAULT_CLAMP.a, ...clamp?.a }
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
var randomInt = (min, max) => {
|
|
29
|
+
const range = max - min + 1;
|
|
30
|
+
const limit = MAX_RANGE - MAX_RANGE % range;
|
|
31
|
+
while (true) {
|
|
32
|
+
if (BUFFER_INDEX >= BUFFER_SIZE) {
|
|
33
|
+
crypto.getRandomValues(BUFFER);
|
|
34
|
+
BUFFER_INDEX = 0;
|
|
35
|
+
}
|
|
36
|
+
const value = BUFFER[BUFFER_INDEX++];
|
|
37
|
+
if (value < limit) {
|
|
38
|
+
return min + value % range;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var generateSalt = () => {
|
|
43
|
+
if (BUFFER_INDEX >= 2048) {
|
|
44
|
+
crypto.getRandomValues(BUFFER);
|
|
45
|
+
BUFFER_INDEX = 0;
|
|
46
|
+
}
|
|
47
|
+
const val = BUFFER[BUFFER_INDEX++];
|
|
48
|
+
return SALT_LOOKUP[val & 15] + SALT_LOOKUP[val >> 4 & 15] + SALT_LOOKUP[val >> 8 & 15];
|
|
49
|
+
};
|
|
50
|
+
var hslid = (options) => {
|
|
51
|
+
const mergedOptions = {
|
|
52
|
+
...DEFAULT_OPTIONS,
|
|
53
|
+
...options,
|
|
54
|
+
clamp: mergeClamp(options?.clamp)
|
|
55
|
+
};
|
|
56
|
+
let h = randomInt(mergedOptions.clamp.h.min, mergedOptions.clamp.h.max).toString();
|
|
57
|
+
let s = randomInt(mergedOptions.clamp.s.min, mergedOptions.clamp.s.max).toString();
|
|
58
|
+
let l = randomInt(mergedOptions.clamp.l.min, mergedOptions.clamp.l.max).toString();
|
|
59
|
+
let a = randomInt(mergedOptions.clamp.a.min, mergedOptions.clamp.a.max).toString();
|
|
60
|
+
const width = mergedOptions.precision <= 3 ? 3 : mergedOptions.precision;
|
|
61
|
+
h = h.padStart(width, "0");
|
|
62
|
+
s = s.padStart(width, "0");
|
|
63
|
+
l = l.padStart(width, "0");
|
|
64
|
+
a = a.padStart(width, "0");
|
|
65
|
+
const salt = mergedOptions.salt ? `.${generateSalt()}` : "";
|
|
66
|
+
let sep = mergedOptions.separator;
|
|
67
|
+
return `h${h}${sep}s${s}${sep}l${l}${sep}a${a}${salt}`;
|
|
68
|
+
};
|
|
69
|
+
var src_default = hslid;
|
|
70
|
+
export {
|
|
71
|
+
src_default as default,
|
|
72
|
+
DEFAULT_OPTIONS
|
|
73
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hslid",
|
|
3
|
+
"description": "Securely generate a random id that converts to an hsla css color.",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"./css": {
|
|
19
|
+
"import": {
|
|
20
|
+
"types": "./dist/css/index.d.ts",
|
|
21
|
+
"default": "./dist/css/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
"author": "JosephY (https://github.com/exoup)",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/exoup/hsl-id.git"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/bun": "latest",
|
|
33
|
+
"bunup": "^0.16.17"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/exoup/hsl-id#readme",
|
|
36
|
+
"keywords": [
|
|
37
|
+
"random",
|
|
38
|
+
"id",
|
|
39
|
+
"number",
|
|
40
|
+
"generator",
|
|
41
|
+
"hsl",
|
|
42
|
+
"hsla"
|
|
43
|
+
],
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"registry": "https://registry.npmjs.org",
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"test": "bun test",
|
|
51
|
+
"build": "bunup"
|
|
52
|
+
}
|
|
53
|
+
}
|