check-ulid 3.0.2

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.
@@ -0,0 +1,339 @@
1
+ 'use strict';
2
+
3
+ var crypto = require('node:crypto');
4
+
5
+ // These values should NEVER change. The values are precisely for
6
+ // generating ULIDs.
7
+ const B32_CHARACTERS = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
8
+ const ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; // Crockford's Base32
9
+ const ENCODING_LEN = 32; // from ENCODING.length;
10
+ const MAX_ULID = "7ZZZZZZZZZZZZZZZZZZZZZZZZZ";
11
+ const MIN_ULID = "00000000000000000000000000";
12
+ const RANDOM_LEN = 16;
13
+ const TIME_LEN = 10;
14
+ const TIME_MAX = 281474976710655; // from Math.pow(2, 48) - 1;
15
+ const ULID_REGEX = /^[0-7][0-9a-hjkmnp-tv-zA-HJKMNP-TV-Z]{25}$/;
16
+ const UUID_REGEX = /^[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$/;
17
+
18
+ exports.ULIDErrorCode = void 0;
19
+ (function (ULIDErrorCode) {
20
+ ULIDErrorCode["Base32IncorrectEncoding"] = "B32_ENC_INVALID";
21
+ ULIDErrorCode["DecodeTimeInvalidCharacter"] = "DEC_TIME_CHAR";
22
+ ULIDErrorCode["DecodeTimeValueMalformed"] = "DEC_TIME_MALFORMED";
23
+ ULIDErrorCode["EncodeTimeNegative"] = "ENC_TIME_NEG";
24
+ ULIDErrorCode["EncodeTimeSizeExceeded"] = "ENC_TIME_SIZE_EXCEED";
25
+ ULIDErrorCode["EncodeTimeValueMalformed"] = "ENC_TIME_MALFORMED";
26
+ ULIDErrorCode["PRNGDetectFailure"] = "PRNG_DETECT";
27
+ ULIDErrorCode["ULIDInvalid"] = "ULID_INVALID";
28
+ ULIDErrorCode["Unexpected"] = "UNEXPECTED";
29
+ ULIDErrorCode["UUIDInvalid"] = "UUID_INVALID";
30
+ })(exports.ULIDErrorCode || (exports.ULIDErrorCode = {}));
31
+ class ULIDError extends Error {
32
+ constructor(errorCode, message) {
33
+ super(`${message} (${errorCode})`);
34
+ this.name = "ULIDError";
35
+ this.code = errorCode;
36
+ }
37
+ }
38
+
39
+ function randomChar(prng) {
40
+ // Currently PRNGs generate fractions from 0 to _less than_ 1, so no "%" is necessary.
41
+ // However, just in case a future PRNG can generate 1,
42
+ // we are applying "% ENCODING LEN" to wrap back to the first character
43
+ const randomPosition = Math.floor(prng() * ENCODING_LEN) % ENCODING_LEN;
44
+ return ENCODING.charAt(randomPosition);
45
+ }
46
+ function replaceCharAt(str, index, char) {
47
+ if (index > str.length - 1) {
48
+ return str;
49
+ }
50
+ return str.substr(0, index) + char + str.substr(index + 1);
51
+ }
52
+
53
+ // Code from https://github.com/devbanana/crockford-base32/blob/develop/src/index.ts
54
+ function crockfordEncode(input) {
55
+ const output = [];
56
+ let bitsRead = 0;
57
+ let buffer = 0;
58
+ const reversedInput = new Uint8Array(input.slice().reverse());
59
+ for (const byte of reversedInput) {
60
+ buffer |= byte << bitsRead;
61
+ bitsRead += 8;
62
+ while (bitsRead >= 5) {
63
+ output.unshift(buffer & 0x1f);
64
+ buffer >>>= 5;
65
+ bitsRead -= 5;
66
+ }
67
+ }
68
+ if (bitsRead > 0) {
69
+ output.unshift(buffer & 0x1f);
70
+ }
71
+ return output.map(byte => B32_CHARACTERS.charAt(byte)).join("");
72
+ }
73
+ function crockfordDecode(input) {
74
+ const sanitizedInput = input.toUpperCase().split("").reverse().join("");
75
+ const output = [];
76
+ let bitsRead = 0;
77
+ let buffer = 0;
78
+ for (const character of sanitizedInput) {
79
+ const byte = B32_CHARACTERS.indexOf(character);
80
+ if (byte === -1) {
81
+ throw new Error(`Invalid base 32 character found in string: ${character}`);
82
+ }
83
+ buffer |= byte << bitsRead;
84
+ bitsRead += 5;
85
+ while (bitsRead >= 8) {
86
+ output.unshift(buffer & 0xff);
87
+ buffer >>>= 8;
88
+ bitsRead -= 8;
89
+ }
90
+ }
91
+ if (bitsRead >= 5 || buffer > 0) {
92
+ output.unshift(buffer & 0xff);
93
+ }
94
+ return new Uint8Array(output);
95
+ }
96
+ /**
97
+ * Fix a ULID's Base32 encoding -
98
+ * i and l (case-insensitive) will be treated as 1 and o (case-insensitive) will be treated as 0.
99
+ * hyphens are ignored during decoding.
100
+ * @param id The ULID
101
+ * @returns The cleaned up ULID
102
+ */
103
+ function fixULIDBase32(id) {
104
+ return id.replace(/i/gi, "1").replace(/l/gi, "1").replace(/o/gi, "0").replace(/-/g, "");
105
+ }
106
+ function incrementBase32(str) {
107
+ let done = undefined, index = str.length, char, charIndex, output = str;
108
+ const maxCharIndex = ENCODING_LEN - 1;
109
+ while (!done && index-- >= 0) {
110
+ char = output[index];
111
+ charIndex = ENCODING.indexOf(char);
112
+ if (charIndex === -1) {
113
+ throw new ULIDError(exports.ULIDErrorCode.Base32IncorrectEncoding, "Incorrectly encoded string");
114
+ }
115
+ if (charIndex === maxCharIndex) {
116
+ output = replaceCharAt(output, index, ENCODING[0]);
117
+ continue;
118
+ }
119
+ done = replaceCharAt(output, index, ENCODING[charIndex + 1]);
120
+ }
121
+ if (typeof done === "string") {
122
+ return done;
123
+ }
124
+ throw new ULIDError(exports.ULIDErrorCode.Base32IncorrectEncoding, "Failed incrementing string");
125
+ }
126
+
127
+ /**
128
+ * Decode time from a ULID
129
+ * @param id The ULID
130
+ * @returns The decoded timestamp
131
+ */
132
+ function decodeTime(id) {
133
+ if (id.length !== TIME_LEN + RANDOM_LEN) {
134
+ throw new ULIDError(exports.ULIDErrorCode.DecodeTimeValueMalformed, "Malformed ULID");
135
+ }
136
+ const time = id
137
+ .substr(0, TIME_LEN)
138
+ .toUpperCase()
139
+ .split("")
140
+ .reverse()
141
+ .reduce((carry, char, index) => {
142
+ const encodingIndex = ENCODING.indexOf(char);
143
+ if (encodingIndex === -1) {
144
+ throw new ULIDError(exports.ULIDErrorCode.DecodeTimeInvalidCharacter, `Time decode error: Invalid character: ${char}`);
145
+ }
146
+ return (carry += encodingIndex * Math.pow(ENCODING_LEN, index));
147
+ }, 0);
148
+ if (time > TIME_MAX) {
149
+ throw new ULIDError(exports.ULIDErrorCode.DecodeTimeValueMalformed, `Malformed ULID: timestamp too large: ${time}`);
150
+ }
151
+ return time;
152
+ }
153
+ /**
154
+ * Detect the best PRNG (pseudo-random number generator)
155
+ * @param root The root to check from (global/window)
156
+ * @returns The PRNG function
157
+ */
158
+ function detectPRNG(root) {
159
+ const rootLookup = detectRoot();
160
+ const globalCrypto = (rootLookup && (rootLookup.crypto || rootLookup.msCrypto)) ||
161
+ (typeof crypto !== "undefined" ? crypto : null);
162
+ if (typeof globalCrypto?.getRandomValues === "function") {
163
+ return () => {
164
+ const buffer = new Uint8Array(1);
165
+ globalCrypto.getRandomValues(buffer);
166
+ return buffer[0] / 256;
167
+ };
168
+ }
169
+ else if (typeof globalCrypto?.randomBytes === "function") {
170
+ return () => globalCrypto.randomBytes(1).readUInt8() / 256;
171
+ }
172
+ else if (crypto?.randomBytes) {
173
+ return () => crypto.randomBytes(1).readUInt8() / 256;
174
+ }
175
+ throw new ULIDError(exports.ULIDErrorCode.PRNGDetectFailure, "Failed to find a reliable PRNG");
176
+ }
177
+ function detectRoot() {
178
+ if (inWebWorker())
179
+ return self;
180
+ if (typeof window !== "undefined") {
181
+ return window;
182
+ }
183
+ if (typeof global !== "undefined") {
184
+ return global;
185
+ }
186
+ if (typeof globalThis !== "undefined") {
187
+ return globalThis;
188
+ }
189
+ return null;
190
+ }
191
+ function encodeRandom(len, prng) {
192
+ let str = "";
193
+ for (; len > 0; len--) {
194
+ str = randomChar(prng) + str;
195
+ }
196
+ return str;
197
+ }
198
+ /**
199
+ * Encode the time portion of a ULID
200
+ * @param now The current timestamp
201
+ * @param len Length to generate
202
+ * @returns The encoded time
203
+ */
204
+ function encodeTime(now, len = TIME_LEN) {
205
+ if (isNaN(now)) {
206
+ throw new ULIDError(exports.ULIDErrorCode.EncodeTimeValueMalformed, `Time must be a number: ${now}`);
207
+ }
208
+ else if (now > TIME_MAX) {
209
+ throw new ULIDError(exports.ULIDErrorCode.EncodeTimeSizeExceeded, `Cannot encode a time larger than ${TIME_MAX}: ${now}`);
210
+ }
211
+ else if (now < 0) {
212
+ throw new ULIDError(exports.ULIDErrorCode.EncodeTimeNegative, `Time must be positive: ${now}`);
213
+ }
214
+ else if (Number.isInteger(now) === false) {
215
+ throw new ULIDError(exports.ULIDErrorCode.EncodeTimeValueMalformed, `Time must be an integer: ${now}`);
216
+ }
217
+ let mod, str = "";
218
+ for (let currentLen = len; currentLen > 0; currentLen--) {
219
+ mod = now % ENCODING_LEN;
220
+ str = ENCODING.charAt(mod) + str;
221
+ now = (now - mod) / ENCODING_LEN;
222
+ }
223
+ return str;
224
+ }
225
+ function inWebWorker() {
226
+ // @ts-ignore
227
+ return typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope;
228
+ }
229
+ /**
230
+ * Check if a ULID is valid
231
+ * @param id The ULID to test
232
+ * @returns True if valid, false otherwise
233
+ * @example
234
+ * isValid("01HNZX8JGFACFA36RBXDHEQN6E"); // true
235
+ * isValid(""); // false
236
+ */
237
+ function isValid(id) {
238
+ return (typeof id === "string" &&
239
+ id.length === TIME_LEN + RANDOM_LEN &&
240
+ id
241
+ .toUpperCase()
242
+ .split("")
243
+ .every(char => ENCODING.indexOf(char) !== -1));
244
+ }
245
+ /**
246
+ * Create a ULID factory to generate monotonically-increasing
247
+ * ULIDs
248
+ * @param prng The PRNG to use
249
+ * @returns A ulid factory
250
+ * @example
251
+ * const ulid = monotonicFactory();
252
+ * ulid(); // "01HNZXD07M5CEN5XA66EMZSRZW"
253
+ */
254
+ function monotonicFactory(prng) {
255
+ const currentPRNG = prng || detectPRNG();
256
+ let lastTime = 0, lastRandom;
257
+ return function _ulid(seedTime) {
258
+ const seed = !seedTime || isNaN(seedTime) ? Date.now() : seedTime;
259
+ if (seed <= lastTime) {
260
+ const incrementedRandom = (lastRandom = incrementBase32(lastRandom));
261
+ return encodeTime(lastTime, TIME_LEN) + incrementedRandom;
262
+ }
263
+ lastTime = seed;
264
+ const newRandom = (lastRandom = encodeRandom(RANDOM_LEN, currentPRNG));
265
+ return encodeTime(seed, TIME_LEN) + newRandom;
266
+ };
267
+ }
268
+ /**
269
+ * Generate a ULID
270
+ * @param seedTime Optional time seed
271
+ * @param prng Optional PRNG function
272
+ * @returns A ULID string
273
+ * @example
274
+ * ulid(); // "01HNZXD07M5CEN5XA66EMZSRZW"
275
+ */
276
+ function ulid(seedTime, prng) {
277
+ const currentPRNG = prng || detectPRNG();
278
+ const seed = !seedTime || isNaN(seedTime) ? Date.now() : seedTime;
279
+ return encodeTime(seed, TIME_LEN) + encodeRandom(RANDOM_LEN, currentPRNG);
280
+ }
281
+
282
+ /**
283
+ * Convert a ULID to a UUID
284
+ * @param ulid The ULID to convert
285
+ * @returns A UUID string
286
+ */
287
+ function ulidToUUID(ulid) {
288
+ const isValid = ULID_REGEX.test(ulid);
289
+ if (!isValid) {
290
+ throw new ULIDError(exports.ULIDErrorCode.ULIDInvalid, `Invalid ULID: ${ulid}`);
291
+ }
292
+ const uint8Array = crockfordDecode(ulid);
293
+ let uuid = Array.from(uint8Array)
294
+ .map(byte => byte.toString(16).padStart(2, "0"))
295
+ .join("");
296
+ uuid =
297
+ uuid.substring(0, 8) +
298
+ "-" +
299
+ uuid.substring(8, 12) +
300
+ "-" +
301
+ uuid.substring(12, 16) +
302
+ "-" +
303
+ uuid.substring(16, 20) +
304
+ "-" +
305
+ uuid.substring(20);
306
+ return uuid.toUpperCase();
307
+ }
308
+ /**
309
+ * Convert a UUID to a ULID
310
+ * @param uuid The UUID to convert
311
+ * @returns A ULID string
312
+ */
313
+ function uuidToULID(uuid) {
314
+ const isValid = UUID_REGEX.test(uuid);
315
+ if (!isValid) {
316
+ throw new ULIDError(exports.ULIDErrorCode.UUIDInvalid, `Invalid UUID: ${uuid}`);
317
+ }
318
+ const bytes = uuid.replace(/-/g, "").match(/.{1,2}/g);
319
+ if (!bytes) {
320
+ throw new ULIDError(exports.ULIDErrorCode.Unexpected, `Failed parsing UUID bytes: ${uuid}`);
321
+ }
322
+ const uint8Array = new Uint8Array(bytes.map(byte => parseInt(byte, 16)));
323
+ return crockfordEncode(uint8Array);
324
+ }
325
+
326
+ exports.MAX_ULID = MAX_ULID;
327
+ exports.MIN_ULID = MIN_ULID;
328
+ exports.TIME_LEN = TIME_LEN;
329
+ exports.TIME_MAX = TIME_MAX;
330
+ exports.ULIDError = ULIDError;
331
+ exports.decodeTime = decodeTime;
332
+ exports.encodeTime = encodeTime;
333
+ exports.fixULIDBase32 = fixULIDBase32;
334
+ exports.incrementBase32 = incrementBase32;
335
+ exports.isValid = isValid;
336
+ exports.monotonicFactory = monotonicFactory;
337
+ exports.ulid = ulid;
338
+ exports.ulidToUUID = ulidToUUID;
339
+ exports.uuidToULID = uuidToULID;
@@ -0,0 +1,324 @@
1
+ import crypto from 'node:crypto';
2
+
3
+ // These values should NEVER change. The values are precisely for
4
+ // generating ULIDs.
5
+ const B32_CHARACTERS = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
6
+ const ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; // Crockford's Base32
7
+ const ENCODING_LEN = 32; // from ENCODING.length;
8
+ const MAX_ULID = "7ZZZZZZZZZZZZZZZZZZZZZZZZZ";
9
+ const MIN_ULID = "00000000000000000000000000";
10
+ const RANDOM_LEN = 16;
11
+ const TIME_LEN = 10;
12
+ const TIME_MAX = 281474976710655; // from Math.pow(2, 48) - 1;
13
+ const ULID_REGEX = /^[0-7][0-9a-hjkmnp-tv-zA-HJKMNP-TV-Z]{25}$/;
14
+ const UUID_REGEX = /^[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$/;
15
+
16
+ var ULIDErrorCode;
17
+ (function (ULIDErrorCode) {
18
+ ULIDErrorCode["Base32IncorrectEncoding"] = "B32_ENC_INVALID";
19
+ ULIDErrorCode["DecodeTimeInvalidCharacter"] = "DEC_TIME_CHAR";
20
+ ULIDErrorCode["DecodeTimeValueMalformed"] = "DEC_TIME_MALFORMED";
21
+ ULIDErrorCode["EncodeTimeNegative"] = "ENC_TIME_NEG";
22
+ ULIDErrorCode["EncodeTimeSizeExceeded"] = "ENC_TIME_SIZE_EXCEED";
23
+ ULIDErrorCode["EncodeTimeValueMalformed"] = "ENC_TIME_MALFORMED";
24
+ ULIDErrorCode["PRNGDetectFailure"] = "PRNG_DETECT";
25
+ ULIDErrorCode["ULIDInvalid"] = "ULID_INVALID";
26
+ ULIDErrorCode["Unexpected"] = "UNEXPECTED";
27
+ ULIDErrorCode["UUIDInvalid"] = "UUID_INVALID";
28
+ })(ULIDErrorCode || (ULIDErrorCode = {}));
29
+ class ULIDError extends Error {
30
+ constructor(errorCode, message) {
31
+ super(`${message} (${errorCode})`);
32
+ this.name = "ULIDError";
33
+ this.code = errorCode;
34
+ }
35
+ }
36
+
37
+ function randomChar(prng) {
38
+ // Currently PRNGs generate fractions from 0 to _less than_ 1, so no "%" is necessary.
39
+ // However, just in case a future PRNG can generate 1,
40
+ // we are applying "% ENCODING LEN" to wrap back to the first character
41
+ const randomPosition = Math.floor(prng() * ENCODING_LEN) % ENCODING_LEN;
42
+ return ENCODING.charAt(randomPosition);
43
+ }
44
+ function replaceCharAt(str, index, char) {
45
+ if (index > str.length - 1) {
46
+ return str;
47
+ }
48
+ return str.substr(0, index) + char + str.substr(index + 1);
49
+ }
50
+
51
+ // Code from https://github.com/devbanana/crockford-base32/blob/develop/src/index.ts
52
+ function crockfordEncode(input) {
53
+ const output = [];
54
+ let bitsRead = 0;
55
+ let buffer = 0;
56
+ const reversedInput = new Uint8Array(input.slice().reverse());
57
+ for (const byte of reversedInput) {
58
+ buffer |= byte << bitsRead;
59
+ bitsRead += 8;
60
+ while (bitsRead >= 5) {
61
+ output.unshift(buffer & 0x1f);
62
+ buffer >>>= 5;
63
+ bitsRead -= 5;
64
+ }
65
+ }
66
+ if (bitsRead > 0) {
67
+ output.unshift(buffer & 0x1f);
68
+ }
69
+ return output.map(byte => B32_CHARACTERS.charAt(byte)).join("");
70
+ }
71
+ function crockfordDecode(input) {
72
+ const sanitizedInput = input.toUpperCase().split("").reverse().join("");
73
+ const output = [];
74
+ let bitsRead = 0;
75
+ let buffer = 0;
76
+ for (const character of sanitizedInput) {
77
+ const byte = B32_CHARACTERS.indexOf(character);
78
+ if (byte === -1) {
79
+ throw new Error(`Invalid base 32 character found in string: ${character}`);
80
+ }
81
+ buffer |= byte << bitsRead;
82
+ bitsRead += 5;
83
+ while (bitsRead >= 8) {
84
+ output.unshift(buffer & 0xff);
85
+ buffer >>>= 8;
86
+ bitsRead -= 8;
87
+ }
88
+ }
89
+ if (bitsRead >= 5 || buffer > 0) {
90
+ output.unshift(buffer & 0xff);
91
+ }
92
+ return new Uint8Array(output);
93
+ }
94
+ /**
95
+ * Fix a ULID's Base32 encoding -
96
+ * i and l (case-insensitive) will be treated as 1 and o (case-insensitive) will be treated as 0.
97
+ * hyphens are ignored during decoding.
98
+ * @param id The ULID
99
+ * @returns The cleaned up ULID
100
+ */
101
+ function fixULIDBase32(id) {
102
+ return id.replace(/i/gi, "1").replace(/l/gi, "1").replace(/o/gi, "0").replace(/-/g, "");
103
+ }
104
+ function incrementBase32(str) {
105
+ let done = undefined, index = str.length, char, charIndex, output = str;
106
+ const maxCharIndex = ENCODING_LEN - 1;
107
+ while (!done && index-- >= 0) {
108
+ char = output[index];
109
+ charIndex = ENCODING.indexOf(char);
110
+ if (charIndex === -1) {
111
+ throw new ULIDError(ULIDErrorCode.Base32IncorrectEncoding, "Incorrectly encoded string");
112
+ }
113
+ if (charIndex === maxCharIndex) {
114
+ output = replaceCharAt(output, index, ENCODING[0]);
115
+ continue;
116
+ }
117
+ done = replaceCharAt(output, index, ENCODING[charIndex + 1]);
118
+ }
119
+ if (typeof done === "string") {
120
+ return done;
121
+ }
122
+ throw new ULIDError(ULIDErrorCode.Base32IncorrectEncoding, "Failed incrementing string");
123
+ }
124
+
125
+ /**
126
+ * Decode time from a ULID
127
+ * @param id The ULID
128
+ * @returns The decoded timestamp
129
+ */
130
+ function decodeTime(id) {
131
+ if (id.length !== TIME_LEN + RANDOM_LEN) {
132
+ throw new ULIDError(ULIDErrorCode.DecodeTimeValueMalformed, "Malformed ULID");
133
+ }
134
+ const time = id
135
+ .substr(0, TIME_LEN)
136
+ .toUpperCase()
137
+ .split("")
138
+ .reverse()
139
+ .reduce((carry, char, index) => {
140
+ const encodingIndex = ENCODING.indexOf(char);
141
+ if (encodingIndex === -1) {
142
+ throw new ULIDError(ULIDErrorCode.DecodeTimeInvalidCharacter, `Time decode error: Invalid character: ${char}`);
143
+ }
144
+ return (carry += encodingIndex * Math.pow(ENCODING_LEN, index));
145
+ }, 0);
146
+ if (time > TIME_MAX) {
147
+ throw new ULIDError(ULIDErrorCode.DecodeTimeValueMalformed, `Malformed ULID: timestamp too large: ${time}`);
148
+ }
149
+ return time;
150
+ }
151
+ /**
152
+ * Detect the best PRNG (pseudo-random number generator)
153
+ * @param root The root to check from (global/window)
154
+ * @returns The PRNG function
155
+ */
156
+ function detectPRNG(root) {
157
+ const rootLookup = detectRoot();
158
+ const globalCrypto = (rootLookup && (rootLookup.crypto || rootLookup.msCrypto)) ||
159
+ (typeof crypto !== "undefined" ? crypto : null);
160
+ if (typeof globalCrypto?.getRandomValues === "function") {
161
+ return () => {
162
+ const buffer = new Uint8Array(1);
163
+ globalCrypto.getRandomValues(buffer);
164
+ return buffer[0] / 256;
165
+ };
166
+ }
167
+ else if (typeof globalCrypto?.randomBytes === "function") {
168
+ return () => globalCrypto.randomBytes(1).readUInt8() / 256;
169
+ }
170
+ else if (crypto?.randomBytes) {
171
+ return () => crypto.randomBytes(1).readUInt8() / 256;
172
+ }
173
+ throw new ULIDError(ULIDErrorCode.PRNGDetectFailure, "Failed to find a reliable PRNG");
174
+ }
175
+ function detectRoot() {
176
+ if (inWebWorker())
177
+ return self;
178
+ if (typeof window !== "undefined") {
179
+ return window;
180
+ }
181
+ if (typeof global !== "undefined") {
182
+ return global;
183
+ }
184
+ if (typeof globalThis !== "undefined") {
185
+ return globalThis;
186
+ }
187
+ return null;
188
+ }
189
+ function encodeRandom(len, prng) {
190
+ let str = "";
191
+ for (; len > 0; len--) {
192
+ str = randomChar(prng) + str;
193
+ }
194
+ return str;
195
+ }
196
+ /**
197
+ * Encode the time portion of a ULID
198
+ * @param now The current timestamp
199
+ * @param len Length to generate
200
+ * @returns The encoded time
201
+ */
202
+ function encodeTime(now, len = TIME_LEN) {
203
+ if (isNaN(now)) {
204
+ throw new ULIDError(ULIDErrorCode.EncodeTimeValueMalformed, `Time must be a number: ${now}`);
205
+ }
206
+ else if (now > TIME_MAX) {
207
+ throw new ULIDError(ULIDErrorCode.EncodeTimeSizeExceeded, `Cannot encode a time larger than ${TIME_MAX}: ${now}`);
208
+ }
209
+ else if (now < 0) {
210
+ throw new ULIDError(ULIDErrorCode.EncodeTimeNegative, `Time must be positive: ${now}`);
211
+ }
212
+ else if (Number.isInteger(now) === false) {
213
+ throw new ULIDError(ULIDErrorCode.EncodeTimeValueMalformed, `Time must be an integer: ${now}`);
214
+ }
215
+ let mod, str = "";
216
+ for (let currentLen = len; currentLen > 0; currentLen--) {
217
+ mod = now % ENCODING_LEN;
218
+ str = ENCODING.charAt(mod) + str;
219
+ now = (now - mod) / ENCODING_LEN;
220
+ }
221
+ return str;
222
+ }
223
+ function inWebWorker() {
224
+ // @ts-ignore
225
+ return typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope;
226
+ }
227
+ /**
228
+ * Check if a ULID is valid
229
+ * @param id The ULID to test
230
+ * @returns True if valid, false otherwise
231
+ * @example
232
+ * isValid("01HNZX8JGFACFA36RBXDHEQN6E"); // true
233
+ * isValid(""); // false
234
+ */
235
+ function isValid(id) {
236
+ return (typeof id === "string" &&
237
+ id.length === TIME_LEN + RANDOM_LEN &&
238
+ id
239
+ .toUpperCase()
240
+ .split("")
241
+ .every(char => ENCODING.indexOf(char) !== -1));
242
+ }
243
+ /**
244
+ * Create a ULID factory to generate monotonically-increasing
245
+ * ULIDs
246
+ * @param prng The PRNG to use
247
+ * @returns A ulid factory
248
+ * @example
249
+ * const ulid = monotonicFactory();
250
+ * ulid(); // "01HNZXD07M5CEN5XA66EMZSRZW"
251
+ */
252
+ function monotonicFactory(prng) {
253
+ const currentPRNG = prng || detectPRNG();
254
+ let lastTime = 0, lastRandom;
255
+ return function _ulid(seedTime) {
256
+ const seed = !seedTime || isNaN(seedTime) ? Date.now() : seedTime;
257
+ if (seed <= lastTime) {
258
+ const incrementedRandom = (lastRandom = incrementBase32(lastRandom));
259
+ return encodeTime(lastTime, TIME_LEN) + incrementedRandom;
260
+ }
261
+ lastTime = seed;
262
+ const newRandom = (lastRandom = encodeRandom(RANDOM_LEN, currentPRNG));
263
+ return encodeTime(seed, TIME_LEN) + newRandom;
264
+ };
265
+ }
266
+ /**
267
+ * Generate a ULID
268
+ * @param seedTime Optional time seed
269
+ * @param prng Optional PRNG function
270
+ * @returns A ULID string
271
+ * @example
272
+ * ulid(); // "01HNZXD07M5CEN5XA66EMZSRZW"
273
+ */
274
+ function ulid(seedTime, prng) {
275
+ const currentPRNG = prng || detectPRNG();
276
+ const seed = !seedTime || isNaN(seedTime) ? Date.now() : seedTime;
277
+ return encodeTime(seed, TIME_LEN) + encodeRandom(RANDOM_LEN, currentPRNG);
278
+ }
279
+
280
+ /**
281
+ * Convert a ULID to a UUID
282
+ * @param ulid The ULID to convert
283
+ * @returns A UUID string
284
+ */
285
+ function ulidToUUID(ulid) {
286
+ const isValid = ULID_REGEX.test(ulid);
287
+ if (!isValid) {
288
+ throw new ULIDError(ULIDErrorCode.ULIDInvalid, `Invalid ULID: ${ulid}`);
289
+ }
290
+ const uint8Array = crockfordDecode(ulid);
291
+ let uuid = Array.from(uint8Array)
292
+ .map(byte => byte.toString(16).padStart(2, "0"))
293
+ .join("");
294
+ uuid =
295
+ uuid.substring(0, 8) +
296
+ "-" +
297
+ uuid.substring(8, 12) +
298
+ "-" +
299
+ uuid.substring(12, 16) +
300
+ "-" +
301
+ uuid.substring(16, 20) +
302
+ "-" +
303
+ uuid.substring(20);
304
+ return uuid.toUpperCase();
305
+ }
306
+ /**
307
+ * Convert a UUID to a ULID
308
+ * @param uuid The UUID to convert
309
+ * @returns A ULID string
310
+ */
311
+ function uuidToULID(uuid) {
312
+ const isValid = UUID_REGEX.test(uuid);
313
+ if (!isValid) {
314
+ throw new ULIDError(ULIDErrorCode.UUIDInvalid, `Invalid UUID: ${uuid}`);
315
+ }
316
+ const bytes = uuid.replace(/-/g, "").match(/.{1,2}/g);
317
+ if (!bytes) {
318
+ throw new ULIDError(ULIDErrorCode.Unexpected, `Failed parsing UUID bytes: ${uuid}`);
319
+ }
320
+ const uint8Array = new Uint8Array(bytes.map(byte => parseInt(byte, 16)));
321
+ return crockfordEncode(uint8Array);
322
+ }
323
+
324
+ export { MAX_ULID, MIN_ULID, TIME_LEN, TIME_MAX, ULIDError, ULIDErrorCode, decodeTime, encodeTime, fixULIDBase32, incrementBase32, isValid, monotonicFactory, ulid, ulidToUUID, uuidToULID };