{"version":3,"sources":["../src/simpleflakes.ts"],"names":["randomBuffer","randomSource","SIMPLEFLAKE_EPOCH","SIMPLEFLAKE_TIMESTAMP_MAX","SIMPLEFLAKE_RANDOM_MAX","UNSIGNED_64BIT_MAX","randomBufferIndex","toBigInt","value","label","BigInt","TypeError","assertInRange","minValue","maxValue","RangeError","getRandomSource","globalCrypto","globalThis","crypto","require","webcrypto","Error","refillRandomBuffer","Uint32Array","getRandomValues","random23","simpleflake","timestamp","Date","now","randomBits","epoch","timestampOffset","timestampValue","resolvedRandomBits","binary","padding","binValue","asUintN","toString","padStart","extractBits","data","shift","length","shiftN","lengthN","SimpleflakeStruct","parseSimpleflake","flake","flakeValue","epochValue"],"mappings":"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","file":"simpleflakes.mjs","sourcesContent":["export const SIMPLEFLAKE_EPOCH = 946684800000n; // Date.UTC(2000, 0, 1)\nconst UNSIGNED_23BIT_MAX = 8388607;\nconst RANDOM_BUFFER_SIZE = 1024;\n\nconst SIMPLEFLAKE_TIMESTAMP_LENGTH = 41n;\nconst SIMPLEFLAKE_RANDOM_LENGTH = 23n;\nconst SIMPLEFLAKE_TIMESTAMP_MAX = (1n << SIMPLEFLAKE_TIMESTAMP_LENGTH) - 1n;\nconst SIMPLEFLAKE_RANDOM_MAX = (1n << SIMPLEFLAKE_RANDOM_LENGTH) - 1n;\nconst UNSIGNED_64BIT_MAX = (1n << 64n) - 1n;\n\nconst SIMPLEFLAKE_RANDOM_SHIFT = 0n;\nconst SIMPLEFLAKE_TIMESTAMP_SHIFT = 23n;\n\ninterface RandomSource {\n  getRandomValues<T extends ArrayBufferView>(array: T): T;\n}\n\ntype NodeCryptoRequire = (moduleName: string) => { webcrypto: RandomSource };\n\ndeclare const require: ((moduleName: string) => unknown) | undefined;\n\nlet randomBuffer: Uint32Array | undefined;\nlet randomBufferIndex = RANDOM_BUFFER_SIZE;\nlet randomSource: RandomSource | undefined;\n\nfunction toBigInt(value: bigint | number | string, label: string): bigint {\n  if (typeof value === \"bigint\") return value;\n  try {\n    return BigInt(value);\n  } catch {\n    throw new TypeError(`${label} must be an integer representable as a BigInt.`);\n  }\n}\n\nfunction assertInRange(\n  value: bigint,\n  minValue: bigint,\n  maxValue: bigint,\n  label: string\n): void {\n  if (value < minValue || value > maxValue) {\n    throw new RangeError(\n      `${label} must be between ${minValue} and ${maxValue}.`\n    );\n  }\n}\n\nfunction getRandomSource(): RandomSource {\n  if (randomSource) return randomSource;\n\n  const globalCrypto = (globalThis as { crypto?: RandomSource }).crypto;\n\n  if (globalCrypto) {\n    randomSource = globalCrypto;\n    return randomSource;\n  }\n\n  try {\n    randomSource = (require as NodeCryptoRequire)(\"node:crypto\").webcrypto;\n    return randomSource;\n  } catch {\n    throw new Error(\n      \"Cryptographically secure random values are unavailable in this environment.\"\n    );\n  }\n}\n\nfunction refillRandomBuffer(): void {\n  if (randomBuffer == null) {\n    randomBuffer = new Uint32Array(RANDOM_BUFFER_SIZE);\n  }\n  getRandomSource().getRandomValues(randomBuffer);\n  randomBufferIndex = 0;\n}\n\nfunction random23(): bigint {\n  if (randomBufferIndex >= RANDOM_BUFFER_SIZE) {\n    refillRandomBuffer();\n  }\n  const value = randomBuffer![randomBufferIndex++]!;\n  return BigInt(value & UNSIGNED_23BIT_MAX);\n}\n\n/**\n * Generates a simpleflake ID\n * @param timestamp - Timestamp in milliseconds (defaults to current time)\n * @param randomBits - Random bits for the ID (defaults to a cryptographically secure random value)\n * @param epoch - Epoch timestamp in milliseconds (defaults to SIMPLEFLAKE_EPOCH)\n * @returns Generated simpleflake as a BigInt\n * @remarks\n * - Collision envelope: the 23-bit random field is a birthday-bound space, not a\n *   sequence counter — ~1% collision probability at ~410 IDs/ms and ~50% at ~3,400\n *   IDs/ms from a single uncoordinated generator. Suitable for up to roughly a few\n *   hundred IDs/ms per generator; higher throughput needs a different algorithm\n *   (e.g. Snowflake's machine/sequence bits, or ULID's 80 random bits), not a parameter tweak.\n * - Epoch horizon: the default 41-bit timestamp (from the 2000-01-01 epoch) is\n *   exhausted around September 2069.\n * - Same-millisecond ordering: IDs generated within the same millisecond are not\n *   guaranteed to be numerically ordered by generation order — there is no sequence bit.\n */\nexport function simpleflake(\n  timestamp: number | bigint = Date.now(),\n  randomBits?: number | bigint,\n  epoch: number | bigint = SIMPLEFLAKE_EPOCH\n): bigint {\n  const timestampValue = toBigInt(timestamp, \"timestamp\");\n  const epochValue = toBigInt(epoch, \"epoch\");\n  const timestampOffset = timestampValue - epochValue;\n  const resolvedRandomBits =\n    randomBits == null ? random23() : toBigInt(randomBits, \"randomBits\");\n\n  assertInRange(timestampOffset, 0n, SIMPLEFLAKE_TIMESTAMP_MAX, \"timestamp - epoch\");\n  assertInRange(resolvedRandomBits, 0n, SIMPLEFLAKE_RANDOM_MAX, \"randomBits\");\n\n  return (\n    (timestampOffset << SIMPLEFLAKE_TIMESTAMP_SHIFT) |\n    resolvedRandomBits\n  );\n}\n\n/**\n * Converts a value to binary representation\n * @param value - The value to convert to binary\n * @param padding - Whether to pad to 64 bits (defaults to true)\n * @returns Binary string representation\n */\nexport function binary(\n  value: bigint | number | string,\n  padding: boolean = true\n): string {\n  const binValue = BigInt.asUintN(64, toBigInt(value, \"value\")).toString(2);\n  return padding ? binValue.padStart(64, \"0\") : binValue;\n}\n\n/**\n * Extracts bits from a data value\n * @param data - The data to extract bits from\n * @param shift - Number of bits to shift\n * @param length - Number of bits to extract\n * @returns Extracted bits as a BigInt\n */\nexport function extractBits(\n  data: bigint | number | string,\n  shift: bigint | number,\n  length: bigint | number\n): bigint {\n  const shiftN = toBigInt(shift, \"shift\");\n  const lengthN = toBigInt(length, \"length\");\n  assertInRange(shiftN, 0n, 63n, \"shift\");\n  assertInRange(lengthN, 0n, 63n, \"length\");\n  // Optimize: shift right first, then mask (avoids creating large bitmask)\n  return (toBigInt(data, \"data\") >> shiftN) & ((1n << lengthN) - 1n);\n}\n\n/**\n * Structure representing a parsed simpleflake\n */\nexport class SimpleflakeStruct {\n  public declare readonly timestamp: bigint;\n  public declare readonly randomBits: bigint;\n\n  constructor(timestamp: bigint, randomBits: bigint) {\n    if (timestamp == null || randomBits == null) {\n      throw new Error(\"Missing argument for SimpleflakeStruct.\");\n    }\n    this.timestamp = timestamp;\n    this.randomBits = randomBits;\n  }\n}\n\n/**\n * Parses a simpleflake into its components\n * @param flake - The simpleflake to parse\n * @param epoch - Epoch timestamp in milliseconds used to reconstruct the absolute timestamp\n * @returns SimpleflakeStruct containing timestamp and random bits\n */\nexport function parseSimpleflake(\n  flake: bigint | number | string,\n  epoch: bigint | number = SIMPLEFLAKE_EPOCH\n): SimpleflakeStruct {\n  const flakeValue = toBigInt(flake, \"flake\");\n  const epochValue = toBigInt(epoch, \"epoch\");\n\n  assertInRange(flakeValue, 0n, UNSIGNED_64BIT_MAX, \"flake\");\n\n  return new SimpleflakeStruct(\n    extractBits(\n      flakeValue,\n      SIMPLEFLAKE_TIMESTAMP_SHIFT,\n      SIMPLEFLAKE_TIMESTAMP_LENGTH\n    ) + epochValue,\n    extractBits(\n      flakeValue,\n      SIMPLEFLAKE_RANDOM_SHIFT,\n      SIMPLEFLAKE_RANDOM_LENGTH\n    )\n  );\n}\n\nexport default {\n  binary,\n  extractBits,\n  parseSimpleflake,\n  SIMPLEFLAKE_EPOCH,\n  simpleflake,\n  SimpleflakeStruct,\n};\n"]}