@lucid-softworks/ulid 0.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lucid Softworks
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
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # `@lucid-softworks/ulid`
2
+
3
+ Canonical ULID generation, timestamp decoding, and monotonic generation with
4
+ injectable clocks and randomness.
5
+
6
+ ```ts
7
+ const id = ulid();
8
+ const createdAt = decodeUlidTime(id);
9
+ const nextId = monotonicUlidFactory();
10
+ ```
11
+
12
+ Generated IDs are 26 uppercase Crockford Base32 characters. Custom random
13
+ sources must return values in `[0, 1)`.
@@ -0,0 +1,8 @@
1
+ export type RandomSource = () => number;
2
+ /** Creates a canonical 26-character ULID. */
3
+ export declare function ulid(time?: number, random?: RandomSource): string;
4
+ /** Returns a ULID generator that remains monotonic within one millisecond. */
5
+ export declare function monotonicUlidFactory(now?: () => number, random?: RandomSource): () => string;
6
+ /** Extracts the millisecond timestamp from a canonical ULID. */
7
+ export declare function decodeUlidTime(value: string): number;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC;AAExC,6CAA6C;AAC7C,wBAAgB,IAAI,CAClB,IAAI,GAAE,MAAmB,EACzB,MAAM,GAAE,YAA0B,GACjC,MAAM,CAER;AAED,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,MAAM,MAAiB,EAC5B,MAAM,GAAE,YAA0B,GACjC,MAAM,MAAM,CAed;AAED,gEAAgE;AAChE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASpD"}
package/dist/index.js ADDED
@@ -0,0 +1,75 @@
1
+ const alphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
2
+ const maximumTime = 2 ** 48 - 1;
3
+ /** Creates a canonical 26-character ULID. */
4
+ export function ulid(time = Date.now(), random = Math.random) {
5
+ return encodeTime(time) + encodeRandom(random);
6
+ }
7
+ /** Returns a ULID generator that remains monotonic within one millisecond. */
8
+ export function monotonicUlidFactory(now = Date.now, random = Math.random) {
9
+ let lastTime = -1;
10
+ let digits = [];
11
+ return () => {
12
+ const observed = now();
13
+ validateTime(observed);
14
+ const time = Math.max(observed, lastTime);
15
+ if (time > lastTime) {
16
+ digits = randomDigits(random);
17
+ lastTime = time;
18
+ }
19
+ else {
20
+ increment(digits);
21
+ }
22
+ return encodeTime(time) + digits.map((digit) => alphabet[digit]).join("");
23
+ };
24
+ }
25
+ /** Extracts the millisecond timestamp from a canonical ULID. */
26
+ export function decodeUlidTime(value) {
27
+ if (!/^[0-9A-HJKMNP-TV-Z]{26}$/u.test(value) || value[0] > "7") {
28
+ throw new RangeError("value must be a canonical ULID");
29
+ }
30
+ let time = 0n;
31
+ for (const character of value.slice(0, 10)) {
32
+ time = time * 32n + BigInt(alphabet.indexOf(character));
33
+ }
34
+ return Number(time);
35
+ }
36
+ function encodeTime(time) {
37
+ validateTime(time);
38
+ let remaining = BigInt(time);
39
+ let output = "";
40
+ for (let index = 0; index < 10; index += 1) {
41
+ output = alphabet[Number(remaining % 32n)] + output;
42
+ remaining /= 32n;
43
+ }
44
+ return output;
45
+ }
46
+ function encodeRandom(random) {
47
+ return randomDigits(random)
48
+ .map((digit) => alphabet[digit])
49
+ .join("");
50
+ }
51
+ function randomDigits(random) {
52
+ return Array.from({ length: 16 }, () => {
53
+ const value = random();
54
+ if (!Number.isFinite(value) || value < 0 || value >= 1) {
55
+ throw new RangeError("random source must return values from 0 up to 1");
56
+ }
57
+ return Math.floor(value * 32);
58
+ });
59
+ }
60
+ function increment(digits) {
61
+ for (let index = digits.length - 1; index >= 0; index -= 1) {
62
+ if (digits[index] < 31) {
63
+ digits[index] = digits[index] + 1;
64
+ return;
65
+ }
66
+ digits[index] = 0;
67
+ }
68
+ throw new RangeError("monotonic ULID random space exhausted");
69
+ }
70
+ function validateTime(time) {
71
+ if (!Number.isSafeInteger(time) || time < 0 || time > maximumTime) {
72
+ throw new RangeError("time must be a non-negative 48-bit integer");
73
+ }
74
+ }
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,QAAQ,GAAG,kCAAkC,CAAC;AACpD,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAIhC,6CAA6C;AAC7C,MAAM,UAAU,IAAI,CAClB,IAAI,GAAW,IAAI,CAAC,GAAG,EAAE,EACzB,MAAM,GAAiB,IAAI,CAAC,MAAM;IAElC,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;AACjD,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,oBAAoB,CAClC,GAAG,GAAiB,IAAI,CAAC,GAAG,EAC5B,MAAM,GAAiB,IAAI,CAAC,MAAM;IAElC,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAC;IAClB,IAAI,MAAM,GAAa,EAAE,CAAC;IAC1B,OAAO,GAAW,EAAE;QAClB,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC;QACvB,YAAY,CAAC,QAAQ,CAAC,CAAC;QACvB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC1C,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;YACpB,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YAC9B,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,SAAS,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC;QACD,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5E,CAAC,CAAC;AACJ,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,IAAI,CAAC,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,IAAK,KAAK,CAAC,CAAC,CAAY,GAAG,GAAG,EAAE,CAAC;QAC3E,MAAM,IAAI,UAAU,CAAC,gCAAgC,CAAC,CAAC;IACzD,CAAC;IACD,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QAC3C,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,YAAY,CAAC,IAAI,CAAC,CAAC;IACnB,IAAI,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC;QACpD,SAAS,IAAI,GAAG,CAAC;IACnB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,YAAY,CAAC,MAAoB;IACxC,OAAO,YAAY,CAAC,MAAM,CAAC;SACxB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SAC/B,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CAAC,MAAoB;IACxC,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE;QACrC,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACvD,MAAM,IAAI,UAAU,CAAC,iDAAiD,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,SAAS,CAAC,MAAgB;IACjC,KAAK,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC3D,IAAK,MAAM,CAAC,KAAK,CAAY,GAAG,EAAE,EAAE,CAAC;YACnC,MAAM,CAAC,KAAK,CAAC,GAAI,MAAM,CAAC,KAAK,CAAY,GAAG,CAAC,CAAC;YAC9C,OAAO;QACT,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;IACD,MAAM,IAAI,UAAU,CAAC,uCAAuC,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,WAAW,EAAE,CAAC;QAClE,MAAM,IAAI,UAAU,CAAC,4CAA4C,CAAC,CAAC;IACrE,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@lucid-softworks/ulid",
3
+ "version": "0.0.0",
4
+ "description": "Canonical and monotonic ULID generation and timestamp decoding.",
5
+ "keywords": [
6
+ "javascript",
7
+ "typescript",
8
+ "ulid",
9
+ "utility"
10
+ ],
11
+ "homepage": "https://github.com/lucid-softworks/ulid#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/lucid-softworks/ulid/issues"
14
+ },
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/lucid-softworks/ulid.git"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "type": "module",
24
+ "sideEffects": false,
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js"
29
+ }
30
+ },
31
+ "publishConfig": {
32
+ "access": "public",
33
+ "provenance": true
34
+ },
35
+ "scripts": {
36
+ "bench": "vitest bench --run",
37
+ "bench:smoke": "VITEST_BENCHMARK_SMOKE=1 vitest bench --run",
38
+ "build": "tsc -p tsconfig.build.json",
39
+ "check": "oxfmt --check . && oxlint . && tsc -p tsconfig.json && tsc -p tsconfig.build.json && vitest run --coverage && publint && VITEST_BENCHMARK_SMOKE=1 vitest bench --run",
40
+ "format": "oxfmt --write .",
41
+ "format:check": "oxfmt --check .",
42
+ "lint": "oxlint .",
43
+ "pack:check": "publint",
44
+ "test": "vitest run --coverage",
45
+ "typecheck": "tsc -p tsconfig.json"
46
+ },
47
+ "devDependencies": {
48
+ "@lucid-softworks/oxfmt-config": "^0.1.1",
49
+ "@lucid-softworks/oxlint-config": "^0.1.0",
50
+ "@lucid-softworks/tsconfig": "^0.1.0",
51
+ "@lucid-softworks/vitest-config": "^0.1.1",
52
+ "@types/node": "26.1.1",
53
+ "@vitest/coverage-v8": "4.1.10",
54
+ "oxfmt": "0.60.0",
55
+ "oxlint": "1.75.0",
56
+ "publint": "0.3.22",
57
+ "typescript": "7.0.2",
58
+ "vitest": "4.1.10"
59
+ },
60
+ "engines": {
61
+ "node": ">=22"
62
+ },
63
+ "packageManager": "pnpm@11.16.0"
64
+ }