@traffical/js-client 0.14.0 → 0.15.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/dist/client.d.ts +10 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +15 -1
- package/dist/client.js.map +1 -1
- package/dist/event-logger.d.ts +7 -0
- package/dist/event-logger.d.ts.map +1 -1
- package/dist/event-logger.js +24 -9
- package/dist/event-logger.js.map +1 -1
- package/dist/traffical.min.js +2 -2
- package/dist/traffical.min.js.map +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../node_modules/.bun/ulid@2.4.0/node_modules/ulid/stubs/crypto.js", "../src/global.ts", "../../../node_modules/.bun/@noble+hashes@2.2.0/node_modules/@noble/hashes/src/utils.ts", "../../../node_modules/.bun/@noble+hashes@2.2.0/node_modules/@noble/hashes/src/_md.ts", "../../../node_modules/.bun/@noble+hashes@2.2.0/node_modules/@noble/hashes/src/sha2.ts", "../../core/src/hashing/assignment-hash.ts", "../../core/src/hashing/bucket.ts", "../../core/src/hashing/weighted.ts", "../../core/src/resolution/conditions.ts", "../../core/src/scoring/contextual.ts", "../../../node_modules/.bun/nanoid@5.1.6/node_modules/nanoid/index.browser.js", "../../../node_modules/.bun/ulid@2.4.0/node_modules/ulid/dist/index.esm.js", "../../core/src/ids/index.ts", "../../core/src/resolution/engine.ts", "../../core/src/dedup/decision-dedup.ts", "../../core-io/src/decision-client.ts", "../src/error-boundary.ts", "../src/event-logger.ts", "../src/exposure-dedup.ts", "../src/stable-id.ts", "../src/storage.ts", "../src/version.ts", "../src/plugins/decision-tracking.ts", "../src/plugins/redirect.ts", "../src/plugins/redirect-attribution.ts", "../src/plugins/debug.ts", "../src/plugins/index.ts", "../src/lifecycle.ts", "../src/client.ts", "../src/plugins/dom-binding.ts"],
|
|
4
|
-
"sourcesContent": ["", "/**\n * Global entry point for IIFE bundle.\n *\n * Exports `window.Traffical` for script tag usage:\n *\n * ```html\n * <script src=\"https://cdn.traffical.io/js-client/v1/traffical.min.js\"></script>\n * <script>\n * Traffical.init({ ... }).then(function(client) {\n * var params = client.getParams({ ... });\n * });\n * </script>\n * ```\n */\n\nimport {\n TrafficalClient,\n createTrafficalClient,\n createTrafficalClientSync,\n type TrafficalClientOptions,\n} from \"./client.js\";\nimport type { TrafficalPlugin } from \"./plugins/index.js\";\nimport {\n createDOMBindingPlugin,\n type DOMBindingPlugin,\n type DOMBindingPluginOptions,\n} from \"./plugins/dom-binding.js\";\nimport {\n createRedirectPlugin,\n type RedirectPluginOptions,\n} from \"./plugins/redirect.js\";\nimport {\n createRedirectAttributionPlugin,\n type RedirectAttributionPluginOptions,\n} from \"./plugins/redirect-attribution.js\";\nimport {\n createDebugPlugin,\n type DebugPluginOptions,\n} from \"./plugins/debug.js\";\n\n// Global state for singleton pattern\nlet _instance: TrafficalClient | null = null;\n\n/**\n * Initialize the Traffical client (async).\n * Returns the client instance.\n */\nasync function init(options: TrafficalClientOptions): Promise<TrafficalClient> {\n if (_instance) {\n console.warn(\"[Traffical] Client already initialized. Returning existing instance.\");\n return _instance;\n }\n\n _instance = await createTrafficalClient(options);\n return _instance;\n}\n\n/**\n * Initialize the Traffical client (sync).\n * Returns the client instance immediately, but config fetch happens async.\n */\nfunction initSync(options: TrafficalClientOptions): TrafficalClient {\n if (_instance) {\n console.warn(\"[Traffical] Client already initialized. Returning existing instance.\");\n return _instance;\n }\n\n _instance = createTrafficalClientSync(options);\n\n // Start async initialization in background\n _instance.initialize().catch((error) => {\n console.warn(\"[Traffical] Initialization error:\", error);\n });\n\n return _instance;\n}\n\n/**\n * Get the singleton client instance.\n * Returns null if not initialized.\n */\nfunction instance(): TrafficalClient | null {\n return _instance;\n}\n\n/**\n * Destroy the singleton instance.\n */\nfunction destroy(): void {\n if (_instance) {\n _instance.destroy();\n _instance = null;\n }\n}\n\n// Export the Traffical global object\nexport {\n init,\n initSync,\n instance,\n destroy,\n TrafficalClient,\n type TrafficalClientOptions,\n type TrafficalPlugin,\n // DOM binding plugin\n createDOMBindingPlugin,\n type DOMBindingPlugin,\n type DOMBindingPluginOptions,\n // Redirect plugins\n createRedirectPlugin,\n type RedirectPluginOptions,\n createRedirectAttributionPlugin,\n type RedirectAttributionPluginOptions,\n // Debug plugin\n createDebugPlugin,\n type DebugPluginOptions,\n};\n\n", "/**\n * Utilities for hex, bytes, CSPRNG.\n * @module\n */\n/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */\n/**\n * Bytes API type helpers for old + new TypeScript.\n *\n * TS 5.6 has `Uint8Array`, while TS 5.9+ made it generic `Uint8Array<ArrayBuffer>`.\n * We can't use specific return type, because TS 5.6 will error.\n * We can't use generic return type, because most TS 5.9 software will expect specific type.\n *\n * Maps typed-array input leaves to broad forms.\n * These are compatibility adapters, not ownership guarantees.\n *\n * - `TArg` keeps byte inputs broad.\n * - `TRet` marks byte outputs for TS 5.6 and TS 5.9+ compatibility.\n */\nexport type TypedArg<T> = T extends BigInt64Array\n ? BigInt64Array\n : T extends BigUint64Array\n ? BigUint64Array\n : T extends Float32Array\n ? Float32Array\n : T extends Float64Array\n ? Float64Array\n : T extends Int16Array\n ? Int16Array\n : T extends Int32Array\n ? Int32Array\n : T extends Int8Array\n ? Int8Array\n : T extends Uint16Array\n ? Uint16Array\n : T extends Uint32Array\n ? Uint32Array\n : T extends Uint8ClampedArray\n ? Uint8ClampedArray\n : T extends Uint8Array\n ? Uint8Array\n : never;\n/** Maps typed-array output leaves to narrow TS-compatible forms. */\nexport type TypedRet<T> = T extends BigInt64Array\n ? ReturnType<typeof BigInt64Array.of>\n : T extends BigUint64Array\n ? ReturnType<typeof BigUint64Array.of>\n : T extends Float32Array\n ? ReturnType<typeof Float32Array.of>\n : T extends Float64Array\n ? ReturnType<typeof Float64Array.of>\n : T extends Int16Array\n ? ReturnType<typeof Int16Array.of>\n : T extends Int32Array\n ? ReturnType<typeof Int32Array.of>\n : T extends Int8Array\n ? ReturnType<typeof Int8Array.of>\n : T extends Uint16Array\n ? ReturnType<typeof Uint16Array.of>\n : T extends Uint32Array\n ? ReturnType<typeof Uint32Array.of>\n : T extends Uint8ClampedArray\n ? ReturnType<typeof Uint8ClampedArray.of>\n : T extends Uint8Array\n ? ReturnType<typeof Uint8Array.of>\n : never;\n/** Recursively adapts byte-carrying API input types. See {@link TypedArg}. */\nexport type TArg<T> =\n | T\n | ([TypedArg<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TRet<A[K]> }) => TArg<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TArg<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends (infer A)[]\n ? TArg<A>[]\n : T extends readonly (infer A)[]\n ? readonly TArg<A>[]\n : T extends Promise<infer A>\n ? Promise<TArg<A>>\n : T extends object\n ? { [K in keyof T]: TArg<T[K]> }\n : T\n : TypedArg<T>);\n/** Recursively adapts byte-carrying API output types. See {@link TypedArg}. */\nexport type TRet<T> = T extends unknown\n ? T &\n ([TypedRet<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TArg<A[K]> }) => TRet<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TRet<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends (infer A)[]\n ? TRet<A>[]\n : T extends readonly (infer A)[]\n ? readonly TRet<A>[]\n : T extends Promise<infer A>\n ? Promise<TRet<A>>\n : T extends object\n ? { [K in keyof T]: TRet<T[K]> }\n : T\n : TypedRet<T>)\n : never;\n/**\n * Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.\n * @param a - value to test\n * @returns `true` when the value is a Uint8Array-compatible view.\n * @example\n * Check whether a value is a Uint8Array-compatible view.\n * ```ts\n * isBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function isBytes(a: unknown): a is Uint8Array {\n // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.\n // The fallback still requires a real ArrayBuffer view, so plain\n // JSON-deserialized `{ constructor: ... }` spoofing is rejected, and\n // `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.\n return (\n a instanceof Uint8Array ||\n (ArrayBuffer.isView(a) &&\n a.constructor.name === 'Uint8Array' &&\n 'BYTES_PER_ELEMENT' in a &&\n a.BYTES_PER_ELEMENT === 1)\n );\n}\n\n/**\n * Asserts something is a non-negative integer.\n * @param n - number to validate\n * @param title - label included in thrown errors\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a non-negative integer option.\n * ```ts\n * anumber(32, 'length');\n * ```\n */\nexport function anumber(n: number, title: string = ''): void {\n if (typeof n !== 'number') {\n const prefix = title && `\"${title}\" `;\n throw new TypeError(`${prefix}expected number, got ${typeof n}`);\n }\n if (!Number.isSafeInteger(n) || n < 0) {\n const prefix = title && `\"${title}\" `;\n throw new RangeError(`${prefix}expected integer >= 0, got ${n}`);\n }\n}\n\n/**\n * Asserts something is Uint8Array.\n * @param value - value to validate\n * @param length - optional exact length constraint\n * @param title - label included in thrown errors\n * @returns The validated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate that a value is a byte array.\n * ```ts\n * abytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function abytes(\n value: TArg<Uint8Array>,\n length?: number,\n title: string = ''\n): TRet<Uint8Array> {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;\n if (!bytes) throw new TypeError(message);\n throw new RangeError(message);\n }\n return value as TRet<Uint8Array>;\n}\n\n/**\n * Copies bytes into a fresh Uint8Array.\n * Buffer-style slices can alias the same backing store, so callers that need ownership should copy.\n * @param bytes - source bytes to clone\n * @returns Freshly allocated copy of `bytes`.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Clone a byte array before mutating it.\n * ```ts\n * const copy = copyBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function copyBytes(bytes: TArg<Uint8Array>): TRet<Uint8Array> {\n // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict\n // because callers use it at byte-validation boundaries before mutating the detached copy.\n return Uint8Array.from(abytes(bytes)) as TRet<Uint8Array>;\n}\n\n/**\n * Asserts something is a wrapped hash constructor.\n * @param h - hash constructor to validate\n * @throws On wrong argument types or invalid hash wrapper shape. {@link TypeError}\n * @throws On invalid hash metadata ranges or values. {@link RangeError}\n * @throws If the hash metadata allows empty outputs or block sizes. {@link Error}\n * @example\n * Validate a callable hash wrapper.\n * ```ts\n * import { ahash } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * ahash(sha256);\n * ```\n */\nexport function ahash(h: TArg<CHash>): void {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new TypeError('Hash must wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n // HMAC and KDF callers treat these as real byte lengths; allowing zero lets fake wrappers pass\n // validation and can produce empty outputs instead of failing fast.\n if (h.outputLen < 1) throw new Error('\"outputLen\" must be >= 1');\n if (h.blockLen < 1) throw new Error('\"blockLen\" must be >= 1');\n}\n\n/**\n * Asserts a hash instance has not been destroyed or finished.\n * @param instance - hash instance to validate\n * @param checkFinished - whether to reject finalized instances\n * @throws If the hash instance has already been destroyed or finalized. {@link Error}\n * @example\n * Validate that a hash instance is still usable.\n * ```ts\n * import { aexists } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aexists(hash);\n * ```\n */\nexport function aexists(instance: any, checkFinished = true): void {\n if (instance.destroyed) throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');\n}\n\n/**\n * Asserts output is a sufficiently-sized byte array.\n * @param out - destination buffer\n * @param instance - hash instance providing output length\n * Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a caller-provided digest buffer.\n * ```ts\n * import { aoutput } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aoutput(new Uint8Array(hash.outputLen), hash);\n * ```\n */\nexport function aoutput(out: any, instance: any): void {\n abytes(out, undefined, 'digestInto() output');\n const min = instance.outputLen;\n if (out.length < min) {\n throw new RangeError('\"digestInto() output\" expected to be of length >=' + min);\n }\n}\n\n/** Generic type encompassing 8/16/32-byte array views, but not 64-bit variants. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/**\n * Casts a typed array view to Uint8Array.\n * @param arr - source typed array\n * @returns Uint8Array view over the same buffer.\n * @example\n * Reinterpret a typed array as bytes.\n * ```ts\n * u8(new Uint32Array([1, 2]));\n * ```\n */\nexport function u8(arr: TArg<TypedArray>): TRet<Uint8Array> {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength) as TRet<Uint8Array>;\n}\n\n/**\n * Casts a typed array view to Uint32Array.\n * `arr.byteOffset` must already be 4-byte aligned or the platform\n * Uint32Array constructor will throw.\n * @param arr - source typed array\n * @returns Uint32Array view over the same buffer.\n * @example\n * Reinterpret a byte array as 32-bit words.\n * ```ts\n * u32(new Uint8Array(8));\n * ```\n */\nexport function u32(arr: TArg<TypedArray>): TRet<Uint32Array> {\n return new Uint32Array(\n arr.buffer,\n arr.byteOffset,\n Math.floor(arr.byteLength / 4)\n ) as TRet<Uint32Array>;\n}\n\n/**\n * Zeroizes typed arrays in place. Warning: JS provides no guarantees.\n * @param arrays - arrays to overwrite with zeros\n * @example\n * Zeroize sensitive buffers in place.\n * ```ts\n * clean(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function clean(...arrays: TArg<TypedArray[]>): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/**\n * Creates a DataView for byte-level manipulation.\n * @param arr - source typed array\n * @returns DataView over the same buffer region.\n * @example\n * Create a DataView over an existing buffer.\n * ```ts\n * createView(new Uint8Array(4));\n * ```\n */\nexport function createView(arr: TArg<TypedArray>): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/**\n * Rotate-right operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the right.\n * ```ts\n * rotr(0x12345678, 8);\n * ```\n */\nexport function rotr(word: number, shift: number): number {\n return (word << (32 - shift)) | (word >>> shift);\n}\n\n/**\n * Rotate-left operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the left.\n * ```ts\n * rotl(0x12345678, 8);\n * ```\n */\nexport function rotl(word: number, shift: number): number {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n\n/** Whether the current platform is little-endian. */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/**\n * Byte-swap operation for uint32 values.\n * @param word - source word\n * @returns Word with reversed byte order.\n * @example\n * Reverse the byte order of a 32-bit word.\n * ```ts\n * byteSwap(0x11223344);\n * ```\n */\nexport function byteSwap(word: number): number {\n return (\n ((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff)\n );\n}\n/**\n * Conditionally byte-swaps one 32-bit word on big-endian platforms.\n * @param n - source word\n * @returns Original or byte-swapped word depending on platform endianness.\n * @example\n * Normalize a 32-bit word for host endianness.\n * ```ts\n * swap8IfBE(0x11223344);\n * ```\n */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n) >>> 0;\n\n/**\n * Byte-swaps every word of a Uint32Array in place.\n * @param arr - array to mutate\n * @returns The same array after mutation; callers pass live state arrays here.\n * @example\n * Reverse the byte order of every word in place.\n * ```ts\n * byteSwap32(new Uint32Array([0x11223344]));\n * ```\n */\nexport function byteSwap32(arr: TArg<Uint32Array>): TRet<Uint32Array> {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr as TRet<Uint32Array>;\n}\n\n/**\n * Conditionally byte-swaps a Uint32Array on big-endian platforms.\n * @param u - array to normalize for host endianness\n * @returns Original or byte-swapped array depending on platform endianness.\n * On big-endian runtimes this mutates `u` in place via `byteSwap32(...)`.\n * @example\n * Normalize a word array for host endianness.\n * ```ts\n * swap32IfBE(new Uint32Array([0x11223344]));\n * ```\n */\nexport const swap32IfBE: (u: TArg<Uint32Array>) => TRet<Uint32Array> = isLE\n ? (u: TArg<Uint32Array>) => u as TRet<Uint32Array>\n : byteSwap32;\n\n// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex\nconst hasHexBuiltin: boolean = /* @__PURE__ */ (() =>\n // @ts-ignore\n typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();\n\n// Array where index 0xf0 (240) is mapped to string 'f0'\nconst hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>\n i.toString(16).padStart(2, '0')\n);\n\n/**\n * Convert byte array to hex string.\n * Uses the built-in function when available and assumes it matches the tested\n * fallback semantics.\n * @param bytes - bytes to encode\n * @returns Lowercase hexadecimal string.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Convert bytes to lowercase hexadecimal.\n * ```ts\n * bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123'\n * ```\n */\nexport function bytesToHex(bytes: TArg<Uint8Array>): string {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin) return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n\n// We use optimized technique to convert hex string to byte array\nconst asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const;\nfunction asciiToBase16(ch: number): number | undefined {\n if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48\n if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10)\n if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10)\n return;\n}\n\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @param hex - hexadecimal string to decode\n * @returns Decoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Decode lowercase hexadecimal into bytes.\n * ```ts\n * hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n * ```\n */\nexport function hexToBytes(hex: string): TRet<Uint8Array> {\n if (typeof hex !== 'string') throw new TypeError('hex string expected, got ' + typeof hex);\n if (hasHexBuiltin) {\n try {\n return (Uint8Array as any).fromHex(hex);\n } catch (error) {\n if (error instanceof SyntaxError) throw new RangeError(error.message);\n throw error;\n }\n }\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new RangeError('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi));\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1));\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new RangeError(\n 'hex string expected, got non-hex character \"' + char + '\" at index ' + hi\n );\n }\n array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163\n }\n return array;\n}\n\n/**\n * There is no setImmediate in browser and setTimeout is slow.\n * This yields to the Promise/microtask scheduler queue, not to timers or the\n * full macrotask event loop.\n * @example\n * Yield to the next scheduler tick.\n * ```ts\n * await nextTick();\n * ```\n */\nexport const nextTick = async (): Promise<void> => {};\n\n/**\n * Returns control to the Promise/microtask scheduler every `tick`\n * milliseconds to avoid blocking long loops.\n * @param iters - number of loop iterations to run\n * @param tick - maximum time slice in milliseconds\n * @param cb - callback executed on each iteration\n * @example\n * Run a loop that periodically yields back to the event loop.\n * ```ts\n * await asyncLoop(2, 0, () => {});\n * ```\n */\nexport async function asyncLoop(\n iters: number,\n tick: number,\n cb: (i: number) => void\n): Promise<void> {\n let ts = Date.now();\n for (let i = 0; i < iters; i++) {\n cb(i);\n // Date.now() is not monotonic, so in case if clock goes backwards we return return control too\n const diff = Date.now() - ts;\n if (diff >= 0 && diff < tick) continue;\n await nextTick();\n ts += diff;\n }\n}\n\n// Global symbols, but ts doesn't see them: https://github.com/microsoft/TypeScript/issues/31535\ndeclare const TextEncoder: any;\n\n/**\n * Converts string to bytes using UTF8 encoding.\n * Built-in doesn't validate input to be string: we do the check.\n * Non-ASCII details are delegated to the platform `TextEncoder`.\n * @param str - string to encode\n * @returns UTF-8 encoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Encode a string as UTF-8 bytes.\n * ```ts\n * utf8ToBytes('abc'); // Uint8Array.from([97, 98, 99])\n * ```\n */\nexport function utf8ToBytes(str: string): TRet<Uint8Array> {\n if (typeof str !== 'string') throw new TypeError('string expected');\n return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809\n}\n\n/** KDFs can accept string or Uint8Array for user convenience. */\nexport type KDFInput = string | Uint8Array;\n\n/**\n * Helper for KDFs: consumes Uint8Array or string.\n * String inputs are UTF-8 encoded; byte-array inputs stay aliased to the caller buffer.\n * @param data - user-provided KDF input\n * @param errorTitle - label included in thrown errors\n * @returns Byte representation of the input.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Normalize KDF input to bytes.\n * ```ts\n * kdfInputToBytes('password');\n * ```\n */\nexport function kdfInputToBytes(data: TArg<KDFInput>, errorTitle = ''): TRet<Uint8Array> {\n if (typeof data === 'string') return utf8ToBytes(data);\n return abytes(data, undefined, errorTitle);\n}\n\n/**\n * Copies several Uint8Arrays into one.\n * @param arrays - arrays to concatenate\n * @returns Concatenated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Concatenate multiple byte arrays.\n * ```ts\n * concatBytes(new Uint8Array([1]), new Uint8Array([2]));\n * ```\n */\nexport function concatBytes(...arrays: TArg<Uint8Array[]>): TRet<Uint8Array> {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res;\n}\n\ntype EmptyObj = {};\n/**\n * Merges default options and passed options.\n * @param defaults - base option object\n * @param opts - user overrides\n * @returns Merged option object. The merge mutates `defaults` in place.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Merge user overrides onto default options.\n * ```ts\n * checkOpts({ dkLen: 32 }, { asyncTick: 10 });\n * ```\n */\nexport function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(\n defaults: T1,\n opts?: T2\n): T1 & T2 {\n if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')\n throw new TypeError('options must be object or undefined');\n const merged = Object.assign(defaults, opts);\n return merged as T1 & T2;\n}\n\n/** Common interface for all hash instances. */\nexport interface Hash<T> {\n /** Bytes processed per compression block. */\n blockLen: number;\n /** Bytes produced by `digest()`. */\n outputLen: number;\n /** Whether the instance supports XOF-style variable-length output via `xof()` / `xofInto()`. */\n canXOF: boolean;\n /**\n * Absorbs more message bytes into the running hash state.\n * @param buf - message chunk to absorb\n * @returns The same hash instance for chaining.\n */\n update(buf: TArg<Uint8Array>): this;\n /**\n * Finalizes the hash into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Nothing. Implementations write into `buf` in place.\n */\n digestInto(buf: TArg<Uint8Array>): void;\n /**\n * Finalizes the hash and returns a freshly allocated digest.\n * @returns Digest bytes.\n */\n digest(): TRet<Uint8Array>;\n /** Wipes internal state and makes the instance unusable. */\n destroy(): void;\n /**\n * Copies the current hash state into an existing or new instance.\n * @param to - Optional destination instance to reuse.\n * @returns Cloned hash state.\n */\n _cloneInto(to?: T): T;\n /**\n * Creates an independent copy of the current hash state.\n * @returns Cloned hash instance.\n */\n clone(): T;\n}\n\n/** Pseudorandom generator interface. */\nexport interface PRG {\n /**\n * Mixes more entropy into the generator state.\n * @param seed - fresh entropy bytes\n * @returns Nothing. Implementations update internal state in place.\n */\n addEntropy(seed: TArg<Uint8Array>): void;\n /**\n * Generates pseudorandom output bytes.\n * @param length - number of bytes to generate\n * @returns Generated pseudorandom bytes.\n */\n randomBytes(length: number): TRet<Uint8Array>;\n /** Wipes generator state and makes the instance unusable. */\n clean(): void;\n}\n\n/**\n * XOF: streaming API to read digest in chunks.\n * Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.\n * When hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot\n * destroy state, next call can require more bytes.\n */\nexport type HashXOF<T extends Hash<T>> = Hash<T> & {\n /**\n * Reads more bytes from the XOF stream.\n * @param bytes - number of bytes to read\n * @returns Requested digest bytes.\n */\n xof(bytes: number): TRet<Uint8Array>;\n /**\n * Reads more bytes from the XOF stream into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Filled output buffer.\n */\n xofInto(buf: TArg<Uint8Array>): TRet<Uint8Array>;\n};\n\n/** Hash constructor or factory type. */\nexport type HasherCons<T, Opts = undefined> = Opts extends undefined ? () => T : (opts?: Opts) => T;\n/** Optional hash metadata. */\nexport type HashInfo = {\n /** DER-encoded object identifier bytes for the hash algorithm. */\n oid?: TRet<Uint8Array>;\n};\n/** Callable hash function type. */\nexport type CHash<T extends Hash<T> = Hash<any>, Opts = undefined> = {\n /** Digest size in bytes. */\n outputLen: number;\n /** Input block size in bytes. */\n blockLen: number;\n /** Whether `.create()` returns a hash instance that can be used as an XOF stream. */\n canXOF: boolean;\n} & HashInfo &\n (Opts extends undefined\n ? {\n (msg: TArg<Uint8Array>): TRet<Uint8Array>;\n create(): T;\n }\n : {\n (msg: TArg<Uint8Array>, opts?: TArg<Opts>): TRet<Uint8Array>;\n create(opts?: Opts): T;\n });\n/** Callable extendable-output hash function type. */\nexport type CHashXOF<T extends HashXOF<T> = HashXOF<any>, Opts = undefined> = CHash<T, Opts>;\n\n/**\n * Creates a callable hash function from a stateful class constructor.\n * @param hashCons - hash constructor or factory\n * @param info - optional metadata such as DER OID\n * @returns Frozen callable hash wrapper with `.create()`.\n * Wrapper construction eagerly calls `hashCons(undefined)` once to read\n * `outputLen` / `blockLen`, so constructor side effects happen at module\n * init time.\n * @example\n * Wrap a stateful hash constructor into a callable helper.\n * ```ts\n * import { createHasher } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const wrapped = createHasher(sha256.create, { oid: sha256.oid });\n * wrapped(new Uint8Array([1]));\n * ```\n */\nexport function createHasher<T extends Hash<T>, Opts = undefined>(\n hashCons: HasherCons<T, Opts>,\n info: TArg<HashInfo> = {}\n): TRet<CHash<T, Opts>> {\n const hashC: any = (msg: TArg<Uint8Array>, opts?: TArg<Opts>) =>\n hashCons(opts as Opts)\n .update(msg)\n .digest();\n const tmp = hashCons(undefined);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.canXOF = tmp.canXOF;\n hashC.create = (opts?: Opts) => hashCons(opts);\n Object.assign(hashC, info);\n return Object.freeze(hashC) as TRet<CHash<T, Opts>>;\n}\n\n/**\n * Cryptographically secure PRNG backed by `crypto.getRandomValues`.\n * @param bytesLength - number of random bytes to generate\n * @returns Random bytes.\n * The platform `getRandomValues()` implementation still defines any\n * single-call length cap, and this helper rejects oversize requests\n * with a stable library `RangeError` instead of host-specific errors.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If the current runtime does not provide `crypto.getRandomValues`. {@link Error}\n * @example\n * Generate a fresh random key or nonce.\n * ```ts\n * const key = randomBytes(16);\n * ```\n */\nexport function randomBytes(bytesLength = 32): TRet<Uint8Array> {\n // Match the repo's other length-taking helpers instead of relying on Uint8Array coercion.\n anumber(bytesLength, 'bytesLength');\n const cr = typeof globalThis === 'object' ? (globalThis as any).crypto : null;\n if (typeof cr?.getRandomValues !== 'function')\n throw new Error('crypto.getRandomValues must be defined');\n // Web Cryptography API Level 2 \u00A710.1.1:\n // if `byteLength > 65536`, throw `QuotaExceededError`.\n // Keep the guard explicit so callers can see the quota in code\n // instead of discovering it by reading the spec or host errors.\n // This wrapper surfaces the same quota as a stable library RangeError.\n if (bytesLength > 65536)\n throw new RangeError(`\"bytesLength\" expected <= 65536, got ${bytesLength}`);\n return cr.getRandomValues(new Uint8Array(bytesLength));\n}\n\n/**\n * Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`.\n * @param suffix - final OID byte for the selected hash.\n * The helper accepts any byte even though only the documented NIST hash\n * suffixes are meaningful downstream.\n * @returns Object containing the DER-encoded OID.\n * @example\n * Build OID metadata for a NIST hash.\n * ```ts\n * oidNist(0x01);\n * ```\n */\nexport const oidNist = (suffix: number): TRet<Required<HashInfo>> => ({\n // Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.\n // Larger suffix values would need base-128 OID encoding and a different length byte.\n oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),\n});\n", "/**\n * Internal Merkle-Damgard hash utils.\n * @module\n */\nimport {\n abytes,\n aexists,\n aoutput,\n clean,\n createView,\n type Hash,\n type TArg,\n type TRet,\n} from './utils.ts';\n\n/**\n * Shared 32-bit conditional boolean primitive reused by SHA-256, SHA-1, and MD5 `F`.\n * Returns bits from `b` when `a` is set, otherwise from `c`.\n * The XOR form is equivalent to MD5's `F(X,Y,Z) = XY v not(X)Z` because the masked terms never\n * set the same bit.\n * @param a - selector word\n * @param b - word chosen when selector bit is set\n * @param c - word chosen when selector bit is clear\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit choice primitive.\n * ```ts\n * Chi(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Chi(a: number, b: number, c: number): number {\n return (a & b) ^ (~a & c);\n}\n\n/**\n * Shared 32-bit majority primitive reused by SHA-256 and SHA-1.\n * Returns bits shared by at least two inputs.\n * @param a - first input word\n * @param b - second input word\n * @param c - third input word\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit majority primitive.\n * ```ts\n * Maj(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Maj(a: number, b: number, c: number): number {\n return (a & b) ^ (a & c) ^ (b & c);\n}\n\n/**\n * Merkle-Damgard hash construction base class.\n * Could be used to create MD5, RIPEMD, SHA1, SHA2.\n * Accepts only byte-aligned `Uint8Array` input, even when the underlying spec describes bit\n * strings with partial-byte tails.\n * @param blockLen - internal block size in bytes\n * @param outputLen - digest size in bytes\n * @param padOffset - trailing length field size in bytes\n * @param isLE - whether length and state words are encoded in little-endian\n * @example\n * Use a concrete subclass to get the shared Merkle-Damgard update/digest flow.\n * ```ts\n * import { _SHA1 } from '@noble/hashes/legacy.js';\n * const hash = new _SHA1();\n * hash.update(new Uint8Array([97, 98, 99]));\n * hash.digest();\n * ```\n */\nexport abstract class HashMD<T extends HashMD<T>> implements Hash<T> {\n // Subclasses must treat `buf` as read-only: `update()` may pass a direct view over caller input\n // when it can process whole blocks without buffering first.\n protected abstract process(buf: DataView, offset: number): void;\n protected abstract get(): number[];\n protected abstract set(...args: number[]): void;\n abstract destroy(): void;\n protected abstract roundClean(): void;\n\n readonly blockLen: number;\n readonly outputLen: number;\n readonly canXOF = false;\n readonly padOffset: number;\n readonly isLE: boolean;\n\n // For partial updates less than block size\n protected buffer: Uint8Array;\n protected view: DataView;\n protected finished = false;\n protected length = 0;\n protected pos = 0;\n protected destroyed = false;\n\n constructor(blockLen: number, outputLen: number, padOffset: number, isLE: boolean) {\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.padOffset = padOffset;\n this.isLE = isLE;\n this.buffer = new Uint8Array(blockLen);\n this.view = createView(this.buffer);\n }\n update(data: TArg<Uint8Array>): this {\n aexists(this);\n abytes(data);\n const { view, buffer, blockLen } = this;\n const len = data.length;\n for (let pos = 0; pos < len; ) {\n const take = Math.min(blockLen - this.pos, len - pos);\n // Fast path only when there is no buffered partial block: `take === blockLen` implies\n // `this.pos === 0`, so we can process full blocks directly from the input view.\n if (take === blockLen) {\n const dataView = createView(data);\n for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);\n continue;\n }\n buffer.set(data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n pos += take;\n if (this.pos === blockLen) {\n this.process(view, 0);\n this.pos = 0;\n }\n }\n this.length += data.length;\n this.roundClean();\n return this;\n }\n digestInto(out: TArg<Uint8Array>): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n // Padding\n // We can avoid allocation of buffer for padding completely if it\n // was previously not allocated here. But it won't change performance.\n const { buffer, view, blockLen, isLE } = this;\n let { pos } = this;\n // append the bit '1' to the message\n buffer[pos++] = 0b10000000;\n clean(this.buffer.subarray(pos));\n // we have less than padOffset left in buffer, so we cannot put length in\n // current block, need process it and pad again\n if (this.padOffset > blockLen - pos) {\n this.process(view, 0);\n pos = 0;\n }\n // Pad until full block byte with zeros\n for (let i = pos; i < blockLen; i++) buffer[i] = 0;\n // `padOffset` reserves the whole length field. For SHA-384/512 the high 64 bits stay zero from\n // the padding fill above, and JS will overflow before user input can make that half non-zero.\n // So we only need to write the low 64 bits here.\n view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);\n this.process(view, 0);\n const oview = createView(out);\n const len = this.outputLen;\n // NOTE: we do division by 4 later, which must be fused in single op with modulo by JIT\n if (len % 4) throw new Error('_sha2: outputLen must be aligned to 32bit');\n const outLen = len / 4;\n const state = this.get();\n if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state');\n for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);\n }\n digest(): TRet<Uint8Array> {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n // Copy before destroy(): subclasses wipe `buffer` during cleanup, but `digest()` must return\n // fresh bytes to the caller.\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res as TRet<Uint8Array>;\n }\n _cloneInto(to?: T): T {\n to ||= new (this.constructor as any)() as T;\n to.set(...this.get());\n const { blockLen, buffer, length, finished, destroyed, pos } = this;\n to.destroyed = destroyed;\n to.finished = finished;\n to.length = length;\n to.pos = pos;\n // Only partial-block bytes need copying: when `length % blockLen === 0`, `pos === 0` and\n // later `update()` / `digestInto()` overwrite `to.buffer` from the start before reading it.\n if (length % blockLen) to.buffer.set(buffer);\n return to as unknown as any;\n }\n clone(): T {\n return this._cloneInto();\n }\n}\n\n/**\n * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.\n * Check out `test/misc/sha2-gen-iv.js` for recomputation guide.\n */\n\n/** Initial SHA256 state from RFC 6234 \u00A76.1: the first 32 bits of the fractional parts of the\n * square roots of the first eight prime numbers. Exported as a shared table; callers must treat\n * it as read-only because constructors copy words from it by index. */\nexport const SHA256_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Initial SHA224 state `H(0)` from RFC 6234 \u00A76.1. Exported as a shared table; callers must\n * treat it as read-only because constructors copy words from it by index. */\nexport const SHA224_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,\n]);\n\n/** Initial SHA384 state from RFC 6234 \u00A76.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the ninth\n * through sixteenth prime numbers. Exported as a shared table; callers must treat it as read-only\n * because constructors copy halves from it by index. */\nexport const SHA384_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xcbbb9d5d, 0xc1059ed8, 0x629a292a, 0x367cd507, 0x9159015a, 0x3070dd17, 0x152fecd8, 0xf70e5939,\n 0x67332667, 0xffc00b31, 0x8eb44a87, 0x68581511, 0xdb0c2e0d, 0x64f98fa7, 0x47b5481d, 0xbefa4fa4,\n]);\n\n/** Initial SHA512 state from RFC 6234 \u00A76.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the first\n * eight prime numbers. Exported as a shared table; callers must treat it as read-only because\n * constructors copy halves from it by index. */\nexport const SHA512_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,\n 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,\n]);\n", "/**\n * SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.\n * SHA256 is the fastest hash implementable in JS, even faster than Blake3.\n * Check out {@link https://www.rfc-editor.org/rfc/rfc4634 | RFC 4634} and\n * {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf | FIPS 180-4}.\n * @module\n */\nimport { Chi, HashMD, Maj, SHA224_IV, SHA256_IV, SHA384_IV, SHA512_IV } from './_md.ts';\nimport * as u64 from './_u64.ts';\nimport { type CHash, clean, createHasher, oidNist, rotr, type TRet } from './utils.ts';\n\n/**\n * SHA-224 / SHA-256 round constants from RFC 6234 \u00A75.1: the first 32 bits\n * of the cube roots of the first 64 primes (2..311).\n */\n// prettier-ignore\nconst SHA256_K = /* @__PURE__ */ Uint32Array.from([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n]);\n\n/** Reusable SHA-224 / SHA-256 message schedule buffer `W_t` from RFC 6234 \u00A76.2 step 1. */\nconst SHA256_W = /* @__PURE__ */ new Uint32Array(64);\n\n/** Internal SHA-224 / SHA-256 compression engine from RFC 6234 \u00A76.2. */\nabstract class SHA2_32B<T extends SHA2_32B<T>> extends HashMD<T> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected abstract A: number;\n protected abstract B: number;\n protected abstract C: number;\n protected abstract D: number;\n protected abstract E: number;\n protected abstract F: number;\n protected abstract G: number;\n protected abstract H: number;\n\n constructor(outputLen: number) {\n super(64, outputLen, 8, false);\n }\n protected get(): [number, number, number, number, number, number, number, number] {\n const { A, B, C, D, E, F, G, H } = this;\n return [A, B, C, D, E, F, G, H];\n }\n // prettier-ignore\n protected set(\n A: number, B: number, C: number, D: number, E: number, F: number, G: number, H: number\n ): void {\n this.A = A | 0;\n this.B = B | 0;\n this.C = C | 0;\n this.D = D | 0;\n this.E = E | 0;\n this.F = F | 0;\n this.G = G | 0;\n this.H = H | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);\n for (let i = 16; i < 64; i++) {\n const W15 = SHA256_W[i - 15];\n const W2 = SHA256_W[i - 2];\n const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);\n const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);\n SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;\n }\n // Compression function main loop, 64 rounds\n let { A, B, C, D, E, F, G, H } = this;\n for (let i = 0; i < 64; i++) {\n const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);\n const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);\n const T2 = (sigma0 + Maj(A, B, C)) | 0;\n H = G;\n G = F;\n F = E;\n E = (D + T1) | 0;\n D = C;\n C = B;\n B = A;\n A = (T1 + T2) | 0;\n }\n // Add the compressed chunk to the current hash value\n A = (A + this.A) | 0;\n B = (B + this.B) | 0;\n C = (C + this.C) | 0;\n D = (D + this.D) | 0;\n E = (E + this.E) | 0;\n F = (F + this.F) | 0;\n G = (G + this.G) | 0;\n H = (H + this.H) | 0;\n this.set(A, B, C, D, E, F, G, H);\n }\n protected roundClean(): void {\n clean(SHA256_W);\n }\n destroy(): void {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n this.set(0, 0, 0, 0, 0, 0, 0, 0);\n clean(this.buffer);\n }\n}\n\n/** Internal SHA-256 hash class grounded in RFC 6234 \u00A76.2. */\nexport class _SHA256 extends SHA2_32B<_SHA256> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected A: number = SHA256_IV[0] | 0;\n protected B: number = SHA256_IV[1] | 0;\n protected C: number = SHA256_IV[2] | 0;\n protected D: number = SHA256_IV[3] | 0;\n protected E: number = SHA256_IV[4] | 0;\n protected F: number = SHA256_IV[5] | 0;\n protected G: number = SHA256_IV[6] | 0;\n protected H: number = SHA256_IV[7] | 0;\n constructor() {\n super(32);\n }\n}\n\n/** Internal SHA-224 hash class grounded in RFC 6234 \u00A76.2 and \u00A78.5. */\nexport class _SHA224 extends SHA2_32B<_SHA224> {\n protected A: number = SHA224_IV[0] | 0;\n protected B: number = SHA224_IV[1] | 0;\n protected C: number = SHA224_IV[2] | 0;\n protected D: number = SHA224_IV[3] | 0;\n protected E: number = SHA224_IV[4] | 0;\n protected F: number = SHA224_IV[5] | 0;\n protected G: number = SHA224_IV[6] | 0;\n protected H: number = SHA224_IV[7] | 0;\n constructor() {\n super(28);\n }\n}\n\n// SHA2-512 is slower than sha256 in js because u64 operations are slow.\n\n// SHA-384 / SHA-512 round constants from RFC 6234 \u00A75.2:\n// 80 full 64-bit words split into high/low halves.\n// prettier-ignore\nconst K512 = /* @__PURE__ */ (() => u64.split([\n '0x428a2f98d728ae22', '0x7137449123ef65cd', '0xb5c0fbcfec4d3b2f', '0xe9b5dba58189dbbc',\n '0x3956c25bf348b538', '0x59f111f1b605d019', '0x923f82a4af194f9b', '0xab1c5ed5da6d8118',\n '0xd807aa98a3030242', '0x12835b0145706fbe', '0x243185be4ee4b28c', '0x550c7dc3d5ffb4e2',\n '0x72be5d74f27b896f', '0x80deb1fe3b1696b1', '0x9bdc06a725c71235', '0xc19bf174cf692694',\n '0xe49b69c19ef14ad2', '0xefbe4786384f25e3', '0x0fc19dc68b8cd5b5', '0x240ca1cc77ac9c65',\n '0x2de92c6f592b0275', '0x4a7484aa6ea6e483', '0x5cb0a9dcbd41fbd4', '0x76f988da831153b5',\n '0x983e5152ee66dfab', '0xa831c66d2db43210', '0xb00327c898fb213f', '0xbf597fc7beef0ee4',\n '0xc6e00bf33da88fc2', '0xd5a79147930aa725', '0x06ca6351e003826f', '0x142929670a0e6e70',\n '0x27b70a8546d22ffc', '0x2e1b21385c26c926', '0x4d2c6dfc5ac42aed', '0x53380d139d95b3df',\n '0x650a73548baf63de', '0x766a0abb3c77b2a8', '0x81c2c92e47edaee6', '0x92722c851482353b',\n '0xa2bfe8a14cf10364', '0xa81a664bbc423001', '0xc24b8b70d0f89791', '0xc76c51a30654be30',\n '0xd192e819d6ef5218', '0xd69906245565a910', '0xf40e35855771202a', '0x106aa07032bbd1b8',\n '0x19a4c116b8d2d0c8', '0x1e376c085141ab53', '0x2748774cdf8eeb99', '0x34b0bcb5e19b48a8',\n '0x391c0cb3c5c95a63', '0x4ed8aa4ae3418acb', '0x5b9cca4f7763e373', '0x682e6ff3d6b2b8a3',\n '0x748f82ee5defb2fc', '0x78a5636f43172f60', '0x84c87814a1f0ab72', '0x8cc702081a6439ec',\n '0x90befffa23631e28', '0xa4506cebde82bde9', '0xbef9a3f7b2c67915', '0xc67178f2e372532b',\n '0xca273eceea26619c', '0xd186b8c721c0c207', '0xeada7dd6cde0eb1e', '0xf57d4f7fee6ed178',\n '0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b',\n '0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c',\n '0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817'\n].map(n => BigInt(n))))();\nconst SHA512_Kh = /* @__PURE__ */ (() => K512[0])();\nconst SHA512_Kl = /* @__PURE__ */ (() => K512[1])();\n\n// Reusable high-half schedule buffer for the RFC 6234 \u00A76.4 64-bit `W_t` words.\nconst SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);\n// Reusable low-half schedule buffer for the RFC 6234 \u00A76.4 64-bit `W_t` words.\nconst SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);\n\n/** Internal SHA-384 / SHA-512 compression engine from RFC 6234 \u00A76.4. */\nabstract class SHA2_64B<T extends SHA2_64B<T>> extends HashMD<T> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n // h -- high 32 bits, l -- low 32 bits\n protected abstract Ah: number;\n protected abstract Al: number;\n protected abstract Bh: number;\n protected abstract Bl: number;\n protected abstract Ch: number;\n protected abstract Cl: number;\n protected abstract Dh: number;\n protected abstract Dl: number;\n protected abstract Eh: number;\n protected abstract El: number;\n protected abstract Fh: number;\n protected abstract Fl: number;\n protected abstract Gh: number;\n protected abstract Gl: number;\n protected abstract Hh: number;\n protected abstract Hl: number;\n\n constructor(outputLen: number) {\n super(128, outputLen, 16, false);\n }\n // prettier-ignore\n protected get(): [\n number, number, number, number, number, number, number, number,\n number, number, number, number, number, number, number, number\n ] {\n const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];\n }\n // prettier-ignore\n protected set(\n Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number,\n Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number\n ): void {\n this.Ah = Ah | 0;\n this.Al = Al | 0;\n this.Bh = Bh | 0;\n this.Bl = Bl | 0;\n this.Ch = Ch | 0;\n this.Cl = Cl | 0;\n this.Dh = Dh | 0;\n this.Dl = Dl | 0;\n this.Eh = Eh | 0;\n this.El = El | 0;\n this.Fh = Fh | 0;\n this.Fl = Fl | 0;\n this.Gh = Gh | 0;\n this.Gl = Gl | 0;\n this.Hh = Hh | 0;\n this.Hl = Hl | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) {\n SHA512_W_H[i] = view.getUint32(offset);\n SHA512_W_L[i] = view.getUint32((offset += 4));\n }\n for (let i = 16; i < 80; i++) {\n // s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7)\n const W15h = SHA512_W_H[i - 15] | 0;\n const W15l = SHA512_W_L[i - 15] | 0;\n const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);\n const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7);\n // s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6)\n const W2h = SHA512_W_H[i - 2] | 0;\n const W2l = SHA512_W_L[i - 2] | 0;\n const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);\n const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);\n // SHA512_W[i] = s0 + s1 + SHA512_W[i - 7] + SHA512_W[i - 16];\n const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);\n const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);\n SHA512_W_H[i] = SUMh | 0;\n SHA512_W_L[i] = SUMl | 0;\n }\n let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n // Compression function main loop, 80 rounds\n for (let i = 0; i < 80; i++) {\n // S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)\n const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);\n const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41);\n //const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const CHIh = (Eh & Fh) ^ (~Eh & Gh);\n const CHIl = (El & Fl) ^ (~El & Gl);\n // T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i]\n // prettier-ignore\n const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);\n const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);\n const T1l = T1ll | 0;\n // S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)\n const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);\n const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39);\n const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch);\n const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl);\n Hh = Gh | 0;\n Hl = Gl | 0;\n Gh = Fh | 0;\n Gl = Fl | 0;\n Fh = Eh | 0;\n Fl = El | 0;\n ({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));\n Dh = Ch | 0;\n Dl = Cl | 0;\n Ch = Bh | 0;\n Cl = Bl | 0;\n Bh = Ah | 0;\n Bl = Al | 0;\n const All = u64.add3L(T1l, sigma0l, MAJl);\n Ah = u64.add3H(All, T1h, sigma0h, MAJh);\n Al = All | 0;\n }\n // Add the compressed chunk to the current hash value\n ({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));\n ({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));\n ({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));\n ({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));\n ({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));\n ({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));\n ({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));\n ({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));\n this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);\n }\n protected roundClean(): void {\n clean(SHA512_W_H, SHA512_W_L);\n }\n destroy(): void {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n clean(this.buffer);\n this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n }\n}\n\n/** Internal SHA-512 hash class grounded in RFC 6234 \u00A76.3 and \u00A76.4. */\nexport class _SHA512 extends SHA2_64B<_SHA512> {\n protected Ah: number = SHA512_IV[0] | 0;\n protected Al: number = SHA512_IV[1] | 0;\n protected Bh: number = SHA512_IV[2] | 0;\n protected Bl: number = SHA512_IV[3] | 0;\n protected Ch: number = SHA512_IV[4] | 0;\n protected Cl: number = SHA512_IV[5] | 0;\n protected Dh: number = SHA512_IV[6] | 0;\n protected Dl: number = SHA512_IV[7] | 0;\n protected Eh: number = SHA512_IV[8] | 0;\n protected El: number = SHA512_IV[9] | 0;\n protected Fh: number = SHA512_IV[10] | 0;\n protected Fl: number = SHA512_IV[11] | 0;\n protected Gh: number = SHA512_IV[12] | 0;\n protected Gl: number = SHA512_IV[13] | 0;\n protected Hh: number = SHA512_IV[14] | 0;\n protected Hl: number = SHA512_IV[15] | 0;\n\n constructor() {\n super(64);\n }\n}\n\n/** Internal SHA-384 hash class grounded in RFC 6234 \u00A76.3 and \u00A76.4. */\nexport class _SHA384 extends SHA2_64B<_SHA384> {\n protected Ah: number = SHA384_IV[0] | 0;\n protected Al: number = SHA384_IV[1] | 0;\n protected Bh: number = SHA384_IV[2] | 0;\n protected Bl: number = SHA384_IV[3] | 0;\n protected Ch: number = SHA384_IV[4] | 0;\n protected Cl: number = SHA384_IV[5] | 0;\n protected Dh: number = SHA384_IV[6] | 0;\n protected Dl: number = SHA384_IV[7] | 0;\n protected Eh: number = SHA384_IV[8] | 0;\n protected El: number = SHA384_IV[9] | 0;\n protected Fh: number = SHA384_IV[10] | 0;\n protected Fl: number = SHA384_IV[11] | 0;\n protected Gh: number = SHA384_IV[12] | 0;\n protected Gl: number = SHA384_IV[13] | 0;\n protected Hh: number = SHA384_IV[14] | 0;\n protected Hl: number = SHA384_IV[15] | 0;\n\n constructor() {\n super(48);\n }\n}\n\n/**\n * Truncated SHA512/256 and SHA512/224.\n * SHA512_IV is XORed with 0xa5a5a5a5a5a5a5a5, then used as \"intermediary\" IV of SHA512/t.\n * Then t hashes string to produce result IV.\n * See the repo-side derivation recipe in `test/misc/sha2-gen-iv.js`.\n * These IV literals are checked against that script rather than a dedicated\n * local RFC section.\n */\n\n/** SHA-512/224 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T224_IV = /* @__PURE__ */ Uint32Array.from([\n 0x8c3d37c8, 0x19544da2, 0x73e19966, 0x89dcd4d6, 0x1dfab7ae, 0x32ff9c82, 0x679dd514, 0x582f9fcf,\n 0x0f6d2b69, 0x7bd44da8, 0x77e36f73, 0x04c48942, 0x3f9d85a8, 0x6a1d36c8, 0x1112e6ad, 0x91d692a1,\n]);\n\n/** SHA-512/256 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T256_IV = /* @__PURE__ */ Uint32Array.from([\n 0x22312194, 0xfc2bf72c, 0x9f555fa3, 0xc84c64c2, 0x2393b86b, 0x6f53b151, 0x96387719, 0x5940eabd,\n 0x96283ee2, 0xa88effe3, 0xbe5e1e25, 0x53863992, 0x2b0199fc, 0x2c85b8aa, 0x0eb72ddc, 0x81c52ca2,\n]);\n\n/** Internal SHA-512/224 hash class using the derived `T224_IV` and the shared\n * RFC 6234 \u00A76.4 compression engine. */\nexport class _SHA512_224 extends SHA2_64B<_SHA512_224> {\n protected Ah: number = T224_IV[0] | 0;\n protected Al: number = T224_IV[1] | 0;\n protected Bh: number = T224_IV[2] | 0;\n protected Bl: number = T224_IV[3] | 0;\n protected Ch: number = T224_IV[4] | 0;\n protected Cl: number = T224_IV[5] | 0;\n protected Dh: number = T224_IV[6] | 0;\n protected Dl: number = T224_IV[7] | 0;\n protected Eh: number = T224_IV[8] | 0;\n protected El: number = T224_IV[9] | 0;\n protected Fh: number = T224_IV[10] | 0;\n protected Fl: number = T224_IV[11] | 0;\n protected Gh: number = T224_IV[12] | 0;\n protected Gl: number = T224_IV[13] | 0;\n protected Hh: number = T224_IV[14] | 0;\n protected Hl: number = T224_IV[15] | 0;\n\n constructor() {\n super(28);\n }\n}\n\n/** Internal SHA-512/256 hash class using the derived `T256_IV` and the shared\n * RFC 6234 \u00A76.4 compression engine. */\nexport class _SHA512_256 extends SHA2_64B<_SHA512_256> {\n protected Ah: number = T256_IV[0] | 0;\n protected Al: number = T256_IV[1] | 0;\n protected Bh: number = T256_IV[2] | 0;\n protected Bl: number = T256_IV[3] | 0;\n protected Ch: number = T256_IV[4] | 0;\n protected Cl: number = T256_IV[5] | 0;\n protected Dh: number = T256_IV[6] | 0;\n protected Dl: number = T256_IV[7] | 0;\n protected Eh: number = T256_IV[8] | 0;\n protected El: number = T256_IV[9] | 0;\n protected Fh: number = T256_IV[10] | 0;\n protected Fl: number = T256_IV[11] | 0;\n protected Gh: number = T256_IV[12] | 0;\n protected Gl: number = T256_IV[13] | 0;\n protected Hh: number = T256_IV[14] | 0;\n protected Hl: number = T256_IV[15] | 0;\n\n constructor() {\n super(32);\n }\n}\n\n/**\n * SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info:\n *\n * - Trying 2^128 hashes would get 50% chance of collision, using birthday attack.\n * - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.\n * - Each sha256 hash is executing 2^18 bit operations.\n * - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-256.\n * ```ts\n * sha256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha256: TRet<CHash<_SHA256>> = /* @__PURE__ */ createHasher(\n () => new _SHA256(),\n /* @__PURE__ */ oidNist(0x01)\n);\n/**\n * SHA2-224 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-224.\n * ```ts\n * sha224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha224: TRet<CHash<_SHA224>> = /* @__PURE__ */ createHasher(\n () => new _SHA224(),\n /* @__PURE__ */ oidNist(0x04)\n);\n\n/**\n * SHA2-512 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512.\n * ```ts\n * sha512(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512: TRet<CHash<_SHA512>> = /* @__PURE__ */ createHasher(\n () => new _SHA512(),\n /* @__PURE__ */ oidNist(0x03)\n);\n/**\n * SHA2-384 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-384.\n * ```ts\n * sha384(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha384: TRet<CHash<_SHA384>> = /* @__PURE__ */ createHasher(\n () => new _SHA384(),\n /* @__PURE__ */ oidNist(0x02)\n);\n\n/**\n * SHA2-512/256 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/256.\n * ```ts\n * sha512_256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_256: TRet<CHash<_SHA512_256>> = /* @__PURE__ */ createHasher(\n () => new _SHA512_256(),\n /* @__PURE__ */ oidNist(0x06)\n);\n/**\n * SHA2-512/224 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/224.\n * ```ts\n * sha512_224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_224: TRet<CHash<_SHA512_224>> = /* @__PURE__ */ createHasher(\n () => new _SHA512_224(),\n /* @__PURE__ */ oidNist(0x05)\n);\n", "/**\n * SHA-256 Assignment Hash (contract v2)\n *\n * The canonical deterministic hash for Traffical bucket assignment and\n * weighted selection. Every Traffical SDK (JS, PHP, Swift) and the edge\n * runtime must produce byte-identical results for the same inputs.\n *\n * Why SHA-256 over the previous FNV-1a:\n * - FNV-1a passed single-layer uniformity but FAILED cross-experiment\n * independence with realistic UUID/ULID unit keys and `lay_*` layer IDs:\n * assignment in one layer could predict assignment in another, breaking\n * orthogonal experiment assignment. SHA-256's avalanche behaviour removes\n * that correlation.\n *\n * Contract:\n * - Input encoding: UTF-8 bytes.\n * - hashInt = first 64 bits of SHA-256(digest) as an unsigned big-endian integer.\n */\n\nimport { sha256 } from \"@noble/hashes/sha2.js\";\n\n/**\n * Shared UTF-8 encoder. The canonical hashing domain is the UTF-8 byte\n * sequence of the input string (NOT UTF-16 code units), so that every\n * Traffical SDK produces identical results regardless of host string\n * representation.\n */\nconst UTF8_ENCODER = new TextEncoder();\n\n/**\n * The domain-separation + version prefix for the assignment hash contract.\n * Bumping `v2` would intentionally re-roll every assignment.\n */\nexport const ASSIGNMENT_HASH_VERSION = \"v2\";\n\n/**\n * Number of UTF-8 bytes in a string. Used for length-framing so that field\n * values containing the `:` or `|` separators cannot create ambiguous inputs.\n * Length is measured in UTF-8 bytes (not UTF-16 code units / grapheme\n * clusters) so the framing is identical across languages.\n */\nexport function utf8ByteLength(value: string): number {\n return UTF8_ENCODER.encode(value).length;\n}\n\n/**\n * Builds the canonical, length-framed, domain-separated assignment input\n * string for bucket computation.\n *\n * Format:\n * traffical:assignment:v2|u:<unitLen>:<unitKeyValue>|l:<layerLen>:<layerId>\n *\n * Example:\n * traffical:assignment:v2|u:26:01JZ0000008K7QF2J9M3P4X1A2B|l:12:lay_kjeJRrjh\n */\nexport function assignmentInput(unitKeyValue: string, layerId: string): string {\n const unitLen = utf8ByteLength(unitKeyValue);\n const layerLen = utf8ByteLength(layerId);\n return `traffical:assignment:${ASSIGNMENT_HASH_VERSION}|u:${unitLen}:${unitKeyValue}|l:${layerLen}:${layerId}`;\n}\n\n/**\n * Computes the SHA-256 digest of a string over its UTF-8 byte encoding.\n */\nexport function sha256Digest(input: string): Uint8Array {\n return sha256(UTF8_ENCODER.encode(input));\n}\n\n/**\n * Interprets the first 8 bytes of a digest as an unsigned big-endian 64-bit\n * integer. Returns a bigint so the full 64 bits are preserved exactly.\n */\nexport function hash64BE(digest: Uint8Array): bigint {\n let value = 0n;\n for (let i = 0; i < 8; i++) {\n value = (value << 8n) | BigInt(digest[i]);\n }\n return value;\n}\n\n/**\n * Convenience helper: the unsigned big-endian 64-bit hash of a string's\n * SHA-256 digest. This is the single primitive used for both bucket\n * assignment and weighted selection.\n */\nexport function hashInt64(input: string): bigint {\n return hash64BE(sha256Digest(input));\n}\n", "/**\n * Bucket Computation\n *\n * Deterministic bucket assignment for traffic splitting.\n * The bucket is computed from the SHA-256 v2 assignment hash:\n * digest = SHA256(assignmentInput(unitKeyValue, layerId))\n * hashInt = first 64 bits of digest, unsigned big-endian\n * bucket = hashInt % bucketCount\n *\n * This ensures:\n * - Same user always gets same bucket for a given layer\n * - Different layers have independent bucketing (orthogonality) \u2014 SHA-256's\n * avalanche behaviour passes cross-experiment independence where FNV-1a did not\n * - Deterministic results across SDK and server\n */\n\nimport { assignmentInput, sha256Digest, hash64BE } from \"./assignment-hash.js\";\n\n/**\n * Computes the bucket for a given unit and layer.\n *\n * @param unitKeyValue - The value of the unit key (e.g., userId value)\n * @param layerId - The layer ID for orthogonal bucketing\n * @param bucketCount - Total number of buckets (e.g., 1000)\n * @returns Bucket number in range [0, bucketCount - 1]\n */\nexport function computeBucket(\n unitKeyValue: string,\n layerId: string,\n bucketCount: number\n): number {\n const digest = sha256Digest(assignmentInput(unitKeyValue, layerId));\n const hashInt = hash64BE(digest);\n return Number(hashInt % BigInt(bucketCount));\n}\n\n/**\n * Checks if a bucket falls within a range.\n *\n * @param bucket - The computed bucket\n * @param range - [start, end] inclusive range\n * @returns True if bucket is in range\n */\nexport function isInBucketRange(\n bucket: number,\n range: [number, number]\n): boolean {\n return bucket >= range[0] && bucket <= range[1];\n}\n\n/**\n * Finds which allocation matches a given bucket.\n *\n * @param bucket - The computed bucket\n * @param allocations - Array of allocations with bucket ranges\n * @returns The matching allocation, or null if none match\n */\nexport function findMatchingAllocation<\n T extends { bucketRange: [number, number] }\n>(bucket: number, allocations: T[]): T | null {\n for (const allocation of allocations) {\n if (isInBucketRange(bucket, allocation.bucketRange)) {\n return allocation;\n }\n }\n return null;\n}\n\n/**\n * Converts a percentage to a bucket range.\n *\n * @param percentage - Traffic percentage (0-100)\n * @param bucketCount - Total buckets\n * @param startBucket - Starting bucket (default 0)\n * @returns [start, end] bucket range\n */\nexport function percentageToBucketRange(\n percentage: number,\n bucketCount: number,\n startBucket = 0\n): [number, number] {\n const bucketsNeeded = Math.floor((percentage / 100) * bucketCount);\n const endBucket = Math.min(startBucket + bucketsNeeded - 1, bucketCount - 1);\n return [startBucket, endBucket];\n}\n\n/**\n * Creates non-overlapping bucket ranges for multiple variants.\n *\n * @param percentages - Array of percentages that should sum to <= 100\n * @param bucketCount - Total buckets\n * @returns Array of [start, end] bucket ranges\n */\nexport function createBucketRanges(\n percentages: number[],\n bucketCount: number\n): [number, number][] {\n const ranges: [number, number][] = [];\n let currentBucket = 0;\n\n for (const percentage of percentages) {\n if (percentage <= 0) continue;\n\n const bucketsNeeded = Math.floor((percentage / 100) * bucketCount);\n if (bucketsNeeded > 0) {\n const endBucket = currentBucket + bucketsNeeded - 1;\n ranges.push([currentBucket, endBucket]);\n currentBucket = endBucket + 1;\n }\n }\n\n return ranges;\n}\n\n", "/**\n * Weighted Selection\n *\n * Deterministic weighted selection using the SHA-256 v2 assignment hash.\n * Used by both per-entity resolution and contextual bandit scoring.\n *\n * The seed string is hashed with SHA-256; the first 64 bits (unsigned,\n * big-endian) are reduced to a uniform value in [0, 1) via mod 2^53 (which\n * keeps full IEEE-754 double precision and stays within a signed 64-bit\n * integer for the PHP/Swift implementations).\n */\n\nimport { hashInt64 } from \"./assignment-hash.js\";\n\n/** 2^53 \u2014 the largest exactly-representable power of two in a JS number. */\nconst UNIFORM_MODULUS = 1n << 53n;\nconst UNIFORM_DENOMINATOR = 9007199254740992; // 2^53\n\n/**\n * Performs deterministic weighted selection using a hash.\n *\n * Uses the seed string to deterministically select an index based on weights.\n * This ensures the same seed always produces the same selection for a given\n * weight distribution.\n *\n * @param weights - Array of weights (should sum to 1.0)\n * @param seed - Seed string for deterministic hashing\n * @returns Index of selected entry\n */\nexport function weightedSelection(weights: number[], seed: string): number {\n if (weights.length === 0) return 0;\n if (weights.length === 1) return 0;\n\n const hashInt = hashInt64(seed);\n const random = Number(hashInt % UNIFORM_MODULUS) / UNIFORM_DENOMINATOR;\n\n let cumulative = 0;\n for (let i = 0; i < weights.length; i++) {\n cumulative += weights[i];\n if (random < cumulative) {\n return i;\n }\n }\n\n return weights.length - 1;\n}\n", "/**\n * Condition Evaluation\n *\n * Evaluates context predicates to determine policy eligibility.\n * Conditions are AND-ed together: all must match for a policy to apply.\n */\n\nimport type { Context, BundleCondition } from \"../types/index.js\";\n\n/**\n * Evaluates a single condition against a context.\n *\n * @param condition - The condition to evaluate\n * @param context - The context to evaluate against\n * @returns True if the condition matches\n */\nexport function evaluateCondition(\n condition: BundleCondition,\n context: Context\n): boolean {\n const { field, op, value, values } = condition;\n\n // Get the context value using dot notation\n const contextValue = getNestedValue(context, field);\n\n switch (op) {\n case \"eq\":\n return contextValue === value;\n\n case \"neq\":\n return contextValue !== value;\n\n case \"in\":\n if (!Array.isArray(values)) return false;\n return values.includes(contextValue);\n\n case \"nin\":\n if (!Array.isArray(values)) return true;\n return !values.includes(contextValue);\n\n case \"gt\":\n return (\n typeof contextValue === \"number\" && contextValue > (value as number)\n );\n\n case \"gte\":\n return (\n typeof contextValue === \"number\" && contextValue >= (value as number)\n );\n\n case \"lt\":\n return (\n typeof contextValue === \"number\" && contextValue < (value as number)\n );\n\n case \"lte\":\n return (\n typeof contextValue === \"number\" && contextValue <= (value as number)\n );\n\n case \"contains\":\n return (\n typeof contextValue === \"string\" &&\n typeof value === \"string\" &&\n contextValue.includes(value)\n );\n\n case \"startsWith\":\n return (\n typeof contextValue === \"string\" &&\n typeof value === \"string\" &&\n contextValue.startsWith(value)\n );\n\n case \"endsWith\":\n return (\n typeof contextValue === \"string\" &&\n typeof value === \"string\" &&\n contextValue.endsWith(value)\n );\n\n case \"regex\":\n if (typeof contextValue !== \"string\" || typeof value !== \"string\") {\n return false;\n }\n try {\n const regex = new RegExp(value);\n return regex.test(contextValue);\n } catch {\n return false;\n }\n\n case \"exists\":\n return contextValue !== undefined && contextValue !== null;\n\n case \"notExists\":\n return contextValue === undefined || contextValue === null;\n\n default:\n // Unknown operator, fail safe by not matching\n return false;\n }\n}\n\n/**\n * Evaluates all conditions against a context.\n * All conditions must match (AND logic).\n *\n * @param conditions - Array of conditions\n * @param context - The context to evaluate against\n * @returns True if all conditions match (or if there are no conditions)\n */\nexport function evaluateConditions(\n conditions: BundleCondition[],\n context: Context\n): boolean {\n // Empty conditions = always match\n if (conditions.length === 0) {\n return true;\n }\n\n // All conditions must match (AND)\n return conditions.every((condition) => evaluateCondition(condition, context));\n}\n\n/**\n * Gets a nested value from an object using dot notation.\n *\n * @example\n * getNestedValue({ user: { name: \"Alice\" } }, \"user.name\") // \"Alice\"\n * getNestedValue({ tags: [\"a\", \"b\"] }, \"tags.0\") // \"a\"\n */\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split(\".\");\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current === null || current === undefined) {\n return undefined;\n }\n\n if (typeof current === \"object\") {\n current = (current as Record<string, unknown>)[part];\n } else {\n return undefined;\n }\n }\n\n return current;\n}\n\n// =============================================================================\n// Condition Builder Helpers\n// =============================================================================\n\n/**\n * Creates an equality condition.\n */\nexport function eq(field: string, value: unknown): BundleCondition {\n return { field, op: \"eq\", value };\n}\n\n/**\n * Creates a not-equal condition.\n */\nexport function neq(field: string, value: unknown): BundleCondition {\n return { field, op: \"neq\", value };\n}\n\n/**\n * Creates an \"in\" condition.\n */\nexport function inValues(field: string, values: unknown[]): BundleCondition {\n return { field, op: \"in\", values };\n}\n\n/**\n * Creates a \"not in\" condition.\n */\nexport function notIn(field: string, values: unknown[]): BundleCondition {\n return { field, op: \"nin\", values };\n}\n\n/**\n * Creates a greater-than condition.\n */\nexport function gt(field: string, value: number): BundleCondition {\n return { field, op: \"gt\", value };\n}\n\n/**\n * Creates a greater-than-or-equal condition.\n */\nexport function gte(field: string, value: number): BundleCondition {\n return { field, op: \"gte\", value };\n}\n\n/**\n * Creates a less-than condition.\n */\nexport function lt(field: string, value: number): BundleCondition {\n return { field, op: \"lt\", value };\n}\n\n/**\n * Creates a less-than-or-equal condition.\n */\nexport function lte(field: string, value: number): BundleCondition {\n return { field, op: \"lte\", value };\n}\n\n/**\n * Creates a string contains condition.\n */\nexport function contains(field: string, value: string): BundleCondition {\n return { field, op: \"contains\", value };\n}\n\n/**\n * Creates a string starts-with condition.\n */\nexport function startsWith(field: string, value: string): BundleCondition {\n return { field, op: \"startsWith\", value };\n}\n\n/**\n * Creates a string ends-with condition.\n */\nexport function endsWith(field: string, value: string): BundleCondition {\n return { field, op: \"endsWith\", value };\n}\n\n/**\n * Creates a regex match condition.\n */\nexport function regex(field: string, pattern: string): BundleCondition {\n return { field, op: \"regex\", value: pattern };\n}\n\n/**\n * Creates an exists condition.\n */\nexport function exists(field: string): BundleCondition {\n return { field, op: \"exists\" };\n}\n\n/**\n * Creates a not-exists condition.\n */\nexport function notExists(field: string): BundleCondition {\n return { field, op: \"notExists\" };\n}\n\n", "/**\n * Contextual Bandit Scoring\n *\n * Pure functions for computing personalized allocation probabilities\n * from a trained linear contextual model. Used by the resolution engine\n * when a policy has a `contextualModel` field.\n *\n * Scoring pipeline:\n * 1. Compute linear score per allocation: intercept + SUM(coef * feature)\n * 2. Apply softmax with gamma temperature to get probabilities\n * 3. Enforce action probability floor (minimum exploration)\n * 4. Deterministic weighted selection via SHA-256 v2 hash\n */\n\nimport type {\n BundlePolicy,\n BundleAllocation,\n BundleAllocationCoefficients,\n BundleContextualModel,\n Context,\n} from \"../types/index.js\";\nimport { weightedSelection } from \"../hashing/weighted.js\";\n\n/**\n * Computes the linear score for a single allocation given context features.\n *\n * score = intercept\n * + SUM_numeric( coef_i * context[key_i] OR missing_i )\n * + SUM_categorical( values[context[key_j]] OR missing_j )\n */\nexport function computeAllocationScore(\n coefficients: BundleAllocationCoefficients,\n context: Context\n): number {\n let score = coefficients.intercept;\n\n for (const { key, coef, missing } of coefficients.numeric) {\n const value = context[key];\n score += typeof value === \"number\" ? coef * value : missing;\n }\n\n for (const { key, values, missing } of coefficients.categorical) {\n const value = context[key];\n const strValue = value !== undefined && value !== null ? String(value) : null;\n score +=\n strValue !== null && strValue in values ? values[strValue] : missing;\n }\n\n return score;\n}\n\n/**\n * Applies softmax with temperature (gamma) to convert raw scores to probabilities.\n *\n * Uses the numerically stable variant: subtract max before exponentiation.\n * Lower gamma makes the distribution more peaked (exploitative);\n * higher gamma makes it more uniform (explorative).\n */\nexport function softmaxProbabilities(\n scores: number[],\n gamma: number\n): number[] {\n if (scores.length === 0) return [];\n if (scores.length === 1) return [1.0];\n\n const safeGamma = Math.max(gamma, 1e-10);\n const scaled = scores.map((s) => s / safeGamma);\n const maxScaled = Math.max(...scaled);\n const exps = scaled.map((s) => Math.exp(s - maxScaled));\n const sumExp = exps.reduce((a, b) => a + b, 0);\n return exps.map((e) => e / sumExp);\n}\n\n/**\n * Enforces a minimum probability floor on each allocation and renormalizes.\n *\n * Any allocation below the floor is raised to it; surplus probability\n * is deducted proportionally from allocations above the floor.\n */\nexport function applyProbabilityFloor(\n probs: number[],\n floor: number\n): number[] {\n if (probs.length === 0) return [];\n if (floor <= 0) return probs;\n\n const n = probs.length;\n const maxFloor = 1.0 / n;\n const effectiveFloor = Math.min(floor, maxFloor);\n\n const floored = probs.map((p) => Math.max(p, effectiveFloor));\n const sum = floored.reduce((a, b) => a + b, 0);\n\n if (sum === 0) return Array(n).fill(1 / n);\n return floored.map((p) => p / sum);\n}\n\n/**\n * Resolves a contextual policy to a specific allocation using the trained model.\n *\n * Steps:\n * 1. Score each allocation using its coefficients (or defaultAllocationScore)\n * 2. Convert scores to probabilities via softmax(gamma)\n * 3. Apply the action probability floor\n * 4. Deterministically select using weightedSelection with a hash seed\n *\n * @returns The selected allocation, or null if the policy has no allocations\n */\nexport function resolveContextualPolicy(\n policy: BundlePolicy,\n context: Context,\n unitKeyValue: string\n): BundleAllocation | null {\n const model = policy.contextualModel;\n if (!model) return null;\n if (policy.allocations.length === 0) return null;\n\n const scores = computeContextualScores(model, policy.allocations, context);\n const probs = softmaxProbabilities(scores, model.gamma);\n const floored = applyProbabilityFloor(probs, model.actionProbabilityFloor);\n\n const seed = `ctx:${unitKeyValue}:${policy.id}`;\n const selectedIndex = weightedSelection(floored, seed);\n\n return policy.allocations[selectedIndex];\n}\n\n/**\n * Computes raw scores for all allocations in a policy.\n */\nfunction computeContextualScores(\n model: BundleContextualModel,\n allocations: BundleAllocation[],\n context: Context\n): number[] {\n return allocations.map((alloc) => {\n const coefficients = model.coefficients[alloc.name];\n if (!coefficients) return model.defaultAllocationScore;\n return computeAllocationScore(coefficients, context);\n });\n}\n", "/* @ts-self-types=\"./index.d.ts\" */\nimport { urlAlphabet as scopedUrlAlphabet } from './url-alphabet/index.js'\nexport { urlAlphabet } from './url-alphabet/index.js'\nexport let random = bytes => crypto.getRandomValues(new Uint8Array(bytes))\nexport let customRandom = (alphabet, defaultSize, getRandom) => {\n let mask = (2 << Math.log2(alphabet.length - 1)) - 1\n let step = -~((1.6 * mask * defaultSize) / alphabet.length)\n return (size = defaultSize) => {\n let id = ''\n while (true) {\n let bytes = getRandom(step)\n let j = step | 0\n while (j--) {\n id += alphabet[bytes[j] & mask] || ''\n if (id.length >= size) return id\n }\n }\n }\n}\nexport let customAlphabet = (alphabet, size = 21) =>\n customRandom(alphabet, size | 0, random)\nexport let nanoid = (size = 21) => {\n let id = ''\n let bytes = crypto.getRandomValues(new Uint8Array((size |= 0)))\n while (size--) {\n id += scopedUrlAlphabet[bytes[size] & 63]\n }\n return id\n}\n", "function createError(message) {\n const err = new Error(message);\n err.source = \"ulid\";\n return err;\n}\n// These values should NEVER change. If\n// they do, we're no longer making ulids!\nconst ENCODING = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\"; // Crockford's Base32\nconst ENCODING_LEN = ENCODING.length;\nconst TIME_MAX = Math.pow(2, 48) - 1;\nconst TIME_LEN = 10;\nconst RANDOM_LEN = 16;\nfunction replaceCharAt(str, index, char) {\n if (index > str.length - 1) {\n return str;\n }\n return str.substr(0, index) + char + str.substr(index + 1);\n}\nfunction incrementBase32(str) {\n let done = undefined;\n let index = str.length;\n let char;\n let charIndex;\n const maxCharIndex = ENCODING_LEN - 1;\n while (!done && index-- >= 0) {\n char = str[index];\n charIndex = ENCODING.indexOf(char);\n if (charIndex === -1) {\n throw createError(\"incorrectly encoded string\");\n }\n if (charIndex === maxCharIndex) {\n str = replaceCharAt(str, index, ENCODING[0]);\n continue;\n }\n done = replaceCharAt(str, index, ENCODING[charIndex + 1]);\n }\n if (typeof done === \"string\") {\n return done;\n }\n throw createError(\"cannot increment this string\");\n}\nfunction randomChar(prng) {\n let rand = Math.floor(prng() * ENCODING_LEN);\n if (rand === ENCODING_LEN) {\n rand = ENCODING_LEN - 1;\n }\n return ENCODING.charAt(rand);\n}\nfunction encodeTime(now, len) {\n if (isNaN(now)) {\n throw new Error(now + \" must be a number\");\n }\n if (now > TIME_MAX) {\n throw createError(\"cannot encode time greater than \" + TIME_MAX);\n }\n if (now < 0) {\n throw createError(\"time must be positive\");\n }\n if (Number.isInteger(Number(now)) === false) {\n throw createError(\"time must be an integer\");\n }\n let mod;\n let str = \"\";\n for (; len > 0; len--) {\n mod = now % ENCODING_LEN;\n str = ENCODING.charAt(mod) + str;\n now = (now - mod) / ENCODING_LEN;\n }\n return str;\n}\nfunction encodeRandom(len, prng) {\n let str = \"\";\n for (; len > 0; len--) {\n str = randomChar(prng) + str;\n }\n return str;\n}\nfunction decodeTime(id) {\n if (id.length !== TIME_LEN + RANDOM_LEN) {\n throw createError(\"malformed ulid\");\n }\n var time = id\n .substr(0, TIME_LEN)\n .split(\"\")\n .reverse()\n .reduce((carry, char, index) => {\n const encodingIndex = ENCODING.indexOf(char);\n if (encodingIndex === -1) {\n throw createError(\"invalid character found: \" + char);\n }\n return (carry += encodingIndex * Math.pow(ENCODING_LEN, index));\n }, 0);\n if (time > TIME_MAX) {\n throw createError(\"malformed ulid, timestamp too large\");\n }\n return time;\n}\nfunction detectPrng(allowInsecure = false, root) {\n if (!root) {\n root = typeof window !== \"undefined\" ? window : null;\n }\n const browserCrypto = root && (root.crypto || root.msCrypto);\n if (browserCrypto) {\n return () => {\n const buffer = new Uint8Array(1);\n browserCrypto.getRandomValues(buffer);\n return buffer[0] / 0xff;\n };\n }\n else {\n try {\n const nodeCrypto = require(\"crypto\");\n return () => nodeCrypto.randomBytes(1).readUInt8() / 0xff;\n }\n catch (e) { }\n }\n if (allowInsecure) {\n try {\n console.error(\"secure crypto unusable, falling back to insecure Math.random()!\");\n }\n catch (e) { }\n return () => Math.random();\n }\n throw createError(\"secure crypto unusable, insecure Math.random not allowed\");\n}\nfunction factory(currPrng) {\n if (!currPrng) {\n currPrng = detectPrng();\n }\n return function ulid(seedTime) {\n if (isNaN(seedTime)) {\n seedTime = Date.now();\n }\n return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN, currPrng);\n };\n}\nfunction monotonicFactory(currPrng) {\n if (!currPrng) {\n currPrng = detectPrng();\n }\n let lastTime = 0;\n let lastRandom;\n return function ulid(seedTime) {\n if (isNaN(seedTime)) {\n seedTime = Date.now();\n }\n if (seedTime <= lastTime) {\n const incrementedRandom = (lastRandom = incrementBase32(lastRandom));\n return encodeTime(lastTime, TIME_LEN) + incrementedRandom;\n }\n lastTime = seedTime;\n const newRandom = (lastRandom = encodeRandom(RANDOM_LEN, currPrng));\n return encodeTime(seedTime, TIME_LEN) + newRandom;\n };\n}\nconst ulid = factory();\n\nexport { decodeTime, detectPrng, encodeRandom, encodeTime, factory, incrementBase32, monotonicFactory, randomChar, replaceCharAt, ulid };\n", "/**\n * ID Generation Utilities\n *\n * Provides consistent ID generation with type prefixes for all entities and events.\n *\n * Entity IDs: 8-character NanoID with prefix (e.g., \"proj_hVF1cCoC\")\n * - Compact and URL-friendly\n * - 64^8 = 281 trillion combinations\n * - With DB constraints, collisions are handled via retry\n *\n * Event IDs: ULID with prefix (e.g., \"dec_01JHFK1WWMMG7M0XPEBTYXZEBW\")\n * - Lexicographically sortable (time-ordered)\n * - Contains millisecond timestamp for analytics\n */\n\nimport { customAlphabet } from \"nanoid\";\nimport { ulid } from \"ulid\";\n\n// =============================================================================\n// NanoID Configuration\n// =============================================================================\n\n/**\n * URL-safe alphabet for NanoID (64 characters).\n * Includes: 0-9, A-Z, a-z (no special chars to avoid URL encoding issues)\n */\nconst NANOID_ALPHABET = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n\n/**\n * Default length for entity IDs (without prefix).\n * 64^8 = 281,474,976,710,656 (~281 trillion) combinations.\n */\nconst ENTITY_ID_LENGTH = 8;\n\n/**\n * NanoID generator with custom alphabet.\n */\nconst nanoid = customAlphabet(NANOID_ALPHABET, ENTITY_ID_LENGTH);\n\n// =============================================================================\n// ID Prefixes\n// =============================================================================\n\n/**\n * Entity ID prefixes for each entity type.\n */\nexport type EntityIdPrefix =\n | \"org\" // Organization\n | \"proj\" // Project\n | \"env\" // Environment\n | \"ns\" // Namespace\n | \"lay\" // Layer\n | \"pol\" // Policy\n | \"alloc\" // Allocation\n | \"param\" // Parameter\n | \"dom\" // DOM Binding\n | \"ovr\" // Environment Override\n | \"ak\"; // API Key\n\n/**\n * Event ID prefixes for each event type.\n */\nexport type EventIdPrefix = \"dec\" | \"exp\" | \"trk\" | \"asn\";\n\n// =============================================================================\n// Generic ID Generation\n// =============================================================================\n\n/**\n * Generates a prefixed 8-char NanoID for the specified entity type.\n *\n * @param prefix - The entity type prefix\n * @returns A prefixed NanoID string (e.g., \"proj_hVF1cCoC\")\n */\nexport function generateEntityId(prefix: EntityIdPrefix): string {\n return `${prefix}_${nanoid()}`;\n}\n\n/**\n * Generates a prefixed ULID for the specified event type.\n * Events use ULID for time-sortability in analytics.\n *\n * @param prefix - The event type prefix\n * @returns A prefixed ULID string (e.g., \"dec_01JHFK1WWMMG7M0XPEBTYXZEBW\")\n */\nexport function generateEventId(prefix: EventIdPrefix): string {\n return `${prefix}_${ulid()}`;\n}\n\n/**\n * Generates a plain 8-char NanoID without prefix.\n * Used for internal IDs that don't need type identification.\n */\nexport function generateShortId(): string {\n return nanoid();\n}\n\n// =============================================================================\n// Entity ID Convenience Functions\n// =============================================================================\n\n/** Generates an Organization ID with \"org_\" prefix */\nexport function generateOrgId(): string {\n return generateEntityId(\"org\");\n}\n\n/** Generates a Project ID with \"proj_\" prefix */\nexport function generateProjectId(): string {\n return generateEntityId(\"proj\");\n}\n\n/** Generates an Environment ID with \"env_\" prefix */\nexport function generateEnvironmentId(): string {\n return generateEntityId(\"env\");\n}\n\n/** Generates a Namespace ID with \"ns_\" prefix */\nexport function generateNamespaceId(): string {\n return generateEntityId(\"ns\");\n}\n\n/** Generates a Layer ID with \"lay_\" prefix */\nexport function generateLayerId(): string {\n return generateEntityId(\"lay\");\n}\n\n/** Generates a Policy ID with \"pol_\" prefix */\nexport function generatePolicyId(): string {\n return generateEntityId(\"pol\");\n}\n\n/** Generates an Allocation ID with \"alloc_\" prefix */\nexport function generateAllocationId(): string {\n return generateEntityId(\"alloc\");\n}\n\n/** Generates a Parameter ID with \"param_\" prefix */\nexport function generateParameterId(): string {\n return generateEntityId(\"param\");\n}\n\n/** Generates a DOM Binding ID with \"dom_\" prefix */\nexport function generateDomBindingId(): string {\n return generateEntityId(\"dom\");\n}\n\n/** Generates an Environment Override ID with \"ovr_\" prefix */\nexport function generateOverrideId(): string {\n return generateEntityId(\"ovr\");\n}\n\n/** Generates an API Key ID with \"ak_\" prefix */\nexport function generateApiKeyId(): string {\n return generateEntityId(\"ak\");\n}\n\n// =============================================================================\n// Event ID Convenience Functions (Keep ULID for time-sortability)\n// =============================================================================\n\n/** Generates a Decision event ID with \"dec_\" prefix (ULID) */\nexport function generateDecisionId(): string {\n return generateEventId(\"dec\");\n}\n\n/** Generates an Exposure event ID with \"exp_\" prefix (ULID) */\nexport function generateExposureId(): string {\n return generateEventId(\"exp\");\n}\n\n/** Generates a Track event ID with \"trk_\" prefix (ULID) */\nexport function generateTrackEventId(): string {\n return generateEventId(\"trk\");\n}\n\n/** Generates an Assignment log entry ID with \"asn_\" prefix (ULID) */\nexport function generateAssignmentId(): string {\n return generateEventId(\"asn\");\n}\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\n/**\n * Extracts the timestamp from a ULID-based ID.\n * Only works for event IDs (ULID format).\n *\n * @param id - A prefixed ULID (e.g., \"dec_01JHFK1WWMMG7M0XPEBTYXZEBW\")\n * @returns The timestamp as a Date, or null if invalid\n */\nexport function getIdTimestamp(id: string): Date | null {\n // Extract the ULID part (after the prefix and underscore)\n const parts = id.split(\"_\");\n if (parts.length < 2) {\n return null;\n }\n\n const ulidPart = parts[1];\n if (!ulidPart || ulidPart.length !== 26) {\n return null;\n }\n\n // ULID timestamp is encoded in the first 10 characters (Crockford's Base32)\n const ENCODING = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\n const TIME_LEN = 10;\n\n let time = 0;\n for (let i = 0; i < TIME_LEN; i++) {\n const char = ulidPart.charAt(i).toUpperCase();\n const index = ENCODING.indexOf(char);\n if (index === -1) {\n return null;\n }\n time = time * 32 + index;\n }\n\n return new Date(time);\n}\n\n/**\n * @deprecated Use getIdTimestamp instead\n */\nexport function getEventIdTimestamp(eventId: string): Date | null {\n return getIdTimestamp(eventId);\n}\n", "/**\n * Resolution Engine\n *\n * Pure functions for parameter resolution using layered config and policies.\n * Implements the Google-inspired layering system where:\n * - Parameters are partitioned into layers\n * - Within a layer, only one policy can be active for a unit\n * - Across layers, policies overlap freely (different parameters)\n *\n * Resolution order (lowest to highest priority):\n * 1. Caller defaults (always safe fallback)\n * 2. Parameter defaults (from bundle)\n * 3. Layer policies (each parameter belongs to exactly one layer)\n */\n\nimport type {\n ConfigBundle,\n BundleParameter,\n BundlePolicy,\n BundleAllocation,\n Context,\n ParameterValue,\n DecisionResult,\n LayerResolution,\n Id,\n} from \"../types/index.js\";\nimport { computeBucket, findMatchingAllocation } from \"../hashing/bucket.js\";\nimport { weightedSelection } from \"../hashing/weighted.js\";\nimport { evaluateConditions } from \"./conditions.js\";\nimport { resolveContextualPolicy } from \"../scoring/contextual.js\";\nimport { generateDecisionId } from \"../ids/index.js\";\n\n/**\n * Filters context to only include fields allowed by matched policies.\n * Collects the union of all allowed fields from policies with contextLogging config.\n *\n * @param context - The full evaluation context\n * @param policies - Matched policies from resolution\n * @returns Filtered context with only allowed fields, or undefined if no fields allowed\n */\nfunction filterContext(\n context: Context,\n policies: BundlePolicy[]\n): Context | undefined {\n // Collect union of all allowed fields from matched policies\n const allowedFields = new Set<string>();\n for (const policy of policies) {\n if (policy.contextLogging?.allowedFields) {\n for (const field of policy.contextLogging.allowedFields) {\n allowedFields.add(field);\n }\n }\n }\n\n // If no fields are allowed, return undefined\n if (allowedFields.size === 0) {\n return undefined;\n }\n\n // Filter context to only include allowed fields\n const filtered: Context = {};\n for (const field of allowedFields) {\n if (field in context) {\n filtered[field] = context[field];\n }\n }\n\n // Return undefined if no fields matched\n return Object.keys(filtered).length > 0 ? filtered : undefined;\n}\n\n// =============================================================================\n// Per-Entity Resolution Helpers\n// =============================================================================\n\n/**\n * Builds an entity ID from context using the policy's entityKeys.\n *\n * @param entityKeys - Array of context keys that identify the entity\n * @param context - The evaluation context\n * @returns Entity ID string, or null if any key is missing\n */\nfunction buildEntityId(entityKeys: string[], context: Context): string | null {\n const parts: string[] = [];\n for (const key of entityKeys) {\n const value = context[key];\n if (value === undefined || value === null) {\n return null;\n }\n parts.push(String(value));\n }\n return parts.join(\"_\");\n}\n\n/**\n * Creates uniform weights for dynamic allocations.\n *\n * @param count - Number of allocations\n * @returns Array of equal weights summing to 1.0\n */\nfunction createUniformWeights(count: number): number[] {\n if (count <= 0) return [];\n const weight = 1 / count;\n return Array(count).fill(weight);\n}\n\n/**\n * Gets entity weights from the bundle's entityState.\n *\n * @param bundle - The config bundle\n * @param policyId - The policy ID\n * @param entityId - The entity ID\n * @param allocationCount - Number of allocations (for dynamic allocations)\n * @returns Entity weights or uniform weights for cold start\n */\nfunction getEntityWeights(\n bundle: ConfigBundle,\n policyId: Id,\n entityId: string,\n allocationCount: number\n): number[] {\n const policyState = bundle.entityState?.[policyId];\n\n if (!policyState) {\n // No state for this policy - use uniform weights\n return createUniformWeights(allocationCount);\n }\n\n // Try entity-specific weights first\n const entityWeights = policyState.entities[entityId];\n if (entityWeights && entityWeights.weights.length === allocationCount) {\n return entityWeights.weights;\n }\n\n // Fall back to global prior\n const globalWeights = policyState._global;\n if (globalWeights && globalWeights.weights.length === allocationCount) {\n return globalWeights.weights;\n }\n\n // Last resort: uniform weights\n return createUniformWeights(allocationCount);\n}\n\n/**\n * Resolves a per-entity policy using weighted selection.\n *\n * @param bundle - The config bundle\n * @param policy - The policy with entityConfig\n * @param context - The evaluation context\n * @param unitKeyValue - The unit key value for hashing\n * @returns The selected allocation and entity ID, or null if cannot resolve\n */\nfunction resolvePerEntityPolicy(\n bundle: ConfigBundle,\n policy: BundlePolicy,\n context: Context,\n unitKeyValue: string\n): { allocation: BundleAllocation; entityId: string } | null {\n const entityConfig = policy.entityConfig;\n if (!entityConfig) return null;\n\n // Build entity ID from context\n const entityId = buildEntityId(entityConfig.entityKeys, context);\n if (!entityId) {\n // Missing entity keys - cannot resolve\n return null;\n }\n\n // Determine allocations\n let allocations: BundleAllocation[];\n let allocationCount: number;\n\n if (entityConfig.dynamicAllocations) {\n // Dynamic allocations from context\n const countKey = entityConfig.dynamicAllocations.countKey;\n const count = context[countKey];\n if (typeof count !== \"number\" || count <= 0) {\n return null;\n }\n allocationCount = Math.floor(count);\n\n // Create synthetic allocations for dynamic mode\n // Each allocation is an index (0, 1, 2, ..., count-1)\n allocations = Array.from({ length: allocationCount }, (_, i) => ({\n id: `${policy.id}_dynamic_${i}`,\n name: String(i),\n bucketRange: [0, 0] as [number, number], // Not used for per-entity\n overrides: {}, // Overrides are applied differently for dynamic\n }));\n } else {\n // Fixed allocations from policy\n allocations = policy.allocations;\n allocationCount = allocations.length;\n }\n\n if (allocationCount === 0) return null;\n\n // Get weights for this entity\n const weights = getEntityWeights(bundle, policy.id, entityId, allocationCount);\n\n // Deterministic weighted selection\n const seed = `${entityId}:${unitKeyValue}:${policy.id}`;\n const selectedIndex = weightedSelection(weights, seed);\n\n return {\n allocation: allocations[selectedIndex],\n entityId,\n };\n}\n\n/**\n * Extracts the unit key value from context using the bundle's hashing config.\n *\n * @param bundle - The config bundle\n * @param context - The evaluation context\n * @returns The unit key value as a string, or null if not found\n */\nexport function getUnitKeyValue(\n bundle: ConfigBundle,\n context: Context\n): string | null {\n const value = context[bundle.hashing.unitKey];\n\n if (value === undefined || value === null) {\n return null;\n }\n\n return String(value);\n}\n\n// =============================================================================\n// Resolve Options (for server-evaluated mode)\n// =============================================================================\n\n/**\n * Options for resolution that allow injecting pre-fetched edge results.\n * Used by server-evaluated mode where the edge worker resolves all policies\n * (including per-entity) in a single request and passes results to the core engine.\n */\nexport interface ResolveOptions {\n /**\n * Pre-fetched edge results keyed by policyId.\n * When provided, edge-mode policies use these instead of being skipped.\n */\n edgeResults?: Map<string, { allocationIndex: number; entityId: string }>;\n}\n\n/**\n * Internal resolution result with metadata.\n */\ninterface ResolutionResult<T> {\n assignments: T;\n unitKeyValue: string;\n layers: LayerResolution[];\n /** Matched policies with context logging config */\n matchedPolicies: BundlePolicy[];\n}\n\n/**\n * Internal function that performs parameter resolution with metadata tracking.\n * This is the single source of truth for resolution logic.\n *\n * @param bundle - The config bundle (can be null if unavailable)\n * @param context - The evaluation context\n * @param defaults - Default values for parameters (required, used as fallback)\n * @returns Resolution result with assignments and metadata\n */\nfunction resolveInternal<T extends Record<string, ParameterValue>>(\n bundle: ConfigBundle | null,\n context: Context,\n defaults: T,\n options?: ResolveOptions\n): ResolutionResult<T> {\n // Start with caller defaults (always safe)\n const assignments = { ...defaults } as Record<string, ParameterValue>;\n const layers: LayerResolution[] = [];\n const matchedPolicies: BundlePolicy[] = [];\n\n // If no bundle, return defaults with empty metadata\n if (!bundle) {\n return { assignments: assignments as T, unitKeyValue: \"\", layers, matchedPolicies };\n }\n\n // Project-level unit key. Layers that don't override `unitKey` use this.\n // In multi-entity projects, individual layers may set `unitKey` to a\n // different context field (e.g. `merchantId` when the project default is\n // `customerId`); those layers compute their own unit value below.\n //\n // We no longer bail out when this is missing \u2014 some layers in multi-entity\n // projects may still resolve via their own unit key. The empty string we\n // store in `unitKeyValue` is the legacy \"no project unit key\" signal that\n // downstream code (decision events) already tolerates.\n //\n // See: ng/docs/design/diversion-types.md\n const projectUnitKeyValue = getUnitKeyValue(bundle, context) ?? \"\";\n\n // Get requested parameter keys from defaults\n const requestedKeys = new Set(Object.keys(defaults));\n\n // Filter bundle parameters to only those requested\n const params = bundle.parameters.filter((p) => requestedKeys.has(p.key));\n\n // Apply bundle defaults (overrides caller defaults)\n for (const param of params) {\n if (param.key in assignments) {\n assignments[param.key] = param.default;\n }\n }\n\n // Group by layer\n const paramsByLayer = new Map<Id, BundleParameter[]>();\n for (const param of params) {\n const existing = paramsByLayer.get(param.layerId) || [];\n existing.push(param);\n paramsByLayer.set(param.layerId, existing);\n }\n\n // Process ALL layers for both parameter resolution and attribution.\n //\n // Layers with matching parameters get their overrides applied (parameter\n // resolution). Layers WITHOUT matching parameters are still processed for\n // bucket/policy/allocation matching so that decision events and track-event\n // attribution include the full set of experiments the user is assigned to.\n //\n // The `attributionOnly` flag distinguishes the two: layers resolved only for\n // attribution are marked `attributionOnly: true`, which tells trackExposure()\n // to skip them (avoiding exposure inflation for experiments the user didn't\n // actually see).\n for (const layer of bundle.layers) {\n const layerParams = paramsByLayer.get(layer.id);\n const hasParams = layerParams && layerParams.length > 0;\n\n // Per-layer unit key resolution. Layers in multi-entity projects may\n // override `unitKey` to read a different context field. When a layer's\n // unit value can't be resolved (missing context field), we still emit a\n // LayerResolution row (with `bucket = -1`) so decision events record the\n // skipped layer, but no bucket-based policy can match.\n const layerUnitKey = layer.unitKey;\n const layerUnitValue = layerUnitKey\n ? String(context[layerUnitKey] ?? \"\")\n : projectUnitKeyValue;\n\n if (!layerUnitValue) {\n layers.push({\n layerId: layer.id,\n bucket: -1,\n ...(layerUnitKey ? { unitKey: layerUnitKey, unitKeyValue: \"\" } : {}),\n ...(hasParams ? {} : { attributionOnly: true }),\n });\n continue;\n }\n\n // Compute bucket (needed for both parameter resolution and attribution)\n const bucket = computeBucket(\n layerUnitValue,\n layer.id,\n bundle.hashing.bucketCount\n );\n\n let matchedPolicy: BundlePolicy | undefined;\n let matchedAllocation: BundleAllocation | undefined;\n\n // Find matching policy\n for (const policy of layer.policies) {\n if (policy.state !== \"running\") continue;\n\n // Check bucket eligibility BEFORE conditions (performance optimization)\n // This enables non-overlapping experiments within a layer\n if (policy.eligibleBucketRange) {\n const { start, end } = policy.eligibleBucketRange;\n if (bucket < start || bucket > end) {\n continue; // User's bucket not eligible for this policy\n }\n }\n\n if (!evaluateConditions(policy.conditions, context)) continue;\n\n // Contextual model scoring: overrides bucket-based allocation\n if (policy.contextualModel) {\n const ctxAllocation = resolveContextualPolicy(policy, context, layerUnitValue);\n if (ctxAllocation) {\n matchedPolicy = policy;\n matchedAllocation = ctxAllocation;\n matchedPolicies.push(policy);\n if (hasParams) {\n for (const [key, value] of Object.entries(ctxAllocation.overrides)) {\n if (key in assignments) {\n assignments[key] = value;\n }\n }\n }\n break;\n }\n }\n\n // Check if this is a per-entity policy\n if (policy.entityConfig && policy.entityConfig.resolutionMode === \"bundle\") {\n const result = resolvePerEntityPolicy(bundle, policy, context, layerUnitValue);\n if (result) {\n matchedPolicy = policy;\n matchedAllocation = result.allocation;\n\n // Track matched policy for context filtering\n matchedPolicies.push(policy);\n\n // Apply overrides only if this layer has matching parameters\n if (hasParams) {\n // For dynamic allocations, the allocation name IS the value\n if (policy.entityConfig.dynamicAllocations) {\n // For per-entity dynamic policies, we return the selected index\n // The SDK caller should use metadata.allocationName to get the index\n // No parameter overrides to apply in this mode\n } else {\n // For fixed allocations, apply normal overrides\n for (const [key, value] of Object.entries(result.allocation.overrides)) {\n if (key in assignments) {\n assignments[key] = value;\n }\n }\n }\n }\n break; // Only one policy per layer\n }\n } else if (policy.entityConfig && policy.entityConfig.resolutionMode === \"edge\") {\n const edgeResult = options?.edgeResults?.get(policy.id);\n if (edgeResult) {\n matchedPolicy = policy;\n matchedPolicies.push(policy);\n\n if (policy.entityConfig.dynamicAllocations) {\n // Dynamic allocations: synthesize allocation from index\n matchedAllocation = {\n id: `${policy.id}_dynamic_${edgeResult.allocationIndex}`,\n name: String(edgeResult.allocationIndex),\n bucketRange: [0, 0] as [number, number],\n overrides: {},\n };\n } else if (policy.allocations[edgeResult.allocationIndex]) {\n matchedAllocation = policy.allocations[edgeResult.allocationIndex];\n if (hasParams && matchedAllocation) {\n for (const [key, value] of Object.entries(matchedAllocation.overrides)) {\n if (key in assignments) {\n assignments[key] = value;\n }\n }\n }\n }\n break;\n }\n // No pre-fetched result: skip this policy gracefully\n continue;\n } else {\n // Standard bucket-based resolution\n const allocation = findMatchingAllocation(bucket, policy.allocations);\n if (allocation) {\n matchedPolicy = policy;\n matchedAllocation = allocation;\n\n // Track matched policy for context filtering\n matchedPolicies.push(policy);\n\n // Apply overrides only if this layer has matching parameters\n if (hasParams) {\n for (const [key, value] of Object.entries(allocation.overrides)) {\n if (key in assignments) {\n assignments[key] = value;\n }\n }\n }\n break; // Only one policy per layer\n }\n }\n }\n\n layers.push({\n layerId: layer.id,\n bucket,\n policyId: matchedPolicy?.id,\n policyKey: (matchedPolicy as any)?.key,\n allocationId: matchedAllocation?.id,\n allocationName: matchedAllocation?.name,\n allocationKey: (matchedAllocation as any)?.key,\n // Record the unit key only when the layer overrides the project\n // default \u2014 keeps the metadata small for the single-entity case while\n // making exposure events auditable in multi-entity projects.\n ...(layerUnitKey ? { unitKey: layerUnitKey, unitKeyValue: layerUnitValue } : {}),\n // Mark layers without requested parameters as attribution-only.\n // These are included in decision events and track-event attribution\n // but skipped by trackExposure() to avoid exposure inflation.\n ...(hasParams ? {} : { attributionOnly: true }),\n });\n }\n\n return { assignments: assignments as T, unitKeyValue: projectUnitKeyValue, layers, matchedPolicies };\n}\n\n/**\n * Resolves parameters with required defaults as fallback.\n * This is the primary SDK function that guarantees safe defaults.\n *\n * Resolution priority (highest wins):\n * 1. Policy overrides (from bundle)\n * 2. Parameter defaults (from bundle)\n * 3. Caller defaults (always safe fallback)\n *\n * @param bundle - The config bundle (can be null if unavailable)\n * @param context - The evaluation context\n * @param defaults - Default values for parameters (required, used as fallback)\n * @returns Resolved parameter assignments (always returns safe values with inferred types)\n */\nexport function resolveParameters<T extends Record<string, ParameterValue>>(\n bundle: ConfigBundle | null,\n context: Context,\n defaults: T,\n options?: ResolveOptions\n): T {\n return resolveInternal(bundle, context, defaults, options).assignments;\n}\n\n/**\n * Makes a decision with full metadata for tracking.\n * Requires defaults for graceful degradation.\n *\n * Resolution priority (highest wins):\n * 1. Policy overrides (from bundle)\n * 2. Parameter defaults (from bundle)\n * 3. Caller defaults (always safe fallback)\n *\n * @param bundle - The config bundle (can be null if unavailable)\n * @param context - The evaluation context\n * @param defaults - Default values for parameters (required, used as fallback)\n * @returns Decision result with metadata (always returns safe values)\n */\nexport function decide<T extends Record<string, ParameterValue>>(\n bundle: ConfigBundle | null,\n context: Context,\n defaults: T,\n options?: ResolveOptions\n): DecisionResult {\n const { assignments, unitKeyValue, layers, matchedPolicies } = resolveInternal(\n bundle,\n context,\n defaults,\n options\n );\n\n // Filter context based on matched policies' contextLogging config\n const filteredContext = filterContext(context, matchedPolicies);\n\n return {\n decisionId: generateDecisionId(),\n assignments,\n metadata: {\n timestamp: new Date().toISOString(),\n unitKeyValue,\n layers,\n filteredContext,\n },\n };\n}\n", "/**\n * DecisionDeduplicator - Pure decision deduplication logic.\n *\n * Tracks which user+assignment combinations have been seen to avoid\n * sending duplicate decision events. This enables efficient decision\n * tracking without overwhelming the event pipeline.\n *\n * Key differences from ExposureDeduplicator:\n * - Pure in-memory (no I/O, no storage dependency)\n * - Deduplicates on unitKey + assignment hash (not policy/variant)\n * - Suitable for use in any JavaScript environment\n */\n\nimport type { ParameterValue } from \"../types/index.js\";\n\nconst DEFAULT_TTL_MS = 3600_000; // 1 hour\nconst DEFAULT_MAX_ENTRIES = 10_000;\nconst CLEANUP_THRESHOLD = 0.2; // Clean when 20% of entries are expired\n\nexport interface DecisionDeduplicatorOptions {\n /**\n * Time-to-live for deduplication entries in milliseconds.\n * After this time, the same decision can be tracked again.\n * Default: 1 hour (3600000 ms)\n */\n ttlMs?: number;\n /**\n * Maximum number of entries to store.\n * When exceeded, oldest entries are removed.\n * Default: 10000\n */\n maxEntries?: number;\n}\n\nexport class DecisionDeduplicator {\n private _seen = new Map<string, number>(); // key -> timestamp\n private readonly _ttlMs: number;\n private readonly _maxEntries: number;\n private _lastCleanup = Date.now();\n\n constructor(options: DecisionDeduplicatorOptions = {}) {\n this._ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;\n this._maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;\n }\n\n /**\n * Generate a stable hash for assignment values.\n * Used to create a deduplication key from assignments.\n */\n static hashAssignments(assignments: Record<string, ParameterValue>): string {\n // Sort keys for deterministic ordering\n const sortedKeys = Object.keys(assignments).sort();\n const parts: string[] = [];\n\n for (const key of sortedKeys) {\n const value = assignments[key];\n // Simple string representation that's stable\n const valueStr = typeof value === \"object\" ? JSON.stringify(value) : String(value);\n parts.push(`${key}=${valueStr}`);\n }\n\n return parts.join(\"|\");\n }\n\n /**\n * Create a deduplication key from unitKey and assignment hash.\n */\n static createKey(unitKey: string, assignmentHash: string): string {\n return `${unitKey}:${assignmentHash}`;\n }\n\n /**\n * Check if this decision is new (not seen before within TTL).\n * If new, marks it as seen.\n *\n * @param unitKey - The unit key (user identifier)\n * @param assignmentHash - Hash of the assignments (from hashAssignments)\n * @returns true if this is a new decision, false if duplicate\n */\n checkAndMark(unitKey: string, assignmentHash: string): boolean {\n const key = DecisionDeduplicator.createKey(unitKey, assignmentHash);\n const now = Date.now();\n const lastSeen = this._seen.get(key);\n\n // Check if we've seen this within TTL\n if (lastSeen !== undefined && now - lastSeen < this._ttlMs) {\n return false; // Duplicate\n }\n\n // Mark as seen\n this._seen.set(key, now);\n\n // Periodic cleanup\n this._maybeCleanup(now);\n\n return true; // New decision\n }\n\n /**\n * Check if a decision would be considered new (without marking it).\n */\n wouldBeNew(unitKey: string, assignmentHash: string): boolean {\n const key = DecisionDeduplicator.createKey(unitKey, assignmentHash);\n const now = Date.now();\n const lastSeen = this._seen.get(key);\n\n if (lastSeen === undefined) {\n return true;\n }\n\n return now - lastSeen >= this._ttlMs;\n }\n\n /**\n * Clear all seen decisions.\n */\n clear(): void {\n this._seen.clear();\n }\n\n /**\n * Get the number of entries in the deduplication cache.\n */\n get size(): number {\n return this._seen.size;\n }\n\n /**\n * Perform cleanup of expired entries.\n * Called periodically based on CLEANUP_THRESHOLD.\n */\n private _maybeCleanup(now: number): void {\n // Only cleanup periodically, not on every call\n const timeSinceCleanup = now - this._lastCleanup;\n const shouldCleanup =\n timeSinceCleanup > this._ttlMs * CLEANUP_THRESHOLD || this._seen.size > this._maxEntries;\n\n if (!shouldCleanup) {\n return;\n }\n\n this._lastCleanup = now;\n this._cleanup(now);\n }\n\n /**\n * Remove expired entries and enforce max size.\n */\n private _cleanup(now: number): void {\n const expiredKeys: string[] = [];\n\n // Find expired entries\n for (const [key, timestamp] of this._seen.entries()) {\n if (now - timestamp >= this._ttlMs) {\n expiredKeys.push(key);\n }\n }\n\n // Remove expired entries\n for (const key of expiredKeys) {\n this._seen.delete(key);\n }\n\n // If still over max, remove oldest entries\n if (this._seen.size > this._maxEntries) {\n const entries = Array.from(this._seen.entries()).sort((a, b) => a[1] - b[1]); // Sort by timestamp\n\n const toRemove = entries.slice(0, this._seen.size - this._maxEntries);\n for (const [key] of toRemove) {\n this._seen.delete(key);\n }\n }\n }\n}\n\n", "/**\n * DecisionClient\n *\n * I/O client for Traffical server-evaluated resolution and per-entity decisions.\n * Platform-agnostic (uses standard fetch API).\n */\n\nimport type {\n Id,\n Context,\n EdgeDecideRequest,\n EdgeDecideResponse,\n EdgeBatchDecideResponse,\n ServerResolveRequest,\n ServerResolveResponse,\n} from \"@traffical/core\";\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface DecisionClientConfig {\n /** Base URL for the edge worker (e.g., \"https://sdk.traffical.io\") */\n baseUrl: string;\n /** Organization ID */\n orgId: Id;\n /** Project ID */\n projectId: Id;\n /** Environment */\n env: string;\n /** API key for authentication */\n apiKey: string;\n /** Default timeout in milliseconds (default: 5000 for resolve, 100 for decide) */\n defaultTimeoutMs?: number;\n}\n\n// =============================================================================\n// DecisionClient\n// =============================================================================\n\nexport class DecisionClient {\n private readonly config: DecisionClientConfig;\n private readonly defaultTimeout: number;\n\n constructor(config: DecisionClientConfig) {\n this.config = config;\n this.defaultTimeout = config.defaultTimeoutMs ?? 5000;\n }\n\n /**\n * Full server-side resolution via POST /v1/resolve.\n * Returns all parameter assignments resolved on the edge worker.\n */\n async resolve(request: ServerResolveRequest): Promise<ServerResolveResponse | null> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.defaultTimeout);\n\n const url = `${this.config.baseUrl}/v1/resolve`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: this._headers(),\n body: JSON.stringify({\n context: request.context,\n env: request.env ?? this.config.env,\n parameters: request.parameters,\n }),\n signal: controller.signal as any,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n console.warn(\n `[Traffical] Resolve failed: ${response.status} ${response.statusText}`\n );\n return null;\n }\n\n return (await response.json()) as ServerResolveResponse;\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n console.warn(`[Traffical] Resolve timed out after ${this.defaultTimeout}ms`);\n } else {\n console.warn(`[Traffical] Resolve error:`, error);\n }\n return null;\n }\n }\n\n /**\n * Per-entity edge decision via POST /v1/decide/:policyId.\n */\n async decideEntity(\n request: EdgeDecideRequest,\n timeoutMs?: number\n ): Promise<EdgeDecideResponse | null> {\n const timeout = timeoutMs ?? Math.min(this.defaultTimeout, 100);\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const url = `${this.config.baseUrl}/v1/decide/${request.policyId}`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: this._headers(),\n body: JSON.stringify({\n entityId: request.entityId,\n unitKeyValue: request.unitKeyValue,\n allocationCount: request.allocationCount,\n context: request.context,\n }),\n signal: controller.signal as any,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n console.warn(\n `[Traffical] Edge decide failed: ${response.status} ${response.statusText}`\n );\n return null;\n }\n\n return (await response.json()) as EdgeDecideResponse;\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n console.warn(`[Traffical] Edge decide timed out after ${timeout}ms`);\n } else {\n console.warn(`[Traffical] Edge decide error:`, error);\n }\n return null;\n }\n }\n\n /**\n * Batch per-entity edge decisions via POST /v1/decide/batch.\n */\n async decideEntityBatch(\n requests: EdgeDecideRequest[],\n timeoutMs?: number\n ): Promise<(EdgeDecideResponse | null)[]> {\n if (requests.length === 0) return [];\n if (requests.length === 1) {\n const result = await this.decideEntity(requests[0], timeoutMs);\n return [result];\n }\n\n const timeout = timeoutMs ?? Math.min(this.defaultTimeout, 200);\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const url = `${this.config.baseUrl}/v1/decide/batch`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: this._headers(),\n body: JSON.stringify({ requests }),\n signal: controller.signal as any,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n console.warn(\n `[Traffical] Edge batch decide failed: ${response.status} ${response.statusText}`\n );\n return requests.map(() => null);\n }\n\n const data = (await response.json()) as EdgeBatchDecideResponse;\n return data.responses;\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n console.warn(`[Traffical] Edge batch decide timed out after ${timeout}ms`);\n } else {\n console.warn(`[Traffical] Edge batch decide error:`, error);\n }\n return requests.map(() => null);\n }\n }\n\n private _headers(): Record<string, string> {\n return {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.config.apiKey}`,\n \"X-Org-Id\": this.config.orgId,\n \"X-Project-Id\": this.config.projectId,\n \"X-Env\": this.config.env,\n };\n }\n}\n\n// =============================================================================\n// Utility Functions (moved from @traffical/core)\n// =============================================================================\n\n/**\n * Creates an edge decide request from policy and context.\n *\n * @param policyId - The policy ID\n * @param entityKeys - Array of context keys that identify the entity\n * @param context - The evaluation context\n * @param unitKeyValue - The unit key value\n * @param allocationCount - Number of allocations (for dynamic)\n * @returns The decide request, or null if entity ID cannot be built\n */\nexport function createEdgeDecideRequest(\n policyId: Id,\n entityKeys: string[],\n context: Context,\n unitKeyValue: string,\n allocationCount?: number\n): EdgeDecideRequest | null {\n const parts: string[] = [];\n for (const key of entityKeys) {\n const value = context[key];\n if (value === undefined || value === null) {\n return null;\n }\n parts.push(String(value));\n }\n const entityId = parts.join(\"_\");\n\n return {\n policyId,\n entityId,\n unitKeyValue,\n allocationCount,\n context,\n };\n}\n", "/**\n * ErrorBoundary - Ensures SDK never crashes customer's application.\n *\n * Wraps all public methods to catch errors and return safe defaults.\n * Optionally reports errors to Traffical backend for monitoring.\n */\n\nexport interface ErrorBoundaryOptions {\n /** Whether to report errors to Traffical backend */\n reportErrors?: boolean;\n /** Endpoint for error reporting */\n errorEndpoint?: string;\n /** SDK key for identification */\n sdkKey?: string;\n /** Callback when error occurs */\n onError?: (tag: string, error: Error) => void;\n}\n\nexport class ErrorBoundary {\n private _seen = new Set<string>();\n private _options: ErrorBoundaryOptions;\n private _lastError: Error | null = null;\n\n constructor(options: ErrorBoundaryOptions = {}) {\n this._options = options;\n }\n\n /**\n * Wrap a synchronous function to catch errors and return fallback.\n */\n capture<T>(tag: string, fn: () => T, fallback: T): T {\n try {\n return fn();\n } catch (error) {\n this._onError(tag, error);\n return fallback;\n }\n }\n\n /**\n * Wrap an async function to catch errors and return fallback.\n */\n async captureAsync<T>(tag: string, fn: () => Promise<T>, fallback: T): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n this._onError(tag, error);\n return fallback;\n }\n }\n\n /**\n * Execute an async operation without expecting a return value.\n * Used for fire-and-forget operations like event tracking.\n */\n async swallow(tag: string, fn: () => Promise<void>): Promise<void> {\n try {\n await fn();\n } catch (error) {\n this._onError(tag, error);\n }\n }\n\n /**\n * Get the last error that occurred (for debugging).\n */\n getLastError(): Error | null {\n const error = this._lastError;\n this._lastError = null;\n return error;\n }\n\n /**\n * Clear the seen errors set (for testing or session reset).\n */\n clearSeen(): void {\n this._seen.clear();\n }\n\n private _onError(tag: string, error: unknown): void {\n const resolvedError = this._resolveError(error);\n this._lastError = resolvedError;\n\n // Deduplicate - only handle each unique error once\n const errorKey = `${tag}:${resolvedError.name}:${resolvedError.message}`;\n if (this._seen.has(errorKey)) {\n return;\n }\n this._seen.add(errorKey);\n\n // Log to console (development)\n console.warn(`[Traffical] Error in ${tag}:`, resolvedError.message);\n\n // Call user-provided callback\n this._options.onError?.(tag, resolvedError);\n\n // Optionally report to backend\n if (this._options.reportErrors && this._options.errorEndpoint) {\n this._reportError(tag, resolvedError).catch(() => {\n // Silently fail - we don't want error reporting to cause errors\n });\n }\n }\n\n private async _reportError(tag: string, error: Error): Promise<void> {\n if (!this._options.errorEndpoint) return;\n\n try {\n await fetch(this._options.errorEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(this._options.sdkKey && { \"X-Traffical-Key\": this._options.sdkKey }),\n },\n body: JSON.stringify({\n tag,\n error: error.name,\n message: error.message,\n stack: error.stack,\n timestamp: new Date().toISOString(),\n sdk: \"@traffical/js-client\",\n userAgent: typeof navigator !== \"undefined\" ? navigator.userAgent : undefined,\n }),\n });\n } catch {\n // Silently fail\n }\n }\n\n private _resolveError(error: unknown): Error {\n if (error instanceof Error) {\n return error;\n }\n if (typeof error === \"string\") {\n return new Error(error);\n }\n return new Error(\"An unknown error occurred\");\n }\n}\n\n", "/**\n * EventLogger - Smart event batching with browser-specific features.\n *\n * Features:\n * - Batches events (flush every N events or M seconds)\n * - Uses navigator.sendBeacon() on page unload\n * - Persists failed events to localStorage\n * - Retries failed events on next session\n * - Visibility-aware: flushes on visibilitychange to hidden\n */\n\nimport type { TrackableEvent, OnSchemaWarnings, EventBatchResponse } from \"@traffical/core\";\nimport type { StorageProvider } from \"./storage.js\";\nimport type { LifecycleProvider, VisibilityState } from \"./lifecycle.js\";\n\nconst FAILED_EVENTS_KEY = \"failed_events\";\nconst DEFAULT_BATCH_SIZE = 10;\nconst DEFAULT_FLUSH_INTERVAL_MS = 30_000; // 30 seconds\nconst MAX_FAILED_EVENTS = 100;\n\nexport interface EventLoggerOptions {\n /** API endpoint for events */\n endpoint: string;\n /** API key for authentication */\n apiKey: string;\n /** Storage provider for failed events */\n storage: StorageProvider;\n /** Lifecycle provider for visibility/unload events */\n lifecycleProvider?: LifecycleProvider;\n /** Max events before auto-flush (default: 10) */\n batchSize?: number;\n /** Auto-flush interval in ms (default: 30000) */\n flushIntervalMs?: number;\n /** Callback on flush error */\n onError?: (error: Error) => void;\n /** Callback when schema validation warnings are received from the edge (dev-mode) */\n onSchemaWarnings?: OnSchemaWarnings;\n}\n\nexport class EventLogger {\n private _endpoint: string;\n private _apiKey: string;\n private _storage: StorageProvider;\n private _batchSize: number;\n private _flushIntervalMs: number;\n private _onError?: (error: Error) => void;\n private _onSchemaWarnings?: OnSchemaWarnings;\n\n private _lifecycleProvider?: LifecycleProvider;\n private _queue: TrackableEvent[] = [];\n private _flushTimer: ReturnType<typeof setTimeout> | null = null;\n private _isFlushing = false;\n private _visibilityCallback?: (state: VisibilityState) => void;\n\n constructor(options: EventLoggerOptions) {\n this._endpoint = options.endpoint;\n this._apiKey = options.apiKey;\n this._storage = options.storage;\n this._batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;\n this._flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n this._onError = options.onError;\n this._onSchemaWarnings = options.onSchemaWarnings;\n this._lifecycleProvider = options.lifecycleProvider;\n\n this._setupListeners();\n\n // Retry failed events from previous session\n this._retryFailedEvents();\n\n // Start flush timer\n this._startFlushTimer();\n }\n\n /**\n * Log an event (added to batch queue).\n */\n log(event: TrackableEvent): void {\n this._queue.push(event);\n\n // Auto-flush if batch is full\n if (this._queue.length >= this._batchSize) {\n this.flush();\n }\n }\n\n /**\n * Flush all queued events immediately.\n */\n async flush(): Promise<void> {\n if (this._isFlushing || this._queue.length === 0) {\n return;\n }\n\n this._isFlushing = true;\n\n // Take current queue\n const events = [...this._queue];\n this._queue = [];\n\n try {\n await this._sendEvents(events);\n } catch (error) {\n // Persist failed events for retry\n this._persistFailedEvents(events);\n this._onError?.(error instanceof Error ? error : new Error(String(error)));\n } finally {\n this._isFlushing = false;\n }\n }\n\n /**\n * Flush using fetch with keepalive (for page unload).\n * \n * We use fetch with keepalive: true instead of sendBeacon because:\n * - sendBeacon cannot send custom headers (like Authorization)\n * - keepalive ensures the request completes even as the page unloads\n * - Same reliability guarantees as sendBeacon\n * \n * Returns true if request was initiated, false otherwise.\n */\n flushBeacon(): boolean {\n if (this._queue.length === 0) {\n return true;\n }\n\n if (typeof fetch === \"undefined\") {\n // Fetch not available - try async flush\n this.flush();\n return false;\n }\n\n const events = [...this._queue];\n this._queue = [];\n\n // Use fetch with keepalive instead of sendBeacon\n // This allows us to include the Authorization header\n fetch(this._endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this._apiKey}`,\n },\n body: JSON.stringify({ events }),\n keepalive: true,\n }).catch(() => {\n // Persist for retry on next session\n this._persistFailedEvents(events);\n });\n\n return true;\n }\n\n /**\n * Get the number of events in the queue.\n */\n get queueSize(): number {\n return this._queue.length;\n }\n\n /**\n * Destroy the logger (cleanup timers and listeners).\n */\n destroy(): void {\n if (this._flushTimer) {\n clearInterval(this._flushTimer);\n this._flushTimer = null;\n }\n this._removeListeners();\n }\n\n private async _sendEvents(events: TrackableEvent[]): Promise<void> {\n const response = await fetch(this._endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this._apiKey}`,\n },\n body: JSON.stringify({ events }),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n if (this._onSchemaWarnings) {\n try {\n const body: EventBatchResponse = await response.json();\n if (body.schemaWarnings && body.schemaWarnings.length > 0) {\n this._onSchemaWarnings(body.schemaWarnings);\n }\n } catch {\n // Response parsing is best-effort for dev-mode warnings\n }\n }\n }\n\n private _persistFailedEvents(events: TrackableEvent[]): void {\n const existing = this._storage.get<TrackableEvent[]>(FAILED_EVENTS_KEY) ?? [];\n\n // Limit total stored events\n const combined = [...existing, ...events].slice(-MAX_FAILED_EVENTS);\n\n this._storage.set(FAILED_EVENTS_KEY, combined);\n }\n\n private _retryFailedEvents(): void {\n const failed = this._storage.get<TrackableEvent[]>(FAILED_EVENTS_KEY);\n if (!failed || failed.length === 0) {\n return;\n }\n\n // Clear stored events\n this._storage.remove(FAILED_EVENTS_KEY);\n\n // Add to queue for retry\n this._queue.push(...failed);\n }\n\n private _startFlushTimer(): void {\n if (this._flushIntervalMs <= 0) return;\n\n this._flushTimer = setInterval(() => {\n this.flush().catch(() => {\n // Errors handled in flush\n });\n }, this._flushIntervalMs);\n }\n\n private _setupListeners(): void {\n if (!this._lifecycleProvider) return;\n\n this._visibilityCallback = (state) => {\n if (state === \"background\") {\n if (this._lifecycleProvider?.isUnloading()) {\n this.flushBeacon();\n } else {\n this.flush().catch(() => {});\n }\n } else {\n this._retryFailedEvents();\n }\n };\n this._lifecycleProvider.onVisibilityChange(this._visibilityCallback);\n }\n\n private _removeListeners(): void {\n if (this._lifecycleProvider && this._visibilityCallback) {\n this._lifecycleProvider.removeVisibilityListener(this._visibilityCallback);\n this._visibilityCallback = undefined;\n }\n }\n}\n\n", "/**\n * ExposureDeduplicator - Prevents duplicate exposure events.\n *\n * Same user seeing same variant should only count as 1 exposure.\n * Uses session-based deduplication with localStorage persistence.\n */\n\nimport type { StorageProvider } from \"./storage.js\";\n\nconst STORAGE_KEY = \"exposure_dedup\";\nconst DEFAULT_SESSION_TTL_MS = 30 * 60 * 1000; // 30 minutes\n\nexport interface ExposureDeduplicatorOptions {\n /** Storage provider for persistence */\n storage: StorageProvider;\n /** Session TTL in milliseconds (default: 30 minutes) */\n sessionTtlMs?: number;\n}\n\ninterface DeduplicationState {\n /** Set of seen exposure keys */\n seen: string[];\n /** Session start timestamp */\n sessionStart: number;\n}\n\nexport class ExposureDeduplicator {\n private _storage: StorageProvider;\n private _sessionTtlMs: number;\n private _seen: Set<string>;\n private _sessionStart: number;\n\n constructor(options: ExposureDeduplicatorOptions) {\n this._storage = options.storage;\n this._sessionTtlMs = options.sessionTtlMs ?? DEFAULT_SESSION_TTL_MS;\n this._seen = new Set();\n this._sessionStart = Date.now();\n\n // Restore from storage\n this._restore();\n }\n\n /**\n * Generate a deduplication key for an exposure.\n *\n * Key format: {unitKey}:{policyId}:{variant}\n */\n static createKey(unitKey: string, policyId: string, variant: string): string {\n return `${unitKey}:${policyId}:${variant}`;\n }\n\n /**\n * Check if an exposure should be tracked (not a duplicate).\n * Returns true if this is a new exposure, false if duplicate.\n */\n shouldTrack(key: string): boolean {\n // Check if session has expired\n if (this._isSessionExpired()) {\n this._resetSession();\n }\n\n if (this._seen.has(key)) {\n return false;\n }\n\n // Mark as seen\n this._seen.add(key);\n this._persist();\n\n return true;\n }\n\n /**\n * Check and mark in one operation.\n * Returns true if this was a new exposure (and is now marked as seen).\n */\n checkAndMark(unitKey: string, policyId: string, variant: string): boolean {\n const key = ExposureDeduplicator.createKey(unitKey, policyId, variant);\n return this.shouldTrack(key);\n }\n\n /**\n * Clear all seen exposures (useful for testing or logout).\n */\n clear(): void {\n this._seen.clear();\n this._storage.remove(STORAGE_KEY);\n }\n\n /**\n * Get the number of unique exposures in the current session.\n */\n get size(): number {\n return this._seen.size;\n }\n\n private _isSessionExpired(): boolean {\n return Date.now() - this._sessionStart > this._sessionTtlMs;\n }\n\n private _resetSession(): void {\n this._seen.clear();\n this._sessionStart = Date.now();\n this._storage.remove(STORAGE_KEY);\n }\n\n private _persist(): void {\n const state: DeduplicationState = {\n seen: Array.from(this._seen),\n sessionStart: this._sessionStart,\n };\n this._storage.set(STORAGE_KEY, state, this._sessionTtlMs);\n }\n\n private _restore(): void {\n const state = this._storage.get<DeduplicationState>(STORAGE_KEY);\n if (!state) return;\n\n // Check if stored session is still valid\n const sessionAge = Date.now() - state.sessionStart;\n if (sessionAge > this._sessionTtlMs) {\n this._storage.remove(STORAGE_KEY);\n return;\n }\n\n // Restore state\n this._seen = new Set(state.seen);\n this._sessionStart = state.sessionStart;\n }\n}\n\n", "/**\n * StableIdProvider - Anonymous user identification for experimentation.\n *\n * Generates a stable ID on first visit and persists it across sessions.\n * Uses localStorage as primary storage with cookie fallback.\n */\n\nimport type { StorageProvider } from \"./storage.js\";\n\nconst STORAGE_KEY = \"stable_id\";\nconst COOKIE_NAME = \"traffical_sid\";\nconst COOKIE_MAX_AGE_DAYS = 365;\n\nexport interface StableIdProviderOptions {\n /** Storage provider (localStorage) */\n storage: StorageProvider;\n /** Whether to use cookie fallback (default: true) */\n useCookieFallback?: boolean;\n /** Custom cookie name (default: traffical_sid) */\n cookieName?: string;\n}\n\nexport class StableIdProvider {\n private _storage: StorageProvider;\n private _useCookieFallback: boolean;\n private _cookieName: string;\n private _cachedId: string | null = null;\n\n constructor(options: StableIdProviderOptions) {\n this._storage = options.storage;\n this._useCookieFallback = options.useCookieFallback ?? true;\n this._cookieName = options.cookieName ?? COOKIE_NAME;\n }\n\n /**\n * Get the stable ID, creating one if it doesn't exist.\n */\n getId(): string {\n // Check cache first\n if (this._cachedId) {\n return this._cachedId;\n }\n\n // Try localStorage\n let id = this._storage.get<string>(STORAGE_KEY);\n if (id) {\n this._cachedId = id;\n return id;\n }\n\n // Try cookie fallback\n if (this._useCookieFallback) {\n id = this._getCookie();\n if (id) {\n // Sync back to localStorage\n this._storage.set(STORAGE_KEY, id);\n this._cachedId = id;\n return id;\n }\n }\n\n // Generate new ID\n id = this._generateId();\n this._persist(id);\n this._cachedId = id;\n\n return id;\n }\n\n /**\n * Set a custom stable ID (e.g., when user logs in).\n */\n setId(id: string): void {\n this._persist(id);\n this._cachedId = id;\n }\n\n /**\n * Clear the stable ID (e.g., on logout).\n */\n clear(): void {\n this._storage.remove(STORAGE_KEY);\n if (this._useCookieFallback) {\n this._deleteCookie();\n }\n this._cachedId = null;\n }\n\n /**\n * Check if a stable ID exists.\n */\n hasId(): boolean {\n return this._storage.get<string>(STORAGE_KEY) !== null || this._getCookie() !== null;\n }\n\n private _persist(id: string): void {\n // Save to localStorage (no TTL - permanent)\n this._storage.set(STORAGE_KEY, id);\n\n // Save to cookie as fallback\n if (this._useCookieFallback) {\n this._setCookie(id);\n }\n }\n\n private _generateId(): string {\n // Use crypto.randomUUID if available (modern browsers)\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n // Fallback to manual UUID v4 generation\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n\n private _getCookie(): string | null {\n if (typeof document === \"undefined\") return null;\n\n try {\n const cookies = document.cookie.split(\";\");\n for (const cookie of cookies) {\n const [name, value] = cookie.trim().split(\"=\");\n if (name === this._cookieName && value) {\n return decodeURIComponent(value);\n }\n }\n } catch {\n // Cookie access failed (e.g., cross-origin iframe)\n }\n\n return null;\n }\n\n private _setCookie(value: string): void {\n if (typeof document === \"undefined\") return;\n\n try {\n const maxAge = COOKIE_MAX_AGE_DAYS * 24 * 60 * 60;\n document.cookie = `${this._cookieName}=${encodeURIComponent(value)}; max-age=${maxAge}; path=/; SameSite=Lax`;\n } catch {\n // Cookie access failed\n }\n }\n\n private _deleteCookie(): void {\n if (typeof document === \"undefined\") return;\n\n try {\n document.cookie = `${this._cookieName}=; max-age=0; path=/`;\n } catch {\n // Cookie access failed\n }\n }\n}\n\n", "/**\n * Storage abstraction for browser environments.\n *\n * Provides a safe wrapper around localStorage with:\n * - Automatic JSON serialization/deserialization\n * - Graceful fallback when localStorage is unavailable\n * - TTL support for expiring entries\n */\n\nexport interface StorageProvider {\n get<T>(key: string): T | null;\n set<T>(key: string, value: T, ttlMs?: number): void;\n remove(key: string): void;\n clear(): void;\n}\n\ninterface StoredValue<T> {\n value: T;\n expiresAt?: number;\n}\n\nconst STORAGE_PREFIX = \"traffical:\";\n\n/**\n * localStorage-based storage provider.\n */\nexport class LocalStorageProvider implements StorageProvider {\n private _available: boolean;\n\n constructor() {\n this._available = this._checkAvailability();\n }\n\n get<T>(key: string): T | null {\n if (!this._available) return null;\n\n try {\n const raw = localStorage.getItem(STORAGE_PREFIX + key);\n if (!raw) return null;\n\n const stored = JSON.parse(raw) as StoredValue<T>;\n\n // Check TTL\n if (stored.expiresAt && Date.now() > stored.expiresAt) {\n this.remove(key);\n return null;\n }\n\n return stored.value;\n } catch {\n return null;\n }\n }\n\n set<T>(key: string, value: T, ttlMs?: number): void {\n if (!this._available) return;\n\n try {\n const stored: StoredValue<T> = {\n value,\n ...(ttlMs && { expiresAt: Date.now() + ttlMs }),\n };\n localStorage.setItem(STORAGE_PREFIX + key, JSON.stringify(stored));\n } catch {\n // Storage full or unavailable - silently fail\n }\n }\n\n remove(key: string): void {\n if (!this._available) return;\n\n try {\n localStorage.removeItem(STORAGE_PREFIX + key);\n } catch {\n // Silently fail\n }\n }\n\n clear(): void {\n if (!this._available) return;\n\n try {\n // Only clear traffical-prefixed keys\n const keysToRemove: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(STORAGE_PREFIX)) {\n keysToRemove.push(key);\n }\n }\n keysToRemove.forEach((key) => localStorage.removeItem(key));\n } catch {\n // Silently fail\n }\n }\n\n private _checkAvailability(): boolean {\n try {\n const testKey = STORAGE_PREFIX + \"__test__\";\n localStorage.setItem(testKey, \"test\");\n localStorage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n }\n}\n\n/**\n * In-memory storage fallback when localStorage is unavailable.\n */\nexport class MemoryStorageProvider implements StorageProvider {\n private _store = new Map<string, StoredValue<unknown>>();\n\n get<T>(key: string): T | null {\n const stored = this._store.get(key) as StoredValue<T> | undefined;\n if (!stored) return null;\n\n // Check TTL\n if (stored.expiresAt && Date.now() > stored.expiresAt) {\n this.remove(key);\n return null;\n }\n\n return stored.value;\n }\n\n set<T>(key: string, value: T, ttlMs?: number): void {\n this._store.set(key, {\n value,\n ...(ttlMs && { expiresAt: Date.now() + ttlMs }),\n });\n }\n\n remove(key: string): void {\n this._store.delete(key);\n }\n\n clear(): void {\n this._store.clear();\n }\n}\n\n/**\n * Creates the appropriate storage provider for the current environment.\n */\nexport function createStorageProvider(): StorageProvider {\n // Try localStorage first\n const localProvider = new LocalStorageProvider();\n if (localProvider.get(\"__check__\") !== null || localStorageAvailable()) {\n return localProvider;\n }\n // Fall back to in-memory\n return new MemoryStorageProvider();\n}\n\nfunction localStorageAvailable(): boolean {\n try {\n const testKey = \"__traffical_storage_test__\";\n localStorage.setItem(testKey, \"test\");\n localStorage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n}\n\n", "// Auto-generated from package.json \u2014 do not edit manually.\nexport const SDK_VERSION = \"0.14.0\";\n", "/**\n * DecisionTrackingPlugin - Automatically tracks decision events.\n *\n * This plugin hooks into the SDK's decision lifecycle and sends a DecisionEvent\n * to the control plane whenever decide() is called. This enables:\n * - Intent-to-treat analysis: tracking all assignments, not just exposures\n * - Debugging: understanding why specific values were computed\n * - Audit trail: tracking all decisions made by the SDK\n *\n * Decision events are deduplicated: the same user seeing the same assignment\n * will only trigger one event within the deduplication TTL.\n */\n\nimport type { TrafficalPlugin } from \"./types.js\";\nimport type { DecisionResult, DecisionEvent, ParameterValue } from \"@traffical/core\";\nimport { DecisionDeduplicator } from \"@traffical/core\";\nimport { SDK_VERSION } from \"../version.js\";\n\nconst SDK_NAME = \"js-client\";\n\n/**\n * Options for the DecisionTrackingPlugin.\n */\nexport interface DecisionTrackingPluginOptions {\n /**\n * Disable decision tracking entirely.\n * Default: false (tracking enabled)\n */\n disabled?: boolean;\n\n /**\n * Time-to-live for deduplication in milliseconds.\n * Same user+assignment combination won't be tracked again within this window.\n * Default: 1 hour (3600000 ms)\n */\n deduplicationTtlMs?: number;\n}\n\n/**\n * Dependencies injected by the SDK client.\n */\nexport interface DecisionTrackingPluginDeps {\n /** Organization ID */\n orgId: string;\n /** Project ID */\n projectId: string;\n /** Environment */\n env: string;\n /**\n * Function to log a decision event.\n * This is typically the EventLogger.log() method.\n */\n log: (event: DecisionEvent) => void;\n}\n\n/**\n * Creates a DecisionTrackingPlugin instance.\n *\n * @param options - Plugin configuration options\n * @param deps - Dependencies injected by the SDK client\n * @returns A TrafficalPlugin that tracks decision events\n *\n * @example\n * ```typescript\n * const plugin = createDecisionTrackingPlugin(\n * { disabled: false },\n * {\n * orgId: \"org_123\",\n * projectId: \"proj_456\",\n * env: \"production\",\n * log: (event) => eventLogger.log(event),\n * }\n * );\n * ```\n */\nexport function createDecisionTrackingPlugin(\n options: DecisionTrackingPluginOptions,\n deps: DecisionTrackingPluginDeps\n): TrafficalPlugin {\n const dedup = new DecisionDeduplicator({\n ttlMs: options.deduplicationTtlMs,\n });\n\n return {\n name: \"decision-tracking\",\n\n onDecision(decision: DecisionResult): void {\n // Skip if disabled\n if (options.disabled) {\n return;\n }\n\n // Skip if no unit key (can't attribute)\n const unitKey = decision.metadata.unitKeyValue;\n if (!unitKey) {\n return;\n }\n\n // Hash assignments for deduplication\n const hash = DecisionDeduplicator.hashAssignments(\n decision.assignments as Record<string, ParameterValue>\n );\n\n // Check deduplication\n if (!dedup.checkAndMark(unitKey, hash)) {\n return; // Duplicate, skip\n }\n\n // Build the decision event\n const event: DecisionEvent = {\n type: \"decision\",\n id: decision.decisionId,\n orgId: deps.orgId,\n projectId: deps.projectId,\n env: deps.env,\n unitKey,\n timestamp: decision.metadata.timestamp,\n assignments: decision.assignments,\n layers: decision.metadata.layers,\n // Include filtered context if available (for contextual bandit training)\n context: decision.metadata.filteredContext,\n sdkName: SDK_NAME,\n sdkVersion: SDK_VERSION,\n };\n\n // Log the event\n deps.log(event);\n },\n\n onDestroy(): void {\n // Clear the deduplication cache\n dedup.clear();\n },\n };\n}\n\n", "import type { TrafficalPlugin, PluginClientAPI } from \"./types.js\";\nimport type { DecisionResult, Context } from \"@traffical/core\";\n\nconst RDR_COOKIE = \"traffical_rdr\";\nconst RDR_MAX_AGE = 24 * 60 * 60; // 24 hours in seconds\n\nexport interface RedirectPluginOptions {\n /** Parameter key to check for a redirect URL. Default: \"redirect.url\" */\n parameterKey?: string;\n /** How to compare the resolved URL with the current location.\n * \"pathname\" compares against window.location.pathname (default).\n * \"href\" compares against the full window.location.href. */\n compareMode?: \"pathname\" | \"href\";\n /** Cookie name for redirect attribution. Default: \"traffical_rdr\" */\n cookieName?: string;\n}\n\nfunction setCookie(name: string, value: string, maxAge: number): void {\n if (typeof document === \"undefined\") return;\n try {\n document.cookie = `${name}=${encodeURIComponent(value)}; max-age=${maxAge}; path=/; SameSite=Lax`;\n } catch {\n // cookie access may fail\n }\n}\n\nexport function createRedirectPlugin(\n options: RedirectPluginOptions = {}\n): TrafficalPlugin {\n const parameterKey = options.parameterKey ?? \"redirect.url\";\n const compareMode = options.compareMode ?? \"pathname\";\n const cookieName = options.cookieName ?? RDR_COOKIE;\n\n return {\n name: \"redirect\",\n\n onInitialize(client: PluginClientAPI): void {\n if (typeof window === \"undefined\") return;\n\n client.decide({\n context: {},\n defaults: { [parameterKey]: \"\" },\n });\n },\n\n onBeforeDecision(context: Context): Context {\n if (typeof window === \"undefined\") return context;\n return {\n \"url.pathname\": window.location.pathname,\n ...context,\n };\n },\n\n onDecision(decision: DecisionResult): void {\n const url = decision.assignments[parameterKey];\n if (typeof url !== \"string\" || !url) return;\n\n const current =\n compareMode === \"href\"\n ? window.location.href\n : window.location.pathname;\n\n if (url === current) return;\n\n const layer = decision.metadata.layers.find(\n (l) => l.policyId && l.allocationName\n );\n if (layer) {\n setCookie(\n cookieName,\n JSON.stringify({\n l: layer.layerId,\n p: layer.policyId,\n a: layer.allocationName,\n ts: Date.now(),\n }),\n RDR_MAX_AGE\n );\n }\n\n window.location.replace(url);\n },\n };\n}\n", "import type { TrafficalPlugin } from \"./types.js\";\nimport type { ExposureEvent, TrackEvent, TrackAttribution } from \"@traffical/core\";\n\nconst COOKIE_NAME = \"traffical_rdr\";\nconst DEFAULT_EXPIRY_MS = 24 * 60 * 60 * 1000; // 24 hours\n\nexport interface RedirectAttributionPluginOptions {\n /** Cookie name to read attribution from. Default: \"traffical_rdr\" */\n cookieName?: string;\n /** How long the attribution cookie is valid in ms. Default: 24 hours */\n expiryMs?: number;\n}\n\ninterface StoredAttribution {\n l: string; // layerId\n p: string; // policyId\n a: string; // allocationName\n ts: number; // timestamp\n}\n\nfunction readCookie(name: string): string | null {\n if (typeof document === \"undefined\") return null;\n try {\n for (const part of document.cookie.split(\";\")) {\n const [k, v] = part.trim().split(\"=\");\n if (k === name && v) return decodeURIComponent(v);\n }\n } catch {\n // cookie access failed\n }\n return null;\n}\n\nfunction parseAttribution(\n cookieName: string,\n expiryMs: number\n): TrackAttribution | null {\n const raw = readCookie(cookieName);\n if (!raw) return null;\n\n try {\n const data: StoredAttribution = JSON.parse(raw);\n if (Date.now() - data.ts > expiryMs) return null;\n return {\n layerId: data.l,\n policyId: data.p,\n allocationName: data.a,\n };\n } catch {\n return null;\n }\n}\n\nexport function createRedirectAttributionPlugin(\n options: RedirectAttributionPluginOptions = {}\n): TrafficalPlugin {\n const cookieName = options.cookieName ?? COOKIE_NAME;\n const expiryMs = options.expiryMs ?? DEFAULT_EXPIRY_MS;\n\n function inject(event: { attribution?: TrackAttribution[] }): void {\n const attr = parseAttribution(cookieName, expiryMs);\n if (!attr) return;\n event.attribution = event.attribution ?? [];\n const already = event.attribution.some(\n (a) => a.layerId === attr.layerId && a.policyId === attr.policyId\n );\n if (!already) {\n event.attribution.push(attr);\n }\n }\n\n return {\n name: \"redirect-attribution\",\n\n onTrack(event: TrackEvent): boolean | void {\n inject(event);\n return true;\n },\n\n onExposure(event: ExposureEvent): boolean | void {\n inject(event as unknown as { attribution?: TrackAttribution[] });\n return true;\n },\n };\n}\n", "/**\n * Debug Plugin for Traffical JS Client SDK.\n *\n * Exposes SDK state via `window.__TRAFFICAL_DEBUG__` for consumption by\n * Traffical DevTools (or any external inspector). Supports multiple\n * simultaneous TrafficalClient instances.\n *\n * @example\n * ```typescript\n * import { createTrafficalClient, createDebugPlugin } from '@traffical/js-client';\n *\n * const client = await createTrafficalClient({\n * orgId: 'org_123',\n * projectId: 'proj_456',\n * env: 'production',\n * apiKey: 'pk_...',\n * plugins: [createDebugPlugin()],\n * });\n * ```\n *\n * @example IIFE / script tag\n * ```html\n * <script>\n * Traffical.init({\n * ...config,\n * plugins: [Traffical.createDebugPlugin({ instanceId: 'my-app' })],\n * });\n * </script>\n * ```\n */\n\nimport type {\n ConfigBundle,\n DecisionResult,\n ExposureEvent,\n TrackEvent,\n ParameterValue,\n LayerResolution,\n} from \"@traffical/core\";\nimport type { TrafficalPlugin, PluginClientAPI } from \"./types.js\";\nimport { SDK_VERSION } from \"../version.js\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport interface DebugPluginOptions {\n /** Unique identifier for this instance. Auto-generated if omitted. */\n instanceId?: string;\n /** Maximum events to retain in the ring buffer (default: 500). */\n maxEvents?: number;\n}\n\nexport interface DebugEvent {\n id: string;\n type: \"decision\" | \"exposure\" | \"track\";\n timestamp: number;\n data: unknown;\n}\n\nexport interface DebugState {\n ready: boolean;\n stableId: string | null;\n /** The unit key actually used for hashing (from last decision metadata). */\n effectiveUnitKey: string | null;\n configVersion: string | null;\n assignments: Record<string, unknown>;\n layers: LayerResolution[];\n lastDecisionId: string | null;\n /** Parameter overrides currently applied by the debug plugin. */\n overrides: Record<string, unknown>;\n}\n\nexport interface TrafficalDebugInstance {\n readonly id: string;\n readonly meta: {\n orgId: string;\n projectId: string;\n env: string;\n sdkVersion: string;\n };\n getState(): DebugState;\n subscribe(cb: (state: DebugState) => void): () => void;\n getEvents(limit?: number): DebugEvent[];\n onEvent(cb: (event: DebugEvent) => void): () => void;\n getConfigBundle(): ConfigBundle | null;\n setUnitKey(key: string): void;\n setOverride(key: string, value: unknown): void;\n clearOverride(key: string): void;\n clearAllOverrides(): void;\n getOverrides(): Record<string, unknown>;\n reDecide(): void;\n refresh(): Promise<void>;\n}\n\nexport type RegistryEventType = \"register\" | \"unregister\";\n\nexport interface RegistryEvent {\n type: RegistryEventType;\n instanceId: string;\n}\n\nexport interface TrafficalDebugRegistry {\n readonly version: 1;\n readonly instances: Record<string, TrafficalDebugInstance>;\n subscribe(cb: (event: RegistryEvent) => void): () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Global window augmentation\n// ---------------------------------------------------------------------------\n\ndeclare global {\n interface Window {\n __TRAFFICAL_DEBUG__?: TrafficalDebugRegistry;\n __TRAFFICAL_INSTANCES__?: unknown[];\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registry (singleton per window)\n// ---------------------------------------------------------------------------\n\nlet _registryListeners: Array<(event: RegistryEvent) => void> = [];\nlet _registryInstances: Record<string, TrafficalDebugInstance> = {};\n\nfunction getOrCreateRegistry(): TrafficalDebugRegistry {\n if (typeof window === \"undefined\") {\n return { version: 1, instances: _registryInstances, subscribe: () => () => {} };\n }\n\n if (!window.__TRAFFICAL_DEBUG__) {\n const registry: TrafficalDebugRegistry = {\n version: 1,\n instances: _registryInstances,\n subscribe(cb: (event: RegistryEvent) => void): () => void {\n _registryListeners.push(cb);\n return () => {\n _registryListeners = _registryListeners.filter((l) => l !== cb);\n };\n },\n };\n window.__TRAFFICAL_DEBUG__ = registry;\n }\n\n return window.__TRAFFICAL_DEBUG__!;\n}\n\nfunction emitRegistryEvent(event: RegistryEvent): void {\n for (const listener of _registryListeners) {\n try {\n listener(event);\n } catch {\n // Ignore listener errors\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nlet _idCounter = 0;\nfunction generateId(): string {\n return `traffical_${Date.now().toString(36)}_${(++_idCounter).toString(36)}`;\n}\n\nfunction generateEventId(): string {\n return `evt_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\nconst PLUGIN_NAME = \"traffical-debug\";\n\nexport function createDebugPlugin(\n options: DebugPluginOptions = {},\n): TrafficalPlugin {\n const instanceId = options.instanceId ?? generateId();\n const maxEvents = options.maxEvents ?? 500;\n\n // Internal state\n let _client: PluginClientAPI | null = null;\n let _bundle: ConfigBundle | null = null;\n let _assignments: Record<string, unknown> = {};\n let _layers: LayerResolution[] = [];\n let _lastDecisionId: string | null = null;\n let _effectiveUnitKey: string | null = null;\n const _events: DebugEvent[] = [];\n let _stateListeners: Array<(state: DebugState) => void> = [];\n let _eventListeners: Array<(event: DebugEvent) => void> = [];\n\n function buildState(): DebugState {\n return {\n ready: _client?.isInitialized === true,\n stableId: _client?.getStableId?.() ?? null,\n effectiveUnitKey: _effectiveUnitKey,\n configVersion: _client?.getConfigVersion?.() ?? null,\n assignments: { ..._assignments },\n layers: [..._layers],\n lastDecisionId: _lastDecisionId,\n overrides: _client?.getOverrides?.() ?? {},\n };\n }\n\n function notifyStateListeners(): void {\n const state = buildState();\n for (const cb of _stateListeners) {\n try {\n cb(state);\n } catch {\n // Ignore\n }\n }\n }\n\n function pushEvent(type: DebugEvent[\"type\"], data: unknown): void {\n const event: DebugEvent = {\n id: generateEventId(),\n type,\n timestamp: Date.now(),\n data,\n };\n _events.push(event);\n if (_events.length > maxEvents) {\n _events.splice(0, _events.length - maxEvents);\n }\n for (const cb of _eventListeners) {\n try {\n cb(event);\n } catch {\n // Ignore\n }\n }\n }\n\n function triggerReDecide(): void {\n if (_client) {\n try {\n _client.decide({ context: {}, defaults: {} });\n } catch {\n // Best-effort\n }\n }\n }\n\n // Build the instance that gets registered in the global registry\n const debugInstance: TrafficalDebugInstance = {\n id: instanceId,\n meta: {\n orgId: \"\",\n projectId: \"\",\n env: \"\",\n sdkVersion: SDK_VERSION,\n },\n\n getState: buildState,\n\n subscribe(cb: (state: DebugState) => void): () => void {\n _stateListeners.push(cb);\n return () => {\n _stateListeners = _stateListeners.filter((l) => l !== cb);\n };\n },\n\n getEvents(limit?: number): DebugEvent[] {\n if (limit !== undefined) {\n return _events.slice(-limit);\n }\n return [..._events];\n },\n\n onEvent(cb: (event: DebugEvent) => void): () => void {\n _eventListeners.push(cb);\n return () => {\n _eventListeners = _eventListeners.filter((l) => l !== cb);\n };\n },\n\n getConfigBundle(): ConfigBundle | null {\n return _bundle;\n },\n\n setUnitKey(key: string): void {\n if (_client?.identify) {\n _client.identify(key);\n } else if (_client?.setStableId) {\n _client.setStableId(key);\n }\n notifyStateListeners();\n },\n\n setOverride(key: string, value: unknown): void {\n if (_client?.applyOverrides) {\n _client.applyOverrides({ [key]: value as ParameterValue });\n }\n notifyStateListeners();\n triggerReDecide();\n },\n\n clearOverride(key: string): void {\n if (_client?.getOverrides && _client?.applyOverrides) {\n const current = _client.getOverrides();\n delete current[key];\n _client.clearOverrides?.();\n _client.applyOverrides(current);\n }\n notifyStateListeners();\n triggerReDecide();\n },\n\n clearAllOverrides(): void {\n _client?.clearOverrides?.();\n notifyStateListeners();\n triggerReDecide();\n },\n\n getOverrides(): Record<string, unknown> {\n return _client?.getOverrides?.() ?? {};\n },\n\n reDecide(): void {\n triggerReDecide();\n },\n\n async refresh(): Promise<void> {\n if (_client?.refreshConfig) {\n await _client.refreshConfig();\n }\n },\n };\n\n // The actual plugin\n const plugin: TrafficalPlugin = {\n name: PLUGIN_NAME,\n\n onInitialize(client: PluginClientAPI): void {\n _client = client;\n\n // Extract meta from the config bundle if available\n if (_bundle) {\n (debugInstance.meta as { orgId: string }).orgId = _bundle.orgId;\n (debugInstance.meta as { projectId: string }).projectId = _bundle.projectId;\n (debugInstance.meta as { env: string }).env = _bundle.env;\n }\n\n // Register in the global registry\n const registry = getOrCreateRegistry();\n (registry.instances as Record<string, TrafficalDebugInstance>)[instanceId] = debugInstance;\n emitRegistryEvent({ type: \"register\", instanceId });\n notifyStateListeners();\n },\n\n onConfigUpdate(bundle: ConfigBundle): void {\n _bundle = bundle;\n\n // Update meta from bundle\n (debugInstance.meta as { orgId: string }).orgId = bundle.orgId;\n (debugInstance.meta as { projectId: string }).projectId = bundle.projectId;\n (debugInstance.meta as { env: string }).env = bundle.env;\n\n notifyStateListeners();\n },\n\n onDecision(decision: DecisionResult): void {\n _assignments = { ...decision.assignments };\n _layers = decision.metadata?.layers ? [...decision.metadata.layers] : [];\n _lastDecisionId = decision.decisionId;\n if (decision.metadata?.unitKeyValue) {\n _effectiveUnitKey = decision.metadata.unitKeyValue;\n }\n pushEvent(\"decision\", decision);\n notifyStateListeners();\n },\n\n onResolve(params: Record<string, ParameterValue>): void {\n _assignments = { ...params };\n notifyStateListeners();\n },\n\n onExposure(event: ExposureEvent): boolean | void {\n pushEvent(\"exposure\", event);\n return true;\n },\n\n onTrack(event: TrackEvent): boolean | void {\n pushEvent(\"track\", event);\n return true;\n },\n\n onDestroy(): void {\n // Unregister from registry\n const registry =\n typeof window !== \"undefined\" ? window.__TRAFFICAL_DEBUG__ : null;\n if (registry) {\n delete (registry.instances as Record<string, TrafficalDebugInstance>)[\n instanceId\n ];\n emitRegistryEvent({ type: \"unregister\", instanceId });\n }\n _stateListeners = [];\n _eventListeners = [];\n _client = null;\n },\n };\n\n return plugin;\n}\n", "/**\n * PluginManager - Manages plugin lifecycle and hook execution.\n *\n * Provides a minimal hook-based system for extending SDK functionality.\n */\n\nimport type {\n ConfigBundle,\n DecisionResult,\n ExposureEvent,\n TrackEvent,\n Context,\n ParameterValue,\n} from \"@traffical/core\";\nimport type { TrafficalPlugin, PluginOptions, PluginClientAPI } from \"./types.js\";\n\ninterface RegisteredPlugin {\n plugin: TrafficalPlugin;\n priority: number;\n}\n\nexport class PluginManager {\n private _plugins: RegisteredPlugin[] = [];\n\n /**\n * Register a plugin. Returns true if the plugin was added,\n * false if a plugin with the same name is already registered.\n */\n register(options: PluginOptions | TrafficalPlugin): boolean {\n const plugin = \"plugin\" in options ? options.plugin : options;\n const priority = \"priority\" in options ? (options.priority ?? 0) : 0;\n\n if (this._plugins.some((p) => p.plugin.name === plugin.name)) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" already registered, skipping.`);\n return false;\n }\n\n this._plugins.push({ plugin, priority });\n this._plugins.sort((a, b) => b.priority - a.priority);\n return true;\n }\n\n /**\n * Unregister a plugin by name.\n */\n unregister(name: string): boolean {\n const index = this._plugins.findIndex((p) => p.plugin.name === name);\n if (index === -1) return false;\n\n this._plugins.splice(index, 1);\n return true;\n }\n\n /**\n * Get a registered plugin by name.\n */\n get(name: string): TrafficalPlugin | undefined {\n return this._plugins.find((p) => p.plugin.name === name)?.plugin;\n }\n\n /**\n * Get all registered plugins.\n */\n getAll(): TrafficalPlugin[] {\n return this._plugins.map((p) => p.plugin);\n }\n\n /**\n * Run onInitialize hooks, passing the client API reference.\n */\n async runInitialize(client: PluginClientAPI): Promise<void> {\n for (const { plugin } of this._plugins) {\n if (plugin.onInitialize) {\n try {\n await plugin.onInitialize(client);\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onInitialize error:`, error);\n }\n }\n }\n }\n\n /**\n * Run onConfigUpdate hooks.\n * Called when config bundle is fetched or refreshed.\n */\n runConfigUpdate(bundle: ConfigBundle): void {\n for (const { plugin } of this._plugins) {\n if (plugin.onConfigUpdate) {\n try {\n plugin.onConfigUpdate(bundle);\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onConfigUpdate error:`, error);\n }\n }\n }\n }\n\n /**\n * Run onBeforeDecision hooks.\n * Returns potentially modified context.\n */\n runBeforeDecision(context: Context): Context {\n let result = context;\n\n for (const { plugin } of this._plugins) {\n if (plugin.onBeforeDecision) {\n try {\n const modified = plugin.onBeforeDecision(result);\n if (modified) {\n result = modified;\n }\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onBeforeDecision error:`, error);\n }\n }\n }\n\n return result;\n }\n\n /**\n * Run onDecision hooks.\n */\n runDecision(decision: DecisionResult): void {\n for (const { plugin } of this._plugins) {\n if (plugin.onDecision) {\n try {\n plugin.onDecision(decision);\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onDecision error:`, error);\n }\n }\n }\n }\n\n /**\n * Run onResolve hooks.\n * Called after getParams() resolves parameters.\n */\n runResolve(params: Record<string, ParameterValue>): void {\n for (const { plugin } of this._plugins) {\n if (plugin.onResolve) {\n try {\n plugin.onResolve(params);\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onResolve error:`, error);\n }\n }\n }\n }\n\n /**\n * Run onExposure hooks.\n * Returns false if any plugin cancels the exposure.\n */\n runExposure(event: ExposureEvent): boolean {\n for (const { plugin } of this._plugins) {\n if (plugin.onExposure) {\n try {\n const result = plugin.onExposure(event);\n if (result === false) {\n return false;\n }\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onExposure error:`, error);\n }\n }\n }\n\n return true;\n }\n\n /**\n * Run onTrack hooks.\n * Returns false if any plugin cancels the track event.\n */\n runTrack(event: TrackEvent): boolean {\n for (const { plugin } of this._plugins) {\n if (plugin.onTrack) {\n try {\n const result = plugin.onTrack(event);\n if (result === false) {\n return false;\n }\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onTrack error:`, error);\n }\n }\n }\n\n return true;\n }\n\n /**\n * Run onDestroy hooks.\n */\n runDestroy(): void {\n for (const { plugin } of this._plugins) {\n if (plugin.onDestroy) {\n try {\n plugin.onDestroy();\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onDestroy error:`, error);\n }\n }\n }\n }\n\n /**\n * Clear all plugins.\n */\n clear(): void {\n this._plugins = [];\n }\n}\n\n// Re-export types\nexport type { TrafficalPlugin, PluginOptions, PluginClientAPI } from \"./types.js\";\n\n// Re-export plugins\nexport {\n createDecisionTrackingPlugin,\n type DecisionTrackingPluginOptions,\n type DecisionTrackingPluginDeps,\n} from \"./decision-tracking.js\";\n\nexport {\n createRedirectPlugin,\n type RedirectPluginOptions,\n} from \"./redirect.js\";\n\nexport {\n createRedirectAttributionPlugin,\n type RedirectAttributionPluginOptions,\n} from \"./redirect-attribution.js\";\n\nexport {\n createWarehouseNativeLoggerPlugin,\n createWarehouseNativeLogger,\n type WarehouseNativeLoggerOptions,\n type JitsuDestination,\n type AnalyticsLike,\n} from \"./warehouse-native-logger.js\";\n\nexport {\n createDebugPlugin,\n type DebugPluginOptions,\n type TrafficalDebugRegistry,\n type TrafficalDebugInstance,\n type DebugState,\n type DebugEvent,\n type RegistryEvent,\n} from \"./debug.js\";\n\n", "export type VisibilityState = \"foreground\" | \"background\";\nexport type VisibilityCallback = (state: VisibilityState) => void;\n\nexport interface LifecycleProvider {\n onVisibilityChange(callback: VisibilityCallback): void;\n removeVisibilityListener(callback: VisibilityCallback): void;\n /** Whether the page/app is in the process of unloading (browser-only concept). */\n isUnloading(): boolean;\n}\n\nexport function createBrowserLifecycleProvider(): LifecycleProvider {\n const listeners: VisibilityCallback[] = [];\n let unloading = false;\n\n function notify(state: VisibilityState): void {\n for (const cb of listeners) cb(state);\n }\n\n const onPageHide = (): void => {\n unloading = true;\n notify(\"background\");\n };\n\n const onVisibilityChange = (): void => {\n if (typeof document !== \"undefined\") {\n notify(document.visibilityState === \"hidden\" ? \"background\" : \"foreground\");\n }\n };\n\n const onBeforeUnload = (): void => {\n unloading = true;\n notify(\"background\");\n };\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"pagehide\", onPageHide);\n window.addEventListener(\"beforeunload\", onBeforeUnload);\n }\n if (typeof document !== \"undefined\") {\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n }\n\n return {\n onVisibilityChange(callback: VisibilityCallback): void {\n listeners.push(callback);\n },\n removeVisibilityListener(callback: VisibilityCallback): void {\n const idx = listeners.indexOf(callback);\n if (idx !== -1) listeners.splice(idx, 1);\n },\n isUnloading(): boolean {\n return unloading;\n },\n };\n}\n", "/**\n * TrafficalClient - JavaScript SDK for browser environments.\n *\n * Features:\n * - Same API as Node SDK: getParams(), decide(), trackExposure(), track()\n * - Error boundary wrapping (P0)\n * - Exposure deduplication (P0)\n * - Smart event batching with beacon on unload (P1)\n * - Plugin system (P2)\n * - Auto stable ID for anonymous users\n */\n\nimport {\n type ConfigBundle,\n type Context,\n type DecisionResult,\n type ParameterValue,\n type ExposureEvent,\n type TrackEvent,\n type TrackAttribution,\n type DecisionEvent,\n type BundlePolicy,\n type ServerResolveResponse,\n type ResolveOptions,\n type AssignmentLogger,\n type AssignmentType,\n type TrackableEvent,\n type TrackableEventLogger,\n type TrackEventMap,\n type OnSchemaWarnings,\n resolveParameters,\n decide as coreDecide,\n getUnitKeyValue,\n generateExposureId,\n generateTrackEventId,\n generateDecisionId,\n generateAssignmentId,\n} from \"@traffical/core\";\n\nimport {\n DecisionClient,\n createEdgeDecideRequest,\n type DecisionClientConfig,\n} from \"@traffical/core-io\";\n\nimport { ErrorBoundary, type ErrorBoundaryOptions } from \"./error-boundary.js\";\nimport { EventLogger } from \"./event-logger.js\";\nimport { ExposureDeduplicator } from \"./exposure-dedup.js\";\nimport { StableIdProvider } from \"./stable-id.js\";\nimport { createStorageProvider, type StorageProvider } from \"./storage.js\";\nimport { PluginManager, type TrafficalPlugin, createDecisionTrackingPlugin } from \"./plugins/index.js\";\nimport { createBrowserLifecycleProvider, type LifecycleProvider } from \"./lifecycle.js\";\nimport { SDK_VERSION } from \"./version.js\";\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst SDK_NAME = \"js-client\";\n\nconst DEFAULT_BASE_URL = \"https://sdk.traffical.io\";\nconst DEFAULT_REFRESH_INTERVAL_MS = 60_000; // 1 minute\nconst OFFLINE_WARNING_INTERVAL_MS = 300_000; // 5 minutes\nconst DECISION_CACHE_MAX_SIZE = 100; // Max decisions to cache for attribution lookup\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface TrafficalClientOptions {\n /** Organization ID */\n orgId: string;\n /** Project ID */\n projectId: string;\n /** Environment (e.g., \"production\", \"staging\") */\n env: string;\n /** API key for authentication */\n apiKey: string;\n /** Base URL for the SDK API (edge worker) */\n baseUrl?: string;\n /** Local config bundle for offline fallback */\n localConfig?: ConfigBundle;\n /** Refresh interval in milliseconds (default: 60000) */\n refreshIntervalMs?: number;\n /** Error boundary options */\n errorBoundary?: ErrorBoundaryOptions;\n /** Event batching options */\n eventBatchSize?: number;\n eventFlushIntervalMs?: number;\n /** Exposure deduplication session TTL */\n exposureSessionTtlMs?: number;\n /**\n * Whether to automatically track decision events (default: true).\n * When enabled, every call to decide() automatically sends a DecisionEvent\n * to the backend, enabling intent-to-treat analysis.\n */\n trackDecisions?: boolean;\n /**\n * Decision deduplication TTL in milliseconds (default: 1 hour).\n * Same user+assignment combination won't be tracked again within this window.\n */\n decisionDeduplicationTtlMs?: number;\n /** Plugins to register on init */\n plugins?: TrafficalPlugin[];\n /** Custom storage provider (default: localStorage) */\n storage?: StorageProvider;\n /** Disable automatic stable ID generation */\n disableAutoStableId?: boolean;\n /**\n * Attribution mode for track events (default: \"cumulative\").\n * - \"cumulative\": Attributes to ALL layers the user was exposed to in this session.\n * Best for cross-page funnels (catalog -> PDP -> checkout).\n * - \"decision\": Attributes only to the layers from the specific decision.\n * Use when strict single-decision attribution is required.\n */\n attributionMode?: \"cumulative\" | \"decision\";\n /**\n * Evaluation mode (default: \"bundle\").\n * - \"bundle\": SDK fetches config bundle, resolves parameters locally.\n * - \"server\": SDK delegates resolution to the edge worker via POST /v1/resolve.\n */\n evaluationMode?: \"bundle\" | \"server\";\n /** Lifecycle provider for visibility/unload events (default: browser lifecycle) */\n lifecycleProvider?: LifecycleProvider;\n\n /**\n * Optional callback for routing assignment events to a customer-managed\n * pipeline (e.g., Segment, Rudderstack, direct DB writes).\n *\n * When provided, called on every decide()/trackExposure() with a structured\n * AssignmentLogEntry. Enables the \"BYO assignment pipeline\" pattern for\n * warehouse-native analytics.\n */\n assignmentLogger?: AssignmentLogger;\n\n /**\n * When true, the SDK will NOT send events (decisions, exposures, tracks)\n * to the Traffical control plane. The SDK still fetches config from\n * Traffical CDN/edge for flag evaluation.\n *\n * Default: false\n */\n disableCloudEvents?: boolean;\n\n /**\n * When true, assignment logger calls are deduplicated per session\n * (same unit+policy+variant won't fire again). Default: true.\n */\n deduplicateAssignmentLogger?: boolean;\n\n /**\n * Optional callback for routing full events (exposure, track, decision)\n * to a customer-managed pipeline (e.g. Jitsu, Segment). Fires regardless\n * of disableCloudEvents, so you can send to your own sink instead of (or\n * in addition to) the Traffical edge.\n */\n eventLogger?: TrackableEventLogger;\n\n /**\n * Callback for schema validation warnings from the edge.\n * Only fires when event schemas are defined and enforcement is \"warn\".\n * Recommended for development builds to surface schema violations.\n *\n * @example\n * onSchemaWarnings: (warnings) => {\n * for (const w of warnings) {\n * console.warn(`[Traffical] Schema warning for \"${w.event}\":`, w.violations);\n * }\n * }\n */\n onSchemaWarnings?: OnSchemaWarnings;\n}\n\ninterface ClientState {\n bundle: ConfigBundle | null;\n etag: string | null;\n lastFetchTime: number;\n lastOfflineWarning: number;\n refreshTimer: ReturnType<typeof setInterval> | null;\n isInitialized: boolean;\n /** Cached server resolve response (server mode only) */\n serverResponse: ServerResolveResponse | null;\n /** Cached edge results for bundle mode with edge policies */\n cachedEdgeResults: ResolveOptions | null;\n}\n\n// =============================================================================\n// TrafficalClient Class\n// =============================================================================\n\nexport class TrafficalClient<TEvents extends TrackEventMap = TrackEventMap> {\n private readonly _options: Required<\n Pick<TrafficalClientOptions, \"orgId\" | \"projectId\" | \"env\" | \"apiKey\" | \"baseUrl\" | \"refreshIntervalMs\">\n > & {\n localConfig?: ConfigBundle;\n attributionMode: \"cumulative\" | \"decision\";\n evaluationMode: \"bundle\" | \"server\";\n };\n\n private _state: ClientState = {\n bundle: null,\n etag: null,\n lastFetchTime: 0,\n lastOfflineWarning: 0,\n refreshTimer: null,\n isInitialized: false,\n serverResponse: null,\n cachedEdgeResults: null,\n };\n\n private readonly _errorBoundary: ErrorBoundary;\n private readonly _storage: StorageProvider;\n private readonly _eventLogger: EventLogger;\n private readonly _exposureDedup: ExposureDeduplicator;\n private readonly _stableId: StableIdProvider;\n private readonly _plugins: PluginManager;\n private readonly _lifecycleProvider: LifecycleProvider;\n private readonly _decisionClient: DecisionClient | null;\n private readonly _assignmentLogger?: AssignmentLogger;\n private readonly _byoEventLogger?: TrackableEventLogger;\n private readonly _disableCloudEvents: boolean;\n private readonly _assignmentLoggerDedup: ExposureDeduplicator | null;\n /** Cache of recent decisions for attribution lookup when track() is called */\n private readonly _decisionCache: Map<string, DecisionResult> = new Map();\n /**\n * Cumulative attribution map, keyed by unitKey \u2192 layerId:policyId \u2192 TrackAttribution.\n * Unlike _decisionCache (bounded to DECISION_CACHE_MAX_SIZE), this map accumulates\n * every attribution entry from every decide() call during the session. This prevents\n * attribution loss when per-entity policies (e.g. per-product OptimizedProductCards)\n * flood the decision cache and evict earlier page-level decisions.\n */\n private readonly _cumulativeAttribution: Map<string, Map<string, TrackAttribution>> = new Map();\n private _identityListeners: Array<(unitKey: string) => void> = [];\n private _overrideListeners: Array<(overrides: Record<string, ParameterValue>) => void> = [];\n private _overrides: Record<string, ParameterValue> = {};\n\n constructor(options: TrafficalClientOptions) {\n const evaluationMode = options.evaluationMode ?? \"bundle\";\n this._options = {\n orgId: options.orgId,\n projectId: options.projectId,\n env: options.env,\n apiKey: options.apiKey,\n baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,\n localConfig: options.localConfig,\n refreshIntervalMs: options.refreshIntervalMs ?? DEFAULT_REFRESH_INTERVAL_MS,\n attributionMode: options.attributionMode ?? \"cumulative\",\n evaluationMode,\n };\n\n // Create DecisionClient when needed (server mode, or bundle mode may use for edge policies)\n const decisionClientConfig: DecisionClientConfig = {\n baseUrl: this._options.baseUrl,\n orgId: this._options.orgId,\n projectId: this._options.projectId,\n env: this._options.env,\n apiKey: this._options.apiKey,\n };\n this._decisionClient = new DecisionClient(decisionClientConfig);\n\n // Initialize components\n this._errorBoundary = new ErrorBoundary(options.errorBoundary);\n this._storage = options.storage ?? createStorageProvider();\n this._lifecycleProvider = options.lifecycleProvider ?? createBrowserLifecycleProvider();\n\n // Default dev-mode schema warnings handler\n if (!options.onSchemaWarnings) {\n try {\n const isDev = typeof globalThis !== \"undefined\"\n && (globalThis as any).process?.env?.NODE_ENV === \"development\";\n if (isDev) {\n options.onSchemaWarnings = (warnings) => {\n for (const w of warnings) {\n console.warn(\n `[Traffical] Schema warning for \"${w.event}\":`,\n w.violations.map((v: { path: string; message: string }) => `${v.path}: ${v.message}`).join(\", \")\n );\n }\n };\n }\n } catch {\n // process may not be available in browser environments\n }\n }\n\n this._eventLogger = new EventLogger({\n endpoint: `${this._options.baseUrl}/v1/events/batch`,\n apiKey: options.apiKey,\n storage: this._storage,\n lifecycleProvider: this._lifecycleProvider,\n batchSize: options.eventBatchSize,\n flushIntervalMs: options.eventFlushIntervalMs,\n onError: (error) => {\n console.warn(\"[Traffical] Event logging error:\", error.message);\n },\n onSchemaWarnings: options.onSchemaWarnings,\n });\n\n this._exposureDedup = new ExposureDeduplicator({\n storage: this._storage,\n sessionTtlMs: options.exposureSessionTtlMs,\n });\n\n this._stableId = new StableIdProvider({\n storage: this._storage,\n });\n\n this._plugins = new PluginManager();\n\n // Warehouse-native options\n this._assignmentLogger = options.assignmentLogger;\n this._byoEventLogger = options.eventLogger;\n this._disableCloudEvents = options.disableCloudEvents ?? false;\n this._assignmentLoggerDedup = (options.deduplicateAssignmentLogger !== false && options.assignmentLogger)\n ? new ExposureDeduplicator({ storage: this._storage, sessionTtlMs: options.exposureSessionTtlMs })\n : null;\n\n // Register decision tracking plugin (enabled by default). Skipped only when\n // cloud events are disabled AND there is no BYO event logger to receive them.\n if (options.trackDecisions !== false && (!this._disableCloudEvents || this._byoEventLogger)) {\n this._plugins.register({\n plugin: createDecisionTrackingPlugin(\n { deduplicationTtlMs: options.decisionDeduplicationTtlMs },\n {\n orgId: this._options.orgId,\n projectId: this._options.projectId,\n env: this._options.env,\n log: (event: DecisionEvent) => this._dispatchEvent(event),\n }\n ),\n priority: 100, // High priority so it runs before user plugins\n });\n }\n\n // Register user-provided plugins\n if (options.plugins) {\n for (const plugin of options.plugins) {\n this._plugins.register(plugin);\n }\n }\n\n // Initialize with local config if provided\n if (this._options.localConfig) {\n this._state.bundle = this._options.localConfig;\n // Notify plugins about the local config\n this._plugins.runConfigUpdate(this._options.localConfig);\n }\n\n // Register on global instance list so DevTools can discover ES-module SDKs\n if (typeof window !== \"undefined\") {\n const w = window as unknown as Record<string, unknown>;\n (w.__TRAFFICAL_INSTANCES__ ??= [] as TrafficalClient[]) as TrafficalClient[];\n (w.__TRAFFICAL_INSTANCES__ as TrafficalClient[]).push(this);\n }\n }\n\n // ===========================================================================\n // Initialization\n // ===========================================================================\n\n /**\n * Initializes the client by fetching the config bundle.\n */\n async initialize(): Promise<void> {\n await this._errorBoundary.captureAsync(\n \"initialize\",\n async () => {\n if (this._options.evaluationMode === \"server\") {\n await this._fetchServerResolve();\n } else {\n await this._fetchConfig();\n }\n this._startBackgroundRefresh();\n this._state.isInitialized = true;\n\n // Run plugin onInitialize hooks (pass client reference for autonomous plugins)\n await this._plugins.runInitialize(this);\n },\n undefined\n );\n }\n\n /**\n * Check if the client is initialized.\n */\n get isInitialized(): boolean {\n return this._state.isInitialized;\n }\n\n /**\n * Stops background refresh and cleans up resources.\n */\n destroy(): void {\n if (this._state.refreshTimer) {\n clearInterval(this._state.refreshTimer);\n this._state.refreshTimer = null;\n }\n\n if (this._lifecycleProvider.isUnloading()) {\n this._eventLogger.flushBeacon();\n } else {\n this._eventLogger.flush().catch(() => {});\n }\n this._eventLogger.destroy();\n\n // Run plugin onDestroy hooks\n this._plugins.runDestroy();\n\n // Clear listeners and overrides\n this._identityListeners = [];\n this._overrideListeners = [];\n this._overrides = {};\n\n // Remove from global instance list\n if (typeof window !== \"undefined\") {\n const w = window as unknown as Record<string, unknown>;\n const instances = w.__TRAFFICAL_INSTANCES__ as TrafficalClient[] | undefined;\n if (instances) {\n const idx = instances.indexOf(this);\n if (idx !== -1) instances.splice(idx, 1);\n }\n }\n }\n\n // ===========================================================================\n // Config Management\n // ===========================================================================\n\n /**\n * Manually refreshes the config bundle.\n */\n async refreshConfig(): Promise<void> {\n await this._errorBoundary.swallow(\"refreshConfig\", async () => {\n if (this._options.evaluationMode === \"server\") {\n await this._fetchServerResolve();\n } else {\n await this._fetchConfig();\n }\n });\n }\n\n /**\n * Gets the current config bundle version.\n */\n getConfigVersion(): string | null {\n return this._state.serverResponse?.stateVersion ?? this._state.bundle?.version ?? null;\n }\n\n // ===========================================================================\n // Parameter Resolution\n // ===========================================================================\n\n /**\n * Resolves parameters with defaults as fallback.\n */\n getParams<T extends Record<string, ParameterValue>>(options: { context: Context; defaults: T }): T {\n return this._errorBoundary.capture(\n \"getParams\",\n () => {\n // Server mode: return from cached server response\n if (this._options.evaluationMode === \"server\" && this._state.serverResponse) {\n const result = { ...options.defaults } as Record<string, ParameterValue>;\n for (const [key, value] of Object.entries(this._state.serverResponse.assignments)) {\n if (key in result) {\n result[key] = value;\n }\n }\n this._plugins.runResolve(result as T);\n this._applyOverridesToResult(result);\n return result as T;\n }\n\n const bundle = this._getEffectiveBundle();\n const context = this._enrichContext(options.context);\n const params = resolveParameters<T>(bundle, context, options.defaults);\n\n // Run plugin onResolve hooks (e.g., DOM binding plugin)\n this._plugins.runResolve(params);\n\n // Apply parameter overrides (post-resolution, post-plugin)\n this._applyOverridesToResult(params);\n\n return params;\n },\n options.defaults\n );\n }\n\n /**\n * Makes a decision with full metadata for tracking.\n */\n decide<T extends Record<string, ParameterValue>>(options: { context: Context; defaults: T }): DecisionResult {\n return this._errorBoundary.capture(\n \"decide\",\n () => {\n // Server mode: return from cached server response\n if (this._options.evaluationMode === \"server\" && this._state.serverResponse) {\n const resp = this._state.serverResponse;\n const assignments = { ...options.defaults } as Record<string, ParameterValue>;\n for (const [key, value] of Object.entries(resp.assignments)) {\n if (key in assignments) {\n assignments[key] = value;\n }\n }\n const decision: DecisionResult = {\n decisionId: resp.decisionId,\n assignments,\n metadata: resp.metadata,\n };\n this._cacheDecision(decision);\n this._updateCumulativeAttribution(decision);\n this._plugins.runDecision(decision);\n this._applyOverridesToResult(decision.assignments);\n this._emitAssignmentLogEntries(decision, \"decision\");\n return decision;\n }\n\n const bundle = this._getEffectiveBundle();\n\n // Run plugin onBeforeDecision hooks\n let context = this._enrichContext(options.context);\n context = this._plugins.runBeforeDecision(context);\n\n // Pass cached edge results (from bundle mode pre-fetch) if available\n const edgeOpts = this._state.cachedEdgeResults ?? undefined;\n const decision = coreDecide<T>(bundle, context, options.defaults, edgeOpts);\n\n // Cache decision for attribution lookup when track() is called\n this._cacheDecision(decision);\n // Accumulate attribution entries (survives decision cache eviction)\n this._updateCumulativeAttribution(decision);\n\n // Run plugin onDecision hooks (e.g., DOM binding plugin)\n this._plugins.runDecision(decision);\n\n // Apply parameter overrides (post-resolution, post-plugin)\n this._applyOverridesToResult(decision.assignments);\n\n this._emitAssignmentLogEntries(decision, \"decision\");\n\n return decision;\n },\n {\n decisionId: generateDecisionId(),\n assignments: options.defaults,\n metadata: {\n timestamp: new Date().toISOString(),\n unitKeyValue: \"\",\n layers: [],\n },\n }\n );\n }\n\n // ===========================================================================\n // Event Tracking\n // ===========================================================================\n\n /**\n * Tracks an exposure event.\n * Automatically deduplicates exposures for the same user/variant.\n *\n * Skips layers marked `attributionOnly` \u2014 those were resolved for\n * attribution/assignment purposes only (no parameters were requested\n * from that layer) and should not count as exposures.\n */\n trackExposure(decision: DecisionResult): void {\n this._errorBoundary.capture(\n \"trackExposure\",\n () => {\n const unitKey = decision.metadata.unitKeyValue;\n if (!unitKey) return;\n\n // Emit to assignment logger (separate from cloud events)\n this._emitAssignmentLogEntries(decision, \"exposure\");\n\n // Check each layer for deduplication\n for (const layer of decision.metadata.layers) {\n if (!layer.policyId || !layer.allocationName) continue;\n\n // Skip attribution-only layers \u2014 the user wasn't exposed to\n // parameters from this layer, so no exposure event should fire.\n if (layer.attributionOnly) continue;\n\n // Deduplicate\n const isNew = this._exposureDedup.checkAndMark(unitKey, layer.policyId, layer.allocationName);\n if (!isNew) continue;\n\n const event: ExposureEvent = {\n type: \"exposure\",\n id: generateExposureId(), // Unique exposure ID (not same as decision)\n decisionId: decision.decisionId,\n orgId: this._options.orgId,\n projectId: this._options.projectId,\n env: this._options.env,\n unitKey,\n timestamp: new Date().toISOString(),\n assignments: decision.assignments,\n layers: decision.metadata.layers,\n context: decision.metadata.filteredContext,\n sdkName: SDK_NAME,\n sdkVersion: SDK_VERSION,\n };\n\n // Run plugin onExposure hooks\n if (!this._plugins.runExposure(event)) {\n continue;\n }\n\n this._dispatchEvent(event);\n }\n },\n undefined\n );\n }\n\n /**\n * Tracks a user event.\n * \n * @param eventName - The event name (e.g., 'purchase', 'add_to_cart')\n * @param properties - Optional event properties (including value for optimization)\n * @param options - Optional tracking options (decisionId, unitKey)\n * \n * @example\n * // Track a purchase with revenue\n * client.track('purchase', { value: 99.99, orderId: 'ord_123' });\n * \n * // Track a simple event\n * client.track('add_to_cart', { itemId: 'sku_456' });\n * \n * // Track with explicit decision attribution\n * client.track('checkout_complete', { value: 1 }, { decisionId: decision.decisionId });\n */\n track<E extends Extract<keyof TEvents, string>>(\n eventName: E,\n properties?: TEvents[E],\n options?: { decisionId?: string; unitKey?: string }\n ): void {\n this._errorBoundary.capture(\n \"track\",\n () => {\n const unitKey = options?.unitKey ?? this._stableId.getId();\n const value = typeof properties?.value === 'number' ? properties.value : undefined;\n\n // Auto-populate attribution from cached decisions\n const attribution = this._buildAttribution(unitKey, options?.decisionId);\n const decisionId = options?.decisionId;\n\n const event: TrackEvent = {\n type: \"track\",\n id: generateTrackEventId(),\n orgId: this._options.orgId,\n projectId: this._options.projectId,\n env: this._options.env,\n unitKey,\n timestamp: new Date().toISOString(),\n event: eventName,\n value,\n properties,\n decisionId,\n attribution,\n sdkName: SDK_NAME,\n sdkVersion: SDK_VERSION,\n };\n\n // Run plugin onTrack hooks\n if (!this._plugins.runTrack(event)) {\n return;\n }\n\n this._dispatchEvent(event);\n },\n undefined\n );\n }\n\n /**\n * Flush pending events immediately.\n */\n async flushEvents(): Promise<void> {\n await this._errorBoundary.swallow(\"flushEvents\", async () => {\n await this._eventLogger.flush();\n });\n }\n\n // ===========================================================================\n // Plugin Management\n // ===========================================================================\n\n /**\n * Register a plugin.\n * If the client is already initialized, fires onInitialize and onConfigUpdate\n * immediately so late-registered plugins (e.g. debug plugin) work correctly.\n */\n use(plugin: TrafficalPlugin): this {\n const added = this._plugins.register(plugin);\n if (!added) return this;\n\n if (this._state.isInitialized) {\n try {\n plugin.onInitialize?.(this);\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" late onInitialize error:`, error);\n }\n if (this._state.bundle) {\n try {\n plugin.onConfigUpdate?.(this._state.bundle);\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" late onConfigUpdate error:`, error);\n }\n }\n }\n\n return this;\n }\n\n /**\n * Get a registered plugin by name.\n */\n getPlugin(name: string): TrafficalPlugin | undefined {\n return this._plugins.get(name);\n }\n\n // ===========================================================================\n // Stable ID\n // ===========================================================================\n\n /**\n * Get the stable ID for the current user.\n */\n getStableId(): string {\n return this._stableId.getId();\n }\n\n /**\n * Set a custom stable ID (e.g., when user logs in).\n * Low-level \u2014 does NOT notify framework providers. Use `identify()` instead\n * when you want the UI to update.\n */\n setStableId(id: string): void {\n this._stableId.setId(id);\n }\n\n /**\n * Change the user identity and notify all listeners (framework providers,\n * plugins, DevTools). This causes React/Svelte/RN providers to re-evaluate\n * decisions with the new identity, updating the UI.\n *\n * @example\n * // After user logs in\n * client.identify('user_logged_in_123');\n */\n identify(unitKey: string): void {\n this._stableId.setId(unitKey);\n for (const cb of this._identityListeners) {\n try {\n cb(unitKey);\n } catch {\n // Ignore listener errors\n }\n }\n }\n\n /**\n * Subscribe to identity changes triggered by `identify()`.\n * Returns an unsubscribe function.\n */\n onIdentityChange(cb: (unitKey: string) => void): () => void {\n this._identityListeners.push(cb);\n return () => {\n this._identityListeners = this._identityListeners.filter(l => l !== cb);\n };\n }\n\n /**\n * Subscribe to override changes triggered by `applyOverrides()` / `clearOverrides()`.\n * Framework providers use this to re-evaluate decisions when overrides change.\n * Returns an unsubscribe function.\n */\n onOverridesChange(cb: (overrides: Record<string, ParameterValue>) => void): () => void {\n this._overrideListeners.push(cb);\n return () => {\n this._overrideListeners = this._overrideListeners.filter(l => l !== cb);\n };\n }\n\n // ===========================================================================\n // Parameter Overrides (Plugin API \u2014 not intended for direct public use)\n // ===========================================================================\n\n /**\n * Set parameter overrides. Only keys present in a decision's assignments\n * or getParams defaults will be overridden. Merges with existing overrides.\n *\n * Exposed via `PluginClientAPI` for debug tooling \u2014 not a public API.\n */\n applyOverrides(overrides: Record<string, ParameterValue>): void {\n Object.assign(this._overrides, overrides);\n this._notifyOverrideListeners();\n }\n\n /**\n * Clear all parameter overrides.\n */\n clearOverrides(): void {\n this._overrides = {};\n this._notifyOverrideListeners();\n }\n\n /**\n * Get a copy of the current overrides map.\n */\n getOverrides(): Record<string, ParameterValue> {\n return { ...this._overrides };\n }\n\n // ===========================================================================\n // Private Methods\n // ===========================================================================\n\n private _notifyOverrideListeners(): void {\n const snapshot = { ...this._overrides };\n for (const cb of this._overrideListeners) {\n try {\n cb(snapshot);\n } catch {\n // Ignore listener errors\n }\n }\n }\n\n /**\n * Routes a built event to the BYO event logger (if configured) and to the\n * Traffical edge batcher (unless cloud events are disabled).\n */\n private _dispatchEvent(event: TrackableEvent): void {\n if (this._byoEventLogger) {\n try {\n this._byoEventLogger(event);\n } catch {\n // Swallow BYO logger errors \u2014 they must not break SDK event handling.\n }\n }\n if (!this._disableCloudEvents) {\n this._eventLogger.log(event);\n }\n }\n\n private _emitAssignmentLogEntries(decision: DecisionResult, type: AssignmentType): void {\n if (!this._assignmentLogger) return;\n const unitKey = decision.metadata.unitKeyValue;\n if (!unitKey) return;\n\n for (const layer of decision.metadata.layers) {\n if (!layer.policyId || !layer.allocationName) continue;\n\n // Dedup: skip if we've already logged this unit+policy+allocation+type in this session\n if (this._assignmentLoggerDedup) {\n const isNew = this._assignmentLoggerDedup.checkAndMark(\n unitKey,\n layer.policyId,\n `${layer.allocationName}:${type}`,\n );\n if (!isNew) continue;\n }\n\n this._assignmentLogger({\n unitKey,\n policyId: layer.policyId,\n policyKey: layer.policyKey,\n allocationName: layer.allocationName,\n allocationKey: layer.allocationKey,\n timestamp: decision.metadata.timestamp,\n layerId: layer.layerId,\n allocationId: layer.allocationId,\n orgId: this._options.orgId,\n projectId: this._options.projectId,\n env: this._options.env,\n sdkName: SDK_NAME,\n sdkVersion: SDK_VERSION,\n properties: decision.metadata.filteredContext,\n type,\n decisionId: decision.decisionId,\n anonymousId: this._stableId.getId(),\n id: generateAssignmentId(),\n });\n }\n }\n\n private _applyOverridesToResult(target: Record<string, ParameterValue>): void {\n const keys = Object.keys(this._overrides);\n if (keys.length === 0) return;\n for (const k of keys) {\n if (k in target) {\n target[k] = this._overrides[k];\n }\n }\n }\n\n private _getEffectiveBundle(): ConfigBundle | null {\n return this._state.bundle ?? this._options.localConfig ?? null;\n }\n\n private _enrichContext(context: Context): Context {\n // Add stable ID if not already present\n const bundle = this._getEffectiveBundle();\n const unitKey = bundle?.hashing?.unitKey ?? \"userId\";\n\n if (!context[unitKey]) {\n return {\n ...context,\n [unitKey]: this._stableId.getId(),\n };\n }\n\n return context;\n }\n\n private async _fetchConfig(): Promise<void> {\n const url = `${this._options.baseUrl}/v1/config/${this._options.projectId}?env=${this._options.env}`;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this._options.apiKey}`,\n };\n\n if (this._state.etag) {\n headers[\"If-None-Match\"] = this._state.etag;\n }\n\n try {\n const response = await fetch(url, { method: \"GET\", headers });\n\n if (response.status === 304) {\n this._state.lastFetchTime = Date.now();\n return;\n }\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const bundle = (await response.json()) as ConfigBundle;\n const etag = response.headers.get(\"ETag\");\n\n this._state.bundle = bundle;\n this._state.etag = etag;\n this._state.lastFetchTime = Date.now();\n\n // Pre-fetch edge results if bundle has edge-mode policies\n if (this._findEdgePolicies(bundle).length > 0) {\n const edgeResults = await this._prefetchEdgeResults(bundle, this._enrichContext({}));\n this._state.cachedEdgeResults = edgeResults;\n } else {\n this._state.cachedEdgeResults = null;\n }\n\n // Run plugin onConfigUpdate hooks (e.g., DOM binding plugin)\n this._plugins.runConfigUpdate(bundle);\n } catch (error) {\n this._logOfflineWarning(error);\n }\n }\n\n private _startBackgroundRefresh(): void {\n const interval = this._options.evaluationMode === \"server\"\n ? (this._state.serverResponse?.suggestedRefreshMs ?? this._options.refreshIntervalMs)\n : this._options.refreshIntervalMs;\n\n if (interval <= 0) return;\n\n this._state.refreshTimer = setInterval(() => {\n if (this._options.evaluationMode === \"server\") {\n this._fetchServerResolve().catch(() => {});\n } else {\n this._fetchConfig().catch(() => {});\n }\n }, interval);\n }\n\n private async _fetchServerResolve(): Promise<void> {\n if (!this._decisionClient) return;\n\n try {\n const context = this._enrichContext({});\n const response = await this._decisionClient.resolve({ context });\n if (response) {\n this._state.serverResponse = response;\n this._state.lastFetchTime = Date.now();\n }\n } catch (error) {\n this._logOfflineWarning(error);\n }\n }\n\n /**\n * Finds edge-mode policies in the current bundle.\n */\n private _findEdgePolicies(bundle: ConfigBundle): BundlePolicy[] {\n const policies: BundlePolicy[] = [];\n for (const layer of bundle.layers) {\n for (const policy of layer.policies) {\n if (\n policy.state === \"running\" &&\n policy.entityConfig?.resolutionMode === \"edge\"\n ) {\n policies.push(policy);\n }\n }\n }\n return policies;\n }\n\n /**\n * Pre-fetches edge results for edge-mode policies in bundle mode.\n * Returns ResolveOptions with edgeResults populated.\n */\n private async _prefetchEdgeResults(\n bundle: ConfigBundle,\n context: Context\n ): Promise<ResolveOptions> {\n if (!this._decisionClient) return {};\n\n const edgePolicies = this._findEdgePolicies(bundle);\n if (edgePolicies.length === 0) return {};\n\n const unitKeyValue = getUnitKeyValue(bundle, context);\n if (!unitKeyValue) return {};\n\n const requests = edgePolicies\n .map((policy) => {\n if (!policy.entityConfig) return null;\n const allocationCount = policy.entityConfig.dynamicAllocations\n ? (typeof context[policy.entityConfig.dynamicAllocations.countKey] === \"number\"\n ? Math.floor(context[policy.entityConfig.dynamicAllocations.countKey] as number)\n : 0)\n : policy.allocations.length;\n\n return createEdgeDecideRequest(\n policy.id,\n policy.entityConfig.entityKeys,\n context,\n unitKeyValue,\n allocationCount || undefined\n );\n })\n .filter((r): r is NonNullable<typeof r> => r !== null);\n\n if (requests.length === 0) return {};\n\n try {\n const responses = await this._decisionClient.decideEntityBatch(requests);\n const edgeResults = new Map<string, { allocationIndex: number; entityId: string }>();\n\n for (let i = 0; i < requests.length; i++) {\n const resp = responses[i];\n if (resp) {\n edgeResults.set(requests[i].policyId, {\n allocationIndex: resp.allocationIndex,\n entityId: requests[i].entityId,\n });\n }\n }\n\n return edgeResults.size > 0 ? { edgeResults } : {};\n } catch {\n return {};\n }\n }\n\n private _logOfflineWarning(error: unknown): void {\n const now = Date.now();\n if (now - this._state.lastOfflineWarning > OFFLINE_WARNING_INTERVAL_MS) {\n console.warn(\n `[Traffical] Failed to fetch config: ${error instanceof Error ? error.message : String(error)}. Using ${this._state.bundle ? \"cached\" : \"local\"} config.`\n );\n this._state.lastOfflineWarning = now;\n }\n }\n\n /**\n * Caches a decision for attribution lookup when track() is called.\n * Maintains a bounded cache to prevent memory leaks.\n */\n private _cacheDecision(decision: DecisionResult): void {\n // Evict oldest entries if cache is full\n if (this._decisionCache.size >= DECISION_CACHE_MAX_SIZE) {\n // Get first (oldest) key and delete it\n const firstKey = this._decisionCache.keys().next().value;\n if (firstKey) {\n this._decisionCache.delete(firstKey);\n }\n }\n this._decisionCache.set(decision.decisionId, decision);\n }\n\n /**\n * Accumulates attribution entries from a decision into the session-level map.\n * Keyed by unitKey \u2192 layerId:policyId with last-write-wins semantics.\n * This ensures attribution survives decision cache eviction.\n */\n private _updateCumulativeAttribution(decision: DecisionResult): void {\n const unitKey = decision.metadata.unitKeyValue;\n if (!unitKey) return;\n\n let userAttrs = this._cumulativeAttribution.get(unitKey);\n if (!userAttrs) {\n userAttrs = new Map<string, TrackAttribution>();\n this._cumulativeAttribution.set(unitKey, userAttrs);\n }\n\n for (const l of decision.metadata.layers) {\n if (!l.policyId || !l.allocationName) continue;\n const key = `${l.layerId}:${l.policyId}`;\n // Last-write-wins: later decisions overwrite earlier ones.\n // For per-entity dynamic allocation policies this keeps only the most\n // recent allocation; for normal policies allocationName is deterministic\n // so the overwrite is a no-op.\n userAttrs.set(key, {\n layerId: l.layerId,\n policyId: l.policyId,\n allocationName: l.allocationName,\n });\n }\n }\n\n /**\n * Builds attribution for a track event based on the configured attribution mode.\n *\n * - \"cumulative\": Collects layers from ALL cached decisions for this unit,\n * deduplicated by layerId:policyId (last-write-wins). This ensures cross-page\n * funnels (e.g., catalog -> PDP -> checkout) attribute correctly to all\n * experiments the user is exposed to. For per-entity dynamic allocation\n * policies, only the most recent allocation is kept to avoid attributing\n * rewards to allocations from other entities (e.g., different products).\n *\n * - \"decision\": Only uses layers from the single decision matching decisionId.\n * Legacy behavior for strict single-decision attribution.\n */\n private _buildAttribution(\n unitKey: string,\n decisionId?: string\n ): TrackAttribution[] | undefined {\n if (this._options.attributionMode === \"decision\") {\n // Legacy behavior: single-decision attribution\n if (!decisionId) return undefined;\n const cachedDecision = this._decisionCache.get(decisionId);\n if (!cachedDecision) return undefined;\n return cachedDecision.metadata.layers\n .filter((l) => l.policyId && l.allocationName)\n .map((l) => ({\n layerId: l.layerId,\n policyId: l.policyId!,\n allocationName: l.allocationName!,\n }));\n }\n\n // Cumulative mode: use the pre-built cumulative attribution map.\n // This map accumulates entries from ALL decide() calls for this unit during\n // the session, deduplicated by layerId:policyId (last-write-wins).\n // Unlike iterating _decisionCache, this is immune to cache eviction \u2014\n // e.g. when per-entity OptimizedProductCard decisions push out earlier\n // page-level decisions that contain important layer assignments.\n const userAttrs = this._cumulativeAttribution.get(unitKey);\n return userAttrs && userAttrs.size > 0\n ? Array.from(userAttrs.values())\n : undefined;\n }\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Creates and initializes a Traffical client.\n */\nexport async function createTrafficalClient<TEvents extends TrackEventMap = TrackEventMap>(options: TrafficalClientOptions): Promise<TrafficalClient<TEvents>> {\n const client = new TrafficalClient<TEvents>(options);\n await client.initialize();\n return client;\n}\n\n/**\n * Creates a Traffical client without initializing (synchronous).\n */\nexport function createTrafficalClientSync<TEvents extends TrackEventMap = TrackEventMap>(options: TrafficalClientOptions): TrafficalClient<TEvents> {\n return new TrafficalClient<TEvents>(options);\n}\n\n", "/**\n * DOM Binding Plugin\n *\n * Automatically applies parameter values to DOM elements based on bindings\n * configured in Traffical via the visual editor.\n *\n * Features:\n * - URL pattern matching to apply bindings only on matching pages\n * - MutationObserver for dynamic content (SPA support)\n * - Supports multiple property types: innerHTML, textContent, src, href, style.*\n *\n * @example\n * ```typescript\n * import { createTrafficalClient, createDOMBindingPlugin } from '@traffical/js-client';\n *\n * const client = await createTrafficalClient({\n * // ... config\n * plugins: [createDOMBindingPlugin()],\n * });\n * ```\n */\n\nimport type { ConfigBundle, BundleDOMBinding, ParameterValue } from \"@traffical/core\";\nimport type { TrafficalPlugin } from \"./types.js\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface DOMBindingPluginOptions {\n /**\n * Whether to start observing DOM mutations automatically.\n * Useful for SPAs where content is dynamically loaded.\n * @default true\n */\n observeMutations?: boolean;\n\n /**\n * Debounce time in ms for mutation-triggered reapplication.\n * @default 100\n */\n debounceMs?: number;\n}\n\n// =============================================================================\n// DOM Binding Plugin\n// =============================================================================\n\n/**\n * Creates a DOM binding plugin instance.\n *\n * This plugin applies parameter values to DOM elements based on bindings\n * defined via the visual editor.\n */\nexport function createDOMBindingPlugin(\n options: DOMBindingPluginOptions = {}\n): TrafficalPlugin & { applyBindings: (params?: Record<string, unknown>) => void; getBindings: () => BundleDOMBinding[] } {\n const config = {\n observeMutations: options.observeMutations ?? true,\n debounceMs: options.debounceMs ?? 100,\n };\n\n // Internal state\n let bindings: BundleDOMBinding[] = [];\n let lastParams: Record<string, unknown> = {};\n let observer: MutationObserver | null = null;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n // ==========================================================================\n // Core binding logic\n // ==========================================================================\n\n /**\n * Check if URL matches the binding's pattern.\n */\n function matchesUrlPattern(pattern: string, path: string): boolean {\n try {\n const regex = new RegExp(pattern);\n return regex.test(path);\n } catch {\n // Invalid regex - fall back to exact match\n return path === pattern;\n }\n }\n\n /**\n * Set a property on an element.\n * Supports: innerHTML, textContent, src, href, style.*\n */\n function setProperty(element: HTMLElement, property: string, value: string): void {\n if (property === \"innerHTML\") {\n element.innerHTML = value;\n } else if (property === \"textContent\") {\n element.textContent = value;\n } else if (property === \"src\" && \"src\" in element) {\n (element as HTMLImageElement).src = value;\n } else if (property === \"href\" && \"href\" in element) {\n (element as HTMLAnchorElement).href = value;\n } else if (property.startsWith(\"style.\")) {\n const styleProp = property.slice(6); // Remove \"style.\" prefix\n (element.style as unknown as Record<string, string>)[styleProp] = value;\n } else {\n // Generic attribute setter for other properties\n element.setAttribute(property, value);\n }\n }\n\n /**\n * Apply a single binding to matching elements.\n */\n function applyBinding(binding: BundleDOMBinding, value: unknown): void {\n const stringValue = String(value);\n\n try {\n const elements = document.querySelectorAll(binding.selector);\n\n for (const element of elements) {\n setProperty(element as HTMLElement, binding.property, stringValue);\n }\n } catch (error) {\n // Invalid selector or other DOM error - silently warn\n console.warn(\n `[Traffical DOM Binding] Failed to apply binding for ${binding.parameterKey}:`,\n error\n );\n }\n }\n\n /**\n * Apply parameter values to matching DOM elements.\n */\n function apply(params: Record<string, unknown>, forceAll = false): void {\n lastParams = params;\n\n const currentPath = typeof window !== \"undefined\" ? window.location.pathname : \"\";\n\n for (const binding of bindings) {\n // Check URL pattern match\n if (!forceAll && !matchesUrlPattern(binding.urlPattern, currentPath)) {\n continue;\n }\n\n // Get parameter value\n const value = params[binding.parameterKey];\n if (value === undefined) {\n continue;\n }\n\n // Apply to matching elements\n applyBinding(binding, value);\n }\n }\n\n /**\n * Debounced reapplication of bindings after DOM mutations.\n */\n function debouncedApply(): void {\n if (debounceTimer) {\n clearTimeout(debounceTimer);\n }\n\n debounceTimer = setTimeout(() => {\n apply(lastParams);\n }, config.debounceMs);\n }\n\n /**\n * Start observing DOM mutations.\n */\n function startObserving(): void {\n if (observer || typeof MutationObserver === \"undefined\" || typeof document === \"undefined\") {\n return;\n }\n\n observer = new MutationObserver(() => {\n debouncedApply();\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n }\n\n /**\n * Stop observing DOM mutations.\n */\n function stopObserving(): void {\n if (observer) {\n observer.disconnect();\n observer = null;\n }\n\n if (debounceTimer) {\n clearTimeout(debounceTimer);\n debounceTimer = null;\n }\n }\n\n // ==========================================================================\n // Plugin implementation\n // ==========================================================================\n\n return {\n name: \"dom-binding\",\n\n onInitialize() {\n // Start observing if enabled\n if (config.observeMutations) {\n startObserving();\n }\n },\n\n onConfigUpdate(bundle: ConfigBundle) {\n // Update bindings from bundle\n bindings = bundle.domBindings ?? [];\n },\n\n onResolve(params: Record<string, ParameterValue>) {\n // Apply bindings after parameters are resolved\n apply(params as Record<string, unknown>);\n },\n\n onDecision(decision) {\n // Also apply after decide() calls\n apply(decision.assignments as Record<string, unknown>);\n },\n\n onDestroy() {\n stopObserving();\n bindings = [];\n lastParams = {};\n },\n\n // ==========================================================================\n // Public API (exposed on plugin instance)\n // ==========================================================================\n\n /**\n * Manually apply DOM bindings with the given parameter values.\n * Use this to re-trigger bindings after dynamic content changes.\n *\n * @param params - Parameter values to apply. If omitted, uses last known params.\n */\n applyBindings(params?: Record<string, unknown>): void {\n if (params) {\n apply(params);\n } else {\n apply(lastParams);\n }\n },\n\n /**\n * Get the current DOM bindings from the config bundle.\n */\n getBindings(): BundleDOMBinding[] {\n return bindings;\n },\n };\n}\n\n// Type for the plugin with its public API\nexport type DOMBindingPlugin = ReturnType<typeof createDOMBindingPlugin>;\n\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["require_crypto", "__commonJSMin", "global_exports", "__export", "TrafficalClient", "createDOMBindingPlugin", "createDebugPlugin", "createRedirectAttributionPlugin", "createRedirectPlugin", "destroy", "init", "initSync", "instance", "isBytes", "a", "abytes", "value", "length", "title", "bytes", "isBytes", "len", "needsLen", "prefix", "ofLen", "got", "message", "aexists", "instance", "checkFinished", "aoutput", "out", "abytes", "min", "clean", "arrays", "i", "createView", "arr", "rotr", "word", "shift", "createHasher", "hashCons", "info", "hashC", "msg", "opts", "tmp", "oidNist", "suffix", "Chi", "a", "b", "c", "Maj", "HashMD", "blockLen", "outputLen", "padOffset", "isLE", "__publicField", "createView", "data", "aexists", "abytes", "view", "buffer", "len", "pos", "take", "dataView", "out", "aoutput", "clean", "i", "oview", "outLen", "state", "res", "to", "length", "finished", "destroyed", "SHA256_IV", "SHA256_K", "SHA256_W", "SHA2_32B", "HashMD", "outputLen", "A", "B", "C", "D", "E", "F", "G", "H", "view", "offset", "i", "W15", "W2", "s0", "rotr", "s1", "sigma1", "T1", "Chi", "T2", "Maj", "clean", "_SHA256", "__publicField", "SHA256_IV", "sha256", "createHasher", "_SHA256", "oidNist", "UTF8_ENCODER", "ASSIGNMENT_HASH_VERSION", "utf8ByteLength", "value", "assignmentInput", "unitKeyValue", "layerId", "unitLen", "layerLen", "sha256Digest", "input", "sha256", "hash64BE", "digest", "i", "hashInt64", "computeBucket", "unitKeyValue", "layerId", "bucketCount", "digest", "sha256Digest", "assignmentInput", "hashInt", "hash64BE", "isInBucketRange", "bucket", "range", "findMatchingAllocation", "allocations", "allocation", "UNIFORM_MODULUS", "UNIFORM_DENOMINATOR", "weightedSelection", "weights", "seed", "hashInt", "hashInt64", "random", "cumulative", "i", "evaluateCondition", "condition", "context", "field", "op", "value", "values", "contextValue", "getNestedValue", "evaluateConditions", "conditions", "obj", "path", "parts", "current", "part", "computeAllocationScore", "coefficients", "context", "score", "key", "coef", "missing", "value", "values", "strValue", "softmaxProbabilities", "scores", "gamma", "safeGamma", "scaled", "s", "maxScaled", "exps", "sumExp", "b", "e", "applyProbabilityFloor", "probs", "floor", "n", "maxFloor", "effectiveFloor", "floored", "p", "sum", "resolveContextualPolicy", "policy", "unitKeyValue", "model", "computeContextualScores", "seed", "selectedIndex", "weightedSelection", "allocations", "alloc", "random", "bytes", "customRandom", "alphabet", "defaultSize", "getRandom", "mask", "step", "size", "id", "j", "customAlphabet", "createError", "message", "err", "ENCODING", "ENCODING_LEN", "TIME_MAX", "TIME_LEN", "RANDOM_LEN", "randomChar", "prng", "rand", "ENCODING_LEN", "ENCODING", "encodeTime", "now", "len", "TIME_MAX", "createError", "mod", "str", "encodeRandom", "detectPrng", "allowInsecure", "root", "browserCrypto", "buffer", "nodeCrypto", "createError", "factory", "currPrng", "seedTime", "encodeTime", "TIME_LEN", "encodeRandom", "RANDOM_LEN", "ulid", "factory", "NANOID_ALPHABET", "ENTITY_ID_LENGTH", "nanoid", "customAlphabet", "generateEventId", "prefix", "ulid", "generateDecisionId", "generateEventId", "generateExposureId", "generateTrackEventId", "generateAssignmentId", "filterContext", "context", "policies", "allowedFields", "policy", "field", "filtered", "buildEntityId", "entityKeys", "parts", "key", "value", "createUniformWeights", "count", "weight", "getEntityWeights", "bundle", "policyId", "entityId", "allocationCount", "policyState", "entityWeights", "globalWeights", "resolvePerEntityPolicy", "unitKeyValue", "entityConfig", "allocations", "countKey", "_", "i", "weights", "seed", "selectedIndex", "weightedSelection", "getUnitKeyValue", "resolveInternal", "defaults", "options", "assignments", "layers", "matchedPolicies", "projectUnitKeyValue", "requestedKeys", "params", "p", "param", "paramsByLayer", "existing", "layer", "layerParams", "hasParams", "layerUnitKey", "layerUnitValue", "bucket", "computeBucket", "matchedPolicy", "matchedAllocation", "start", "end", "evaluateConditions", "ctxAllocation", "resolveContextualPolicy", "result", "edgeResult", "allocation", "findMatchingAllocation", "resolveParameters", "decide", "filteredContext", "generateDecisionId", "DecisionDeduplicator", "_DecisionDeduplicator", "options", "__publicField", "assignments", "sortedKeys", "parts", "key", "value", "valueStr", "unitKey", "assignmentHash", "now", "lastSeen", "expiredKeys", "timestamp", "toRemove", "a", "b", "DecisionClient", "config", "__publicField", "request", "controller", "timeoutId", "url", "response", "error", "timeoutMs", "timeout", "requests", "createEdgeDecideRequest", "policyId", "entityKeys", "context", "unitKeyValue", "allocationCount", "parts", "key", "value", "entityId", "ErrorBoundary", "options", "tag", "fn", "fallback", "error", "resolvedError", "errorKey", "FAILED_EVENTS_KEY", "EventLogger", "options", "event", "events", "error", "response", "body", "combined", "FAILED_EVENTS_KEY", "failed", "state", "STORAGE_KEY", "ExposureDeduplicator", "_ExposureDeduplicator", "options", "unitKey", "policyId", "variant", "key", "STORAGE_KEY", "state", "STORAGE_KEY", "COOKIE_NAME", "StableIdProvider", "options", "COOKIE_NAME", "id", "STORAGE_KEY", "c", "r", "cookies", "cookie", "name", "value", "STORAGE_PREFIX", "LocalStorageProvider", "key", "raw", "stored", "value", "ttlMs", "keysToRemove", "i", "testKey", "MemoryStorageProvider", "createStorageProvider", "localProvider", "localStorageAvailable", "SDK_VERSION", "SDK_NAME", "createDecisionTrackingPlugin", "options", "deps", "dedup", "DecisionDeduplicator", "decision", "unitKey", "hash", "event", "SDK_VERSION", "RDR_COOKIE", "setCookie", "name", "value", "maxAge", "createRedirectPlugin", "options", "parameterKey", "compareMode", "cookieName", "RDR_COOKIE", "client", "context", "decision", "url", "current", "layer", "l", "COOKIE_NAME", "readCookie", "name", "part", "k", "v", "parseAttribution", "cookieName", "expiryMs", "raw", "data", "createRedirectAttributionPlugin", "options", "COOKIE_NAME", "inject", "event", "attr", "_registryListeners", "_registryInstances", "getOrCreateRegistry", "registry", "cb", "l", "emitRegistryEvent", "event", "listener", "_idCounter", "generateId", "generateEventId", "PLUGIN_NAME", "createDebugPlugin", "options", "instanceId", "maxEvents", "_client", "_bundle", "_assignments", "_layers", "_lastDecisionId", "_effectiveUnitKey", "_events", "_stateListeners", "_eventListeners", "buildState", "notifyStateListeners", "state", "pushEvent", "type", "data", "triggerReDecide", "debugInstance", "SDK_VERSION", "limit", "key", "value", "current", "client", "bundle", "decision", "params", "PluginManager", "options", "plugin", "priority", "p", "a", "b", "name", "index", "client", "error", "bundle", "context", "result", "modified", "decision", "params", "event", "createBrowserLifecycleProvider", "listeners", "unloading", "notify", "state", "cb", "onPageHide", "onVisibilityChange", "onBeforeUnload", "callback", "idx", "SDK_NAME", "DEFAULT_BASE_URL", "DEFAULT_REFRESH_INTERVAL_MS", "OFFLINE_WARNING_INTERVAL_MS", "DECISION_CACHE_MAX_SIZE", "TrafficalClient", "options", "evaluationMode", "decisionClientConfig", "DecisionClient", "ErrorBoundary", "createStorageProvider", "createBrowserLifecycleProvider", "warnings", "w", "v", "EventLogger", "error", "ExposureDeduplicator", "StableIdProvider", "PluginManager", "createDecisionTrackingPlugin", "event", "plugin", "instances", "idx", "result", "key", "value", "bundle", "context", "params", "resolveParameters", "resp", "assignments", "decision", "edgeOpts", "decide", "generateDecisionId", "unitKey", "layer", "generateExposureId", "SDK_VERSION", "eventName", "properties", "attribution", "decisionId", "generateTrackEventId", "name", "id", "cb", "l", "overrides", "snapshot", "type", "generateAssignmentId", "target", "keys", "k", "url", "headers", "response", "etag", "edgeResults", "interval", "policies", "policy", "edgePolicies", "unitKeyValue", "getUnitKeyValue", "requests", "allocationCount", "createEdgeDecideRequest", "r", "responses", "i", "now", "firstKey", "userAttrs", "cachedDecision", "createTrafficalClient", "client", "createTrafficalClientSync", "createDOMBindingPlugin", "options", "config", "bindings", "lastParams", "observer", "debounceTimer", "matchesUrlPattern", "pattern", "path", "setProperty", "element", "property", "value", "styleProp", "applyBinding", "binding", "stringValue", "elements", "error", "apply", "params", "forceAll", "currentPath", "debouncedApply", "startObserving", "stopObserving", "bundle", "decision", "_instance", "init", "options", "createTrafficalClient", "initSync", "createTrafficalClientSync", "error", "instance", "destroy"]
|
|
4
|
+
"sourcesContent": ["", "/**\n * Global entry point for IIFE bundle.\n *\n * Exports `window.Traffical` for script tag usage:\n *\n * ```html\n * <script src=\"https://cdn.traffical.io/js-client/v1/traffical.min.js\"></script>\n * <script>\n * Traffical.init({ ... }).then(function(client) {\n * var params = client.getParams({ ... });\n * });\n * </script>\n * ```\n */\n\nimport {\n TrafficalClient,\n createTrafficalClient,\n createTrafficalClientSync,\n type TrafficalClientOptions,\n} from \"./client.js\";\nimport type { TrafficalPlugin } from \"./plugins/index.js\";\nimport {\n createDOMBindingPlugin,\n type DOMBindingPlugin,\n type DOMBindingPluginOptions,\n} from \"./plugins/dom-binding.js\";\nimport {\n createRedirectPlugin,\n type RedirectPluginOptions,\n} from \"./plugins/redirect.js\";\nimport {\n createRedirectAttributionPlugin,\n type RedirectAttributionPluginOptions,\n} from \"./plugins/redirect-attribution.js\";\nimport {\n createDebugPlugin,\n type DebugPluginOptions,\n} from \"./plugins/debug.js\";\n\n// Global state for singleton pattern\nlet _instance: TrafficalClient | null = null;\n\n/**\n * Initialize the Traffical client (async).\n * Returns the client instance.\n */\nasync function init(options: TrafficalClientOptions): Promise<TrafficalClient> {\n if (_instance) {\n console.warn(\"[Traffical] Client already initialized. Returning existing instance.\");\n return _instance;\n }\n\n _instance = await createTrafficalClient(options);\n return _instance;\n}\n\n/**\n * Initialize the Traffical client (sync).\n * Returns the client instance immediately, but config fetch happens async.\n */\nfunction initSync(options: TrafficalClientOptions): TrafficalClient {\n if (_instance) {\n console.warn(\"[Traffical] Client already initialized. Returning existing instance.\");\n return _instance;\n }\n\n _instance = createTrafficalClientSync(options);\n\n // Start async initialization in background\n _instance.initialize().catch((error) => {\n console.warn(\"[Traffical] Initialization error:\", error);\n });\n\n return _instance;\n}\n\n/**\n * Get the singleton client instance.\n * Returns null if not initialized.\n */\nfunction instance(): TrafficalClient | null {\n return _instance;\n}\n\n/**\n * Destroy the singleton instance.\n */\nfunction destroy(): void {\n if (_instance) {\n _instance.destroy();\n _instance = null;\n }\n}\n\n// Export the Traffical global object\nexport {\n init,\n initSync,\n instance,\n destroy,\n TrafficalClient,\n type TrafficalClientOptions,\n type TrafficalPlugin,\n // DOM binding plugin\n createDOMBindingPlugin,\n type DOMBindingPlugin,\n type DOMBindingPluginOptions,\n // Redirect plugins\n createRedirectPlugin,\n type RedirectPluginOptions,\n createRedirectAttributionPlugin,\n type RedirectAttributionPluginOptions,\n // Debug plugin\n createDebugPlugin,\n type DebugPluginOptions,\n};\n\n", "/**\n * Utilities for hex, bytes, CSPRNG.\n * @module\n */\n/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */\n/**\n * Bytes API type helpers for old + new TypeScript.\n *\n * TS 5.6 has `Uint8Array`, while TS 5.9+ made it generic `Uint8Array<ArrayBuffer>`.\n * We can't use specific return type, because TS 5.6 will error.\n * We can't use generic return type, because most TS 5.9 software will expect specific type.\n *\n * Maps typed-array input leaves to broad forms.\n * These are compatibility adapters, not ownership guarantees.\n *\n * - `TArg` keeps byte inputs broad.\n * - `TRet` marks byte outputs for TS 5.6 and TS 5.9+ compatibility.\n */\nexport type TypedArg<T> = T extends BigInt64Array\n ? BigInt64Array\n : T extends BigUint64Array\n ? BigUint64Array\n : T extends Float32Array\n ? Float32Array\n : T extends Float64Array\n ? Float64Array\n : T extends Int16Array\n ? Int16Array\n : T extends Int32Array\n ? Int32Array\n : T extends Int8Array\n ? Int8Array\n : T extends Uint16Array\n ? Uint16Array\n : T extends Uint32Array\n ? Uint32Array\n : T extends Uint8ClampedArray\n ? Uint8ClampedArray\n : T extends Uint8Array\n ? Uint8Array\n : never;\n/** Maps typed-array output leaves to narrow TS-compatible forms. */\nexport type TypedRet<T> = T extends BigInt64Array\n ? ReturnType<typeof BigInt64Array.of>\n : T extends BigUint64Array\n ? ReturnType<typeof BigUint64Array.of>\n : T extends Float32Array\n ? ReturnType<typeof Float32Array.of>\n : T extends Float64Array\n ? ReturnType<typeof Float64Array.of>\n : T extends Int16Array\n ? ReturnType<typeof Int16Array.of>\n : T extends Int32Array\n ? ReturnType<typeof Int32Array.of>\n : T extends Int8Array\n ? ReturnType<typeof Int8Array.of>\n : T extends Uint16Array\n ? ReturnType<typeof Uint16Array.of>\n : T extends Uint32Array\n ? ReturnType<typeof Uint32Array.of>\n : T extends Uint8ClampedArray\n ? ReturnType<typeof Uint8ClampedArray.of>\n : T extends Uint8Array\n ? ReturnType<typeof Uint8Array.of>\n : never;\n/** Recursively adapts byte-carrying API input types. See {@link TypedArg}. */\nexport type TArg<T> =\n | T\n | ([TypedArg<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TRet<A[K]> }) => TArg<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TArg<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends (infer A)[]\n ? TArg<A>[]\n : T extends readonly (infer A)[]\n ? readonly TArg<A>[]\n : T extends Promise<infer A>\n ? Promise<TArg<A>>\n : T extends object\n ? { [K in keyof T]: TArg<T[K]> }\n : T\n : TypedArg<T>);\n/** Recursively adapts byte-carrying API output types. See {@link TypedArg}. */\nexport type TRet<T> = T extends unknown\n ? T &\n ([TypedRet<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TArg<A[K]> }) => TRet<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TRet<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends (infer A)[]\n ? TRet<A>[]\n : T extends readonly (infer A)[]\n ? readonly TRet<A>[]\n : T extends Promise<infer A>\n ? Promise<TRet<A>>\n : T extends object\n ? { [K in keyof T]: TRet<T[K]> }\n : T\n : TypedRet<T>)\n : never;\n/**\n * Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.\n * @param a - value to test\n * @returns `true` when the value is a Uint8Array-compatible view.\n * @example\n * Check whether a value is a Uint8Array-compatible view.\n * ```ts\n * isBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function isBytes(a: unknown): a is Uint8Array {\n // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.\n // The fallback still requires a real ArrayBuffer view, so plain\n // JSON-deserialized `{ constructor: ... }` spoofing is rejected, and\n // `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.\n return (\n a instanceof Uint8Array ||\n (ArrayBuffer.isView(a) &&\n a.constructor.name === 'Uint8Array' &&\n 'BYTES_PER_ELEMENT' in a &&\n a.BYTES_PER_ELEMENT === 1)\n );\n}\n\n/**\n * Asserts something is a non-negative integer.\n * @param n - number to validate\n * @param title - label included in thrown errors\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a non-negative integer option.\n * ```ts\n * anumber(32, 'length');\n * ```\n */\nexport function anumber(n: number, title: string = ''): void {\n if (typeof n !== 'number') {\n const prefix = title && `\"${title}\" `;\n throw new TypeError(`${prefix}expected number, got ${typeof n}`);\n }\n if (!Number.isSafeInteger(n) || n < 0) {\n const prefix = title && `\"${title}\" `;\n throw new RangeError(`${prefix}expected integer >= 0, got ${n}`);\n }\n}\n\n/**\n * Asserts something is Uint8Array.\n * @param value - value to validate\n * @param length - optional exact length constraint\n * @param title - label included in thrown errors\n * @returns The validated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate that a value is a byte array.\n * ```ts\n * abytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function abytes(\n value: TArg<Uint8Array>,\n length?: number,\n title: string = ''\n): TRet<Uint8Array> {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;\n if (!bytes) throw new TypeError(message);\n throw new RangeError(message);\n }\n return value as TRet<Uint8Array>;\n}\n\n/**\n * Copies bytes into a fresh Uint8Array.\n * Buffer-style slices can alias the same backing store, so callers that need ownership should copy.\n * @param bytes - source bytes to clone\n * @returns Freshly allocated copy of `bytes`.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Clone a byte array before mutating it.\n * ```ts\n * const copy = copyBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function copyBytes(bytes: TArg<Uint8Array>): TRet<Uint8Array> {\n // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict\n // because callers use it at byte-validation boundaries before mutating the detached copy.\n return Uint8Array.from(abytes(bytes)) as TRet<Uint8Array>;\n}\n\n/**\n * Asserts something is a wrapped hash constructor.\n * @param h - hash constructor to validate\n * @throws On wrong argument types or invalid hash wrapper shape. {@link TypeError}\n * @throws On invalid hash metadata ranges or values. {@link RangeError}\n * @throws If the hash metadata allows empty outputs or block sizes. {@link Error}\n * @example\n * Validate a callable hash wrapper.\n * ```ts\n * import { ahash } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * ahash(sha256);\n * ```\n */\nexport function ahash(h: TArg<CHash>): void {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new TypeError('Hash must wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n // HMAC and KDF callers treat these as real byte lengths; allowing zero lets fake wrappers pass\n // validation and can produce empty outputs instead of failing fast.\n if (h.outputLen < 1) throw new Error('\"outputLen\" must be >= 1');\n if (h.blockLen < 1) throw new Error('\"blockLen\" must be >= 1');\n}\n\n/**\n * Asserts a hash instance has not been destroyed or finished.\n * @param instance - hash instance to validate\n * @param checkFinished - whether to reject finalized instances\n * @throws If the hash instance has already been destroyed or finalized. {@link Error}\n * @example\n * Validate that a hash instance is still usable.\n * ```ts\n * import { aexists } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aexists(hash);\n * ```\n */\nexport function aexists(instance: any, checkFinished = true): void {\n if (instance.destroyed) throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');\n}\n\n/**\n * Asserts output is a sufficiently-sized byte array.\n * @param out - destination buffer\n * @param instance - hash instance providing output length\n * Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a caller-provided digest buffer.\n * ```ts\n * import { aoutput } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aoutput(new Uint8Array(hash.outputLen), hash);\n * ```\n */\nexport function aoutput(out: any, instance: any): void {\n abytes(out, undefined, 'digestInto() output');\n const min = instance.outputLen;\n if (out.length < min) {\n throw new RangeError('\"digestInto() output\" expected to be of length >=' + min);\n }\n}\n\n/** Generic type encompassing 8/16/32-byte array views, but not 64-bit variants. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/**\n * Casts a typed array view to Uint8Array.\n * @param arr - source typed array\n * @returns Uint8Array view over the same buffer.\n * @example\n * Reinterpret a typed array as bytes.\n * ```ts\n * u8(new Uint32Array([1, 2]));\n * ```\n */\nexport function u8(arr: TArg<TypedArray>): TRet<Uint8Array> {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength) as TRet<Uint8Array>;\n}\n\n/**\n * Casts a typed array view to Uint32Array.\n * `arr.byteOffset` must already be 4-byte aligned or the platform\n * Uint32Array constructor will throw.\n * @param arr - source typed array\n * @returns Uint32Array view over the same buffer.\n * @example\n * Reinterpret a byte array as 32-bit words.\n * ```ts\n * u32(new Uint8Array(8));\n * ```\n */\nexport function u32(arr: TArg<TypedArray>): TRet<Uint32Array> {\n return new Uint32Array(\n arr.buffer,\n arr.byteOffset,\n Math.floor(arr.byteLength / 4)\n ) as TRet<Uint32Array>;\n}\n\n/**\n * Zeroizes typed arrays in place. Warning: JS provides no guarantees.\n * @param arrays - arrays to overwrite with zeros\n * @example\n * Zeroize sensitive buffers in place.\n * ```ts\n * clean(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function clean(...arrays: TArg<TypedArray[]>): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/**\n * Creates a DataView for byte-level manipulation.\n * @param arr - source typed array\n * @returns DataView over the same buffer region.\n * @example\n * Create a DataView over an existing buffer.\n * ```ts\n * createView(new Uint8Array(4));\n * ```\n */\nexport function createView(arr: TArg<TypedArray>): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/**\n * Rotate-right operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the right.\n * ```ts\n * rotr(0x12345678, 8);\n * ```\n */\nexport function rotr(word: number, shift: number): number {\n return (word << (32 - shift)) | (word >>> shift);\n}\n\n/**\n * Rotate-left operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the left.\n * ```ts\n * rotl(0x12345678, 8);\n * ```\n */\nexport function rotl(word: number, shift: number): number {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n\n/** Whether the current platform is little-endian. */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/**\n * Byte-swap operation for uint32 values.\n * @param word - source word\n * @returns Word with reversed byte order.\n * @example\n * Reverse the byte order of a 32-bit word.\n * ```ts\n * byteSwap(0x11223344);\n * ```\n */\nexport function byteSwap(word: number): number {\n return (\n ((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff)\n );\n}\n/**\n * Conditionally byte-swaps one 32-bit word on big-endian platforms.\n * @param n - source word\n * @returns Original or byte-swapped word depending on platform endianness.\n * @example\n * Normalize a 32-bit word for host endianness.\n * ```ts\n * swap8IfBE(0x11223344);\n * ```\n */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n) >>> 0;\n\n/**\n * Byte-swaps every word of a Uint32Array in place.\n * @param arr - array to mutate\n * @returns The same array after mutation; callers pass live state arrays here.\n * @example\n * Reverse the byte order of every word in place.\n * ```ts\n * byteSwap32(new Uint32Array([0x11223344]));\n * ```\n */\nexport function byteSwap32(arr: TArg<Uint32Array>): TRet<Uint32Array> {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr as TRet<Uint32Array>;\n}\n\n/**\n * Conditionally byte-swaps a Uint32Array on big-endian platforms.\n * @param u - array to normalize for host endianness\n * @returns Original or byte-swapped array depending on platform endianness.\n * On big-endian runtimes this mutates `u` in place via `byteSwap32(...)`.\n * @example\n * Normalize a word array for host endianness.\n * ```ts\n * swap32IfBE(new Uint32Array([0x11223344]));\n * ```\n */\nexport const swap32IfBE: (u: TArg<Uint32Array>) => TRet<Uint32Array> = isLE\n ? (u: TArg<Uint32Array>) => u as TRet<Uint32Array>\n : byteSwap32;\n\n// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex\nconst hasHexBuiltin: boolean = /* @__PURE__ */ (() =>\n // @ts-ignore\n typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();\n\n// Array where index 0xf0 (240) is mapped to string 'f0'\nconst hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>\n i.toString(16).padStart(2, '0')\n);\n\n/**\n * Convert byte array to hex string.\n * Uses the built-in function when available and assumes it matches the tested\n * fallback semantics.\n * @param bytes - bytes to encode\n * @returns Lowercase hexadecimal string.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Convert bytes to lowercase hexadecimal.\n * ```ts\n * bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123'\n * ```\n */\nexport function bytesToHex(bytes: TArg<Uint8Array>): string {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin) return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n\n// We use optimized technique to convert hex string to byte array\nconst asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const;\nfunction asciiToBase16(ch: number): number | undefined {\n if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48\n if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10)\n if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10)\n return;\n}\n\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @param hex - hexadecimal string to decode\n * @returns Decoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Decode lowercase hexadecimal into bytes.\n * ```ts\n * hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n * ```\n */\nexport function hexToBytes(hex: string): TRet<Uint8Array> {\n if (typeof hex !== 'string') throw new TypeError('hex string expected, got ' + typeof hex);\n if (hasHexBuiltin) {\n try {\n return (Uint8Array as any).fromHex(hex);\n } catch (error) {\n if (error instanceof SyntaxError) throw new RangeError(error.message);\n throw error;\n }\n }\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new RangeError('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi));\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1));\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new RangeError(\n 'hex string expected, got non-hex character \"' + char + '\" at index ' + hi\n );\n }\n array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163\n }\n return array;\n}\n\n/**\n * There is no setImmediate in browser and setTimeout is slow.\n * This yields to the Promise/microtask scheduler queue, not to timers or the\n * full macrotask event loop.\n * @example\n * Yield to the next scheduler tick.\n * ```ts\n * await nextTick();\n * ```\n */\nexport const nextTick = async (): Promise<void> => {};\n\n/**\n * Returns control to the Promise/microtask scheduler every `tick`\n * milliseconds to avoid blocking long loops.\n * @param iters - number of loop iterations to run\n * @param tick - maximum time slice in milliseconds\n * @param cb - callback executed on each iteration\n * @example\n * Run a loop that periodically yields back to the event loop.\n * ```ts\n * await asyncLoop(2, 0, () => {});\n * ```\n */\nexport async function asyncLoop(\n iters: number,\n tick: number,\n cb: (i: number) => void\n): Promise<void> {\n let ts = Date.now();\n for (let i = 0; i < iters; i++) {\n cb(i);\n // Date.now() is not monotonic, so in case if clock goes backwards we return return control too\n const diff = Date.now() - ts;\n if (diff >= 0 && diff < tick) continue;\n await nextTick();\n ts += diff;\n }\n}\n\n// Global symbols, but ts doesn't see them: https://github.com/microsoft/TypeScript/issues/31535\ndeclare const TextEncoder: any;\n\n/**\n * Converts string to bytes using UTF8 encoding.\n * Built-in doesn't validate input to be string: we do the check.\n * Non-ASCII details are delegated to the platform `TextEncoder`.\n * @param str - string to encode\n * @returns UTF-8 encoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Encode a string as UTF-8 bytes.\n * ```ts\n * utf8ToBytes('abc'); // Uint8Array.from([97, 98, 99])\n * ```\n */\nexport function utf8ToBytes(str: string): TRet<Uint8Array> {\n if (typeof str !== 'string') throw new TypeError('string expected');\n return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809\n}\n\n/** KDFs can accept string or Uint8Array for user convenience. */\nexport type KDFInput = string | Uint8Array;\n\n/**\n * Helper for KDFs: consumes Uint8Array or string.\n * String inputs are UTF-8 encoded; byte-array inputs stay aliased to the caller buffer.\n * @param data - user-provided KDF input\n * @param errorTitle - label included in thrown errors\n * @returns Byte representation of the input.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Normalize KDF input to bytes.\n * ```ts\n * kdfInputToBytes('password');\n * ```\n */\nexport function kdfInputToBytes(data: TArg<KDFInput>, errorTitle = ''): TRet<Uint8Array> {\n if (typeof data === 'string') return utf8ToBytes(data);\n return abytes(data, undefined, errorTitle);\n}\n\n/**\n * Copies several Uint8Arrays into one.\n * @param arrays - arrays to concatenate\n * @returns Concatenated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Concatenate multiple byte arrays.\n * ```ts\n * concatBytes(new Uint8Array([1]), new Uint8Array([2]));\n * ```\n */\nexport function concatBytes(...arrays: TArg<Uint8Array[]>): TRet<Uint8Array> {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res;\n}\n\ntype EmptyObj = {};\n/**\n * Merges default options and passed options.\n * @param defaults - base option object\n * @param opts - user overrides\n * @returns Merged option object. The merge mutates `defaults` in place.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Merge user overrides onto default options.\n * ```ts\n * checkOpts({ dkLen: 32 }, { asyncTick: 10 });\n * ```\n */\nexport function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(\n defaults: T1,\n opts?: T2\n): T1 & T2 {\n if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')\n throw new TypeError('options must be object or undefined');\n const merged = Object.assign(defaults, opts);\n return merged as T1 & T2;\n}\n\n/** Common interface for all hash instances. */\nexport interface Hash<T> {\n /** Bytes processed per compression block. */\n blockLen: number;\n /** Bytes produced by `digest()`. */\n outputLen: number;\n /** Whether the instance supports XOF-style variable-length output via `xof()` / `xofInto()`. */\n canXOF: boolean;\n /**\n * Absorbs more message bytes into the running hash state.\n * @param buf - message chunk to absorb\n * @returns The same hash instance for chaining.\n */\n update(buf: TArg<Uint8Array>): this;\n /**\n * Finalizes the hash into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Nothing. Implementations write into `buf` in place.\n */\n digestInto(buf: TArg<Uint8Array>): void;\n /**\n * Finalizes the hash and returns a freshly allocated digest.\n * @returns Digest bytes.\n */\n digest(): TRet<Uint8Array>;\n /** Wipes internal state and makes the instance unusable. */\n destroy(): void;\n /**\n * Copies the current hash state into an existing or new instance.\n * @param to - Optional destination instance to reuse.\n * @returns Cloned hash state.\n */\n _cloneInto(to?: T): T;\n /**\n * Creates an independent copy of the current hash state.\n * @returns Cloned hash instance.\n */\n clone(): T;\n}\n\n/** Pseudorandom generator interface. */\nexport interface PRG {\n /**\n * Mixes more entropy into the generator state.\n * @param seed - fresh entropy bytes\n * @returns Nothing. Implementations update internal state in place.\n */\n addEntropy(seed: TArg<Uint8Array>): void;\n /**\n * Generates pseudorandom output bytes.\n * @param length - number of bytes to generate\n * @returns Generated pseudorandom bytes.\n */\n randomBytes(length: number): TRet<Uint8Array>;\n /** Wipes generator state and makes the instance unusable. */\n clean(): void;\n}\n\n/**\n * XOF: streaming API to read digest in chunks.\n * Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.\n * When hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot\n * destroy state, next call can require more bytes.\n */\nexport type HashXOF<T extends Hash<T>> = Hash<T> & {\n /**\n * Reads more bytes from the XOF stream.\n * @param bytes - number of bytes to read\n * @returns Requested digest bytes.\n */\n xof(bytes: number): TRet<Uint8Array>;\n /**\n * Reads more bytes from the XOF stream into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Filled output buffer.\n */\n xofInto(buf: TArg<Uint8Array>): TRet<Uint8Array>;\n};\n\n/** Hash constructor or factory type. */\nexport type HasherCons<T, Opts = undefined> = Opts extends undefined ? () => T : (opts?: Opts) => T;\n/** Optional hash metadata. */\nexport type HashInfo = {\n /** DER-encoded object identifier bytes for the hash algorithm. */\n oid?: TRet<Uint8Array>;\n};\n/** Callable hash function type. */\nexport type CHash<T extends Hash<T> = Hash<any>, Opts = undefined> = {\n /** Digest size in bytes. */\n outputLen: number;\n /** Input block size in bytes. */\n blockLen: number;\n /** Whether `.create()` returns a hash instance that can be used as an XOF stream. */\n canXOF: boolean;\n} & HashInfo &\n (Opts extends undefined\n ? {\n (msg: TArg<Uint8Array>): TRet<Uint8Array>;\n create(): T;\n }\n : {\n (msg: TArg<Uint8Array>, opts?: TArg<Opts>): TRet<Uint8Array>;\n create(opts?: Opts): T;\n });\n/** Callable extendable-output hash function type. */\nexport type CHashXOF<T extends HashXOF<T> = HashXOF<any>, Opts = undefined> = CHash<T, Opts>;\n\n/**\n * Creates a callable hash function from a stateful class constructor.\n * @param hashCons - hash constructor or factory\n * @param info - optional metadata such as DER OID\n * @returns Frozen callable hash wrapper with `.create()`.\n * Wrapper construction eagerly calls `hashCons(undefined)` once to read\n * `outputLen` / `blockLen`, so constructor side effects happen at module\n * init time.\n * @example\n * Wrap a stateful hash constructor into a callable helper.\n * ```ts\n * import { createHasher } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const wrapped = createHasher(sha256.create, { oid: sha256.oid });\n * wrapped(new Uint8Array([1]));\n * ```\n */\nexport function createHasher<T extends Hash<T>, Opts = undefined>(\n hashCons: HasherCons<T, Opts>,\n info: TArg<HashInfo> = {}\n): TRet<CHash<T, Opts>> {\n const hashC: any = (msg: TArg<Uint8Array>, opts?: TArg<Opts>) =>\n hashCons(opts as Opts)\n .update(msg)\n .digest();\n const tmp = hashCons(undefined);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.canXOF = tmp.canXOF;\n hashC.create = (opts?: Opts) => hashCons(opts);\n Object.assign(hashC, info);\n return Object.freeze(hashC) as TRet<CHash<T, Opts>>;\n}\n\n/**\n * Cryptographically secure PRNG backed by `crypto.getRandomValues`.\n * @param bytesLength - number of random bytes to generate\n * @returns Random bytes.\n * The platform `getRandomValues()` implementation still defines any\n * single-call length cap, and this helper rejects oversize requests\n * with a stable library `RangeError` instead of host-specific errors.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If the current runtime does not provide `crypto.getRandomValues`. {@link Error}\n * @example\n * Generate a fresh random key or nonce.\n * ```ts\n * const key = randomBytes(16);\n * ```\n */\nexport function randomBytes(bytesLength = 32): TRet<Uint8Array> {\n // Match the repo's other length-taking helpers instead of relying on Uint8Array coercion.\n anumber(bytesLength, 'bytesLength');\n const cr = typeof globalThis === 'object' ? (globalThis as any).crypto : null;\n if (typeof cr?.getRandomValues !== 'function')\n throw new Error('crypto.getRandomValues must be defined');\n // Web Cryptography API Level 2 \u00A710.1.1:\n // if `byteLength > 65536`, throw `QuotaExceededError`.\n // Keep the guard explicit so callers can see the quota in code\n // instead of discovering it by reading the spec or host errors.\n // This wrapper surfaces the same quota as a stable library RangeError.\n if (bytesLength > 65536)\n throw new RangeError(`\"bytesLength\" expected <= 65536, got ${bytesLength}`);\n return cr.getRandomValues(new Uint8Array(bytesLength));\n}\n\n/**\n * Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`.\n * @param suffix - final OID byte for the selected hash.\n * The helper accepts any byte even though only the documented NIST hash\n * suffixes are meaningful downstream.\n * @returns Object containing the DER-encoded OID.\n * @example\n * Build OID metadata for a NIST hash.\n * ```ts\n * oidNist(0x01);\n * ```\n */\nexport const oidNist = (suffix: number): TRet<Required<HashInfo>> => ({\n // Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.\n // Larger suffix values would need base-128 OID encoding and a different length byte.\n oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),\n});\n", "/**\n * Internal Merkle-Damgard hash utils.\n * @module\n */\nimport {\n abytes,\n aexists,\n aoutput,\n clean,\n createView,\n type Hash,\n type TArg,\n type TRet,\n} from './utils.ts';\n\n/**\n * Shared 32-bit conditional boolean primitive reused by SHA-256, SHA-1, and MD5 `F`.\n * Returns bits from `b` when `a` is set, otherwise from `c`.\n * The XOR form is equivalent to MD5's `F(X,Y,Z) = XY v not(X)Z` because the masked terms never\n * set the same bit.\n * @param a - selector word\n * @param b - word chosen when selector bit is set\n * @param c - word chosen when selector bit is clear\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit choice primitive.\n * ```ts\n * Chi(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Chi(a: number, b: number, c: number): number {\n return (a & b) ^ (~a & c);\n}\n\n/**\n * Shared 32-bit majority primitive reused by SHA-256 and SHA-1.\n * Returns bits shared by at least two inputs.\n * @param a - first input word\n * @param b - second input word\n * @param c - third input word\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit majority primitive.\n * ```ts\n * Maj(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Maj(a: number, b: number, c: number): number {\n return (a & b) ^ (a & c) ^ (b & c);\n}\n\n/**\n * Merkle-Damgard hash construction base class.\n * Could be used to create MD5, RIPEMD, SHA1, SHA2.\n * Accepts only byte-aligned `Uint8Array` input, even when the underlying spec describes bit\n * strings with partial-byte tails.\n * @param blockLen - internal block size in bytes\n * @param outputLen - digest size in bytes\n * @param padOffset - trailing length field size in bytes\n * @param isLE - whether length and state words are encoded in little-endian\n * @example\n * Use a concrete subclass to get the shared Merkle-Damgard update/digest flow.\n * ```ts\n * import { _SHA1 } from '@noble/hashes/legacy.js';\n * const hash = new _SHA1();\n * hash.update(new Uint8Array([97, 98, 99]));\n * hash.digest();\n * ```\n */\nexport abstract class HashMD<T extends HashMD<T>> implements Hash<T> {\n // Subclasses must treat `buf` as read-only: `update()` may pass a direct view over caller input\n // when it can process whole blocks without buffering first.\n protected abstract process(buf: DataView, offset: number): void;\n protected abstract get(): number[];\n protected abstract set(...args: number[]): void;\n abstract destroy(): void;\n protected abstract roundClean(): void;\n\n readonly blockLen: number;\n readonly outputLen: number;\n readonly canXOF = false;\n readonly padOffset: number;\n readonly isLE: boolean;\n\n // For partial updates less than block size\n protected buffer: Uint8Array;\n protected view: DataView;\n protected finished = false;\n protected length = 0;\n protected pos = 0;\n protected destroyed = false;\n\n constructor(blockLen: number, outputLen: number, padOffset: number, isLE: boolean) {\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.padOffset = padOffset;\n this.isLE = isLE;\n this.buffer = new Uint8Array(blockLen);\n this.view = createView(this.buffer);\n }\n update(data: TArg<Uint8Array>): this {\n aexists(this);\n abytes(data);\n const { view, buffer, blockLen } = this;\n const len = data.length;\n for (let pos = 0; pos < len; ) {\n const take = Math.min(blockLen - this.pos, len - pos);\n // Fast path only when there is no buffered partial block: `take === blockLen` implies\n // `this.pos === 0`, so we can process full blocks directly from the input view.\n if (take === blockLen) {\n const dataView = createView(data);\n for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);\n continue;\n }\n buffer.set(data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n pos += take;\n if (this.pos === blockLen) {\n this.process(view, 0);\n this.pos = 0;\n }\n }\n this.length += data.length;\n this.roundClean();\n return this;\n }\n digestInto(out: TArg<Uint8Array>): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n // Padding\n // We can avoid allocation of buffer for padding completely if it\n // was previously not allocated here. But it won't change performance.\n const { buffer, view, blockLen, isLE } = this;\n let { pos } = this;\n // append the bit '1' to the message\n buffer[pos++] = 0b10000000;\n clean(this.buffer.subarray(pos));\n // we have less than padOffset left in buffer, so we cannot put length in\n // current block, need process it and pad again\n if (this.padOffset > blockLen - pos) {\n this.process(view, 0);\n pos = 0;\n }\n // Pad until full block byte with zeros\n for (let i = pos; i < blockLen; i++) buffer[i] = 0;\n // `padOffset` reserves the whole length field. For SHA-384/512 the high 64 bits stay zero from\n // the padding fill above, and JS will overflow before user input can make that half non-zero.\n // So we only need to write the low 64 bits here.\n view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);\n this.process(view, 0);\n const oview = createView(out);\n const len = this.outputLen;\n // NOTE: we do division by 4 later, which must be fused in single op with modulo by JIT\n if (len % 4) throw new Error('_sha2: outputLen must be aligned to 32bit');\n const outLen = len / 4;\n const state = this.get();\n if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state');\n for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);\n }\n digest(): TRet<Uint8Array> {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n // Copy before destroy(): subclasses wipe `buffer` during cleanup, but `digest()` must return\n // fresh bytes to the caller.\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res as TRet<Uint8Array>;\n }\n _cloneInto(to?: T): T {\n to ||= new (this.constructor as any)() as T;\n to.set(...this.get());\n const { blockLen, buffer, length, finished, destroyed, pos } = this;\n to.destroyed = destroyed;\n to.finished = finished;\n to.length = length;\n to.pos = pos;\n // Only partial-block bytes need copying: when `length % blockLen === 0`, `pos === 0` and\n // later `update()` / `digestInto()` overwrite `to.buffer` from the start before reading it.\n if (length % blockLen) to.buffer.set(buffer);\n return to as unknown as any;\n }\n clone(): T {\n return this._cloneInto();\n }\n}\n\n/**\n * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.\n * Check out `test/misc/sha2-gen-iv.js` for recomputation guide.\n */\n\n/** Initial SHA256 state from RFC 6234 \u00A76.1: the first 32 bits of the fractional parts of the\n * square roots of the first eight prime numbers. Exported as a shared table; callers must treat\n * it as read-only because constructors copy words from it by index. */\nexport const SHA256_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Initial SHA224 state `H(0)` from RFC 6234 \u00A76.1. Exported as a shared table; callers must\n * treat it as read-only because constructors copy words from it by index. */\nexport const SHA224_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,\n]);\n\n/** Initial SHA384 state from RFC 6234 \u00A76.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the ninth\n * through sixteenth prime numbers. Exported as a shared table; callers must treat it as read-only\n * because constructors copy halves from it by index. */\nexport const SHA384_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xcbbb9d5d, 0xc1059ed8, 0x629a292a, 0x367cd507, 0x9159015a, 0x3070dd17, 0x152fecd8, 0xf70e5939,\n 0x67332667, 0xffc00b31, 0x8eb44a87, 0x68581511, 0xdb0c2e0d, 0x64f98fa7, 0x47b5481d, 0xbefa4fa4,\n]);\n\n/** Initial SHA512 state from RFC 6234 \u00A76.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the first\n * eight prime numbers. Exported as a shared table; callers must treat it as read-only because\n * constructors copy halves from it by index. */\nexport const SHA512_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,\n 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,\n]);\n", "/**\n * SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.\n * SHA256 is the fastest hash implementable in JS, even faster than Blake3.\n * Check out {@link https://www.rfc-editor.org/rfc/rfc4634 | RFC 4634} and\n * {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf | FIPS 180-4}.\n * @module\n */\nimport { Chi, HashMD, Maj, SHA224_IV, SHA256_IV, SHA384_IV, SHA512_IV } from './_md.ts';\nimport * as u64 from './_u64.ts';\nimport { type CHash, clean, createHasher, oidNist, rotr, type TRet } from './utils.ts';\n\n/**\n * SHA-224 / SHA-256 round constants from RFC 6234 \u00A75.1: the first 32 bits\n * of the cube roots of the first 64 primes (2..311).\n */\n// prettier-ignore\nconst SHA256_K = /* @__PURE__ */ Uint32Array.from([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n]);\n\n/** Reusable SHA-224 / SHA-256 message schedule buffer `W_t` from RFC 6234 \u00A76.2 step 1. */\nconst SHA256_W = /* @__PURE__ */ new Uint32Array(64);\n\n/** Internal SHA-224 / SHA-256 compression engine from RFC 6234 \u00A76.2. */\nabstract class SHA2_32B<T extends SHA2_32B<T>> extends HashMD<T> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected abstract A: number;\n protected abstract B: number;\n protected abstract C: number;\n protected abstract D: number;\n protected abstract E: number;\n protected abstract F: number;\n protected abstract G: number;\n protected abstract H: number;\n\n constructor(outputLen: number) {\n super(64, outputLen, 8, false);\n }\n protected get(): [number, number, number, number, number, number, number, number] {\n const { A, B, C, D, E, F, G, H } = this;\n return [A, B, C, D, E, F, G, H];\n }\n // prettier-ignore\n protected set(\n A: number, B: number, C: number, D: number, E: number, F: number, G: number, H: number\n ): void {\n this.A = A | 0;\n this.B = B | 0;\n this.C = C | 0;\n this.D = D | 0;\n this.E = E | 0;\n this.F = F | 0;\n this.G = G | 0;\n this.H = H | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);\n for (let i = 16; i < 64; i++) {\n const W15 = SHA256_W[i - 15];\n const W2 = SHA256_W[i - 2];\n const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);\n const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);\n SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;\n }\n // Compression function main loop, 64 rounds\n let { A, B, C, D, E, F, G, H } = this;\n for (let i = 0; i < 64; i++) {\n const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);\n const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);\n const T2 = (sigma0 + Maj(A, B, C)) | 0;\n H = G;\n G = F;\n F = E;\n E = (D + T1) | 0;\n D = C;\n C = B;\n B = A;\n A = (T1 + T2) | 0;\n }\n // Add the compressed chunk to the current hash value\n A = (A + this.A) | 0;\n B = (B + this.B) | 0;\n C = (C + this.C) | 0;\n D = (D + this.D) | 0;\n E = (E + this.E) | 0;\n F = (F + this.F) | 0;\n G = (G + this.G) | 0;\n H = (H + this.H) | 0;\n this.set(A, B, C, D, E, F, G, H);\n }\n protected roundClean(): void {\n clean(SHA256_W);\n }\n destroy(): void {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n this.set(0, 0, 0, 0, 0, 0, 0, 0);\n clean(this.buffer);\n }\n}\n\n/** Internal SHA-256 hash class grounded in RFC 6234 \u00A76.2. */\nexport class _SHA256 extends SHA2_32B<_SHA256> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected A: number = SHA256_IV[0] | 0;\n protected B: number = SHA256_IV[1] | 0;\n protected C: number = SHA256_IV[2] | 0;\n protected D: number = SHA256_IV[3] | 0;\n protected E: number = SHA256_IV[4] | 0;\n protected F: number = SHA256_IV[5] | 0;\n protected G: number = SHA256_IV[6] | 0;\n protected H: number = SHA256_IV[7] | 0;\n constructor() {\n super(32);\n }\n}\n\n/** Internal SHA-224 hash class grounded in RFC 6234 \u00A76.2 and \u00A78.5. */\nexport class _SHA224 extends SHA2_32B<_SHA224> {\n protected A: number = SHA224_IV[0] | 0;\n protected B: number = SHA224_IV[1] | 0;\n protected C: number = SHA224_IV[2] | 0;\n protected D: number = SHA224_IV[3] | 0;\n protected E: number = SHA224_IV[4] | 0;\n protected F: number = SHA224_IV[5] | 0;\n protected G: number = SHA224_IV[6] | 0;\n protected H: number = SHA224_IV[7] | 0;\n constructor() {\n super(28);\n }\n}\n\n// SHA2-512 is slower than sha256 in js because u64 operations are slow.\n\n// SHA-384 / SHA-512 round constants from RFC 6234 \u00A75.2:\n// 80 full 64-bit words split into high/low halves.\n// prettier-ignore\nconst K512 = /* @__PURE__ */ (() => u64.split([\n '0x428a2f98d728ae22', '0x7137449123ef65cd', '0xb5c0fbcfec4d3b2f', '0xe9b5dba58189dbbc',\n '0x3956c25bf348b538', '0x59f111f1b605d019', '0x923f82a4af194f9b', '0xab1c5ed5da6d8118',\n '0xd807aa98a3030242', '0x12835b0145706fbe', '0x243185be4ee4b28c', '0x550c7dc3d5ffb4e2',\n '0x72be5d74f27b896f', '0x80deb1fe3b1696b1', '0x9bdc06a725c71235', '0xc19bf174cf692694',\n '0xe49b69c19ef14ad2', '0xefbe4786384f25e3', '0x0fc19dc68b8cd5b5', '0x240ca1cc77ac9c65',\n '0x2de92c6f592b0275', '0x4a7484aa6ea6e483', '0x5cb0a9dcbd41fbd4', '0x76f988da831153b5',\n '0x983e5152ee66dfab', '0xa831c66d2db43210', '0xb00327c898fb213f', '0xbf597fc7beef0ee4',\n '0xc6e00bf33da88fc2', '0xd5a79147930aa725', '0x06ca6351e003826f', '0x142929670a0e6e70',\n '0x27b70a8546d22ffc', '0x2e1b21385c26c926', '0x4d2c6dfc5ac42aed', '0x53380d139d95b3df',\n '0x650a73548baf63de', '0x766a0abb3c77b2a8', '0x81c2c92e47edaee6', '0x92722c851482353b',\n '0xa2bfe8a14cf10364', '0xa81a664bbc423001', '0xc24b8b70d0f89791', '0xc76c51a30654be30',\n '0xd192e819d6ef5218', '0xd69906245565a910', '0xf40e35855771202a', '0x106aa07032bbd1b8',\n '0x19a4c116b8d2d0c8', '0x1e376c085141ab53', '0x2748774cdf8eeb99', '0x34b0bcb5e19b48a8',\n '0x391c0cb3c5c95a63', '0x4ed8aa4ae3418acb', '0x5b9cca4f7763e373', '0x682e6ff3d6b2b8a3',\n '0x748f82ee5defb2fc', '0x78a5636f43172f60', '0x84c87814a1f0ab72', '0x8cc702081a6439ec',\n '0x90befffa23631e28', '0xa4506cebde82bde9', '0xbef9a3f7b2c67915', '0xc67178f2e372532b',\n '0xca273eceea26619c', '0xd186b8c721c0c207', '0xeada7dd6cde0eb1e', '0xf57d4f7fee6ed178',\n '0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b',\n '0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c',\n '0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817'\n].map(n => BigInt(n))))();\nconst SHA512_Kh = /* @__PURE__ */ (() => K512[0])();\nconst SHA512_Kl = /* @__PURE__ */ (() => K512[1])();\n\n// Reusable high-half schedule buffer for the RFC 6234 \u00A76.4 64-bit `W_t` words.\nconst SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);\n// Reusable low-half schedule buffer for the RFC 6234 \u00A76.4 64-bit `W_t` words.\nconst SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);\n\n/** Internal SHA-384 / SHA-512 compression engine from RFC 6234 \u00A76.4. */\nabstract class SHA2_64B<T extends SHA2_64B<T>> extends HashMD<T> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n // h -- high 32 bits, l -- low 32 bits\n protected abstract Ah: number;\n protected abstract Al: number;\n protected abstract Bh: number;\n protected abstract Bl: number;\n protected abstract Ch: number;\n protected abstract Cl: number;\n protected abstract Dh: number;\n protected abstract Dl: number;\n protected abstract Eh: number;\n protected abstract El: number;\n protected abstract Fh: number;\n protected abstract Fl: number;\n protected abstract Gh: number;\n protected abstract Gl: number;\n protected abstract Hh: number;\n protected abstract Hl: number;\n\n constructor(outputLen: number) {\n super(128, outputLen, 16, false);\n }\n // prettier-ignore\n protected get(): [\n number, number, number, number, number, number, number, number,\n number, number, number, number, number, number, number, number\n ] {\n const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];\n }\n // prettier-ignore\n protected set(\n Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number,\n Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number\n ): void {\n this.Ah = Ah | 0;\n this.Al = Al | 0;\n this.Bh = Bh | 0;\n this.Bl = Bl | 0;\n this.Ch = Ch | 0;\n this.Cl = Cl | 0;\n this.Dh = Dh | 0;\n this.Dl = Dl | 0;\n this.Eh = Eh | 0;\n this.El = El | 0;\n this.Fh = Fh | 0;\n this.Fl = Fl | 0;\n this.Gh = Gh | 0;\n this.Gl = Gl | 0;\n this.Hh = Hh | 0;\n this.Hl = Hl | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) {\n SHA512_W_H[i] = view.getUint32(offset);\n SHA512_W_L[i] = view.getUint32((offset += 4));\n }\n for (let i = 16; i < 80; i++) {\n // s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7)\n const W15h = SHA512_W_H[i - 15] | 0;\n const W15l = SHA512_W_L[i - 15] | 0;\n const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);\n const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7);\n // s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6)\n const W2h = SHA512_W_H[i - 2] | 0;\n const W2l = SHA512_W_L[i - 2] | 0;\n const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);\n const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);\n // SHA512_W[i] = s0 + s1 + SHA512_W[i - 7] + SHA512_W[i - 16];\n const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);\n const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);\n SHA512_W_H[i] = SUMh | 0;\n SHA512_W_L[i] = SUMl | 0;\n }\n let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n // Compression function main loop, 80 rounds\n for (let i = 0; i < 80; i++) {\n // S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)\n const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);\n const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41);\n //const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const CHIh = (Eh & Fh) ^ (~Eh & Gh);\n const CHIl = (El & Fl) ^ (~El & Gl);\n // T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i]\n // prettier-ignore\n const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);\n const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);\n const T1l = T1ll | 0;\n // S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)\n const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);\n const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39);\n const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch);\n const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl);\n Hh = Gh | 0;\n Hl = Gl | 0;\n Gh = Fh | 0;\n Gl = Fl | 0;\n Fh = Eh | 0;\n Fl = El | 0;\n ({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));\n Dh = Ch | 0;\n Dl = Cl | 0;\n Ch = Bh | 0;\n Cl = Bl | 0;\n Bh = Ah | 0;\n Bl = Al | 0;\n const All = u64.add3L(T1l, sigma0l, MAJl);\n Ah = u64.add3H(All, T1h, sigma0h, MAJh);\n Al = All | 0;\n }\n // Add the compressed chunk to the current hash value\n ({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));\n ({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));\n ({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));\n ({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));\n ({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));\n ({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));\n ({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));\n ({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));\n this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);\n }\n protected roundClean(): void {\n clean(SHA512_W_H, SHA512_W_L);\n }\n destroy(): void {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n clean(this.buffer);\n this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n }\n}\n\n/** Internal SHA-512 hash class grounded in RFC 6234 \u00A76.3 and \u00A76.4. */\nexport class _SHA512 extends SHA2_64B<_SHA512> {\n protected Ah: number = SHA512_IV[0] | 0;\n protected Al: number = SHA512_IV[1] | 0;\n protected Bh: number = SHA512_IV[2] | 0;\n protected Bl: number = SHA512_IV[3] | 0;\n protected Ch: number = SHA512_IV[4] | 0;\n protected Cl: number = SHA512_IV[5] | 0;\n protected Dh: number = SHA512_IV[6] | 0;\n protected Dl: number = SHA512_IV[7] | 0;\n protected Eh: number = SHA512_IV[8] | 0;\n protected El: number = SHA512_IV[9] | 0;\n protected Fh: number = SHA512_IV[10] | 0;\n protected Fl: number = SHA512_IV[11] | 0;\n protected Gh: number = SHA512_IV[12] | 0;\n protected Gl: number = SHA512_IV[13] | 0;\n protected Hh: number = SHA512_IV[14] | 0;\n protected Hl: number = SHA512_IV[15] | 0;\n\n constructor() {\n super(64);\n }\n}\n\n/** Internal SHA-384 hash class grounded in RFC 6234 \u00A76.3 and \u00A76.4. */\nexport class _SHA384 extends SHA2_64B<_SHA384> {\n protected Ah: number = SHA384_IV[0] | 0;\n protected Al: number = SHA384_IV[1] | 0;\n protected Bh: number = SHA384_IV[2] | 0;\n protected Bl: number = SHA384_IV[3] | 0;\n protected Ch: number = SHA384_IV[4] | 0;\n protected Cl: number = SHA384_IV[5] | 0;\n protected Dh: number = SHA384_IV[6] | 0;\n protected Dl: number = SHA384_IV[7] | 0;\n protected Eh: number = SHA384_IV[8] | 0;\n protected El: number = SHA384_IV[9] | 0;\n protected Fh: number = SHA384_IV[10] | 0;\n protected Fl: number = SHA384_IV[11] | 0;\n protected Gh: number = SHA384_IV[12] | 0;\n protected Gl: number = SHA384_IV[13] | 0;\n protected Hh: number = SHA384_IV[14] | 0;\n protected Hl: number = SHA384_IV[15] | 0;\n\n constructor() {\n super(48);\n }\n}\n\n/**\n * Truncated SHA512/256 and SHA512/224.\n * SHA512_IV is XORed with 0xa5a5a5a5a5a5a5a5, then used as \"intermediary\" IV of SHA512/t.\n * Then t hashes string to produce result IV.\n * See the repo-side derivation recipe in `test/misc/sha2-gen-iv.js`.\n * These IV literals are checked against that script rather than a dedicated\n * local RFC section.\n */\n\n/** SHA-512/224 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T224_IV = /* @__PURE__ */ Uint32Array.from([\n 0x8c3d37c8, 0x19544da2, 0x73e19966, 0x89dcd4d6, 0x1dfab7ae, 0x32ff9c82, 0x679dd514, 0x582f9fcf,\n 0x0f6d2b69, 0x7bd44da8, 0x77e36f73, 0x04c48942, 0x3f9d85a8, 0x6a1d36c8, 0x1112e6ad, 0x91d692a1,\n]);\n\n/** SHA-512/256 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T256_IV = /* @__PURE__ */ Uint32Array.from([\n 0x22312194, 0xfc2bf72c, 0x9f555fa3, 0xc84c64c2, 0x2393b86b, 0x6f53b151, 0x96387719, 0x5940eabd,\n 0x96283ee2, 0xa88effe3, 0xbe5e1e25, 0x53863992, 0x2b0199fc, 0x2c85b8aa, 0x0eb72ddc, 0x81c52ca2,\n]);\n\n/** Internal SHA-512/224 hash class using the derived `T224_IV` and the shared\n * RFC 6234 \u00A76.4 compression engine. */\nexport class _SHA512_224 extends SHA2_64B<_SHA512_224> {\n protected Ah: number = T224_IV[0] | 0;\n protected Al: number = T224_IV[1] | 0;\n protected Bh: number = T224_IV[2] | 0;\n protected Bl: number = T224_IV[3] | 0;\n protected Ch: number = T224_IV[4] | 0;\n protected Cl: number = T224_IV[5] | 0;\n protected Dh: number = T224_IV[6] | 0;\n protected Dl: number = T224_IV[7] | 0;\n protected Eh: number = T224_IV[8] | 0;\n protected El: number = T224_IV[9] | 0;\n protected Fh: number = T224_IV[10] | 0;\n protected Fl: number = T224_IV[11] | 0;\n protected Gh: number = T224_IV[12] | 0;\n protected Gl: number = T224_IV[13] | 0;\n protected Hh: number = T224_IV[14] | 0;\n protected Hl: number = T224_IV[15] | 0;\n\n constructor() {\n super(28);\n }\n}\n\n/** Internal SHA-512/256 hash class using the derived `T256_IV` and the shared\n * RFC 6234 \u00A76.4 compression engine. */\nexport class _SHA512_256 extends SHA2_64B<_SHA512_256> {\n protected Ah: number = T256_IV[0] | 0;\n protected Al: number = T256_IV[1] | 0;\n protected Bh: number = T256_IV[2] | 0;\n protected Bl: number = T256_IV[3] | 0;\n protected Ch: number = T256_IV[4] | 0;\n protected Cl: number = T256_IV[5] | 0;\n protected Dh: number = T256_IV[6] | 0;\n protected Dl: number = T256_IV[7] | 0;\n protected Eh: number = T256_IV[8] | 0;\n protected El: number = T256_IV[9] | 0;\n protected Fh: number = T256_IV[10] | 0;\n protected Fl: number = T256_IV[11] | 0;\n protected Gh: number = T256_IV[12] | 0;\n protected Gl: number = T256_IV[13] | 0;\n protected Hh: number = T256_IV[14] | 0;\n protected Hl: number = T256_IV[15] | 0;\n\n constructor() {\n super(32);\n }\n}\n\n/**\n * SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info:\n *\n * - Trying 2^128 hashes would get 50% chance of collision, using birthday attack.\n * - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.\n * - Each sha256 hash is executing 2^18 bit operations.\n * - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-256.\n * ```ts\n * sha256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha256: TRet<CHash<_SHA256>> = /* @__PURE__ */ createHasher(\n () => new _SHA256(),\n /* @__PURE__ */ oidNist(0x01)\n);\n/**\n * SHA2-224 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-224.\n * ```ts\n * sha224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha224: TRet<CHash<_SHA224>> = /* @__PURE__ */ createHasher(\n () => new _SHA224(),\n /* @__PURE__ */ oidNist(0x04)\n);\n\n/**\n * SHA2-512 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512.\n * ```ts\n * sha512(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512: TRet<CHash<_SHA512>> = /* @__PURE__ */ createHasher(\n () => new _SHA512(),\n /* @__PURE__ */ oidNist(0x03)\n);\n/**\n * SHA2-384 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-384.\n * ```ts\n * sha384(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha384: TRet<CHash<_SHA384>> = /* @__PURE__ */ createHasher(\n () => new _SHA384(),\n /* @__PURE__ */ oidNist(0x02)\n);\n\n/**\n * SHA2-512/256 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/256.\n * ```ts\n * sha512_256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_256: TRet<CHash<_SHA512_256>> = /* @__PURE__ */ createHasher(\n () => new _SHA512_256(),\n /* @__PURE__ */ oidNist(0x06)\n);\n/**\n * SHA2-512/224 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/224.\n * ```ts\n * sha512_224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_224: TRet<CHash<_SHA512_224>> = /* @__PURE__ */ createHasher(\n () => new _SHA512_224(),\n /* @__PURE__ */ oidNist(0x05)\n);\n", "/**\n * SHA-256 Assignment Hash (contract v2)\n *\n * The canonical deterministic hash for Traffical bucket assignment and\n * weighted selection. Every Traffical SDK (JS, PHP, Swift) and the edge\n * runtime must produce byte-identical results for the same inputs.\n *\n * Why SHA-256 over the previous FNV-1a:\n * - FNV-1a passed single-layer uniformity but FAILED cross-experiment\n * independence with realistic UUID/ULID unit keys and `lay_*` layer IDs:\n * assignment in one layer could predict assignment in another, breaking\n * orthogonal experiment assignment. SHA-256's avalanche behaviour removes\n * that correlation.\n *\n * Contract:\n * - Input encoding: UTF-8 bytes.\n * - hashInt = first 64 bits of SHA-256(digest) as an unsigned big-endian integer.\n */\n\nimport { sha256 } from \"@noble/hashes/sha2.js\";\n\n/**\n * Shared UTF-8 encoder. The canonical hashing domain is the UTF-8 byte\n * sequence of the input string (NOT UTF-16 code units), so that every\n * Traffical SDK produces identical results regardless of host string\n * representation.\n */\nconst UTF8_ENCODER = new TextEncoder();\n\n/**\n * The domain-separation + version prefix for the assignment hash contract.\n * Bumping `v2` would intentionally re-roll every assignment.\n */\nexport const ASSIGNMENT_HASH_VERSION = \"v2\";\n\n/**\n * Number of UTF-8 bytes in a string. Used for length-framing so that field\n * values containing the `:` or `|` separators cannot create ambiguous inputs.\n * Length is measured in UTF-8 bytes (not UTF-16 code units / grapheme\n * clusters) so the framing is identical across languages.\n */\nexport function utf8ByteLength(value: string): number {\n return UTF8_ENCODER.encode(value).length;\n}\n\n/**\n * Builds the canonical, length-framed, domain-separated assignment input\n * string for bucket computation.\n *\n * Format:\n * traffical:assignment:v2|u:<unitLen>:<unitKeyValue>|l:<layerLen>:<layerId>\n *\n * Example:\n * traffical:assignment:v2|u:26:01JZ0000008K7QF2J9M3P4X1A2B|l:12:lay_kjeJRrjh\n */\nexport function assignmentInput(unitKeyValue: string, layerId: string): string {\n const unitLen = utf8ByteLength(unitKeyValue);\n const layerLen = utf8ByteLength(layerId);\n return `traffical:assignment:${ASSIGNMENT_HASH_VERSION}|u:${unitLen}:${unitKeyValue}|l:${layerLen}:${layerId}`;\n}\n\n/**\n * Computes the SHA-256 digest of a string over its UTF-8 byte encoding.\n */\nexport function sha256Digest(input: string): Uint8Array {\n return sha256(UTF8_ENCODER.encode(input));\n}\n\n/**\n * Interprets the first 8 bytes of a digest as an unsigned big-endian 64-bit\n * integer. Returns a bigint so the full 64 bits are preserved exactly.\n */\nexport function hash64BE(digest: Uint8Array): bigint {\n let value = 0n;\n for (let i = 0; i < 8; i++) {\n value = (value << 8n) | BigInt(digest[i]);\n }\n return value;\n}\n\n/**\n * Convenience helper: the unsigned big-endian 64-bit hash of a string's\n * SHA-256 digest. This is the single primitive used for both bucket\n * assignment and weighted selection.\n */\nexport function hashInt64(input: string): bigint {\n return hash64BE(sha256Digest(input));\n}\n", "/**\n * Bucket Computation\n *\n * Deterministic bucket assignment for traffic splitting.\n * The bucket is computed from the SHA-256 v2 assignment hash:\n * digest = SHA256(assignmentInput(unitKeyValue, layerId))\n * hashInt = first 64 bits of digest, unsigned big-endian\n * bucket = hashInt % bucketCount\n *\n * This ensures:\n * - Same user always gets same bucket for a given layer\n * - Different layers have independent bucketing (orthogonality) \u2014 SHA-256's\n * avalanche behaviour passes cross-experiment independence where FNV-1a did not\n * - Deterministic results across SDK and server\n */\n\nimport { assignmentInput, sha256Digest, hash64BE } from \"./assignment-hash.js\";\n\n/**\n * Computes the bucket for a given unit and layer.\n *\n * @param unitKeyValue - The value of the unit key (e.g., userId value)\n * @param layerId - The layer ID for orthogonal bucketing\n * @param bucketCount - Total number of buckets (e.g., 1000)\n * @returns Bucket number in range [0, bucketCount - 1]\n */\nexport function computeBucket(\n unitKeyValue: string,\n layerId: string,\n bucketCount: number\n): number {\n const digest = sha256Digest(assignmentInput(unitKeyValue, layerId));\n const hashInt = hash64BE(digest);\n return Number(hashInt % BigInt(bucketCount));\n}\n\n/**\n * Checks if a bucket falls within a range.\n *\n * @param bucket - The computed bucket\n * @param range - [start, end] inclusive range\n * @returns True if bucket is in range\n */\nexport function isInBucketRange(\n bucket: number,\n range: [number, number]\n): boolean {\n return bucket >= range[0] && bucket <= range[1];\n}\n\n/**\n * Finds which allocation matches a given bucket.\n *\n * @param bucket - The computed bucket\n * @param allocations - Array of allocations with bucket ranges\n * @returns The matching allocation, or null if none match\n */\nexport function findMatchingAllocation<\n T extends { bucketRange: [number, number] }\n>(bucket: number, allocations: T[]): T | null {\n for (const allocation of allocations) {\n if (isInBucketRange(bucket, allocation.bucketRange)) {\n return allocation;\n }\n }\n return null;\n}\n\n/**\n * Converts a percentage to a bucket range.\n *\n * @param percentage - Traffic percentage (0-100)\n * @param bucketCount - Total buckets\n * @param startBucket - Starting bucket (default 0)\n * @returns [start, end] bucket range\n */\nexport function percentageToBucketRange(\n percentage: number,\n bucketCount: number,\n startBucket = 0\n): [number, number] {\n const bucketsNeeded = Math.floor((percentage / 100) * bucketCount);\n const endBucket = Math.min(startBucket + bucketsNeeded - 1, bucketCount - 1);\n return [startBucket, endBucket];\n}\n\n/**\n * Creates non-overlapping bucket ranges for multiple variants.\n *\n * @param percentages - Array of percentages that should sum to <= 100\n * @param bucketCount - Total buckets\n * @returns Array of [start, end] bucket ranges\n */\nexport function createBucketRanges(\n percentages: number[],\n bucketCount: number\n): [number, number][] {\n const ranges: [number, number][] = [];\n let currentBucket = 0;\n\n for (const percentage of percentages) {\n if (percentage <= 0) continue;\n\n const bucketsNeeded = Math.floor((percentage / 100) * bucketCount);\n if (bucketsNeeded > 0) {\n const endBucket = currentBucket + bucketsNeeded - 1;\n ranges.push([currentBucket, endBucket]);\n currentBucket = endBucket + 1;\n }\n }\n\n return ranges;\n}\n\n", "/**\n * Weighted Selection\n *\n * Deterministic weighted selection using the SHA-256 v2 assignment hash.\n * Used by both per-entity resolution and contextual bandit scoring.\n *\n * The seed string is hashed with SHA-256; the first 64 bits (unsigned,\n * big-endian) are reduced to a uniform value in [0, 1) via mod 2^53 (which\n * keeps full IEEE-754 double precision and stays within a signed 64-bit\n * integer for the PHP/Swift implementations).\n */\n\nimport { hashInt64 } from \"./assignment-hash.js\";\n\n/** 2^53 \u2014 the largest exactly-representable power of two in a JS number. */\nconst UNIFORM_MODULUS = 1n << 53n;\nconst UNIFORM_DENOMINATOR = 9007199254740992; // 2^53\n\n/**\n * Performs deterministic weighted selection using a hash.\n *\n * Uses the seed string to deterministically select an index based on weights.\n * This ensures the same seed always produces the same selection for a given\n * weight distribution.\n *\n * @param weights - Array of weights (should sum to 1.0)\n * @param seed - Seed string for deterministic hashing\n * @returns Index of selected entry\n */\nexport function weightedSelection(weights: number[], seed: string): number {\n if (weights.length === 0) return 0;\n if (weights.length === 1) return 0;\n\n const hashInt = hashInt64(seed);\n const random = Number(hashInt % UNIFORM_MODULUS) / UNIFORM_DENOMINATOR;\n\n let cumulative = 0;\n for (let i = 0; i < weights.length; i++) {\n cumulative += weights[i];\n if (random < cumulative) {\n return i;\n }\n }\n\n return weights.length - 1;\n}\n", "/**\n * Condition Evaluation\n *\n * Evaluates context predicates to determine policy eligibility.\n * Conditions are AND-ed together: all must match for a policy to apply.\n */\n\nimport type { Context, BundleCondition } from \"../types/index.js\";\n\n/**\n * Evaluates a single condition against a context.\n *\n * @param condition - The condition to evaluate\n * @param context - The context to evaluate against\n * @returns True if the condition matches\n */\nexport function evaluateCondition(\n condition: BundleCondition,\n context: Context\n): boolean {\n const { field, op, value, values } = condition;\n\n // Get the context value using dot notation\n const contextValue = getNestedValue(context, field);\n\n switch (op) {\n case \"eq\":\n return contextValue === value;\n\n case \"neq\":\n return contextValue !== value;\n\n case \"in\":\n if (!Array.isArray(values)) return false;\n return values.includes(contextValue);\n\n case \"nin\":\n if (!Array.isArray(values)) return true;\n return !values.includes(contextValue);\n\n case \"gt\":\n return (\n typeof contextValue === \"number\" && contextValue > (value as number)\n );\n\n case \"gte\":\n return (\n typeof contextValue === \"number\" && contextValue >= (value as number)\n );\n\n case \"lt\":\n return (\n typeof contextValue === \"number\" && contextValue < (value as number)\n );\n\n case \"lte\":\n return (\n typeof contextValue === \"number\" && contextValue <= (value as number)\n );\n\n case \"contains\":\n return (\n typeof contextValue === \"string\" &&\n typeof value === \"string\" &&\n contextValue.includes(value)\n );\n\n case \"startsWith\":\n return (\n typeof contextValue === \"string\" &&\n typeof value === \"string\" &&\n contextValue.startsWith(value)\n );\n\n case \"endsWith\":\n return (\n typeof contextValue === \"string\" &&\n typeof value === \"string\" &&\n contextValue.endsWith(value)\n );\n\n case \"regex\":\n if (typeof contextValue !== \"string\" || typeof value !== \"string\") {\n return false;\n }\n try {\n const regex = new RegExp(value);\n return regex.test(contextValue);\n } catch {\n return false;\n }\n\n case \"exists\":\n return contextValue !== undefined && contextValue !== null;\n\n case \"notExists\":\n return contextValue === undefined || contextValue === null;\n\n default:\n // Unknown operator, fail safe by not matching\n return false;\n }\n}\n\n/**\n * Evaluates all conditions against a context.\n * All conditions must match (AND logic).\n *\n * @param conditions - Array of conditions\n * @param context - The context to evaluate against\n * @returns True if all conditions match (or if there are no conditions)\n */\nexport function evaluateConditions(\n conditions: BundleCondition[],\n context: Context\n): boolean {\n // Empty conditions = always match\n if (conditions.length === 0) {\n return true;\n }\n\n // All conditions must match (AND)\n return conditions.every((condition) => evaluateCondition(condition, context));\n}\n\n/**\n * Gets a nested value from an object using dot notation.\n *\n * @example\n * getNestedValue({ user: { name: \"Alice\" } }, \"user.name\") // \"Alice\"\n * getNestedValue({ tags: [\"a\", \"b\"] }, \"tags.0\") // \"a\"\n */\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split(\".\");\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current === null || current === undefined) {\n return undefined;\n }\n\n if (typeof current === \"object\") {\n current = (current as Record<string, unknown>)[part];\n } else {\n return undefined;\n }\n }\n\n return current;\n}\n\n// =============================================================================\n// Condition Builder Helpers\n// =============================================================================\n\n/**\n * Creates an equality condition.\n */\nexport function eq(field: string, value: unknown): BundleCondition {\n return { field, op: \"eq\", value };\n}\n\n/**\n * Creates a not-equal condition.\n */\nexport function neq(field: string, value: unknown): BundleCondition {\n return { field, op: \"neq\", value };\n}\n\n/**\n * Creates an \"in\" condition.\n */\nexport function inValues(field: string, values: unknown[]): BundleCondition {\n return { field, op: \"in\", values };\n}\n\n/**\n * Creates a \"not in\" condition.\n */\nexport function notIn(field: string, values: unknown[]): BundleCondition {\n return { field, op: \"nin\", values };\n}\n\n/**\n * Creates a greater-than condition.\n */\nexport function gt(field: string, value: number): BundleCondition {\n return { field, op: \"gt\", value };\n}\n\n/**\n * Creates a greater-than-or-equal condition.\n */\nexport function gte(field: string, value: number): BundleCondition {\n return { field, op: \"gte\", value };\n}\n\n/**\n * Creates a less-than condition.\n */\nexport function lt(field: string, value: number): BundleCondition {\n return { field, op: \"lt\", value };\n}\n\n/**\n * Creates a less-than-or-equal condition.\n */\nexport function lte(field: string, value: number): BundleCondition {\n return { field, op: \"lte\", value };\n}\n\n/**\n * Creates a string contains condition.\n */\nexport function contains(field: string, value: string): BundleCondition {\n return { field, op: \"contains\", value };\n}\n\n/**\n * Creates a string starts-with condition.\n */\nexport function startsWith(field: string, value: string): BundleCondition {\n return { field, op: \"startsWith\", value };\n}\n\n/**\n * Creates a string ends-with condition.\n */\nexport function endsWith(field: string, value: string): BundleCondition {\n return { field, op: \"endsWith\", value };\n}\n\n/**\n * Creates a regex match condition.\n */\nexport function regex(field: string, pattern: string): BundleCondition {\n return { field, op: \"regex\", value: pattern };\n}\n\n/**\n * Creates an exists condition.\n */\nexport function exists(field: string): BundleCondition {\n return { field, op: \"exists\" };\n}\n\n/**\n * Creates a not-exists condition.\n */\nexport function notExists(field: string): BundleCondition {\n return { field, op: \"notExists\" };\n}\n\n", "/**\n * Contextual Bandit Scoring\n *\n * Pure functions for computing personalized allocation probabilities\n * from a trained linear contextual model. Used by the resolution engine\n * when a policy has a `contextualModel` field.\n *\n * Scoring pipeline:\n * 1. Compute linear score per allocation: intercept + SUM(coef * feature)\n * 2. Apply softmax with gamma temperature to get probabilities\n * 3. Enforce action probability floor (minimum exploration)\n * 4. Deterministic weighted selection via SHA-256 v2 hash\n */\n\nimport type {\n BundlePolicy,\n BundleAllocation,\n BundleAllocationCoefficients,\n BundleContextualModel,\n Context,\n} from \"../types/index.js\";\nimport { weightedSelection } from \"../hashing/weighted.js\";\n\n/**\n * Computes the linear score for a single allocation given context features.\n *\n * score = intercept\n * + SUM_numeric( coef_i * context[key_i] OR missing_i )\n * + SUM_categorical( values[context[key_j]] OR missing_j )\n */\nexport function computeAllocationScore(\n coefficients: BundleAllocationCoefficients,\n context: Context\n): number {\n let score = coefficients.intercept;\n\n for (const { key, coef, missing } of coefficients.numeric) {\n const value = context[key];\n score += typeof value === \"number\" ? coef * value : missing;\n }\n\n for (const { key, values, missing } of coefficients.categorical) {\n const value = context[key];\n const strValue = value !== undefined && value !== null ? String(value) : null;\n score +=\n strValue !== null && strValue in values ? values[strValue] : missing;\n }\n\n return score;\n}\n\n/**\n * Applies softmax with temperature (gamma) to convert raw scores to probabilities.\n *\n * Uses the numerically stable variant: subtract max before exponentiation.\n * Lower gamma makes the distribution more peaked (exploitative);\n * higher gamma makes it more uniform (explorative).\n */\nexport function softmaxProbabilities(\n scores: number[],\n gamma: number\n): number[] {\n if (scores.length === 0) return [];\n if (scores.length === 1) return [1.0];\n\n const safeGamma = Math.max(gamma, 1e-10);\n const scaled = scores.map((s) => s / safeGamma);\n const maxScaled = Math.max(...scaled);\n const exps = scaled.map((s) => Math.exp(s - maxScaled));\n const sumExp = exps.reduce((a, b) => a + b, 0);\n return exps.map((e) => e / sumExp);\n}\n\n/**\n * Enforces a minimum probability floor on each allocation and renormalizes.\n *\n * Any allocation below the floor is raised to it; surplus probability\n * is deducted proportionally from allocations above the floor.\n */\nexport function applyProbabilityFloor(\n probs: number[],\n floor: number\n): number[] {\n if (probs.length === 0) return [];\n if (floor <= 0) return probs;\n\n const n = probs.length;\n const maxFloor = 1.0 / n;\n const effectiveFloor = Math.min(floor, maxFloor);\n\n const floored = probs.map((p) => Math.max(p, effectiveFloor));\n const sum = floored.reduce((a, b) => a + b, 0);\n\n if (sum === 0) return Array(n).fill(1 / n);\n return floored.map((p) => p / sum);\n}\n\n/**\n * Resolves a contextual policy to a specific allocation using the trained model.\n *\n * Steps:\n * 1. Score each allocation using its coefficients (or defaultAllocationScore)\n * 2. Convert scores to probabilities via softmax(gamma)\n * 3. Apply the action probability floor\n * 4. Deterministically select using weightedSelection with a hash seed\n *\n * @returns The selected allocation, or null if the policy has no allocations\n */\nexport function resolveContextualPolicy(\n policy: BundlePolicy,\n context: Context,\n unitKeyValue: string\n): BundleAllocation | null {\n const model = policy.contextualModel;\n if (!model) return null;\n if (policy.allocations.length === 0) return null;\n\n const scores = computeContextualScores(model, policy.allocations, context);\n const probs = softmaxProbabilities(scores, model.gamma);\n const floored = applyProbabilityFloor(probs, model.actionProbabilityFloor);\n\n const seed = `ctx:${unitKeyValue}:${policy.id}`;\n const selectedIndex = weightedSelection(floored, seed);\n\n return policy.allocations[selectedIndex];\n}\n\n/**\n * Computes raw scores for all allocations in a policy.\n */\nfunction computeContextualScores(\n model: BundleContextualModel,\n allocations: BundleAllocation[],\n context: Context\n): number[] {\n return allocations.map((alloc) => {\n const coefficients = model.coefficients[alloc.name];\n if (!coefficients) return model.defaultAllocationScore;\n return computeAllocationScore(coefficients, context);\n });\n}\n", "/* @ts-self-types=\"./index.d.ts\" */\nimport { urlAlphabet as scopedUrlAlphabet } from './url-alphabet/index.js'\nexport { urlAlphabet } from './url-alphabet/index.js'\nexport let random = bytes => crypto.getRandomValues(new Uint8Array(bytes))\nexport let customRandom = (alphabet, defaultSize, getRandom) => {\n let mask = (2 << Math.log2(alphabet.length - 1)) - 1\n let step = -~((1.6 * mask * defaultSize) / alphabet.length)\n return (size = defaultSize) => {\n let id = ''\n while (true) {\n let bytes = getRandom(step)\n let j = step | 0\n while (j--) {\n id += alphabet[bytes[j] & mask] || ''\n if (id.length >= size) return id\n }\n }\n }\n}\nexport let customAlphabet = (alphabet, size = 21) =>\n customRandom(alphabet, size | 0, random)\nexport let nanoid = (size = 21) => {\n let id = ''\n let bytes = crypto.getRandomValues(new Uint8Array((size |= 0)))\n while (size--) {\n id += scopedUrlAlphabet[bytes[size] & 63]\n }\n return id\n}\n", "function createError(message) {\n const err = new Error(message);\n err.source = \"ulid\";\n return err;\n}\n// These values should NEVER change. If\n// they do, we're no longer making ulids!\nconst ENCODING = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\"; // Crockford's Base32\nconst ENCODING_LEN = ENCODING.length;\nconst TIME_MAX = Math.pow(2, 48) - 1;\nconst TIME_LEN = 10;\nconst RANDOM_LEN = 16;\nfunction replaceCharAt(str, index, char) {\n if (index > str.length - 1) {\n return str;\n }\n return str.substr(0, index) + char + str.substr(index + 1);\n}\nfunction incrementBase32(str) {\n let done = undefined;\n let index = str.length;\n let char;\n let charIndex;\n const maxCharIndex = ENCODING_LEN - 1;\n while (!done && index-- >= 0) {\n char = str[index];\n charIndex = ENCODING.indexOf(char);\n if (charIndex === -1) {\n throw createError(\"incorrectly encoded string\");\n }\n if (charIndex === maxCharIndex) {\n str = replaceCharAt(str, index, ENCODING[0]);\n continue;\n }\n done = replaceCharAt(str, index, ENCODING[charIndex + 1]);\n }\n if (typeof done === \"string\") {\n return done;\n }\n throw createError(\"cannot increment this string\");\n}\nfunction randomChar(prng) {\n let rand = Math.floor(prng() * ENCODING_LEN);\n if (rand === ENCODING_LEN) {\n rand = ENCODING_LEN - 1;\n }\n return ENCODING.charAt(rand);\n}\nfunction encodeTime(now, len) {\n if (isNaN(now)) {\n throw new Error(now + \" must be a number\");\n }\n if (now > TIME_MAX) {\n throw createError(\"cannot encode time greater than \" + TIME_MAX);\n }\n if (now < 0) {\n throw createError(\"time must be positive\");\n }\n if (Number.isInteger(Number(now)) === false) {\n throw createError(\"time must be an integer\");\n }\n let mod;\n let str = \"\";\n for (; len > 0; len--) {\n mod = now % ENCODING_LEN;\n str = ENCODING.charAt(mod) + str;\n now = (now - mod) / ENCODING_LEN;\n }\n return str;\n}\nfunction encodeRandom(len, prng) {\n let str = \"\";\n for (; len > 0; len--) {\n str = randomChar(prng) + str;\n }\n return str;\n}\nfunction decodeTime(id) {\n if (id.length !== TIME_LEN + RANDOM_LEN) {\n throw createError(\"malformed ulid\");\n }\n var time = id\n .substr(0, TIME_LEN)\n .split(\"\")\n .reverse()\n .reduce((carry, char, index) => {\n const encodingIndex = ENCODING.indexOf(char);\n if (encodingIndex === -1) {\n throw createError(\"invalid character found: \" + char);\n }\n return (carry += encodingIndex * Math.pow(ENCODING_LEN, index));\n }, 0);\n if (time > TIME_MAX) {\n throw createError(\"malformed ulid, timestamp too large\");\n }\n return time;\n}\nfunction detectPrng(allowInsecure = false, root) {\n if (!root) {\n root = typeof window !== \"undefined\" ? window : null;\n }\n const browserCrypto = root && (root.crypto || root.msCrypto);\n if (browserCrypto) {\n return () => {\n const buffer = new Uint8Array(1);\n browserCrypto.getRandomValues(buffer);\n return buffer[0] / 0xff;\n };\n }\n else {\n try {\n const nodeCrypto = require(\"crypto\");\n return () => nodeCrypto.randomBytes(1).readUInt8() / 0xff;\n }\n catch (e) { }\n }\n if (allowInsecure) {\n try {\n console.error(\"secure crypto unusable, falling back to insecure Math.random()!\");\n }\n catch (e) { }\n return () => Math.random();\n }\n throw createError(\"secure crypto unusable, insecure Math.random not allowed\");\n}\nfunction factory(currPrng) {\n if (!currPrng) {\n currPrng = detectPrng();\n }\n return function ulid(seedTime) {\n if (isNaN(seedTime)) {\n seedTime = Date.now();\n }\n return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN, currPrng);\n };\n}\nfunction monotonicFactory(currPrng) {\n if (!currPrng) {\n currPrng = detectPrng();\n }\n let lastTime = 0;\n let lastRandom;\n return function ulid(seedTime) {\n if (isNaN(seedTime)) {\n seedTime = Date.now();\n }\n if (seedTime <= lastTime) {\n const incrementedRandom = (lastRandom = incrementBase32(lastRandom));\n return encodeTime(lastTime, TIME_LEN) + incrementedRandom;\n }\n lastTime = seedTime;\n const newRandom = (lastRandom = encodeRandom(RANDOM_LEN, currPrng));\n return encodeTime(seedTime, TIME_LEN) + newRandom;\n };\n}\nconst ulid = factory();\n\nexport { decodeTime, detectPrng, encodeRandom, encodeTime, factory, incrementBase32, monotonicFactory, randomChar, replaceCharAt, ulid };\n", "/**\n * ID Generation Utilities\n *\n * Provides consistent ID generation with type prefixes for all entities and events.\n *\n * Entity IDs: 8-character NanoID with prefix (e.g., \"proj_hVF1cCoC\")\n * - Compact and URL-friendly\n * - 64^8 = 281 trillion combinations\n * - With DB constraints, collisions are handled via retry\n *\n * Event IDs: ULID with prefix (e.g., \"dec_01JHFK1WWMMG7M0XPEBTYXZEBW\")\n * - Lexicographically sortable (time-ordered)\n * - Contains millisecond timestamp for analytics\n */\n\nimport { customAlphabet } from \"nanoid\";\nimport { ulid } from \"ulid\";\n\n// =============================================================================\n// NanoID Configuration\n// =============================================================================\n\n/**\n * URL-safe alphabet for NanoID (64 characters).\n * Includes: 0-9, A-Z, a-z (no special chars to avoid URL encoding issues)\n */\nconst NANOID_ALPHABET = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n\n/**\n * Default length for entity IDs (without prefix).\n * 64^8 = 281,474,976,710,656 (~281 trillion) combinations.\n */\nconst ENTITY_ID_LENGTH = 8;\n\n/**\n * NanoID generator with custom alphabet.\n */\nconst nanoid = customAlphabet(NANOID_ALPHABET, ENTITY_ID_LENGTH);\n\n// =============================================================================\n// ID Prefixes\n// =============================================================================\n\n/**\n * Entity ID prefixes for each entity type.\n */\nexport type EntityIdPrefix =\n | \"org\" // Organization\n | \"proj\" // Project\n | \"env\" // Environment\n | \"ns\" // Namespace\n | \"lay\" // Layer\n | \"pol\" // Policy\n | \"alloc\" // Allocation\n | \"param\" // Parameter\n | \"dom\" // DOM Binding\n | \"ovr\" // Environment Override\n | \"ak\"; // API Key\n\n/**\n * Event ID prefixes for each event type.\n */\nexport type EventIdPrefix = \"dec\" | \"exp\" | \"trk\" | \"asn\";\n\n// =============================================================================\n// Generic ID Generation\n// =============================================================================\n\n/**\n * Generates a prefixed 8-char NanoID for the specified entity type.\n *\n * @param prefix - The entity type prefix\n * @returns A prefixed NanoID string (e.g., \"proj_hVF1cCoC\")\n */\nexport function generateEntityId(prefix: EntityIdPrefix): string {\n return `${prefix}_${nanoid()}`;\n}\n\n/**\n * Generates a prefixed ULID for the specified event type.\n * Events use ULID for time-sortability in analytics.\n *\n * @param prefix - The event type prefix\n * @returns A prefixed ULID string (e.g., \"dec_01JHFK1WWMMG7M0XPEBTYXZEBW\")\n */\nexport function generateEventId(prefix: EventIdPrefix): string {\n return `${prefix}_${ulid()}`;\n}\n\n/**\n * Generates a plain 8-char NanoID without prefix.\n * Used for internal IDs that don't need type identification.\n */\nexport function generateShortId(): string {\n return nanoid();\n}\n\n// =============================================================================\n// Entity ID Convenience Functions\n// =============================================================================\n\n/** Generates an Organization ID with \"org_\" prefix */\nexport function generateOrgId(): string {\n return generateEntityId(\"org\");\n}\n\n/** Generates a Project ID with \"proj_\" prefix */\nexport function generateProjectId(): string {\n return generateEntityId(\"proj\");\n}\n\n/** Generates an Environment ID with \"env_\" prefix */\nexport function generateEnvironmentId(): string {\n return generateEntityId(\"env\");\n}\n\n/** Generates a Namespace ID with \"ns_\" prefix */\nexport function generateNamespaceId(): string {\n return generateEntityId(\"ns\");\n}\n\n/** Generates a Layer ID with \"lay_\" prefix */\nexport function generateLayerId(): string {\n return generateEntityId(\"lay\");\n}\n\n/** Generates a Policy ID with \"pol_\" prefix */\nexport function generatePolicyId(): string {\n return generateEntityId(\"pol\");\n}\n\n/** Generates an Allocation ID with \"alloc_\" prefix */\nexport function generateAllocationId(): string {\n return generateEntityId(\"alloc\");\n}\n\n/** Generates a Parameter ID with \"param_\" prefix */\nexport function generateParameterId(): string {\n return generateEntityId(\"param\");\n}\n\n/** Generates a DOM Binding ID with \"dom_\" prefix */\nexport function generateDomBindingId(): string {\n return generateEntityId(\"dom\");\n}\n\n/** Generates an Environment Override ID with \"ovr_\" prefix */\nexport function generateOverrideId(): string {\n return generateEntityId(\"ovr\");\n}\n\n/** Generates an API Key ID with \"ak_\" prefix */\nexport function generateApiKeyId(): string {\n return generateEntityId(\"ak\");\n}\n\n// =============================================================================\n// Event ID Convenience Functions (Keep ULID for time-sortability)\n// =============================================================================\n\n/** Generates a Decision event ID with \"dec_\" prefix (ULID) */\nexport function generateDecisionId(): string {\n return generateEventId(\"dec\");\n}\n\n/** Generates an Exposure event ID with \"exp_\" prefix (ULID) */\nexport function generateExposureId(): string {\n return generateEventId(\"exp\");\n}\n\n/** Generates a Track event ID with \"trk_\" prefix (ULID) */\nexport function generateTrackEventId(): string {\n return generateEventId(\"trk\");\n}\n\n/** Generates an Assignment log entry ID with \"asn_\" prefix (ULID) */\nexport function generateAssignmentId(): string {\n return generateEventId(\"asn\");\n}\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\n/**\n * Extracts the timestamp from a ULID-based ID.\n * Only works for event IDs (ULID format).\n *\n * @param id - A prefixed ULID (e.g., \"dec_01JHFK1WWMMG7M0XPEBTYXZEBW\")\n * @returns The timestamp as a Date, or null if invalid\n */\nexport function getIdTimestamp(id: string): Date | null {\n // Extract the ULID part (after the prefix and underscore)\n const parts = id.split(\"_\");\n if (parts.length < 2) {\n return null;\n }\n\n const ulidPart = parts[1];\n if (!ulidPart || ulidPart.length !== 26) {\n return null;\n }\n\n // ULID timestamp is encoded in the first 10 characters (Crockford's Base32)\n const ENCODING = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\n const TIME_LEN = 10;\n\n let time = 0;\n for (let i = 0; i < TIME_LEN; i++) {\n const char = ulidPart.charAt(i).toUpperCase();\n const index = ENCODING.indexOf(char);\n if (index === -1) {\n return null;\n }\n time = time * 32 + index;\n }\n\n return new Date(time);\n}\n\n/**\n * @deprecated Use getIdTimestamp instead\n */\nexport function getEventIdTimestamp(eventId: string): Date | null {\n return getIdTimestamp(eventId);\n}\n", "/**\n * Resolution Engine\n *\n * Pure functions for parameter resolution using layered config and policies.\n * Implements the Google-inspired layering system where:\n * - Parameters are partitioned into layers\n * - Within a layer, only one policy can be active for a unit\n * - Across layers, policies overlap freely (different parameters)\n *\n * Resolution order (lowest to highest priority):\n * 1. Caller defaults (always safe fallback)\n * 2. Parameter defaults (from bundle)\n * 3. Layer policies (each parameter belongs to exactly one layer)\n */\n\nimport type {\n ConfigBundle,\n BundleParameter,\n BundlePolicy,\n BundleAllocation,\n Context,\n ParameterValue,\n DecisionResult,\n LayerResolution,\n Id,\n} from \"../types/index.js\";\nimport { computeBucket, findMatchingAllocation } from \"../hashing/bucket.js\";\nimport { weightedSelection } from \"../hashing/weighted.js\";\nimport { evaluateConditions } from \"./conditions.js\";\nimport { resolveContextualPolicy } from \"../scoring/contextual.js\";\nimport { generateDecisionId } from \"../ids/index.js\";\n\n/**\n * Filters context to only include fields allowed by matched policies.\n * Collects the union of all allowed fields from policies with contextLogging config.\n *\n * @param context - The full evaluation context\n * @param policies - Matched policies from resolution\n * @returns Filtered context with only allowed fields, or undefined if no fields allowed\n */\nfunction filterContext(\n context: Context,\n policies: BundlePolicy[]\n): Context | undefined {\n // Collect union of all allowed fields from matched policies\n const allowedFields = new Set<string>();\n for (const policy of policies) {\n if (policy.contextLogging?.allowedFields) {\n for (const field of policy.contextLogging.allowedFields) {\n allowedFields.add(field);\n }\n }\n }\n\n // If no fields are allowed, return undefined\n if (allowedFields.size === 0) {\n return undefined;\n }\n\n // Filter context to only include allowed fields\n const filtered: Context = {};\n for (const field of allowedFields) {\n if (field in context) {\n filtered[field] = context[field];\n }\n }\n\n // Return undefined if no fields matched\n return Object.keys(filtered).length > 0 ? filtered : undefined;\n}\n\n// =============================================================================\n// Per-Entity Resolution Helpers\n// =============================================================================\n\n/**\n * Builds an entity ID from context using the policy's entityKeys.\n *\n * @param entityKeys - Array of context keys that identify the entity\n * @param context - The evaluation context\n * @returns Entity ID string, or null if any key is missing\n */\nfunction buildEntityId(entityKeys: string[], context: Context): string | null {\n const parts: string[] = [];\n for (const key of entityKeys) {\n const value = context[key];\n if (value === undefined || value === null) {\n return null;\n }\n parts.push(String(value));\n }\n return parts.join(\"_\");\n}\n\n/**\n * Creates uniform weights for dynamic allocations.\n *\n * @param count - Number of allocations\n * @returns Array of equal weights summing to 1.0\n */\nfunction createUniformWeights(count: number): number[] {\n if (count <= 0) return [];\n const weight = 1 / count;\n return Array(count).fill(weight);\n}\n\n/**\n * Gets entity weights from the bundle's entityState.\n *\n * @param bundle - The config bundle\n * @param policyId - The policy ID\n * @param entityId - The entity ID\n * @param allocationCount - Number of allocations (for dynamic allocations)\n * @returns Entity weights or uniform weights for cold start\n */\nfunction getEntityWeights(\n bundle: ConfigBundle,\n policyId: Id,\n entityId: string,\n allocationCount: number\n): number[] {\n const policyState = bundle.entityState?.[policyId];\n\n if (!policyState) {\n // No state for this policy - use uniform weights\n return createUniformWeights(allocationCount);\n }\n\n // Try entity-specific weights first\n const entityWeights = policyState.entities[entityId];\n if (entityWeights && entityWeights.weights.length === allocationCount) {\n return entityWeights.weights;\n }\n\n // Fall back to global prior\n const globalWeights = policyState._global;\n if (globalWeights && globalWeights.weights.length === allocationCount) {\n return globalWeights.weights;\n }\n\n // Last resort: uniform weights\n return createUniformWeights(allocationCount);\n}\n\n/**\n * Resolves a per-entity policy using weighted selection.\n *\n * @param bundle - The config bundle\n * @param policy - The policy with entityConfig\n * @param context - The evaluation context\n * @param unitKeyValue - The unit key value for hashing\n * @returns The selected allocation and entity ID, or null if cannot resolve\n */\nfunction resolvePerEntityPolicy(\n bundle: ConfigBundle,\n policy: BundlePolicy,\n context: Context,\n unitKeyValue: string\n): { allocation: BundleAllocation; entityId: string } | null {\n const entityConfig = policy.entityConfig;\n if (!entityConfig) return null;\n\n // Build entity ID from context\n const entityId = buildEntityId(entityConfig.entityKeys, context);\n if (!entityId) {\n // Missing entity keys - cannot resolve\n return null;\n }\n\n // Determine allocations\n let allocations: BundleAllocation[];\n let allocationCount: number;\n\n if (entityConfig.dynamicAllocations) {\n // Dynamic allocations from context\n const countKey = entityConfig.dynamicAllocations.countKey;\n const count = context[countKey];\n if (typeof count !== \"number\" || count <= 0) {\n return null;\n }\n allocationCount = Math.floor(count);\n\n // Create synthetic allocations for dynamic mode\n // Each allocation is an index (0, 1, 2, ..., count-1)\n allocations = Array.from({ length: allocationCount }, (_, i) => ({\n id: `${policy.id}_dynamic_${i}`,\n name: String(i),\n bucketRange: [0, 0] as [number, number], // Not used for per-entity\n overrides: {}, // Overrides are applied differently for dynamic\n }));\n } else {\n // Fixed allocations from policy\n allocations = policy.allocations;\n allocationCount = allocations.length;\n }\n\n if (allocationCount === 0) return null;\n\n // Get weights for this entity\n const weights = getEntityWeights(bundle, policy.id, entityId, allocationCount);\n\n // Deterministic weighted selection\n const seed = `${entityId}:${unitKeyValue}:${policy.id}`;\n const selectedIndex = weightedSelection(weights, seed);\n\n return {\n allocation: allocations[selectedIndex],\n entityId,\n };\n}\n\n/**\n * Extracts the unit key value from context using the bundle's hashing config.\n *\n * @param bundle - The config bundle\n * @param context - The evaluation context\n * @returns The unit key value as a string, or null if not found\n */\nexport function getUnitKeyValue(\n bundle: ConfigBundle,\n context: Context\n): string | null {\n const value = context[bundle.hashing.unitKey];\n\n if (value === undefined || value === null) {\n return null;\n }\n\n return String(value);\n}\n\n// =============================================================================\n// Resolve Options (for server-evaluated mode)\n// =============================================================================\n\n/**\n * Options for resolution that allow injecting pre-fetched edge results.\n * Used by server-evaluated mode where the edge worker resolves all policies\n * (including per-entity) in a single request and passes results to the core engine.\n */\nexport interface ResolveOptions {\n /**\n * Pre-fetched edge results keyed by policyId.\n * When provided, edge-mode policies use these instead of being skipped.\n */\n edgeResults?: Map<string, { allocationIndex: number; entityId: string }>;\n}\n\n/**\n * Internal resolution result with metadata.\n */\ninterface ResolutionResult<T> {\n assignments: T;\n unitKeyValue: string;\n layers: LayerResolution[];\n /** Matched policies with context logging config */\n matchedPolicies: BundlePolicy[];\n}\n\n/**\n * Internal function that performs parameter resolution with metadata tracking.\n * This is the single source of truth for resolution logic.\n *\n * @param bundle - The config bundle (can be null if unavailable)\n * @param context - The evaluation context\n * @param defaults - Default values for parameters (required, used as fallback)\n * @returns Resolution result with assignments and metadata\n */\nfunction resolveInternal<T extends Record<string, ParameterValue>>(\n bundle: ConfigBundle | null,\n context: Context,\n defaults: T,\n options?: ResolveOptions\n): ResolutionResult<T> {\n // Start with caller defaults (always safe)\n const assignments = { ...defaults } as Record<string, ParameterValue>;\n const layers: LayerResolution[] = [];\n const matchedPolicies: BundlePolicy[] = [];\n\n // If no bundle, return defaults with empty metadata\n if (!bundle) {\n return { assignments: assignments as T, unitKeyValue: \"\", layers, matchedPolicies };\n }\n\n // Project-level unit key. Layers that don't override `unitKey` use this.\n // In multi-entity projects, individual layers may set `unitKey` to a\n // different context field (e.g. `merchantId` when the project default is\n // `customerId`); those layers compute their own unit value below.\n //\n // We no longer bail out when this is missing \u2014 some layers in multi-entity\n // projects may still resolve via their own unit key. The empty string we\n // store in `unitKeyValue` is the legacy \"no project unit key\" signal that\n // downstream code (decision events) already tolerates.\n //\n // See: ng/docs/design/diversion-types.md\n const projectUnitKeyValue = getUnitKeyValue(bundle, context) ?? \"\";\n\n // Get requested parameter keys from defaults\n const requestedKeys = new Set(Object.keys(defaults));\n\n // Filter bundle parameters to only those requested\n const params = bundle.parameters.filter((p) => requestedKeys.has(p.key));\n\n // Apply bundle defaults (overrides caller defaults)\n for (const param of params) {\n if (param.key in assignments) {\n assignments[param.key] = param.default;\n }\n }\n\n // Group by layer\n const paramsByLayer = new Map<Id, BundleParameter[]>();\n for (const param of params) {\n const existing = paramsByLayer.get(param.layerId) || [];\n existing.push(param);\n paramsByLayer.set(param.layerId, existing);\n }\n\n // Process ALL layers for both parameter resolution and attribution.\n //\n // Layers with matching parameters get their overrides applied (parameter\n // resolution). Layers WITHOUT matching parameters are still processed for\n // bucket/policy/allocation matching so that decision events and track-event\n // attribution include the full set of experiments the user is assigned to.\n //\n // The `attributionOnly` flag distinguishes the two: layers resolved only for\n // attribution are marked `attributionOnly: true`, which tells trackExposure()\n // to skip them (avoiding exposure inflation for experiments the user didn't\n // actually see).\n for (const layer of bundle.layers) {\n const layerParams = paramsByLayer.get(layer.id);\n const hasParams = layerParams && layerParams.length > 0;\n\n // Per-layer unit key resolution. Layers in multi-entity projects may\n // override `unitKey` to read a different context field. When a layer's\n // unit value can't be resolved (missing context field), we still emit a\n // LayerResolution row (with `bucket = -1`) so decision events record the\n // skipped layer, but no bucket-based policy can match.\n const layerUnitKey = layer.unitKey;\n const layerUnitValue = layerUnitKey\n ? String(context[layerUnitKey] ?? \"\")\n : projectUnitKeyValue;\n\n if (!layerUnitValue) {\n layers.push({\n layerId: layer.id,\n bucket: -1,\n ...(layerUnitKey ? { unitKey: layerUnitKey, unitKeyValue: \"\" } : {}),\n ...(hasParams ? {} : { attributionOnly: true }),\n });\n continue;\n }\n\n // Compute bucket (needed for both parameter resolution and attribution)\n const bucket = computeBucket(\n layerUnitValue,\n layer.id,\n bundle.hashing.bucketCount\n );\n\n let matchedPolicy: BundlePolicy | undefined;\n let matchedAllocation: BundleAllocation | undefined;\n\n // Find matching policy\n for (const policy of layer.policies) {\n if (policy.state !== \"running\") continue;\n\n // Check bucket eligibility BEFORE conditions (performance optimization)\n // This enables non-overlapping experiments within a layer\n if (policy.eligibleBucketRange) {\n const { start, end } = policy.eligibleBucketRange;\n if (bucket < start || bucket > end) {\n continue; // User's bucket not eligible for this policy\n }\n }\n\n if (!evaluateConditions(policy.conditions, context)) continue;\n\n // Contextual model scoring: overrides bucket-based allocation\n if (policy.contextualModel) {\n const ctxAllocation = resolveContextualPolicy(policy, context, layerUnitValue);\n if (ctxAllocation) {\n matchedPolicy = policy;\n matchedAllocation = ctxAllocation;\n matchedPolicies.push(policy);\n if (hasParams) {\n for (const [key, value] of Object.entries(ctxAllocation.overrides)) {\n if (key in assignments) {\n assignments[key] = value;\n }\n }\n }\n break;\n }\n }\n\n // Check if this is a per-entity policy\n if (policy.entityConfig && policy.entityConfig.resolutionMode === \"bundle\") {\n const result = resolvePerEntityPolicy(bundle, policy, context, layerUnitValue);\n if (result) {\n matchedPolicy = policy;\n matchedAllocation = result.allocation;\n\n // Track matched policy for context filtering\n matchedPolicies.push(policy);\n\n // Apply overrides only if this layer has matching parameters\n if (hasParams) {\n // For dynamic allocations, the allocation name IS the value\n if (policy.entityConfig.dynamicAllocations) {\n // For per-entity dynamic policies, we return the selected index\n // The SDK caller should use metadata.allocationName to get the index\n // No parameter overrides to apply in this mode\n } else {\n // For fixed allocations, apply normal overrides\n for (const [key, value] of Object.entries(result.allocation.overrides)) {\n if (key in assignments) {\n assignments[key] = value;\n }\n }\n }\n }\n break; // Only one policy per layer\n }\n } else if (policy.entityConfig && policy.entityConfig.resolutionMode === \"edge\") {\n const edgeResult = options?.edgeResults?.get(policy.id);\n if (edgeResult) {\n matchedPolicy = policy;\n matchedPolicies.push(policy);\n\n if (policy.entityConfig.dynamicAllocations) {\n // Dynamic allocations: synthesize allocation from index\n matchedAllocation = {\n id: `${policy.id}_dynamic_${edgeResult.allocationIndex}`,\n name: String(edgeResult.allocationIndex),\n bucketRange: [0, 0] as [number, number],\n overrides: {},\n };\n } else if (policy.allocations[edgeResult.allocationIndex]) {\n matchedAllocation = policy.allocations[edgeResult.allocationIndex];\n if (hasParams && matchedAllocation) {\n for (const [key, value] of Object.entries(matchedAllocation.overrides)) {\n if (key in assignments) {\n assignments[key] = value;\n }\n }\n }\n }\n break;\n }\n // No pre-fetched result: skip this policy gracefully\n continue;\n } else {\n // Standard bucket-based resolution\n const allocation = findMatchingAllocation(bucket, policy.allocations);\n if (allocation) {\n matchedPolicy = policy;\n matchedAllocation = allocation;\n\n // Track matched policy for context filtering\n matchedPolicies.push(policy);\n\n // Apply overrides only if this layer has matching parameters\n if (hasParams) {\n for (const [key, value] of Object.entries(allocation.overrides)) {\n if (key in assignments) {\n assignments[key] = value;\n }\n }\n }\n break; // Only one policy per layer\n }\n }\n }\n\n layers.push({\n layerId: layer.id,\n bucket,\n policyId: matchedPolicy?.id,\n policyKey: (matchedPolicy as any)?.key,\n allocationId: matchedAllocation?.id,\n allocationName: matchedAllocation?.name,\n allocationKey: (matchedAllocation as any)?.key,\n // Record the unit key only when the layer overrides the project\n // default \u2014 keeps the metadata small for the single-entity case while\n // making exposure events auditable in multi-entity projects.\n ...(layerUnitKey ? { unitKey: layerUnitKey, unitKeyValue: layerUnitValue } : {}),\n // Mark layers without requested parameters as attribution-only.\n // These are included in decision events and track-event attribution\n // but skipped by trackExposure() to avoid exposure inflation.\n ...(hasParams ? {} : { attributionOnly: true }),\n });\n }\n\n return { assignments: assignments as T, unitKeyValue: projectUnitKeyValue, layers, matchedPolicies };\n}\n\n/**\n * Resolves parameters with required defaults as fallback.\n * This is the primary SDK function that guarantees safe defaults.\n *\n * Resolution priority (highest wins):\n * 1. Policy overrides (from bundle)\n * 2. Parameter defaults (from bundle)\n * 3. Caller defaults (always safe fallback)\n *\n * @param bundle - The config bundle (can be null if unavailable)\n * @param context - The evaluation context\n * @param defaults - Default values for parameters (required, used as fallback)\n * @returns Resolved parameter assignments (always returns safe values with inferred types)\n */\nexport function resolveParameters<T extends Record<string, ParameterValue>>(\n bundle: ConfigBundle | null,\n context: Context,\n defaults: T,\n options?: ResolveOptions\n): T {\n return resolveInternal(bundle, context, defaults, options).assignments;\n}\n\n/**\n * Makes a decision with full metadata for tracking.\n * Requires defaults for graceful degradation.\n *\n * Resolution priority (highest wins):\n * 1. Policy overrides (from bundle)\n * 2. Parameter defaults (from bundle)\n * 3. Caller defaults (always safe fallback)\n *\n * @param bundle - The config bundle (can be null if unavailable)\n * @param context - The evaluation context\n * @param defaults - Default values for parameters (required, used as fallback)\n * @returns Decision result with metadata (always returns safe values)\n */\nexport function decide<T extends Record<string, ParameterValue>>(\n bundle: ConfigBundle | null,\n context: Context,\n defaults: T,\n options?: ResolveOptions\n): DecisionResult {\n const { assignments, unitKeyValue, layers, matchedPolicies } = resolveInternal(\n bundle,\n context,\n defaults,\n options\n );\n\n // Filter context based on matched policies' contextLogging config\n const filteredContext = filterContext(context, matchedPolicies);\n\n return {\n decisionId: generateDecisionId(),\n assignments,\n metadata: {\n timestamp: new Date().toISOString(),\n unitKeyValue,\n layers,\n filteredContext,\n },\n };\n}\n", "/**\n * DecisionDeduplicator - Pure decision deduplication logic.\n *\n * Tracks which user+assignment combinations have been seen to avoid\n * sending duplicate decision events. This enables efficient decision\n * tracking without overwhelming the event pipeline.\n *\n * Key differences from ExposureDeduplicator:\n * - Pure in-memory (no I/O, no storage dependency)\n * - Deduplicates on unitKey + assignment hash (not policy/variant)\n * - Suitable for use in any JavaScript environment\n */\n\nimport type { ParameterValue } from \"../types/index.js\";\n\nconst DEFAULT_TTL_MS = 3600_000; // 1 hour\nconst DEFAULT_MAX_ENTRIES = 10_000;\nconst CLEANUP_THRESHOLD = 0.2; // Clean when 20% of entries are expired\n\nexport interface DecisionDeduplicatorOptions {\n /**\n * Time-to-live for deduplication entries in milliseconds.\n * After this time, the same decision can be tracked again.\n * Default: 1 hour (3600000 ms)\n */\n ttlMs?: number;\n /**\n * Maximum number of entries to store.\n * When exceeded, oldest entries are removed.\n * Default: 10000\n */\n maxEntries?: number;\n}\n\nexport class DecisionDeduplicator {\n private _seen = new Map<string, number>(); // key -> timestamp\n private readonly _ttlMs: number;\n private readonly _maxEntries: number;\n private _lastCleanup = Date.now();\n\n constructor(options: DecisionDeduplicatorOptions = {}) {\n this._ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;\n this._maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;\n }\n\n /**\n * Generate a stable hash for assignment values.\n * Used to create a deduplication key from assignments.\n */\n static hashAssignments(assignments: Record<string, ParameterValue>): string {\n // Sort keys for deterministic ordering\n const sortedKeys = Object.keys(assignments).sort();\n const parts: string[] = [];\n\n for (const key of sortedKeys) {\n const value = assignments[key];\n // Simple string representation that's stable\n const valueStr = typeof value === \"object\" ? JSON.stringify(value) : String(value);\n parts.push(`${key}=${valueStr}`);\n }\n\n return parts.join(\"|\");\n }\n\n /**\n * Create a deduplication key from unitKey and assignment hash.\n */\n static createKey(unitKey: string, assignmentHash: string): string {\n return `${unitKey}:${assignmentHash}`;\n }\n\n /**\n * Check if this decision is new (not seen before within TTL).\n * If new, marks it as seen.\n *\n * @param unitKey - The unit key (user identifier)\n * @param assignmentHash - Hash of the assignments (from hashAssignments)\n * @returns true if this is a new decision, false if duplicate\n */\n checkAndMark(unitKey: string, assignmentHash: string): boolean {\n const key = DecisionDeduplicator.createKey(unitKey, assignmentHash);\n const now = Date.now();\n const lastSeen = this._seen.get(key);\n\n // Check if we've seen this within TTL\n if (lastSeen !== undefined && now - lastSeen < this._ttlMs) {\n return false; // Duplicate\n }\n\n // Mark as seen\n this._seen.set(key, now);\n\n // Periodic cleanup\n this._maybeCleanup(now);\n\n return true; // New decision\n }\n\n /**\n * Check if a decision would be considered new (without marking it).\n */\n wouldBeNew(unitKey: string, assignmentHash: string): boolean {\n const key = DecisionDeduplicator.createKey(unitKey, assignmentHash);\n const now = Date.now();\n const lastSeen = this._seen.get(key);\n\n if (lastSeen === undefined) {\n return true;\n }\n\n return now - lastSeen >= this._ttlMs;\n }\n\n /**\n * Clear all seen decisions.\n */\n clear(): void {\n this._seen.clear();\n }\n\n /**\n * Get the number of entries in the deduplication cache.\n */\n get size(): number {\n return this._seen.size;\n }\n\n /**\n * Perform cleanup of expired entries.\n * Called periodically based on CLEANUP_THRESHOLD.\n */\n private _maybeCleanup(now: number): void {\n // Only cleanup periodically, not on every call\n const timeSinceCleanup = now - this._lastCleanup;\n const shouldCleanup =\n timeSinceCleanup > this._ttlMs * CLEANUP_THRESHOLD || this._seen.size > this._maxEntries;\n\n if (!shouldCleanup) {\n return;\n }\n\n this._lastCleanup = now;\n this._cleanup(now);\n }\n\n /**\n * Remove expired entries and enforce max size.\n */\n private _cleanup(now: number): void {\n const expiredKeys: string[] = [];\n\n // Find expired entries\n for (const [key, timestamp] of this._seen.entries()) {\n if (now - timestamp >= this._ttlMs) {\n expiredKeys.push(key);\n }\n }\n\n // Remove expired entries\n for (const key of expiredKeys) {\n this._seen.delete(key);\n }\n\n // If still over max, remove oldest entries\n if (this._seen.size > this._maxEntries) {\n const entries = Array.from(this._seen.entries()).sort((a, b) => a[1] - b[1]); // Sort by timestamp\n\n const toRemove = entries.slice(0, this._seen.size - this._maxEntries);\n for (const [key] of toRemove) {\n this._seen.delete(key);\n }\n }\n }\n}\n\n", "/**\n * DecisionClient\n *\n * I/O client for Traffical server-evaluated resolution and per-entity decisions.\n * Platform-agnostic (uses standard fetch API).\n */\n\nimport type {\n Id,\n Context,\n EdgeDecideRequest,\n EdgeDecideResponse,\n EdgeBatchDecideResponse,\n ServerResolveRequest,\n ServerResolveResponse,\n} from \"@traffical/core\";\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface DecisionClientConfig {\n /** Base URL for the edge worker (e.g., \"https://sdk.traffical.io\") */\n baseUrl: string;\n /** Organization ID */\n orgId: Id;\n /** Project ID */\n projectId: Id;\n /** Environment */\n env: string;\n /** API key for authentication */\n apiKey: string;\n /** Default timeout in milliseconds (default: 5000 for resolve, 100 for decide) */\n defaultTimeoutMs?: number;\n}\n\n// =============================================================================\n// DecisionClient\n// =============================================================================\n\nexport class DecisionClient {\n private readonly config: DecisionClientConfig;\n private readonly defaultTimeout: number;\n\n constructor(config: DecisionClientConfig) {\n this.config = config;\n this.defaultTimeout = config.defaultTimeoutMs ?? 5000;\n }\n\n /**\n * Full server-side resolution via POST /v1/resolve.\n * Returns all parameter assignments resolved on the edge worker.\n */\n async resolve(request: ServerResolveRequest): Promise<ServerResolveResponse | null> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.defaultTimeout);\n\n const url = `${this.config.baseUrl}/v1/resolve`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: this._headers(),\n body: JSON.stringify({\n context: request.context,\n env: request.env ?? this.config.env,\n parameters: request.parameters,\n }),\n signal: controller.signal as any,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n console.warn(\n `[Traffical] Resolve failed: ${response.status} ${response.statusText}`\n );\n return null;\n }\n\n return (await response.json()) as ServerResolveResponse;\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n console.warn(`[Traffical] Resolve timed out after ${this.defaultTimeout}ms`);\n } else {\n console.warn(`[Traffical] Resolve error:`, error);\n }\n return null;\n }\n }\n\n /**\n * Per-entity edge decision via POST /v1/decide/:policyId.\n */\n async decideEntity(\n request: EdgeDecideRequest,\n timeoutMs?: number\n ): Promise<EdgeDecideResponse | null> {\n const timeout = timeoutMs ?? Math.min(this.defaultTimeout, 100);\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const url = `${this.config.baseUrl}/v1/decide/${request.policyId}`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: this._headers(),\n body: JSON.stringify({\n entityId: request.entityId,\n unitKeyValue: request.unitKeyValue,\n allocationCount: request.allocationCount,\n context: request.context,\n }),\n signal: controller.signal as any,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n console.warn(\n `[Traffical] Edge decide failed: ${response.status} ${response.statusText}`\n );\n return null;\n }\n\n return (await response.json()) as EdgeDecideResponse;\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n console.warn(`[Traffical] Edge decide timed out after ${timeout}ms`);\n } else {\n console.warn(`[Traffical] Edge decide error:`, error);\n }\n return null;\n }\n }\n\n /**\n * Batch per-entity edge decisions via POST /v1/decide/batch.\n */\n async decideEntityBatch(\n requests: EdgeDecideRequest[],\n timeoutMs?: number\n ): Promise<(EdgeDecideResponse | null)[]> {\n if (requests.length === 0) return [];\n if (requests.length === 1) {\n const result = await this.decideEntity(requests[0], timeoutMs);\n return [result];\n }\n\n const timeout = timeoutMs ?? Math.min(this.defaultTimeout, 200);\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const url = `${this.config.baseUrl}/v1/decide/batch`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: this._headers(),\n body: JSON.stringify({ requests }),\n signal: controller.signal as any,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n console.warn(\n `[Traffical] Edge batch decide failed: ${response.status} ${response.statusText}`\n );\n return requests.map(() => null);\n }\n\n const data = (await response.json()) as EdgeBatchDecideResponse;\n return data.responses;\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n console.warn(`[Traffical] Edge batch decide timed out after ${timeout}ms`);\n } else {\n console.warn(`[Traffical] Edge batch decide error:`, error);\n }\n return requests.map(() => null);\n }\n }\n\n private _headers(): Record<string, string> {\n return {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.config.apiKey}`,\n \"X-Org-Id\": this.config.orgId,\n \"X-Project-Id\": this.config.projectId,\n \"X-Env\": this.config.env,\n };\n }\n}\n\n// =============================================================================\n// Utility Functions (moved from @traffical/core)\n// =============================================================================\n\n/**\n * Creates an edge decide request from policy and context.\n *\n * @param policyId - The policy ID\n * @param entityKeys - Array of context keys that identify the entity\n * @param context - The evaluation context\n * @param unitKeyValue - The unit key value\n * @param allocationCount - Number of allocations (for dynamic)\n * @returns The decide request, or null if entity ID cannot be built\n */\nexport function createEdgeDecideRequest(\n policyId: Id,\n entityKeys: string[],\n context: Context,\n unitKeyValue: string,\n allocationCount?: number\n): EdgeDecideRequest | null {\n const parts: string[] = [];\n for (const key of entityKeys) {\n const value = context[key];\n if (value === undefined || value === null) {\n return null;\n }\n parts.push(String(value));\n }\n const entityId = parts.join(\"_\");\n\n return {\n policyId,\n entityId,\n unitKeyValue,\n allocationCount,\n context,\n };\n}\n", "/**\n * ErrorBoundary - Ensures SDK never crashes customer's application.\n *\n * Wraps all public methods to catch errors and return safe defaults.\n * Optionally reports errors to Traffical backend for monitoring.\n */\n\nexport interface ErrorBoundaryOptions {\n /** Whether to report errors to Traffical backend */\n reportErrors?: boolean;\n /** Endpoint for error reporting */\n errorEndpoint?: string;\n /** SDK key for identification */\n sdkKey?: string;\n /** Callback when error occurs */\n onError?: (tag: string, error: Error) => void;\n}\n\nexport class ErrorBoundary {\n private _seen = new Set<string>();\n private _options: ErrorBoundaryOptions;\n private _lastError: Error | null = null;\n\n constructor(options: ErrorBoundaryOptions = {}) {\n this._options = options;\n }\n\n /**\n * Wrap a synchronous function to catch errors and return fallback.\n */\n capture<T>(tag: string, fn: () => T, fallback: T): T {\n try {\n return fn();\n } catch (error) {\n this._onError(tag, error);\n return fallback;\n }\n }\n\n /**\n * Wrap an async function to catch errors and return fallback.\n */\n async captureAsync<T>(tag: string, fn: () => Promise<T>, fallback: T): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n this._onError(tag, error);\n return fallback;\n }\n }\n\n /**\n * Execute an async operation without expecting a return value.\n * Used for fire-and-forget operations like event tracking.\n */\n async swallow(tag: string, fn: () => Promise<void>): Promise<void> {\n try {\n await fn();\n } catch (error) {\n this._onError(tag, error);\n }\n }\n\n /**\n * Get the last error that occurred (for debugging).\n */\n getLastError(): Error | null {\n const error = this._lastError;\n this._lastError = null;\n return error;\n }\n\n /**\n * Clear the seen errors set (for testing or session reset).\n */\n clearSeen(): void {\n this._seen.clear();\n }\n\n private _onError(tag: string, error: unknown): void {\n const resolvedError = this._resolveError(error);\n this._lastError = resolvedError;\n\n // Deduplicate - only handle each unique error once\n const errorKey = `${tag}:${resolvedError.name}:${resolvedError.message}`;\n if (this._seen.has(errorKey)) {\n return;\n }\n this._seen.add(errorKey);\n\n // Log to console (development)\n console.warn(`[Traffical] Error in ${tag}:`, resolvedError.message);\n\n // Call user-provided callback\n this._options.onError?.(tag, resolvedError);\n\n // Optionally report to backend\n if (this._options.reportErrors && this._options.errorEndpoint) {\n this._reportError(tag, resolvedError).catch(() => {\n // Silently fail - we don't want error reporting to cause errors\n });\n }\n }\n\n private async _reportError(tag: string, error: Error): Promise<void> {\n if (!this._options.errorEndpoint) return;\n\n try {\n await fetch(this._options.errorEndpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(this._options.sdkKey && { \"X-Traffical-Key\": this._options.sdkKey }),\n },\n body: JSON.stringify({\n tag,\n error: error.name,\n message: error.message,\n stack: error.stack,\n timestamp: new Date().toISOString(),\n sdk: \"@traffical/js-client\",\n userAgent: typeof navigator !== \"undefined\" ? navigator.userAgent : undefined,\n }),\n });\n } catch {\n // Silently fail\n }\n }\n\n private _resolveError(error: unknown): Error {\n if (error instanceof Error) {\n return error;\n }\n if (typeof error === \"string\") {\n return new Error(error);\n }\n return new Error(\"An unknown error occurred\");\n }\n}\n\n", "/**\n * EventLogger - Smart event batching with browser-specific features.\n *\n * Features:\n * - Batches events (flush every N events or M seconds)\n * - Uses navigator.sendBeacon() on page unload\n * - Persists failed events to localStorage\n * - Retries failed events on next session\n * - Visibility-aware: flushes on visibilitychange to hidden\n */\n\nimport type { TrackableEvent, OnSchemaWarnings, EventBatchResponse } from \"@traffical/core\";\nimport type { StorageProvider } from \"./storage.js\";\nimport type { LifecycleProvider, VisibilityState } from \"./lifecycle.js\";\n\nconst FAILED_EVENTS_KEY = \"failed_events\";\nconst DEFAULT_BATCH_SIZE = 10;\nconst DEFAULT_FLUSH_INTERVAL_MS = 30_000; // 30 seconds\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10_000; // 10 seconds\nconst MAX_FAILED_EVENTS = 100;\n\nexport interface EventLoggerOptions {\n /** API endpoint for events */\n endpoint: string;\n /** API key for authentication */\n apiKey: string;\n /** Storage provider for failed events */\n storage: StorageProvider;\n /** Lifecycle provider for visibility/unload events */\n lifecycleProvider?: LifecycleProvider;\n /** Max events before auto-flush (default: 10) */\n batchSize?: number;\n /** Auto-flush interval in ms (default: 30000) */\n flushIntervalMs?: number;\n /**\n * Timeout in ms for the event batch POST (default: 10000).\n * On timeout the request is aborted and treated like a failed send:\n * events are persisted to storage for retry.\n */\n requestTimeoutMs?: number;\n /** Callback on flush error */\n onError?: (error: Error) => void;\n /** Callback when schema validation warnings are received from the edge (dev-mode) */\n onSchemaWarnings?: OnSchemaWarnings;\n}\n\nexport class EventLogger {\n private _endpoint: string;\n private _apiKey: string;\n private _storage: StorageProvider;\n private _batchSize: number;\n private _flushIntervalMs: number;\n private _requestTimeoutMs: number;\n private _onError?: (error: Error) => void;\n private _onSchemaWarnings?: OnSchemaWarnings;\n\n private _lifecycleProvider?: LifecycleProvider;\n private _queue: TrackableEvent[] = [];\n private _flushTimer: ReturnType<typeof setTimeout> | null = null;\n private _isFlushing = false;\n private _visibilityCallback?: (state: VisibilityState) => void;\n\n constructor(options: EventLoggerOptions) {\n this._endpoint = options.endpoint;\n this._apiKey = options.apiKey;\n this._storage = options.storage;\n this._batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;\n this._flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n this._requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this._onError = options.onError;\n this._onSchemaWarnings = options.onSchemaWarnings;\n this._lifecycleProvider = options.lifecycleProvider;\n\n this._setupListeners();\n\n // Retry failed events from previous session\n this._retryFailedEvents();\n\n // Start flush timer\n this._startFlushTimer();\n }\n\n /**\n * Log an event (added to batch queue).\n */\n log(event: TrackableEvent): void {\n this._queue.push(event);\n\n // Auto-flush if batch is full\n if (this._queue.length >= this._batchSize) {\n this.flush();\n }\n }\n\n /**\n * Flush all queued events immediately.\n */\n async flush(): Promise<void> {\n if (this._isFlushing || this._queue.length === 0) {\n return;\n }\n\n this._isFlushing = true;\n\n // Take current queue\n const events = [...this._queue];\n this._queue = [];\n\n try {\n await this._sendEvents(events);\n } catch (error) {\n // Persist failed events for retry\n this._persistFailedEvents(events);\n this._onError?.(error instanceof Error ? error : new Error(String(error)));\n } finally {\n this._isFlushing = false;\n }\n }\n\n /**\n * Flush using fetch with keepalive (for page unload).\n * \n * We use fetch with keepalive: true instead of sendBeacon because:\n * - sendBeacon cannot send custom headers (like Authorization)\n * - keepalive ensures the request completes even as the page unloads\n * - Same reliability guarantees as sendBeacon\n * \n * Returns true if request was initiated, false otherwise.\n */\n flushBeacon(): boolean {\n if (this._queue.length === 0) {\n return true;\n }\n\n if (typeof fetch === \"undefined\") {\n // Fetch not available - try async flush\n this.flush();\n return false;\n }\n\n const events = [...this._queue];\n this._queue = [];\n\n // Use fetch with keepalive instead of sendBeacon\n // This allows us to include the Authorization header.\n // Intentionally no abort timeout here: keepalive requests are meant to\n // outlive the page, and a timer may never fire during unload anyway.\n fetch(this._endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this._apiKey}`,\n },\n body: JSON.stringify({ events }),\n keepalive: true,\n }).catch(() => {\n // Persist for retry on next session\n this._persistFailedEvents(events);\n });\n\n return true;\n }\n\n /**\n * Get the number of events in the queue.\n */\n get queueSize(): number {\n return this._queue.length;\n }\n\n /**\n * Destroy the logger (cleanup timers and listeners).\n */\n destroy(): void {\n if (this._flushTimer) {\n clearInterval(this._flushTimer);\n this._flushTimer = null;\n }\n this._removeListeners();\n }\n\n private async _sendEvents(events: TrackableEvent[]): Promise<void> {\n // Abort the request if the edge hangs so the flush settles and events\n // go down the persist-for-retry path (same as any network failure).\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this._requestTimeoutMs);\n\n let response: Response;\n try {\n response = await fetch(this._endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this._apiKey}`,\n },\n body: JSON.stringify({ events }),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n if (this._onSchemaWarnings) {\n try {\n const body: EventBatchResponse = await response.json();\n if (body.schemaWarnings && body.schemaWarnings.length > 0) {\n this._onSchemaWarnings(body.schemaWarnings);\n }\n } catch {\n // Response parsing is best-effort for dev-mode warnings\n }\n }\n }\n\n private _persistFailedEvents(events: TrackableEvent[]): void {\n const existing = this._storage.get<TrackableEvent[]>(FAILED_EVENTS_KEY) ?? [];\n\n // Limit total stored events\n const combined = [...existing, ...events].slice(-MAX_FAILED_EVENTS);\n\n this._storage.set(FAILED_EVENTS_KEY, combined);\n }\n\n private _retryFailedEvents(): void {\n const failed = this._storage.get<TrackableEvent[]>(FAILED_EVENTS_KEY);\n if (!failed || failed.length === 0) {\n return;\n }\n\n // Clear stored events\n this._storage.remove(FAILED_EVENTS_KEY);\n\n // Add to queue for retry\n this._queue.push(...failed);\n }\n\n private _startFlushTimer(): void {\n if (this._flushIntervalMs <= 0) return;\n\n this._flushTimer = setInterval(() => {\n this.flush().catch(() => {\n // Errors handled in flush\n });\n }, this._flushIntervalMs);\n }\n\n private _setupListeners(): void {\n if (!this._lifecycleProvider) return;\n\n this._visibilityCallback = (state) => {\n if (state === \"background\") {\n if (this._lifecycleProvider?.isUnloading()) {\n this.flushBeacon();\n } else {\n this.flush().catch(() => {});\n }\n } else {\n this._retryFailedEvents();\n }\n };\n this._lifecycleProvider.onVisibilityChange(this._visibilityCallback);\n }\n\n private _removeListeners(): void {\n if (this._lifecycleProvider && this._visibilityCallback) {\n this._lifecycleProvider.removeVisibilityListener(this._visibilityCallback);\n this._visibilityCallback = undefined;\n }\n }\n}\n\n", "/**\n * ExposureDeduplicator - Prevents duplicate exposure events.\n *\n * Same user seeing same variant should only count as 1 exposure.\n * Uses session-based deduplication with localStorage persistence.\n */\n\nimport type { StorageProvider } from \"./storage.js\";\n\nconst STORAGE_KEY = \"exposure_dedup\";\nconst DEFAULT_SESSION_TTL_MS = 30 * 60 * 1000; // 30 minutes\n\nexport interface ExposureDeduplicatorOptions {\n /** Storage provider for persistence */\n storage: StorageProvider;\n /** Session TTL in milliseconds (default: 30 minutes) */\n sessionTtlMs?: number;\n}\n\ninterface DeduplicationState {\n /** Set of seen exposure keys */\n seen: string[];\n /** Session start timestamp */\n sessionStart: number;\n}\n\nexport class ExposureDeduplicator {\n private _storage: StorageProvider;\n private _sessionTtlMs: number;\n private _seen: Set<string>;\n private _sessionStart: number;\n\n constructor(options: ExposureDeduplicatorOptions) {\n this._storage = options.storage;\n this._sessionTtlMs = options.sessionTtlMs ?? DEFAULT_SESSION_TTL_MS;\n this._seen = new Set();\n this._sessionStart = Date.now();\n\n // Restore from storage\n this._restore();\n }\n\n /**\n * Generate a deduplication key for an exposure.\n *\n * Key format: {unitKey}:{policyId}:{variant}\n */\n static createKey(unitKey: string, policyId: string, variant: string): string {\n return `${unitKey}:${policyId}:${variant}`;\n }\n\n /**\n * Check if an exposure should be tracked (not a duplicate).\n * Returns true if this is a new exposure, false if duplicate.\n */\n shouldTrack(key: string): boolean {\n // Check if session has expired\n if (this._isSessionExpired()) {\n this._resetSession();\n }\n\n if (this._seen.has(key)) {\n return false;\n }\n\n // Mark as seen\n this._seen.add(key);\n this._persist();\n\n return true;\n }\n\n /**\n * Check and mark in one operation.\n * Returns true if this was a new exposure (and is now marked as seen).\n */\n checkAndMark(unitKey: string, policyId: string, variant: string): boolean {\n const key = ExposureDeduplicator.createKey(unitKey, policyId, variant);\n return this.shouldTrack(key);\n }\n\n /**\n * Clear all seen exposures (useful for testing or logout).\n */\n clear(): void {\n this._seen.clear();\n this._storage.remove(STORAGE_KEY);\n }\n\n /**\n * Get the number of unique exposures in the current session.\n */\n get size(): number {\n return this._seen.size;\n }\n\n private _isSessionExpired(): boolean {\n return Date.now() - this._sessionStart > this._sessionTtlMs;\n }\n\n private _resetSession(): void {\n this._seen.clear();\n this._sessionStart = Date.now();\n this._storage.remove(STORAGE_KEY);\n }\n\n private _persist(): void {\n const state: DeduplicationState = {\n seen: Array.from(this._seen),\n sessionStart: this._sessionStart,\n };\n this._storage.set(STORAGE_KEY, state, this._sessionTtlMs);\n }\n\n private _restore(): void {\n const state = this._storage.get<DeduplicationState>(STORAGE_KEY);\n if (!state) return;\n\n // Check if stored session is still valid\n const sessionAge = Date.now() - state.sessionStart;\n if (sessionAge > this._sessionTtlMs) {\n this._storage.remove(STORAGE_KEY);\n return;\n }\n\n // Restore state\n this._seen = new Set(state.seen);\n this._sessionStart = state.sessionStart;\n }\n}\n\n", "/**\n * StableIdProvider - Anonymous user identification for experimentation.\n *\n * Generates a stable ID on first visit and persists it across sessions.\n * Uses localStorage as primary storage with cookie fallback.\n */\n\nimport type { StorageProvider } from \"./storage.js\";\n\nconst STORAGE_KEY = \"stable_id\";\nconst COOKIE_NAME = \"traffical_sid\";\nconst COOKIE_MAX_AGE_DAYS = 365;\n\nexport interface StableIdProviderOptions {\n /** Storage provider (localStorage) */\n storage: StorageProvider;\n /** Whether to use cookie fallback (default: true) */\n useCookieFallback?: boolean;\n /** Custom cookie name (default: traffical_sid) */\n cookieName?: string;\n}\n\nexport class StableIdProvider {\n private _storage: StorageProvider;\n private _useCookieFallback: boolean;\n private _cookieName: string;\n private _cachedId: string | null = null;\n\n constructor(options: StableIdProviderOptions) {\n this._storage = options.storage;\n this._useCookieFallback = options.useCookieFallback ?? true;\n this._cookieName = options.cookieName ?? COOKIE_NAME;\n }\n\n /**\n * Get the stable ID, creating one if it doesn't exist.\n */\n getId(): string {\n // Check cache first\n if (this._cachedId) {\n return this._cachedId;\n }\n\n // Try localStorage\n let id = this._storage.get<string>(STORAGE_KEY);\n if (id) {\n this._cachedId = id;\n return id;\n }\n\n // Try cookie fallback\n if (this._useCookieFallback) {\n id = this._getCookie();\n if (id) {\n // Sync back to localStorage\n this._storage.set(STORAGE_KEY, id);\n this._cachedId = id;\n return id;\n }\n }\n\n // Generate new ID\n id = this._generateId();\n this._persist(id);\n this._cachedId = id;\n\n return id;\n }\n\n /**\n * Set a custom stable ID (e.g., when user logs in).\n */\n setId(id: string): void {\n this._persist(id);\n this._cachedId = id;\n }\n\n /**\n * Clear the stable ID (e.g., on logout).\n */\n clear(): void {\n this._storage.remove(STORAGE_KEY);\n if (this._useCookieFallback) {\n this._deleteCookie();\n }\n this._cachedId = null;\n }\n\n /**\n * Check if a stable ID exists.\n */\n hasId(): boolean {\n return this._storage.get<string>(STORAGE_KEY) !== null || this._getCookie() !== null;\n }\n\n private _persist(id: string): void {\n // Save to localStorage (no TTL - permanent)\n this._storage.set(STORAGE_KEY, id);\n\n // Save to cookie as fallback\n if (this._useCookieFallback) {\n this._setCookie(id);\n }\n }\n\n private _generateId(): string {\n // Use crypto.randomUUID if available (modern browsers)\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n // Fallback to manual UUID v4 generation\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n\n private _getCookie(): string | null {\n if (typeof document === \"undefined\") return null;\n\n try {\n const cookies = document.cookie.split(\";\");\n for (const cookie of cookies) {\n const [name, value] = cookie.trim().split(\"=\");\n if (name === this._cookieName && value) {\n return decodeURIComponent(value);\n }\n }\n } catch {\n // Cookie access failed (e.g., cross-origin iframe)\n }\n\n return null;\n }\n\n private _setCookie(value: string): void {\n if (typeof document === \"undefined\") return;\n\n try {\n const maxAge = COOKIE_MAX_AGE_DAYS * 24 * 60 * 60;\n document.cookie = `${this._cookieName}=${encodeURIComponent(value)}; max-age=${maxAge}; path=/; SameSite=Lax`;\n } catch {\n // Cookie access failed\n }\n }\n\n private _deleteCookie(): void {\n if (typeof document === \"undefined\") return;\n\n try {\n document.cookie = `${this._cookieName}=; max-age=0; path=/`;\n } catch {\n // Cookie access failed\n }\n }\n}\n\n", "/**\n * Storage abstraction for browser environments.\n *\n * Provides a safe wrapper around localStorage with:\n * - Automatic JSON serialization/deserialization\n * - Graceful fallback when localStorage is unavailable\n * - TTL support for expiring entries\n */\n\nexport interface StorageProvider {\n get<T>(key: string): T | null;\n set<T>(key: string, value: T, ttlMs?: number): void;\n remove(key: string): void;\n clear(): void;\n}\n\ninterface StoredValue<T> {\n value: T;\n expiresAt?: number;\n}\n\nconst STORAGE_PREFIX = \"traffical:\";\n\n/**\n * localStorage-based storage provider.\n */\nexport class LocalStorageProvider implements StorageProvider {\n private _available: boolean;\n\n constructor() {\n this._available = this._checkAvailability();\n }\n\n get<T>(key: string): T | null {\n if (!this._available) return null;\n\n try {\n const raw = localStorage.getItem(STORAGE_PREFIX + key);\n if (!raw) return null;\n\n const stored = JSON.parse(raw) as StoredValue<T>;\n\n // Check TTL\n if (stored.expiresAt && Date.now() > stored.expiresAt) {\n this.remove(key);\n return null;\n }\n\n return stored.value;\n } catch {\n return null;\n }\n }\n\n set<T>(key: string, value: T, ttlMs?: number): void {\n if (!this._available) return;\n\n try {\n const stored: StoredValue<T> = {\n value,\n ...(ttlMs && { expiresAt: Date.now() + ttlMs }),\n };\n localStorage.setItem(STORAGE_PREFIX + key, JSON.stringify(stored));\n } catch {\n // Storage full or unavailable - silently fail\n }\n }\n\n remove(key: string): void {\n if (!this._available) return;\n\n try {\n localStorage.removeItem(STORAGE_PREFIX + key);\n } catch {\n // Silently fail\n }\n }\n\n clear(): void {\n if (!this._available) return;\n\n try {\n // Only clear traffical-prefixed keys\n const keysToRemove: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(STORAGE_PREFIX)) {\n keysToRemove.push(key);\n }\n }\n keysToRemove.forEach((key) => localStorage.removeItem(key));\n } catch {\n // Silently fail\n }\n }\n\n private _checkAvailability(): boolean {\n try {\n const testKey = STORAGE_PREFIX + \"__test__\";\n localStorage.setItem(testKey, \"test\");\n localStorage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n }\n}\n\n/**\n * In-memory storage fallback when localStorage is unavailable.\n */\nexport class MemoryStorageProvider implements StorageProvider {\n private _store = new Map<string, StoredValue<unknown>>();\n\n get<T>(key: string): T | null {\n const stored = this._store.get(key) as StoredValue<T> | undefined;\n if (!stored) return null;\n\n // Check TTL\n if (stored.expiresAt && Date.now() > stored.expiresAt) {\n this.remove(key);\n return null;\n }\n\n return stored.value;\n }\n\n set<T>(key: string, value: T, ttlMs?: number): void {\n this._store.set(key, {\n value,\n ...(ttlMs && { expiresAt: Date.now() + ttlMs }),\n });\n }\n\n remove(key: string): void {\n this._store.delete(key);\n }\n\n clear(): void {\n this._store.clear();\n }\n}\n\n/**\n * Creates the appropriate storage provider for the current environment.\n */\nexport function createStorageProvider(): StorageProvider {\n // Try localStorage first\n const localProvider = new LocalStorageProvider();\n if (localProvider.get(\"__check__\") !== null || localStorageAvailable()) {\n return localProvider;\n }\n // Fall back to in-memory\n return new MemoryStorageProvider();\n}\n\nfunction localStorageAvailable(): boolean {\n try {\n const testKey = \"__traffical_storage_test__\";\n localStorage.setItem(testKey, \"test\");\n localStorage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n}\n\n", "// Auto-generated from package.json \u2014 do not edit manually.\nexport const SDK_VERSION = \"0.15.0\";\n", "/**\n * DecisionTrackingPlugin - Automatically tracks decision events.\n *\n * This plugin hooks into the SDK's decision lifecycle and sends a DecisionEvent\n * to the control plane whenever decide() is called. This enables:\n * - Intent-to-treat analysis: tracking all assignments, not just exposures\n * - Debugging: understanding why specific values were computed\n * - Audit trail: tracking all decisions made by the SDK\n *\n * Decision events are deduplicated: the same user seeing the same assignment\n * will only trigger one event within the deduplication TTL.\n */\n\nimport type { TrafficalPlugin } from \"./types.js\";\nimport type { DecisionResult, DecisionEvent, ParameterValue } from \"@traffical/core\";\nimport { DecisionDeduplicator } from \"@traffical/core\";\nimport { SDK_VERSION } from \"../version.js\";\n\nconst SDK_NAME = \"js-client\";\n\n/**\n * Options for the DecisionTrackingPlugin.\n */\nexport interface DecisionTrackingPluginOptions {\n /**\n * Disable decision tracking entirely.\n * Default: false (tracking enabled)\n */\n disabled?: boolean;\n\n /**\n * Time-to-live for deduplication in milliseconds.\n * Same user+assignment combination won't be tracked again within this window.\n * Default: 1 hour (3600000 ms)\n */\n deduplicationTtlMs?: number;\n}\n\n/**\n * Dependencies injected by the SDK client.\n */\nexport interface DecisionTrackingPluginDeps {\n /** Organization ID */\n orgId: string;\n /** Project ID */\n projectId: string;\n /** Environment */\n env: string;\n /**\n * Function to log a decision event.\n * This is typically the EventLogger.log() method.\n */\n log: (event: DecisionEvent) => void;\n}\n\n/**\n * Creates a DecisionTrackingPlugin instance.\n *\n * @param options - Plugin configuration options\n * @param deps - Dependencies injected by the SDK client\n * @returns A TrafficalPlugin that tracks decision events\n *\n * @example\n * ```typescript\n * const plugin = createDecisionTrackingPlugin(\n * { disabled: false },\n * {\n * orgId: \"org_123\",\n * projectId: \"proj_456\",\n * env: \"production\",\n * log: (event) => eventLogger.log(event),\n * }\n * );\n * ```\n */\nexport function createDecisionTrackingPlugin(\n options: DecisionTrackingPluginOptions,\n deps: DecisionTrackingPluginDeps\n): TrafficalPlugin {\n const dedup = new DecisionDeduplicator({\n ttlMs: options.deduplicationTtlMs,\n });\n\n return {\n name: \"decision-tracking\",\n\n onDecision(decision: DecisionResult): void {\n // Skip if disabled\n if (options.disabled) {\n return;\n }\n\n // Skip if no unit key (can't attribute)\n const unitKey = decision.metadata.unitKeyValue;\n if (!unitKey) {\n return;\n }\n\n // Hash assignments for deduplication\n const hash = DecisionDeduplicator.hashAssignments(\n decision.assignments as Record<string, ParameterValue>\n );\n\n // Check deduplication\n if (!dedup.checkAndMark(unitKey, hash)) {\n return; // Duplicate, skip\n }\n\n // Build the decision event\n const event: DecisionEvent = {\n type: \"decision\",\n id: decision.decisionId,\n orgId: deps.orgId,\n projectId: deps.projectId,\n env: deps.env,\n unitKey,\n timestamp: decision.metadata.timestamp,\n assignments: decision.assignments,\n layers: decision.metadata.layers,\n // Include filtered context if available (for contextual bandit training)\n context: decision.metadata.filteredContext,\n sdkName: SDK_NAME,\n sdkVersion: SDK_VERSION,\n };\n\n // Log the event\n deps.log(event);\n },\n\n onDestroy(): void {\n // Clear the deduplication cache\n dedup.clear();\n },\n };\n}\n\n", "import type { TrafficalPlugin, PluginClientAPI } from \"./types.js\";\nimport type { DecisionResult, Context } from \"@traffical/core\";\n\nconst RDR_COOKIE = \"traffical_rdr\";\nconst RDR_MAX_AGE = 24 * 60 * 60; // 24 hours in seconds\n\nexport interface RedirectPluginOptions {\n /** Parameter key to check for a redirect URL. Default: \"redirect.url\" */\n parameterKey?: string;\n /** How to compare the resolved URL with the current location.\n * \"pathname\" compares against window.location.pathname (default).\n * \"href\" compares against the full window.location.href. */\n compareMode?: \"pathname\" | \"href\";\n /** Cookie name for redirect attribution. Default: \"traffical_rdr\" */\n cookieName?: string;\n}\n\nfunction setCookie(name: string, value: string, maxAge: number): void {\n if (typeof document === \"undefined\") return;\n try {\n document.cookie = `${name}=${encodeURIComponent(value)}; max-age=${maxAge}; path=/; SameSite=Lax`;\n } catch {\n // cookie access may fail\n }\n}\n\nexport function createRedirectPlugin(\n options: RedirectPluginOptions = {}\n): TrafficalPlugin {\n const parameterKey = options.parameterKey ?? \"redirect.url\";\n const compareMode = options.compareMode ?? \"pathname\";\n const cookieName = options.cookieName ?? RDR_COOKIE;\n\n return {\n name: \"redirect\",\n\n onInitialize(client: PluginClientAPI): void {\n if (typeof window === \"undefined\") return;\n\n client.decide({\n context: {},\n defaults: { [parameterKey]: \"\" },\n });\n },\n\n onBeforeDecision(context: Context): Context {\n if (typeof window === \"undefined\") return context;\n return {\n \"url.pathname\": window.location.pathname,\n ...context,\n };\n },\n\n onDecision(decision: DecisionResult): void {\n const url = decision.assignments[parameterKey];\n if (typeof url !== \"string\" || !url) return;\n\n const current =\n compareMode === \"href\"\n ? window.location.href\n : window.location.pathname;\n\n if (url === current) return;\n\n const layer = decision.metadata.layers.find(\n (l) => l.policyId && l.allocationName\n );\n if (layer) {\n setCookie(\n cookieName,\n JSON.stringify({\n l: layer.layerId,\n p: layer.policyId,\n a: layer.allocationName,\n ts: Date.now(),\n }),\n RDR_MAX_AGE\n );\n }\n\n window.location.replace(url);\n },\n };\n}\n", "import type { TrafficalPlugin } from \"./types.js\";\nimport type { ExposureEvent, TrackEvent, TrackAttribution } from \"@traffical/core\";\n\nconst COOKIE_NAME = \"traffical_rdr\";\nconst DEFAULT_EXPIRY_MS = 24 * 60 * 60 * 1000; // 24 hours\n\nexport interface RedirectAttributionPluginOptions {\n /** Cookie name to read attribution from. Default: \"traffical_rdr\" */\n cookieName?: string;\n /** How long the attribution cookie is valid in ms. Default: 24 hours */\n expiryMs?: number;\n}\n\ninterface StoredAttribution {\n l: string; // layerId\n p: string; // policyId\n a: string; // allocationName\n ts: number; // timestamp\n}\n\nfunction readCookie(name: string): string | null {\n if (typeof document === \"undefined\") return null;\n try {\n for (const part of document.cookie.split(\";\")) {\n const [k, v] = part.trim().split(\"=\");\n if (k === name && v) return decodeURIComponent(v);\n }\n } catch {\n // cookie access failed\n }\n return null;\n}\n\nfunction parseAttribution(\n cookieName: string,\n expiryMs: number\n): TrackAttribution | null {\n const raw = readCookie(cookieName);\n if (!raw) return null;\n\n try {\n const data: StoredAttribution = JSON.parse(raw);\n if (Date.now() - data.ts > expiryMs) return null;\n return {\n layerId: data.l,\n policyId: data.p,\n allocationName: data.a,\n };\n } catch {\n return null;\n }\n}\n\nexport function createRedirectAttributionPlugin(\n options: RedirectAttributionPluginOptions = {}\n): TrafficalPlugin {\n const cookieName = options.cookieName ?? COOKIE_NAME;\n const expiryMs = options.expiryMs ?? DEFAULT_EXPIRY_MS;\n\n function inject(event: { attribution?: TrackAttribution[] }): void {\n const attr = parseAttribution(cookieName, expiryMs);\n if (!attr) return;\n event.attribution = event.attribution ?? [];\n const already = event.attribution.some(\n (a) => a.layerId === attr.layerId && a.policyId === attr.policyId\n );\n if (!already) {\n event.attribution.push(attr);\n }\n }\n\n return {\n name: \"redirect-attribution\",\n\n onTrack(event: TrackEvent): boolean | void {\n inject(event);\n return true;\n },\n\n onExposure(event: ExposureEvent): boolean | void {\n inject(event as unknown as { attribution?: TrackAttribution[] });\n return true;\n },\n };\n}\n", "/**\n * Debug Plugin for Traffical JS Client SDK.\n *\n * Exposes SDK state via `window.__TRAFFICAL_DEBUG__` for consumption by\n * Traffical DevTools (or any external inspector). Supports multiple\n * simultaneous TrafficalClient instances.\n *\n * @example\n * ```typescript\n * import { createTrafficalClient, createDebugPlugin } from '@traffical/js-client';\n *\n * const client = await createTrafficalClient({\n * orgId: 'org_123',\n * projectId: 'proj_456',\n * env: 'production',\n * apiKey: 'pk_...',\n * plugins: [createDebugPlugin()],\n * });\n * ```\n *\n * @example IIFE / script tag\n * ```html\n * <script>\n * Traffical.init({\n * ...config,\n * plugins: [Traffical.createDebugPlugin({ instanceId: 'my-app' })],\n * });\n * </script>\n * ```\n */\n\nimport type {\n ConfigBundle,\n DecisionResult,\n ExposureEvent,\n TrackEvent,\n ParameterValue,\n LayerResolution,\n} from \"@traffical/core\";\nimport type { TrafficalPlugin, PluginClientAPI } from \"./types.js\";\nimport { SDK_VERSION } from \"../version.js\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport interface DebugPluginOptions {\n /** Unique identifier for this instance. Auto-generated if omitted. */\n instanceId?: string;\n /** Maximum events to retain in the ring buffer (default: 500). */\n maxEvents?: number;\n}\n\nexport interface DebugEvent {\n id: string;\n type: \"decision\" | \"exposure\" | \"track\";\n timestamp: number;\n data: unknown;\n}\n\nexport interface DebugState {\n ready: boolean;\n stableId: string | null;\n /** The unit key actually used for hashing (from last decision metadata). */\n effectiveUnitKey: string | null;\n configVersion: string | null;\n assignments: Record<string, unknown>;\n layers: LayerResolution[];\n lastDecisionId: string | null;\n /** Parameter overrides currently applied by the debug plugin. */\n overrides: Record<string, unknown>;\n}\n\nexport interface TrafficalDebugInstance {\n readonly id: string;\n readonly meta: {\n orgId: string;\n projectId: string;\n env: string;\n sdkVersion: string;\n };\n getState(): DebugState;\n subscribe(cb: (state: DebugState) => void): () => void;\n getEvents(limit?: number): DebugEvent[];\n onEvent(cb: (event: DebugEvent) => void): () => void;\n getConfigBundle(): ConfigBundle | null;\n setUnitKey(key: string): void;\n setOverride(key: string, value: unknown): void;\n clearOverride(key: string): void;\n clearAllOverrides(): void;\n getOverrides(): Record<string, unknown>;\n reDecide(): void;\n refresh(): Promise<void>;\n}\n\nexport type RegistryEventType = \"register\" | \"unregister\";\n\nexport interface RegistryEvent {\n type: RegistryEventType;\n instanceId: string;\n}\n\nexport interface TrafficalDebugRegistry {\n readonly version: 1;\n readonly instances: Record<string, TrafficalDebugInstance>;\n subscribe(cb: (event: RegistryEvent) => void): () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Global window augmentation\n// ---------------------------------------------------------------------------\n\ndeclare global {\n interface Window {\n __TRAFFICAL_DEBUG__?: TrafficalDebugRegistry;\n __TRAFFICAL_INSTANCES__?: unknown[];\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registry (singleton per window)\n// ---------------------------------------------------------------------------\n\nlet _registryListeners: Array<(event: RegistryEvent) => void> = [];\nlet _registryInstances: Record<string, TrafficalDebugInstance> = {};\n\nfunction getOrCreateRegistry(): TrafficalDebugRegistry {\n if (typeof window === \"undefined\") {\n return { version: 1, instances: _registryInstances, subscribe: () => () => {} };\n }\n\n if (!window.__TRAFFICAL_DEBUG__) {\n const registry: TrafficalDebugRegistry = {\n version: 1,\n instances: _registryInstances,\n subscribe(cb: (event: RegistryEvent) => void): () => void {\n _registryListeners.push(cb);\n return () => {\n _registryListeners = _registryListeners.filter((l) => l !== cb);\n };\n },\n };\n window.__TRAFFICAL_DEBUG__ = registry;\n }\n\n return window.__TRAFFICAL_DEBUG__!;\n}\n\nfunction emitRegistryEvent(event: RegistryEvent): void {\n for (const listener of _registryListeners) {\n try {\n listener(event);\n } catch {\n // Ignore listener errors\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nlet _idCounter = 0;\nfunction generateId(): string {\n return `traffical_${Date.now().toString(36)}_${(++_idCounter).toString(36)}`;\n}\n\nfunction generateEventId(): string {\n return `evt_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\nconst PLUGIN_NAME = \"traffical-debug\";\n\nexport function createDebugPlugin(\n options: DebugPluginOptions = {},\n): TrafficalPlugin {\n const instanceId = options.instanceId ?? generateId();\n const maxEvents = options.maxEvents ?? 500;\n\n // Internal state\n let _client: PluginClientAPI | null = null;\n let _bundle: ConfigBundle | null = null;\n let _assignments: Record<string, unknown> = {};\n let _layers: LayerResolution[] = [];\n let _lastDecisionId: string | null = null;\n let _effectiveUnitKey: string | null = null;\n const _events: DebugEvent[] = [];\n let _stateListeners: Array<(state: DebugState) => void> = [];\n let _eventListeners: Array<(event: DebugEvent) => void> = [];\n\n function buildState(): DebugState {\n return {\n ready: _client?.isInitialized === true,\n stableId: _client?.getStableId?.() ?? null,\n effectiveUnitKey: _effectiveUnitKey,\n configVersion: _client?.getConfigVersion?.() ?? null,\n assignments: { ..._assignments },\n layers: [..._layers],\n lastDecisionId: _lastDecisionId,\n overrides: _client?.getOverrides?.() ?? {},\n };\n }\n\n function notifyStateListeners(): void {\n const state = buildState();\n for (const cb of _stateListeners) {\n try {\n cb(state);\n } catch {\n // Ignore\n }\n }\n }\n\n function pushEvent(type: DebugEvent[\"type\"], data: unknown): void {\n const event: DebugEvent = {\n id: generateEventId(),\n type,\n timestamp: Date.now(),\n data,\n };\n _events.push(event);\n if (_events.length > maxEvents) {\n _events.splice(0, _events.length - maxEvents);\n }\n for (const cb of _eventListeners) {\n try {\n cb(event);\n } catch {\n // Ignore\n }\n }\n }\n\n function triggerReDecide(): void {\n if (_client) {\n try {\n _client.decide({ context: {}, defaults: {} });\n } catch {\n // Best-effort\n }\n }\n }\n\n // Build the instance that gets registered in the global registry\n const debugInstance: TrafficalDebugInstance = {\n id: instanceId,\n meta: {\n orgId: \"\",\n projectId: \"\",\n env: \"\",\n sdkVersion: SDK_VERSION,\n },\n\n getState: buildState,\n\n subscribe(cb: (state: DebugState) => void): () => void {\n _stateListeners.push(cb);\n return () => {\n _stateListeners = _stateListeners.filter((l) => l !== cb);\n };\n },\n\n getEvents(limit?: number): DebugEvent[] {\n if (limit !== undefined) {\n return _events.slice(-limit);\n }\n return [..._events];\n },\n\n onEvent(cb: (event: DebugEvent) => void): () => void {\n _eventListeners.push(cb);\n return () => {\n _eventListeners = _eventListeners.filter((l) => l !== cb);\n };\n },\n\n getConfigBundle(): ConfigBundle | null {\n return _bundle;\n },\n\n setUnitKey(key: string): void {\n if (_client?.identify) {\n _client.identify(key);\n } else if (_client?.setStableId) {\n _client.setStableId(key);\n }\n notifyStateListeners();\n },\n\n setOverride(key: string, value: unknown): void {\n if (_client?.applyOverrides) {\n _client.applyOverrides({ [key]: value as ParameterValue });\n }\n notifyStateListeners();\n triggerReDecide();\n },\n\n clearOverride(key: string): void {\n if (_client?.getOverrides && _client?.applyOverrides) {\n const current = _client.getOverrides();\n delete current[key];\n _client.clearOverrides?.();\n _client.applyOverrides(current);\n }\n notifyStateListeners();\n triggerReDecide();\n },\n\n clearAllOverrides(): void {\n _client?.clearOverrides?.();\n notifyStateListeners();\n triggerReDecide();\n },\n\n getOverrides(): Record<string, unknown> {\n return _client?.getOverrides?.() ?? {};\n },\n\n reDecide(): void {\n triggerReDecide();\n },\n\n async refresh(): Promise<void> {\n if (_client?.refreshConfig) {\n await _client.refreshConfig();\n }\n },\n };\n\n // The actual plugin\n const plugin: TrafficalPlugin = {\n name: PLUGIN_NAME,\n\n onInitialize(client: PluginClientAPI): void {\n _client = client;\n\n // Extract meta from the config bundle if available\n if (_bundle) {\n (debugInstance.meta as { orgId: string }).orgId = _bundle.orgId;\n (debugInstance.meta as { projectId: string }).projectId = _bundle.projectId;\n (debugInstance.meta as { env: string }).env = _bundle.env;\n }\n\n // Register in the global registry\n const registry = getOrCreateRegistry();\n (registry.instances as Record<string, TrafficalDebugInstance>)[instanceId] = debugInstance;\n emitRegistryEvent({ type: \"register\", instanceId });\n notifyStateListeners();\n },\n\n onConfigUpdate(bundle: ConfigBundle): void {\n _bundle = bundle;\n\n // Update meta from bundle\n (debugInstance.meta as { orgId: string }).orgId = bundle.orgId;\n (debugInstance.meta as { projectId: string }).projectId = bundle.projectId;\n (debugInstance.meta as { env: string }).env = bundle.env;\n\n notifyStateListeners();\n },\n\n onDecision(decision: DecisionResult): void {\n _assignments = { ...decision.assignments };\n _layers = decision.metadata?.layers ? [...decision.metadata.layers] : [];\n _lastDecisionId = decision.decisionId;\n if (decision.metadata?.unitKeyValue) {\n _effectiveUnitKey = decision.metadata.unitKeyValue;\n }\n pushEvent(\"decision\", decision);\n notifyStateListeners();\n },\n\n onResolve(params: Record<string, ParameterValue>): void {\n _assignments = { ...params };\n notifyStateListeners();\n },\n\n onExposure(event: ExposureEvent): boolean | void {\n pushEvent(\"exposure\", event);\n return true;\n },\n\n onTrack(event: TrackEvent): boolean | void {\n pushEvent(\"track\", event);\n return true;\n },\n\n onDestroy(): void {\n // Unregister from registry\n const registry =\n typeof window !== \"undefined\" ? window.__TRAFFICAL_DEBUG__ : null;\n if (registry) {\n delete (registry.instances as Record<string, TrafficalDebugInstance>)[\n instanceId\n ];\n emitRegistryEvent({ type: \"unregister\", instanceId });\n }\n _stateListeners = [];\n _eventListeners = [];\n _client = null;\n },\n };\n\n return plugin;\n}\n", "/**\n * PluginManager - Manages plugin lifecycle and hook execution.\n *\n * Provides a minimal hook-based system for extending SDK functionality.\n */\n\nimport type {\n ConfigBundle,\n DecisionResult,\n ExposureEvent,\n TrackEvent,\n Context,\n ParameterValue,\n} from \"@traffical/core\";\nimport type { TrafficalPlugin, PluginOptions, PluginClientAPI } from \"./types.js\";\n\ninterface RegisteredPlugin {\n plugin: TrafficalPlugin;\n priority: number;\n}\n\nexport class PluginManager {\n private _plugins: RegisteredPlugin[] = [];\n\n /**\n * Register a plugin. Returns true if the plugin was added,\n * false if a plugin with the same name is already registered.\n */\n register(options: PluginOptions | TrafficalPlugin): boolean {\n const plugin = \"plugin\" in options ? options.plugin : options;\n const priority = \"priority\" in options ? (options.priority ?? 0) : 0;\n\n if (this._plugins.some((p) => p.plugin.name === plugin.name)) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" already registered, skipping.`);\n return false;\n }\n\n this._plugins.push({ plugin, priority });\n this._plugins.sort((a, b) => b.priority - a.priority);\n return true;\n }\n\n /**\n * Unregister a plugin by name.\n */\n unregister(name: string): boolean {\n const index = this._plugins.findIndex((p) => p.plugin.name === name);\n if (index === -1) return false;\n\n this._plugins.splice(index, 1);\n return true;\n }\n\n /**\n * Get a registered plugin by name.\n */\n get(name: string): TrafficalPlugin | undefined {\n return this._plugins.find((p) => p.plugin.name === name)?.plugin;\n }\n\n /**\n * Get all registered plugins.\n */\n getAll(): TrafficalPlugin[] {\n return this._plugins.map((p) => p.plugin);\n }\n\n /**\n * Run onInitialize hooks, passing the client API reference.\n */\n async runInitialize(client: PluginClientAPI): Promise<void> {\n for (const { plugin } of this._plugins) {\n if (plugin.onInitialize) {\n try {\n await plugin.onInitialize(client);\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onInitialize error:`, error);\n }\n }\n }\n }\n\n /**\n * Run onConfigUpdate hooks.\n * Called when config bundle is fetched or refreshed.\n */\n runConfigUpdate(bundle: ConfigBundle): void {\n for (const { plugin } of this._plugins) {\n if (plugin.onConfigUpdate) {\n try {\n plugin.onConfigUpdate(bundle);\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onConfigUpdate error:`, error);\n }\n }\n }\n }\n\n /**\n * Run onBeforeDecision hooks.\n * Returns potentially modified context.\n */\n runBeforeDecision(context: Context): Context {\n let result = context;\n\n for (const { plugin } of this._plugins) {\n if (plugin.onBeforeDecision) {\n try {\n const modified = plugin.onBeforeDecision(result);\n if (modified) {\n result = modified;\n }\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onBeforeDecision error:`, error);\n }\n }\n }\n\n return result;\n }\n\n /**\n * Run onDecision hooks.\n */\n runDecision(decision: DecisionResult): void {\n for (const { plugin } of this._plugins) {\n if (plugin.onDecision) {\n try {\n plugin.onDecision(decision);\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onDecision error:`, error);\n }\n }\n }\n }\n\n /**\n * Run onResolve hooks.\n * Called after getParams() resolves parameters.\n */\n runResolve(params: Record<string, ParameterValue>): void {\n for (const { plugin } of this._plugins) {\n if (plugin.onResolve) {\n try {\n plugin.onResolve(params);\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onResolve error:`, error);\n }\n }\n }\n }\n\n /**\n * Run onExposure hooks.\n * Returns false if any plugin cancels the exposure.\n */\n runExposure(event: ExposureEvent): boolean {\n for (const { plugin } of this._plugins) {\n if (plugin.onExposure) {\n try {\n const result = plugin.onExposure(event);\n if (result === false) {\n return false;\n }\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onExposure error:`, error);\n }\n }\n }\n\n return true;\n }\n\n /**\n * Run onTrack hooks.\n * Returns false if any plugin cancels the track event.\n */\n runTrack(event: TrackEvent): boolean {\n for (const { plugin } of this._plugins) {\n if (plugin.onTrack) {\n try {\n const result = plugin.onTrack(event);\n if (result === false) {\n return false;\n }\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onTrack error:`, error);\n }\n }\n }\n\n return true;\n }\n\n /**\n * Run onDestroy hooks.\n */\n runDestroy(): void {\n for (const { plugin } of this._plugins) {\n if (plugin.onDestroy) {\n try {\n plugin.onDestroy();\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" onDestroy error:`, error);\n }\n }\n }\n }\n\n /**\n * Clear all plugins.\n */\n clear(): void {\n this._plugins = [];\n }\n}\n\n// Re-export types\nexport type { TrafficalPlugin, PluginOptions, PluginClientAPI } from \"./types.js\";\n\n// Re-export plugins\nexport {\n createDecisionTrackingPlugin,\n type DecisionTrackingPluginOptions,\n type DecisionTrackingPluginDeps,\n} from \"./decision-tracking.js\";\n\nexport {\n createRedirectPlugin,\n type RedirectPluginOptions,\n} from \"./redirect.js\";\n\nexport {\n createRedirectAttributionPlugin,\n type RedirectAttributionPluginOptions,\n} from \"./redirect-attribution.js\";\n\nexport {\n createWarehouseNativeLoggerPlugin,\n createWarehouseNativeLogger,\n type WarehouseNativeLoggerOptions,\n type JitsuDestination,\n type AnalyticsLike,\n} from \"./warehouse-native-logger.js\";\n\nexport {\n createDebugPlugin,\n type DebugPluginOptions,\n type TrafficalDebugRegistry,\n type TrafficalDebugInstance,\n type DebugState,\n type DebugEvent,\n type RegistryEvent,\n} from \"./debug.js\";\n\n", "export type VisibilityState = \"foreground\" | \"background\";\nexport type VisibilityCallback = (state: VisibilityState) => void;\n\nexport interface LifecycleProvider {\n onVisibilityChange(callback: VisibilityCallback): void;\n removeVisibilityListener(callback: VisibilityCallback): void;\n /** Whether the page/app is in the process of unloading (browser-only concept). */\n isUnloading(): boolean;\n}\n\nexport function createBrowserLifecycleProvider(): LifecycleProvider {\n const listeners: VisibilityCallback[] = [];\n let unloading = false;\n\n function notify(state: VisibilityState): void {\n for (const cb of listeners) cb(state);\n }\n\n const onPageHide = (): void => {\n unloading = true;\n notify(\"background\");\n };\n\n const onVisibilityChange = (): void => {\n if (typeof document !== \"undefined\") {\n notify(document.visibilityState === \"hidden\" ? \"background\" : \"foreground\");\n }\n };\n\n const onBeforeUnload = (): void => {\n unloading = true;\n notify(\"background\");\n };\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"pagehide\", onPageHide);\n window.addEventListener(\"beforeunload\", onBeforeUnload);\n }\n if (typeof document !== \"undefined\") {\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n }\n\n return {\n onVisibilityChange(callback: VisibilityCallback): void {\n listeners.push(callback);\n },\n removeVisibilityListener(callback: VisibilityCallback): void {\n const idx = listeners.indexOf(callback);\n if (idx !== -1) listeners.splice(idx, 1);\n },\n isUnloading(): boolean {\n return unloading;\n },\n };\n}\n", "/**\n * TrafficalClient - JavaScript SDK for browser environments.\n *\n * Features:\n * - Same API as Node SDK: getParams(), decide(), trackExposure(), track()\n * - Error boundary wrapping (P0)\n * - Exposure deduplication (P0)\n * - Smart event batching with beacon on unload (P1)\n * - Plugin system (P2)\n * - Auto stable ID for anonymous users\n */\n\nimport {\n type ConfigBundle,\n type Context,\n type DecisionResult,\n type ParameterValue,\n type ExposureEvent,\n type TrackEvent,\n type TrackAttribution,\n type DecisionEvent,\n type BundlePolicy,\n type ServerResolveResponse,\n type ResolveOptions,\n type AssignmentLogger,\n type AssignmentType,\n type TrackableEvent,\n type TrackableEventLogger,\n type TrackEventMap,\n type OnSchemaWarnings,\n resolveParameters,\n decide as coreDecide,\n getUnitKeyValue,\n generateExposureId,\n generateTrackEventId,\n generateDecisionId,\n generateAssignmentId,\n} from \"@traffical/core\";\n\nimport {\n DecisionClient,\n createEdgeDecideRequest,\n type DecisionClientConfig,\n} from \"@traffical/core-io\";\n\nimport { ErrorBoundary, type ErrorBoundaryOptions } from \"./error-boundary.js\";\nimport { EventLogger } from \"./event-logger.js\";\nimport { ExposureDeduplicator } from \"./exposure-dedup.js\";\nimport { StableIdProvider } from \"./stable-id.js\";\nimport { createStorageProvider, type StorageProvider } from \"./storage.js\";\nimport { PluginManager, type TrafficalPlugin, createDecisionTrackingPlugin } from \"./plugins/index.js\";\nimport { createBrowserLifecycleProvider, type LifecycleProvider } from \"./lifecycle.js\";\nimport { SDK_VERSION } from \"./version.js\";\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst SDK_NAME = \"js-client\";\n\nconst DEFAULT_BASE_URL = \"https://sdk.traffical.io\";\nconst DEFAULT_REFRESH_INTERVAL_MS = 60_000; // 1 minute\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10_000; // 10 seconds\nconst OFFLINE_WARNING_INTERVAL_MS = 300_000; // 5 minutes\nconst DECISION_CACHE_MAX_SIZE = 100; // Max decisions to cache for attribution lookup\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface TrafficalClientOptions {\n /** Organization ID */\n orgId: string;\n /** Project ID */\n projectId: string;\n /** Environment (e.g., \"production\", \"staging\") */\n env: string;\n /** API key for authentication */\n apiKey: string;\n /** Base URL for the SDK API (edge worker) */\n baseUrl?: string;\n /** Local config bundle for offline fallback */\n localConfig?: ConfigBundle;\n /** Refresh interval in milliseconds (default: 60000) */\n refreshIntervalMs?: number;\n /**\n * Timeout in milliseconds for SDK network requests \u2014 the config bundle\n * fetch and event batch POSTs (default: 10000).\n *\n * On timeout the request is aborted and treated exactly like a network\n * failure: config fetches fall back to the cached/local config, and event\n * batches are persisted for retry.\n */\n requestTimeoutMs?: number;\n /** Error boundary options */\n errorBoundary?: ErrorBoundaryOptions;\n /** Event batching options */\n eventBatchSize?: number;\n eventFlushIntervalMs?: number;\n /** Exposure deduplication session TTL */\n exposureSessionTtlMs?: number;\n /**\n * Whether to automatically track decision events (default: true).\n * When enabled, every call to decide() automatically sends a DecisionEvent\n * to the backend, enabling intent-to-treat analysis.\n */\n trackDecisions?: boolean;\n /**\n * Decision deduplication TTL in milliseconds (default: 1 hour).\n * Same user+assignment combination won't be tracked again within this window.\n */\n decisionDeduplicationTtlMs?: number;\n /** Plugins to register on init */\n plugins?: TrafficalPlugin[];\n /** Custom storage provider (default: localStorage) */\n storage?: StorageProvider;\n /** Disable automatic stable ID generation */\n disableAutoStableId?: boolean;\n /**\n * Attribution mode for track events (default: \"cumulative\").\n * - \"cumulative\": Attributes to ALL layers the user was exposed to in this session.\n * Best for cross-page funnels (catalog -> PDP -> checkout).\n * - \"decision\": Attributes only to the layers from the specific decision.\n * Use when strict single-decision attribution is required.\n */\n attributionMode?: \"cumulative\" | \"decision\";\n /**\n * Evaluation mode (default: \"bundle\").\n * - \"bundle\": SDK fetches config bundle, resolves parameters locally.\n * - \"server\": SDK delegates resolution to the edge worker via POST /v1/resolve.\n */\n evaluationMode?: \"bundle\" | \"server\";\n /** Lifecycle provider for visibility/unload events (default: browser lifecycle) */\n lifecycleProvider?: LifecycleProvider;\n\n /**\n * Optional callback for routing assignment events to a customer-managed\n * pipeline (e.g., Segment, Rudderstack, direct DB writes).\n *\n * When provided, called on every decide()/trackExposure() with a structured\n * AssignmentLogEntry. Enables the \"BYO assignment pipeline\" pattern for\n * warehouse-native analytics.\n */\n assignmentLogger?: AssignmentLogger;\n\n /**\n * When true, the SDK will NOT send events (decisions, exposures, tracks)\n * to the Traffical control plane. The SDK still fetches config from\n * Traffical CDN/edge for flag evaluation.\n *\n * Default: false\n */\n disableCloudEvents?: boolean;\n\n /**\n * When true, assignment logger calls are deduplicated per session\n * (same unit+policy+variant won't fire again). Default: true.\n */\n deduplicateAssignmentLogger?: boolean;\n\n /**\n * Optional callback for routing full events (exposure, track, decision)\n * to a customer-managed pipeline (e.g. Jitsu, Segment). Fires regardless\n * of disableCloudEvents, so you can send to your own sink instead of (or\n * in addition to) the Traffical edge.\n */\n eventLogger?: TrackableEventLogger;\n\n /**\n * Callback for schema validation warnings from the edge.\n * Only fires when event schemas are defined and enforcement is \"warn\".\n * Recommended for development builds to surface schema violations.\n *\n * @example\n * onSchemaWarnings: (warnings) => {\n * for (const w of warnings) {\n * console.warn(`[Traffical] Schema warning for \"${w.event}\":`, w.violations);\n * }\n * }\n */\n onSchemaWarnings?: OnSchemaWarnings;\n}\n\ninterface ClientState {\n bundle: ConfigBundle | null;\n etag: string | null;\n lastFetchTime: number;\n lastOfflineWarning: number;\n refreshTimer: ReturnType<typeof setInterval> | null;\n isInitialized: boolean;\n /** Cached server resolve response (server mode only) */\n serverResponse: ServerResolveResponse | null;\n /** Cached edge results for bundle mode with edge policies */\n cachedEdgeResults: ResolveOptions | null;\n}\n\n// =============================================================================\n// TrafficalClient Class\n// =============================================================================\n\nexport class TrafficalClient<TEvents extends TrackEventMap = TrackEventMap> {\n private readonly _options: Required<\n Pick<TrafficalClientOptions, \"orgId\" | \"projectId\" | \"env\" | \"apiKey\" | \"baseUrl\" | \"refreshIntervalMs\">\n > & {\n localConfig?: ConfigBundle;\n attributionMode: \"cumulative\" | \"decision\";\n evaluationMode: \"bundle\" | \"server\";\n };\n\n private _state: ClientState = {\n bundle: null,\n etag: null,\n lastFetchTime: 0,\n lastOfflineWarning: 0,\n refreshTimer: null,\n isInitialized: false,\n serverResponse: null,\n cachedEdgeResults: null,\n };\n\n private readonly _errorBoundary: ErrorBoundary;\n private readonly _storage: StorageProvider;\n private readonly _eventLogger: EventLogger;\n private readonly _exposureDedup: ExposureDeduplicator;\n private readonly _stableId: StableIdProvider;\n private readonly _plugins: PluginManager;\n private readonly _lifecycleProvider: LifecycleProvider;\n private readonly _decisionClient: DecisionClient | null;\n private readonly _requestTimeoutMs: number;\n private readonly _assignmentLogger?: AssignmentLogger;\n private readonly _byoEventLogger?: TrackableEventLogger;\n private readonly _disableCloudEvents: boolean;\n private readonly _assignmentLoggerDedup: ExposureDeduplicator | null;\n /** Cache of recent decisions for attribution lookup when track() is called */\n private readonly _decisionCache: Map<string, DecisionResult> = new Map();\n /**\n * Cumulative attribution map, keyed by unitKey \u2192 layerId:policyId \u2192 TrackAttribution.\n * Unlike _decisionCache (bounded to DECISION_CACHE_MAX_SIZE), this map accumulates\n * every attribution entry from every decide() call during the session. This prevents\n * attribution loss when per-entity policies (e.g. per-product OptimizedProductCards)\n * flood the decision cache and evict earlier page-level decisions.\n */\n private readonly _cumulativeAttribution: Map<string, Map<string, TrackAttribution>> = new Map();\n private _identityListeners: Array<(unitKey: string) => void> = [];\n private _overrideListeners: Array<(overrides: Record<string, ParameterValue>) => void> = [];\n private _overrides: Record<string, ParameterValue> = {};\n\n constructor(options: TrafficalClientOptions) {\n const evaluationMode = options.evaluationMode ?? \"bundle\";\n this._options = {\n orgId: options.orgId,\n projectId: options.projectId,\n env: options.env,\n apiKey: options.apiKey,\n baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,\n localConfig: options.localConfig,\n refreshIntervalMs: options.refreshIntervalMs ?? DEFAULT_REFRESH_INTERVAL_MS,\n attributionMode: options.attributionMode ?? \"cumulative\",\n evaluationMode,\n };\n this._requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n\n // Create DecisionClient when needed (server mode, or bundle mode may use for edge policies)\n const decisionClientConfig: DecisionClientConfig = {\n baseUrl: this._options.baseUrl,\n orgId: this._options.orgId,\n projectId: this._options.projectId,\n env: this._options.env,\n apiKey: this._options.apiKey,\n };\n this._decisionClient = new DecisionClient(decisionClientConfig);\n\n // Initialize components\n this._errorBoundary = new ErrorBoundary(options.errorBoundary);\n this._storage = options.storage ?? createStorageProvider();\n this._lifecycleProvider = options.lifecycleProvider ?? createBrowserLifecycleProvider();\n\n // Default dev-mode schema warnings handler\n if (!options.onSchemaWarnings) {\n try {\n const isDev = typeof globalThis !== \"undefined\"\n && (globalThis as any).process?.env?.NODE_ENV === \"development\";\n if (isDev) {\n options.onSchemaWarnings = (warnings) => {\n for (const w of warnings) {\n console.warn(\n `[Traffical] Schema warning for \"${w.event}\":`,\n w.violations.map((v: { path: string; message: string }) => `${v.path}: ${v.message}`).join(\", \")\n );\n }\n };\n }\n } catch {\n // process may not be available in browser environments\n }\n }\n\n this._eventLogger = new EventLogger({\n endpoint: `${this._options.baseUrl}/v1/events/batch`,\n apiKey: options.apiKey,\n storage: this._storage,\n lifecycleProvider: this._lifecycleProvider,\n batchSize: options.eventBatchSize,\n flushIntervalMs: options.eventFlushIntervalMs,\n requestTimeoutMs: options.requestTimeoutMs,\n onError: (error) => {\n console.warn(\"[Traffical] Event logging error:\", error.message);\n },\n onSchemaWarnings: options.onSchemaWarnings,\n });\n\n this._exposureDedup = new ExposureDeduplicator({\n storage: this._storage,\n sessionTtlMs: options.exposureSessionTtlMs,\n });\n\n this._stableId = new StableIdProvider({\n storage: this._storage,\n });\n\n this._plugins = new PluginManager();\n\n // Warehouse-native options\n this._assignmentLogger = options.assignmentLogger;\n this._byoEventLogger = options.eventLogger;\n this._disableCloudEvents = options.disableCloudEvents ?? false;\n this._assignmentLoggerDedup = (options.deduplicateAssignmentLogger !== false && options.assignmentLogger)\n ? new ExposureDeduplicator({ storage: this._storage, sessionTtlMs: options.exposureSessionTtlMs })\n : null;\n\n // Register decision tracking plugin (enabled by default). Skipped only when\n // cloud events are disabled AND there is no BYO event logger to receive them.\n if (options.trackDecisions !== false && (!this._disableCloudEvents || this._byoEventLogger)) {\n this._plugins.register({\n plugin: createDecisionTrackingPlugin(\n { deduplicationTtlMs: options.decisionDeduplicationTtlMs },\n {\n orgId: this._options.orgId,\n projectId: this._options.projectId,\n env: this._options.env,\n log: (event: DecisionEvent) => this._dispatchEvent(event),\n }\n ),\n priority: 100, // High priority so it runs before user plugins\n });\n }\n\n // Register user-provided plugins\n if (options.plugins) {\n for (const plugin of options.plugins) {\n this._plugins.register(plugin);\n }\n }\n\n // Initialize with local config if provided\n if (this._options.localConfig) {\n this._state.bundle = this._options.localConfig;\n // Notify plugins about the local config\n this._plugins.runConfigUpdate(this._options.localConfig);\n }\n\n // Register on global instance list so DevTools can discover ES-module SDKs\n if (typeof window !== \"undefined\") {\n const w = window as unknown as Record<string, unknown>;\n (w.__TRAFFICAL_INSTANCES__ ??= [] as TrafficalClient[]) as TrafficalClient[];\n (w.__TRAFFICAL_INSTANCES__ as TrafficalClient[]).push(this);\n }\n }\n\n // ===========================================================================\n // Initialization\n // ===========================================================================\n\n /**\n * Initializes the client by fetching the config bundle.\n */\n async initialize(): Promise<void> {\n await this._errorBoundary.captureAsync(\n \"initialize\",\n async () => {\n if (this._options.evaluationMode === \"server\") {\n await this._fetchServerResolve();\n } else {\n await this._fetchConfig();\n }\n this._startBackgroundRefresh();\n this._state.isInitialized = true;\n\n // Run plugin onInitialize hooks (pass client reference for autonomous plugins)\n await this._plugins.runInitialize(this);\n },\n undefined\n );\n }\n\n /**\n * Check if the client is initialized.\n */\n get isInitialized(): boolean {\n return this._state.isInitialized;\n }\n\n /**\n * Stops background refresh and cleans up resources.\n */\n destroy(): void {\n if (this._state.refreshTimer) {\n clearInterval(this._state.refreshTimer);\n this._state.refreshTimer = null;\n }\n\n if (this._lifecycleProvider.isUnloading()) {\n this._eventLogger.flushBeacon();\n } else {\n this._eventLogger.flush().catch(() => {});\n }\n this._eventLogger.destroy();\n\n // Run plugin onDestroy hooks\n this._plugins.runDestroy();\n\n // Clear listeners and overrides\n this._identityListeners = [];\n this._overrideListeners = [];\n this._overrides = {};\n\n // Remove from global instance list\n if (typeof window !== \"undefined\") {\n const w = window as unknown as Record<string, unknown>;\n const instances = w.__TRAFFICAL_INSTANCES__ as TrafficalClient[] | undefined;\n if (instances) {\n const idx = instances.indexOf(this);\n if (idx !== -1) instances.splice(idx, 1);\n }\n }\n }\n\n // ===========================================================================\n // Config Management\n // ===========================================================================\n\n /**\n * Manually refreshes the config bundle.\n */\n async refreshConfig(): Promise<void> {\n await this._errorBoundary.swallow(\"refreshConfig\", async () => {\n if (this._options.evaluationMode === \"server\") {\n await this._fetchServerResolve();\n } else {\n await this._fetchConfig();\n }\n });\n }\n\n /**\n * Gets the current config bundle version.\n */\n getConfigVersion(): string | null {\n return this._state.serverResponse?.stateVersion ?? this._state.bundle?.version ?? null;\n }\n\n // ===========================================================================\n // Parameter Resolution\n // ===========================================================================\n\n /**\n * Resolves parameters with defaults as fallback.\n */\n getParams<T extends Record<string, ParameterValue>>(options: { context: Context; defaults: T }): T {\n return this._errorBoundary.capture(\n \"getParams\",\n () => {\n // Server mode: return from cached server response\n if (this._options.evaluationMode === \"server\" && this._state.serverResponse) {\n const result = { ...options.defaults } as Record<string, ParameterValue>;\n for (const [key, value] of Object.entries(this._state.serverResponse.assignments)) {\n if (key in result) {\n result[key] = value;\n }\n }\n this._plugins.runResolve(result as T);\n this._applyOverridesToResult(result);\n return result as T;\n }\n\n const bundle = this._getEffectiveBundle();\n const context = this._enrichContext(options.context);\n const params = resolveParameters<T>(bundle, context, options.defaults);\n\n // Run plugin onResolve hooks (e.g., DOM binding plugin)\n this._plugins.runResolve(params);\n\n // Apply parameter overrides (post-resolution, post-plugin)\n this._applyOverridesToResult(params);\n\n return params;\n },\n options.defaults\n );\n }\n\n /**\n * Makes a decision with full metadata for tracking.\n */\n decide<T extends Record<string, ParameterValue>>(options: { context: Context; defaults: T }): DecisionResult {\n return this._errorBoundary.capture(\n \"decide\",\n () => {\n // Server mode: return from cached server response\n if (this._options.evaluationMode === \"server\" && this._state.serverResponse) {\n const resp = this._state.serverResponse;\n const assignments = { ...options.defaults } as Record<string, ParameterValue>;\n for (const [key, value] of Object.entries(resp.assignments)) {\n if (key in assignments) {\n assignments[key] = value;\n }\n }\n const decision: DecisionResult = {\n decisionId: resp.decisionId,\n assignments,\n metadata: resp.metadata,\n };\n this._cacheDecision(decision);\n this._updateCumulativeAttribution(decision);\n this._plugins.runDecision(decision);\n this._applyOverridesToResult(decision.assignments);\n this._emitAssignmentLogEntries(decision, \"decision\");\n return decision;\n }\n\n const bundle = this._getEffectiveBundle();\n\n // Run plugin onBeforeDecision hooks\n let context = this._enrichContext(options.context);\n context = this._plugins.runBeforeDecision(context);\n\n // Pass cached edge results (from bundle mode pre-fetch) if available\n const edgeOpts = this._state.cachedEdgeResults ?? undefined;\n const decision = coreDecide<T>(bundle, context, options.defaults, edgeOpts);\n\n // Cache decision for attribution lookup when track() is called\n this._cacheDecision(decision);\n // Accumulate attribution entries (survives decision cache eviction)\n this._updateCumulativeAttribution(decision);\n\n // Run plugin onDecision hooks (e.g., DOM binding plugin)\n this._plugins.runDecision(decision);\n\n // Apply parameter overrides (post-resolution, post-plugin)\n this._applyOverridesToResult(decision.assignments);\n\n this._emitAssignmentLogEntries(decision, \"decision\");\n\n return decision;\n },\n {\n decisionId: generateDecisionId(),\n assignments: options.defaults,\n metadata: {\n timestamp: new Date().toISOString(),\n unitKeyValue: \"\",\n layers: [],\n },\n }\n );\n }\n\n // ===========================================================================\n // Event Tracking\n // ===========================================================================\n\n /**\n * Tracks an exposure event.\n * Automatically deduplicates exposures for the same user/variant.\n *\n * Skips layers marked `attributionOnly` \u2014 those were resolved for\n * attribution/assignment purposes only (no parameters were requested\n * from that layer) and should not count as exposures.\n */\n trackExposure(decision: DecisionResult): void {\n this._errorBoundary.capture(\n \"trackExposure\",\n () => {\n const unitKey = decision.metadata.unitKeyValue;\n if (!unitKey) return;\n\n // Emit to assignment logger (separate from cloud events)\n this._emitAssignmentLogEntries(decision, \"exposure\");\n\n // Check each layer for deduplication\n for (const layer of decision.metadata.layers) {\n if (!layer.policyId || !layer.allocationName) continue;\n\n // Skip attribution-only layers \u2014 the user wasn't exposed to\n // parameters from this layer, so no exposure event should fire.\n if (layer.attributionOnly) continue;\n\n // Deduplicate\n const isNew = this._exposureDedup.checkAndMark(unitKey, layer.policyId, layer.allocationName);\n if (!isNew) continue;\n\n const event: ExposureEvent = {\n type: \"exposure\",\n id: generateExposureId(), // Unique exposure ID (not same as decision)\n decisionId: decision.decisionId,\n orgId: this._options.orgId,\n projectId: this._options.projectId,\n env: this._options.env,\n unitKey,\n timestamp: new Date().toISOString(),\n assignments: decision.assignments,\n layers: decision.metadata.layers,\n context: decision.metadata.filteredContext,\n sdkName: SDK_NAME,\n sdkVersion: SDK_VERSION,\n };\n\n // Run plugin onExposure hooks\n if (!this._plugins.runExposure(event)) {\n continue;\n }\n\n this._dispatchEvent(event);\n }\n },\n undefined\n );\n }\n\n /**\n * Tracks a user event.\n * \n * @param eventName - The event name (e.g., 'purchase', 'add_to_cart')\n * @param properties - Optional event properties (including value for optimization)\n * @param options - Optional tracking options (decisionId, unitKey)\n * \n * @example\n * // Track a purchase with revenue\n * client.track('purchase', { value: 99.99, orderId: 'ord_123' });\n * \n * // Track a simple event\n * client.track('add_to_cart', { itemId: 'sku_456' });\n * \n * // Track with explicit decision attribution\n * client.track('checkout_complete', { value: 1 }, { decisionId: decision.decisionId });\n */\n track<E extends Extract<keyof TEvents, string>>(\n eventName: E,\n properties?: TEvents[E],\n options?: { decisionId?: string; unitKey?: string }\n ): void {\n this._errorBoundary.capture(\n \"track\",\n () => {\n const unitKey = options?.unitKey ?? this._stableId.getId();\n const value = typeof properties?.value === 'number' ? properties.value : undefined;\n\n // Auto-populate attribution from cached decisions\n const attribution = this._buildAttribution(unitKey, options?.decisionId);\n const decisionId = options?.decisionId;\n\n const event: TrackEvent = {\n type: \"track\",\n id: generateTrackEventId(),\n orgId: this._options.orgId,\n projectId: this._options.projectId,\n env: this._options.env,\n unitKey,\n timestamp: new Date().toISOString(),\n event: eventName,\n value,\n properties,\n decisionId,\n attribution,\n sdkName: SDK_NAME,\n sdkVersion: SDK_VERSION,\n };\n\n // Run plugin onTrack hooks\n if (!this._plugins.runTrack(event)) {\n return;\n }\n\n this._dispatchEvent(event);\n },\n undefined\n );\n }\n\n /**\n * Flush pending events immediately.\n */\n async flushEvents(): Promise<void> {\n await this._errorBoundary.swallow(\"flushEvents\", async () => {\n await this._eventLogger.flush();\n });\n }\n\n // ===========================================================================\n // Plugin Management\n // ===========================================================================\n\n /**\n * Register a plugin.\n * If the client is already initialized, fires onInitialize and onConfigUpdate\n * immediately so late-registered plugins (e.g. debug plugin) work correctly.\n */\n use(plugin: TrafficalPlugin): this {\n const added = this._plugins.register(plugin);\n if (!added) return this;\n\n if (this._state.isInitialized) {\n try {\n plugin.onInitialize?.(this);\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" late onInitialize error:`, error);\n }\n if (this._state.bundle) {\n try {\n plugin.onConfigUpdate?.(this._state.bundle);\n } catch (error) {\n console.warn(`[Traffical] Plugin \"${plugin.name}\" late onConfigUpdate error:`, error);\n }\n }\n }\n\n return this;\n }\n\n /**\n * Get a registered plugin by name.\n */\n getPlugin(name: string): TrafficalPlugin | undefined {\n return this._plugins.get(name);\n }\n\n // ===========================================================================\n // Stable ID\n // ===========================================================================\n\n /**\n * Get the stable ID for the current user.\n */\n getStableId(): string {\n return this._stableId.getId();\n }\n\n /**\n * Set a custom stable ID (e.g., when user logs in).\n * Low-level \u2014 does NOT notify framework providers. Use `identify()` instead\n * when you want the UI to update.\n */\n setStableId(id: string): void {\n this._stableId.setId(id);\n }\n\n /**\n * Change the user identity and notify all listeners (framework providers,\n * plugins, DevTools). This causes React/Svelte/RN providers to re-evaluate\n * decisions with the new identity, updating the UI.\n *\n * @example\n * // After user logs in\n * client.identify('user_logged_in_123');\n */\n identify(unitKey: string): void {\n this._stableId.setId(unitKey);\n for (const cb of this._identityListeners) {\n try {\n cb(unitKey);\n } catch {\n // Ignore listener errors\n }\n }\n }\n\n /**\n * Subscribe to identity changes triggered by `identify()`.\n * Returns an unsubscribe function.\n */\n onIdentityChange(cb: (unitKey: string) => void): () => void {\n this._identityListeners.push(cb);\n return () => {\n this._identityListeners = this._identityListeners.filter(l => l !== cb);\n };\n }\n\n /**\n * Subscribe to override changes triggered by `applyOverrides()` / `clearOverrides()`.\n * Framework providers use this to re-evaluate decisions when overrides change.\n * Returns an unsubscribe function.\n */\n onOverridesChange(cb: (overrides: Record<string, ParameterValue>) => void): () => void {\n this._overrideListeners.push(cb);\n return () => {\n this._overrideListeners = this._overrideListeners.filter(l => l !== cb);\n };\n }\n\n // ===========================================================================\n // Parameter Overrides (Plugin API \u2014 not intended for direct public use)\n // ===========================================================================\n\n /**\n * Set parameter overrides. Only keys present in a decision's assignments\n * or getParams defaults will be overridden. Merges with existing overrides.\n *\n * Exposed via `PluginClientAPI` for debug tooling \u2014 not a public API.\n */\n applyOverrides(overrides: Record<string, ParameterValue>): void {\n Object.assign(this._overrides, overrides);\n this._notifyOverrideListeners();\n }\n\n /**\n * Clear all parameter overrides.\n */\n clearOverrides(): void {\n this._overrides = {};\n this._notifyOverrideListeners();\n }\n\n /**\n * Get a copy of the current overrides map.\n */\n getOverrides(): Record<string, ParameterValue> {\n return { ...this._overrides };\n }\n\n // ===========================================================================\n // Private Methods\n // ===========================================================================\n\n private _notifyOverrideListeners(): void {\n const snapshot = { ...this._overrides };\n for (const cb of this._overrideListeners) {\n try {\n cb(snapshot);\n } catch {\n // Ignore listener errors\n }\n }\n }\n\n /**\n * Routes a built event to the BYO event logger (if configured) and to the\n * Traffical edge batcher (unless cloud events are disabled).\n */\n private _dispatchEvent(event: TrackableEvent): void {\n if (this._byoEventLogger) {\n try {\n this._byoEventLogger(event);\n } catch {\n // Swallow BYO logger errors \u2014 they must not break SDK event handling.\n }\n }\n if (!this._disableCloudEvents) {\n this._eventLogger.log(event);\n }\n }\n\n private _emitAssignmentLogEntries(decision: DecisionResult, type: AssignmentType): void {\n if (!this._assignmentLogger) return;\n const unitKey = decision.metadata.unitKeyValue;\n if (!unitKey) return;\n\n for (const layer of decision.metadata.layers) {\n if (!layer.policyId || !layer.allocationName) continue;\n\n // Dedup: skip if we've already logged this unit+policy+allocation+type in this session\n if (this._assignmentLoggerDedup) {\n const isNew = this._assignmentLoggerDedup.checkAndMark(\n unitKey,\n layer.policyId,\n `${layer.allocationName}:${type}`,\n );\n if (!isNew) continue;\n }\n\n this._assignmentLogger({\n unitKey,\n policyId: layer.policyId,\n policyKey: layer.policyKey,\n allocationName: layer.allocationName,\n allocationKey: layer.allocationKey,\n timestamp: decision.metadata.timestamp,\n layerId: layer.layerId,\n allocationId: layer.allocationId,\n orgId: this._options.orgId,\n projectId: this._options.projectId,\n env: this._options.env,\n sdkName: SDK_NAME,\n sdkVersion: SDK_VERSION,\n properties: decision.metadata.filteredContext,\n type,\n decisionId: decision.decisionId,\n anonymousId: this._stableId.getId(),\n id: generateAssignmentId(),\n });\n }\n }\n\n private _applyOverridesToResult(target: Record<string, ParameterValue>): void {\n const keys = Object.keys(this._overrides);\n if (keys.length === 0) return;\n for (const k of keys) {\n if (k in target) {\n target[k] = this._overrides[k];\n }\n }\n }\n\n private _getEffectiveBundle(): ConfigBundle | null {\n return this._state.bundle ?? this._options.localConfig ?? null;\n }\n\n private _enrichContext(context: Context): Context {\n // Add stable ID if not already present\n const bundle = this._getEffectiveBundle();\n const unitKey = bundle?.hashing?.unitKey ?? \"userId\";\n\n if (!context[unitKey]) {\n return {\n ...context,\n [unitKey]: this._stableId.getId(),\n };\n }\n\n return context;\n }\n\n private async _fetchConfig(): Promise<void> {\n const url = `${this._options.baseUrl}/v1/config/${this._options.projectId}?env=${this._options.env}`;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this._options.apiKey}`,\n };\n\n if (this._state.etag) {\n headers[\"If-None-Match\"] = this._state.etag;\n }\n\n // Abort the request if the edge hangs (slow TCP, not a 5xx) so the\n // promise settles and we fall back to cached/local config.\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this._requestTimeoutMs);\n\n try {\n const response = await fetch(url, {\n method: \"GET\",\n headers,\n signal: controller.signal,\n });\n\n if (response.status === 304) {\n this._state.lastFetchTime = Date.now();\n return;\n }\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const bundle = (await response.json()) as ConfigBundle;\n const etag = response.headers.get(\"ETag\");\n\n this._state.bundle = bundle;\n this._state.etag = etag;\n this._state.lastFetchTime = Date.now();\n\n // Pre-fetch edge results if bundle has edge-mode policies\n if (this._findEdgePolicies(bundle).length > 0) {\n const edgeResults = await this._prefetchEdgeResults(bundle, this._enrichContext({}));\n this._state.cachedEdgeResults = edgeResults;\n } else {\n this._state.cachedEdgeResults = null;\n }\n\n // Run plugin onConfigUpdate hooks (e.g., DOM binding plugin)\n this._plugins.runConfigUpdate(bundle);\n } catch (error) {\n this._logOfflineWarning(error);\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private _startBackgroundRefresh(): void {\n const interval = this._options.evaluationMode === \"server\"\n ? (this._state.serverResponse?.suggestedRefreshMs ?? this._options.refreshIntervalMs)\n : this._options.refreshIntervalMs;\n\n if (interval <= 0) return;\n\n this._state.refreshTimer = setInterval(() => {\n if (this._options.evaluationMode === \"server\") {\n this._fetchServerResolve().catch(() => {});\n } else {\n this._fetchConfig().catch(() => {});\n }\n }, interval);\n }\n\n private async _fetchServerResolve(): Promise<void> {\n if (!this._decisionClient) return;\n\n try {\n const context = this._enrichContext({});\n const response = await this._decisionClient.resolve({ context });\n if (response) {\n this._state.serverResponse = response;\n this._state.lastFetchTime = Date.now();\n }\n } catch (error) {\n this._logOfflineWarning(error);\n }\n }\n\n /**\n * Finds edge-mode policies in the current bundle.\n */\n private _findEdgePolicies(bundle: ConfigBundle): BundlePolicy[] {\n const policies: BundlePolicy[] = [];\n for (const layer of bundle.layers) {\n for (const policy of layer.policies) {\n if (\n policy.state === \"running\" &&\n policy.entityConfig?.resolutionMode === \"edge\"\n ) {\n policies.push(policy);\n }\n }\n }\n return policies;\n }\n\n /**\n * Pre-fetches edge results for edge-mode policies in bundle mode.\n * Returns ResolveOptions with edgeResults populated.\n */\n private async _prefetchEdgeResults(\n bundle: ConfigBundle,\n context: Context\n ): Promise<ResolveOptions> {\n if (!this._decisionClient) return {};\n\n const edgePolicies = this._findEdgePolicies(bundle);\n if (edgePolicies.length === 0) return {};\n\n const unitKeyValue = getUnitKeyValue(bundle, context);\n if (!unitKeyValue) return {};\n\n const requests = edgePolicies\n .map((policy) => {\n if (!policy.entityConfig) return null;\n const allocationCount = policy.entityConfig.dynamicAllocations\n ? (typeof context[policy.entityConfig.dynamicAllocations.countKey] === \"number\"\n ? Math.floor(context[policy.entityConfig.dynamicAllocations.countKey] as number)\n : 0)\n : policy.allocations.length;\n\n return createEdgeDecideRequest(\n policy.id,\n policy.entityConfig.entityKeys,\n context,\n unitKeyValue,\n allocationCount || undefined\n );\n })\n .filter((r): r is NonNullable<typeof r> => r !== null);\n\n if (requests.length === 0) return {};\n\n try {\n const responses = await this._decisionClient.decideEntityBatch(requests);\n const edgeResults = new Map<string, { allocationIndex: number; entityId: string }>();\n\n for (let i = 0; i < requests.length; i++) {\n const resp = responses[i];\n if (resp) {\n edgeResults.set(requests[i].policyId, {\n allocationIndex: resp.allocationIndex,\n entityId: requests[i].entityId,\n });\n }\n }\n\n return edgeResults.size > 0 ? { edgeResults } : {};\n } catch {\n return {};\n }\n }\n\n private _logOfflineWarning(error: unknown): void {\n const now = Date.now();\n if (now - this._state.lastOfflineWarning > OFFLINE_WARNING_INTERVAL_MS) {\n console.warn(\n `[Traffical] Failed to fetch config: ${error instanceof Error ? error.message : String(error)}. Using ${this._state.bundle ? \"cached\" : \"local\"} config.`\n );\n this._state.lastOfflineWarning = now;\n }\n }\n\n /**\n * Caches a decision for attribution lookup when track() is called.\n * Maintains a bounded cache to prevent memory leaks.\n */\n private _cacheDecision(decision: DecisionResult): void {\n // Evict oldest entries if cache is full\n if (this._decisionCache.size >= DECISION_CACHE_MAX_SIZE) {\n // Get first (oldest) key and delete it\n const firstKey = this._decisionCache.keys().next().value;\n if (firstKey) {\n this._decisionCache.delete(firstKey);\n }\n }\n this._decisionCache.set(decision.decisionId, decision);\n }\n\n /**\n * Accumulates attribution entries from a decision into the session-level map.\n * Keyed by unitKey \u2192 layerId:policyId with last-write-wins semantics.\n * This ensures attribution survives decision cache eviction.\n */\n private _updateCumulativeAttribution(decision: DecisionResult): void {\n const unitKey = decision.metadata.unitKeyValue;\n if (!unitKey) return;\n\n let userAttrs = this._cumulativeAttribution.get(unitKey);\n if (!userAttrs) {\n userAttrs = new Map<string, TrackAttribution>();\n this._cumulativeAttribution.set(unitKey, userAttrs);\n }\n\n for (const l of decision.metadata.layers) {\n if (!l.policyId || !l.allocationName) continue;\n const key = `${l.layerId}:${l.policyId}`;\n // Last-write-wins: later decisions overwrite earlier ones.\n // For per-entity dynamic allocation policies this keeps only the most\n // recent allocation; for normal policies allocationName is deterministic\n // so the overwrite is a no-op.\n userAttrs.set(key, {\n layerId: l.layerId,\n policyId: l.policyId,\n allocationName: l.allocationName,\n });\n }\n }\n\n /**\n * Builds attribution for a track event based on the configured attribution mode.\n *\n * - \"cumulative\": Collects layers from ALL cached decisions for this unit,\n * deduplicated by layerId:policyId (last-write-wins). This ensures cross-page\n * funnels (e.g., catalog -> PDP -> checkout) attribute correctly to all\n * experiments the user is exposed to. For per-entity dynamic allocation\n * policies, only the most recent allocation is kept to avoid attributing\n * rewards to allocations from other entities (e.g., different products).\n *\n * - \"decision\": Only uses layers from the single decision matching decisionId.\n * Legacy behavior for strict single-decision attribution.\n */\n private _buildAttribution(\n unitKey: string,\n decisionId?: string\n ): TrackAttribution[] | undefined {\n if (this._options.attributionMode === \"decision\") {\n // Legacy behavior: single-decision attribution\n if (!decisionId) return undefined;\n const cachedDecision = this._decisionCache.get(decisionId);\n if (!cachedDecision) return undefined;\n return cachedDecision.metadata.layers\n .filter((l) => l.policyId && l.allocationName)\n .map((l) => ({\n layerId: l.layerId,\n policyId: l.policyId!,\n allocationName: l.allocationName!,\n }));\n }\n\n // Cumulative mode: use the pre-built cumulative attribution map.\n // This map accumulates entries from ALL decide() calls for this unit during\n // the session, deduplicated by layerId:policyId (last-write-wins).\n // Unlike iterating _decisionCache, this is immune to cache eviction \u2014\n // e.g. when per-entity OptimizedProductCard decisions push out earlier\n // page-level decisions that contain important layer assignments.\n const userAttrs = this._cumulativeAttribution.get(unitKey);\n return userAttrs && userAttrs.size > 0\n ? Array.from(userAttrs.values())\n : undefined;\n }\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Creates and initializes a Traffical client.\n */\nexport async function createTrafficalClient<TEvents extends TrackEventMap = TrackEventMap>(options: TrafficalClientOptions): Promise<TrafficalClient<TEvents>> {\n const client = new TrafficalClient<TEvents>(options);\n await client.initialize();\n return client;\n}\n\n/**\n * Creates a Traffical client without initializing (synchronous).\n */\nexport function createTrafficalClientSync<TEvents extends TrackEventMap = TrackEventMap>(options: TrafficalClientOptions): TrafficalClient<TEvents> {\n return new TrafficalClient<TEvents>(options);\n}\n\n", "/**\n * DOM Binding Plugin\n *\n * Automatically applies parameter values to DOM elements based on bindings\n * configured in Traffical via the visual editor.\n *\n * Features:\n * - URL pattern matching to apply bindings only on matching pages\n * - MutationObserver for dynamic content (SPA support)\n * - Supports multiple property types: innerHTML, textContent, src, href, style.*\n *\n * @example\n * ```typescript\n * import { createTrafficalClient, createDOMBindingPlugin } from '@traffical/js-client';\n *\n * const client = await createTrafficalClient({\n * // ... config\n * plugins: [createDOMBindingPlugin()],\n * });\n * ```\n */\n\nimport type { ConfigBundle, BundleDOMBinding, ParameterValue } from \"@traffical/core\";\nimport type { TrafficalPlugin } from \"./types.js\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface DOMBindingPluginOptions {\n /**\n * Whether to start observing DOM mutations automatically.\n * Useful for SPAs where content is dynamically loaded.\n * @default true\n */\n observeMutations?: boolean;\n\n /**\n * Debounce time in ms for mutation-triggered reapplication.\n * @default 100\n */\n debounceMs?: number;\n}\n\n// =============================================================================\n// DOM Binding Plugin\n// =============================================================================\n\n/**\n * Creates a DOM binding plugin instance.\n *\n * This plugin applies parameter values to DOM elements based on bindings\n * defined via the visual editor.\n */\nexport function createDOMBindingPlugin(\n options: DOMBindingPluginOptions = {}\n): TrafficalPlugin & { applyBindings: (params?: Record<string, unknown>) => void; getBindings: () => BundleDOMBinding[] } {\n const config = {\n observeMutations: options.observeMutations ?? true,\n debounceMs: options.debounceMs ?? 100,\n };\n\n // Internal state\n let bindings: BundleDOMBinding[] = [];\n let lastParams: Record<string, unknown> = {};\n let observer: MutationObserver | null = null;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n // ==========================================================================\n // Core binding logic\n // ==========================================================================\n\n /**\n * Check if URL matches the binding's pattern.\n */\n function matchesUrlPattern(pattern: string, path: string): boolean {\n try {\n const regex = new RegExp(pattern);\n return regex.test(path);\n } catch {\n // Invalid regex - fall back to exact match\n return path === pattern;\n }\n }\n\n /**\n * Set a property on an element.\n * Supports: innerHTML, textContent, src, href, style.*\n */\n function setProperty(element: HTMLElement, property: string, value: string): void {\n if (property === \"innerHTML\") {\n element.innerHTML = value;\n } else if (property === \"textContent\") {\n element.textContent = value;\n } else if (property === \"src\" && \"src\" in element) {\n (element as HTMLImageElement).src = value;\n } else if (property === \"href\" && \"href\" in element) {\n (element as HTMLAnchorElement).href = value;\n } else if (property.startsWith(\"style.\")) {\n const styleProp = property.slice(6); // Remove \"style.\" prefix\n (element.style as unknown as Record<string, string>)[styleProp] = value;\n } else {\n // Generic attribute setter for other properties\n element.setAttribute(property, value);\n }\n }\n\n /**\n * Apply a single binding to matching elements.\n */\n function applyBinding(binding: BundleDOMBinding, value: unknown): void {\n const stringValue = String(value);\n\n try {\n const elements = document.querySelectorAll(binding.selector);\n\n for (const element of elements) {\n setProperty(element as HTMLElement, binding.property, stringValue);\n }\n } catch (error) {\n // Invalid selector or other DOM error - silently warn\n console.warn(\n `[Traffical DOM Binding] Failed to apply binding for ${binding.parameterKey}:`,\n error\n );\n }\n }\n\n /**\n * Apply parameter values to matching DOM elements.\n */\n function apply(params: Record<string, unknown>, forceAll = false): void {\n lastParams = params;\n\n const currentPath = typeof window !== \"undefined\" ? window.location.pathname : \"\";\n\n for (const binding of bindings) {\n // Check URL pattern match\n if (!forceAll && !matchesUrlPattern(binding.urlPattern, currentPath)) {\n continue;\n }\n\n // Get parameter value\n const value = params[binding.parameterKey];\n if (value === undefined) {\n continue;\n }\n\n // Apply to matching elements\n applyBinding(binding, value);\n }\n }\n\n /**\n * Debounced reapplication of bindings after DOM mutations.\n */\n function debouncedApply(): void {\n if (debounceTimer) {\n clearTimeout(debounceTimer);\n }\n\n debounceTimer = setTimeout(() => {\n apply(lastParams);\n }, config.debounceMs);\n }\n\n /**\n * Start observing DOM mutations.\n */\n function startObserving(): void {\n if (observer || typeof MutationObserver === \"undefined\" || typeof document === \"undefined\") {\n return;\n }\n\n observer = new MutationObserver(() => {\n debouncedApply();\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n }\n\n /**\n * Stop observing DOM mutations.\n */\n function stopObserving(): void {\n if (observer) {\n observer.disconnect();\n observer = null;\n }\n\n if (debounceTimer) {\n clearTimeout(debounceTimer);\n debounceTimer = null;\n }\n }\n\n // ==========================================================================\n // Plugin implementation\n // ==========================================================================\n\n return {\n name: \"dom-binding\",\n\n onInitialize() {\n // Start observing if enabled\n if (config.observeMutations) {\n startObserving();\n }\n },\n\n onConfigUpdate(bundle: ConfigBundle) {\n // Update bindings from bundle\n bindings = bundle.domBindings ?? [];\n },\n\n onResolve(params: Record<string, ParameterValue>) {\n // Apply bindings after parameters are resolved\n apply(params as Record<string, unknown>);\n },\n\n onDecision(decision) {\n // Also apply after decide() calls\n apply(decision.assignments as Record<string, unknown>);\n },\n\n onDestroy() {\n stopObserving();\n bindings = [];\n lastParams = {};\n },\n\n // ==========================================================================\n // Public API (exposed on plugin instance)\n // ==========================================================================\n\n /**\n * Manually apply DOM bindings with the given parameter values.\n * Use this to re-trigger bindings after dynamic content changes.\n *\n * @param params - Parameter values to apply. If omitted, uses last known params.\n */\n applyBindings(params?: Record<string, unknown>): void {\n if (params) {\n apply(params);\n } else {\n apply(lastParams);\n }\n },\n\n /**\n * Get the current DOM bindings from the config bundle.\n */\n getBindings(): BundleDOMBinding[] {\n return bindings;\n },\n };\n}\n\n// Type for the plugin with its public API\nexport type DOMBindingPlugin = ReturnType<typeof createDOMBindingPlugin>;\n\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["require_crypto", "__commonJSMin", "global_exports", "__export", "TrafficalClient", "createDOMBindingPlugin", "createDebugPlugin", "createRedirectAttributionPlugin", "createRedirectPlugin", "destroy", "init", "initSync", "instance", "isBytes", "a", "abytes", "value", "length", "title", "bytes", "isBytes", "len", "needsLen", "prefix", "ofLen", "got", "message", "aexists", "instance", "checkFinished", "aoutput", "out", "abytes", "min", "clean", "arrays", "i", "createView", "arr", "rotr", "word", "shift", "createHasher", "hashCons", "info", "hashC", "msg", "opts", "tmp", "oidNist", "suffix", "Chi", "a", "b", "c", "Maj", "HashMD", "blockLen", "outputLen", "padOffset", "isLE", "__publicField", "createView", "data", "aexists", "abytes", "view", "buffer", "len", "pos", "take", "dataView", "out", "aoutput", "clean", "i", "oview", "outLen", "state", "res", "to", "length", "finished", "destroyed", "SHA256_IV", "SHA256_K", "SHA256_W", "SHA2_32B", "HashMD", "outputLen", "A", "B", "C", "D", "E", "F", "G", "H", "view", "offset", "i", "W15", "W2", "s0", "rotr", "s1", "sigma1", "T1", "Chi", "T2", "Maj", "clean", "_SHA256", "__publicField", "SHA256_IV", "sha256", "createHasher", "_SHA256", "oidNist", "UTF8_ENCODER", "ASSIGNMENT_HASH_VERSION", "utf8ByteLength", "value", "assignmentInput", "unitKeyValue", "layerId", "unitLen", "layerLen", "sha256Digest", "input", "sha256", "hash64BE", "digest", "i", "hashInt64", "computeBucket", "unitKeyValue", "layerId", "bucketCount", "digest", "sha256Digest", "assignmentInput", "hashInt", "hash64BE", "isInBucketRange", "bucket", "range", "findMatchingAllocation", "allocations", "allocation", "UNIFORM_MODULUS", "UNIFORM_DENOMINATOR", "weightedSelection", "weights", "seed", "hashInt", "hashInt64", "random", "cumulative", "i", "evaluateCondition", "condition", "context", "field", "op", "value", "values", "contextValue", "getNestedValue", "evaluateConditions", "conditions", "obj", "path", "parts", "current", "part", "computeAllocationScore", "coefficients", "context", "score", "key", "coef", "missing", "value", "values", "strValue", "softmaxProbabilities", "scores", "gamma", "safeGamma", "scaled", "s", "maxScaled", "exps", "sumExp", "b", "e", "applyProbabilityFloor", "probs", "floor", "n", "maxFloor", "effectiveFloor", "floored", "p", "sum", "resolveContextualPolicy", "policy", "unitKeyValue", "model", "computeContextualScores", "seed", "selectedIndex", "weightedSelection", "allocations", "alloc", "random", "bytes", "customRandom", "alphabet", "defaultSize", "getRandom", "mask", "step", "size", "id", "j", "customAlphabet", "createError", "message", "err", "ENCODING", "ENCODING_LEN", "TIME_MAX", "TIME_LEN", "RANDOM_LEN", "randomChar", "prng", "rand", "ENCODING_LEN", "ENCODING", "encodeTime", "now", "len", "TIME_MAX", "createError", "mod", "str", "encodeRandom", "detectPrng", "allowInsecure", "root", "browserCrypto", "buffer", "nodeCrypto", "createError", "factory", "currPrng", "seedTime", "encodeTime", "TIME_LEN", "encodeRandom", "RANDOM_LEN", "ulid", "factory", "NANOID_ALPHABET", "ENTITY_ID_LENGTH", "nanoid", "customAlphabet", "generateEventId", "prefix", "ulid", "generateDecisionId", "generateEventId", "generateExposureId", "generateTrackEventId", "generateAssignmentId", "filterContext", "context", "policies", "allowedFields", "policy", "field", "filtered", "buildEntityId", "entityKeys", "parts", "key", "value", "createUniformWeights", "count", "weight", "getEntityWeights", "bundle", "policyId", "entityId", "allocationCount", "policyState", "entityWeights", "globalWeights", "resolvePerEntityPolicy", "unitKeyValue", "entityConfig", "allocations", "countKey", "_", "i", "weights", "seed", "selectedIndex", "weightedSelection", "getUnitKeyValue", "resolveInternal", "defaults", "options", "assignments", "layers", "matchedPolicies", "projectUnitKeyValue", "requestedKeys", "params", "p", "param", "paramsByLayer", "existing", "layer", "layerParams", "hasParams", "layerUnitKey", "layerUnitValue", "bucket", "computeBucket", "matchedPolicy", "matchedAllocation", "start", "end", "evaluateConditions", "ctxAllocation", "resolveContextualPolicy", "result", "edgeResult", "allocation", "findMatchingAllocation", "resolveParameters", "decide", "filteredContext", "generateDecisionId", "DecisionDeduplicator", "_DecisionDeduplicator", "options", "__publicField", "assignments", "sortedKeys", "parts", "key", "value", "valueStr", "unitKey", "assignmentHash", "now", "lastSeen", "expiredKeys", "timestamp", "toRemove", "a", "b", "DecisionClient", "config", "__publicField", "request", "controller", "timeoutId", "url", "response", "error", "timeoutMs", "timeout", "requests", "createEdgeDecideRequest", "policyId", "entityKeys", "context", "unitKeyValue", "allocationCount", "parts", "key", "value", "entityId", "ErrorBoundary", "options", "tag", "fn", "fallback", "error", "resolvedError", "errorKey", "FAILED_EVENTS_KEY", "EventLogger", "options", "event", "events", "error", "controller", "timeoutId", "response", "body", "combined", "FAILED_EVENTS_KEY", "failed", "state", "STORAGE_KEY", "ExposureDeduplicator", "_ExposureDeduplicator", "options", "unitKey", "policyId", "variant", "key", "STORAGE_KEY", "state", "STORAGE_KEY", "COOKIE_NAME", "StableIdProvider", "options", "COOKIE_NAME", "id", "STORAGE_KEY", "c", "r", "cookies", "cookie", "name", "value", "STORAGE_PREFIX", "LocalStorageProvider", "key", "raw", "stored", "value", "ttlMs", "keysToRemove", "i", "testKey", "MemoryStorageProvider", "createStorageProvider", "localProvider", "localStorageAvailable", "SDK_VERSION", "SDK_NAME", "createDecisionTrackingPlugin", "options", "deps", "dedup", "DecisionDeduplicator", "decision", "unitKey", "hash", "event", "SDK_VERSION", "RDR_COOKIE", "setCookie", "name", "value", "maxAge", "createRedirectPlugin", "options", "parameterKey", "compareMode", "cookieName", "RDR_COOKIE", "client", "context", "decision", "url", "current", "layer", "l", "COOKIE_NAME", "readCookie", "name", "part", "k", "v", "parseAttribution", "cookieName", "expiryMs", "raw", "data", "createRedirectAttributionPlugin", "options", "COOKIE_NAME", "inject", "event", "attr", "_registryListeners", "_registryInstances", "getOrCreateRegistry", "registry", "cb", "l", "emitRegistryEvent", "event", "listener", "_idCounter", "generateId", "generateEventId", "PLUGIN_NAME", "createDebugPlugin", "options", "instanceId", "maxEvents", "_client", "_bundle", "_assignments", "_layers", "_lastDecisionId", "_effectiveUnitKey", "_events", "_stateListeners", "_eventListeners", "buildState", "notifyStateListeners", "state", "pushEvent", "type", "data", "triggerReDecide", "debugInstance", "SDK_VERSION", "limit", "key", "value", "current", "client", "bundle", "decision", "params", "PluginManager", "options", "plugin", "priority", "p", "a", "b", "name", "index", "client", "error", "bundle", "context", "result", "modified", "decision", "params", "event", "createBrowserLifecycleProvider", "listeners", "unloading", "notify", "state", "cb", "onPageHide", "onVisibilityChange", "onBeforeUnload", "callback", "idx", "SDK_NAME", "DEFAULT_BASE_URL", "DEFAULT_REFRESH_INTERVAL_MS", "DEFAULT_REQUEST_TIMEOUT_MS", "OFFLINE_WARNING_INTERVAL_MS", "DECISION_CACHE_MAX_SIZE", "TrafficalClient", "options", "evaluationMode", "decisionClientConfig", "DecisionClient", "ErrorBoundary", "createStorageProvider", "createBrowserLifecycleProvider", "warnings", "w", "v", "EventLogger", "error", "ExposureDeduplicator", "StableIdProvider", "PluginManager", "createDecisionTrackingPlugin", "event", "plugin", "instances", "idx", "result", "key", "value", "bundle", "context", "params", "resolveParameters", "resp", "assignments", "decision", "edgeOpts", "decide", "generateDecisionId", "unitKey", "layer", "generateExposureId", "SDK_VERSION", "eventName", "properties", "attribution", "decisionId", "generateTrackEventId", "name", "id", "cb", "l", "overrides", "snapshot", "type", "generateAssignmentId", "target", "keys", "k", "url", "headers", "controller", "timeoutId", "response", "etag", "edgeResults", "interval", "policies", "policy", "edgePolicies", "unitKeyValue", "getUnitKeyValue", "requests", "allocationCount", "createEdgeDecideRequest", "r", "responses", "i", "now", "firstKey", "userAttrs", "cachedDecision", "createTrafficalClient", "client", "createTrafficalClientSync", "createDOMBindingPlugin", "options", "config", "bindings", "lastParams", "observer", "debounceTimer", "matchesUrlPattern", "pattern", "path", "setProperty", "element", "property", "value", "styleProp", "applyBinding", "binding", "stringValue", "elements", "error", "apply", "params", "forceAll", "currentPath", "debouncedApply", "startObserving", "stopObserving", "bundle", "decision", "_instance", "init", "options", "createTrafficalClient", "initSync", "createTrafficalClientSync", "error", "instance", "destroy"]
|
|
7
7
|
}
|