uniku 0.1.0 → 0.3.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 +141 -22
- package/build/{bytes-DzcOKIx1.mjs → bytes-xqWxFYsM.mjs} +1 -1
- package/build/{bytes-DzcOKIx1.mjs.map → bytes-xqWxFYsM.mjs.map} +1 -1
- package/build/cuid/v2.d.mts +2 -0
- package/build/cuid/v2.mjs +1 -0
- package/build/cuid2/cuid2.d.mts +5 -1
- package/build/cuid2/cuid2.d.mts.map +1 -1
- package/build/cuid2/cuid2.mjs +1 -1
- package/build/cuid2/cuid2.mjs.map +1 -1
- package/build/errors-Dgoyi-ci.d.mts +38 -0
- package/build/errors-Dgoyi-ci.d.mts.map +1 -0
- package/build/errors.d.mts +2 -38
- package/build/errors.mjs +1 -1
- package/build/errors.mjs.map +1 -1
- package/build/generators.d.mts +17 -0
- package/build/generators.d.mts.map +1 -0
- package/build/generators.mjs +2 -0
- package/build/generators.mjs.map +1 -0
- package/build/ksuid/ksuid.d.mts +3 -5
- package/build/ksuid/ksuid.d.mts.map +1 -1
- package/build/ksuid/ksuid.mjs +1 -1
- package/build/ksuid/ksuid.mjs.map +1 -1
- package/build/nanoid/nanoid.d.mts +3 -6
- package/build/nanoid/nanoid.d.mts.map +1 -1
- package/build/nanoid/nanoid.mjs +1 -1
- package/build/nanoid/nanoid.mjs.map +1 -1
- package/build/objectid/objectid.d.mts +60 -0
- package/build/objectid/objectid.d.mts.map +1 -0
- package/build/objectid/objectid.mjs +2 -0
- package/build/objectid/objectid.mjs.map +1 -0
- package/build/random-Chp-Nkzi.mjs +2 -0
- package/build/{random-CGMm8owG.mjs.map → random-Chp-Nkzi.mjs.map} +1 -1
- package/build/tsid/tsid.d.mts +82 -0
- package/build/tsid/tsid.d.mts.map +1 -0
- package/build/tsid/tsid.mjs +2 -0
- package/build/tsid/tsid.mjs.map +1 -0
- package/build/typeid/typeid.d.mts +1 -1
- package/build/typeid/typeid.d.mts.map +1 -1
- package/build/typeid/typeid.mjs.map +1 -1
- package/build/ulid/ulid.d.mts +3 -5
- package/build/ulid/ulid.d.mts.map +1 -1
- package/build/ulid/ulid.mjs +1 -1
- package/build/ulid/ulid.mjs.map +1 -1
- package/build/uuid/v4.d.mts +3 -5
- package/build/uuid/v4.d.mts.map +1 -1
- package/build/uuid/v4.mjs +1 -1
- package/build/uuid/v4.mjs.map +1 -1
- package/build/uuid/v7.d.mts +3 -5
- package/build/uuid/v7.d.mts.map +1 -1
- package/build/uuid/v7.mjs +1 -1
- package/build/uuid/v7.mjs.map +1 -1
- package/build/{uuid-Ub6ladEr.mjs → uuid-BBjwHv7J.mjs} +1 -1
- package/build/uuid-BBjwHv7J.mjs.map +1 -0
- package/package.json +35 -5
- package/src/cuid/v2.ts +19 -0
- package/src/cuid2/cuid2.ts +4 -0
- package/src/generators.ts +17 -0
- package/src/objectid/hex.ts +89 -0
- package/src/objectid/objectid.ts +266 -0
- package/src/tsid/crockford64.ts +75 -0
- package/src/tsid/tsid.ts +310 -0
- package/build/errors.d.mts.map +0 -1
- package/build/random-CGMm8owG.mjs +0 -2
- package/build/tsconfig.tsbuildinfo +0 -1
- package/build/uuid-Ub6ladEr.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ksuid.mjs","names":["KSUID_BYTES","KSUID_STRING_LEN","timestamp","timestamp: number","ksuid: Ksuid"],"sources":["../../src/ksuid/base62.ts","../../src/ksuid/ksuid.ts"],"sourcesContent":["import { BufferError, ParseError } from '../errors'\n\n/**\n * Base62 encoding/decoding for KSUID.\n * Alphabet: 0-9A-Za-z (standard Base62 ordering)\n *\n * KSUID binary format: 20 bytes (160 bits)\n * - 4 bytes: timestamp (seconds since KSUID epoch)\n * - 16 bytes: payload (cryptographically random)\n *\n * String format: 27 characters of Base62\n */\n\n// Base62 alphabet: digits (0-9), uppercase (A-Z), lowercase (a-z)\nconst BASE62_ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'\nconst BASE = 62n\nconst MAX_KSUID_VALUE = (1n << 160n) - 1n\nconst KSUID_BYTES = 20\nconst KSUID_STRING_LEN = 27\n\n// Pre-computed decode table covering every UTF-16 code unit charCodeAt can\n// return, so lookups never go out of bounds and a single `=== 255` check\n// rejects invalid input (including non-ASCII) without a per-character range\n// check. Costs 64 KiB once at module load; valid inputs only touch the first\n// 128 bytes, so cache behavior is unaffected.\n// Note: Base62 is case-sensitive - 'A' (value 10) and 'a' (value 36) are different\nconst DECODING = new Uint8Array(65536)\nDECODING.fill(255) // 255 = invalid marker\n\nfor (let i = 0; i < BASE62_ALPHABET.length; i += 1) {\n DECODING[BASE62_ALPHABET.charCodeAt(i)] = i\n}\n\n/**\n * Encode a 20-byte KSUID to a 27-character Base62 string.\n *\n * Algorithm: Convert the 160-bit number to base 62 using BigInt.\n * V8 has highly optimized BigInt operations for this size.\n */\nexport function encodeBase62(bytes: Uint8Array): string {\n if (bytes.length < KSUID_BYTES) {\n throw new BufferError(\n 'KSUID_BYTES_TOO_SHORT',\n `KSUID bytes must be at least ${KSUID_BYTES} bytes, got ${bytes.length}`,\n )\n }\n\n // Convert bytes to BigInt (big-endian)\n let num = 0n\n for (let i = 0; i < KSUID_BYTES; i += 1) {\n num = (num << 8n) | BigInt(bytes[i])\n }\n\n // Convert to Base62 string (build from right to left)\n // Direct string concatenation is faster than array + join in V8\n let encoded = ''\n while (num > 0n) {\n const remainder = num % BASE\n num = num / BASE\n encoded = BASE62_ALPHABET[Number(remainder)] + encoded\n }\n\n // Pad the result with the zero-character ('0') to ensure a fixed length\n return encoded.padStart(KSUID_STRING_LEN, '0')\n}\n\n/**\n * Decode a 27-character Base62 string to a 20-byte KSUID.\n *\n * Algorithm: Convert Base62 string to BigInt, then to bytes.\n * V8 has highly optimized BigInt operations.\n */\nexport function decodeBase62(str: string): Uint8Array {\n if (str.length !== KSUID_STRING_LEN) {\n throw new ParseError(\n 'KSUID_INVALID_LENGTH',\n `KSUID string must be ${KSUID_STRING_LEN} characters, got ${str.length}`,\n )\n }\n\n // Convert Base62 string to BigInt\n let num = 0n\n for (let i = 0; i < KSUID_STRING_LEN; i += 1) {\n const value = DECODING[str.charCodeAt(i)]\n if (value === 255) {\n throw new ParseError('KSUID_INVALID_CHAR', `Invalid KSUID character: ${str[i]}`)\n }\n num = num * BASE + BigInt(value)\n }\n\n if (num > MAX_KSUID_VALUE) {\n throw new ParseError('KSUID_OVERFLOW', 'KSUID string exceeds 160-bit range')\n }\n\n // Convert BigInt to bytes (big-endian)\n const bytes = new Uint8Array(KSUID_BYTES)\n for (let i = KSUID_BYTES - 1; i >= 0; i -= 1) {\n bytes[i] = Number(num & 0xffn)\n num = num >> 8n\n }\n\n return bytes\n}\n","import { writeTimestamp32 } from '../common/bytes'\nimport { rng } from '../common/random'\nimport { BufferError, InvalidInputError } from '../errors'\nimport { decodeBase62, encodeBase62 } from './base62'\n\n/**\n * KSUID (K-Sortable Unique Identifier)\n *\n * A 160-bit identifier consisting of:\n * - 4 bytes: timestamp (seconds since KSUID epoch: May 13, 2014)\n * - 16 bytes: cryptographically random payload\n *\n * Encoded as a 27-character Base62 string.\n */\n\n// KSUID epoch: May 13, 2014 00:00:00 UTC (Unix timestamp in seconds)\nconst KSUID_EPOCH = 1400000000\n\nconst KSUID_BYTES = 20\nconst KSUID_STRING_LEN = 27\nconst TIMESTAMP_BYTES = 4\nconst PAYLOAD_BYTES = 16\nconst KSUID_MAX_SECS = KSUID_EPOCH + 0xffffffff\n// 2^160 - 1 in Base62 ('aWgEPTl1tmebfsQzFP4bxwgy80V'), derived from the\n// encoder so it cannot drift from the decoder's 160-bit overflow bound.\nconst KSUID_MAX_STRING = encodeBase62(new Uint8Array(KSUID_BYTES).fill(0xff))\n\n// Validation regex: 27 alphanumeric characters\n// Note: Both cases are valid Base62 characters, but they decode to different values\n// (e.g., 'A' = 10, 'a' = 36). The regex validates format, not semantic equivalence.\nconst KSUID_REGEX = /^[0-9A-Za-z]{27}$/\n\nexport type KsuidOptions = {\n /**\n * 16 bytes of random data to use for KSUID payload.\n */\n random?: Uint8Array\n /**\n * Timestamp in seconds since Unix epoch.\n * Defaults to Math.floor(Date.now() / 1000).\n * KSUID natively uses second precision.\n */\n secs?: number\n}\n\nexport type Ksuid = {\n (): string\n <TBuf extends Uint8Array = Uint8Array>(options: KsuidOptions | undefined, buf: TBuf, offset?: number): TBuf\n (options?: KsuidOptions, buf?: undefined, offset?: number): string\n toBytes(id: string): Uint8Array\n fromBytes(bytes: Uint8Array): string\n timestamp(id: string): number\n isValid(id: unknown): id is string\n /** The nil KSUID (all zeros) */\n NIL: string\n /** The max KSUID (maximum valid value) */\n MAX: string\n}\n\n/**\n * Write KSUID bytes to a buffer.\n */\nfunction ksuidBytes(timestamp: number, payload: Uint8Array, buf?: Uint8Array, offset = 0): Uint8Array {\n if (!buf) {\n buf = new Uint8Array(KSUID_BYTES)\n offset = 0\n } else if (offset < 0 || offset + KSUID_BYTES > buf.length) {\n throw new BufferError(\n 'KSUID_BUFFER_OUT_OF_BOUNDS',\n `KSUID byte range ${offset}:${offset + KSUID_BYTES - 1} is out of buffer bounds`,\n )\n }\n\n // Timestamp (32-bit big-endian seconds since KSUID epoch) -> bytes 0-3\n writeTimestamp32(buf, offset, timestamp)\n\n // Payload (128 bits) -> bytes 4-19\n for (let i = 0; i < PAYLOAD_BYTES; i += 1) {\n buf[offset + TIMESTAMP_BYTES + i] = payload[i]\n }\n\n return buf\n}\n\n/*\n * Overload: no buffer => return a KSUID string.\n */\nfunction ksuidFn(options?: KsuidOptions, buf?: undefined, offset?: number): string\n/*\n * Overload: caller provides a buffer slice to fill with KSUID bytes.\n */\nfunction ksuidFn<TBuf extends Uint8Array = Uint8Array>(\n options: KsuidOptions | undefined,\n buf: TBuf,\n offset?: number,\n): TBuf\nfunction ksuidFn<TBuf extends Uint8Array = Uint8Array>(options?: KsuidOptions, buf?: TBuf, offset = 0): string | TBuf {\n const random = options?.random\n if (random && random.length < PAYLOAD_BYTES) {\n throw new InvalidInputError('KSUID_RANDOM_BYTES_TOO_SHORT', 'Random bytes length must be >= 16 for KSUID')\n }\n\n let timestamp: number\n const secs = options?.secs\n if (secs !== undefined) {\n if (secs < KSUID_EPOCH) {\n throw new InvalidInputError('KSUID_TIMESTAMP_TOO_LOW', 'Timestamp must be >= KSUID epoch')\n }\n\n if (secs > KSUID_MAX_SECS) {\n throw new InvalidInputError('KSUID_TIMESTAMP_TOO_HIGH', 'Timestamp must be <= maximum KSUID timestamp')\n }\n\n timestamp = secs - KSUID_EPOCH\n } else {\n /**\n * Note: by default, Cloudflare Workers \"freezes\" time during request handling to prevent\n * side-channel attacks. This means that Date.now() will return the same value for the entire\n * duration of a request.\n * Implications:\n * - all KSUIDs generated within a single request will have the same timestamp.\n */\n timestamp = Math.floor(Date.now() / 1000) - KSUID_EPOCH\n }\n\n const payload = random ?? rng()\n\n if (buf) {\n ksuidBytes(timestamp, payload, buf, offset)\n return buf\n }\n\n const bytes = ksuidBytes(timestamp, payload)\n\n // String mode: create bytes then encode to Base62\n return encodeBase62(bytes)\n}\n\n/**\n * Convert a KSUID string to 20 bytes.\n *\n * Note: Base62 is case-sensitive. 'A' (value 10) and 'a' (value 36) decode\n * to different byte values. This function accepts both cases as valid input.\n */\nfunction toBytes(id: string): Uint8Array {\n return decodeBase62(id)\n}\n\n/**\n * Convert 20 bytes to a KSUID string.\n */\nfunction fromBytes(bytes: Uint8Array): string {\n return encodeBase62(bytes)\n}\n\n/**\n * Extract the timestamp from a KSUID string.\n * Returns Unix timestamp in milliseconds for API consistency with ulid/uuidv7.\n * Note: KSUID only has second precision, so the returned value will always end in 000.\n */\nfunction timestamp(id: string): number {\n const bytes = decodeBase62(id)\n // First 4 bytes are big-endian timestamp (seconds since KSUID epoch)\n const secs = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0\n // Add KSUID epoch and convert to milliseconds\n return (secs + KSUID_EPOCH) * 1000\n}\n\n/**\n * Validate a KSUID string format and 160-bit numeric range.\n */\nfunction isValid(id: unknown): id is string {\n return (\n typeof id === 'string' &&\n id.length === KSUID_STRING_LEN &&\n KSUID_REGEX.test(id) &&\n // Fixed-length Base62 strings preserve numeric order because the alphabet is ASCII-sorted.\n id <= KSUID_MAX_STRING\n )\n}\n\n/**\n * Generate a KSUID string or write the bytes into a buffer.\n * Also includes helpers to convert to and from byte arrays.\n */\nexport const ksuid: Ksuid = Object.assign(ksuidFn, {\n toBytes,\n fromBytes,\n timestamp,\n isValid,\n NIL: '000000000000000000000000000',\n MAX: KSUID_MAX_STRING,\n})\n\nexport { BufferError, InvalidInputError, ParseError, UniqueIdError } from '../errors'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ksuid.mjs","names":["KSUID_BYTES","KSUID_STRING_LEN"],"sources":["../../src/ksuid/base62.ts","../../src/ksuid/ksuid.ts"],"sourcesContent":["import { BufferError, ParseError } from '../errors'\n\n/**\n * Base62 encoding/decoding for KSUID.\n * Alphabet: 0-9A-Za-z (standard Base62 ordering)\n *\n * KSUID binary format: 20 bytes (160 bits)\n * - 4 bytes: timestamp (seconds since KSUID epoch)\n * - 16 bytes: payload (cryptographically random)\n *\n * String format: 27 characters of Base62\n */\n\n// Base62 alphabet: digits (0-9), uppercase (A-Z), lowercase (a-z)\nconst BASE62_ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'\nconst BASE = 62n\nconst MAX_KSUID_VALUE = (1n << 160n) - 1n\nconst KSUID_BYTES = 20\nconst KSUID_STRING_LEN = 27\n\n// Pre-computed decode table covering every UTF-16 code unit charCodeAt can\n// return, so lookups never go out of bounds and a single `=== 255` check\n// rejects invalid input (including non-ASCII) without a per-character range\n// check. Costs 64 KiB once at module load; valid inputs only touch the first\n// 128 bytes, so cache behavior is unaffected.\n// Note: Base62 is case-sensitive - 'A' (value 10) and 'a' (value 36) are different\nconst DECODING = new Uint8Array(65536)\nDECODING.fill(255) // 255 = invalid marker\n\nfor (let i = 0; i < BASE62_ALPHABET.length; i += 1) {\n DECODING[BASE62_ALPHABET.charCodeAt(i)] = i\n}\n\n/**\n * Encode a 20-byte KSUID to a 27-character Base62 string.\n *\n * Algorithm: Convert the 160-bit number to base 62 using BigInt.\n * V8 has highly optimized BigInt operations for this size.\n */\nexport function encodeBase62(bytes: Uint8Array): string {\n if (bytes.length < KSUID_BYTES) {\n throw new BufferError(\n 'KSUID_BYTES_TOO_SHORT',\n `KSUID bytes must be at least ${KSUID_BYTES} bytes, got ${bytes.length}`,\n )\n }\n\n // Convert bytes to BigInt (big-endian)\n let num = 0n\n for (let i = 0; i < KSUID_BYTES; i += 1) {\n num = (num << 8n) | BigInt(bytes[i])\n }\n\n // Convert to Base62 string (build from right to left)\n // Direct string concatenation is faster than array + join in V8\n let encoded = ''\n while (num > 0n) {\n const remainder = num % BASE\n num = num / BASE\n encoded = BASE62_ALPHABET[Number(remainder)] + encoded\n }\n\n // Pad the result with the zero-character ('0') to ensure a fixed length\n return encoded.padStart(KSUID_STRING_LEN, '0')\n}\n\n/**\n * Decode a 27-character Base62 string to a 20-byte KSUID.\n *\n * Algorithm: Convert Base62 string to BigInt, then to bytes.\n * V8 has highly optimized BigInt operations.\n */\nexport function decodeBase62(str: string): Uint8Array {\n if (str.length !== KSUID_STRING_LEN) {\n throw new ParseError(\n 'KSUID_INVALID_LENGTH',\n `KSUID string must be ${KSUID_STRING_LEN} characters, got ${str.length}`,\n )\n }\n\n // Convert Base62 string to BigInt\n let num = 0n\n for (let i = 0; i < KSUID_STRING_LEN; i += 1) {\n const value = DECODING[str.charCodeAt(i)]\n if (value === 255) {\n throw new ParseError('KSUID_INVALID_CHAR', `Invalid KSUID character: ${str[i]}`)\n }\n num = num * BASE + BigInt(value)\n }\n\n if (num > MAX_KSUID_VALUE) {\n throw new ParseError('KSUID_OVERFLOW', 'KSUID string exceeds 160-bit range')\n }\n\n // Convert BigInt to bytes (big-endian)\n const bytes = new Uint8Array(KSUID_BYTES)\n for (let i = KSUID_BYTES - 1; i >= 0; i -= 1) {\n bytes[i] = Number(num & 0xffn)\n num = num >> 8n\n }\n\n return bytes\n}\n","import { writeTimestamp32 } from '../common/bytes'\nimport { rng } from '../common/random'\nimport { BufferError, InvalidInputError } from '../errors'\nimport { decodeBase62, encodeBase62 } from './base62'\n\n/**\n * KSUID (K-Sortable Unique Identifier)\n *\n * A 160-bit identifier consisting of:\n * - 4 bytes: timestamp (seconds since KSUID epoch: May 13, 2014)\n * - 16 bytes: cryptographically random payload\n *\n * Encoded as a 27-character Base62 string.\n */\n\n// KSUID epoch: May 13, 2014 00:00:00 UTC (Unix timestamp in seconds)\nconst KSUID_EPOCH = 1400000000\n\nconst KSUID_BYTES = 20\nconst KSUID_STRING_LEN = 27\nconst TIMESTAMP_BYTES = 4\nconst PAYLOAD_BYTES = 16\nconst KSUID_MAX_SECS = KSUID_EPOCH + 0xffffffff\n// 2^160 - 1 in Base62 ('aWgEPTl1tmebfsQzFP4bxwgy80V'), derived from the\n// encoder so it cannot drift from the decoder's 160-bit overflow bound.\nconst KSUID_MAX_STRING = encodeBase62(new Uint8Array(KSUID_BYTES).fill(0xff))\n\n// Validation regex: 27 alphanumeric characters\n// Note: Both cases are valid Base62 characters, but they decode to different values\n// (e.g., 'A' = 10, 'a' = 36). The regex validates format, not semantic equivalence.\nconst KSUID_REGEX = /^[0-9A-Za-z]{27}$/\n\nexport type KsuidOptions = {\n /**\n * 16 bytes of random data to use for KSUID payload.\n */\n random?: Uint8Array\n /**\n * Timestamp in seconds since Unix epoch.\n * Defaults to Math.floor(Date.now() / 1000).\n * KSUID natively uses second precision.\n */\n secs?: number\n}\n\nexport type Ksuid = {\n (): string\n <TBuf extends Uint8Array = Uint8Array>(options: KsuidOptions | undefined, buf: TBuf, offset?: number): TBuf\n (options?: KsuidOptions, buf?: undefined, offset?: number): string\n toBytes(id: string): Uint8Array\n fromBytes(bytes: Uint8Array): string\n timestamp(id: string): number\n isValid(id: unknown): id is string\n /** The nil KSUID (all zeros) */\n NIL: string\n /** The max KSUID (maximum valid value) */\n MAX: string\n}\n\n/**\n * Write KSUID bytes to a buffer.\n */\nfunction ksuidBytes(timestamp: number, payload: Uint8Array, buf?: Uint8Array, offset = 0): Uint8Array {\n if (!buf) {\n buf = new Uint8Array(KSUID_BYTES)\n offset = 0\n } else if (offset < 0 || offset + KSUID_BYTES > buf.length) {\n throw new BufferError(\n 'KSUID_BUFFER_OUT_OF_BOUNDS',\n `KSUID byte range ${offset}:${offset + KSUID_BYTES - 1} is out of buffer bounds`,\n )\n }\n\n // Timestamp (32-bit big-endian seconds since KSUID epoch) -> bytes 0-3\n writeTimestamp32(buf, offset, timestamp)\n\n // Payload (128 bits) -> bytes 4-19\n for (let i = 0; i < PAYLOAD_BYTES; i += 1) {\n buf[offset + TIMESTAMP_BYTES + i] = payload[i]\n }\n\n return buf\n}\n\n/*\n * Overload: no buffer => return a KSUID string.\n */\nfunction ksuidFn(options?: KsuidOptions, buf?: undefined, offset?: number): string\n/*\n * Overload: caller provides a buffer slice to fill with KSUID bytes.\n */\nfunction ksuidFn<TBuf extends Uint8Array = Uint8Array>(\n options: KsuidOptions | undefined,\n buf: TBuf,\n offset?: number,\n): TBuf\nfunction ksuidFn<TBuf extends Uint8Array = Uint8Array>(options?: KsuidOptions, buf?: TBuf, offset = 0): string | TBuf {\n const random = options?.random\n if (random && random.length < PAYLOAD_BYTES) {\n throw new InvalidInputError('KSUID_RANDOM_BYTES_TOO_SHORT', 'Random bytes length must be >= 16 for KSUID')\n }\n\n let timestamp: number\n const secs = options?.secs\n if (secs !== undefined) {\n if (secs < KSUID_EPOCH) {\n throw new InvalidInputError('KSUID_TIMESTAMP_TOO_LOW', 'Timestamp must be >= KSUID epoch')\n }\n\n if (secs > KSUID_MAX_SECS) {\n throw new InvalidInputError('KSUID_TIMESTAMP_TOO_HIGH', 'Timestamp must be <= maximum KSUID timestamp')\n }\n\n timestamp = secs - KSUID_EPOCH\n } else {\n /**\n * Note: by default, Cloudflare Workers \"freezes\" time during request handling to prevent\n * side-channel attacks. This means that Date.now() will return the same value for the entire\n * duration of a request.\n * Implications:\n * - all KSUIDs generated within a single request will have the same timestamp.\n */\n timestamp = Math.floor(Date.now() / 1000) - KSUID_EPOCH\n }\n\n const payload = random ?? rng()\n\n if (buf) {\n ksuidBytes(timestamp, payload, buf, offset)\n return buf\n }\n\n const bytes = ksuidBytes(timestamp, payload)\n\n // String mode: create bytes then encode to Base62\n return encodeBase62(bytes)\n}\n\n/**\n * Convert a KSUID string to 20 bytes.\n *\n * Note: Base62 is case-sensitive. 'A' (value 10) and 'a' (value 36) decode\n * to different byte values. This function accepts both cases as valid input.\n */\nfunction toBytes(id: string): Uint8Array {\n return decodeBase62(id)\n}\n\n/**\n * Convert 20 bytes to a KSUID string.\n */\nfunction fromBytes(bytes: Uint8Array): string {\n return encodeBase62(bytes)\n}\n\n/**\n * Extract the timestamp from a KSUID string.\n * Returns Unix timestamp in milliseconds for API consistency with ulid/uuidv7.\n * Note: KSUID only has second precision, so the returned value will always end in 000.\n */\nfunction timestamp(id: string): number {\n const bytes = decodeBase62(id)\n // First 4 bytes are big-endian timestamp (seconds since KSUID epoch)\n const secs = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0\n // Add KSUID epoch and convert to milliseconds\n return (secs + KSUID_EPOCH) * 1000\n}\n\n/**\n * Validate a KSUID string format and 160-bit numeric range.\n */\nfunction isValid(id: unknown): id is string {\n return (\n typeof id === 'string' &&\n id.length === KSUID_STRING_LEN &&\n KSUID_REGEX.test(id) &&\n // Fixed-length Base62 strings preserve numeric order because the alphabet is ASCII-sorted.\n id <= KSUID_MAX_STRING\n )\n}\n\n/**\n * Generate a KSUID string or write the bytes into a buffer.\n * Also includes helpers to convert to and from byte arrays.\n */\nexport const ksuid: Ksuid = Object.assign(ksuidFn, {\n toBytes,\n fromBytes,\n timestamp,\n isValid,\n NIL: '000000000000000000000000000',\n MAX: KSUID_MAX_STRING,\n})\n\nexport { BufferError, InvalidInputError, ParseError, UniqueIdError } from '../errors'\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as UniqueIdError, n as InvalidInputError } from "../errors-Dgoyi-ci.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/nanoid/nanoid.d.ts
|
|
4
4
|
/** Default URL-safe alphabet (64 characters): A-Z, a-z, 0-9, underscore, hyphen */
|
|
@@ -21,11 +21,8 @@ type NanoidOptions = {
|
|
|
21
21
|
size?: number;
|
|
22
22
|
};
|
|
23
23
|
type Nanoid = {
|
|
24
|
-
/** Generate nanoid with default settings */
|
|
25
|
-
(): string;
|
|
26
|
-
/** Generate nanoid with custom size */
|
|
27
|
-
(size: number): string;
|
|
28
|
-
/** Generate nanoid with options */
|
|
24
|
+
/** Generate nanoid with default settings */(): string; /** Generate nanoid with custom size */
|
|
25
|
+
(size: number): string; /** Generate nanoid with options */
|
|
29
26
|
(options: NanoidOptions): string;
|
|
30
27
|
/**
|
|
31
28
|
* Validate a nanoid string against the default URL-safe alphabet.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nanoid.d.mts","names":[
|
|
1
|
+
{"version":3,"file":"nanoid.d.mts","names":[],"sources":["../../src/nanoid/nanoid.ts"],"mappings":";;;;cAGa,YAAA;AAAA,KAmCD,aAAA;EAnCZ;;;;AAAa;EAyCX,MAAA,GAAS,UAAA;EANC;;;;EAWV,QAAA;;;;EAIA,IAAA;AAAA;AAAA,KAGU,MAAA;;GAIT,IAAA;GAEA,OAAA,EAAS,aAAA;;;;AAKY;EAAtB,OAAA,CAAQ,EAAA,YAAc,EAAA;AAAA;;;AAyLH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAR,MAAA,EAAQ,MAAA"}
|
package/build/nanoid/nanoid.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{InvalidInputError as e,UniqueIdError as t}from"../errors.mjs";const n=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-`,r=2048,i=/^[A-Za-z0-9_-]+$/;let a,o=0;function s(e){let t=Math.min(e*128,65536);!a||a.length<t?(a=new Uint8Array(t),crypto.getRandomValues(a),o=0):o+e>a.length&&(crypto.getRandomValues(a),o=0),o+=e}function c(e){s(e);let t=``;for(let r=o-e;r<o;r++)t+=n[a[r]&63];return t}function l(t){if(t.length<2)throw new e(`NANOID_ALPHABET_TOO_SHORT`,`Alphabet must contain at least 2 characters`);if(t.length>256)throw new e(`NANOID_ALPHABET_TOO_LONG`,`Alphabet must not exceed 256 characters`);let n=new Set;for(let r of t){let t=r.charCodeAt(0);if(t<32||t>126)throw new e(`NANOID_ALPHABET_INVALID_CHAR`,`Alphabet must contain only printable ASCII characters (32-126)`);if(n.has(r))throw new e(`NANOID_ALPHABET_DUPLICATE`,`Duplicate character in alphabet: "${r}"`);n.add(r)}}function u(t){if(!Number.isInteger(t)||t<0)throw new e(`NANOID_SIZE_INVALID`,`Size must be a non-negative integer`);if(t>r)throw new e(`NANOID_SIZE_TOO_LARGE`,`Size must not exceed ${r}`)}function d(t){if(t===void 0)return c(21);let r=21,i=n,a;if(typeof t==`number`?r=t:(r=t.size??21,i=t.alphabet
|
|
1
|
+
import{InvalidInputError as e,UniqueIdError as t}from"../errors.mjs";const n=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-`,r=2048,i=/^[A-Za-z0-9_-]+$/;let a,o=0;function s(e){let t=Math.min(e*128,65536);!a||a.length<t?(a=new Uint8Array(t),crypto.getRandomValues(a),o=0):o+e>a.length&&(crypto.getRandomValues(a),o=0),o+=e}function c(e){s(e);let t=``;for(let r=o-e;r<o;r++)t+=n[a[r]&63];return t}function l(t){if(t.length<2)throw new e(`NANOID_ALPHABET_TOO_SHORT`,`Alphabet must contain at least 2 characters`);if(t.length>256)throw new e(`NANOID_ALPHABET_TOO_LONG`,`Alphabet must not exceed 256 characters`);let n=new Set;for(let r of t){let t=r.charCodeAt(0);if(t<32||t>126)throw new e(`NANOID_ALPHABET_INVALID_CHAR`,`Alphabet must contain only printable ASCII characters (32-126)`);if(n.has(r))throw new e(`NANOID_ALPHABET_DUPLICATE`,`Duplicate character in alphabet: "${r}"`);n.add(r)}}function u(t){if(!Number.isInteger(t)||t<0)throw new e(`NANOID_SIZE_INVALID`,`Size must be a non-negative integer`);if(t>r)throw new e(`NANOID_SIZE_TOO_LARGE`,`Size must not exceed ${r}`)}function d(t){if(t===void 0)return c(21);let r=21,i=n,a;if(typeof t==`number`?r=t:(r=t.size??21,i=t.alphabet??`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-`,a=t.random,t.alphabet!==void 0&&l(i)),u(r),r===0)return``;if(i===`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-`&&a===void 0)return c(r);let o=i.length;if(!(o&o-1)){let t=o-1;if(a&&a.length<r)throw new e(`NANOID_RANDOM_BYTES_INSUFFICIENT`,`Insufficient random bytes: need ${r}, have ${a.length}`);let n=a?.subarray(0,r)??globalThis.crypto.getRandomValues(new Uint8Array(r)),s=``;for(let e=0;e<r;e++)s+=i[n[e]&t];return s}let s=(2<<31-Math.clz32(o-1|1))-1,d=Math.ceil(1.6*s*r/o),f=``,p=0;for(;f.length<r;){let t;if(a){if(a.length-p<d)throw new e(`NANOID_RANDOM_BYTES_INSUFFICIENT`,`Insufficient random bytes: need at least ${d} more, have ${a.length-p}`);t=a.subarray(p,p+d),p+=d}else t=globalThis.crypto.getRandomValues(new Uint8Array(d));for(let e=0;e<t.length&&f.length<r;e++){let n=t[e]&s;n<o&&(f+=i[n])}}return f}function f(e){return typeof e==`string`&&e.length>0&&i.test(e)}const p=Object.assign(d,{isValid:f});export{e as InvalidInputError,n as URL_ALPHABET,t as UniqueIdError,p as nanoid};
|
|
2
2
|
//# sourceMappingURL=nanoid.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nanoid.mjs","names":["pool: Uint8Array | undefined","randomBytes: Uint8Array | undefined","mask","id","bytes: Uint8Array","nanoid: Nanoid"],"sources":["../../src/nanoid/nanoid.ts"],"sourcesContent":["import { InvalidInputError } from '../errors'\n\n/** Default URL-safe alphabet (64 characters): A-Z, a-z, 0-9, underscore, hyphen */\nexport const URL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'\n\nconst DEFAULT_SIZE = 21\nconst MAX_SIZE = 2048\nconst NANOID_REGEX = /^[A-Za-z0-9_-]+$/\n\n// Keep Nanoid's private pool for its default hot path. It consumes `size`\n// bytes directly without the shared helper's UUID/CUID2-oriented branches.\nconst POOL_SIZE_MULTIPLIER = 128\nconst MAX_POOL_SIZE = 65_536\nlet pool: Uint8Array | undefined\nlet poolOffset = 0\n\nfunction fillPool(bytes: number): void {\n const size = Math.min(bytes * POOL_SIZE_MULTIPLIER, MAX_POOL_SIZE)\n if (!pool || pool.length < size) {\n pool = new Uint8Array(size)\n crypto.getRandomValues(pool)\n poolOffset = 0\n } else if (poolOffset + bytes > pool.length) {\n crypto.getRandomValues(pool)\n poolOffset = 0\n }\n poolOffset += bytes\n}\n\nfunction defaultAlphabetFromPool(size: number): string {\n fillPool(size)\n let id = ''\n for (let i = poolOffset - size; i < poolOffset; i++) {\n id += URL_ALPHABET[pool![i] & 63]\n }\n return id\n}\n\nexport type NanoidOptions = {\n /**\n * Random bytes for deterministic output (testing).\n * For power-of-2 alphabets (2, 4, 8, 16, 32, 64, 128, 256): exactly `size` bytes needed.\n * For other alphabets: ~size * 2 bytes needed (rejection sampling).\n */\n random?: Uint8Array\n /**\n * Custom alphabet to use. Default: URL-safe A-Za-z0-9_-\n * Must be 2-256 printable ASCII characters (32-126) with no duplicates.\n */\n alphabet?: string\n /**\n * Length of generated ID. Default: 21. Maximum: 2048.\n */\n size?: number\n}\n\nexport type Nanoid = {\n /** Generate nanoid with default settings */\n (): string\n /** Generate nanoid with custom size */\n (size: number): string\n /** Generate nanoid with options */\n (options: NanoidOptions): string\n /**\n * Validate a nanoid string against the default URL-safe alphabet.\n * Note: Does not validate IDs generated with custom alphabets.\n */\n isValid(id: unknown): id is string\n}\n\n/**\n * Validate alphabet: 2-256 printable ASCII chars, no duplicates\n */\nfunction validateAlphabet(alphabet: string): void {\n if (alphabet.length < 2) {\n throw new InvalidInputError('NANOID_ALPHABET_TOO_SHORT', 'Alphabet must contain at least 2 characters')\n }\n if (alphabet.length > 256) {\n throw new InvalidInputError('NANOID_ALPHABET_TOO_LONG', 'Alphabet must not exceed 256 characters')\n }\n const seen = new Set<string>()\n for (const char of alphabet) {\n const code = char.charCodeAt(0)\n if (code < 32 || code > 126) {\n throw new InvalidInputError(\n 'NANOID_ALPHABET_INVALID_CHAR',\n 'Alphabet must contain only printable ASCII characters (32-126)',\n )\n }\n if (seen.has(char)) {\n throw new InvalidInputError('NANOID_ALPHABET_DUPLICATE', `Duplicate character in alphabet: \"${char}\"`)\n }\n seen.add(char)\n }\n}\n\n/**\n * Validate size parameter\n */\nfunction validateSize(size: number): void {\n if (!Number.isInteger(size) || size < 0) {\n throw new InvalidInputError('NANOID_SIZE_INVALID', 'Size must be a non-negative integer')\n }\n if (size > MAX_SIZE) {\n throw new InvalidInputError('NANOID_SIZE_TOO_LARGE', `Size must not exceed ${MAX_SIZE}`)\n }\n}\n\n// Overloads\nfunction nanoidFn(): string\nfunction nanoidFn(size: number): string\nfunction nanoidFn(options: NanoidOptions): string\nfunction nanoidFn(sizeOrOptions?: number | NanoidOptions): string {\n // ULTRA-FAST PATH: No arguments = default nanoid\n // Uses simple pooled random bytes (npm nanoid style) for best performance\n if (sizeOrOptions === undefined) {\n return defaultAlphabetFromPool(DEFAULT_SIZE)\n }\n\n let size = DEFAULT_SIZE\n let alphabet = URL_ALPHABET\n let randomBytes: Uint8Array | undefined\n\n if (typeof sizeOrOptions === 'number') {\n size = sizeOrOptions\n } else {\n size = sizeOrOptions.size ?? DEFAULT_SIZE\n alphabet = sizeOrOptions.alphabet ?? URL_ALPHABET\n randomBytes = sizeOrOptions.random\n if (sizeOrOptions.alphabet !== undefined) {\n validateAlphabet(alphabet)\n }\n }\n\n validateSize(size)\n\n if (size === 0) return ''\n\n if (alphabet === URL_ALPHABET && randomBytes === undefined) {\n return defaultAlphabetFromPool(size)\n }\n\n const alphabetLen = alphabet.length\n\n // FAST PATH: Power-of-2 alphabet (includes default 64-char)\n // No rejection needed - each byte maps directly to a character\n if ((alphabetLen & (alphabetLen - 1)) === 0) {\n const mask = alphabetLen - 1\n if (randomBytes && randomBytes.length < size) {\n throw new InvalidInputError(\n 'NANOID_RANDOM_BYTES_INSUFFICIENT',\n `Insufficient random bytes: need ${size}, have ${randomBytes.length}`,\n )\n }\n const bytes = randomBytes?.subarray(0, size) ?? globalThis.crypto.getRandomValues(new Uint8Array(size))\n let id = ''\n for (let i = 0; i < size; i++) {\n id += alphabet[bytes[i] & mask]\n }\n return id\n }\n\n // SLOW PATH: Rejection sampling for non-power-of-2 alphabets\n // Calculate mask: smallest power-of-2 minus 1 that covers alphabet size\n const mask = (2 << (31 - Math.clz32((alphabetLen - 1) | 1))) - 1\n // Calculate step: random bytes per batch (1.6x accounts for rejection)\n const step = Math.ceil((1.6 * mask * size) / alphabetLen)\n\n let id = ''\n let randomOffset = 0\n\n while (id.length < size) {\n let bytes: Uint8Array\n if (randomBytes) {\n if (randomBytes.length - randomOffset < step) {\n throw new InvalidInputError(\n 'NANOID_RANDOM_BYTES_INSUFFICIENT',\n `Insufficient random bytes: need at least ${step} more, have ${randomBytes.length - randomOffset}`,\n )\n }\n bytes = randomBytes.subarray(randomOffset, randomOffset + step)\n randomOffset += step\n } else {\n bytes = globalThis.crypto.getRandomValues(new Uint8Array(step))\n }\n\n for (let i = 0; i < bytes.length && id.length < size; i++) {\n const index = bytes[i] & mask\n if (index < alphabetLen) {\n id += alphabet[index]\n }\n // Otherwise reject and continue (no modulo bias)\n }\n }\n\n return id\n}\n\n/**\n * Validate a nanoid string against the default URL-safe alphabet.\n * Note: Does not validate IDs generated with custom alphabets.\n */\nfunction isValid(id: unknown): id is string {\n return typeof id === 'string' && id.length > 0 && NANOID_REGEX.test(id)\n}\n\n/**\n * Generate a URL-friendly unique string ID.\n *\n * Nanoid is a tiny, secure, URL-friendly unique string ID generator.\n * It uses a URL-safe alphabet (A-Za-z0-9_-) and generates 21-character\n * IDs by default with 126 bits of entropy.\n *\n * Unlike UUID v7 or ULID, nanoid is NOT time-ordered. Use it for:\n * - URL shorteners\n * - Session tokens\n * - Invite codes\n * - Any case where you need short, random IDs\n *\n * @example Basic usage\n * ```ts\n * import { nanoid } from 'uniku/nanoid'\n *\n * const id = nanoid()\n * // => \"V1StGXR8_Z5jdHi6B-myT\"\n * ```\n *\n * @example Custom size\n * ```ts\n * const shortId = nanoid(10)\n * // => \"IRFa-VaY2b\"\n * ```\n *\n * @example Custom alphabet (hex)\n * ```ts\n * const hexId = nanoid({ alphabet: '0123456789abcdef', size: 12 })\n * // => \"4f90d13a42bc\"\n * ```\n *\n * @example Validation\n * ```ts\n * const maybeId: unknown = getUserInput()\n * if (nanoid.isValid(maybeId)) {\n * // TypeScript knows maybeId is string\n * console.log(maybeId.length)\n * }\n * ```\n *\n * @throws {InvalidInputError} Size must be between 0 and 2048\n * @throws {InvalidInputError} Alphabet must contain 2-256 unique printable ASCII characters\n * @throws {InvalidInputError} Insufficient random bytes for requested size\n */\nexport const nanoid: Nanoid = Object.assign(nanoidFn, {\n isValid,\n})\n\nexport { InvalidInputError, UniqueIdError } from '../errors'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nanoid.mjs","names":[],"sources":["../../src/nanoid/nanoid.ts"],"sourcesContent":["import { InvalidInputError } from '../errors'\n\n/** Default URL-safe alphabet (64 characters): A-Z, a-z, 0-9, underscore, hyphen */\nexport const URL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'\n\nconst DEFAULT_SIZE = 21\nconst MAX_SIZE = 2048\nconst NANOID_REGEX = /^[A-Za-z0-9_-]+$/\n\n// Keep Nanoid's private pool for its default hot path. It consumes `size`\n// bytes directly without the shared helper's UUID/CUID2-oriented branches.\nconst POOL_SIZE_MULTIPLIER = 128\nconst MAX_POOL_SIZE = 65_536\nlet pool: Uint8Array | undefined\nlet poolOffset = 0\n\nfunction fillPool(bytes: number): void {\n const size = Math.min(bytes * POOL_SIZE_MULTIPLIER, MAX_POOL_SIZE)\n if (!pool || pool.length < size) {\n pool = new Uint8Array(size)\n crypto.getRandomValues(pool)\n poolOffset = 0\n } else if (poolOffset + bytes > pool.length) {\n crypto.getRandomValues(pool)\n poolOffset = 0\n }\n poolOffset += bytes\n}\n\nfunction defaultAlphabetFromPool(size: number): string {\n fillPool(size)\n let id = ''\n for (let i = poolOffset - size; i < poolOffset; i++) {\n id += URL_ALPHABET[pool![i] & 63]\n }\n return id\n}\n\nexport type NanoidOptions = {\n /**\n * Random bytes for deterministic output (testing).\n * For power-of-2 alphabets (2, 4, 8, 16, 32, 64, 128, 256): exactly `size` bytes needed.\n * For other alphabets: ~size * 2 bytes needed (rejection sampling).\n */\n random?: Uint8Array\n /**\n * Custom alphabet to use. Default: URL-safe A-Za-z0-9_-\n * Must be 2-256 printable ASCII characters (32-126) with no duplicates.\n */\n alphabet?: string\n /**\n * Length of generated ID. Default: 21. Maximum: 2048.\n */\n size?: number\n}\n\nexport type Nanoid = {\n /** Generate nanoid with default settings */\n (): string\n /** Generate nanoid with custom size */\n (size: number): string\n /** Generate nanoid with options */\n (options: NanoidOptions): string\n /**\n * Validate a nanoid string against the default URL-safe alphabet.\n * Note: Does not validate IDs generated with custom alphabets.\n */\n isValid(id: unknown): id is string\n}\n\n/**\n * Validate alphabet: 2-256 printable ASCII chars, no duplicates\n */\nfunction validateAlphabet(alphabet: string): void {\n if (alphabet.length < 2) {\n throw new InvalidInputError('NANOID_ALPHABET_TOO_SHORT', 'Alphabet must contain at least 2 characters')\n }\n if (alphabet.length > 256) {\n throw new InvalidInputError('NANOID_ALPHABET_TOO_LONG', 'Alphabet must not exceed 256 characters')\n }\n const seen = new Set<string>()\n for (const char of alphabet) {\n const code = char.charCodeAt(0)\n if (code < 32 || code > 126) {\n throw new InvalidInputError(\n 'NANOID_ALPHABET_INVALID_CHAR',\n 'Alphabet must contain only printable ASCII characters (32-126)',\n )\n }\n if (seen.has(char)) {\n throw new InvalidInputError('NANOID_ALPHABET_DUPLICATE', `Duplicate character in alphabet: \"${char}\"`)\n }\n seen.add(char)\n }\n}\n\n/**\n * Validate size parameter\n */\nfunction validateSize(size: number): void {\n if (!Number.isInteger(size) || size < 0) {\n throw new InvalidInputError('NANOID_SIZE_INVALID', 'Size must be a non-negative integer')\n }\n if (size > MAX_SIZE) {\n throw new InvalidInputError('NANOID_SIZE_TOO_LARGE', `Size must not exceed ${MAX_SIZE}`)\n }\n}\n\n// Overloads\nfunction nanoidFn(): string\nfunction nanoidFn(size: number): string\nfunction nanoidFn(options: NanoidOptions): string\nfunction nanoidFn(sizeOrOptions?: number | NanoidOptions): string {\n // ULTRA-FAST PATH: No arguments = default nanoid\n // Uses simple pooled random bytes (npm nanoid style) for best performance\n if (sizeOrOptions === undefined) {\n return defaultAlphabetFromPool(DEFAULT_SIZE)\n }\n\n let size = DEFAULT_SIZE\n let alphabet = URL_ALPHABET\n let randomBytes: Uint8Array | undefined\n\n if (typeof sizeOrOptions === 'number') {\n size = sizeOrOptions\n } else {\n size = sizeOrOptions.size ?? DEFAULT_SIZE\n alphabet = sizeOrOptions.alphabet ?? URL_ALPHABET\n randomBytes = sizeOrOptions.random\n if (sizeOrOptions.alphabet !== undefined) {\n validateAlphabet(alphabet)\n }\n }\n\n validateSize(size)\n\n if (size === 0) return ''\n\n if (alphabet === URL_ALPHABET && randomBytes === undefined) {\n return defaultAlphabetFromPool(size)\n }\n\n const alphabetLen = alphabet.length\n\n // FAST PATH: Power-of-2 alphabet (includes default 64-char)\n // No rejection needed - each byte maps directly to a character\n if ((alphabetLen & (alphabetLen - 1)) === 0) {\n const mask = alphabetLen - 1\n if (randomBytes && randomBytes.length < size) {\n throw new InvalidInputError(\n 'NANOID_RANDOM_BYTES_INSUFFICIENT',\n `Insufficient random bytes: need ${size}, have ${randomBytes.length}`,\n )\n }\n const bytes = randomBytes?.subarray(0, size) ?? globalThis.crypto.getRandomValues(new Uint8Array(size))\n let id = ''\n for (let i = 0; i < size; i++) {\n id += alphabet[bytes[i] & mask]\n }\n return id\n }\n\n // SLOW PATH: Rejection sampling for non-power-of-2 alphabets\n // Calculate mask: smallest power-of-2 minus 1 that covers alphabet size\n const mask = (2 << (31 - Math.clz32((alphabetLen - 1) | 1))) - 1\n // Calculate step: random bytes per batch (1.6x accounts for rejection)\n const step = Math.ceil((1.6 * mask * size) / alphabetLen)\n\n let id = ''\n let randomOffset = 0\n\n while (id.length < size) {\n let bytes: Uint8Array\n if (randomBytes) {\n if (randomBytes.length - randomOffset < step) {\n throw new InvalidInputError(\n 'NANOID_RANDOM_BYTES_INSUFFICIENT',\n `Insufficient random bytes: need at least ${step} more, have ${randomBytes.length - randomOffset}`,\n )\n }\n bytes = randomBytes.subarray(randomOffset, randomOffset + step)\n randomOffset += step\n } else {\n bytes = globalThis.crypto.getRandomValues(new Uint8Array(step))\n }\n\n for (let i = 0; i < bytes.length && id.length < size; i++) {\n const index = bytes[i] & mask\n if (index < alphabetLen) {\n id += alphabet[index]\n }\n // Otherwise reject and continue (no modulo bias)\n }\n }\n\n return id\n}\n\n/**\n * Validate a nanoid string against the default URL-safe alphabet.\n * Note: Does not validate IDs generated with custom alphabets.\n */\nfunction isValid(id: unknown): id is string {\n return typeof id === 'string' && id.length > 0 && NANOID_REGEX.test(id)\n}\n\n/**\n * Generate a URL-friendly unique string ID.\n *\n * Nanoid is a tiny, secure, URL-friendly unique string ID generator.\n * It uses a URL-safe alphabet (A-Za-z0-9_-) and generates 21-character\n * IDs by default with 126 bits of entropy.\n *\n * Unlike UUID v7 or ULID, nanoid is NOT time-ordered. Use it for:\n * - URL shorteners\n * - Session tokens\n * - Invite codes\n * - Any case where you need short, random IDs\n *\n * @example Basic usage\n * ```ts\n * import { nanoid } from 'uniku/nanoid'\n *\n * const id = nanoid()\n * // => \"V1StGXR8_Z5jdHi6B-myT\"\n * ```\n *\n * @example Custom size\n * ```ts\n * const shortId = nanoid(10)\n * // => \"IRFa-VaY2b\"\n * ```\n *\n * @example Custom alphabet (hex)\n * ```ts\n * const hexId = nanoid({ alphabet: '0123456789abcdef', size: 12 })\n * // => \"4f90d13a42bc\"\n * ```\n *\n * @example Validation\n * ```ts\n * const maybeId: unknown = getUserInput()\n * if (nanoid.isValid(maybeId)) {\n * // TypeScript knows maybeId is string\n * console.log(maybeId.length)\n * }\n * ```\n *\n * @throws {InvalidInputError} Size must be between 0 and 2048\n * @throws {InvalidInputError} Alphabet must contain 2-256 unique printable ASCII characters\n * @throws {InvalidInputError} Insufficient random bytes for requested size\n */\nexport const nanoid: Nanoid = Object.assign(nanoidFn, {\n isValid,\n})\n\nexport { InvalidInputError, UniqueIdError } from '../errors'\n"],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { i as UniqueIdError, n as InvalidInputError, r as ParseError, t as BufferError } from "../errors-Dgoyi-ci.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/objectid/objectid.d.ts
|
|
4
|
+
type ObjectIdOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* 5 bytes of random data to use for the ObjectID's random field.
|
|
7
|
+
*/
|
|
8
|
+
random?: Uint8Array;
|
|
9
|
+
/**
|
|
10
|
+
* Timestamp in seconds since Unix epoch.
|
|
11
|
+
* Defaults to Math.floor(Date.now() / 1000).
|
|
12
|
+
*/
|
|
13
|
+
secs?: number;
|
|
14
|
+
/**
|
|
15
|
+
* 24-bit counter value (0 to 0xFFFFFF).
|
|
16
|
+
*/
|
|
17
|
+
counter?: number;
|
|
18
|
+
};
|
|
19
|
+
type ObjectId = {
|
|
20
|
+
(): string;
|
|
21
|
+
<TBuf extends Uint8Array = Uint8Array>(options: ObjectIdOptions | undefined, buf: TBuf, offset?: number): TBuf;
|
|
22
|
+
(options?: ObjectIdOptions, buf?: undefined, offset?: number): string;
|
|
23
|
+
toBytes(id: string): Uint8Array;
|
|
24
|
+
fromBytes(bytes: Uint8Array): string;
|
|
25
|
+
timestamp(id: string): number;
|
|
26
|
+
isValid(id: unknown): id is string; /** The nil ObjectID (all zeros) */
|
|
27
|
+
NIL: string; /** The max ObjectID (all 0xff) */
|
|
28
|
+
MAX: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Generate a MongoDB ObjectID string or write the bytes into a buffer.
|
|
32
|
+
*
|
|
33
|
+
* ObjectID is a 12-byte identifier consisting of a 4-byte timestamp, a 5-byte
|
|
34
|
+
* per-process random value, and a 3-byte always-incrementing counter, encoded
|
|
35
|
+
* as a 24-character lowercase hex string. It is time-ordered and compatible
|
|
36
|
+
* with MongoDB's own ObjectID implementation.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { objectid } from 'uniku/objectid'
|
|
41
|
+
*
|
|
42
|
+
* const id = objectid()
|
|
43
|
+
* // => "667c3f2a1e2b3c4d5e6f7081"
|
|
44
|
+
*
|
|
45
|
+
* // Extract timestamp
|
|
46
|
+
* const ts = objectid.timestamp(id)
|
|
47
|
+
* console.log(new Date(ts))
|
|
48
|
+
*
|
|
49
|
+
* // Validate
|
|
50
|
+
* objectid.isValid(id) // true
|
|
51
|
+
*
|
|
52
|
+
* // Convert to/from bytes (12 bytes)
|
|
53
|
+
* const bytes = objectid.toBytes(id)
|
|
54
|
+
* const restored = objectid.fromBytes(bytes)
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
declare const objectid: ObjectId;
|
|
58
|
+
//#endregion
|
|
59
|
+
export { BufferError, InvalidInputError, ObjectId, ObjectIdOptions, ParseError, UniqueIdError, objectid };
|
|
60
|
+
//# sourceMappingURL=objectid.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"objectid.d.mts","names":[],"sources":["../../src/objectid/objectid.ts"],"mappings":";;;KA0BY,eAAA;;;AAAZ;EAIE,MAAA,GAAS,UAAA;;;;;EAKT,IAAA;;;AAIA;EAAA,OAAA;AAAA;AAAA,KAGU,QAAA;EAAA;EAAA,cAEI,UAAA,GAAa,UAAA,EAAY,OAAA,EAAS,eAAA,cAA6B,GAAA,EAAK,IAAA,EAAM,MAAA,YAAkB,IAAA;EAAA,CACzG,OAAA,GAAU,eAAA,EAAiB,GAAA,cAAiB,MAAA;EAC7C,OAAA,CAAQ,EAAA,WAAa,UAAA;EACrB,SAAA,CAAU,KAAA,EAAO,UAAA;EACjB,SAAA,CAAU,EAAA;EACV,OAAA,CAAQ,EAAA,YAAc,EAAA;EAEtB,GAAA,UAJiB;EAMjB,GAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AA2MF;cAAa,QAAA,EAAU,QAAA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{n as e,t}from"../random-Chp-Nkzi.mjs";import{BufferError as n,InvalidInputError as r,ParseError as i,UniqueIdError as a}from"../errors.mjs";import{n as o}from"../bytes-xqWxFYsM.mjs";const s=`0123456789abcdef`,c=Array.from({length:256},(e,t)=>s[t>>4&15]+s[t&15]),l=new Uint8Array(65536);l.fill(255);for(let e=0;e<=9;e+=1)l[48+e]=e;for(let e=0;e<6;e+=1)l[97+e]=10+e,l[65+e]=10+e;function u(e){if(e.length<12)throw new n(`OBJECTID_BYTES_TOO_SHORT`,`ObjectID bytes must be at least 12 bytes, got ${e.length}`);let t=``;for(let n=0;n<12;n+=1)t+=c[e[n]];return t}function d(e){if(e.length!==24)throw new i(`OBJECTID_INVALID_LENGTH`,`ObjectID string must be 24 characters, got ${e.length}`);let t=new Uint8Array(12);for(let n=0;n<12;n+=1){let r=n*2,a=r+1,o=l[e.charCodeAt(r)],s=l[e.charCodeAt(a)];if(o===255)throw new i(`OBJECTID_INVALID_CHAR`,`Invalid ObjectID character: ${e[r]}`);if(s===255)throw new i(`OBJECTID_INVALID_CHAR`,`Invalid ObjectID character: ${e[a]}`);t[n]=o<<4|s}return t}const f=4294967295,p=16777215,m=/^[0-9a-f]{24}$/i,h={random:void 0,counter:void 0};function g(){let e=new Uint8Array(5);return e.set(t(5)),e}function _(e,t,r,i,a=0){if(!i)i=new Uint8Array(12),a=0;else if(a<0||a+12>i.length)throw new n(`OBJECTID_BUFFER_OUT_OF_BOUNDS`,`ObjectID byte range ${a}:${a+12-1} is out of buffer bounds`);o(i,a,e);for(let e=0;e<5;e+=1)i[a+4+e]=t[e];return i[a+4+5]=r>>>16&255,i[a+4+5+1]=r>>>8&255,i[a+4+5+3-1]=r&255,i}function v(t,n,i=0){let a,o,s;if(t){let n=t.random;if(n&&n.length<5)throw new r(`OBJECTID_RANDOM_BYTES_TOO_SHORT`,`Random bytes length must be >= 5 for ObjectID`);let i=t.secs;if(i!==void 0&&(i<0||i>f))throw new r(`OBJECTID_TIMESTAMP_OUT_OF_RANGE`,`Timestamp must be between 0 and ${f}`);let c=t.counter;if(c!==void 0&&(c<0||c>p))throw new r(`OBJECTID_COUNTER_OUT_OF_RANGE`,`Counter must be between 0 and ${p}`);a=i??Math.floor(Date.now()/1e3),o=n??g(),s=c??e()&p}else h.random===void 0&&(h.random=g()),h.counter===void 0&&(h.counter=e()&p),a=Math.floor(Date.now()/1e3),o=h.random,h.counter=h.counter+1&p,s=h.counter;return n?(_(a,o,s,n,i),n):u(_(a,o,s))}function y(e){return d(e)}function b(e){return u(e)}function x(e){let t=d(e);return((t[0]<<24|t[1]<<16|t[2]<<8|t[3])>>>0)*1e3}function S(e){return typeof e==`string`&&m.test(e)}const C=Object.assign(v,{toBytes:y,fromBytes:b,timestamp:x,isValid:S,NIL:`0`.repeat(24),MAX:`f`.repeat(24)});export{n as BufferError,r as InvalidInputError,i as ParseError,a as UniqueIdError,C as objectid};
|
|
2
|
+
//# sourceMappingURL=objectid.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"objectid.mjs","names":["OBJECTID_BYTES"],"sources":["../../src/objectid/hex.ts","../../src/objectid/objectid.ts"],"sourcesContent":["import { BufferError, ParseError } from '../errors'\n\n/**\n * Hex encoding/decoding for ObjectID.\n * Alphabet: 0-9a-f (lowercase on encode, case-insensitive on decode)\n *\n * ObjectID binary format: 12 bytes (96 bits)\n * - 4 bytes: big-endian Unix timestamp in seconds\n * - 5 bytes: per-process random value\n * - 3 bytes: big-endian counter\n *\n * String format: 24 characters of hex\n */\n\nconst HEX_CHARS = '0123456789abcdef'\nconst OBJECTID_BYTES = 12\nconst OBJECTID_STRING_LEN = 24\n\n// Pre-computed encode table: byte value (0-255) -> 2-char lowercase hex string.\nconst ENCODE_TABLE: string[] = Array.from(\n { length: 256 },\n (_, byte) => HEX_CHARS[(byte >> 4) & 0xf] + HEX_CHARS[byte & 0xf],\n)\n\n// Pre-computed decode table covering every UTF-16 code unit charCodeAt can\n// return, so lookups never go out of bounds and a single `=== 255` check\n// rejects invalid input (including non-ASCII) without a per-character range\n// check. Costs 128 KiB once at module load; valid inputs only touch the first\n// 128 bytes, so cache behavior is unaffected.\n// Note: unlike Base62, hex decoding is case-insensitive - 'a' and 'A' both decode to 10.\nconst DECODING = new Uint8Array(65536)\nDECODING.fill(255) // 255 = invalid marker\n\nfor (let i = 0; i <= 9; i += 1) {\n DECODING['0'.charCodeAt(0) + i] = i\n}\nfor (let i = 0; i < 6; i += 1) {\n DECODING['a'.charCodeAt(0) + i] = 10 + i\n DECODING['A'.charCodeAt(0) + i] = 10 + i\n}\n\n/**\n * Encode 12 ObjectID bytes to a 24-character lowercase hex string.\n */\nexport function encodeObjectIdHex(bytes: Uint8Array): string {\n if (bytes.length < OBJECTID_BYTES) {\n throw new BufferError(\n 'OBJECTID_BYTES_TOO_SHORT',\n `ObjectID bytes must be at least ${OBJECTID_BYTES} bytes, got ${bytes.length}`,\n )\n }\n\n let encoded = ''\n for (let i = 0; i < OBJECTID_BYTES; i += 1) {\n encoded += ENCODE_TABLE[bytes[i]]\n }\n return encoded\n}\n\n/**\n * Decode a 24-character hex string to 12 ObjectID bytes.\n */\nexport function decodeObjectIdHex(str: string): Uint8Array {\n if (str.length !== OBJECTID_STRING_LEN) {\n throw new ParseError(\n 'OBJECTID_INVALID_LENGTH',\n `ObjectID string must be ${OBJECTID_STRING_LEN} characters, got ${str.length}`,\n )\n }\n\n const bytes = new Uint8Array(OBJECTID_BYTES)\n for (let i = 0; i < OBJECTID_BYTES; i += 1) {\n const hiIndex = i * 2\n const loIndex = hiIndex + 1\n const hi = DECODING[str.charCodeAt(hiIndex)]\n const lo = DECODING[str.charCodeAt(loIndex)]\n\n if (hi === 255) {\n throw new ParseError('OBJECTID_INVALID_CHAR', `Invalid ObjectID character: ${str[hiIndex]}`)\n }\n if (lo === 255) {\n throw new ParseError('OBJECTID_INVALID_CHAR', `Invalid ObjectID character: ${str[loIndex]}`)\n }\n\n bytes[i] = (hi << 4) | lo\n }\n\n return bytes\n}\n","import { writeTimestamp32 } from '../common/bytes'\nimport { randomBytes, randomUint32 } from '../common/random'\nimport { BufferError, InvalidInputError } from '../errors'\nimport { decodeObjectIdHex, encodeObjectIdHex } from './hex'\n\n/**\n * MongoDB ObjectID\n *\n * A 96-bit (12-byte) identifier consisting of:\n * - 4 bytes: big-endian Unix timestamp in seconds\n * - 5 bytes: per-process random value\n * - 3 bytes: big-endian counter, always incrementing (wraps at 0xFFFFFF back to 0)\n *\n * Encoded as a 24-character lowercase hex string.\n */\n\nconst OBJECTID_BYTES = 12\nconst TIMESTAMP_BYTES = 4\nconst RANDOM_BYTES = 5\nconst COUNTER_BYTES = 3\nconst MAX_SECS = 0xffffffff\nconst MAX_COUNTER = 0xffffff\n\n// Validation regex: case-insensitive per KTD7, matching bson's ObjectId.isValid()\nconst OBJECTID_REGEX = /^[0-9a-f]{24}$/i\n\nexport type ObjectIdOptions = {\n /**\n * 5 bytes of random data to use for the ObjectID's random field.\n */\n random?: Uint8Array\n /**\n * Timestamp in seconds since Unix epoch.\n * Defaults to Math.floor(Date.now() / 1000).\n */\n secs?: number\n /**\n * 24-bit counter value (0 to 0xFFFFFF).\n */\n counter?: number\n}\n\nexport type ObjectId = {\n (): string\n <TBuf extends Uint8Array = Uint8Array>(options: ObjectIdOptions | undefined, buf: TBuf, offset?: number): TBuf\n (options?: ObjectIdOptions, buf?: undefined, offset?: number): string\n toBytes(id: string): Uint8Array\n fromBytes(bytes: Uint8Array): string\n timestamp(id: string): number\n isValid(id: unknown): id is string\n /** The nil ObjectID (all zeros) */\n NIL: string\n /** The max ObjectID (all 0xff) */\n MAX: string\n}\n\ntype ObjectIdState = {\n random: Uint8Array | undefined\n counter: number | undefined\n}\n\n/**\n * Module-level state for the per-process random value and the always-incrementing counter.\n *\n * IMPORTANT: This state persists across all objectid() calls in the module's lifetime.\n * - In serverless/edge functions with warm starts, state persists between invocations.\n * Unlike ULID/UUIDv7's per-timestamp sequence reset, the counter continuing to climb\n * across warm starts is exactly the anti-collision behavior the ObjectID spec intends.\n * - For isolated state, pass explicit `random`, `secs`, or `counter` via options.\n * - Tests should mock Date.now() or provide explicit options for deterministic behavior.\n */\nconst state: ObjectIdState = {\n random: undefined,\n counter: undefined,\n}\n\n/**\n * Draw 5 fresh random bytes into a newly-owned buffer, copying immediately so the\n * result is unaffected by any later pool refill triggered by other random draws\n * (e.g. the counter's `randomUint32()` call) before these bytes are consumed.\n */\nfunction freshRandom(): Uint8Array {\n const bytes = new Uint8Array(RANDOM_BYTES)\n bytes.set(randomBytes(RANDOM_BYTES))\n return bytes\n}\n\nfunction objectIdBytes(secs: number, random: Uint8Array, counter: number, buf?: Uint8Array, offset = 0): Uint8Array {\n if (!buf) {\n buf = new Uint8Array(OBJECTID_BYTES)\n offset = 0\n } else if (offset < 0 || offset + OBJECTID_BYTES > buf.length) {\n throw new BufferError(\n 'OBJECTID_BUFFER_OUT_OF_BOUNDS',\n `ObjectID byte range ${offset}:${offset + OBJECTID_BYTES - 1} is out of buffer bounds`,\n )\n }\n\n // Timestamp (32-bit big-endian seconds since Unix epoch) -> bytes 0-3\n writeTimestamp32(buf, offset, secs)\n\n // Random (5 bytes) -> bytes 4-8\n for (let i = 0; i < RANDOM_BYTES; i += 1) {\n buf[offset + TIMESTAMP_BYTES + i] = random[i]\n }\n\n // Counter (24-bit big-endian) -> bytes 9-11\n buf[offset + TIMESTAMP_BYTES + RANDOM_BYTES] = (counter >>> 16) & 0xff\n buf[offset + TIMESTAMP_BYTES + RANDOM_BYTES + 1] = (counter >>> 8) & 0xff\n buf[offset + TIMESTAMP_BYTES + RANDOM_BYTES + COUNTER_BYTES - 1] = counter & 0xff\n\n return buf\n}\n\n/*\n * Overload: no buffer => return an ObjectID string.\n */\nfunction objectIdFn(options?: ObjectIdOptions, buf?: undefined, offset?: number): string\n/*\n * Overload: caller provides a buffer slice to fill with ObjectID bytes.\n */\nfunction objectIdFn<TBuf extends Uint8Array = Uint8Array>(\n options: ObjectIdOptions | undefined,\n buf: TBuf,\n offset?: number,\n): TBuf\nfunction objectIdFn<TBuf extends Uint8Array = Uint8Array>(\n options?: ObjectIdOptions,\n buf?: TBuf,\n offset = 0,\n): string | TBuf {\n let secs: number\n let random: Uint8Array\n let counter: number\n\n if (options) {\n const optRandom = options.random\n if (optRandom && optRandom.length < RANDOM_BYTES) {\n throw new InvalidInputError(\n 'OBJECTID_RANDOM_BYTES_TOO_SHORT',\n `Random bytes length must be >= ${RANDOM_BYTES} for ObjectID`,\n )\n }\n\n const optSecs = options.secs\n if (optSecs !== undefined && (optSecs < 0 || optSecs > MAX_SECS)) {\n throw new InvalidInputError('OBJECTID_TIMESTAMP_OUT_OF_RANGE', `Timestamp must be between 0 and ${MAX_SECS}`)\n }\n\n const optCounter = options.counter\n if (optCounter !== undefined && (optCounter < 0 || optCounter > MAX_COUNTER)) {\n throw new InvalidInputError('OBJECTID_COUNTER_OUT_OF_RANGE', `Counter must be between 0 and ${MAX_COUNTER}`)\n }\n\n // Options bypass persistent state entirely: every field is sourced fresh (given\n // value, or an independently-defaulted one), never read from or written to\n // `state` (see KTD2). This is what makes options-based generation deterministic\n // when all three fields are supplied.\n secs = optSecs ?? Math.floor(Date.now() / 1000)\n random = optRandom ?? freshRandom()\n counter = optCounter ?? randomUint32() & MAX_COUNTER\n } else {\n // Lazily initialize persistent state on first no-option call.\n if (state.random === undefined) {\n state.random = freshRandom()\n }\n if (state.counter === undefined) {\n state.counter = randomUint32() & MAX_COUNTER\n }\n\n /**\n * Note: by default, Cloudflare Workers \"freezes\" time during request handling to prevent\n * side-channel attacks. This means that Date.now() will return the same value for the entire\n * duration of a request.\n * Implications:\n * - all ObjectIDs generated within a single request will share the same timestamp.\n * - monotonic ordering relies entirely on the ever-incrementing counter.\n */\n secs = Math.floor(Date.now() / 1000)\n random = state.random\n state.counter = (state.counter + 1) & MAX_COUNTER\n counter = state.counter\n }\n\n if (buf) {\n objectIdBytes(secs, random, counter, buf, offset)\n return buf\n }\n\n const bytes = objectIdBytes(secs, random, counter)\n return encodeObjectIdHex(bytes)\n}\n\n/**\n * Convert an ObjectID hex string to 12 bytes.\n */\nfunction toBytes(id: string): Uint8Array {\n return decodeObjectIdHex(id)\n}\n\n/**\n * Convert 12 bytes to an ObjectID hex string.\n */\nfunction fromBytes(bytes: Uint8Array): string {\n return encodeObjectIdHex(bytes)\n}\n\n/**\n * Extract the timestamp from an ObjectID string.\n * Returns Unix timestamp in milliseconds for API consistency with ulid/uuidv7/ksuid.\n * Note: ObjectID only has second precision, so the returned value will always end in 000.\n */\nfunction timestamp(id: string): number {\n const bytes = decodeObjectIdHex(id)\n // First 4 bytes are big-endian timestamp (seconds since Unix epoch)\n const secs = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0\n return secs * 1000\n}\n\n/**\n * Validate an ObjectID string format.\n * Case-insensitive (KTD7), matching bson's ObjectId.isValid() and this repo's existing\n * convention (ulid, ksuid) of accepting mixed-case input for parsing, even though\n * generation always emits lowercase (matching MongoDB driver output).\n */\nfunction isValid(id: unknown): id is string {\n return typeof id === 'string' && OBJECTID_REGEX.test(id)\n}\n\n/**\n * Generate a MongoDB ObjectID string or write the bytes into a buffer.\n *\n * ObjectID is a 12-byte identifier consisting of a 4-byte timestamp, a 5-byte\n * per-process random value, and a 3-byte always-incrementing counter, encoded\n * as a 24-character lowercase hex string. It is time-ordered and compatible\n * with MongoDB's own ObjectID implementation.\n *\n * @example\n * ```ts\n * import { objectid } from 'uniku/objectid'\n *\n * const id = objectid()\n * // => \"667c3f2a1e2b3c4d5e6f7081\"\n *\n * // Extract timestamp\n * const ts = objectid.timestamp(id)\n * console.log(new Date(ts))\n *\n * // Validate\n * objectid.isValid(id) // true\n *\n * // Convert to/from bytes (12 bytes)\n * const bytes = objectid.toBytes(id)\n * const restored = objectid.fromBytes(bytes)\n * ```\n */\nexport const objectid: ObjectId = Object.assign(objectIdFn, {\n toBytes,\n fromBytes,\n timestamp,\n isValid,\n NIL: '0'.repeat(24),\n MAX: 'f'.repeat(24),\n})\n\nexport { BufferError, InvalidInputError, ParseError, UniqueIdError } from '../errors'\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=globalThis.crypto.getRandomValues.bind(globalThis.crypto),t=new Uint8Array(256);let n=256;function r(){e(t),n=0}function i(i){if(i>256)return e(new Uint8Array(i));n+i>256&&r();let a=n;return n+=i,t.subarray(a,n)}function a(){n>252&&r();let e=t[n]*16777216+t[n+1]*65536+t[n+2]*256+t[n+3]>>>0;return n+=4,e}function o(){n>240&&r();let e=n;return n+=16,t.subarray(e,n)}export{a as n,o as r,i as t};
|
|
2
|
+
//# sourceMappingURL=random-Chp-Nkzi.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"random-
|
|
1
|
+
{"version":3,"file":"random-Chp-Nkzi.mjs","names":[],"sources":["../src/common/random.ts"],"sourcesContent":["const randomFill = /*@__PURE__*/ globalThis.crypto.getRandomValues.bind(globalThis.crypto)\n\n/**\n * Simple random byte pool for ID generation.\n *\n * Module-level state is per JavaScript isolate, so SharedArrayBuffer and Atomics\n * only add overhead here. Returned views are consumed synchronously by callers.\n */\n\nconst POOL_SIZE = 256\nconst pool = new Uint8Array(POOL_SIZE)\nlet poolOffset = POOL_SIZE\n\nfunction refillPool(): void {\n randomFill(pool)\n poolOffset = 0\n}\n\n/**\n * Return a view of `count` random bytes from the pool.\n */\nexport function randomBytes(count: number): Uint8Array {\n if (count > POOL_SIZE) {\n return randomFill(new Uint8Array(count))\n }\n\n if (poolOffset + count > POOL_SIZE) {\n refillPool()\n }\n\n const start = poolOffset\n poolOffset += count\n return pool.subarray(start, poolOffset)\n}\n\n/**\n * Generate a random unsigned 32-bit integer from the shared pool.\n */\nexport function randomUint32(): number {\n if (poolOffset > POOL_SIZE - 4) {\n refillPool()\n }\n\n const value =\n (pool[poolOffset] * 0x1000000 +\n pool[poolOffset + 1] * 0x10000 +\n pool[poolOffset + 2] * 0x100 +\n pool[poolOffset + 3]) >>>\n 0\n poolOffset += 4\n return value\n}\n\n/**\n * Generate 16 bytes of cryptographically strong random data.\n * Uses a pre-filled pool to minimize crypto API calls.\n */\nexport function rng(): Uint8Array {\n if (poolOffset > POOL_SIZE - 16) {\n refillPool()\n }\n\n const start = poolOffset\n poolOffset += 16\n return pool.subarray(start, poolOffset)\n}\n"],"mappings":"AAAA,MAAM,EAA2B,WAAW,OAAO,gBAAgB,KAAK,WAAW,MAAM,EAUnF,EAAO,IAAI,WAAW,GAAS,EACrC,IAAI,EAAa,IAEjB,SAAS,GAAmB,CAC1B,EAAW,CAAI,EACf,EAAa,CACf,CAKA,SAAgB,EAAY,EAA2B,CACrD,GAAI,EAAQ,IACV,OAAO,EAAW,IAAI,WAAW,CAAK,CAAC,EAGrC,EAAa,EAAQ,KACvB,EAAW,EAGb,IAAM,EAAQ,EAEd,MADA,IAAc,EACP,EAAK,SAAS,EAAO,CAAU,CACxC,CAKA,SAAgB,GAAuB,CACjC,EAAa,KACf,EAAW,EAGb,IAAM,EACH,EAAK,GAAc,SAClB,EAAK,EAAa,GAAK,MACvB,EAAK,EAAa,GAAK,IACvB,EAAK,EAAa,KACpB,EAEF,MADA,IAAc,EACP,CACT,CAMA,SAAgB,GAAkB,CAC5B,EAAa,KACf,EAAW,EAGb,IAAM,EAAQ,EAEd,MADA,IAAc,GACP,EAAK,SAAS,EAAO,CAAU,CACxC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { i as UniqueIdError, n as InvalidInputError, r as ParseError, t as BufferError } from "../errors-Dgoyi-ci.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/tsid/tsid.d.ts
|
|
4
|
+
type TsidOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* Timestamp in milliseconds since Unix epoch.
|
|
7
|
+
* Defaults to Date.now().
|
|
8
|
+
*/
|
|
9
|
+
msecs?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Custom epoch in milliseconds since Unix epoch.
|
|
12
|
+
* Defaults to 1577836800000 (2020-01-01T00:00:00.000Z).
|
|
13
|
+
*/
|
|
14
|
+
epoch?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Node ID (0 to 2^nodeBits - 1).
|
|
17
|
+
* Defaults to a lazily-initialized, persistent random value.
|
|
18
|
+
*/
|
|
19
|
+
node?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Number of bits allocated to the node ID (0-20).
|
|
22
|
+
* The remaining bits (22 - nodeBits) are allocated to the counter.
|
|
23
|
+
* Defaults to 10.
|
|
24
|
+
*/
|
|
25
|
+
nodeBits?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Per-millisecond counter (0 to 2^(22 - nodeBits) - 1).
|
|
28
|
+
* Defaults to a fresh random value on a new millisecond, or the previous
|
|
29
|
+
* value incremented by 1 within the same millisecond.
|
|
30
|
+
*/
|
|
31
|
+
counter?: number;
|
|
32
|
+
};
|
|
33
|
+
type Tsid = {
|
|
34
|
+
(): bigint;
|
|
35
|
+
<TBuf extends Uint8Array = Uint8Array>(options: TsidOptions | undefined, buf: TBuf, offset?: number): TBuf;
|
|
36
|
+
(options?: TsidOptions, buf?: undefined, offset?: number): bigint;
|
|
37
|
+
toBytes(id: bigint): Uint8Array;
|
|
38
|
+
fromBytes(bytes: Uint8Array): bigint;
|
|
39
|
+
toString(id: bigint): string;
|
|
40
|
+
fromString(str: string): bigint;
|
|
41
|
+
timestamp(id: bigint, epoch?: number): number;
|
|
42
|
+
isValid(id: unknown): id is bigint; /** The nil TSID (all zeros) */
|
|
43
|
+
NIL: bigint; /** The max TSID (maximum valid 64-bit value) */
|
|
44
|
+
MAX: bigint;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Generate a TSID bigint or write the bytes into a buffer.
|
|
48
|
+
*
|
|
49
|
+
* TSID (Time-Sorted Unique Identifier) is a 64-bit Snowflake-style identifier
|
|
50
|
+
* combining a millisecond timestamp, a node ID, and a per-millisecond counter.
|
|
51
|
+
* Unlike every other uniku generator, it returns a `bigint` by default, since
|
|
52
|
+
* TSID's entire value proposition is native numeric storage (e.g. a database
|
|
53
|
+
* BIGINT primary key).
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* import { tsid } from 'uniku/tsid'
|
|
58
|
+
*
|
|
59
|
+
* const id = tsid()
|
|
60
|
+
* // => 862301223059968074n
|
|
61
|
+
*
|
|
62
|
+
* // Canonical string form
|
|
63
|
+
* const str = tsid.toString(id)
|
|
64
|
+
* // => "0QXW2CK4XZM2A"
|
|
65
|
+
* tsid.fromString(str) === id // true
|
|
66
|
+
*
|
|
67
|
+
* // Extract timestamp
|
|
68
|
+
* const ts = tsid.timestamp(id)
|
|
69
|
+
* console.log(new Date(ts))
|
|
70
|
+
*
|
|
71
|
+
* // Validate
|
|
72
|
+
* tsid.isValid(id) // true
|
|
73
|
+
*
|
|
74
|
+
* // Convert to/from bytes (8 bytes)
|
|
75
|
+
* const bytes = tsid.toBytes(id)
|
|
76
|
+
* const restored = tsid.fromBytes(bytes)
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
declare const tsid: Tsid;
|
|
80
|
+
//#endregion
|
|
81
|
+
export { BufferError, InvalidInputError, ParseError, Tsid, TsidOptions, UniqueIdError, tsid };
|
|
82
|
+
//# sourceMappingURL=tsid.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tsid.d.mts","names":[],"sources":["../../src/tsid/tsid.ts"],"mappings":";;;KAgCY,WAAA;;;AAAZ;;EAKE,KAAA;EALU;;;;EAUV,KAAA;;;AAiBA;AAGF;EAfE,IAAA;;;;;;EAMA,QAAA;;;;;;EAMA,OAAA;AAAA;AAAA,KAGU,IAAA;EAAA;EAAA,cAEI,UAAA,GAAa,UAAA,EAAY,OAAA,EAAS,WAAA,cAAyB,GAAA,EAAK,IAAA,EAAM,MAAA,YAAkB,IAAA;EAAA,CACrG,OAAA,GAAU,WAAA,EAAa,GAAA,cAAiB,MAAA;EACzC,OAAA,CAAQ,EAAA,WAAa,UAAA;EACrB,SAAA,CAAU,KAAA,EAAO,UAAA;EACjB,QAAA,CAAS,EAAA;EACT,UAAA,CAAW,GAAA;EACX,SAAA,CAAU,EAAA,UAAY,KAAA;EACtB,OAAA,CAAQ,EAAA,YAAc,EAAA;EAEtB,GAAA;EAEA,GAAA;AAAA;;;;;;;;;;;;;;;;AAAA;AA+NF;;;;AAAmB;;;;;;;;;;;;;cAAN,IAAA,EAAM,IAAA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{n as e}from"../random-Chp-Nkzi.mjs";import{BufferError as t,InvalidInputError as n,ParseError as r,UniqueIdError as i}from"../errors.mjs";const a=`0123456789ABCDEFGHJKMNPQRSTVWXYZ`,o=new Uint8Array(128).fill(255);for(let e=0;e<32;e+=1){let t=a[e],n=a.charCodeAt(e);o[n]=e,t>=`A`&&t<=`Z`&&(o[n+32]=e)}function s(e){let t=``;for(let n=0;n<13;n+=1){let r=BigInt(60-n*5),i=Number(e>>r&31n);t+=a[i]}return t}function c(e){if(e.length!==13)throw new r(`TSID_INVALID_LENGTH`,`TSID string must be 13 characters, got ${e.length}`);let t=0n;for(let n=0;n<13;n+=1){let i=e.charCodeAt(n),a=i<128?o[i]:255;if(a===255)throw new r(`TSID_INVALID_CHAR`,`Invalid TSID character: ${e[n]}`);if(n===0&&a>15)throw new r(`TSID_LEADING_CHAR_OUT_OF_RANGE`,`TSID leading character must be one of 0-9, A-F, got: ${e[n]}`);t=t<<5n|BigInt(a)}return t}const l=15778368e5,u=4095,d=(1n<<42n)-1n,f={node:void 0,msecs:-1/0,counter:0};function p(e,t,n,r){return e<<BigInt(22)|BigInt(t)<<BigInt(n)|BigInt(r)}function m(e,n,r=0){if(!n)n=new Uint8Array(8),r=0;else if(r<0||r+8>n.length)throw new t(`TSID_BUFFER_OUT_OF_BOUNDS`,`TSID byte range ${r}:${r+8-1} is out of buffer bounds`);for(let t=0;t<8;t+=1)n[r+t]=Number(e>>BigInt((7-t)*8)&255n);return n}function h(t,r,i=0){let a,o,s,c;if(t){let r=t.nodeBits??10;if(r<0||r>20)throw new n(`TSID_NODE_BITS_OUT_OF_RANGE`,`nodeBits must be between 0 and 20`);s=22-r;let i=(1<<r)-1,u=(1<<s)-1,f=t.node;if(f!==void 0){if(f<0||f>i)throw new n(`TSID_NODE_OUT_OF_RANGE`,`node must be between 0 and ${i}`);o=f}else o=e()&i;let p=t.counter;if(p!==void 0){if(p<0||p>u)throw new n(`TSID_COUNTER_OUT_OF_RANGE`,`counter must be between 0 and ${u}`);c=p}else c=e()&u;let m=t.epoch??l,h=t.msecs??Date.now(),g=BigInt(h)-BigInt(m);if(g<0n||g>d)throw new n(`TSID_TIMESTAMP_OUT_OF_RANGE`,`msecs - epoch must be between 0 and ${d}`);a=g}else{f.node===void 0&&(f.node=e()&1023),o=f.node,s=12;let t=Date.now();t>f.msecs?(f.msecs=t,f.counter=e()&u):(f.counter+=1,f.counter>u&&(f.msecs+=1,f.counter=0)),c=f.counter,a=BigInt(f.msecs)-BigInt(l)}let h=p(a,o,s,c);return r?(m(h,r,i),r):h}function g(e){return m(e)}function _(e){if(e.length!==8)throw new t(`TSID_BYTES_INVALID_LENGTH`,`TSID bytes must be exactly 8 bytes, got ${e.length}`);let n=0n;for(let t=0;t<8;t+=1)n=n<<8n|BigInt(e[t]);return n}function v(e,t=l){return t+Number(e>>BigInt(22))}function y(e){return typeof e==`bigint`&&e>=0n&&e<=b}const b=(1n<<64n)-1n,x=Object.assign(h,{toBytes:g,fromBytes:_,toString:s,fromString:c,timestamp:v,isValid:y,NIL:0n,MAX:b});export{t as BufferError,n as InvalidInputError,r as ParseError,i as UniqueIdError,x as tsid};
|
|
2
|
+
//# sourceMappingURL=tsid.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tsid.mjs","names":[],"sources":["../../src/tsid/crockford64.ts","../../src/tsid/tsid.ts"],"sourcesContent":["import { ParseError } from '../errors'\n\n/**\n * Crockford Base32 codec for TSID, operating directly on the primary `bigint`\n * value rather than an intermediate byte array (unlike ulid/crockford.ts) -\n * tsid's primary type never routes through a `Uint8Array` during string\n * conversion.\n *\n * TSID binary format: 64 bits (8 bytes), encoded as 13 Crockford Base32 characters.\n * 13 chars * 5 bits = 65 bits of capacity for a 64-bit value, leaving exactly 1 bit\n * of headroom on the leading character. That headroom bit is always 0, so the\n * leading character only carries 4 significant real bits (values 0-15, i.e. the\n * alphabet's first 16 symbols \"0\"-\"9\"/\"A\"-\"F\") rather than the full 32-symbol\n * range - the same overflow-prevention technique ULID uses for its own leading\n * `[0-7]` restriction (2 headroom bits there vs 1 here). Confirmed against\n * `tsid-ts`'s own `toCanonicalString`: `(number >> BigInt(60 - i * 5)) & 0x1f`.\n */\n\nconst TSID_ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'\nconst TSID_STRING_LEN = 13\nconst TSID_LEADING_MAX = 15 // 4 significant bits on the leading character (see above)\n\n// Pre-computed decode table (case-insensitive) covering ASCII, so lookups never\n// go out of bounds and a single `=== 255` check rejects invalid input.\nconst DECODING = new Uint8Array(128).fill(255)\nfor (let i = 0; i < TSID_ALPHABET.length; i += 1) {\n const char = TSID_ALPHABET[i]\n const upperCode = TSID_ALPHABET.charCodeAt(i)\n DECODING[upperCode] = i\n // Only letters have a lowercase counterpart - applying +32 to digit codes\n // would land on unrelated uppercase letters (e.g. '5' + 32 -> 'U', which is\n // excluded from this alphabet and would never get overwritten back to 255).\n if (char >= 'A' && char <= 'Z') {\n DECODING[upperCode + 32] = i\n }\n}\n\n/**\n * Encode a TSID `bigint` value to its 13-character canonical Crockford Base32 string.\n */\nexport function encodeTsidString(value: bigint): string {\n let result = ''\n for (let i = 0; i < TSID_STRING_LEN; i += 1) {\n const shift = BigInt(60 - i * 5)\n const group = Number((value >> shift) & 0x1fn)\n result += TSID_ALPHABET[group]\n }\n return result\n}\n\n/**\n * Decode a 13-character canonical Crockford Base32 string to a TSID `bigint` value.\n */\nexport function decodeTsidString(str: string): bigint {\n if (str.length !== TSID_STRING_LEN) {\n throw new ParseError('TSID_INVALID_LENGTH', `TSID string must be ${TSID_STRING_LEN} characters, got ${str.length}`)\n }\n\n let value = 0n\n for (let i = 0; i < TSID_STRING_LEN; i += 1) {\n const code = str.charCodeAt(i)\n const group = code < 128 ? DECODING[code] : 255\n if (group === 255) {\n throw new ParseError('TSID_INVALID_CHAR', `Invalid TSID character: ${str[i]}`)\n }\n if (i === 0 && group > TSID_LEADING_MAX) {\n throw new ParseError(\n 'TSID_LEADING_CHAR_OUT_OF_RANGE',\n `TSID leading character must be one of 0-9, A-F, got: ${str[i]}`,\n )\n }\n value = (value << 5n) | BigInt(group)\n }\n return value\n}\n","import { randomUint32 } from '../common/random'\nimport { BufferError, InvalidInputError } from '../errors'\nimport { decodeTsidString, encodeTsidString } from './crockford64'\n\n/**\n * TSID (Time-Sorted Unique Identifier)\n *\n * A 64-bit Snowflake-style identifier consisting of:\n * - 42 bits: millisecond timestamp relative to a custom epoch (default 2020-01-01T00:00:00.000Z)\n * - 10 bits: node ID (default; configurable via `nodeBits`)\n * - 12 bits: per-millisecond counter (default; `22 - nodeBits`)\n *\n * Unlike every other uniku generator, `tsid()` returns a `bigint` by default -\n * this is a deliberate design decision reflecting that TSID's value proposition\n * is native numeric storage (e.g. a database BIGINT primary key), not a\n * string-first identifier that happens to have a byte encoding. `toString`/\n * `fromString` are the boundary conversions to/from the 13-character canonical\n * Crockford Base32 string.\n *\n * Encoded as 8 big-endian bytes in buffer mode.\n */\n\nconst TSID_EPOCH = 1577836800000 // 2020-01-01T00:00:00.000Z\nconst TSID_BYTES = 8\nconst RANDOM_BITS = 22 // combined node + counter bits\nconst DEFAULT_NODE_BITS = 10\nconst DEFAULT_COUNTER_BITS = RANDOM_BITS - DEFAULT_NODE_BITS\nconst DEFAULT_NODE_MASK = (1 << DEFAULT_NODE_BITS) - 1\nconst DEFAULT_COUNTER_MASK = (1 << DEFAULT_COUNTER_BITS) - 1\nconst MAX_NODE_BITS = 20\nconst MAX_TIMESTAMP_DIFF = (1n << 42n) - 1n\n\nexport type TsidOptions = {\n /**\n * Timestamp in milliseconds since Unix epoch.\n * Defaults to Date.now().\n */\n msecs?: number\n /**\n * Custom epoch in milliseconds since Unix epoch.\n * Defaults to 1577836800000 (2020-01-01T00:00:00.000Z).\n */\n epoch?: number\n /**\n * Node ID (0 to 2^nodeBits - 1).\n * Defaults to a lazily-initialized, persistent random value.\n */\n node?: number\n /**\n * Number of bits allocated to the node ID (0-20).\n * The remaining bits (22 - nodeBits) are allocated to the counter.\n * Defaults to 10.\n */\n nodeBits?: number\n /**\n * Per-millisecond counter (0 to 2^(22 - nodeBits) - 1).\n * Defaults to a fresh random value on a new millisecond, or the previous\n * value incremented by 1 within the same millisecond.\n */\n counter?: number\n}\n\nexport type Tsid = {\n (): bigint\n <TBuf extends Uint8Array = Uint8Array>(options: TsidOptions | undefined, buf: TBuf, offset?: number): TBuf\n (options?: TsidOptions, buf?: undefined, offset?: number): bigint\n toBytes(id: bigint): Uint8Array\n fromBytes(bytes: Uint8Array): bigint\n toString(id: bigint): string\n fromString(str: string): bigint\n timestamp(id: bigint, epoch?: number): number\n isValid(id: unknown): id is bigint\n /** The nil TSID (all zeros) */\n NIL: bigint\n /** The max TSID (maximum valid 64-bit value) */\n MAX: bigint\n}\n\ntype TsidState = {\n node: number | undefined\n msecs: number\n counter: number\n}\n\n/**\n * Module-level state for the lazily-initialized persistent node ID and the\n * per-millisecond-reset counter.\n *\n * IMPORTANT: This state persists across all tsid() calls in the module's lifetime.\n * - In serverless/edge functions with warm starts, state persists between invocations.\n * - For isolated state, pass explicit `msecs`, `epoch`, `node`, `nodeBits`, or\n * `counter` via options - doing so bypasses `state` entirely.\n * - Tests should mock Date.now() or provide explicit options for deterministic behavior.\n */\nconst state: TsidState = {\n node: undefined,\n msecs: -Infinity,\n counter: 0,\n}\n\nfunction pack(msecsDiff: bigint, node: number, counterBits: number, counter: number): bigint {\n return (msecsDiff << BigInt(RANDOM_BITS)) | (BigInt(node) << BigInt(counterBits)) | BigInt(counter)\n}\n\nfunction writeTsidBytes(value: bigint, buf?: Uint8Array, offset = 0): Uint8Array {\n if (!buf) {\n buf = new Uint8Array(TSID_BYTES)\n offset = 0\n } else if (offset < 0 || offset + TSID_BYTES > buf.length) {\n throw new BufferError(\n 'TSID_BUFFER_OUT_OF_BOUNDS',\n `TSID byte range ${offset}:${offset + TSID_BYTES - 1} is out of buffer bounds`,\n )\n }\n\n for (let i = 0; i < TSID_BYTES; i += 1) {\n buf[offset + i] = Number((value >> BigInt((TSID_BYTES - 1 - i) * 8)) & 0xffn)\n }\n\n return buf\n}\n\n/*\n * Overload: no buffer => return a TSID bigint.\n */\nfunction tsidFn(options?: TsidOptions, buf?: undefined, offset?: number): bigint\n/*\n * Overload: caller provides a buffer slice to fill with TSID bytes.\n */\nfunction tsidFn<TBuf extends Uint8Array = Uint8Array>(\n options: TsidOptions | undefined,\n buf: TBuf,\n offset?: number,\n): TBuf\nfunction tsidFn<TBuf extends Uint8Array = Uint8Array>(options?: TsidOptions, buf?: TBuf, offset = 0): bigint | TBuf {\n let msecsDiff: bigint\n let node: number\n let counterBits: number\n let counter: number\n\n if (options) {\n const nodeBits = options.nodeBits ?? DEFAULT_NODE_BITS\n if (nodeBits < 0 || nodeBits > MAX_NODE_BITS) {\n throw new InvalidInputError('TSID_NODE_BITS_OUT_OF_RANGE', `nodeBits must be between 0 and ${MAX_NODE_BITS}`)\n }\n counterBits = RANDOM_BITS - nodeBits\n const nodeMask = (1 << nodeBits) - 1\n const counterMask = (1 << counterBits) - 1\n\n const optNode = options.node\n if (optNode !== undefined) {\n if (optNode < 0 || optNode > nodeMask) {\n throw new InvalidInputError('TSID_NODE_OUT_OF_RANGE', `node must be between 0 and ${nodeMask}`)\n }\n node = optNode\n } else {\n node = randomUint32() & nodeMask\n }\n\n const optCounter = options.counter\n if (optCounter !== undefined) {\n if (optCounter < 0 || optCounter > counterMask) {\n throw new InvalidInputError('TSID_COUNTER_OUT_OF_RANGE', `counter must be between 0 and ${counterMask}`)\n }\n counter = optCounter\n } else {\n counter = randomUint32() & counterMask\n }\n\n const epoch = options.epoch ?? TSID_EPOCH\n const msecs = options.msecs ?? Date.now()\n const diff = BigInt(msecs) - BigInt(epoch)\n if (diff < 0n || diff > MAX_TIMESTAMP_DIFF) {\n throw new InvalidInputError(\n 'TSID_TIMESTAMP_OUT_OF_RANGE',\n `msecs - epoch must be between 0 and ${MAX_TIMESTAMP_DIFF}`,\n )\n }\n msecsDiff = diff\n } else {\n // Lazily initialize the persistent node ID on first no-option call.\n if (state.node === undefined) {\n state.node = randomUint32() & DEFAULT_NODE_MASK\n }\n node = state.node\n counterBits = DEFAULT_COUNTER_BITS\n\n /**\n * Note: by default, Cloudflare Workers \"freezes\" time during request handling\n * to prevent side-channel attacks, so Date.now() returns the same value for\n * the entire duration of a request. Monotonic ordering within such a request\n * relies entirely on the counter (and its clock-drift-ahead overflow below).\n */\n const now = Date.now()\n if (now > state.msecs) {\n state.msecs = now\n state.counter = randomUint32() & DEFAULT_COUNTER_MASK\n } else {\n state.counter += 1\n if (state.counter > DEFAULT_COUNTER_MASK) {\n // Counter overflowed within this real millisecond: advance the internal\n // virtual timestamp ahead of wall-clock time rather than throwing. A\n // 12-bit counter (4096 values/ms) is a realistic throughput ceiling.\n state.msecs += 1\n state.counter = 0\n }\n }\n counter = state.counter\n msecsDiff = BigInt(state.msecs) - BigInt(TSID_EPOCH)\n }\n\n const packed = pack(msecsDiff, node, counterBits, counter)\n\n if (buf) {\n writeTsidBytes(packed, buf, offset)\n return buf\n }\n\n return packed\n}\n\n/**\n * Convert a TSID bigint to 8 bytes.\n */\nfunction toBytes(id: bigint): Uint8Array {\n return writeTsidBytes(id)\n}\n\n/**\n * Convert 8 bytes to a TSID bigint.\n */\nfunction fromBytes(bytes: Uint8Array): bigint {\n if (bytes.length !== TSID_BYTES) {\n throw new BufferError(\n 'TSID_BYTES_INVALID_LENGTH',\n `TSID bytes must be exactly ${TSID_BYTES} bytes, got ${bytes.length}`,\n )\n }\n\n let value = 0n\n for (let i = 0; i < TSID_BYTES; i += 1) {\n value = (value << 8n) | BigInt(bytes[i])\n }\n return value\n}\n\n/**\n * Extract the timestamp (milliseconds since Unix epoch) from a TSID.\n * The epoch is not self-describing in the packed value - pass the same\n * `epoch` used at generation time if it was overridden from the default.\n */\nfunction timestamp(id: bigint, epoch: number = TSID_EPOCH): number {\n return epoch + Number(id >> BigInt(RANDOM_BITS))\n}\n\n/**\n * Validate that a value is a TSID bigint within the 64-bit range.\n */\nfunction isValid(id: unknown): id is bigint {\n return typeof id === 'bigint' && id >= 0n && id <= MAX\n}\n\nconst NIL = 0n\nconst MAX = (1n << 64n) - 1n\n\n/**\n * Generate a TSID bigint or write the bytes into a buffer.\n *\n * TSID (Time-Sorted Unique Identifier) is a 64-bit Snowflake-style identifier\n * combining a millisecond timestamp, a node ID, and a per-millisecond counter.\n * Unlike every other uniku generator, it returns a `bigint` by default, since\n * TSID's entire value proposition is native numeric storage (e.g. a database\n * BIGINT primary key).\n *\n * @example\n * ```ts\n * import { tsid } from 'uniku/tsid'\n *\n * const id = tsid()\n * // => 862301223059968074n\n *\n * // Canonical string form\n * const str = tsid.toString(id)\n * // => \"0QXW2CK4XZM2A\"\n * tsid.fromString(str) === id // true\n *\n * // Extract timestamp\n * const ts = tsid.timestamp(id)\n * console.log(new Date(ts))\n *\n * // Validate\n * tsid.isValid(id) // true\n *\n * // Convert to/from bytes (8 bytes)\n * const bytes = tsid.toBytes(id)\n * const restored = tsid.fromBytes(bytes)\n * ```\n */\nexport const tsid: Tsid = Object.assign(tsidFn, {\n toBytes,\n fromBytes,\n toString: encodeTsidString,\n fromString: decodeTsidString,\n timestamp,\n isValid,\n NIL,\n MAX,\n})\n\nexport { BufferError, InvalidInputError, ParseError, UniqueIdError } from '../errors'\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as UniqueIdError, n as InvalidInputError, r as ParseError, t as BufferError } from "../errors-Dgoyi-ci.mjs";
|
|
2
2
|
import { UuidV7Options } from "../uuid/v7.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/typeid/typeid.d.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeid.d.mts","names":[
|
|
1
|
+
{"version":3,"file":"typeid.d.mts","names":[],"sources":["../../src/typeid/typeid.ts"],"mappings":";;;;KAIY,aAAA,GAAgB,aAAA;AAAA,KAEhB,MAAA;EAAA,CACT,MAAA,UAAgB,OAAA,GAAU,aAAA;EAC3B,OAAA,CAAQ,EAAA,WAAa,UAAA;EACrB,SAAA,CAAU,MAAA,UAAgB,KAAA,EAAO,UAAA;EACjC,MAAA,CAAO,EAAA;EACP,QAAA,CAAS,MAAA,UAAgB,IAAA;EACzB,SAAA,CAAU,EAAA;EACV,MAAA,CAAO,EAAA;EACP,MAAA,CAAO,EAAA;EACP,OAAA,CAAQ,EAAA,YAAc,EAAA;AAAA;;;;;;;;;;;;;;;;;;;cAgQX,MAAA,EAAQ,MAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeid.mjs","names":["BASE32_DECODE: number[]","prefix","suffix","typeid: Typeid"],"sources":["../../src/typeid/typeid.ts"],"sourcesContent":["import { InvalidInputError, ParseError } from '../errors'\nimport type { UuidV7Options } from '../uuid/v7'\nimport { uuidv7 } from '../uuid/v7'\n\nexport type TypeidOptions = UuidV7Options\n\nexport type Typeid = {\n (prefix: string, options?: TypeidOptions): string\n toBytes(id: string): Uint8Array\n fromBytes(prefix: string, bytes: Uint8Array): string\n toUuid(id: string): string\n fromUuid(prefix: string, uuid: string): string\n timestamp(id: string): number\n prefix(id: string): string\n suffix(id: string): string\n isValid(id: unknown): id is string\n}\n\nconst TYPEID_SUFFIX_LENGTH = 26\nconst TYPEID_UUID_BYTE_LENGTH = 16\nconst TYPEID_ALPHABET = '0123456789abcdefghjkmnpqrstvwxyz'\n\nconst BASE32_DECODE: number[] = Array.from({ length: 128 }, () => -1)\nfor (let i = 0; i < TYPEID_ALPHABET.length; i += 1) {\n BASE32_DECODE[TYPEID_ALPHABET.charCodeAt(i)] = i\n}\n\ntype ParsedTypeid = {\n prefix: string\n suffix: string\n bytes: Uint8Array\n}\n\nfunction assertValidPrefix(prefix: string): void {\n if (prefix.length === 0) {\n return\n }\n\n if (prefix.length > 63) {\n throw new InvalidInputError('TYPEID_PREFIX_TOO_LONG', 'TypeID prefix must be at most 63 characters')\n }\n\n for (let i = 0; i < prefix.length; i += 1) {\n const code = prefix.charCodeAt(i)\n const isLowercaseLetter = code >= 97 && code <= 122\n const isUnderscore = code === 95\n\n if (!isLowercaseLetter && !isUnderscore) {\n throw new InvalidInputError(\n 'TYPEID_PREFIX_INVALID_CHARACTER',\n 'TypeID prefix must contain only lowercase ASCII letters and underscores',\n )\n }\n\n if ((i === 0 || i === prefix.length - 1) && !isLowercaseLetter) {\n throw new InvalidInputError('TYPEID_PREFIX_INVALID_BOUNDARY', 'TypeID prefix must start and end with a-z')\n }\n }\n}\n\nfunction decodeSuffixValue(suffix: string, index: number): number {\n const code = suffix.charCodeAt(index)\n const value = code < BASE32_DECODE.length ? BASE32_DECODE[code] : -1\n\n if (value === -1) {\n throw new ParseError(\n 'TYPEID_SUFFIX_INVALID_CHARACTER',\n `TypeID suffix contains invalid character at position ${index}`,\n )\n }\n\n return value\n}\n\nfunction assertValidSuffixShape(suffix: string): void {\n if (suffix.length !== TYPEID_SUFFIX_LENGTH) {\n throw new ParseError('TYPEID_SUFFIX_INVALID_LENGTH', `TypeID suffix must be 26 characters, got ${suffix.length}`)\n }\n\n if (suffix[0] > '7') {\n throw new ParseError('TYPEID_SUFFIX_OVERFLOW', 'TypeID suffix must encode a 128-bit value')\n }\n}\n\nfunction encodeBytesToSuffix(bytes: Uint8Array): string {\n if (bytes.length !== TYPEID_UUID_BYTE_LENGTH) {\n throw new InvalidInputError('TYPEID_UUID_BYTES_INVALID_LENGTH', `UUID bytes must be 16 bytes, got ${bytes.length}`)\n }\n\n return (\n TYPEID_ALPHABET[(bytes[0] & 0xe0) >> 5] +\n TYPEID_ALPHABET[bytes[0] & 0x1f] +\n TYPEID_ALPHABET[(bytes[1] & 0xf8) >> 3] +\n TYPEID_ALPHABET[((bytes[1] & 0x07) << 2) | ((bytes[2] & 0xc0) >> 6)] +\n TYPEID_ALPHABET[(bytes[2] & 0x3e) >> 1] +\n TYPEID_ALPHABET[((bytes[2] & 0x01) << 4) | ((bytes[3] & 0xf0) >> 4)] +\n TYPEID_ALPHABET[((bytes[3] & 0x0f) << 1) | ((bytes[4] & 0x80) >> 7)] +\n TYPEID_ALPHABET[(bytes[4] & 0x7c) >> 2] +\n TYPEID_ALPHABET[((bytes[4] & 0x03) << 3) | ((bytes[5] & 0xe0) >> 5)] +\n TYPEID_ALPHABET[bytes[5] & 0x1f] +\n TYPEID_ALPHABET[(bytes[6] & 0xf8) >> 3] +\n TYPEID_ALPHABET[((bytes[6] & 0x07) << 2) | ((bytes[7] & 0xc0) >> 6)] +\n TYPEID_ALPHABET[(bytes[7] & 0x3e) >> 1] +\n TYPEID_ALPHABET[((bytes[7] & 0x01) << 4) | ((bytes[8] & 0xf0) >> 4)] +\n TYPEID_ALPHABET[((bytes[8] & 0x0f) << 1) | ((bytes[9] & 0x80) >> 7)] +\n TYPEID_ALPHABET[(bytes[9] & 0x7c) >> 2] +\n TYPEID_ALPHABET[((bytes[9] & 0x03) << 3) | ((bytes[10] & 0xe0) >> 5)] +\n TYPEID_ALPHABET[bytes[10] & 0x1f] +\n TYPEID_ALPHABET[(bytes[11] & 0xf8) >> 3] +\n TYPEID_ALPHABET[((bytes[11] & 0x07) << 2) | ((bytes[12] & 0xc0) >> 6)] +\n TYPEID_ALPHABET[(bytes[12] & 0x3e) >> 1] +\n TYPEID_ALPHABET[((bytes[12] & 0x01) << 4) | ((bytes[13] & 0xf0) >> 4)] +\n TYPEID_ALPHABET[((bytes[13] & 0x0f) << 1) | ((bytes[14] & 0x80) >> 7)] +\n TYPEID_ALPHABET[(bytes[14] & 0x7c) >> 2] +\n TYPEID_ALPHABET[((bytes[14] & 0x03) << 3) | ((bytes[15] & 0xe0) >> 5)] +\n TYPEID_ALPHABET[bytes[15] & 0x1f]\n )\n}\n\nfunction decodeSuffixToBytes(suffix: string): Uint8Array {\n assertValidSuffixShape(suffix)\n\n const values = new Array<number>(TYPEID_SUFFIX_LENGTH)\n for (let i = 0; i < TYPEID_SUFFIX_LENGTH; i += 1) {\n values[i] = decodeSuffixValue(suffix, i)\n }\n\n const bytes = new Uint8Array(TYPEID_UUID_BYTE_LENGTH)\n bytes[0] = (values[0] << 5) | values[1]\n bytes[1] = (values[2] << 3) | (values[3] >> 2)\n bytes[2] = ((values[3] & 0x03) << 6) | (values[4] << 1) | (values[5] >> 4)\n bytes[3] = ((values[5] & 0x0f) << 4) | (values[6] >> 1)\n bytes[4] = ((values[6] & 0x01) << 7) | (values[7] << 2) | (values[8] >> 3)\n bytes[5] = ((values[8] & 0x07) << 5) | values[9]\n bytes[6] = (values[10] << 3) | (values[11] >> 2)\n bytes[7] = ((values[11] & 0x03) << 6) | (values[12] << 1) | (values[13] >> 4)\n bytes[8] = ((values[13] & 0x0f) << 4) | (values[14] >> 1)\n bytes[9] = ((values[14] & 0x01) << 7) | (values[15] << 2) | (values[16] >> 3)\n bytes[10] = ((values[16] & 0x07) << 5) | values[17]\n bytes[11] = (values[18] << 3) | (values[19] >> 2)\n bytes[12] = ((values[19] & 0x03) << 6) | (values[20] << 1) | (values[21] >> 4)\n bytes[13] = ((values[21] & 0x0f) << 4) | (values[22] >> 1)\n bytes[14] = ((values[22] & 0x01) << 7) | (values[23] << 2) | (values[24] >> 3)\n bytes[15] = ((values[24] & 0x07) << 5) | values[25]\n\n return bytes\n}\n\nfunction assertUuidV7Bytes(bytes: Uint8Array): void {\n if (bytes.length !== TYPEID_UUID_BYTE_LENGTH) {\n throw new InvalidInputError('TYPEID_UUID_BYTES_INVALID_LENGTH', `UUID bytes must be 16 bytes, got ${bytes.length}`)\n }\n\n if (bytes[6] >> 4 !== 7 || (bytes[8] & 0xc0) !== 0x80) {\n throw new InvalidInputError('TYPEID_UUID_NOT_V7', 'TypeID UUID bytes must encode a UUID v7 value')\n }\n}\n\nfunction uuidV7BytesFromSuffix(suffix: string): Uint8Array {\n const bytes = decodeSuffixToBytes(suffix)\n assertUuidV7Bytes(bytes)\n return bytes\n}\n\nfunction uuidV7BytesFromUuid(uuid: string): Uint8Array {\n if (!uuidv7.isValid(uuid)) {\n throw new InvalidInputError('TYPEID_UUID_NOT_V7', 'TypeID can only wrap UUID v7 values')\n }\n\n return uuidv7.toBytes(uuid)\n}\n\nfunction parseTypeid(id: string): ParsedTypeid {\n const separatorIndex = id.lastIndexOf('_')\n\n if (separatorIndex === 0) {\n throw new ParseError('TYPEID_INVALID_FORMAT', 'TypeID must not start with \"_\"')\n }\n\n const prefix = separatorIndex === -1 ? '' : id.slice(0, separatorIndex)\n const suffix = separatorIndex === -1 ? id : id.slice(separatorIndex + 1)\n\n assertValidPrefix(prefix)\n const bytes = uuidV7BytesFromSuffix(suffix)\n\n return { prefix, suffix, bytes }\n}\n\nfunction joinPrefixAndSuffix(prefix: string, suffix: string): string {\n return prefix === '' ? suffix : `${prefix}_${suffix}`\n}\n\nfunction formatTypeidFromUuidV7Bytes(prefix: string, bytes: Uint8Array): string {\n assertValidPrefix(prefix)\n assertUuidV7Bytes(bytes)\n return joinPrefixAndSuffix(prefix, encodeBytesToSuffix(bytes))\n}\n\nfunction timestampFromUuidV7Bytes(bytes: Uint8Array): number {\n let msecs = 0\n for (let i = 0; i < 6; i += 1) {\n msecs = msecs * 256 + bytes[i]\n }\n return msecs\n}\n\nfunction typeidFn(prefix: string, options?: TypeidOptions): string {\n const bytes = uuidv7(options, new Uint8Array(TYPEID_UUID_BYTE_LENGTH))\n return formatTypeidFromUuidV7Bytes(prefix, bytes)\n}\n\nfunction typeidToBytes(id: string): Uint8Array {\n return parseTypeid(id).bytes\n}\n\nfunction typeidFromBytes(prefix: string, bytes: Uint8Array): string {\n return formatTypeidFromUuidV7Bytes(prefix, bytes)\n}\n\nfunction typeidToUuid(id: string): string {\n return uuidv7.fromBytes(typeidToBytes(id))\n}\n\nfunction typeidFromUuid(prefix: string, uuid: string): string {\n return formatTypeidFromUuidV7Bytes(prefix, uuidV7BytesFromUuid(uuid))\n}\n\nfunction timestamp(id: string): number {\n return timestampFromUuidV7Bytes(parseTypeid(id).bytes)\n}\n\nfunction prefix(id: string): string {\n return parseTypeid(id).prefix\n}\n\nfunction suffix(id: string): string {\n return parseTypeid(id).suffix\n}\n\nfunction isValid(id: unknown): id is string {\n if (typeof id !== 'string') {\n return false\n }\n\n try {\n parseTypeid(id)\n } catch {\n return false\n }\n\n return true\n}\n\n/**\n * Generate a TypeID string backed by UUID v7.\n *\n * TypeID combines a lowercase snake_case type prefix with a 26-character\n * modified base32 encoding of a UUID v7, producing sortable domain identifiers\n * such as `user_01h2xcejqtf2nbrexx3vqjhp41`.\n *\n * @example\n * ```ts\n * import { typeid } from 'uniku/typeid'\n *\n * const id = typeid('user')\n * // => \"user_01h2xcejqtf2nbrexx3vqjhp41\"\n *\n * const uuid = typeid.toUuid(id)\n * const restored = typeid.fromUuid('user', uuid)\n * ```\n */\nexport const typeid: Typeid = Object.assign(typeidFn, {\n toBytes: typeidToBytes,\n fromBytes: typeidFromBytes,\n toUuid: typeidToUuid,\n fromUuid: typeidFromUuid,\n timestamp,\n prefix,\n suffix,\n isValid,\n})\n\nexport { BufferError, InvalidInputError, ParseError, UniqueIdError } from '../errors'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"typeid.mjs","names":[],"sources":["../../src/typeid/typeid.ts"],"sourcesContent":["import { InvalidInputError, ParseError } from '../errors'\nimport type { UuidV7Options } from '../uuid/v7'\nimport { uuidv7 } from '../uuid/v7'\n\nexport type TypeidOptions = UuidV7Options\n\nexport type Typeid = {\n (prefix: string, options?: TypeidOptions): string\n toBytes(id: string): Uint8Array\n fromBytes(prefix: string, bytes: Uint8Array): string\n toUuid(id: string): string\n fromUuid(prefix: string, uuid: string): string\n timestamp(id: string): number\n prefix(id: string): string\n suffix(id: string): string\n isValid(id: unknown): id is string\n}\n\nconst TYPEID_SUFFIX_LENGTH = 26\nconst TYPEID_UUID_BYTE_LENGTH = 16\nconst TYPEID_ALPHABET = '0123456789abcdefghjkmnpqrstvwxyz'\n\nconst BASE32_DECODE: number[] = Array.from({ length: 128 }, () => -1)\nfor (let i = 0; i < TYPEID_ALPHABET.length; i += 1) {\n BASE32_DECODE[TYPEID_ALPHABET.charCodeAt(i)] = i\n}\n\ntype ParsedTypeid = {\n prefix: string\n suffix: string\n bytes: Uint8Array\n}\n\nfunction assertValidPrefix(prefix: string): void {\n if (prefix.length === 0) {\n return\n }\n\n if (prefix.length > 63) {\n throw new InvalidInputError('TYPEID_PREFIX_TOO_LONG', 'TypeID prefix must be at most 63 characters')\n }\n\n for (let i = 0; i < prefix.length; i += 1) {\n const code = prefix.charCodeAt(i)\n const isLowercaseLetter = code >= 97 && code <= 122\n const isUnderscore = code === 95\n\n if (!isLowercaseLetter && !isUnderscore) {\n throw new InvalidInputError(\n 'TYPEID_PREFIX_INVALID_CHARACTER',\n 'TypeID prefix must contain only lowercase ASCII letters and underscores',\n )\n }\n\n if ((i === 0 || i === prefix.length - 1) && !isLowercaseLetter) {\n throw new InvalidInputError('TYPEID_PREFIX_INVALID_BOUNDARY', 'TypeID prefix must start and end with a-z')\n }\n }\n}\n\nfunction decodeSuffixValue(suffix: string, index: number): number {\n const code = suffix.charCodeAt(index)\n const value = code < BASE32_DECODE.length ? BASE32_DECODE[code] : -1\n\n if (value === -1) {\n throw new ParseError(\n 'TYPEID_SUFFIX_INVALID_CHARACTER',\n `TypeID suffix contains invalid character at position ${index}`,\n )\n }\n\n return value\n}\n\nfunction assertValidSuffixShape(suffix: string): void {\n if (suffix.length !== TYPEID_SUFFIX_LENGTH) {\n throw new ParseError('TYPEID_SUFFIX_INVALID_LENGTH', `TypeID suffix must be 26 characters, got ${suffix.length}`)\n }\n\n if (suffix[0] > '7') {\n throw new ParseError('TYPEID_SUFFIX_OVERFLOW', 'TypeID suffix must encode a 128-bit value')\n }\n}\n\nfunction encodeBytesToSuffix(bytes: Uint8Array): string {\n if (bytes.length !== TYPEID_UUID_BYTE_LENGTH) {\n throw new InvalidInputError('TYPEID_UUID_BYTES_INVALID_LENGTH', `UUID bytes must be 16 bytes, got ${bytes.length}`)\n }\n\n return (\n TYPEID_ALPHABET[(bytes[0] & 0xe0) >> 5] +\n TYPEID_ALPHABET[bytes[0] & 0x1f] +\n TYPEID_ALPHABET[(bytes[1] & 0xf8) >> 3] +\n TYPEID_ALPHABET[((bytes[1] & 0x07) << 2) | ((bytes[2] & 0xc0) >> 6)] +\n TYPEID_ALPHABET[(bytes[2] & 0x3e) >> 1] +\n TYPEID_ALPHABET[((bytes[2] & 0x01) << 4) | ((bytes[3] & 0xf0) >> 4)] +\n TYPEID_ALPHABET[((bytes[3] & 0x0f) << 1) | ((bytes[4] & 0x80) >> 7)] +\n TYPEID_ALPHABET[(bytes[4] & 0x7c) >> 2] +\n TYPEID_ALPHABET[((bytes[4] & 0x03) << 3) | ((bytes[5] & 0xe0) >> 5)] +\n TYPEID_ALPHABET[bytes[5] & 0x1f] +\n TYPEID_ALPHABET[(bytes[6] & 0xf8) >> 3] +\n TYPEID_ALPHABET[((bytes[6] & 0x07) << 2) | ((bytes[7] & 0xc0) >> 6)] +\n TYPEID_ALPHABET[(bytes[7] & 0x3e) >> 1] +\n TYPEID_ALPHABET[((bytes[7] & 0x01) << 4) | ((bytes[8] & 0xf0) >> 4)] +\n TYPEID_ALPHABET[((bytes[8] & 0x0f) << 1) | ((bytes[9] & 0x80) >> 7)] +\n TYPEID_ALPHABET[(bytes[9] & 0x7c) >> 2] +\n TYPEID_ALPHABET[((bytes[9] & 0x03) << 3) | ((bytes[10] & 0xe0) >> 5)] +\n TYPEID_ALPHABET[bytes[10] & 0x1f] +\n TYPEID_ALPHABET[(bytes[11] & 0xf8) >> 3] +\n TYPEID_ALPHABET[((bytes[11] & 0x07) << 2) | ((bytes[12] & 0xc0) >> 6)] +\n TYPEID_ALPHABET[(bytes[12] & 0x3e) >> 1] +\n TYPEID_ALPHABET[((bytes[12] & 0x01) << 4) | ((bytes[13] & 0xf0) >> 4)] +\n TYPEID_ALPHABET[((bytes[13] & 0x0f) << 1) | ((bytes[14] & 0x80) >> 7)] +\n TYPEID_ALPHABET[(bytes[14] & 0x7c) >> 2] +\n TYPEID_ALPHABET[((bytes[14] & 0x03) << 3) | ((bytes[15] & 0xe0) >> 5)] +\n TYPEID_ALPHABET[bytes[15] & 0x1f]\n )\n}\n\nfunction decodeSuffixToBytes(suffix: string): Uint8Array {\n assertValidSuffixShape(suffix)\n\n const values = new Array<number>(TYPEID_SUFFIX_LENGTH)\n for (let i = 0; i < TYPEID_SUFFIX_LENGTH; i += 1) {\n values[i] = decodeSuffixValue(suffix, i)\n }\n\n const bytes = new Uint8Array(TYPEID_UUID_BYTE_LENGTH)\n bytes[0] = (values[0] << 5) | values[1]\n bytes[1] = (values[2] << 3) | (values[3] >> 2)\n bytes[2] = ((values[3] & 0x03) << 6) | (values[4] << 1) | (values[5] >> 4)\n bytes[3] = ((values[5] & 0x0f) << 4) | (values[6] >> 1)\n bytes[4] = ((values[6] & 0x01) << 7) | (values[7] << 2) | (values[8] >> 3)\n bytes[5] = ((values[8] & 0x07) << 5) | values[9]\n bytes[6] = (values[10] << 3) | (values[11] >> 2)\n bytes[7] = ((values[11] & 0x03) << 6) | (values[12] << 1) | (values[13] >> 4)\n bytes[8] = ((values[13] & 0x0f) << 4) | (values[14] >> 1)\n bytes[9] = ((values[14] & 0x01) << 7) | (values[15] << 2) | (values[16] >> 3)\n bytes[10] = ((values[16] & 0x07) << 5) | values[17]\n bytes[11] = (values[18] << 3) | (values[19] >> 2)\n bytes[12] = ((values[19] & 0x03) << 6) | (values[20] << 1) | (values[21] >> 4)\n bytes[13] = ((values[21] & 0x0f) << 4) | (values[22] >> 1)\n bytes[14] = ((values[22] & 0x01) << 7) | (values[23] << 2) | (values[24] >> 3)\n bytes[15] = ((values[24] & 0x07) << 5) | values[25]\n\n return bytes\n}\n\nfunction assertUuidV7Bytes(bytes: Uint8Array): void {\n if (bytes.length !== TYPEID_UUID_BYTE_LENGTH) {\n throw new InvalidInputError('TYPEID_UUID_BYTES_INVALID_LENGTH', `UUID bytes must be 16 bytes, got ${bytes.length}`)\n }\n\n if (bytes[6] >> 4 !== 7 || (bytes[8] & 0xc0) !== 0x80) {\n throw new InvalidInputError('TYPEID_UUID_NOT_V7', 'TypeID UUID bytes must encode a UUID v7 value')\n }\n}\n\nfunction uuidV7BytesFromSuffix(suffix: string): Uint8Array {\n const bytes = decodeSuffixToBytes(suffix)\n assertUuidV7Bytes(bytes)\n return bytes\n}\n\nfunction uuidV7BytesFromUuid(uuid: string): Uint8Array {\n if (!uuidv7.isValid(uuid)) {\n throw new InvalidInputError('TYPEID_UUID_NOT_V7', 'TypeID can only wrap UUID v7 values')\n }\n\n return uuidv7.toBytes(uuid)\n}\n\nfunction parseTypeid(id: string): ParsedTypeid {\n const separatorIndex = id.lastIndexOf('_')\n\n if (separatorIndex === 0) {\n throw new ParseError('TYPEID_INVALID_FORMAT', 'TypeID must not start with \"_\"')\n }\n\n const prefix = separatorIndex === -1 ? '' : id.slice(0, separatorIndex)\n const suffix = separatorIndex === -1 ? id : id.slice(separatorIndex + 1)\n\n assertValidPrefix(prefix)\n const bytes = uuidV7BytesFromSuffix(suffix)\n\n return { prefix, suffix, bytes }\n}\n\nfunction joinPrefixAndSuffix(prefix: string, suffix: string): string {\n return prefix === '' ? suffix : `${prefix}_${suffix}`\n}\n\nfunction formatTypeidFromUuidV7Bytes(prefix: string, bytes: Uint8Array): string {\n assertValidPrefix(prefix)\n assertUuidV7Bytes(bytes)\n return joinPrefixAndSuffix(prefix, encodeBytesToSuffix(bytes))\n}\n\nfunction timestampFromUuidV7Bytes(bytes: Uint8Array): number {\n let msecs = 0\n for (let i = 0; i < 6; i += 1) {\n msecs = msecs * 256 + bytes[i]\n }\n return msecs\n}\n\nfunction typeidFn(prefix: string, options?: TypeidOptions): string {\n const bytes = uuidv7(options, new Uint8Array(TYPEID_UUID_BYTE_LENGTH))\n return formatTypeidFromUuidV7Bytes(prefix, bytes)\n}\n\nfunction typeidToBytes(id: string): Uint8Array {\n return parseTypeid(id).bytes\n}\n\nfunction typeidFromBytes(prefix: string, bytes: Uint8Array): string {\n return formatTypeidFromUuidV7Bytes(prefix, bytes)\n}\n\nfunction typeidToUuid(id: string): string {\n return uuidv7.fromBytes(typeidToBytes(id))\n}\n\nfunction typeidFromUuid(prefix: string, uuid: string): string {\n return formatTypeidFromUuidV7Bytes(prefix, uuidV7BytesFromUuid(uuid))\n}\n\nfunction timestamp(id: string): number {\n return timestampFromUuidV7Bytes(parseTypeid(id).bytes)\n}\n\nfunction prefix(id: string): string {\n return parseTypeid(id).prefix\n}\n\nfunction suffix(id: string): string {\n return parseTypeid(id).suffix\n}\n\nfunction isValid(id: unknown): id is string {\n if (typeof id !== 'string') {\n return false\n }\n\n try {\n parseTypeid(id)\n } catch {\n return false\n }\n\n return true\n}\n\n/**\n * Generate a TypeID string backed by UUID v7.\n *\n * TypeID combines a lowercase snake_case type prefix with a 26-character\n * modified base32 encoding of a UUID v7, producing sortable domain identifiers\n * such as `user_01h2xcejqtf2nbrexx3vqjhp41`.\n *\n * @example\n * ```ts\n * import { typeid } from 'uniku/typeid'\n *\n * const id = typeid('user')\n * // => \"user_01h2xcejqtf2nbrexx3vqjhp41\"\n *\n * const uuid = typeid.toUuid(id)\n * const restored = typeid.fromUuid('user', uuid)\n * ```\n */\nexport const typeid: Typeid = Object.assign(typeidFn, {\n toBytes: typeidToBytes,\n fromBytes: typeidFromBytes,\n toUuid: typeidToUuid,\n fromUuid: typeidFromUuid,\n timestamp,\n prefix,\n suffix,\n isValid,\n})\n\nexport { BufferError, InvalidInputError, ParseError, UniqueIdError } from '../errors'\n"],"mappings":"8IAkBA,MAEM,EAAkB,mCAElB,EAA0B,MAAM,KAAK,CAAE,OAAQ,GAAI,MAAS,EAAE,EACpE,IAAK,IAAI,EAAI,EAAG,EAAI,GAAwB,GAAK,EAC/C,EAAc,EAAgB,WAAW,CAAC,GAAK,EASjD,SAAS,EAAkB,EAAsB,CAC3C,KAAO,SAAW,EAItB,IAAI,EAAO,OAAS,GAClB,MAAM,IAAI,EAAkB,yBAA0B,6CAA6C,EAGrG,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,GAAK,EAAG,CACzC,IAAM,EAAO,EAAO,WAAW,CAAC,EAC1B,EAAoB,GAAQ,IAAM,GAAQ,IAGhD,GAAI,CAAC,GAFgB,IAAS,GAG5B,MAAM,IAAI,EACR,kCACA,yEACF,EAGF,IAAK,IAAM,GAAK,IAAM,EAAO,OAAS,IAAM,CAAC,EAC3C,MAAM,IAAI,EAAkB,iCAAkC,2CAA2C,CAE7G,CAlBqG,CAmBvG,CAEA,SAAS,EAAkB,EAAgB,EAAuB,CAChE,IAAM,EAAO,EAAO,WAAW,CAAK,EAC9B,EAAQ,EAAO,EAAc,OAAS,EAAc,GAAQ,GAElE,GAAI,IAAU,GACZ,MAAM,IAAI,EACR,kCACA,wDAAwD,GAC1D,EAGF,OAAO,CACT,CAEA,SAAS,EAAuB,EAAsB,CACpD,GAAI,EAAO,SAAW,GACpB,MAAM,IAAI,EAAW,+BAAgC,4CAA4C,EAAO,QAAQ,EAGlH,GAAI,EAAO,GAAK,IACd,MAAM,IAAI,EAAW,yBAA0B,2CAA2C,CAE9F,CAEA,SAAS,EAAoB,EAA2B,CACtD,GAAI,EAAM,SAAW,GACnB,MAAM,IAAI,EAAkB,mCAAoC,oCAAoC,EAAM,QAAQ,EAGpH,OACE,GAAiB,EAAM,GAAK,MAAS,GACrC,EAAgB,EAAM,GAAK,IAC3B,GAAiB,EAAM,GAAK,MAAS,GACrC,GAAkB,EAAM,GAAK,IAAS,GAAO,EAAM,GAAK,MAAS,GACjE,GAAiB,EAAM,GAAK,KAAS,GACrC,GAAkB,EAAM,GAAK,IAAS,GAAO,EAAM,GAAK,MAAS,GACjE,GAAkB,EAAM,GAAK,KAAS,GAAO,EAAM,GAAK,MAAS,GACjE,GAAiB,EAAM,GAAK,MAAS,GACrC,GAAkB,EAAM,GAAK,IAAS,GAAO,EAAM,GAAK,MAAS,GACjE,EAAgB,EAAM,GAAK,IAC3B,GAAiB,EAAM,GAAK,MAAS,GACrC,GAAkB,EAAM,GAAK,IAAS,GAAO,EAAM,GAAK,MAAS,GACjE,GAAiB,EAAM,GAAK,KAAS,GACrC,GAAkB,EAAM,GAAK,IAAS,GAAO,EAAM,GAAK,MAAS,GACjE,GAAkB,EAAM,GAAK,KAAS,GAAO,EAAM,GAAK,MAAS,GACjE,GAAiB,EAAM,GAAK,MAAS,GACrC,GAAkB,EAAM,GAAK,IAAS,GAAO,EAAM,IAAM,MAAS,GAClE,EAAgB,EAAM,IAAM,IAC5B,GAAiB,EAAM,IAAM,MAAS,GACtC,GAAkB,EAAM,IAAM,IAAS,GAAO,EAAM,IAAM,MAAS,GACnE,GAAiB,EAAM,IAAM,KAAS,GACtC,GAAkB,EAAM,IAAM,IAAS,GAAO,EAAM,IAAM,MAAS,GACnE,GAAkB,EAAM,IAAM,KAAS,GAAO,EAAM,IAAM,MAAS,GACnE,GAAiB,EAAM,IAAM,MAAS,GACtC,GAAkB,EAAM,IAAM,IAAS,GAAO,EAAM,IAAM,MAAS,GACnE,EAAgB,EAAM,IAAM,GAEhC,CAEA,SAAS,EAAoB,EAA4B,CACvD,EAAuB,CAAM,EAE7B,IAAM,EAAa,MAAc,EAAoB,EACrD,IAAK,IAAI,EAAI,EAAG,EAAI,GAAsB,GAAK,EAC7C,EAAO,GAAK,EAAkB,EAAQ,CAAC,EAGzC,IAAM,EAAQ,IAAI,WAAW,EAAuB,EAkBpD,MAjBA,GAAM,GAAM,EAAO,IAAM,EAAK,EAAO,GACrC,EAAM,GAAM,EAAO,IAAM,EAAM,EAAO,IAAM,EAC5C,EAAM,IAAO,EAAO,GAAK,IAAS,EAAM,EAAO,IAAM,EAAM,EAAO,IAAM,EACxE,EAAM,IAAO,EAAO,GAAK,KAAS,EAAM,EAAO,IAAM,EACrD,EAAM,IAAO,EAAO,GAAK,IAAS,EAAM,EAAO,IAAM,EAAM,EAAO,IAAM,EACxE,EAAM,IAAO,EAAO,GAAK,IAAS,EAAK,EAAO,GAC9C,EAAM,GAAM,EAAO,KAAO,EAAM,EAAO,KAAO,EAC9C,EAAM,IAAO,EAAO,IAAM,IAAS,EAAM,EAAO,KAAO,EAAM,EAAO,KAAO,EAC3E,EAAM,IAAO,EAAO,IAAM,KAAS,EAAM,EAAO,KAAO,EACvD,EAAM,IAAO,EAAO,IAAM,IAAS,EAAM,EAAO,KAAO,EAAM,EAAO,KAAO,EAC3E,EAAM,KAAQ,EAAO,IAAM,IAAS,EAAK,EAAO,IAChD,EAAM,IAAO,EAAO,KAAO,EAAM,EAAO,KAAO,EAC/C,EAAM,KAAQ,EAAO,IAAM,IAAS,EAAM,EAAO,KAAO,EAAM,EAAO,KAAO,EAC5E,EAAM,KAAQ,EAAO,IAAM,KAAS,EAAM,EAAO,KAAO,EACxD,EAAM,KAAQ,EAAO,IAAM,IAAS,EAAM,EAAO,KAAO,EAAM,EAAO,KAAO,EAC5E,EAAM,KAAQ,EAAO,IAAM,IAAS,EAAK,EAAO,IAEzC,CACT,CAEA,SAAS,EAAkB,EAAyB,CAClD,GAAI,EAAM,SAAW,GACnB,MAAM,IAAI,EAAkB,mCAAoC,oCAAoC,EAAM,QAAQ,EAGpH,GAAI,EAAM,IAAM,GAAM,IAAM,EAAM,GAAK,MAAU,IAC/C,MAAM,IAAI,EAAkB,qBAAsB,+CAA+C,CAErG,CAEA,SAAS,EAAsB,EAA4B,CACzD,IAAM,EAAQ,EAAoB,CAAM,EAExC,OADA,EAAkB,CAAK,EAChB,CACT,CAEA,SAAS,EAAoB,EAA0B,CACrD,GAAI,CAAC,EAAO,QAAQ,CAAI,EACtB,MAAM,IAAI,EAAkB,qBAAsB,qCAAqC,EAGzF,OAAO,EAAO,QAAQ,CAAI,CAC5B,CAEA,SAAS,EAAY,EAA0B,CAC7C,IAAM,EAAiB,EAAG,YAAY,GAAG,EAEzC,GAAI,IAAmB,EACrB,MAAM,IAAI,EAAW,wBAAyB,gCAAgC,EAGhF,IAAM,EAAS,IAAmB,GAAK,GAAK,EAAG,MAAM,EAAG,CAAc,EAChE,EAAS,IAAmB,GAAK,EAAK,EAAG,MAAM,EAAiB,CAAC,EAKvE,OAHA,EAAkB,CAAM,EAGjB,CAAE,SAAQ,SAAQ,MAFX,EAAsB,CAEP,CAAE,CACjC,CAEA,SAAS,EAAoB,EAAgB,EAAwB,CACnE,OAAO,IAAW,GAAK,EAAS,GAAG,EAAO,GAAG,GAC/C,CAEA,SAAS,EAA4B,EAAgB,EAA2B,CAG9E,OAFA,EAAkB,CAAM,EACxB,EAAkB,CAAK,EAChB,EAAoB,EAAQ,EAAoB,CAAK,CAAC,CAC/D,CAEA,SAAS,EAAyB,EAA2B,CAC3D,IAAI,EAAQ,EACZ,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,GAAK,EAC1B,EAAQ,EAAQ,IAAM,EAAM,GAE9B,OAAO,CACT,CAEA,SAAS,EAAS,EAAgB,EAAiC,CAEjE,OAAO,EAA4B,EADrB,EAAO,EAAS,IAAI,WAAW,EAAuB,CACrB,CAAC,CAClD,CAEA,SAAS,EAAc,EAAwB,CAC7C,OAAO,EAAY,CAAE,CAAC,CAAC,KACzB,CAEA,SAAS,EAAgB,EAAgB,EAA2B,CAClE,OAAO,EAA4B,EAAQ,CAAK,CAClD,CAEA,SAAS,EAAa,EAAoB,CACxC,OAAO,EAAO,UAAU,EAAc,CAAE,CAAC,CAC3C,CAEA,SAAS,EAAe,EAAgB,EAAsB,CAC5D,OAAO,EAA4B,EAAQ,EAAoB,CAAI,CAAC,CACtE,CAEA,SAAS,EAAU,EAAoB,CACrC,OAAO,EAAyB,EAAY,CAAE,CAAC,CAAC,KAAK,CACvD,CAEA,SAAS,EAAO,EAAoB,CAClC,OAAO,EAAY,CAAE,CAAC,CAAC,MACzB,CAEA,SAAS,EAAO,EAAoB,CAClC,OAAO,EAAY,CAAE,CAAC,CAAC,MACzB,CAEA,SAAS,EAAQ,EAA2B,CAC1C,GAAI,OAAO,GAAO,SAChB,MAAO,GAGT,GAAI,CACF,EAAY,CAAE,CAChB,MAAQ,CACN,MAAO,EACT,CAEA,MAAO,EACT,CAoBA,MAAa,EAAiB,OAAO,OAAO,EAAU,CACpD,QAAS,EACT,UAAW,EACX,OAAQ,EACR,SAAU,EACV,YACA,SACA,SACA,SACF,CAAC"}
|
package/build/ulid/ulid.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as UniqueIdError, n as InvalidInputError, r as ParseError, t as BufferError } from "../errors-Dgoyi-ci.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/ulid/ulid.d.ts
|
|
4
4
|
type UlidOptions = {
|
|
@@ -20,10 +20,8 @@ type Ulid = {
|
|
|
20
20
|
toBytes(id: string): Uint8Array;
|
|
21
21
|
fromBytes(bytes: Uint8Array): string;
|
|
22
22
|
timestamp(id: string): number;
|
|
23
|
-
isValid(id: unknown): id is string;
|
|
24
|
-
/** The
|
|
25
|
-
NIL: string;
|
|
26
|
-
/** The max ULID (maximum valid value) */
|
|
23
|
+
isValid(id: unknown): id is string; /** The nil ULID (all zeros) */
|
|
24
|
+
NIL: string; /** The max ULID (maximum valid value) */
|
|
27
25
|
MAX: string;
|
|
28
26
|
};
|
|
29
27
|
declare const ulid: Ulid;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ulid.d.mts","names":[
|
|
1
|
+
{"version":3,"file":"ulid.d.mts","names":[],"sources":["../../src/ulid/ulid.ts"],"mappings":";;;KAKY,WAAA;;;AAAZ;;EAKE,MAAA,GAAS,UAAA;EAAA;;;;EAKT,KAAA;AAAA;AAAA,KAGU,IAAA;EAAA;EAAA,cAEI,UAAA,GAAa,UAAA,EAAY,OAAA,EAAS,WAAA,cAAyB,GAAA,EAAK,IAAA,EAAM,MAAA,YAAkB,IAAA;EAAA,CACrG,OAAA,GAAU,WAAA,EAAa,GAAA,cAAiB,MAAA;EACzC,OAAA,CAAQ,EAAA,WAAa,UAAA;EACrB,SAAA,CAAU,KAAA,EAAO,UAAA;EACjB,SAAA,CAAU,EAAA;EACV,OAAA,CAAQ,EAAA,YAAc,EAAA;EAEtB,GAAA;EAEA,GAAA;AAAA;AAAA,cAiJW,IAAA,EAAM,IAAA"}
|
package/build/ulid/ulid.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{r as e}from"../random-Chp-Nkzi.mjs";import{BufferError as t,InvalidInputError as n,ParseError as r,UniqueIdError as i}from"../errors.mjs";import{r as a,t as o}from"../bytes-xqWxFYsM.mjs";const s=`0123456789ABCDEFGHJKMNPQRSTVWXYZ`,c=new Uint8Array(65536);c.fill(255);for(let e=0;e<32;e+=1){let t=s.charCodeAt(e),n=s[e].toLowerCase().charCodeAt(0);c[t]=e,c[n]=e}function l(e,t){return new r(`ULID_INVALID_CHAR`,`Invalid ULID character: ${e[t]}`)}function u(){return new r(`ULID_TIMESTAMP_OVERFLOW`,`ULID timestamp exceeds 48 bits`)}function d(e){let t=c[e.charCodeAt(0)];if(t===255)throw l(e,0);if(t>7)throw u();let n=t;for(let t=1;t<10;t+=1){let r=c[e.charCodeAt(t)];if(r===255)throw l(e,t);n=n*32+r}return n}function f(e){return s[Math.floor(e/35184372088832)&31]+s[Math.floor(e/1099511627776)&31]+s[Math.floor(e/34359738368)&31]+s[Math.floor(e/1073741824)&31]+s[Math.floor(e/33554432)&31]+s[Math.floor(e/1048576)&31]+s[Math.floor(e/32768)&31]+s[Math.floor(e/1024)&31]+s[Math.floor(e/32)&31]+s[e&31]}function p(e){return s[e[0]>>3&31]+s[(e[0]<<2|e[1]>>6)&31]+s[e[1]>>1&31]+s[(e[1]<<4|e[2]>>4)&31]+s[(e[2]<<1|e[3]>>7)&31]+s[e[3]>>2&31]+s[(e[3]<<3|e[4]>>5)&31]+s[e[4]&31]+s[e[5]>>3&31]+s[(e[5]<<2|e[6]>>6)&31]+s[e[6]>>1&31]+s[(e[6]<<4|e[7]>>4)&31]+s[(e[7]<<1|e[8]>>7)&31]+s[e[8]>>2&31]+s[(e[8]<<3|e[9]>>5)&31]+s[e[9]&31]}function m(e){if(e.length!==26)throw new r(`ULID_INVALID_LENGTH`,`ULID string must be 26 characters`);return d(e)}function h(e){if(e.length!==26)throw new r(`ULID_INVALID_LENGTH`,`ULID string must be 26 characters`);let t=new Uint8Array(16),n=c[e.charCodeAt(0)],i=c[e.charCodeAt(1)],a=c[e.charCodeAt(2)],o=c[e.charCodeAt(3)],s=c[e.charCodeAt(4)],d=c[e.charCodeAt(5)],f=c[e.charCodeAt(6)],p=c[e.charCodeAt(7)],m=c[e.charCodeAt(8)],h=c[e.charCodeAt(9)],g=c[e.charCodeAt(10)],_=c[e.charCodeAt(11)],v=c[e.charCodeAt(12)],y=c[e.charCodeAt(13)],b=c[e.charCodeAt(14)],x=c[e.charCodeAt(15)],S=c[e.charCodeAt(16)],C=c[e.charCodeAt(17)],w=c[e.charCodeAt(18)],T=c[e.charCodeAt(19)],E=c[e.charCodeAt(20)],D=c[e.charCodeAt(21)],O=c[e.charCodeAt(22)],k=c[e.charCodeAt(23)],A=c[e.charCodeAt(24)],j=c[e.charCodeAt(25)];if((n|i|a|o|s|d|f|p|m|h|g|_|v|y|b|x|S|C|w|T|E|D|O|k|A|j)&128){for(let t=0;t<26;t+=1)if(c[e.charCodeAt(t)]===255)throw l(e,t)}if(n>7)throw u();return t[0]=n<<5|i,t[1]=a<<3|o>>2,t[2]=o<<6|s<<1|d>>4,t[3]=d<<4|f>>1,t[4]=f<<7|p<<2|m>>3,t[5]=m<<5|h,t[6]=g<<3|_>>2,t[7]=_<<6|v<<1|y>>4,t[8]=y<<4|b>>1,t[9]=b<<7|x<<2|S>>3,t[10]=S<<5|C,t[11]=w<<3|T>>2,t[12]=T<<6|E<<1|D>>4,t[13]=D<<4|O>>1,t[14]=O<<7|k<<2|A>>3,t[15]=A<<5|j,t}function g(e){if(e.length<16)throw new t(`ULID_BYTES_TOO_SHORT`,`Byte array must be at least 16 bytes`);let n=0;for(let t=0;t<6;t+=1)n=n*256+e[t];return f(n)+p(e.subarray(6,16))}const _=/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/i,v={msecs:-1/0,lastRandom:new Uint8Array(10)};function y(e,n,r,i=0){if(!r)r=new Uint8Array(16),i=0;else if(i<0||i+16>r.length)throw new t(`ULID_BUFFER_OUT_OF_BOUNDS`,`ULID byte range ${i}:${i+15} is out of buffer bounds`);a(r,i,e);for(let e=0;e<10;e+=1)r[i+6+e]=n[e];return r}function b(t,r,i=0){let a,s,c=Date.now();if(t)if(a=t.msecs??c,t.random){if(t.random.length<10)throw new n(`ULID_RANDOM_BYTES_TOO_SHORT`,`Random bytes length must be >= 10 for ULID`);s=t.random}else s=e();else if(a=c,a>v.msecs)s=e(),v.msecs=a,v.lastRandom.set(s.subarray(0,10));else{if(a=v.msecs,!o(v.lastRandom))throw v.lastRandom.fill(255),new n(`ULID_RANDOM_OVERFLOW`,`ULID random component overflowed while preserving monotonic order`);s=v.lastRandom}return r?(y(a,s,r,i),r):f(a)+p(s)}function x(e){return typeof e==`string`&&_.test(e)}const S=Object.assign(b,{toBytes:e=>h(e),fromBytes:e=>g(e),timestamp:e=>m(e),isValid:x,NIL:`00000000000000000000000000`,MAX:`7ZZZZZZZZZZZZZZZZZZZZZZZZZ`});export{t as BufferError,n as InvalidInputError,r as ParseError,i as UniqueIdError,S as ulid};
|
|
2
2
|
//# sourceMappingURL=ulid.mjs.map
|