@elu-labs/analytics-browser 1.410.1 → 1.410.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +92 -227
- package/NOTICE-ELU +15 -3
- package/README.md +55 -28
- package/THIRD_PARTY_NOTICES.txt +480 -9
- package/dist/cold-session.mjs.map +1 -1
- package/dist/elu.js +1 -1
- package/dist/elu.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/main.cjs +1 -1
- package/dist/main.cjs.map +1 -1
- package/dist/module.mjs +1 -1
- package/dist/module.mjs.map +1 -1
- package/dist/recorder.js.map +1 -1
- package/dist/recorder.mjs.map +1 -1
- package/dist/storage.js +1 -1
- package/dist/storage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cold-session.mjs","sources":["elu-js/packages/browser/scripts/runtime/deflated-text-data.ts","elu-js/packages/browser/src/elu-http-v1/validation.ts","elu-js/packages/browser/src/elu-http-v1/replay/schedule.ts","elu-js/packages/browser/src/elu-http-v1/transport.ts","elu-js/packages/browser/src/elu-http-v1/exact-timestamp.ts","elu-js/packages/browser/src/elu-http-v1/indexeddb-storage.ts","elu-js/packages/browser/src/elu-http-v1/replay/digest.ts","elu-js/packages/browser/src/elu-http-v1/durable-state.ts","../dependencies/fflate/0.4.8/fflate/esm/browser.js","elu-js/packages/browser/src/elu-http-v1/replay/errors.ts","elu-js/packages/browser/src/elu-http-v1/replay/base64.ts","elu-js/packages/browser/src/elu-http-v1/replay/canonical-payload-json.ts","elu-js/packages/browser/src/elu-http-v1/replay/masking-profile.ts","elu-js/packages/browser/src/elu-http-v1/replay/masking-proof.ts","elu-js/packages/browser/src/elu-http-v1/replay/codec.ts","elu-js/packages/browser/src/elu-http-v1/replay/dom-masker.ts","elu-js/packages/browser/src/elu-http-v1/replay/request.ts","elu-js/packages/browser/src/elu-http-v1/replay/seal-chunk.ts","elu-js/packages/browser/src/elu-http-v1/replay/session-driver.ts"],"sourcesContent":["/** Decodes build-owned ASCII JSON data. It never evaluates or imports source code. */\nexport const unpackV1DeflatedTextData = (encoded: string, expectedBytes: number, expectedCount: number): readonly string[] => {\n const invalid = (): never => { throw new Error('Invalid internal text data') }\n if (!Number.isSafeInteger(expectedBytes) || expectedBytes < 1 || expectedBytes > 1_000_000 || !Number.isSafeInteger(expectedCount) || expectedCount < 1 || expectedCount > 10_000 || encoded.length > 2_000_000 || encoded.length % 4 !== 0) invalid()\n const padding = encoded.endsWith('==') ? 2 : encoded.endsWith('=') ? 1 : 0\n const bytes = new Uint8Array((encoded.length / 4) * 3 - padding)\n const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\n let byteCount = 0\n let bits = 0\n let value = 0\n for (let index = 0; index < encoded.length - padding; index++) {\n const digit = alphabet.indexOf(encoded.charAt(index))\n if (digit < 0) invalid()\n value = (value << 6) | digit\n bits += 6\n if (bits >= 8) { bits -= 8; bytes[byteCount++] = (value >>> bits) & 255 }\n }\n if (bits && (value & ((1 << bits) - 1))) invalid()\n\n const output = new Uint8Array(expectedBytes)\n let written = 0\n let position = 0\n const read = (count: number): number => {\n if (position + count > bytes.length * 8) invalid()\n const offset = position >>> 3\n const result = ((bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16)) >>> (position & 7)) & ((1 << count) - 1)\n position += count\n return result\n }\n type Tree = { counts: Uint16Array; symbols: Uint16Array; maximum: number }\n // Code-length trees must be complete. Data trees may have one one-bit symbol;\n // the distance tree alone may be empty when the block contains no match.\n const tree = (lengths: Uint8Array, single: boolean, empty: boolean): Tree => {\n const counts = new Uint16Array(16)\n let maximum = 0\n for (let index = 0; index < lengths.length; index++) {\n const length = lengths[index]\n if (length > 15) invalid()\n if (length) { counts[length]++; maximum = Math.max(maximum, length) }\n }\n let remaining = 1\n for (let length = 1; length <= 15; length++) {\n remaining = remaining * 2 - counts[length]\n if (remaining < 0) invalid()\n }\n if (!maximum ? !empty : remaining && !(single && maximum === 1 && counts[1] === 1)) invalid()\n const offsets = new Uint16Array(16)\n for (let length = 1; length < 15; length++) offsets[length + 1] = offsets[length] + counts[length]\n const symbols = new Uint16Array(lengths.length)\n for (let symbol = 0; symbol < lengths.length; symbol++) {\n const length = lengths[symbol]\n if (length) symbols[offsets[length]++] = symbol\n }\n return { counts, symbols, maximum }\n }\n const symbol = (codes: Tree): number => {\n let code = 0\n let first = 0\n let index = 0\n for (let length = 1; length <= codes.maximum; length++) {\n code = (code << 1) | read(1)\n const count = codes.counts[length]\n if (code >= first && code - first < count) return codes.symbols[index + code - first]\n index += count\n first = (first + count) << 1\n }\n return invalid()\n }\n let final = 0\n let blocks = 0\n let fixedLiterals: Tree | undefined\n let fixedDistances: Tree | undefined\n while (!final) {\n // Build-owned tables allow at most 1,024 blocks, including empty blocks.\n if (++blocks > 1024) invalid()\n final = read(1)\n const kind = read(2)\n if (kind === 0) {\n // Stored blocks start at the next byte; these alignment bits carry no data.\n position = (position + 7) & ~7\n const length = read(16)\n if ((length ^ read(16)) !== 65535 || written + length > expectedBytes || position + length * 8 > bytes.length * 8) invalid()\n output.set(bytes.subarray(position >>> 3, (position >>> 3) + length), written)\n written += length\n position += length * 8\n } else {\n let literals: Tree\n let distances: Tree\n if (kind === 1) {\n if (!fixedLiterals || !fixedDistances) {\n const lengths = new Uint8Array(288)\n for (let index = 0; index < lengths.length; index++) lengths[index] = index < 144 ? 8 : index < 256 ? 9 : index < 280 ? 7 : 8\n fixedLiterals = tree(lengths, false, false)\n const distanceLengths = new Uint8Array(32)\n for (let index = 0; index < distanceLengths.length; index++) distanceLengths[index] = 5\n fixedDistances = tree(distanceLengths, false, false)\n }\n literals = fixedLiterals\n distances = fixedDistances\n } else if (kind === 2) {\n const literalCount = read(5) + 257\n const distanceCount = read(5) + 1\n const codeCount = read(4) + 4\n if (literalCount > 286) invalid()\n const order = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]\n const codeLengths = new Uint8Array(19)\n for (let index = 0; index < codeCount; index++) codeLengths[order[index]] = read(3)\n const codeTree = tree(codeLengths, false, false)\n const lengths = new Uint8Array(literalCount + distanceCount)\n let index = 0\n while (index < lengths.length) {\n const length = symbol(codeTree)\n if (length < 16) lengths[index++] = length\n else {\n if (length === 16 && !index) invalid()\n const repeated = length === 16 ? lengths[index - 1] : 0\n const count = length === 16 ? read(2) + 3 : length === 17 ? read(3) + 3 : read(7) + 11\n if (index + count > lengths.length) invalid()\n for (let end = index + count; index < end; index++) lengths[index] = repeated\n }\n }\n if (!lengths[256]) invalid()\n literals = tree(lengths.subarray(0, literalCount), true, false)\n distances = tree(lengths.subarray(literalCount), true, true)\n } else return invalid()\n for (;;) {\n const next = symbol(literals)\n if (next === 256) break\n if (next < 256) {\n if (written === expectedBytes) invalid()\n output[written++] = next\n } else {\n if (next > 285) invalid()\n const index = next - 257\n const extra = (index >>> 2) - 1\n const length = index < 8 ? index + 3 : index === 28 ? 258 : (((index & 3) + 4) << extra) + 3 + read(extra)\n const distanceSymbol = symbol(distances)\n if (distanceSymbol > 29) invalid()\n const distanceExtra = (distanceSymbol >>> 1) - 1\n const distance = distanceSymbol < 4 ? distanceSymbol + 1 : (((distanceSymbol & 1) + 2) << distanceExtra) + 1 + read(distanceExtra)\n if (distance > written || written + length > expectedBytes) invalid()\n // Overlapping matches deliberately read the bytes just written.\n for (let index = 0; index < length; index++) { output[written] = output[written - distance]; written++ }\n }\n }\n }\n }\n // A raw stream has no trailer. Ignore only its final byte's unused alignment bits.\n if (written !== expectedBytes || Math.ceil(position / 8) !== bytes.length) invalid()\n let decoded = ''\n // Keep native apply argument lists bounded on older supported browsers.\n for (let offset = 0; offset < written; offset += 4096) decoded += String.fromCharCode.apply(null, output.subarray(offset, Math.min(offset + 4096, written)) as unknown as number[])\n const texts: unknown = JSON.parse(decoded)\n if (!Array.isArray(texts) || texts.length !== expectedCount || texts.some(text => typeof text !== 'string')) invalid()\n return Object.freeze(texts as string[])\n}\n","const { isArray } = Array\nexport const isArrayValue = (value: unknown): value is unknown[] => isArray(value)\n\nexport const isRecordValue = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value != null && !isArrayValue(value)\n\nexport const isStringValue = (value: unknown): value is string => typeof value === 'string'\n\nexport const isBooleanValue = (value: unknown): value is boolean => value === true || value === false\n\nexport const isFunctionValue = (value: unknown): value is (...args: never[]) => unknown => {\n switch (typeof value) {\n case 'function':\n return true\n default:\n return false\n }\n}\n\nexport const unicodeCodePointLength = (value: string): number | null => {\n let length = 0\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index)\n if (code >= 0xd800 && code <= 0xdbff) {\n const next = value.charCodeAt(index + 1)\n if (index + 1 >= value.length || next < 0xdc00 || next > 0xdfff) {\n return null\n }\n index += 1\n } else if (code >= 0xdc00 && code <= 0xdfff) {\n return null\n }\n length += 1\n }\n return length\n}\n\nexport const isNumberValue = (value: unknown): value is number => {\n switch (typeof value) {\n case 'number':\n return Number.isFinite(value)\n default:\n return false\n }\n}\n\nexport const isNonNegativeInteger = (value: unknown): value is number =>\n isNumberValue(value) && Number.isSafeInteger(value) && value >= 0\n\nexport const isNullValue = (value: unknown): value is null => Object.is(value, null)\n\nexport const hasOwn = (value: Record<string, unknown>, key: string): boolean =>\n Object.prototype.hasOwnProperty.call(value, key)\n\nexport const hasOwnIndex = (value: readonly unknown[], index: number): boolean =>\n Object.prototype.hasOwnProperty.call(value, index)\n\nexport const hasOnlyKeys = (value: Record<string, unknown>, allowedKeys: readonly string[]): boolean => {\n const allowed = new Set(allowedKeys)\n return Object.keys(value).every((key) => allowed.has(key))\n}\n\nexport const utf8ByteLength = (value: string): number => {\n let bytes = 0\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index)\n if (code <= 0x7f) {\n bytes += 1\n } else if (code <= 0x7ff) {\n bytes += 2\n } else if (code >= 0xd800 && code <= 0xdbff && index + 1 < value.length) {\n const next = value.charCodeAt(index + 1)\n if (next >= 0xdc00 && next <= 0xdfff) {\n bytes += 4\n index += 1\n } else {\n bytes += 3\n }\n } else {\n bytes += 3\n }\n }\n return bytes\n}\n\nexport const parseBoundedJson = (\n rawJson: string,\n maximumBytes: number,\n field: string\n): { readonly value: unknown; readonly serialized: string } => {\n if (!isStringValue(rawJson)) {\n throw new Error(`${field} must be raw JSON text`)\n }\n if (utf8ByteLength(rawJson) > maximumBytes) {\n throw new Error(`${field} exceeds the ${maximumBytes}-byte limit`)\n }\n\n let value: unknown\n try {\n value = JSON.parse(rawJson)\n } catch {\n throw new Error(`${field} must be valid JSON`)\n }\n\n const serialized = JSON.stringify(value)\n if (!isStringValue(serialized)) {\n throw new Error(`${field} must be a JSON value`)\n }\n return { value, serialized }\n}\n\n/**\n * Shares native freezing without caching the intrinsic or changing its overloads.\n * Assumes native Object.freeze semantics; every value is still frozen exactly once.\n */\nexport const freezeV1Value: typeof Object.freeze = <T>(value: T): Readonly<T> => Object.freeze(value)\n","import { freezeV1Value } from '../validation'\n\n/**\n * When one recorded stretch ends and the next begins.\n *\n * Every boundary here comes from the configuration document rather than from a constant: how long a\n * session may idle, how long it may run, how short a replay is worth keeping, and how many bytes a\n * chunk may carry. The state machine is pure — it is given each event's instant and size and returns\n * what to do — so a boundary is a decision that can be read back rather than a timer that fired.\n */\n\nexport interface V2ReplaySchedulePolicy {\n /** No activity for this long ends the session. */\n readonly idleTimeoutSeconds: number\n /** A session may not run longer than this, however active it is. */\n readonly maximumDurationSeconds: number\n /** A replay shorter than this is not worth delivering. */\n readonly minimumDurationSeconds: number\n /** The chunk ceiling from the document, in bytes. */\n readonly replayChunkBytes: number\n}\n\nexport interface V2ReplayScheduleState {\n /** Null before the first event of a session. */\n readonly sessionStartedAtMs: number | null\n readonly lastActivityAtMs: number | null\n readonly chunkStartedAtMs: number | null\n readonly chunkBytes: number\n readonly chunkEvents: number\n readonly sequence: number\n}\n\nexport type V2ReplayScheduleAction =\n | /** Add the event to the open chunk. */ 'buffer'\n | /** Seal the open chunk, then start a new one holding this event. */ 'cut-chunk'\n | /** Seal the open chunk and end the session; this event opens a new replay. */ 'cut-session'\n\nexport interface V2ReplayScheduleDecision {\n readonly action: V2ReplayScheduleAction\n /** True when the stretch being sealed is too short to be worth delivering. */\n readonly discardSealedChunk: boolean\n readonly state: V2ReplayScheduleState\n}\n\nexport const initialV2ReplayScheduleState = (): V2ReplayScheduleState =>\n freezeV1Value({\n sessionStartedAtMs: null,\n lastActivityAtMs: null,\n chunkStartedAtMs: null,\n chunkBytes: 0,\n chunkEvents: 0,\n sequence: 0,\n })\n\nconst openChunk = (state: V2ReplayScheduleState, atMs: number, bytes: number, sequence: number) =>\n freezeV1Value({\n sessionStartedAtMs: state.sessionStartedAtMs,\n lastActivityAtMs: atMs,\n chunkStartedAtMs: atMs,\n chunkBytes: bytes,\n chunkEvents: 1,\n sequence,\n })\n\n/**\n * Decides where one event falls.\n *\n * The order of the checks is the policy: a session that has idled out or run its maximum ends before\n * anything else is considered, because a chunk cut inside a session that should already have closed\n * would carry the new activity under the old replay's identity.\n */\nexport const advanceV2ReplaySchedule = (\n state: V2ReplayScheduleState,\n event: { readonly atMs: number; readonly byteLength: number },\n policy: V2ReplaySchedulePolicy\n): V2ReplayScheduleDecision => {\n const { atMs, byteLength } = event\n\n // The first event of a session opens it; there is nothing to seal.\n if (state.sessionStartedAtMs === null || state.lastActivityAtMs === null) {\n return freezeV1Value({\n action: 'buffer' as const,\n discardSealedChunk: false,\n state: freezeV1Value({\n ...openChunk(state, atMs, byteLength, 0),\n sessionStartedAtMs: atMs,\n }),\n })\n }\n\n const idleForMs = atMs - state.lastActivityAtMs\n const sessionAgeMs = atMs - state.sessionStartedAtMs\n const endsSession =\n idleForMs >= policy.idleTimeoutSeconds * 1000 || sessionAgeMs >= policy.maximumDurationSeconds * 1000\n\n if (endsSession) {\n // What is sealed is the stretch that just ended, so its length decides whether it is kept.\n const sealedMs = state.lastActivityAtMs - (state.sessionStartedAtMs ?? state.lastActivityAtMs)\n return freezeV1Value({\n action: 'cut-session' as const,\n discardSealedChunk: sealedMs < policy.minimumDurationSeconds * 1000,\n state: freezeV1Value({\n ...openChunk(state, atMs, byteLength, 0),\n sessionStartedAtMs: atMs,\n }),\n })\n }\n\n // A chunk that cannot hold this event is sealed and the event opens the next one. An event\n // larger than the whole ceiling still gets its own chunk rather than being dropped here; the\n // sealing stage refuses it, which is where a payload that cannot be sent belongs.\n if (state.chunkEvents > 0 && state.chunkBytes + byteLength > policy.replayChunkBytes) {\n return freezeV1Value({\n action: 'cut-chunk' as const,\n discardSealedChunk: false,\n state: openChunk(state, atMs, byteLength, state.sequence + 1),\n })\n }\n\n return freezeV1Value({\n action: 'buffer' as const,\n discardSealedChunk: false,\n state: freezeV1Value({\n ...state,\n lastActivityAtMs: atMs,\n chunkStartedAtMs: state.chunkStartedAtMs ?? atMs,\n chunkBytes: state.chunkBytes + byteLength,\n chunkEvents: state.chunkEvents + 1,\n }),\n })\n}\n","import type {\n V1BatchRequest,\n V1BeaconPostRequest,\n V1ConfigDocumentVersion,\n V1ConfigHttpRequest,\n V1EndpointName,\n V1EndpointSet,\n V1FlagsRequest,\n V1NormalPostRequest,\n V1PostRequest,\n V1ReplayRequest,\n V1ValidatedEndpointSet,\n} from './types'\nimport {\n freezeV1Value,\n hasOnlyKeys,\n hasOwn,\n isNullValue,\n isRecordValue,\n isStringValue,\n unicodeCodePointLength,\n} from './validation'\n\nexport const V1_CONFIG_ORIGIN = 'https://elu.dev'\nconst INGEST_ORIGIN = 'https://ingest.elu.dev'\nconst ASSETS_ORIGIN = 'https://assets.elu.dev'\nconst SITE_KEY_QUERY_PARAMETER = 'site_key'\nconst BEACON_CONTENT_TYPE = 'text/plain;charset=UTF-8'\nconst MALFORMED_PERCENT_ESCAPE_PATTERN = /%(?![0-9A-Fa-f]{2})/\nconst RFC3986_FORBIDDEN_RAW_CHARACTER_PATTERN = /[\"<>\\\\^`{|}]/\nconst ABSOLUTE_URL_RAW_PATH_PATTERN = /^[A-Za-z][A-Za-z0-9+.-]*:\\/\\/[^/?#]+([^?#]*)/\n\nconst hasCharacterOutsideVisibleAscii = (value: string): boolean => {\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index)\n if (code <= 0x20 || code >= 0x7f) {\n return true\n }\n }\n return false\n}\n\nexport const V1_ENDPOINT_ORIGIN_ALLOWLIST: Readonly<Record<V1EndpointName, readonly string[]>> = freezeV1Value({\n events: freezeV1Value([INGEST_ORIGIN]),\n replay: freezeV1Value([INGEST_ORIGIN]),\n flags: freezeV1Value([INGEST_ORIGIN]),\n assets: freezeV1Value([ASSETS_ORIGIN]),\n})\nexport const V1_ENDPOINT_PATHS: Readonly<Record<V1EndpointName, string>> = freezeV1Value({\n events: '/v1/events',\n replay: '/v1/replay',\n flags: '/v1/flags',\n assets: '/sdk/',\n})\n/**\n * Config v2 keeps the v1 event and flag roles and moves replay to its own major. The standalone\n * replay endpoint does not accept replay v1, so a v2 document must never advertise the v1 path and a\n * v1 document must never advertise the v2 path.\n */\nexport const V2_ENDPOINT_PATHS: Readonly<Record<V1EndpointName, string>> = freezeV1Value({\n events: '/v1/events',\n replay: '/v2/replay',\n flags: '/v1/flags',\n assets: '/sdk/',\n})\nconst ENDPOINT_PATHS_BY_CONFIG_VERSION: Readonly<\n Record<V1ConfigDocumentVersion, Readonly<Record<V1EndpointName, string>>>\n> = freezeV1Value({ 1: V1_ENDPOINT_PATHS, 2: V2_ENDPOINT_PATHS })\nconst CONFIG_PATH_PREFIX_BY_VERSION: Readonly<Record<V1ConfigDocumentVersion, string>> = freezeV1Value({\n 1: '/sdk/v1/',\n 2: '/sdk/v2/',\n})\n\nexport class V1TransportConfigurationError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'V1TransportConfigurationError'\n }\n}\n\nconst assertSiteKey = (siteKey: string): void => {\n if (!isStringValue(siteKey) || siteKey.length === 0) {\n throw new V1TransportConfigurationError('site key must be a non-empty string')\n }\n if (isNullValue(unicodeCodePointLength(siteKey))) {\n throw new V1TransportConfigurationError('site key must not contain an unpaired Unicode surrogate')\n }\n if (siteKey === '.' || siteKey === '..') {\n throw new V1TransportConfigurationError('site key must not be a URL path dot segment')\n }\n for (let index = 0; index < siteKey.length; index += 1) {\n const codePoint = siteKey.charCodeAt(index)\n if (codePoint <= 31 || codePoint === 127) {\n throw new V1TransportConfigurationError('site key must not contain ASCII control characters')\n }\n }\n}\n\n/** Validates a site key from an authorized delivery snapshot. */\nexport const validateV1SiteKey = (value: unknown): string => {\n if (!isStringValue(value)) {\n throw new V1TransportConfigurationError('site key must be a string')\n }\n assertSiteKey(value)\n return value\n}\n\nconst validateEndpoint = (\n name: V1EndpointName,\n value: unknown,\n expectedPath: string = V1_ENDPOINT_PATHS[name]\n): string => {\n if (!isStringValue(value)) {\n throw new V1TransportConfigurationError(`${name} endpoint must be a string`)\n }\n if (isNullValue(unicodeCodePointLength(value))) {\n throw new V1TransportConfigurationError(`${name} endpoint must not contain an unpaired Unicode surrogate`)\n }\n if (hasCharacterOutsideVisibleAscii(value)) {\n throw new V1TransportConfigurationError(\n `${name} endpoint must contain only visible ASCII characters; encode non-ASCII bytes`\n )\n }\n if (MALFORMED_PERCENT_ESCAPE_PATTERN.test(value)) {\n throw new V1TransportConfigurationError(`${name} endpoint contains a malformed percent escape`)\n }\n if (RFC3986_FORBIDDEN_RAW_CHARACTER_PATTERN.test(value)) {\n throw new V1TransportConfigurationError(`${name} endpoint contains a character forbidden in an RFC 3986 URI`)\n }\n\n // URL parsers normalize dot segments before exposing pathname. Compare the configured bytes so\n // a path such as /v1/../v1/events cannot acquire an allowlisted path through normalization.\n const rawPath = ABSOLUTE_URL_RAW_PATH_PATTERN.exec(value)?.[1]\n if (rawPath !== expectedPath) {\n throw new V1TransportConfigurationError(`${name} endpoint path is not allowlisted`)\n }\n\n let parsed: URL\n try {\n // The trust check requires native absolute-URL parsing and fails closed when it is unavailable.\n // eslint-disable-next-line compat/compat\n parsed = new URL(value)\n } catch {\n throw new V1TransportConfigurationError(`${name} endpoint must be an absolute URL`)\n }\n\n if (parsed.protocol !== 'https:') {\n throw new V1TransportConfigurationError(`${name} endpoint must use HTTPS`)\n }\n if (parsed.username || parsed.password) {\n throw new V1TransportConfigurationError(`${name} endpoint must not contain user information`)\n }\n if (!V1_ENDPOINT_ORIGIN_ALLOWLIST[name].includes(parsed.origin)) {\n throw new V1TransportConfigurationError(`${name} endpoint origin is not allowlisted`)\n }\n if (parsed.pathname !== expectedPath) {\n throw new V1TransportConfigurationError(`${name} endpoint path is not allowlisted`)\n }\n if (parsed.searchParams.has(SITE_KEY_QUERY_PARAMETER)) {\n throw new V1TransportConfigurationError(`${name} endpoint must not define the reserved site_key parameter`)\n }\n if (value.includes('#')) {\n throw new V1TransportConfigurationError(`${name} endpoint must not contain a fragment`)\n }\n\n return parsed.href\n}\n\n/** Validates an event endpoint from an authorized delivery snapshot. */\nexport const validateV1EventsEndpoint = (value: unknown): string => validateEndpoint('events', value)\n\n/** Validates the standalone replay role of a v2 config; the v1 replay path is not interchangeable. */\nexport const validateV2ReplayEndpoint = (value: unknown): string =>\n validateEndpoint('replay', value, V2_ENDPOINT_PATHS.replay)\n\n/** Validates only the flags transport role for the independent flag authorization projection. */\nexport const validateV1FlagsEndpoint = (value: unknown): string => validateEndpoint('flags', value)\n\n/** Validates the configured asset base; this is an endpoint check, not loading authority. */\nexport const validateV1AssetsEndpoint = (value: unknown): string => validateEndpoint('assets', value)\n\n/**\n * Resolves transport endpoints from a config that has already passed the config manager's\n * complete schema, status, expiry, privacy, and feature-gate checks. This function validates\n * only exact endpoint shape, role path, and origin; it is not a config authorization decision.\n */\nconst validateEnabledConfigEndpoints = (\n value: unknown,\n configVersion: V1ConfigDocumentVersion\n): V1ValidatedEndpointSet => {\n if (!isRecordValue(value)) {\n throw new V1TransportConfigurationError('enabled config endpoint set must be an object')\n }\n if (!hasOnlyKeys(value, ['events', 'replay', 'flags', 'assets'])) {\n throw new V1TransportConfigurationError('enabled config endpoint set contains an unsupported endpoint')\n }\n const paths = ENDPOINT_PATHS_BY_CONFIG_VERSION[configVersion]\n\n const endpoints: V1EndpointSet = {\n events: validateEndpoint('events', value.events, paths.events),\n flags: validateEndpoint('flags', value.flags, paths.flags),\n ...(hasOwn(value, 'replay') ? { replay: validateEndpoint('replay', value.replay, paths.replay) } : {}),\n ...(hasOwn(value, 'assets') ? { assets: validateEndpoint('assets', value.assets, paths.assets) } : {}),\n }\n return freezeV1Value(endpoints) as V1ValidatedEndpointSet\n}\n\nexport const validateV1EnabledConfigEndpoints = (value: unknown): V1ValidatedEndpointSet =>\n validateEnabledConfigEndpoints(value, 1)\n\n/** The v2 endpoint set differs from v1 only in the replay role, which must use the v2 replay path. */\nexport const validateV2EnabledConfigEndpoints = (value: unknown): V1ValidatedEndpointSet =>\n validateEnabledConfigEndpoints(value, 2)\n\nconst serializeBody = (body: unknown): string => {\n const serialized = JSON.stringify(body)\n if (!isStringValue(serialized)) {\n throw new V1TransportConfigurationError('request body must be JSON serializable')\n }\n return serialized\n}\n\nconst resolvePostEndpoint = (endpoints: V1ValidatedEndpointSet, name: 'events' | 'replay' | 'flags'): string => {\n const endpoint = endpoints[name]\n if (!endpoint) {\n throw new V1TransportConfigurationError(`${name} endpoint is not advertised by the enabled config`)\n }\n return endpoint\n}\n\nconst buildNormalPostRequest = (siteKey: string, endpoint: string, body: unknown): V1NormalPostRequest => {\n assertSiteKey(siteKey)\n const headers: V1NormalPostRequest['init']['headers'] = freezeV1Value({\n Authorization: `Bearer ${siteKey}`,\n 'Content-Type': 'application/json',\n })\n const init: V1NormalPostRequest['init'] = freezeV1Value({\n method: 'POST',\n mode: 'cors',\n credentials: 'omit',\n headers,\n body: serializeBody(body),\n })\n return freezeV1Value({ delivery: 'normal', url: endpoint, init })\n}\n\nconst buildBeaconPostRequest = (siteKey: string, endpoint: string, body: unknown): V1BeaconPostRequest => {\n assertSiteKey(siteKey)\n // Config endpoint validation already required native URL parsing and an exact allowlisted origin.\n // eslint-disable-next-line compat/compat\n const url = new URL(endpoint)\n url.searchParams.set(SITE_KEY_QUERY_PARAMETER, siteKey)\n return freezeV1Value({\n delivery: 'beacon',\n url: url.href,\n body: new Blob([serializeBody(body)], { type: BEACON_CONTENT_TYPE }),\n })\n}\n\nconst buildPostRequest = (\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n endpointName: 'events' | 'replay',\n body: unknown,\n delivery: 'normal' | 'beacon'\n): V1PostRequest => {\n const endpoint = resolvePostEndpoint(endpoints, endpointName)\n return delivery === 'beacon'\n ? buildBeaconPostRequest(siteKey, endpoint, body)\n : buildNormalPostRequest(siteKey, endpoint, body)\n}\n\nconst buildConfigRequest = (siteKey: string, configVersion: V1ConfigDocumentVersion): V1ConfigHttpRequest => {\n assertSiteKey(siteKey)\n const encodedSiteKey = encodeURIComponent(siteKey)\n const init: V1ConfigHttpRequest['init'] = freezeV1Value({\n method: 'GET',\n mode: 'cors',\n credentials: 'omit',\n })\n return freezeV1Value({\n delivery: 'normal',\n url: `${V1_CONFIG_ORIGIN}${CONFIG_PATH_PREFIX_BY_VERSION[configVersion]}${encodedSiteKey}/config`,\n init,\n })\n}\n\nexport const buildV1ConfigRequest = (siteKey: string): V1ConfigHttpRequest => buildConfigRequest(siteKey, 1)\n\n/** The owned standalone handshake. The site key is the only credential and travels in the path. */\nexport const buildV2ConfigRequest = (siteKey: string): V1ConfigHttpRequest => buildConfigRequest(siteKey, 2)\n\nexport function buildV1EventsRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1BatchRequest\n): V1NormalPostRequest\nexport function buildV1EventsRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1BatchRequest,\n delivery: 'normal'\n): V1NormalPostRequest\nexport function buildV1EventsRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1BatchRequest,\n delivery: 'beacon'\n): V1BeaconPostRequest\nexport function buildV1EventsRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1BatchRequest,\n delivery: 'normal' | 'beacon' = 'normal'\n): V1PostRequest {\n return buildPostRequest(siteKey, endpoints, 'events', request, delivery)\n}\n\n/** Builds a request from an already-authorized event endpoint snapshot. */\nexport function buildV1EventsRequestForEndpoint(\n siteKey: string,\n eventsEndpoint: string,\n request: V1BatchRequest,\n delivery: 'normal'\n): V1NormalPostRequest\nexport function buildV1EventsRequestForEndpoint(\n siteKey: string,\n eventsEndpoint: string,\n request: V1BatchRequest,\n delivery: 'beacon'\n): V1BeaconPostRequest\nexport function buildV1EventsRequestForEndpoint(\n siteKey: string,\n eventsEndpoint: string,\n request: V1BatchRequest,\n delivery: 'normal' | 'beacon'\n): V1PostRequest {\n const endpoint = validateV1EventsEndpoint(eventsEndpoint)\n return delivery === 'beacon'\n ? buildBeaconPostRequest(siteKey, endpoint, request)\n : buildNormalPostRequest(siteKey, endpoint, request)\n}\n\nexport function buildV1ReplayRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1ReplayRequest\n): V1NormalPostRequest\nexport function buildV1ReplayRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1ReplayRequest,\n delivery: 'normal'\n): V1NormalPostRequest\nexport function buildV1ReplayRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1ReplayRequest,\n delivery: 'beacon'\n): V1BeaconPostRequest\nexport function buildV1ReplayRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1ReplayRequest,\n delivery: 'normal' | 'beacon' = 'normal'\n): V1PostRequest {\n return buildPostRequest(siteKey, endpoints, 'replay', request, delivery)\n}\n\nexport const buildV1FlagsRequest = (\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1FlagsRequest\n): V1NormalPostRequest => buildNormalPostRequest(siteKey, resolvePostEndpoint(endpoints, 'flags'), request)\n","import { freezeV1Value, isNullValue, isNumberValue, isStringValue, unicodeCodePointLength } from './validation'\n\nconst RFC3339_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})[Tt](\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?(?:[Zz]|([+-])(\\d{2}):(\\d{2}))$/\nconst SECONDS_PER_DAY = 86_400\n\nexport interface V1ExactTimestamp {\n readonly source: string\n readonly epochDay: number\n readonly secondOfDay: number\n /** True when source used RFC 3339 second 60; secondOfDay names the preceding :59 second. */\n readonly leapSecond: boolean\n readonly fractionalDigits: string\n}\n\nconst fail = (field: string, message: string): never => {\n throw new Error(`${field} ${message}`)\n}\n\nconst daysInMonth = (year: number, month: number): number => {\n const date = new Date(0)\n date.setUTCFullYear(year, month, 0)\n return date.getUTCDate()\n}\n\nconst splitEpochSecond = (epochSecond: number): Readonly<{ epochDay: number; secondOfDay: number }> => {\n const epochDay = Math.floor(epochSecond / SECONDS_PER_DAY)\n return freezeV1Value({ epochDay, secondOfDay: epochSecond - epochDay * SECONDS_PER_DAY })\n}\n\nexport const parseV1ExactTimestamp = (\n value: unknown,\n field: string,\n maximumUnicodeScalars = 65_536\n): V1ExactTimestamp => {\n if (!isStringValue(value)) return fail(field, 'must be an RFC 3339 date-time string')\n const scalarLength = unicodeCodePointLength(value)\n if (isNullValue(scalarLength) || scalarLength < 1 || scalarLength > maximumUnicodeScalars) {\n return fail(field, `must contain between 1 and ${maximumUnicodeScalars} Unicode scalars`)\n }\n const match = RFC3339_PATTERN.exec(value)\n if (!match) return fail(field, 'must be an RFC 3339 date-time')\n const year = Number(match[1])\n const month = Number(match[2])\n const day = Number(match[3])\n const hour = Number(match[4])\n const minute = Number(match[5])\n const second = Number(match[6])\n const fractionalDigits = (match[7] ?? '').replace(/0+$/, '')\n const offsetHour = match[9] ? Number(match[9]) : 0\n const offsetMinute = match[10] ? Number(match[10]) : 0\n if (\n month < 1 ||\n month > 12 ||\n day < 1 ||\n day > daysInMonth(year, month) ||\n hour > 23 ||\n minute > 59 ||\n second > 60 ||\n offsetHour > 23 ||\n offsetMinute > 59\n ) {\n fail(field, 'must be a valid RFC 3339 date-time')\n }\n\n const local = new Date(0)\n local.setUTCFullYear(year, month - 1, day)\n local.setUTCHours(hour, minute, Math.min(second, 59), 0)\n const localEpochSecond = local.getTime() / 1000\n const offsetDirection = match[8] === '-' ? -1 : 1\n const offsetSeconds = offsetDirection * (offsetHour * 60 + offsetMinute) * 60\n const epochSecond = localEpochSecond - offsetSeconds\n if (!isNumberValue(epochSecond) || !Number.isSafeInteger(epochSecond)) {\n fail(field, 'is outside the supported epoch range')\n }\n\n const leapSecond = second === 60\n if (leapSecond) {\n const nextUtcSecond = new Date((epochSecond + 1) * 1000)\n const isLeapBoundary =\n nextUtcSecond.getUTCHours() === 0 &&\n nextUtcSecond.getUTCMinutes() === 0 &&\n nextUtcSecond.getUTCSeconds() === 0 &&\n nextUtcSecond.getUTCDate() === 1 &&\n (nextUtcSecond.getUTCMonth() === 0 || nextUtcSecond.getUTCMonth() === 6)\n if (!isLeapBoundary) fail(field, 'must use second 60 only at a shifted UTC June or December leap boundary')\n }\n\n const split = splitEpochSecond(epochSecond)\n return freezeV1Value({ source: value, ...split, leapSecond, fractionalDigits })\n}\n\nexport const compareV1ExactTimestamps = (left: V1ExactTimestamp, right: V1ExactTimestamp): number => {\n if (left.epochDay !== right.epochDay) return left.epochDay < right.epochDay ? -1 : 1\n if (left.secondOfDay !== right.secondOfDay) return left.secondOfDay < right.secondOfDay ? -1 : 1\n if (left.leapSecond !== right.leapSecond) return left.leapSecond ? 1 : -1\n const maximumDigits = Math.max(left.fractionalDigits.length, right.fractionalDigits.length)\n for (let index = 0; index < maximumDigits; index += 1) {\n const leftDigit = left.fractionalDigits.charCodeAt(index) || 48\n const rightDigit = right.fractionalDigits.charCodeAt(index) || 48\n if (leftDigit !== rightDigit) return leftDigit < rightDigit ? -1 : 1\n }\n return 0\n}\n\nexport const v1ExactTimestampToApproximateMs = (value: V1ExactTimestamp): number =>\n (value.epochDay * SECONDS_PER_DAY + value.secondOfDay + (value.leapSecond ? 1 : 0)) * 1000 +\n Number((value.fractionalDigits + '000').slice(0, 3))\n\nexport const assertV1WallNow = (nowMs: number, field = 'nowMs'): void => {\n if (!isNumberValue(nowMs) || !Number.isSafeInteger(nowMs) || !Number.isFinite(new Date(nowMs).getTime())) {\n fail(field, 'must be a valid integer epoch timestamp')\n }\n}\n\nexport const v1ExactTimestampFromEpochMs = (nowMs: number, field = 'nowMs'): V1ExactTimestamp => {\n assertV1WallNow(nowMs, field)\n const epochSecond = Math.floor(nowMs / 1000)\n const milliseconds = nowMs - epochSecond * 1000\n const split = splitEpochSecond(epochSecond)\n return freezeV1Value({\n source: new Date(nowMs).toISOString(),\n ...split,\n leapSecond: false,\n fractionalDigits: String(milliseconds).padStart(3, '0').replace(/0+$/, ''),\n })\n}\n\nexport const sameV1ExactTimestamp = (left: V1ExactTimestamp, right: V1ExactTimestamp): boolean =>\n compareV1ExactTimestamps(left, right) === 0\n","/* eslint-disable compat/compat, elu-client/no-direct-null-check -- Native IndexedDB is asynchronous and this runtime fails closed without Promise; its open state uses exact null checks. */\n\nimport type {\n V1DurableCommitOutcome,\n V1DurableRecordVisitor,\n V1DurableStorage,\n V1DurableStorageMode,\n V1DurableStorageTransaction,\n} from './durable-storage'\nimport { V1DurableStorageError } from './durable-storage'\nimport { freezeV1Value, isArrayValue } from './validation'\nimport { initializeV1IndexedDBConnection } from './indexeddb-initialization'\n\nexport const V1_INDEXED_DB_NAME = 'elu-sdk-v1'\n/** Schema versions of the shared database: base stores, plus the flag cache, plus the replay stores. */\nexport const V1_INDEXED_DB_VERSION = 1\nexport const V1_INDEXED_DB_FLAG_VERSION = 2\nexport const V1_INDEXED_DB_REPLAY_VERSION = 3\n\nexport const V1_INSTALLATIONS_STORE = 'installations'\nexport const V1_RECORDS_STORE = 'records'\nexport const V1_FLAG_CACHE_STORE = 'flagCache'\nexport const V2_REPLAY_CHUNKS_STORE = 'replayChunks'\nexport const V2_REPLAY_STREAMS_STORE = 'replayStreams'\nexport const V2_REPLAY_RESOLUTIONS_STORE = 'replayResolutions'\n/** Unique index on [siteKey, replayId, chunkId] over live replay chunks. */\nexport const V2_REPLAY_CHUNK_INDEX = 'chunk'\n/** Unique index on [siteKey, replayId, sequence] over live chunks and over retained resolutions. */\nexport const V2_REPLAY_SEQUENCE_INDEX = 'sequence'\nconst INSTALLATIONS_STORE = V1_INSTALLATIONS_STORE\nconst RECORDS_STORE = V1_RECORDS_STORE\nconst STORE_NAMES = [INSTALLATIONS_STORE, RECORDS_STORE] as const\n\nconst requestResult = <T>(request: IDBRequest<T>): Promise<T> =>\n new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result)\n request.onerror = () => reject(request.error ?? new Error('IndexedDB request failed'))\n })\n\ntype V1IndexedDBCompletion = Readonly<{ status: 'committed' }> | Readonly<{ status: 'aborted'; error: Error }>\n\nconst transactionCompletion = (transaction: IDBTransaction): Promise<V1IndexedDBCompletion> =>\n new Promise((resolve) => {\n let observedError: Error | null = null\n transaction.oncomplete = () => resolve(freezeV1Value({ status: 'committed' }))\n transaction.onerror = () => {\n observedError = transaction.error ?? new Error('IndexedDB transaction failed')\n }\n transaction.onabort = () =>\n resolve(\n freezeV1Value({\n status: 'aborted',\n error: transaction.error ?? observedError ?? new Error('IndexedDB transaction aborted'),\n })\n )\n })\n\nconst keyPathMatches = (actual: string | string[] | null, expected: string | readonly string[]): boolean => {\n if (typeof expected === 'string') {\n return actual === expected\n }\n if (!isArrayValue(actual) || actual.length !== expected.length) {\n return false\n }\n return expected.every((part, index) => actual[index] === part)\n}\n\nclass V1NativeIndexedDBTransaction implements V1DurableStorageTransaction {\n private readonly _installations: IDBObjectStore\n private readonly _records: IDBObjectStore\n private readonly _keyRange: typeof IDBKeyRange\n\n constructor(transaction: IDBTransaction, keyRange: typeof IDBKeyRange) {\n this._installations = transaction.objectStore(INSTALLATIONS_STORE)\n this._records = transaction.objectStore(RECORDS_STORE)\n this._keyRange = keyRange\n }\n\n getInstallation(siteKey: string): Promise<unknown | undefined> {\n return requestResult(this._installations.get(siteKey))\n }\n\n async addInstallation(value: unknown): Promise<void> {\n await requestResult(this._installations.add(value))\n }\n\n async putInstallation(value: unknown): Promise<void> {\n await requestResult(this._installations.put(value))\n }\n\n scanAllRecords(siteKey: string, visit: V1DurableRecordVisitor): Promise<void> {\n return this._scan(\n this._records.openCursor(this._keyRange.bound([siteKey], [siteKey, []])),\n Number.MAX_SAFE_INTEGER,\n visit\n )\n }\n\n scanRecordPrefix(\n siteKey: string,\n startSequence: number,\n maximumCount: number,\n visit: V1DurableRecordVisitor\n ): Promise<void> {\n if (maximumCount === 0) {\n return Promise.resolve()\n }\n return this._scan(\n this._records.openCursor(\n this._keyRange.bound([siteKey, startSequence], [siteKey, Number.MAX_SAFE_INTEGER])\n ),\n maximumCount,\n visit\n )\n }\n\n async addRecord(value: unknown): Promise<void> {\n await requestResult(this._records.add(value))\n }\n\n async deleteRecord(siteKey: string, sequence: number): Promise<void> {\n await requestResult(this._records.delete([siteKey, sequence]))\n }\n\n private _scan(\n request: IDBRequest<IDBCursorWithValue | null>,\n maximumCount: number,\n visit: V1DurableRecordVisitor\n ): Promise<void> {\n return new Promise((resolve, reject) => {\n let visited = 0\n request.onerror = () => reject(request.error ?? new Error('IndexedDB cursor failed'))\n request.onsuccess = () => {\n const cursor = request.result\n if (!cursor || visited >= maximumCount) {\n resolve()\n return\n }\n try {\n visited += 1\n if (!visit(cursor.value) || visited >= maximumCount) {\n resolve()\n return\n }\n cursor.continue()\n } catch (error) {\n reject(error)\n }\n }\n })\n }\n}\n\nclass V1NativeIndexedDBStorage implements V1DurableStorage {\n private readonly _database: IDBDatabase\n private readonly _keyRange: typeof IDBKeyRange\n private _closed = false\n\n constructor(database: IDBDatabase, keyRange: typeof IDBKeyRange) {\n this._database = database\n this._keyRange = keyRange\n this._database.onversionchange = () => this.close()\n }\n\n async runTransaction<T>(\n mode: V1DurableStorageMode,\n operation: (transaction: V1DurableStorageTransaction) => Promise<T>\n ): Promise<T> {\n if (this._closed) {\n throw new V1DurableStorageError('closed', 'IndexedDB storage is closed')\n }\n\n if (mode === 'readwrite') {\n const outcome = await this.runTransactionWithCommitOutcome(operation)\n if (outcome.status === 'committed') return outcome.value\n throw outcome.error\n }\n\n let transaction: IDBTransaction\n try {\n transaction = this._database.transaction([...STORE_NAMES], mode)\n } catch {\n throw new V1DurableStorageError('unavailable', 'IndexedDB transaction could not be opened')\n }\n const completion = transactionCompletion(transaction)\n try {\n const result = await operation(new V1NativeIndexedDBTransaction(transaction, this._keyRange))\n const outcome = await completion\n if (outcome.status === 'committed') return result\n throw outcome.error\n } catch (error) {\n try {\n transaction.abort()\n } catch {\n // The transaction may already have aborted or completed.\n }\n await completion\n throw error\n }\n }\n\n async runTransactionWithCommitOutcome<T>(\n operation: (transaction: V1DurableStorageTransaction) => Promise<T>\n ): Promise<V1DurableCommitOutcome<T>> {\n if (this._closed) {\n return freezeV1Value({\n status: 'proven-not-committed',\n error: new V1DurableStorageError('closed', 'IndexedDB storage is closed'),\n })\n }\n\n let transaction: IDBTransaction\n try {\n transaction = this._database.transaction([...STORE_NAMES], 'readwrite')\n } catch {\n return freezeV1Value({\n status: 'proven-not-committed',\n error: new V1DurableStorageError('unavailable', 'IndexedDB transaction could not be opened'),\n })\n }\n const completion = transactionCompletion(transaction)\n let value: T\n try {\n value = await operation(new V1NativeIndexedDBTransaction(transaction, this._keyRange))\n } catch (error) {\n try {\n transaction.abort()\n } catch {\n // A transaction that completed while the callback awaited unrelated work is ambiguous.\n }\n const completed = await completion\n const normalized = error instanceof Error ? error : new Error('IndexedDB operation failed')\n return completed.status === 'aborted'\n ? freezeV1Value({ status: 'proven-not-committed', error: normalized })\n : freezeV1Value({ status: 'unknown', error: normalized })\n }\n\n const completed = await completion\n return completed.status === 'committed'\n ? freezeV1Value({ status: 'committed', value })\n : freezeV1Value({ status: 'proven-not-committed', error: completed.error })\n }\n\n close(): void {\n if (!this._closed) {\n this._closed = true\n this._database.close()\n }\n }\n}\n\nexport type V1IndexedDBSchema = 'base' | 'flags' | 'replay'\n\nexport interface V1OpenIndexedDBDatabaseOptions {\n /** The schema the caller needs; the database is upgraded to that version only when it is older. */\n readonly schema: V1IndexedDBSchema\n /** Lazy owners may inspect an existing schema, but must never close active peers with an upgrade. */\n readonly upgradePolicy?: 'allow' | 'require-existing'\n readonly factory?: IDBFactory | null\n /** How long a version upgrade may stay blocked by other connections before it fails closed. */\n readonly upgradeTimeoutMs?: number\n}\n\ninterface V1IndexedDBIndexDefinition {\n readonly name: string\n readonly keyPath: readonly string[]\n readonly unique: boolean\n}\n\ninterface V1IndexedDBStoreDefinition {\n readonly name: string\n readonly keyPath: string | readonly string[]\n /** The schema version that introduces the store. */\n readonly version: number\n readonly indexes: readonly V1IndexedDBIndexDefinition[]\n}\n\nconst SCHEMA_VERSIONS: Readonly<Record<V1IndexedDBSchema, number>> = freezeV1Value({\n base: V1_INDEXED_DB_VERSION,\n flags: V1_INDEXED_DB_FLAG_VERSION,\n replay: V1_INDEXED_DB_REPLAY_VERSION,\n})\n\nconst STORE_DEFINITIONS: readonly V1IndexedDBStoreDefinition[] = freezeV1Value([\n { name: INSTALLATIONS_STORE, keyPath: 'siteKey', version: 1, indexes: [] },\n { name: RECORDS_STORE, keyPath: ['siteKey', 'sequence'], version: 1, indexes: [] },\n { name: V1_FLAG_CACHE_STORE, keyPath: ['siteNamespaceDigest', 'recordKind'], version: 2, indexes: [] },\n {\n name: V2_REPLAY_CHUNKS_STORE,\n keyPath: ['siteKey', 'enqueueOrdinal'],\n version: 3,\n indexes: [\n { name: V2_REPLAY_CHUNK_INDEX, keyPath: ['siteKey', 'replayId', 'chunkId'], unique: true },\n { name: V2_REPLAY_SEQUENCE_INDEX, keyPath: ['siteKey', 'replayId', 'sequence'], unique: true },\n ],\n },\n { name: V2_REPLAY_STREAMS_STORE, keyPath: ['siteKey', 'replayId'], version: 3, indexes: [] },\n {\n name: V2_REPLAY_RESOLUTIONS_STORE,\n keyPath: ['siteKey', 'replayId', 'chunkId'],\n version: 3,\n indexes: [{ name: V2_REPLAY_SEQUENCE_INDEX, keyPath: ['siteKey', 'replayId', 'sequence'], unique: true }],\n },\n])\n\nconst DEFAULT_UPGRADE_TIMEOUT_MS = 5_000\nconst MAXIMUM_UPGRADE_TIMEOUT_MS = 60_000\n\nconst storeKeyPath = (definition: V1IndexedDBStoreDefinition): string | string[] =>\n typeof definition.keyPath === 'string' ? definition.keyPath : [...definition.keyPath]\n\nconst storesAtVersion = (version: number): readonly V1IndexedDBStoreDefinition[] =>\n STORE_DEFINITIONS.filter((store) => store.version <= version)\n\nconst unsupported = (message: string): V1DurableStorageError => new V1DurableStorageError('unsupported', message)\n\nconst storeNames = (names: DOMStringList): string[] => {\n const result: string[] = []\n for (let index = 0; index < names.length; index += 1) {\n const name = names.item(index)\n if (name !== null) result.push(name)\n }\n return result.sort()\n}\n\n/**\n * Checks that the stores the transaction can see are exactly the stores of the given schema version,\n * with the expected key paths and indexes. Used both on an open connection and inside an upgrade\n * transaction, where it proves the pre-upgrade schema before anything is added.\n */\nconst validateStores = (\n names: DOMStringList,\n objectStore: (name: string) => IDBObjectStore,\n version: number,\n what: string\n): void => {\n const expected = storesAtVersion(version)\n const actual = storeNames(names)\n const expectedNames = expected.map((store) => store.name).sort()\n if (actual.length !== expectedNames.length || actual.some((name, index) => name !== expectedNames[index])) {\n throw unsupported(`IndexedDB ${what} schema does not match version ${version}`)\n }\n for (const definition of expected) {\n const store = objectStore(definition.name)\n if (!keyPathMatches(store.keyPath, definition.keyPath)) {\n throw unsupported(`IndexedDB ${what} key paths are incompatible`)\n }\n const indexNames = storeNames(store.indexNames)\n const expectedIndexNames = definition.indexes.map((index) => index.name).sort()\n if (\n indexNames.length !== expectedIndexNames.length ||\n indexNames.some((name, position) => name !== expectedIndexNames[position])\n ) {\n throw unsupported(`IndexedDB ${what} indexes are incompatible`)\n }\n for (const definitionIndex of definition.indexes) {\n const index = store.index(definitionIndex.name)\n if (!keyPathMatches(index.keyPath, definitionIndex.keyPath) || index.unique !== definitionIndex.unique) {\n throw unsupported(`IndexedDB ${what} index key paths are incompatible`)\n }\n }\n }\n}\n\n/** Validates a connection against the closed schema of its own version; any other version fails closed. */\nexport const validateV1IndexedDBSchema = (database: IDBDatabase): void => {\n if (\n database.version !== V1_INDEXED_DB_VERSION &&\n database.version !== V1_INDEXED_DB_FLAG_VERSION &&\n database.version !== V1_INDEXED_DB_REPLAY_VERSION\n ) {\n throw unsupported('IndexedDB schema version is unsupported')\n }\n const expectedNames = storesAtVersion(database.version).map((store) => store.name)\n if (storeNames(database.objectStoreNames).join(',') !== [...expectedNames].sort().join(',')) {\n throw unsupported(`IndexedDB schema does not match version ${database.version}`)\n }\n let transaction: IDBTransaction\n try {\n transaction = database.transaction(expectedNames, 'readonly')\n } catch {\n throw unsupported('IndexedDB stores cannot be opened')\n }\n try {\n validateStores(database.objectStoreNames, (name) => transaction.objectStore(name), database.version, 'stored')\n } catch (error) {\n try {\n transaction.abort()\n } catch {\n // A completed read transaction needs no cleanup.\n }\n throw error\n }\n}\n\nconst applyUpgrade = (\n database: IDBDatabase,\n transaction: IDBTransaction,\n oldVersion: number,\n newVersion: number\n): void => {\n if (\n (oldVersion !== V1_INDEXED_DB_VERSION && oldVersion !== V1_INDEXED_DB_FLAG_VERSION) ||\n newVersion <= oldVersion ||\n newVersion > V1_INDEXED_DB_REPLAY_VERSION\n ) {\n throw unsupported('IndexedDB upgrade path is unsupported')\n }\n validateStores(database.objectStoreNames, (name) => transaction.objectStore(name), oldVersion, 'pre-upgrade')\n for (const definition of STORE_DEFINITIONS) {\n if (definition.version <= oldVersion || definition.version > newVersion) continue\n const store = database.createObjectStore(definition.name, { keyPath: storeKeyPath(definition) })\n for (const index of definition.indexes) {\n store.createIndex(index.name, [...index.keyPath], { unique: index.unique })\n }\n }\n}\n\nconst resolveFactory = (factory: IDBFactory | null | undefined): IDBFactory | undefined =>\n typeof factory === 'undefined'\n ? typeof globalThis === 'undefined'\n ? undefined\n : globalThis.indexedDB\n : (factory ?? undefined)\n\ntype V1InspectOutcome =\n | Readonly<{ status: 'ready'; database: IDBDatabase }>\n | Readonly<{ status: 'older'; version: number }>\n\n/**\n * Opens the database without a version and reports whether it already satisfies the requested schema.\n * A missing database is created at version 1 for the base schema only; the flag and replay schemas\n * need an installation to exist first, so they abort the creation and fail closed. Inspection never\n * requests a version change, so a blocked open here is unexpected and fails immediately.\n */\nconst inspectDatabase = (\n factory: IDBFactory,\n schema: V1IndexedDBSchema,\n allowCreation = true\n): Promise<V1InspectOutcome> =>\n new Promise((resolve, reject) => {\n let request: IDBOpenDBRequest\n try {\n request = factory.open(V1_INDEXED_DB_NAME)\n } catch {\n reject(new V1DurableStorageError('unavailable', 'IndexedDB open was denied'))\n return\n }\n let settled = false\n let upgradeFailure: V1DurableStorageError | null = null\n const rejectOnce = (error: V1DurableStorageError): void => {\n if (!settled) {\n settled = true\n reject(error)\n }\n }\n request.onupgradeneeded = (event) => {\n const database = request.result\n if (event.oldVersion !== 0 || schema !== 'base' || !allowCreation) {\n upgradeFailure = unsupported(\n schema === 'base'\n ? 'IndexedDB schema version is unsupported'\n : 'IndexedDB has no base schema to extend'\n )\n request.transaction?.abort()\n return\n }\n if (database.objectStoreNames.length !== 0) {\n upgradeFailure = unsupported('IndexedDB contains an unexpected v1 schema')\n request.transaction?.abort()\n return\n }\n for (const definition of storesAtVersion(V1_INDEXED_DB_VERSION)) {\n database.createObjectStore(definition.name, { keyPath: storeKeyPath(definition) })\n }\n }\n request.onblocked = () => rejectOnce(new V1DurableStorageError('blocked', 'IndexedDB open is blocked'))\n request.onerror = () => {\n const code = request.error?.name === 'VersionError' ? 'unsupported' : 'unavailable'\n rejectOnce(upgradeFailure ?? new V1DurableStorageError(code, 'IndexedDB could not be opened'))\n }\n request.onsuccess = () => {\n const database = request.result\n if (settled) {\n database.close()\n return\n }\n settled = true\n try {\n validateV1IndexedDBSchema(database)\n } catch (error) {\n database.close()\n reject(\n error instanceof V1DurableStorageError ? error : unsupported('IndexedDB schema validation failed')\n )\n return\n }\n if (database.version >= SCHEMA_VERSIONS[schema]) {\n database.onversionchange = () => database.close()\n resolve(freezeV1Value({ status: 'ready', database }))\n return\n }\n database.close()\n resolve(freezeV1Value({ status: 'older', version: database.version }))\n }\n })\n\ntype V1UpgradeOutcome = Readonly<{ status: 'ready'; database: IDBDatabase }> | Readonly<{ status: 'overtaken' }>\n\n/**\n * Requests exactly the schema version the caller needs. The upgrade callback trusts oldVersion, not\n * the earlier inspection, so a concurrent lower upgrade by another connection is extended rather\n * than raced. A VersionError means another connection already moved past the requested version;\n * the caller inspects once more and accepts it when it is a known schema.\n */\nconst upgradeDatabase = (\n factory: IDBFactory,\n schema: V1IndexedDBSchema,\n upgradeTimeoutMs: number\n): Promise<V1UpgradeOutcome> =>\n new Promise((resolve, reject) => {\n const version = SCHEMA_VERSIONS[schema]\n let request: IDBOpenDBRequest\n try {\n request = factory.open(V1_INDEXED_DB_NAME, version)\n } catch {\n reject(new V1DurableStorageError('unavailable', 'IndexedDB open was denied'))\n return\n }\n let settled = false\n let upgradeFailure: V1DurableStorageError | null = null\n let blockedTimer: ReturnType<typeof setTimeout> | null = null\n const clearBlockedTimer = (): void => {\n if (blockedTimer !== null) {\n clearTimeout(blockedTimer)\n blockedTimer = null\n }\n }\n const settle = (outcome: () => void): void => {\n if (settled) return\n settled = true\n clearBlockedTimer()\n outcome()\n }\n const abortUpgrade = (): void => {\n try {\n request.transaction?.abort()\n } catch {\n // The upgrade transaction may not exist yet or may already have completed.\n }\n }\n request.onblocked = () => {\n if (settled || blockedTimer !== null) return\n blockedTimer = setTimeout(() => {\n abortUpgrade()\n settle(() => reject(new V1DurableStorageError('blocked', 'IndexedDB upgrade remained blocked')))\n }, upgradeTimeoutMs)\n }\n request.onupgradeneeded = (event) => {\n clearBlockedTimer()\n if (settled) {\n // The caller already gave up; a late upgrade must not change the schema behind it.\n abortUpgrade()\n return\n }\n try {\n const transaction = request.transaction ?? fail(unsupported('IndexedDB upgrade transaction is missing'))\n applyUpgrade(request.result, transaction, event.oldVersion, event.newVersion ?? version)\n } catch (error) {\n upgradeFailure =\n error instanceof V1DurableStorageError ? error : unsupported('IndexedDB upgrade failed')\n abortUpgrade()\n }\n }\n request.onerror = () => {\n if (upgradeFailure === null && request.error?.name === 'VersionError') {\n settle(() => resolve(freezeV1Value({ status: 'overtaken' })))\n return\n }\n settle(() =>\n reject(upgradeFailure ?? new V1DurableStorageError('unavailable', 'IndexedDB could not be opened'))\n )\n }\n request.onsuccess = () => {\n const database = request.result\n if (settled) {\n database.close()\n return\n }\n try {\n validateV1IndexedDBSchema(database)\n } catch (error) {\n database.close()\n settle(() =>\n reject(\n error instanceof V1DurableStorageError\n ? error\n : unsupported('IndexedDB schema validation failed')\n )\n )\n return\n }\n database.onversionchange = () => database.close()\n settle(() => resolve(freezeV1Value({ status: 'ready', database })))\n }\n })\n\nfunction fail(error: V1DurableStorageError): never {\n throw error\n}\n\n/**\n * The one opener every internal owner of `elu-sdk-v1` goes through. It inspects the current version\n * first, without requesting one, and upgrades only when the database is older than the schema the\n * caller needs: v1 to v2 adds the flag cache, v1 or v2 to v3 adds the replay stores, and every\n * existing store keeps its rows. It never requests a lower version, a future or foreign schema fails\n * closed, and a blocked upgrade or an unexpected version change leaves the database untouched.\n */\nexport const openV1IndexedDBDatabase = async (options: V1OpenIndexedDBDatabaseOptions): Promise<IDBDatabase> => {\n const schema = options.schema\n if (schema !== 'base' && schema !== 'flags' && schema !== 'replay') {\n throw new V1DurableStorageError('unavailable', 'IndexedDB schema request is invalid')\n }\n const upgradePolicy = options.upgradePolicy ?? 'allow'\n if (upgradePolicy !== 'allow' && upgradePolicy !== 'require-existing') {\n throw new V1DurableStorageError('unavailable', 'IndexedDB upgrade policy is invalid')\n }\n const upgradeTimeoutMs = options.upgradeTimeoutMs ?? DEFAULT_UPGRADE_TIMEOUT_MS\n if (\n !Number.isSafeInteger(upgradeTimeoutMs) ||\n upgradeTimeoutMs < 1 ||\n upgradeTimeoutMs > MAXIMUM_UPGRADE_TIMEOUT_MS\n ) {\n throw new V1DurableStorageError('unavailable', 'IndexedDB upgrade timeout is invalid')\n }\n const factory = resolveFactory(options.factory)\n if (!factory) {\n throw new V1DurableStorageError('unavailable', 'IndexedDB is unavailable')\n }\n\n const inspected = await inspectDatabase(factory, schema, upgradePolicy === 'allow')\n if (inspected.status === 'ready') return inspected.database\n if (upgradePolicy === 'require-existing') {\n throw unsupported('IndexedDB required schema has not been prepared')\n }\n\n const upgraded = await upgradeDatabase(factory, schema, upgradeTimeoutMs)\n if (upgraded.status === 'ready') return upgraded.database\n\n const reinspected = await inspectDatabase(factory, schema)\n if (reinspected.status === 'ready') return reinspected.database\n throw unsupported('IndexedDB schema changed during upgrade')\n}\n\n/**\n * Initializes an owner after overlapping opens have settled. The connection lifecycle helper keeps\n * its version-change observer independent of the owner's handler and only retries proven loss.\n */\nexport const initializeV1IndexedDBDatabase = async <T>(\n options: V1OpenIndexedDBDatabaseOptions,\n initialize: (database: IDBDatabase) => Promise<T>,\n lostConnection: (error: unknown) => boolean\n): Promise<T> => {\n const factory = resolveFactory(options.factory)\n if (!factory) throw new V1DurableStorageError('unavailable', 'IndexedDB is unavailable')\n return initializeV1IndexedDBConnection({\n factory,\n open: () => openV1IndexedDBDatabase({ ...options, factory }),\n initialize,\n lostConnection,\n })\n}\n\nexport const openV1IndexedDBStorage = async (\n factory?: IDBFactory | null,\n keyRangeFactory?: typeof IDBKeyRange | null\n): Promise<V1DurableStorage> => {\n const indexedDBKeyRange =\n typeof keyRangeFactory === 'undefined'\n ? typeof globalThis === 'undefined'\n ? undefined\n : globalThis.IDBKeyRange\n : (keyRangeFactory ?? undefined)\n if (!resolveFactory(factory) || !indexedDBKeyRange) {\n throw new V1DurableStorageError('unavailable', 'IndexedDB is unavailable')\n }\n const database = await openV1IndexedDBDatabase({ schema: 'base', factory })\n return new V1NativeIndexedDBStorage(database, indexedDBKeyRange)\n}\n","import { isStringValue } from '../validation'\nimport type { V2ReplaySha256, V2Sha256Hash } from './types'\n\nconst SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/\n\n/** Lowercase-hex SHA-256 over the Web Crypto API; null when no digest provider is available. */\nexport const defaultV2ReplaySha256: V2ReplaySha256 = async (value) => {\n const cryptoApi = typeof globalThis === 'undefined' ? undefined : globalThis.crypto\n if (!cryptoApi?.subtle) {\n return null\n }\n try {\n const digest = await cryptoApi.subtle.digest('SHA-256', value)\n let hexadecimal = ''\n for (const byte of new Uint8Array(digest)) {\n hexadecimal += byte.toString(16).padStart(2, '0')\n }\n return hexadecimal\n } catch {\n return null\n }\n}\n\nexport const sha256Hex = async (value: Uint8Array, sha256: V2ReplaySha256 = defaultV2ReplaySha256): Promise<string> => {\n const digest = await sha256(value)\n if (!isStringValue(digest) || !SHA256_HEX_PATTERN.test(digest.toLowerCase())) {\n throw new Error('SHA-256 digest generation is unavailable')\n }\n return digest.toLowerCase()\n}\n\nexport const isV2Sha256Hash = (value: unknown): value is V2Sha256Hash =>\n isStringValue(value) && /^sha256:[a-f0-9]{64}$/.test(value)\n\nexport const utf8Encode = (value: string): Uint8Array => new TextEncoder().encode(value)\n\n/** Strict UTF-8 decode; malformed bytes throw rather than becoming replacement characters. */\nexport const utf8Decode = (value: Uint8Array): string => new TextDecoder('utf-8', { fatal: true }).decode(value)\n","/* eslint-disable compat/compat -- Durable storage requires native Promise and IndexedDB support. */\n\nimport type {\n V1BatchRecord,\n V1Event,\n V1IdentityReference,\n V1JsonObject,\n V1JsonValue,\n V1Mutation,\n V1MutationChange,\n V1MutationSubject,\n V1VersionContext,\n} from './types'\nimport type {\n V1DurableCommitOutcome,\n V1DurableStorage,\n V1DurableStorageFactory,\n V1DurableStorageTransaction,\n} from './durable-storage'\nimport { V1DurableStorageError } from './durable-storage'\nimport type { V1CaptureCandidateEvaluation, V1Sha256 } from './config-manager'\nimport { V1ConfigManager } from './config-manager'\nimport { openV1IndexedDBStorage } from './indexeddb-storage'\nimport { initialV2ReplayInstallationCounters, validateV2ReplayInstallationCounters } from './replay/installation'\nimport type { V2ReplayInstallationCounters } from './replay/installation'\nimport { v1FlagJsonFromUnknown } from './feature-flags/json'\nimport type { V1FlagEvaluationContext } from './feature-flags/types'\nimport {\n freezeV1Value,\n hasOnlyKeys,\n hasOwn,\n hasOwnIndex,\n isArrayValue,\n isBooleanValue,\n isFunctionValue,\n isNonNegativeInteger,\n isNullValue,\n isNumberValue,\n isRecordValue,\n isStringValue,\n parseBoundedJson,\n unicodeCodePointLength,\n utf8ByteLength,\n} from './validation'\n\nexport const V1_DURABLE_STORAGE_SCHEMA_VERSION = 1\nexport const V1_MAX_DURABLE_QUEUE_COUNT = 10_000\nexport const V1_MAX_DURABLE_QUEUE_BYTES = 268_435_456\n\nconst V1_MAX_CAPTURE_PROPERTY_COUNT = 256\nconst V1_MAX_CAPTURE_PROPERTIES_BYTES = 1_048_576\n\nconst IDENTITY_KEYS = [\n 'schemaVersion',\n 'revision',\n 'contextRevision',\n 'anonymousId',\n 'userId',\n 'groups',\n 'superProperties',\n 'session',\n 'optedOut',\n 'updatedAt',\n 'migration',\n 'flagContext',\n] as const\nconst IDENTITY_REQUIRED_KEYS = IDENTITY_KEYS.filter((key) => key !== 'migration' && key !== 'flagContext')\nconst SESSION_KEYS = [\n 'id',\n 'startedAt',\n 'lastActivityAt',\n 'timeoutSeconds',\n 'maximumDurationSeconds',\n 'lifecycle',\n 'backgroundedAt',\n] as const\nconst MIGRATION_KEYS = ['sourceSchema', 'completedAt'] as const\nconst INSTALLATION_KEYS = [\n 'storageSchemaVersion',\n 'siteKey',\n 'identity',\n 'streamId',\n 'nextSequence',\n 'headSequence',\n 'queuedCount',\n 'queuedBytes',\n 'authorityBarrier',\n 'flagAuthorityInitialized',\n 'replay',\n] as const\nconst INSTALLATION_REQUIRED_KEYS = INSTALLATION_KEYS.filter(\n (key) => key !== 'authorityBarrier' && key !== 'flagAuthorityInitialized' && key !== 'replay'\n)\nconst FLAG_CONTEXT_KEYS = ['schemaVersion', 'personProperties', 'groupProperties'] as const\nconst AUTHORITY_BARRIER_KEYS = [\n 'kind',\n 'terminalScope',\n 'configIssuedAt',\n 'configSemanticHash',\n 'policySourceHash',\n 'contextRevision',\n 'decisionHash',\n] as const\nconst AUTHORITY_BARRIER_REQUIRED_KEYS = AUTHORITY_BARRIER_KEYS.filter((key) => key !== 'terminalScope')\nconst STORED_RECORD_KEYS = [\n 'storageSchemaVersion',\n 'siteKey',\n 'streamId',\n 'sequence',\n 'kind',\n 'recordId',\n 'captureVersions',\n 'payloadJson',\n 'payloadBytes',\n] as const\nconst EVENT_KEYS = [\n 'schemaVersion',\n 'eventId',\n 'streamId',\n 'sequence',\n 'contextRevision',\n 'kind',\n 'name',\n 'occurredAt',\n 'identity',\n 'sessionId',\n 'properties',\n 'groups',\n 'versions',\n] as const\nconst EVENT_DRAFT_KEYS = ['kind', 'name', 'occurredAt', 'expectedSessionId', 'properties', 'versions'] as const\nconst MUTATION_KEYS = ['mutationId', 'sequence', 'contextRevision', 'occurredAt', 'subject', 'change'] as const\nconst MUTATION_DRAFT_KEYS = ['occurredAt', 'change', 'versions'] as const\nconst IDENTITY_REFERENCE_KEYS = ['anonymousId', 'userId', 'revision'] as const\nconst MUTATION_SUBJECT_KEYS = ['anonymousId', 'userId', 'identityRevision'] as const\nconst VERSION_KEYS = ['schemaVersion', 'contractVersion', 'platform', 'runtime', 'facade', 'build'] as const\nconst VERSION_REQUIRED_KEYS = VERSION_KEYS.filter((key) => key !== 'build')\nconst RUNTIME_VERSION_KEYS = ['name', 'version'] as const\nconst FACADE_VERSION_KEYS = ['name', 'version'] as const\nconst ACKNOWLEDGEMENT_KEYS = ['siteKey', 'streamId', 'records'] as const\nconst SESSION_UPDATE_KEYS = ['type', 'expectedCurrentSessionId', 'session'] as const\nconst ACKNOWLEDGEMENT_REFERENCE_KEYS = ['sequence', 'kind', 'recordId'] as const\nconst RFC3339_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})[Tt](\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?(?:[Zz]|([+-])(\\d{2}):(\\d{2}))$/\nconst SHA256_PATTERN = /^sha256:[a-f0-9]{64}$/\nconst RUNTIME_NAME_PATTERN = /^elu-[a-z0-9-]+$/\nconst FACADE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9._-]+$/\n\nexport interface V1PersistedSessionState {\n readonly id: string\n readonly startedAt: string\n readonly lastActivityAt: string\n readonly timeoutSeconds: number\n readonly maximumDurationSeconds: 86_400\n readonly lifecycle: 'active' | 'background'\n readonly backgroundedAt: string | null\n}\n\nexport interface V1PersistedMigrationState {\n readonly sourceSchema: string\n readonly completedAt: string\n}\n\nexport interface V1PersistedIdentityState {\n readonly schemaVersion: 1\n readonly revision: number\n readonly contextRevision: number\n readonly anonymousId: string\n readonly userId: string | null\n readonly groups: Readonly<Record<string, string>>\n readonly superProperties: V1JsonObject\n readonly session: V1PersistedSessionState | null\n readonly optedOut: boolean\n readonly updatedAt: string\n readonly migration?: V1PersistedMigrationState\n readonly flagContext?: V1PersistedFlagContext\n}\n\nexport interface V1PersistedFlagContext {\n readonly schemaVersion: 1\n readonly personProperties: V1JsonObject\n readonly groupProperties: Readonly<Record<string, V1JsonObject>>\n}\n\nexport interface V1DurableEventDraft {\n readonly kind: V1Event['kind']\n readonly name: string\n readonly occurredAt: string\n /** Causality assertion only; the persisted session remains authoritative. */\n readonly expectedSessionId: string\n readonly properties: V1JsonObject\n readonly versions: V1VersionContext\n}\n\nexport interface V1DurableMutationDraft {\n readonly occurredAt: string\n readonly change: V1MutationChange\n readonly versions: V1VersionContext\n}\n\nexport type V1DurableEventSessionUpdate =\n | Readonly<{ type: 'preserve' }>\n | Readonly<{\n type: 'replace'\n /** Compare-and-swap assertion for the transaction-current pre-transition session. */\n expectedCurrentSessionId: string | null\n session: V1PersistedSessionState\n }>\n\nexport type V1DurableLocalChange =\n | Readonly<{ type: 'setOptedOut'; optedOut: boolean; occurredAt: string }>\n | Readonly<{ type: 'resetGroups'; occurredAt: string }>\n | Readonly<{ type: 'resetIdentity'; anonymousId: string; occurredAt: string }>\n | Readonly<{\n type: 'setSuperProperties'\n set: V1JsonObject\n setOnce: V1JsonObject\n unset: readonly string[]\n occurredAt: string\n }>\n | Readonly<{\n type: 'replaceSession'\n expectedCurrentSessionId: string | null\n session: V1PersistedSessionState\n }>\n | Readonly<{ type: 'clearSession'; expectedCurrentSessionId: string | null; occurredAt: string }>\n /** Replaces the persisted flag evaluation context; a null member leaves that member unchanged. */\n | Readonly<{\n type: 'setFlagContext'\n personProperties: V1JsonObject | null\n groupProperties: Readonly<Record<string, V1JsonObject>> | null\n /** Merges the person properties into the transaction-current member instead of replacing it. */\n mergePersonProperties?: boolean\n /** Merges each named group type into the transaction-current member instead of replacing it. */\n mergeGroupProperties?: boolean\n /** Group types dropped from the transaction-current member before the group properties apply. */\n unsetGroupTypes?: readonly string[]\n occurredAt: string\n }>\n\nexport interface V1DurableRecordReference {\n readonly sequence: number\n readonly kind: 'event' | 'mutation'\n readonly recordId: string\n}\n\nexport interface V1DurableQueueRecord extends V1DurableRecordReference {\n readonly siteKey: string\n readonly streamId: string\n /** Capture-time SDK/facade metadata; deliberately excluded from the canonical payload bytes. */\n readonly captureVersions: V1VersionContext\n readonly batchRecord: V1BatchRecord\n readonly payloadJson: string\n /** Exact canonical V1BatchRecord bytes. Request-envelope overhead is intentionally separate. */\n readonly payloadBytes: number\n}\n\nexport interface V1DurableStateSnapshot {\n readonly siteKey: string\n readonly identity: V1PersistedIdentityState\n readonly streamId: string\n readonly nextSequence: number\n readonly headSequence: number\n readonly queuedCount: number\n readonly queuedBytes: number\n}\n\nexport interface V1DurableCaptureState {\n readonly state: V1DurableStateSnapshot\n readonly authorized: boolean\n /** Validated transaction observation; later replay writes must compare it to their current row. */\n readonly authorityBarrier: V1InstallationRow['authorityBarrier']\n}\n\nexport interface V1CaptureAdmissionDecision {\n readonly authorized: boolean\n /** A clock-terminal barrier must commit in the caller's current transaction before it returns. */\n readonly terminalInstallation: V1InstallationRow | null\n}\n\n/** Synchronous so replay can check authority inside its existing owner-row transaction. */\nexport type V1CaptureAdmissionGuard = (installation: unknown) => V1CaptureAdmissionDecision\n\nexport interface V1DurableAppendResult {\n readonly state: V1DurableStateSnapshot\n readonly records: readonly V1DurableQueueRecord[]\n}\n\nexport interface V1DurableRuntimeLimits {\n readonly maxCount: number\n readonly maxBytes: number\n}\n\nexport interface V1DurableRuntimeOptions {\n readonly siteKey: string\n readonly limits: V1DurableRuntimeLimits\n readonly storageFactory?: V1DurableStorageFactory\n readonly captureRuntime?: V1CaptureRuntimeOptions\n}\n\nexport interface V1CaptureRuntimeClock {\n readonly wallNow: () => string\n readonly monotonicNowMs: () => number\n}\n\nexport interface V1CaptureRuntimeOptions {\n readonly sha256?: V1Sha256\n readonly clock?: V1CaptureRuntimeClock\n readonly createSessionId?: () => string\n}\n\nexport interface V1CaptureActivityCommand {\n /** Session-eligible activity kinds; diagnostics never touch the capture session. */\n readonly kind: 'capture' | 'screen' | 'exception'\n readonly name: string\n readonly occurredAt: string\n readonly properties: V1JsonObject\n readonly versions: V1VersionContext\n}\n\nexport type V1CaptureAuthorityUpdateResult =\n | Readonly<{ status: 'authorized'; ownerEpoch: number }>\n | Readonly<{ status: 'terminal'; reason: string }>\n | Readonly<{ status: 'stale'; reason: string }>\n\nexport interface V1CaptureLifecycleWindowTarget {\n addEventListener(type: string, listener: () => void): void\n removeEventListener(type: string, listener: () => void): void\n}\n\nexport interface V1CaptureLifecycleDocumentTarget extends V1CaptureLifecycleWindowTarget {\n readonly hidden: boolean\n}\n\nexport interface V1DurableInitialState {\n readonly identity: V1PersistedIdentityState\n readonly streamId: string\n}\n\nexport interface V1DurableAcknowledgement {\n readonly siteKey: string\n readonly streamId: string\n readonly records: readonly V1DurableRecordReference[]\n}\n\nexport type V1DurableStateErrorCode =\n | 'invalid-input'\n | 'not-initialized'\n | 'queue-full'\n | 'head-record-too-large'\n | 'sequence-exhausted'\n | 'corrupt-storage'\n | 'unsupported-storage'\n | 'capture-not-authorized'\n | 'commit-unknown'\n\nexport class V1DurableStateError extends Error {\n readonly code: V1DurableStateErrorCode\n\n constructor(code: V1DurableStateErrorCode, message: string) {\n super(message)\n this.name = 'V1DurableStateError'\n this.code = code\n }\n}\n\nclass V1CaptureConfigTerminalError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'V1CaptureConfigTerminalError'\n }\n}\n\nexport interface V1InstallationRow extends V1DurableStateSnapshot {\n readonly storageSchemaVersion: 1\n readonly authorityBarrier: V1PersistedAuthorityBarrier | null\n readonly flagAuthorityInitialized?: true\n /** Present once the replay store has activated; counted with the event queue at admission. */\n readonly replay?: V2ReplayInstallationCounters\n}\n\ninterface V1PersistedAuthorityBarrier {\n readonly kind: 'authorized' | 'terminal'\n readonly terminalScope: 'config' | 'context' | null\n readonly configIssuedAt: string | null\n readonly configSemanticHash: `sha256:${string}` | null\n readonly policySourceHash: `sha256:${string}` | null\n readonly contextRevision: number | null\n readonly decisionHash: `sha256:${string}` | null\n}\n\ninterface V1CaptureSourceClock {\n readonly sourceToken: object\n readonly expiresAt: string\n readonly installedWallAt: string\n readonly monotonicStartedAtUs: number\n readonly monotonicBudgetUs: number\n}\n\n/** Owner-local issuance age survives executable identity and lifecycle replacement. */\ninterface V1CaptureSourceIssuance {\n readonly token: object\n readonly configIssuedAt: string\n readonly configSemanticHash: `sha256:${string}`\n readonly installedWallAt: string\n readonly observedWallFloor: string\n readonly monotonicStartedAtUs: number\n readonly monotonicBudgetUs: number | null\n readonly denied: boolean\n}\n\ninterface V1ExecutableCaptureAuthority extends V1CaptureSourceClock {\n readonly ownerEpoch: number\n readonly barrier: V1PersistedAuthorityBarrier\n readonly configSiteId: string\n readonly streamId: string\n readonly identityRevision: number\n readonly contextRevision: number\n readonly identityOptedOut: false\n readonly lifecycleEpoch: number\n readonly idleTimeoutSeconds: number\n readonly maximumDurationSeconds: 86_400\n}\n\ninterface V1StoredRecordRow {\n readonly storageSchemaVersion: 1\n readonly siteKey: string\n readonly streamId: string\n readonly sequence: number\n readonly kind: 'event' | 'mutation'\n readonly recordId: string\n readonly captureVersions: V1VersionContext\n readonly payloadJson: string\n readonly payloadBytes: number\n}\n\ninterface V1EncodedRecord {\n readonly stored: V1StoredRecordRow\n readonly exposed: V1DurableQueueRecord\n}\n\ntype V1NormalizedEventDraft = V1DurableEventDraft\n\ntype V1NormalizedMutationDraft = V1DurableMutationDraft\n\nconst fail = (code: V1DurableStateErrorCode, message: string): never => {\n throw new V1DurableStateError(code, message)\n}\n\nconst failInput = (message: string): never => fail('invalid-input', message)\nconst failCorrupt = (message: string): never => fail('corrupt-storage', message)\n\nconst assertExactRecord = (\n value: unknown,\n allowedKeys: readonly string[],\n requiredKeys: readonly string[],\n field: string,\n failure: (message: string) => never\n): Record<string, unknown> => {\n if (!isRecordValue(value) || Object.getPrototypeOf(value) !== Object.prototype) {\n failure(`${field} must be a plain object`)\n }\n if (!hasOnlyKeys(value, allowedKeys)) {\n failure(`${field} contains an unsupported field`)\n }\n for (const key of requiredKeys) {\n if (!hasOwn(value, key)) {\n failure(`${field}.${key} is required`)\n }\n }\n return value\n}\n\nconst assertString = (\n value: unknown,\n field: string,\n minimumLength: number,\n maximumLength: number,\n failure: (message: string) => never\n): string => {\n if (!isStringValue(value)) {\n failure(`${field} must be a string`)\n }\n const length = unicodeCodePointLength(value)\n if (isNullValue(length)) {\n failure(`${field} contains an unpaired Unicode surrogate`)\n }\n if (length < minimumLength || length > maximumLength) {\n failure(`${field} must contain between ${minimumLength} and ${maximumLength} Unicode code points`)\n }\n return value\n}\n\nconst assertIdentifier = (\n value: unknown,\n field: string,\n maximumLength: number,\n failure: (message: string) => never\n): string => assertString(value, field, 1, maximumLength, failure)\n\n// Closed field delegation retains the original validator and evaluation order.\nconst assertIdentifierField = (value: unknown, key: string, field: string, maximumLength: number, failure: (message: string) => never): string =>\n assertIdentifier(value, `${field}.${key}`, maximumLength, failure)\n\nconst assertInteger = (\n value: unknown,\n field: string,\n minimum: number,\n maximum: number,\n failure: (message: string) => never\n): number => {\n if (!isNonNegativeInteger(value) || value < minimum || value > maximum) {\n failure(`${field} must be an integer between ${minimum} and ${maximum}`)\n }\n return value\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst assertIntegerField = (value: unknown, key: string, field: string, minimum: number, maximum: number, failure: (message: string) => never): number =>\n assertInteger(value, `${field}.${key}`, minimum, maximum, failure)\n\nconst assertBoolean = (value: unknown, field: string, failure: (message: string) => never): boolean => {\n if (!isBooleanValue(value)) {\n failure(`${field} must be a boolean`)\n }\n return value\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst assertBooleanField = (value: unknown, key: string, field: string, failure: (message: string) => never): boolean =>\n assertBoolean(value, `${field}.${key}`, failure)\n\nconst daysInMonth = (year: number, month: number): number => {\n const date = new Date(0)\n date.setUTCFullYear(year, month, 0)\n return date.getUTCDate()\n}\n\ninterface V1ParsedDateTime {\n readonly source: string\n /** Ordinary POSIX second, or the preceding :59 second when leapSecond is true. */\n readonly epochSecond: number\n readonly leapSecond: boolean\n readonly fractionalDigits: string\n}\n\nconst parseDateTime = (value: unknown, field: string, failure: (message: string) => never): V1ParsedDateTime => {\n const source = assertString(value, field, 1, 128, failure)\n const match = RFC3339_PATTERN.exec(source)\n if (!match) {\n failure(`${field} must be an RFC 3339 date-time`)\n }\n const year = Number(match[1])\n const month = Number(match[2])\n const day = Number(match[3])\n const hour = Number(match[4])\n const minute = Number(match[5])\n const second = Number(match[6])\n const offsetHour = match[9] ? Number(match[9]) : 0\n const offsetMinute = match[10] ? Number(match[10]) : 0\n if (\n month < 1 ||\n month > 12 ||\n day < 1 ||\n day > daysInMonth(year, month) ||\n hour > 23 ||\n minute > 59 ||\n second > 60 ||\n offsetHour > 23 ||\n offsetMinute > 59\n ) {\n failure(`${field} must be a valid RFC 3339 date-time`)\n }\n const local = new Date(0)\n local.setUTCFullYear(year, month - 1, day)\n local.setUTCHours(hour, minute, Math.min(second, 59), 0)\n const localEpochSecond = local.getTime() / 1000\n const offsetDirection = match[8] === '-' ? -1 : 1\n const offsetSeconds = offsetDirection * (offsetHour * 60 + offsetMinute) * 60\n const epochSecond = localEpochSecond - offsetSeconds\n if (!isNumberValue(epochSecond) || !Number.isSafeInteger(epochSecond)) {\n failure(`${field} is outside the supported epoch range`)\n }\n if (second === 60) {\n const nextUtcSecond = new Date((epochSecond + 1) * 1000)\n const isLeapBoundary =\n nextUtcSecond.getUTCHours() === 0 &&\n nextUtcSecond.getUTCMinutes() === 0 &&\n nextUtcSecond.getUTCSeconds() === 0 &&\n nextUtcSecond.getUTCDate() === 1 &&\n (nextUtcSecond.getUTCMonth() === 0 || nextUtcSecond.getUTCMonth() === 6)\n if (!isLeapBoundary) {\n failure(`${field} must use second 60 only at a shifted UTC June or December leap boundary`)\n }\n }\n return freezeV1Value({\n source,\n epochSecond,\n leapSecond: second === 60,\n fractionalDigits: match[7] ?? '',\n })\n}\n\nconst assertDateTime = (value: unknown, field: string, failure: (message: string) => never): string =>\n parseDateTime(value, field, failure).source\n\n// Closed field delegation retains the original validator and evaluation order.\nconst assertDateTimeField = (value: unknown, key: string, field: string, failure: (message: string) => never): string =>\n assertDateTime(value, `${field}.${key}`, failure)\n\n/** Exact RFC 3339 comparison shared by durable state and batch delivery. */\nexport const compareV1DateTimes = (left: string, right: string): number => {\n const parsedLeft = parseDateTime(left, 'left date-time', failInput)\n const parsedRight = parseDateTime(right, 'right date-time', failInput)\n if (parsedLeft.epochSecond !== parsedRight.epochSecond) {\n return parsedLeft.epochSecond < parsedRight.epochSecond ? -1 : 1\n }\n if (parsedLeft.leapSecond !== parsedRight.leapSecond) {\n return parsedLeft.leapSecond ? 1 : -1\n }\n const width = Math.max(parsedLeft.fractionalDigits.length, parsedRight.fractionalDigits.length)\n const leftFraction = parsedLeft.fractionalDigits.padEnd(width, '0')\n const rightFraction = parsedRight.fractionalDigits.padEnd(width, '0')\n return leftFraction === rightFraction ? 0 : leftFraction < rightFraction ? -1 : 1\n}\n\n/** Exact age predicate shared by durable state and batch delivery. */\nexport const hasV1ElapsedAtLeastSeconds = (later: string, earlier: string, minimumSeconds: number): boolean => {\n const parsedLater = parseDateTime(later, 'later date-time', failInput)\n const parsedEarlier = parseDateTime(earlier, 'earlier date-time', failInput)\n const wholeSecondDifference = parsedLater.epochSecond - parsedEarlier.epochSecond + (parsedLater.leapSecond ? 1 : 0)\n if (wholeSecondDifference !== minimumSeconds) {\n return wholeSecondDifference > minimumSeconds\n }\n const width = Math.max(parsedLater.fractionalDigits.length, parsedEarlier.fractionalDigits.length)\n return parsedLater.fractionalDigits.padEnd(width, '0') >= parsedEarlier.fractionalDigits.padEnd(width, '0')\n}\n\n/**\n * Conservative whole-millisecond duration for a strict later/earlier pair. The later fractional\n * component is floored so a monotonic authorization cap never extends past exact RFC 3339 expiry.\n */\nexport const v1MillisecondsBetween = (later: string, earlier: string): number => {\n const parsedLater = parseDateTime(later, 'later date-time', failInput)\n const parsedEarlier = parseDateTime(earlier, 'earlier date-time', failInput)\n const millisecondFloor = (value: V1ParsedDateTime): number =>\n (value.epochSecond + (value.leapSecond ? 1 : 0)) * 1_000 +\n Number(value.fractionalDigits.slice(0, 3).padEnd(3, '0'))\n return millisecondFloor(parsedLater) - millisecondFloor(parsedEarlier)\n}\n\nexport const v1MicrosecondsBetween = (later: string, earlier: string): number => {\n const parsedLater = parseDateTime(later, 'later date-time', failInput)\n const parsedEarlier = parseDateTime(earlier, 'earlier date-time', failInput)\n const laterSecond = parsedLater.epochSecond + (parsedLater.leapSecond ? 1 : 0)\n const earlierSecond = parsedEarlier.epochSecond + (parsedEarlier.leapSecond ? 1 : 0)\n const secondDifference = laterSecond - earlierSecond\n if (!Number.isSafeInteger(secondDifference) || !Number.isSafeInteger(secondDifference * 1_000_000)) {\n return failInput('date-time duration exceeds the exact microsecond range')\n }\n const laterMicroseconds = Number(parsedLater.fractionalDigits.slice(0, 6).padEnd(6, '0'))\n const earlierMicroseconds = Number(parsedEarlier.fractionalDigits.slice(0, 6).padEnd(6, '0'))\n const residualWidth = Math.max(\n Math.max(0, parsedLater.fractionalDigits.length - 6),\n Math.max(0, parsedEarlier.fractionalDigits.length - 6)\n )\n const laterResidual = parsedLater.fractionalDigits.slice(6).padEnd(residualWidth, '0')\n const earlierResidual = parsedEarlier.fractionalDigits.slice(6).padEnd(residualWidth, '0')\n const residualFloorAdjustment = laterResidual < earlierResidual ? -1 : 0\n const result = secondDifference * 1_000_000 + laterMicroseconds - earlierMicroseconds + residualFloorAdjustment\n return Number.isSafeInteger(result) ? result : failInput('date-time duration exceeds the exact microsecond range')\n}\n\nconst validateHash = (\n value: unknown,\n field: string,\n nullable: boolean,\n failure: (message: string) => never\n): `sha256:${string}` | null => {\n if (nullable && isNullValue(value)) return null\n const hash = assertString(value, field, 71, 71, failure)\n if (!SHA256_PATTERN.test(hash)) failure(`${field} must be a lowercase SHA-256 identity`)\n return hash as `sha256:${string}`\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst validateHashField = (value: unknown, key: string, field: string, nullable: boolean, failure: (message: string) => never): `sha256:${string}` | null =>\n validateHash(value, `${field}.${key}`, nullable, failure)\n\nconst validateAuthorityBarrier = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1PersistedAuthorityBarrier | null => {\n if (isNullValue(value)) return null\n const barrier = assertExactRecord(value, AUTHORITY_BARRIER_KEYS, AUTHORITY_BARRIER_REQUIRED_KEYS, field, failure)\n const kind =\n barrier.kind === 'authorized'\n ? 'authorized'\n : barrier.kind === 'terminal'\n ? 'terminal'\n : failure(`${field}.kind is unsupported`)\n const terminalScope = hasOwn(barrier, 'terminalScope')\n ? isNullValue(barrier.terminalScope)\n ? null\n : barrier.terminalScope === 'config' || barrier.terminalScope === 'context'\n ? barrier.terminalScope\n : failure(`${field}.terminalScope is unsupported`)\n : kind === 'terminal'\n ? 'config'\n : null\n const configIssuedAt = isNullValue(barrier.configIssuedAt)\n ? null\n : assertDateTimeField(barrier.configIssuedAt, \"configIssuedAt\", field, failure)\n const configSemanticHash = validateHashField(barrier.configSemanticHash, \"configSemanticHash\", field, true, failure)\n const policySourceHash = validateHashField(barrier.policySourceHash, \"policySourceHash\", field, true, failure)\n const contextRevision = isNullValue(barrier.contextRevision)\n ? null\n : assertIntegerField(barrier.contextRevision, \"contextRevision\", field, 0, Number.MAX_SAFE_INTEGER, failure)\n const decisionHash = validateHashField(barrier.decisionHash, \"decisionHash\", field, true, failure)\n if (isNullValue(configIssuedAt) !== isNullValue(configSemanticHash)) {\n failure(`${field} config boundary must be wholly present or absent`)\n }\n if (\n kind === 'authorized' &&\n (!configIssuedAt || !configSemanticHash || !policySourceHash || isNullValue(contextRevision) || !decisionHash)\n ) {\n failure(`${field} authorized state must contain the complete decision fingerprint`)\n }\n if ((kind === 'authorized' && !isNullValue(terminalScope)) || (kind === 'terminal' && isNullValue(terminalScope))) {\n failure(`${field} terminal scope does not match its barrier kind`)\n }\n return freezeV1Value({\n kind,\n terminalScope,\n configIssuedAt,\n configSemanticHash,\n policySourceHash,\n contextRevision,\n decisionHash,\n })\n}\n\nconst normalizeJsonValue = (\n value: unknown,\n field: string,\n failure: (message: string) => never,\n ancestors: WeakSet<object> = new WeakSet(),\n depth = 0\n): V1JsonValue => {\n if (depth > 64) {\n failure(`${field} exceeds the maximum JSON nesting depth`)\n }\n if (isNullValue(value) || isBooleanValue(value) || isStringValue(value)) {\n if (isStringValue(value) && isNullValue(unicodeCodePointLength(value))) {\n failure(`${field} contains an unpaired Unicode surrogate`)\n }\n return value\n }\n if (isNumberValue(value)) {\n return Object.is(value, -0) ? 0 : value\n }\n if (typeof value !== 'object' || isNullValue(value)) {\n failure(`${field} is outside the JSON value domain`)\n }\n if (ancestors.has(value)) {\n failure(`${field} contains a JSON cycle`)\n }\n ancestors.add(value)\n\n if (isArrayValue(value)) {\n const ownNames = Object.getOwnPropertyNames(value).filter((name) => name !== 'length')\n if (ownNames.length !== value.length) {\n failure(`${field} must be a dense JSON array without extra properties`)\n }\n const result: V1JsonValue[] = []\n for (let index = 0; index < value.length; index += 1) {\n if (!hasOwnIndex(value, index)) {\n failure(`${field} must not be sparse`)\n }\n const descriptor = Object.getOwnPropertyDescriptor(value, String(index))\n if (!descriptor || !hasOwn(descriptor as unknown as Record<string, unknown>, 'value')) {\n failure(`${field}[${index}] must be a data property`)\n }\n result.push(normalizeJsonValue(value[index], `${field}[${index}]`, failure, ancestors, depth + 1))\n }\n ancestors.delete(value)\n return freezeV1Value(result)\n }\n\n if (Object.getPrototypeOf(value) !== Object.prototype) {\n failure(`${field} must be a plain JSON object`)\n }\n const objectValue = value as Record<string, unknown>\n if (Object.getOwnPropertySymbols(value).length !== 0) {\n failure(`${field} must not contain symbol properties`)\n }\n const keys = Object.keys(value)\n if (Object.getOwnPropertyNames(value).length !== keys.length) {\n failure(`${field} must not contain hidden properties`)\n }\n const result: Record<string, V1JsonValue> = {}\n for (const key of keys) {\n if (isNullValue(unicodeCodePointLength(key))) {\n failure(`${field} contains an invalid Unicode property name`)\n }\n const descriptor = Object.getOwnPropertyDescriptor(value, key)\n if (!descriptor || !hasOwn(descriptor as unknown as Record<string, unknown>, 'value')) {\n failure(`${field}.${key} must be a data property`)\n }\n Object.defineProperty(result, key, {\n value: normalizeJsonValue(objectValue[key], `${field}.${key}`, failure, ancestors, depth + 1),\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n ancestors.delete(value)\n return freezeV1Value(result)\n}\n\nconst normalizeJsonObject = (value: unknown, field: string, failure: (message: string) => never): V1JsonObject => {\n const normalized = normalizeJsonValue(value, field, failure)\n if (!isRecordValue(normalized)) {\n failure(`${field} must be a JSON object`)\n }\n return normalized as V1JsonObject\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst normalizeJsonObjectField = (value: unknown, key: string, field: string, failure: (message: string) => never): V1JsonObject =>\n normalizeJsonObject(value, `${field}.${key}`, failure)\n\nconst validateGroups = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): Readonly<Record<string, string>> => {\n const groups = normalizeJsonObject(value, field, failure)\n const keys = Object.keys(groups)\n if (keys.length > 64) {\n failure(`${field} must not contain more than 64 groups`)\n }\n const result: Record<string, string> = {}\n for (const key of keys) {\n Object.defineProperty(result, key, {\n value: assertIdentifier(groups[key], `${field}.${key}`, 512, failure),\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n return freezeV1Value(result)\n}\n\nconst validateFlagProperties = (value: unknown, field: string, failure: (message: string) => never): V1JsonObject => {\n const properties = normalizeJsonObject(value, field, failure)\n if (Object.keys(properties).length > 1_024) failure(`${field} must not contain more than 1024 properties`)\n try {\n if (v1FlagJsonFromUnknown(properties, field).kind !== 'object') failure(`${field} must be an object`)\n } catch (error) {\n failure(error instanceof Error ? error.message : `${field} is outside the flag JSON domain`)\n }\n return properties\n}\n\nconst validateFlagContext = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1PersistedFlagContext => {\n const context = assertExactRecord(value, FLAG_CONTEXT_KEYS, FLAG_CONTEXT_KEYS, field, failure)\n if (context.schemaVersion !== 1) failure(`${field}.schemaVersion must be 1`)\n const rawGroups = normalizeJsonObjectField(context.groupProperties, \"groupProperties\", field, failure)\n const groupTypes = Object.keys(rawGroups)\n if (groupTypes.length > 64) failure(`${field}.groupProperties must not contain more than 64 group types`)\n const groupProperties: Record<string, V1JsonObject> = {}\n for (const groupType of groupTypes) {\n Object.defineProperty(groupProperties, groupType, {\n value: validateFlagProperties(rawGroups[groupType], `${field}.groupProperties.${groupType}`, failure),\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n return freezeV1Value({\n schemaVersion: 1,\n personProperties: validateFlagProperties(context.personProperties, `${field}.personProperties`, failure),\n groupProperties: freezeV1Value(groupProperties),\n })\n}\n\nconst emptyFlagContext = (): V1PersistedFlagContext =>\n freezeV1Value({\n schemaVersion: 1,\n personProperties: freezeV1Value({}),\n groupProperties: freezeV1Value({}),\n })\n\nconst flagContextHasValues = (context: V1PersistedFlagContext | undefined): boolean =>\n Boolean(\n context && (Object.keys(context.personProperties).length > 0 || Object.keys(context.groupProperties).length > 0)\n )\n\nconst validateSession = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1PersistedSessionState | null => {\n if (isNullValue(value)) {\n return null\n }\n const session = assertExactRecord(value, SESSION_KEYS, SESSION_KEYS, field, failure)\n const lifecycle = session.lifecycle\n if (lifecycle !== 'active' && lifecycle !== 'background') {\n failure(`${field}.lifecycle is unsupported`)\n }\n const backgroundedAt = isNullValue(session.backgroundedAt)\n ? null\n : assertDateTimeField(session.backgroundedAt, \"backgroundedAt\", field, failure)\n const result: V1PersistedSessionState = {\n id: assertIdentifierField(session.id, \"id\", field, 256, failure),\n startedAt: assertDateTimeField(session.startedAt, \"startedAt\", field, failure),\n lastActivityAt: assertDateTimeField(session.lastActivityAt, \"lastActivityAt\", field, failure),\n timeoutSeconds: assertIntegerField(session.timeoutSeconds, \"timeoutSeconds\", field, 60, 36_000, failure),\n maximumDurationSeconds: assertIntegerField(session.maximumDurationSeconds, \"maximumDurationSeconds\", field, 86_400, 86_400, failure) as 86_400,\n lifecycle,\n backgroundedAt,\n }\n if (compareV1DateTimes(result.lastActivityAt, result.startedAt) < 0) {\n failure(`${field}.lastActivityAt must not precede ${field}.startedAt`)\n }\n if (hasV1ElapsedAtLeastSeconds(result.lastActivityAt, result.startedAt, result.maximumDurationSeconds)) {\n failure(`${field}.lastActivityAt must precede the session maximum-duration boundary`)\n }\n if (result.lifecycle === 'active' && result.backgroundedAt) {\n failure(`${field}.backgroundedAt must be null while the session is active`)\n }\n if (result.lifecycle === 'background') {\n if (!result.backgroundedAt) {\n failure(`${field}.backgroundedAt is required while the session is backgrounded`)\n }\n if (compareV1DateTimes(result.backgroundedAt, result.lastActivityAt) < 0) {\n failure(`${field}.backgroundedAt must not precede ${field}.lastActivityAt`)\n }\n }\n return freezeV1Value(result)\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst validateSessionField = (value: unknown, key: string, field: string, failure: (message: string) => never): V1PersistedSessionState | null =>\n validateSession(value, `${field}.${key}`, failure)\n\nconst validateMigration = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1PersistedMigrationState => {\n const migration = assertExactRecord(value, MIGRATION_KEYS, MIGRATION_KEYS, field, failure)\n return freezeV1Value({\n sourceSchema: assertIdentifierField(migration.sourceSchema, \"sourceSchema\", field, 128, failure),\n completedAt: assertDateTimeField(migration.completedAt, \"completedAt\", field, failure),\n })\n}\n\nconst validateIdentity = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1PersistedIdentityState => {\n const normalized = normalizeJsonObject(value, field, failure)\n const identity = assertExactRecord(normalized, IDENTITY_KEYS, IDENTITY_REQUIRED_KEYS, field, failure)\n if (identity.schemaVersion !== 1) {\n failure(`${field}.schemaVersion must be 1`)\n }\n const userId = isNullValue(identity.userId)\n ? null\n : assertIdentifierField(identity.userId, \"userId\", field, 512, failure)\n const result: V1PersistedIdentityState = {\n schemaVersion: 1,\n revision: assertIntegerField(identity.revision, \"revision\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n contextRevision: assertIntegerField(identity.contextRevision, \"contextRevision\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n anonymousId: assertIdentifierField(identity.anonymousId, \"anonymousId\", field, 256, failure),\n userId,\n groups: validateGroups(identity.groups, `${field}.groups`, failure),\n superProperties: normalizeJsonObjectField(identity.superProperties, \"superProperties\", field, failure),\n session: validateSessionField(identity.session, \"session\", field, failure),\n optedOut: assertBooleanField(identity.optedOut, \"optedOut\", field, failure),\n updatedAt: assertDateTimeField(identity.updatedAt, \"updatedAt\", field, failure),\n ...(hasOwn(identity, 'migration')\n ? { migration: validateMigration(identity.migration, `${field}.migration`, failure) }\n : {}),\n ...(hasOwn(identity, 'flagContext')\n ? { flagContext: validateFlagContext(identity.flagContext, `${field}.flagContext`, failure) }\n : {}),\n }\n if (result.revision > result.contextRevision) {\n failure(`${field}.revision must not exceed ${field}.contextRevision`)\n }\n if (\n result.session &&\n (compareV1DateTimes(result.updatedAt, result.session.lastActivityAt) < 0 ||\n (result.session.backgroundedAt && compareV1DateTimes(result.updatedAt, result.session.backgroundedAt) < 0))\n ) {\n failure(`${field}.updatedAt must not precede session activity`)\n }\n return freezeV1Value(result)\n}\n\nconst validateIdentityReference = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1IdentityReference => {\n const identity = assertExactRecord(value, IDENTITY_REFERENCE_KEYS, IDENTITY_REFERENCE_KEYS, field, failure)\n return freezeV1Value({\n anonymousId: assertIdentifierField(identity.anonymousId, \"anonymousId\", field, 256, failure),\n userId: isNullValue(identity.userId)\n ? null\n : assertIdentifierField(identity.userId, \"userId\", field, 512, failure),\n revision: assertIntegerField(identity.revision, \"revision\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n })\n}\n\nconst validateMutationSubject = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1MutationSubject => {\n const subject = assertExactRecord(value, MUTATION_SUBJECT_KEYS, MUTATION_SUBJECT_KEYS, field, failure)\n return freezeV1Value({\n anonymousId: assertIdentifierField(subject.anonymousId, \"anonymousId\", field, 256, failure),\n userId: isNullValue(subject.userId) ? null : assertIdentifierField(subject.userId, \"userId\", field, 512, failure),\n identityRevision: assertIntegerField(subject.identityRevision, \"identityRevision\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n })\n}\n\nconst validatePropertyNames = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): readonly string[] => {\n if (!isArrayValue(value) || value.length > 256) {\n failure(`${field} must be an array of at most 256 property names`)\n }\n const names: string[] = []\n const seen = new Set<string>()\n for (let index = 0; index < value.length; index += 1) {\n if (!hasOwnIndex(value, index)) {\n failure(`${field} must not be sparse`)\n }\n const name = assertIdentifier(value[index], `${field}[${index}]`, 512, failure)\n if (seen.has(name)) {\n failure(`${field} must contain unique property names`)\n }\n seen.add(name)\n names.push(name)\n }\n return freezeV1Value(names)\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst validatePropertyNamesField = (value: unknown, key: string, field: string, failure: (message: string) => never): readonly string[] =>\n validatePropertyNames(value, `${field}.${key}`, failure)\n\nconst validateMutationChange = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1MutationChange => {\n const change = normalizeJsonObject(value, field, failure)\n switch (change.type) {\n case 'identify': {\n const parsed = assertExactRecord(\n change,\n ['type', 'userId', 'set', 'setOnce'],\n ['type', 'userId', 'set', 'setOnce'],\n field,\n failure\n )\n return freezeV1Value({\n type: 'identify',\n userId: assertIdentifierField(parsed.userId, \"userId\", field, 512, failure),\n set: normalizeJsonObjectField(parsed.set, \"set\", field, failure),\n setOnce: normalizeJsonObjectField(parsed.setOnce, \"setOnce\", field, failure),\n })\n }\n case 'linkAlias': {\n const parsed = assertExactRecord(\n change,\n ['type', 'aliasId', 'canonicalId'],\n ['type', 'aliasId', 'canonicalId'],\n field,\n failure\n )\n return freezeV1Value({\n type: 'linkAlias',\n aliasId: assertIdentifierField(parsed.aliasId, \"aliasId\", field, 512, failure),\n canonicalId: assertIdentifierField(parsed.canonicalId, \"canonicalId\", field, 512, failure),\n })\n }\n case 'setPersonProperties': {\n const parsed = assertExactRecord(\n change,\n ['type', 'set', 'setOnce', 'unset'],\n ['type', 'set', 'setOnce', 'unset'],\n field,\n failure\n )\n return freezeV1Value({\n type: 'setPersonProperties',\n set: normalizeJsonObjectField(parsed.set, \"set\", field, failure),\n setOnce: normalizeJsonObjectField(parsed.setOnce, \"setOnce\", field, failure),\n unset: validatePropertyNamesField(parsed.unset, \"unset\", field, failure),\n })\n }\n case 'associateGroup': {\n const parsed = assertExactRecord(\n change,\n ['type', 'groupType', 'groupKey'],\n ['type', 'groupType', 'groupKey'],\n field,\n failure\n )\n return freezeV1Value({\n type: 'associateGroup',\n groupType: assertIdentifierField(parsed.groupType, \"groupType\", field, 256, failure),\n groupKey: assertIdentifierField(parsed.groupKey, \"groupKey\", field, 512, failure),\n })\n }\n case 'setGroupProperties': {\n const parsed = assertExactRecord(\n change,\n ['type', 'groupType', 'groupKey', 'set', 'setOnce', 'unset'],\n ['type', 'groupType', 'groupKey', 'set', 'setOnce', 'unset'],\n field,\n failure\n )\n return freezeV1Value({\n type: 'setGroupProperties',\n groupType: assertIdentifierField(parsed.groupType, \"groupType\", field, 256, failure),\n groupKey: assertIdentifierField(parsed.groupKey, \"groupKey\", field, 512, failure),\n set: normalizeJsonObjectField(parsed.set, \"set\", field, failure),\n setOnce: normalizeJsonObjectField(parsed.setOnce, \"setOnce\", field, failure),\n unset: validatePropertyNamesField(parsed.unset, \"unset\", field, failure),\n })\n }\n default:\n return failure(`${field}.type is unsupported`)\n }\n}\n\nconst validateVersions = (value: unknown, field: string, failure: (message: string) => never): V1VersionContext => {\n const versions = assertExactRecord(value, VERSION_KEYS, VERSION_REQUIRED_KEYS, field, failure)\n if (versions.schemaVersion !== 1 || versions.contractVersion !== '1.0.0' || versions.platform !== 'browser') {\n failure(`${field} is not a browser v1 version context`)\n }\n const runtime = assertExactRecord(\n versions.runtime,\n RUNTIME_VERSION_KEYS,\n RUNTIME_VERSION_KEYS,\n `${field}.runtime`,\n failure\n )\n const facade = assertExactRecord(\n versions.facade,\n FACADE_VERSION_KEYS,\n FACADE_VERSION_KEYS,\n `${field}.facade`,\n failure\n )\n const runtimeName = assertIdentifier(runtime.name, `${field}.runtime.name`, V1_MAX_DURABLE_QUEUE_BYTES, failure)\n if (!RUNTIME_NAME_PATTERN.test(runtimeName)) {\n failure(`${field}.runtime.name must match the v1 runtime identifier pattern`)\n }\n const facadeName = assertIdentifier(facade.name, `${field}.facade.name`, V1_MAX_DURABLE_QUEUE_BYTES, failure)\n if (!FACADE_NAME_PATTERN.test(facadeName)) {\n failure(`${field}.facade.name must match the v1 facade identifier pattern`)\n }\n const result: V1VersionContext = {\n schemaVersion: 1,\n contractVersion: '1.0.0',\n platform: 'browser',\n runtime: freezeV1Value({\n name: runtimeName as `elu-${string}`,\n version: assertIdentifier(runtime.version, `${field}.runtime.version`, 64, failure),\n }),\n facade: freezeV1Value({\n name: facadeName,\n version: assertIdentifier(facade.version, `${field}.facade.version`, 64, failure),\n }),\n ...(hasOwn(versions, 'build')\n ? { build: assertIdentifierField(versions.build, \"build\", field, 128, failure) }\n : {}),\n }\n return freezeV1Value(result)\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst validateVersionsField = (value: unknown, key: string, field: string, failure: (message: string) => never): V1VersionContext =>\n validateVersions(value, `${field}.${key}`, failure)\n\nconst sameVersionContext = (left: V1VersionContext, right: V1VersionContext): boolean =>\n left.schemaVersion === right.schemaVersion &&\n left.contractVersion === right.contractVersion &&\n left.platform === right.platform &&\n left.runtime.name === right.runtime.name &&\n left.runtime.version === right.runtime.version &&\n left.facade.name === right.facade.name &&\n left.facade.version === right.facade.version &&\n left.build === right.build\n\nconst validateEvent = (\n value: unknown,\n expected: V1DurableRecordReference & Readonly<{ streamId: string }>,\n field: string,\n failure: (message: string) => never\n): V1Event => {\n const event = assertExactRecord(value, EVENT_KEYS, EVENT_KEYS, field, failure)\n if (event.schemaVersion !== 1) {\n failure(`${field}.schemaVersion must be 1`)\n }\n if (\n event.eventId !== expected.recordId ||\n event.streamId !== expected.streamId ||\n event.sequence !== expected.sequence\n ) {\n failure(`${field} identity does not match its stored queue row`)\n }\n if (\n event.kind !== 'capture' &&\n event.kind !== 'screen' &&\n event.kind !== 'exception' &&\n event.kind !== 'diagnostic'\n ) {\n failure(`${field}.kind is unsupported`)\n }\n return freezeV1Value({\n schemaVersion: 1,\n eventId: assertIdentifierField(event.eventId, \"eventId\", field, 256, failure),\n streamId: assertIdentifierField(event.streamId, \"streamId\", field, 256, failure),\n sequence: assertIntegerField(event.sequence, \"sequence\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n contextRevision: assertIntegerField(event.contextRevision, \"contextRevision\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n kind: event.kind,\n name: assertIdentifierField(event.name, \"name\", field, 512, failure),\n occurredAt: assertDateTimeField(event.occurredAt, \"occurredAt\", field, failure),\n identity: validateIdentityReference(event.identity, `${field}.identity`, failure),\n sessionId: assertIdentifierField(event.sessionId, \"sessionId\", field, 256, failure),\n properties: normalizeJsonObjectField(event.properties, \"properties\", field, failure),\n groups: validateGroups(event.groups, `${field}.groups`, failure),\n versions: validateVersionsField(event.versions, \"versions\", field, failure),\n })\n}\n\nconst validateMutation = (\n value: unknown,\n expected: V1DurableRecordReference,\n field: string,\n failure: (message: string) => never\n): V1Mutation => {\n const mutation = assertExactRecord(value, MUTATION_KEYS, MUTATION_KEYS, field, failure)\n if (mutation.mutationId !== expected.recordId || mutation.sequence !== expected.sequence) {\n failure(`${field} identity does not match its stored queue row`)\n }\n return freezeV1Value({\n mutationId: assertIdentifierField(mutation.mutationId, \"mutationId\", field, 256, failure),\n sequence: assertIntegerField(mutation.sequence, \"sequence\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n contextRevision: assertIntegerField(mutation.contextRevision, \"contextRevision\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n occurredAt: assertDateTimeField(mutation.occurredAt, \"occurredAt\", field, failure),\n subject: validateMutationSubject(mutation.subject, `${field}.subject`, failure),\n change: validateMutationChange(mutation.change, `${field}.change`, failure),\n })\n}\n\nconst validateInstallation = (value: unknown, siteKey: string): V1InstallationRow => {\n const installation = assertExactRecord(\n value,\n INSTALLATION_KEYS,\n INSTALLATION_REQUIRED_KEYS,\n 'stored installation',\n failCorrupt\n )\n if (installation.storageSchemaVersion !== V1_DURABLE_STORAGE_SCHEMA_VERSION) {\n fail('unsupported-storage', 'stored installation schema version is unsupported')\n }\n if (installation.siteKey !== siteKey) {\n failCorrupt('stored installation site key does not match its storage scope')\n }\n const nextSequence = assertInteger(\n installation.nextSequence,\n 'stored installation nextSequence',\n 0,\n Number.MAX_SAFE_INTEGER,\n failCorrupt\n )\n const headSequence = assertInteger(\n installation.headSequence,\n 'stored installation headSequence',\n 0,\n Number.MAX_SAFE_INTEGER,\n failCorrupt\n )\n const queuedCount = assertInteger(\n installation.queuedCount,\n 'stored installation queuedCount',\n 0,\n V1_MAX_DURABLE_QUEUE_COUNT,\n failCorrupt\n )\n if (headSequence > nextSequence || nextSequence - headSequence !== queuedCount) {\n failCorrupt('stored installation sequence counters are inconsistent')\n }\n const row: V1InstallationRow = freezeV1Value({\n storageSchemaVersion: 1,\n siteKey: assertIdentifier(installation.siteKey, 'stored installation siteKey', 128, failCorrupt),\n identity: validateIdentity(installation.identity, 'stored installation identity', failCorrupt),\n streamId: assertIdentifier(installation.streamId, 'stored installation streamId', 256, failCorrupt),\n nextSequence,\n headSequence,\n queuedCount,\n queuedBytes: assertInteger(\n installation.queuedBytes,\n 'stored installation queuedBytes',\n 0,\n V1_MAX_DURABLE_QUEUE_BYTES,\n failCorrupt\n ),\n authorityBarrier: hasOwn(installation, 'authorityBarrier')\n ? validateAuthorityBarrier(\n installation.authorityBarrier,\n 'stored installation authorityBarrier',\n failCorrupt\n )\n : null,\n ...(hasOwn(installation, 'flagAuthorityInitialized')\n ? installation.flagAuthorityInitialized === true\n ? { flagAuthorityInitialized: true as const }\n : failCorrupt('stored installation flagAuthorityInitialized must be true when present')\n : {}),\n ...(hasOwn(installation, 'replay')\n ? {\n replay: validateV2ReplayInstallationCounters(\n installation.replay,\n 'stored installation replay',\n failCorrupt,\n { siteKey: installation.siteKey, streamId: installation.streamId }\n ),\n }\n : {}),\n })\n return row\n}\n\n/** Internal bridge used by the unwired flag store to share the durable identity authority. */\nexport const validateV1FlagInstallation = (value: unknown, siteKey: string): V1InstallationRow =>\n validateInstallation(value, siteKey)\n\n/** Internal bridge used by the unwired replay store to read the shared installation row. */\nexport const validateV1ReplayInstallation = (value: unknown, siteKey: string): V1InstallationRow =>\n validateInstallation(value, siteKey)\n\n/** Adds only the zeroed replay counters; every other installation field is preserved. */\nexport const initializeV1ReplayInstallation = (value: unknown, siteKey: string): V1InstallationRow => {\n const current = validateInstallation(value, siteKey)\n return current.replay\n ? current\n : validateInstallation({ ...current, replay: initialV2ReplayInstallationCounters() }, siteKey)\n}\n\n/** Adds only the lazy empty flag context and marker; all queue/session fields are preserved. */\nexport const initializeV1FlagInstallation = (value: unknown, siteKey: string): V1InstallationRow => {\n const current = validateInstallation(value, siteKey)\n return validateInstallation(\n {\n ...current,\n identity: current.identity.flagContext\n ? current.identity\n : freezeV1Value({ ...current.identity, flagContext: emptyFlagContext() }),\n flagAuthorityInitialized: true,\n },\n siteKey\n )\n}\n\nconst requireFlagJsonObject = (\n value: unknown,\n field: string\n): ReturnType<typeof v1FlagJsonFromUnknown> & {\n readonly kind: 'object'\n} => {\n const node = v1FlagJsonFromUnknown(value, field)\n if (node.kind !== 'object') return failCorrupt(`${field} must be an object`)\n return node\n}\n\n/** Builds a detached flag context from the transaction-current installation row. */\nexport const v1FlagEvaluationContextFromInstallation = (\n value: unknown,\n siteKey: string,\n versionsValue: unknown\n): V1FlagEvaluationContext => {\n const installation = validateInstallation(value, siteKey)\n const versions = validateVersions(versionsValue, 'flag versions', failCorrupt)\n const context = installation.identity.flagContext ?? emptyFlagContext()\n const associatedGroupProperties: Record<string, V1JsonObject> = {}\n for (const groupType of Object.keys(installation.identity.groups)) {\n const properties = context.groupProperties[groupType]\n if (properties) {\n Object.defineProperty(associatedGroupProperties, groupType, {\n value: properties,\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n }\n return freezeV1Value({\n identity: freezeV1Value({\n anonymousId: installation.identity.anonymousId,\n userId: installation.identity.userId,\n revision: installation.identity.revision,\n }),\n contextRevision: installation.identity.contextRevision,\n optedOut: installation.identity.optedOut,\n personProperties: requireFlagJsonObject(context.personProperties, 'flag personProperties'),\n groups: requireFlagJsonObject(installation.identity.groups, 'flag groups'),\n groupProperties: requireFlagJsonObject(freezeV1Value(associatedGroupProperties), 'flag groupProperties'),\n versions: requireFlagJsonObject(versions, 'flag versions'),\n })\n}\n\nconst validateStoredRecord = (\n value: unknown,\n installation: V1InstallationRow,\n expectedSequence: number\n): V1DurableQueueRecord => {\n const stored = assertExactRecord(value, STORED_RECORD_KEYS, STORED_RECORD_KEYS, 'stored record', failCorrupt)\n if (stored.storageSchemaVersion !== V1_DURABLE_STORAGE_SCHEMA_VERSION) {\n fail('unsupported-storage', 'stored record schema version is unsupported')\n }\n if (stored.siteKey !== installation.siteKey || stored.streamId !== installation.streamId) {\n failCorrupt('stored record scope does not match its installation')\n }\n if (stored.sequence !== expectedSequence) {\n failCorrupt('stored records are not one contiguous ordered prefix')\n }\n const kind =\n stored.kind === 'event'\n ? 'event'\n : stored.kind === 'mutation'\n ? 'mutation'\n : failCorrupt('stored record kind is unsupported')\n const recordId = assertIdentifier(stored.recordId, 'stored record recordId', 256, failCorrupt)\n const sequence = assertInteger(stored.sequence, 'stored record sequence', 0, Number.MAX_SAFE_INTEGER, failCorrupt)\n const captureVersions = validateVersions(stored.captureVersions, 'stored record captureVersions', failCorrupt)\n const payloadBytes = assertInteger(\n stored.payloadBytes,\n 'stored record payloadBytes',\n 1,\n V1_MAX_DURABLE_QUEUE_BYTES,\n failCorrupt\n )\n const payloadJson = assertString(\n stored.payloadJson,\n 'stored record payloadJson',\n 1,\n V1_MAX_DURABLE_QUEUE_BYTES,\n failCorrupt\n )\n if (utf8ByteLength(payloadJson) !== payloadBytes) {\n failCorrupt('stored record payload byte count is inconsistent')\n }\n\n let parsed: { readonly value: unknown; readonly serialized: string }\n try {\n parsed = parseBoundedJson(payloadJson, V1_MAX_DURABLE_QUEUE_BYTES, 'stored record payload')\n } catch {\n return failCorrupt('stored record payload is not valid bounded JSON')\n }\n if (parsed.serialized !== payloadJson) {\n failCorrupt('stored record payload is not in the stable JSON encoding')\n }\n const wrapper = assertExactRecord(\n parsed.value,\n kind === 'event' ? ['kind', 'event'] : ['kind', 'mutation'],\n kind === 'event' ? ['kind', 'event'] : ['kind', 'mutation'],\n 'stored batch record',\n failCorrupt\n )\n if (wrapper.kind !== kind) {\n failCorrupt('stored batch record kind does not match its queue row')\n }\n const reference: V1DurableRecordReference = freezeV1Value({ sequence, kind, recordId })\n const batchRecord: V1BatchRecord =\n kind === 'event'\n ? freezeV1Value({\n kind: 'event',\n event: validateEvent(\n wrapper.event,\n { ...reference, streamId: installation.streamId },\n 'stored event',\n failCorrupt\n ),\n })\n : freezeV1Value({\n kind: 'mutation',\n mutation: validateMutation(wrapper.mutation, reference, 'stored mutation', failCorrupt),\n })\n if (JSON.stringify(batchRecord) !== payloadJson) {\n failCorrupt('stored batch record is not in the canonical v1 encoding')\n }\n if (batchRecord.kind === 'event' && !sameVersionContext(captureVersions, batchRecord.event.versions)) {\n failCorrupt('stored event capture versions do not match its payload versions')\n }\n return freezeV1Value({\n ...reference,\n siteKey: installation.siteKey,\n streamId: installation.streamId,\n captureVersions,\n batchRecord,\n payloadJson,\n payloadBytes,\n })\n}\n\nconst snapshotFromRow = (row: V1InstallationRow): V1DurableStateSnapshot =>\n freezeV1Value({\n siteKey: row.siteKey,\n identity: row.identity,\n streamId: row.streamId,\n nextSequence: row.nextSequence,\n headSequence: row.headSequence,\n queuedCount: row.queuedCount,\n queuedBytes: row.queuedBytes,\n })\n\nconst auditStoredQueue = async (transaction: V1DurableStorageTransaction, row: V1InstallationRow): Promise<void> => {\n let queuedCount = 0\n let queuedBytes = 0\n await transaction.scanAllRecords(row.siteKey, (rawRecord) => {\n if (queuedCount >= row.queuedCount) {\n return failCorrupt('stored queue contains more records than its counter')\n }\n const record = validateStoredRecord(rawRecord, row, row.headSequence + queuedCount)\n if (!Number.isSafeInteger(queuedBytes + record.payloadBytes)) {\n failCorrupt('stored queue byte counter is exhausted')\n }\n queuedBytes += record.payloadBytes\n queuedCount += 1\n return true\n })\n if (queuedCount !== row.queuedCount) {\n failCorrupt('stored queue count does not match its records')\n }\n if (queuedBytes !== row.queuedBytes) {\n failCorrupt('stored queue byte count does not match its records')\n }\n}\n\ninterface V1AuditedInstallation {\n readonly row: V1InstallationRow\n readonly requiresMigrationWrite: boolean\n}\n\nconst readAuditedState = async (\n transaction: V1DurableStorageTransaction,\n siteKey: string\n): Promise<V1AuditedInstallation | null> => {\n const installation = await transaction.getInstallation(siteKey)\n if (typeof installation === 'undefined') {\n let hasOrphanRecord = false\n await transaction.scanAllRecords(siteKey, () => {\n hasOrphanRecord = true\n return false\n })\n if (hasOrphanRecord) {\n failCorrupt('stored records exist without an installation')\n }\n return null\n }\n let row = validateInstallation(installation, siteKey)\n await auditStoredQueue(transaction, row)\n const legacyBarrier = !hasOwn(installation as Record<string, unknown>, 'authorityBarrier')\n const rawBarrier = (installation as Record<string, unknown>).authorityBarrier\n const legacyBarrierScope = isRecordValue(rawBarrier) && !hasOwn(rawBarrier, 'terminalScope')\n const legacyOptedOutSession = row.identity.optedOut && !isNullValue(row.identity.session)\n if (legacyOptedOutSession) {\n row = freezeV1Value({\n ...row,\n identity: validateIdentity({ ...row.identity, session: null }, 'migrated opted-out identity', failCorrupt),\n })\n }\n return freezeV1Value({\n row,\n requiresMigrationWrite: legacyBarrier || legacyBarrierScope || legacyOptedOutSession,\n })\n}\n\nconst readInstallation = async (\n transaction: V1DurableStorageTransaction,\n siteKey: string\n): Promise<V1InstallationRow> => {\n const installation = await transaction.getInstallation(siteKey)\n if (typeof installation === 'undefined') {\n return failCorrupt('installation disappeared after initialization')\n }\n return validateInstallation(installation, siteKey)\n}\n\nconst normalizeDraftArray = (value: unknown, field: string): readonly unknown[] => {\n const values = isArrayValue(value) ? value : failInput(`${field} must be an array of 1 to 100 records`)\n if (values.length < 1 || values.length > 100) {\n failInput(`${field} must be an array of 1 to 100 records`)\n }\n for (let index = 0; index < values.length; index += 1) {\n if (!hasOwnIndex(values, index)) {\n failInput(`${field} must not be sparse`)\n }\n }\n return values\n}\n\nconst normalizeEventDrafts = (value: unknown): readonly V1NormalizedEventDraft[] => {\n const values = normalizeDraftArray(value, 'event drafts')\n return freezeV1Value(\n values.map((value, index) => {\n const field = `event draft ${index}`\n const normalized = normalizeJsonObject(value, field, failInput)\n const draft = assertExactRecord(normalized, EVENT_DRAFT_KEYS, EVENT_DRAFT_KEYS, field, failInput)\n const kind: V1Event['kind'] =\n draft.kind === 'capture' ||\n draft.kind === 'screen' ||\n draft.kind === 'exception' ||\n draft.kind === 'diagnostic'\n ? draft.kind\n : failInput(`${field}.kind is unsupported`)\n return freezeV1Value({\n kind,\n name: assertIdentifierField(draft.name, \"name\", field, 512, failInput),\n occurredAt: assertDateTimeField(draft.occurredAt, \"occurredAt\", field, failInput),\n expectedSessionId: assertIdentifierField(draft.expectedSessionId, \"expectedSessionId\", field, 256, failInput),\n properties: normalizeJsonObjectField(draft.properties, \"properties\", field, failInput),\n versions: validateVersionsField(draft.versions, \"versions\", field, failInput),\n })\n })\n )\n}\n\nconst normalizeMutationDrafts = (value: unknown): readonly V1NormalizedMutationDraft[] => {\n const values = normalizeDraftArray(value, 'mutation drafts')\n return freezeV1Value(\n values.map((value, index) => {\n const field = `mutation draft ${index}`\n const normalized = normalizeJsonObject(value, field, failInput)\n const draft = assertExactRecord(normalized, MUTATION_DRAFT_KEYS, MUTATION_DRAFT_KEYS, field, failInput)\n return freezeV1Value({\n occurredAt: assertDateTimeField(draft.occurredAt, \"occurredAt\", field, failInput),\n change: validateMutationChange(draft.change, `${field}.change`, failInput),\n versions: validateVersionsField(draft.versions, \"versions\", field, failInput),\n })\n })\n )\n}\n\nconst normalizeSessionUpdate = (value: unknown): V1DurableEventSessionUpdate => {\n const normalized = normalizeJsonObject(value, 'event session update', failInput)\n if (normalized.type === 'preserve') {\n assertExactRecord(normalized, ['type'], ['type'], 'event session update', failInput)\n return freezeV1Value({ type: 'preserve' })\n }\n if (normalized.type === 'replace') {\n const update = assertExactRecord(\n normalized,\n SESSION_UPDATE_KEYS,\n SESSION_UPDATE_KEYS,\n 'event session update',\n failInput\n )\n const session = validateSessionField(update.session, \"session\", \"event session update\", failInput)\n if (!session) {\n return failInput('event session update.session must not be null')\n }\n const expectedCurrentSessionId = isNullValue(update.expectedCurrentSessionId)\n ? null\n : assertIdentifierField(update.expectedCurrentSessionId, \"expectedCurrentSessionId\", \"event session update\", 256, failInput)\n return freezeV1Value({ type: 'replace', expectedCurrentSessionId, session })\n }\n return failInput('event session update.type is unsupported')\n}\n\nconst normalizeLocalChange = (value: unknown): V1DurableLocalChange => {\n const normalized = normalizeJsonObject(value, 'local change', failInput)\n const occurredAt = (): string => assertDateTimeField(normalized.occurredAt, \"occurredAt\", \"local change\", failInput)\n switch (normalized.type) {\n case 'setOptedOut': {\n const change = assertExactRecord(\n normalized,\n ['type', 'optedOut', 'occurredAt'],\n ['type', 'optedOut', 'occurredAt'],\n 'local change',\n failInput\n )\n return freezeV1Value({\n type: 'setOptedOut',\n optedOut: assertBooleanField(change.optedOut, \"optedOut\", \"local change\", failInput),\n occurredAt: occurredAt(),\n })\n }\n case 'resetGroups':\n assertExactRecord(normalized, ['type', 'occurredAt'], ['type', 'occurredAt'], 'local change', failInput)\n return freezeV1Value({ type: 'resetGroups', occurredAt: occurredAt() })\n case 'resetIdentity': {\n const change = assertExactRecord(\n normalized,\n ['type', 'anonymousId', 'occurredAt'],\n ['type', 'anonymousId', 'occurredAt'],\n 'local change',\n failInput\n )\n return freezeV1Value({\n type: 'resetIdentity',\n anonymousId: assertIdentifierField(change.anonymousId, \"anonymousId\", \"local change\", 256, failInput),\n occurredAt: occurredAt(),\n })\n }\n case 'setSuperProperties': {\n const change = assertExactRecord(\n normalized,\n ['type', 'set', 'setOnce', 'unset', 'occurredAt'],\n ['type', 'set', 'setOnce', 'unset', 'occurredAt'],\n 'local change',\n failInput\n )\n return freezeV1Value({\n type: 'setSuperProperties',\n set: normalizeJsonObjectField(change.set, \"set\", \"local change\", failInput),\n setOnce: normalizeJsonObjectField(change.setOnce, \"setOnce\", \"local change\", failInput),\n unset: validatePropertyNamesField(change.unset, \"unset\", \"local change\", failInput),\n occurredAt: occurredAt(),\n })\n }\n case 'replaceSession': {\n const change = assertExactRecord(\n normalized,\n ['type', 'expectedCurrentSessionId', 'session'],\n ['type', 'expectedCurrentSessionId', 'session'],\n 'local change',\n failInput\n )\n const session = validateSessionField(change.session, \"session\", \"local change\", failInput)\n if (!session) {\n return failInput('local change.session must not be null')\n }\n return freezeV1Value({\n type: 'replaceSession',\n expectedCurrentSessionId: isNullValue(change.expectedCurrentSessionId)\n ? null\n : assertIdentifier(\n change.expectedCurrentSessionId,\n 'local change.expectedCurrentSessionId',\n 256,\n failInput\n ),\n session,\n })\n }\n case 'clearSession': {\n const change = assertExactRecord(\n normalized,\n ['type', 'expectedCurrentSessionId', 'occurredAt'],\n ['type', 'expectedCurrentSessionId', 'occurredAt'],\n 'local change',\n failInput\n )\n return freezeV1Value({\n type: 'clearSession',\n expectedCurrentSessionId: isNullValue(change.expectedCurrentSessionId)\n ? null\n : assertIdentifier(\n change.expectedCurrentSessionId,\n 'local change.expectedCurrentSessionId',\n 256,\n failInput\n ),\n occurredAt: occurredAt(),\n })\n }\n case 'setFlagContext': {\n const change = assertExactRecord(\n normalized,\n [\n 'type',\n 'personProperties',\n 'groupProperties',\n 'mergePersonProperties',\n 'mergeGroupProperties',\n 'unsetGroupTypes',\n 'occurredAt',\n ],\n ['type', 'personProperties', 'groupProperties', 'occurredAt'],\n 'local change',\n failInput\n )\n const groupProperties = isNullValue(change.groupProperties)\n ? null\n : validateFlagContext(\n { schemaVersion: 1, personProperties: {}, groupProperties: change.groupProperties },\n 'local change',\n failInput\n ).groupProperties\n return freezeV1Value({\n type: 'setFlagContext',\n personProperties: isNullValue(change.personProperties)\n ? null\n : validateFlagProperties(change.personProperties, 'local change.personProperties', failInput),\n groupProperties,\n ...(hasOwn(change, 'mergePersonProperties')\n ? {\n mergePersonProperties: assertBooleanField(change.mergePersonProperties, \"mergePersonProperties\", \"local change\", failInput),\n }\n : {}),\n ...(hasOwn(change, 'mergeGroupProperties')\n ? {\n mergeGroupProperties: assertBooleanField(change.mergeGroupProperties, \"mergeGroupProperties\", \"local change\", failInput),\n }\n : {}),\n ...(hasOwn(change, 'unsetGroupTypes')\n ? {\n unsetGroupTypes: validatePropertyNamesField(change.unsetGroupTypes, \"unsetGroupTypes\", \"local change\", failInput),\n }\n : {}),\n occurredAt: occurredAt(),\n })\n }\n default:\n return failInput('local change.type is unsupported')\n }\n}\n\nconst streamFingerprint = (streamId: string): string => {\n const moduli = [4_294_967_291, 4_294_967_279, 4_294_967_231, 4_294_967_197] as const\n const multipliers = [131, 137, 139, 149] as const\n const hashes = [2_166_136_261, 2_166_136_247, 2_166_136_229, 2_166_136_213]\n for (let index = 0; index < streamId.length; index += 1) {\n const code = streamId.charCodeAt(index)\n for (let part = 0; part < hashes.length; part += 1) {\n hashes[part] = (hashes[part] * multipliers[part] + code + part) % moduli[part]\n }\n }\n return hashes.map((hash) => hash.toString(16).padStart(8, '0')).join('')\n}\n\nconst recordIdFor = (kind: 'event' | 'mutation', streamId: string, sequence: number): string =>\n `elu_${kind === 'event' ? 'e' : 'm'}_${streamFingerprint(streamId)}_${sequence.toString(36)}`\n\nconst incrementStateCounter = (value: number, field: string): number => {\n if (value >= Number.MAX_SAFE_INTEGER) {\n return fail('sequence-exhausted', `${field} is exhausted`)\n }\n return value + 1\n}\n\nconst requireTimestampNotBefore = (candidate: string, current: string, field: string): void => {\n if (compareV1DateTimes(candidate, current) < 0) {\n failInput(`${field} must not move persisted time backward`)\n }\n}\n\nconst requireCurrentSessionId = (\n current: V1PersistedIdentityState,\n expectedCurrentSessionId: string | null,\n field: string\n): void => {\n if (expectedCurrentSessionId !== (current.session?.id ?? null)) {\n failInput(`${field} lost its current-session compare-and-swap`)\n }\n}\n\nconst copyGroupsWithAssociation = (\n groups: Readonly<Record<string, string>>,\n groupType: string,\n groupKey: string\n): Readonly<Record<string, string>> => {\n const result: Record<string, string> = {}\n for (const key of Object.keys(groups)) {\n Object.defineProperty(result, key, {\n value: groups[key],\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n Object.defineProperty(result, groupType, {\n value: groupKey,\n enumerable: true,\n configurable: true,\n writable: true,\n })\n return freezeV1Value(result)\n}\n\nconst applyJsonProperties = (\n current: V1JsonObject,\n set: V1JsonObject,\n setOnce: V1JsonObject,\n unset: readonly string[]\n): V1JsonObject => {\n const result: Record<string, V1JsonValue> = {}\n const define = (key: string, value: V1JsonValue): void => {\n Object.defineProperty(result, key, { value, enumerable: true, configurable: true, writable: true })\n }\n for (const key of Object.keys(current)) define(key, current[key])\n for (const key of unset) delete result[key]\n for (const key of Object.keys(setOnce)) {\n if (!hasOwn(result, key)) define(key, setOnce[key])\n }\n for (const key of Object.keys(set)) define(key, set[key])\n return freezeV1Value(result)\n}\n\nconst copyFlagGroupProperties = (\n groups: Readonly<Record<string, V1JsonObject>>,\n groupType: string,\n properties: V1JsonObject | null\n): Readonly<Record<string, V1JsonObject>> => {\n const result: Record<string, V1JsonObject> = {}\n for (const key of Object.keys(groups)) {\n if (key !== groupType) {\n Object.defineProperty(result, key, {\n value: groups[key],\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n }\n if (properties) {\n Object.defineProperty(result, groupType, {\n value: properties,\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n return freezeV1Value(result)\n}\n\nconst mergeFlagGroupProperties = (\n current: Readonly<Record<string, V1JsonObject>>,\n overlay: Readonly<Record<string, V1JsonObject>>\n): Readonly<Record<string, V1JsonObject>> => {\n let result = current\n for (const groupType of Object.keys(overlay)) {\n result = copyFlagGroupProperties(\n result,\n groupType,\n applyJsonProperties(result[groupType] ?? freezeV1Value({}), overlay[groupType], freezeV1Value({}), [])\n )\n }\n return result\n}\n\nconst withFlagContext = (\n current: V1PersistedIdentityState,\n update: (context: V1PersistedFlagContext) => V1PersistedFlagContext\n): Readonly<{ flagContext?: V1PersistedFlagContext }> =>\n current.flagContext ? freezeV1Value({ flagContext: update(current.flagContext) }) : freezeV1Value({})\n\nconst updatePersonFlagContext = (\n context: V1PersistedFlagContext,\n set: V1JsonObject,\n setOnce: V1JsonObject,\n unset: readonly string[]\n): V1PersistedFlagContext =>\n freezeV1Value({\n ...context,\n personProperties: applyJsonProperties(context.personProperties, set, setOnce, unset),\n })\n\nconst mergeEventProperties = (\n superProperties: V1JsonObject,\n eventProperties: V1JsonObject,\n versions: V1VersionContext\n): V1JsonObject => {\n const merged = applyJsonProperties(superProperties, eventProperties, freezeV1Value({}), freezeV1Value([]))\n return applyJsonProperties(\n merged,\n freezeV1Value({\n $elu_contract_version: versions.contractVersion,\n $elu_sdk_version: versions.runtime.version,\n $elu_facade_version: versions.facade.version,\n }),\n freezeV1Value({}),\n freezeV1Value([])\n )\n}\n\nconst validateSessionTransition = (\n current: V1PersistedIdentityState,\n session: V1PersistedSessionState,\n eventEligible: boolean\n): V1PersistedIdentityState => {\n if (eventEligible && session.lifecycle !== 'active') {\n failInput('event session must be active')\n }\n if ((!current.session || current.session.id !== session.id) && session.lifecycle !== 'active') {\n failInput('a replacement session must begin in the active lifecycle')\n }\n const transitionAt = session.backgroundedAt ?? session.lastActivityAt\n requireTimestampNotBefore(transitionAt, current.updatedAt, 'session activity')\n if (current.session) {\n if (current.session.id === session.id) {\n if (current.session.startedAt !== session.startedAt) {\n failInput('a retained session ID must keep its original startedAt')\n }\n requireTimestampNotBefore(session.lastActivityAt, current.session.lastActivityAt, 'session lastActivityAt')\n if (\n hasV1ElapsedAtLeastSeconds(\n session.lastActivityAt,\n current.session.lastActivityAt,\n Math.min(current.session.timeoutSeconds, session.timeoutSeconds)\n )\n ) {\n failInput('a retained session must remain before its idle timeout')\n }\n } else {\n requireTimestampNotBefore(\n session.startedAt,\n current.session.lastActivityAt,\n 'replacement session startedAt'\n )\n }\n }\n return validateIdentity({ ...current, session, updatedAt: transitionAt }, 'derived session identity', failInput)\n}\n\nconst applyMutation = (\n current: V1PersistedIdentityState,\n draft: V1NormalizedMutationDraft\n): V1PersistedIdentityState => {\n requireTimestampNotBefore(draft.occurredAt, current.updatedAt, 'mutation occurredAt')\n const contextRevision = incrementStateCounter(current.contextRevision, 'identity context revision')\n const base = { ...current, contextRevision, updatedAt: draft.occurredAt }\n const change = draft.change\n switch (change.type) {\n case 'identify': {\n const identityChanged = change.userId !== current.userId\n return validateIdentity(\n {\n ...base,\n ...withFlagContext(current, (context) =>\n updatePersonFlagContext(context, change.set, change.setOnce, freezeV1Value([]))\n ),\n userId: change.userId,\n revision: identityChanged\n ? incrementStateCounter(current.revision, 'identity revision')\n : current.revision,\n },\n 'derived identify identity',\n failInput\n )\n }\n case 'linkAlias':\n return validateIdentity(base, 'derived mutation identity', failInput)\n case 'setPersonProperties':\n return validateIdentity(\n {\n ...base,\n ...withFlagContext(current, (context) =>\n updatePersonFlagContext(context, change.set, change.setOnce, change.unset)\n ),\n },\n 'derived person properties identity',\n failInput\n )\n case 'associateGroup':\n return validateIdentity(\n {\n ...base,\n groups: copyGroupsWithAssociation(current.groups, change.groupType, change.groupKey),\n ...(current.groups[change.groupType] !== change.groupKey\n ? withFlagContext(current, (context) =>\n freezeV1Value({\n ...context,\n groupProperties: copyFlagGroupProperties(\n context.groupProperties,\n change.groupType,\n null\n ),\n })\n )\n : {}),\n },\n 'derived group identity',\n failInput\n )\n case 'setGroupProperties':\n if (current.groups[change.groupType] !== change.groupKey) {\n return failInput('group properties mutation must match the persisted group association')\n }\n return validateIdentity(\n {\n ...base,\n ...withFlagContext(current, (context) => {\n const properties = applyJsonProperties(\n context.groupProperties[change.groupType] ?? freezeV1Value({}),\n change.set,\n change.setOnce,\n change.unset\n )\n return freezeV1Value({\n ...context,\n groupProperties: copyFlagGroupProperties(\n context.groupProperties,\n change.groupType,\n properties\n ),\n })\n }),\n },\n 'derived group properties identity',\n failInput\n )\n }\n}\n\nconst applyLocalChange = (\n current: V1PersistedIdentityState,\n change: V1DurableLocalChange\n): V1PersistedIdentityState => {\n switch (change.type) {\n case 'replaceSession':\n requireCurrentSessionId(current, change.expectedCurrentSessionId, 'local session replacement')\n return validateSessionTransition(current, change.session, false)\n case 'clearSession':\n requireCurrentSessionId(current, change.expectedCurrentSessionId, 'local session clear')\n requireTimestampNotBefore(change.occurredAt, current.updatedAt, 'local change occurredAt')\n return validateIdentity(\n { ...current, session: null, updatedAt: change.occurredAt },\n 'derived local identity',\n failInput\n )\n case 'setOptedOut': {\n requireTimestampNotBefore(change.occurredAt, current.updatedAt, 'local change occurredAt')\n const optChanged = change.optedOut !== current.optedOut\n const flagContextWasCleared = change.optedOut && flagContextHasValues(current.flagContext)\n return validateIdentity(\n {\n ...current,\n optedOut: change.optedOut,\n session: change.optedOut ? null : current.session,\n contextRevision:\n optChanged || flagContextWasCleared\n ? incrementStateCounter(current.contextRevision, 'identity context revision')\n : current.contextRevision,\n ...(change.optedOut && current.flagContext ? { flagContext: emptyFlagContext() } : {}),\n updatedAt: change.occurredAt,\n },\n 'derived local identity',\n failInput\n )\n }\n case 'resetGroups':\n requireTimestampNotBefore(change.occurredAt, current.updatedAt, 'local change occurredAt')\n return validateIdentity(\n {\n ...current,\n groups: freezeV1Value({}),\n ...(current.flagContext\n ? { flagContext: freezeV1Value({ ...current.flagContext, groupProperties: freezeV1Value({}) }) }\n : {}),\n contextRevision: incrementStateCounter(current.contextRevision, 'identity context revision'),\n updatedAt: change.occurredAt,\n },\n 'derived local identity',\n failInput\n )\n case 'resetIdentity':\n requireTimestampNotBefore(change.occurredAt, current.updatedAt, 'local change occurredAt')\n if (change.anonymousId === current.anonymousId) {\n return failInput('resetIdentity must rotate the anonymous ID')\n }\n return validateIdentity(\n {\n ...current,\n revision: incrementStateCounter(current.revision, 'identity revision'),\n contextRevision: incrementStateCounter(current.contextRevision, 'identity context revision'),\n anonymousId: change.anonymousId,\n userId: null,\n groups: freezeV1Value({}),\n superProperties: freezeV1Value({}),\n ...(current.flagContext ? { flagContext: emptyFlagContext() } : {}),\n session: null,\n updatedAt: change.occurredAt,\n },\n 'derived reset identity',\n failInput\n )\n case 'setSuperProperties':\n requireTimestampNotBefore(change.occurredAt, current.updatedAt, 'local change occurredAt')\n return validateIdentity(\n {\n ...current,\n contextRevision: incrementStateCounter(current.contextRevision, 'identity context revision'),\n superProperties: applyJsonProperties(\n current.superProperties,\n change.set,\n change.setOnce,\n change.unset\n ),\n updatedAt: change.occurredAt,\n },\n 'derived local identity',\n failInput\n )\n case 'setFlagContext': {\n requireTimestampNotBefore(change.occurredAt, current.updatedAt, 'local change occurredAt')\n const context = current.flagContext ?? emptyFlagContext()\n let personProperties = context.personProperties\n if (change.personProperties) {\n personProperties = change.mergePersonProperties\n ? applyJsonProperties(context.personProperties, change.personProperties, freezeV1Value({}), [])\n : change.personProperties\n }\n let groupProperties = context.groupProperties\n if (change.unsetGroupTypes) {\n for (const groupType of change.unsetGroupTypes) {\n groupProperties = copyFlagGroupProperties(groupProperties, groupType, null)\n }\n }\n if (change.groupProperties) {\n groupProperties = change.mergeGroupProperties\n ? mergeFlagGroupProperties(groupProperties, change.groupProperties)\n : change.groupProperties\n }\n return validateIdentity(\n {\n ...current,\n contextRevision: incrementStateCounter(current.contextRevision, 'identity context revision'),\n flagContext: freezeV1Value({\n schemaVersion: 1,\n personProperties,\n groupProperties,\n }),\n updatedAt: change.occurredAt,\n },\n 'derived local identity',\n failInput\n )\n }\n }\n}\n\nconst storedRecordFromBatch = (\n batchRecord: V1BatchRecord,\n kind: 'event' | 'mutation',\n recordId: string,\n captureVersions: V1VersionContext,\n installation: V1InstallationRow,\n sequence: number\n): { readonly stored: V1StoredRecordRow; readonly exposed: V1DurableQueueRecord } => {\n const reference: V1DurableRecordReference = freezeV1Value({\n sequence,\n kind,\n recordId,\n })\n const payloadJson = JSON.stringify(batchRecord)\n const payloadBytes = utf8ByteLength(payloadJson)\n const stored: V1StoredRecordRow = freezeV1Value({\n storageSchemaVersion: 1,\n siteKey: installation.siteKey,\n streamId: installation.streamId,\n sequence,\n kind,\n recordId,\n captureVersions,\n payloadJson,\n payloadBytes,\n })\n const exposed: V1DurableQueueRecord = freezeV1Value({\n ...reference,\n siteKey: installation.siteKey,\n streamId: installation.streamId,\n captureVersions,\n batchRecord,\n payloadJson,\n payloadBytes,\n })\n return freezeV1Value({ stored, exposed })\n}\n\nconst encodeEventDraft = (\n draft: V1NormalizedEventDraft,\n installation: V1InstallationRow,\n sequence: number\n): { readonly stored: V1StoredRecordRow; readonly exposed: V1DurableQueueRecord } => {\n const session = installation.identity.session\n if (!session || session.lifecycle !== 'active') {\n return failInput('events require an active persisted session')\n }\n if (draft.expectedSessionId !== session.id) {\n return failInput('event session expectation does not match the persisted session')\n }\n if (\n compareV1DateTimes(draft.occurredAt, session.startedAt) < 0 ||\n compareV1DateTimes(draft.occurredAt, session.lastActivityAt) > 0\n ) {\n return failInput('event occurredAt must fall within the persisted session activity window')\n }\n const recordId = recordIdFor('event', installation.streamId, sequence)\n const reference = freezeV1Value({ sequence, kind: 'event' as const, recordId })\n const event = validateEvent(\n {\n schemaVersion: 1,\n eventId: recordId,\n streamId: installation.streamId,\n sequence,\n contextRevision: installation.identity.contextRevision,\n kind: draft.kind,\n name: draft.name,\n occurredAt: draft.occurredAt,\n identity: {\n anonymousId: installation.identity.anonymousId,\n userId: installation.identity.userId,\n revision: installation.identity.revision,\n },\n sessionId: session.id,\n properties: mergeEventProperties(installation.identity.superProperties, draft.properties, draft.versions),\n groups: installation.identity.groups,\n versions: draft.versions,\n },\n { ...reference, streamId: installation.streamId },\n 'derived event',\n failInput\n )\n return storedRecordFromBatch(\n freezeV1Value({ kind: 'event', event }),\n 'event',\n recordId,\n event.versions,\n installation,\n sequence\n )\n}\n\nconst validateEventCausality = (\n lowerBound: string,\n session: V1PersistedSessionState | null,\n drafts: readonly V1NormalizedEventDraft[]\n): void => {\n if (!session || session.lifecycle !== 'active') {\n return failInput('events require an active persisted session')\n }\n let previousOccurredAt = lowerBound\n for (let index = 0; index < drafts.length; index += 1) {\n const draft = drafts[index]\n if (draft.expectedSessionId !== session.id) {\n failInput(`event draft ${index} session expectation does not match the persisted session`)\n }\n requireTimestampNotBefore(draft.occurredAt, previousOccurredAt, `event draft ${index} occurredAt`)\n if (\n compareV1DateTimes(draft.occurredAt, session.startedAt) < 0 ||\n compareV1DateTimes(draft.occurredAt, session.lastActivityAt) > 0\n ) {\n failInput(`event draft ${index} occurredAt must fall within the persisted session activity window`)\n }\n previousOccurredAt = draft.occurredAt\n }\n}\n\nconst encodeMutationDraft = (\n draft: V1NormalizedMutationDraft,\n installation: V1InstallationRow,\n sequence: number\n): { readonly stored: V1StoredRecordRow; readonly exposed: V1DurableQueueRecord } => {\n const recordId = recordIdFor('mutation', installation.streamId, sequence)\n const mutation = validateMutation(\n {\n mutationId: recordId,\n sequence,\n contextRevision: installation.identity.contextRevision,\n occurredAt: draft.occurredAt,\n subject: {\n anonymousId: installation.identity.anonymousId,\n userId: installation.identity.userId,\n identityRevision: installation.identity.revision,\n },\n change: draft.change,\n },\n freezeV1Value({ sequence, kind: 'mutation', recordId }),\n 'derived mutation',\n failInput\n )\n return storedRecordFromBatch(\n freezeV1Value({ kind: 'mutation', mutation }),\n 'mutation',\n recordId,\n draft.versions,\n installation,\n sequence\n )\n}\n\nconst normalizeReferences = (value: unknown): readonly V1DurableRecordReference[] => {\n const values = isArrayValue(value)\n ? value\n : failInput('acknowledgement references must be an array of at most 1000 records')\n if (values.length > 1000) failInput('acknowledgement references must be an array of at most 1000 records')\n const references: V1DurableRecordReference[] = []\n for (let index = 0; index < values.length; index += 1) {\n if (!hasOwnIndex(values, index)) {\n failInput('acknowledgement references must not be sparse')\n }\n const reference = assertExactRecord(\n values[index],\n ACKNOWLEDGEMENT_REFERENCE_KEYS,\n ACKNOWLEDGEMENT_REFERENCE_KEYS,\n `acknowledgement reference ${index}`,\n failInput\n )\n const kind =\n reference.kind === 'event'\n ? 'event'\n : reference.kind === 'mutation'\n ? 'mutation'\n : failInput(`acknowledgement reference ${index}.kind is unsupported`)\n const sequence = assertInteger(\n reference.sequence,\n `acknowledgement reference ${index}.sequence`,\n 0,\n Number.MAX_SAFE_INTEGER,\n failInput\n )\n if (index > 0 && sequence !== references[index - 1].sequence + 1) {\n failInput('acknowledgement references must be contiguous and ordered')\n }\n references.push(\n freezeV1Value({\n sequence,\n kind,\n recordId: assertIdentifier(\n reference.recordId,\n `acknowledgement reference ${index}.recordId`,\n 256,\n failInput\n ),\n })\n )\n }\n return freezeV1Value(references)\n}\n\nconst normalizeAcknowledgement = (value: unknown): V1DurableAcknowledgement => {\n const normalized = normalizeJsonObject(value, 'acknowledgement', failInput)\n const acknowledgement = assertExactRecord(\n normalized,\n ACKNOWLEDGEMENT_KEYS,\n ACKNOWLEDGEMENT_KEYS,\n 'acknowledgement',\n failInput\n )\n return freezeV1Value({\n siteKey: assertIdentifierField(acknowledgement.siteKey, \"siteKey\", \"acknowledgement\", 128, failInput),\n streamId: assertIdentifierField(acknowledgement.streamId, \"streamId\", \"acknowledgement\", 256, failInput),\n records: normalizeReferences(acknowledgement.records),\n })\n}\n\nconst sameReference = (left: V1DurableRecordReference, right: V1DurableRecordReference): boolean =>\n left.sequence === right.sequence && left.kind === right.kind && left.recordId === right.recordId\n\ninterface V1NormalizedRuntimeOptions {\n readonly siteKey: string\n readonly limits: V1DurableRuntimeLimits\n readonly storageFactory: V1DurableStorageFactory\n readonly captureRuntime: Readonly<{\n readonly sha256?: V1Sha256\n readonly clock: V1CaptureRuntimeClock\n readonly createSessionId: () => string\n }>\n}\n\nconst defaultCaptureClock: V1CaptureRuntimeClock = freezeV1Value({\n wallNow: (): string => new Date().toISOString(),\n monotonicNowMs: (): number => {\n const clock = typeof globalThis === 'undefined' ? undefined : globalThis.performance\n return clock ? clock.now() : Number.NaN\n },\n})\n\n/** Lowercase hex from platform randomness; fails closed when the platform offers none. */\nexport const v1SecureRandomHex = (byteCount: number, purpose: string): string => {\n const cryptoApi = typeof globalThis === 'undefined' ? undefined : globalThis.crypto\n if (!cryptoApi || !isFunctionValue(cryptoApi.getRandomValues)) {\n return failInput(`secure randomness is unavailable for ${purpose}`)\n }\n const bytes = new Uint8Array(byteCount)\n cryptoApi.getRandomValues(bytes)\n let hexadecimal = ''\n for (const byte of bytes) hexadecimal += byte.toString(16).padStart(2, '0')\n return hexadecimal\n}\n\nconst defaultSessionId = (): string => `elu_s_${v1SecureRandomHex(16, 'capture session creation')}`\n\n/** Mints a fresh anonymous identity from 128 bits of platform randomness; never derived from other state. */\nexport const createV1AnonymousId = (): string => `anon_${v1SecureRandomHex(16, 'anonymous identity creation')}`\n\n/** Mints the installation stream identity that events and mutations share for the life of the install. */\nexport const createV1StreamId = (): string => `stream_${v1SecureRandomHex(16, 'stream identity creation')}`\n\n/**\n * The identity a fresh install persists before its first event: no user, groups, super properties,\n * or session, opted in, at revision zero. Callers mint the anonymous ID once and persist this row\n * atomically; later constructions read the persisted row instead of minting again.\n */\nexport const createV1FreshInstallIdentity = (anonymousId: string, updatedAt: string): V1PersistedIdentityState =>\n validateIdentity(\n {\n schemaVersion: 1,\n revision: 0,\n contextRevision: 0,\n anonymousId,\n userId: null,\n groups: {},\n superProperties: {},\n session: null,\n optedOut: false,\n updatedAt,\n },\n 'fresh install identity',\n failInput\n )\n\nconst validateOptions = (value: V1DurableRuntimeOptions): V1NormalizedRuntimeOptions => {\n const options = assertExactRecord(\n value,\n ['siteKey', 'limits', 'storageFactory', 'captureRuntime'],\n ['siteKey', 'limits'],\n 'options',\n failInput\n )\n const limits = assertExactRecord(\n options.limits,\n ['maxCount', 'maxBytes'],\n ['maxCount', 'maxBytes'],\n 'options.limits',\n failInput\n )\n const storageFactoryCandidate = hasOwn(options, 'storageFactory') ? options.storageFactory : openV1IndexedDBStorage\n const storageFactory: V1DurableStorageFactory = isFunctionValue(storageFactoryCandidate)\n ? (storageFactoryCandidate as V1DurableStorageFactory)\n : failInput('options.storageFactory must be a function')\n const captureRuntime = hasOwn(options, 'captureRuntime')\n ? assertExactRecord(\n options.captureRuntime,\n ['sha256', 'clock', 'createSessionId'],\n [],\n 'options.captureRuntime',\n failInput\n )\n : {}\n const clock = hasOwn(captureRuntime, 'clock')\n ? assertExactRecord(\n captureRuntime.clock,\n ['wallNow', 'monotonicNowMs'],\n ['wallNow', 'monotonicNowMs'],\n 'options.captureRuntime.clock',\n failInput\n )\n : defaultCaptureClock\n if (!isFunctionValue(clock.wallNow) || !isFunctionValue(clock.monotonicNowMs)) {\n failInput('options.captureRuntime.clock functions are required')\n }\n if (hasOwn(captureRuntime, 'sha256') && !isFunctionValue(captureRuntime.sha256)) {\n failInput('options.captureRuntime.sha256 must be a function')\n }\n const createSessionId = hasOwn(captureRuntime, 'createSessionId')\n ? captureRuntime.createSessionId\n : defaultSessionId\n if (!isFunctionValue(createSessionId)) failInput('options.captureRuntime.createSessionId must be a function')\n return freezeV1Value({\n siteKey: assertIdentifierField(options.siteKey, \"siteKey\", \"options\", 128, failInput),\n limits: freezeV1Value({\n maxCount: assertIntegerField(limits.maxCount, \"maxCount\", \"options.limits\", 1, V1_MAX_DURABLE_QUEUE_COUNT, failInput),\n maxBytes: assertIntegerField(limits.maxBytes, \"maxBytes\", \"options.limits\", 1, V1_MAX_DURABLE_QUEUE_BYTES, failInput),\n }),\n storageFactory,\n captureRuntime: freezeV1Value({\n ...(hasOwn(captureRuntime, 'sha256') ? { sha256: captureRuntime.sha256 as V1Sha256 } : {}),\n clock: freezeV1Value({\n wallNow: clock.wallNow as () => string,\n monotonicNowMs: clock.monotonicNowMs as () => number,\n }),\n createSessionId: createSessionId as () => string,\n }),\n })\n}\n\nconst sameAuthorityBarrier = (\n left: V1PersistedAuthorityBarrier | null,\n right: V1PersistedAuthorityBarrier | null\n): boolean =>\n left === right ||\n (!!left &&\n !!right &&\n left.kind === right.kind &&\n left.terminalScope === right.terminalScope &&\n left.configIssuedAt === right.configIssuedAt &&\n left.configSemanticHash === right.configSemanticHash &&\n left.policySourceHash === right.policySourceHash &&\n left.contextRevision === right.contextRevision &&\n left.decisionHash === right.decisionHash)\n\nconst terminalBarrierFrom = (\n boundary: Pick<\n V1PersistedAuthorityBarrier,\n 'configIssuedAt' | 'configSemanticHash' | 'policySourceHash' | 'contextRevision' | 'decisionHash'\n >,\n terminalScope: 'config' | 'context'\n): V1PersistedAuthorityBarrier => freezeV1Value({ kind: 'terminal', terminalScope, ...boundary })\n\nconst terminalBarrierAtCurrentConfig = (\n current: V1PersistedAuthorityBarrier | null,\n terminalScope: 'config' | 'context' = 'config'\n): V1PersistedAuthorityBarrier =>\n terminalBarrierFrom(\n {\n configIssuedAt: current?.configIssuedAt ?? null,\n configSemanticHash: current?.configSemanticHash ?? null,\n policySourceHash: current?.policySourceHash ?? null,\n contextRevision: current?.contextRevision ?? null,\n decisionHash: current?.decisionHash ?? null,\n },\n terminalScope\n )\n\nconst mergeAuthorityBarrier = (\n current: V1PersistedAuthorityBarrier | null,\n incoming: V1PersistedAuthorityBarrier\n): V1PersistedAuthorityBarrier => {\n if (!current) return incoming\n if (!incoming.configIssuedAt || !incoming.configSemanticHash) {\n return terminalBarrierAtCurrentConfig(current, 'config')\n }\n if (!current.configIssuedAt || !current.configSemanticHash) return incoming\n const configOrder = compareV1DateTimes(incoming.configIssuedAt, current.configIssuedAt)\n if (configOrder < 0) return current\n if (configOrder > 0) return incoming\n if (incoming.configSemanticHash !== current.configSemanticHash) {\n return terminalBarrierAtCurrentConfig(current, 'config')\n }\n if (current.kind === 'terminal' && current.terminalScope === 'config') return current\n if (incoming.kind === 'terminal' && incoming.terminalScope === 'config') {\n return terminalBarrierFrom(\n {\n configIssuedAt: current.configIssuedAt,\n configSemanticHash: current.configSemanticHash,\n policySourceHash: current.policySourceHash,\n contextRevision: current.contextRevision,\n decisionHash: incoming.decisionHash ?? current.decisionHash,\n },\n 'config'\n )\n }\n if (incoming.policySourceHash !== current.policySourceHash) {\n return terminalBarrierAtCurrentConfig(current, 'config')\n }\n const currentContext = current.contextRevision ?? -1\n const incomingContext = incoming.contextRevision ?? currentContext\n if (incomingContext < currentContext) return current\n if (incomingContext > currentContext) return freezeV1Value({ ...incoming, contextRevision: incomingContext })\n if (current.kind === 'terminal' || incoming.kind === 'terminal') {\n return terminalBarrierFrom(\n {\n configIssuedAt: current.configIssuedAt,\n configSemanticHash: current.configSemanticHash,\n policySourceHash: current.policySourceHash,\n contextRevision: current.contextRevision,\n decisionHash:\n incoming.kind === 'terminal'\n ? (incoming.decisionHash ?? current.decisionHash)\n : current.decisionHash,\n },\n 'context'\n )\n }\n if (incoming.decisionHash !== current.decisionHash) {\n return terminalBarrierFrom(\n {\n configIssuedAt: current.configIssuedAt,\n configSemanticHash: current.configSemanticHash,\n policySourceHash: current.policySourceHash,\n contextRevision: current.contextRevision,\n decisionHash: current.decisionHash,\n },\n 'context'\n )\n }\n return incoming\n}\n\nconst barrierForEvaluation = (evaluation: V1CaptureCandidateEvaluation): V1PersistedAuthorityBarrier => {\n if (evaluation.status === 'authorized') {\n return freezeV1Value({\n kind: 'authorized',\n terminalScope: null,\n configIssuedAt: evaluation.configIssuedAt,\n configSemanticHash: evaluation.configSemanticHash,\n policySourceHash: evaluation.policySourceHash,\n contextRevision: evaluation.contextRevision,\n decisionHash: evaluation.decisionHash,\n })\n }\n if (evaluation.status === 'stale') return failInput('stale evaluation has no authority barrier')\n return freezeV1Value({\n kind: 'terminal',\n terminalScope: evaluation.terminalScope,\n configIssuedAt: evaluation.trustedBoundary?.configIssuedAt ?? null,\n configSemanticHash: evaluation.trustedBoundary?.configSemanticHash ?? null,\n policySourceHash: evaluation.trustedBoundary?.policySourceHash ?? null,\n contextRevision: evaluation.contextRevision,\n decisionHash: evaluation.decisionHash,\n })\n}\n\nconst normalizeCaptureCommand = (value: V1CaptureActivityCommand): V1CaptureActivityCommand => {\n const normalized = normalizeJsonObject(value, 'capture command', failInput)\n const command = assertExactRecord(\n normalized,\n ['kind', 'name', 'occurredAt', 'properties', 'versions'],\n ['kind', 'name', 'occurredAt', 'properties', 'versions'],\n 'capture command',\n failInput\n )\n const kind =\n command.kind === 'capture'\n ? 'capture'\n : command.kind === 'screen'\n ? 'screen'\n : command.kind === 'exception'\n ? 'exception'\n : failInput('capture command.kind must be capture, screen, or exception')\n const properties = normalizeJsonObjectField(command.properties, \"properties\", \"capture command\", failInput)\n if (Object.keys(properties).length > V1_MAX_CAPTURE_PROPERTY_COUNT) {\n return failInput(`capture command.properties must contain at most ${V1_MAX_CAPTURE_PROPERTY_COUNT} properties`)\n }\n if (utf8ByteLength(JSON.stringify(properties)) > V1_MAX_CAPTURE_PROPERTIES_BYTES) {\n return failInput(\n `capture command.properties must serialize to at most ${V1_MAX_CAPTURE_PROPERTIES_BYTES} UTF-8 bytes`\n )\n }\n return freezeV1Value({\n kind,\n name: assertIdentifierField(command.name, \"name\", \"capture command\", 512, failInput),\n occurredAt: assertDateTimeField(command.occurredAt, \"occurredAt\", \"capture command\", failInput),\n properties,\n versions: validateVersionsField(command.versions, \"versions\", \"capture command\", failInput),\n })\n}\n\nconst captureWallNow = (clock: V1CaptureRuntimeClock): { readonly text: string; readonly milliseconds: number } => {\n let raw: unknown\n try {\n raw = clock.wallNow()\n } catch {\n return failInput('capture wall clock threw while being sampled')\n }\n const text = assertDateTime(raw, 'capture wall clock', failInput)\n const milliseconds = Date.parse(text)\n if (!Number.isSafeInteger(milliseconds)) failInput('capture wall clock cannot be represented safely')\n return freezeV1Value({ text, milliseconds })\n}\n\nconst captureMonotonicUs = (clock: V1CaptureRuntimeClock, rounding: 'floor' | 'ceil'): number => {\n let milliseconds: unknown\n try {\n milliseconds = clock.monotonicNowMs()\n } catch {\n return failInput('capture monotonic clock threw while being sampled')\n }\n if (!isNumberValue(milliseconds) || milliseconds < 0) {\n failInput('capture monotonic clock is unavailable')\n }\n const sampledMilliseconds = milliseconds as number\n const microseconds =\n rounding === 'floor' ? Math.floor(sampledMilliseconds * 1000) : Math.ceil(sampledMilliseconds * 1000)\n if (!Number.isSafeInteger(microseconds)) failInput('capture monotonic clock exceeds the exact range')\n return microseconds\n}\n\nconst durableWallFloor = (identity: V1PersistedIdentityState): string => {\n let floor = identity.updatedAt\n if (identity.session && compareV1DateTimes(identity.session.lastActivityAt, floor) > 0) {\n floor = identity.session.lastActivityAt\n }\n if (identity.session?.backgroundedAt && compareV1DateTimes(identity.session.backgroundedAt, floor) > 0) {\n floor = identity.session.backgroundedAt\n }\n return floor\n}\n\nconst planCaptureSession = (\n identity: V1PersistedIdentityState,\n occurredAt: string,\n authority: V1ExecutableCaptureAuthority,\n createSessionId: () => string\n): V1PersistedSessionState => {\n requireTimestampNotBefore(occurredAt, identity.updatedAt, 'capture occurredAt')\n const current = identity.session\n const mustRotate =\n !current ||\n hasV1ElapsedAtLeastSeconds(\n occurredAt,\n current.lastActivityAt,\n Math.min(current.timeoutSeconds, authority.idleTimeoutSeconds)\n ) ||\n hasV1ElapsedAtLeastSeconds(occurredAt, current.startedAt, authority.maximumDurationSeconds)\n if (mustRotate) {\n return freezeV1Value({\n id: assertIdentifier(createSessionId(), 'generated capture session ID', 256, failInput),\n startedAt: occurredAt,\n lastActivityAt: occurredAt,\n timeoutSeconds: authority.idleTimeoutSeconds,\n maximumDurationSeconds: 86_400,\n lifecycle: 'active',\n backgroundedAt: null,\n })\n }\n return freezeV1Value({\n ...current,\n lastActivityAt: occurredAt,\n timeoutSeconds: Math.min(current.timeoutSeconds, authority.idleTimeoutSeconds),\n lifecycle: 'active',\n backgroundedAt: null,\n })\n}\n\n/** Durable queue implementation used by the browser SDK. */\nexport class V1DurableState {\n private readonly _siteKey: string\n private readonly _limits: V1DurableRuntimeLimits\n private readonly _storageFactory: V1DurableStorageFactory\n private readonly _configManager: V1ConfigManager\n private readonly _captureClock: V1CaptureRuntimeClock\n private readonly _createSessionId: () => string\n private _storage: V1DurableStorage | null = null\n private _snapshot: V1DurableStateSnapshot | null = null\n private _poisoned: Error | null = null\n private _tail: Promise<void> = Promise.resolve()\n private _captureAuthority: V1ExecutableCaptureAuthority | null = null\n private _captureAdmissionDecision: object = {}\n private _sealedReplaySourceDecision: object = {}\n private _captureSourceIssuance: V1CaptureSourceIssuance | null = null\n private _captureTerminalIntent: V1PersistedAuthorityBarrier | null = null\n private _captureOwnerEpoch = 0\n private _captureLifecycleEpoch = 0\n private _captureLifecycleProtected = false\n private _captureLifecycleCleanup: (() => void) | null = null\n private _captureConfigSiteId: string | null = null\n // A lost/expired source never restores the larger constructor default. This restriction is\n // not capture authority; every append also compares its exact source with the current row.\n private _queuePolicy: Readonly<{ configIssuedAt: string; configSemanticHash: string; maxBytes: number }> | null = null\n\n constructor(options: V1DurableRuntimeOptions) {\n const normalized = validateOptions(options)\n this._siteKey = normalized.siteKey\n this._limits = normalized.limits\n this._storageFactory = normalized.storageFactory\n this._configManager = new V1ConfigManager(\n normalized.captureRuntime.sha256 ? { sha256: normalized.captureRuntime.sha256 } : {}\n )\n this._captureClock = normalized.captureRuntime.clock\n this._createSessionId = normalized.captureRuntime.createSessionId\n }\n\n initialize(createInitialState: () => V1DurableInitialState): Promise<V1DurableStateSnapshot> {\n return this._serialize(async () => {\n if (this._storage && this._snapshot) {\n return this._snapshot\n }\n // Validate the initializer without invoking it before durable storage is known to be available.\n if (!isFunctionValue(createInitialState)) {\n return failInput('initial state factory must be a function')\n }\n\n this._poisoned = null\n const storage = await this._storageFactory()\n\n try {\n const snapshot = await storage.runTransaction('readwrite', async (transaction) => {\n const existing = await readAuditedState(transaction, this._siteKey)\n if (existing) {\n if (existing.requiresMigrationWrite) await transaction.putInstallation(existing.row)\n return snapshotFromRow(existing.row)\n }\n\n const initial = assertExactRecord(\n createInitialState(),\n ['identity', 'streamId'],\n ['identity', 'streamId'],\n 'initial state',\n failInput\n )\n const row: V1InstallationRow = freezeV1Value({\n storageSchemaVersion: 1,\n siteKey: this._siteKey,\n identity: validateIdentity(initial.identity, 'initial state identity', failInput),\n streamId: assertIdentifier(initial.streamId, 'initial state streamId', 256, failInput),\n nextSequence: 0,\n headSequence: 0,\n queuedCount: 0,\n queuedBytes: 0,\n authorityBarrier: null,\n })\n await transaction.addInstallation(row)\n return snapshotFromRow(row)\n })\n this._storage = storage\n this._snapshot = snapshot\n return snapshot\n } catch (error) {\n storage.close()\n this._rememberFatalFailure(error)\n throw error\n }\n })\n }\n\n installCaptureLifecycleProtection(\n windowTarget: V1CaptureLifecycleWindowTarget,\n documentTarget: V1CaptureLifecycleDocumentTarget\n ): Promise<boolean> {\n return this._serialize(async () => {\n const previousCleanup = this._captureLifecycleCleanup\n this._captureLifecycleCleanup = null\n this._captureLifecycleProtected = false\n this._captureAuthority = null\n try {\n previousCleanup?.()\n } catch {\n // Authority is already cleared; stale observer cleanup is best effort.\n }\n\n if (documentTarget.hidden) return false\n\n const lifecycleEpochBeforeInstall = this._captureLifecycleEpoch\n const invalidate = (): void => {\n this._invalidateCaptureAuthorityForLifecycleNow()\n }\n const invalidateWhenHidden = (): void => {\n if (documentTarget.hidden) invalidate()\n }\n const windowEvents = ['freeze', 'pagehide', 'pageshow', 'resume'] as const\n try {\n for (const event of windowEvents) {\n // Internal security-only target abstraction; the public browser helper is deliberately not wired.\n // eslint-disable-next-line elu-client/no-add-event-listener\n windowTarget.addEventListener(event, invalidate)\n }\n // eslint-disable-next-line elu-client/no-add-event-listener\n documentTarget.addEventListener('visibilitychange', invalidateWhenHidden)\n } catch {\n for (const event of windowEvents) {\n try {\n windowTarget.removeEventListener(event, invalidate)\n } catch {\n // Best-effort cleanup after a partially installed security observer.\n }\n }\n try {\n documentTarget.removeEventListener('visibilitychange', invalidateWhenHidden)\n } catch {\n // Best-effort cleanup after a partially installed security observer.\n }\n return false\n }\n const cleanup = (): void => {\n for (const event of windowEvents) {\n try {\n windowTarget.removeEventListener(event, invalidate)\n } catch {\n // Best-effort cleanup; authority is invalidated before this callback runs.\n }\n }\n try {\n documentTarget.removeEventListener('visibilitychange', invalidateWhenHidden)\n } catch {\n // Best-effort cleanup; authority is invalidated before this callback runs.\n }\n }\n if (documentTarget.hidden || this._captureLifecycleEpoch !== lifecycleEpochBeforeInstall) {\n cleanup()\n return false\n }\n this._captureLifecycleCleanup = cleanup\n this._captureLifecycleProtected = true\n return true\n })\n }\n\n invalidateCaptureAuthorityForLifecycle(): Promise<void> {\n try {\n this._invalidateCaptureAuthorityForLifecycleNow()\n return Promise.resolve()\n } catch (error) {\n return Promise.reject(error)\n }\n }\n\n submitCaptureAuthority(\n configInput: string,\n privacyInput: string | undefined\n ): Promise<V1CaptureAuthorityUpdateResult> {\n this._captureAdmissionDecision = {}\n this._sealedReplaySourceDecision = {}\n return this._serialize(async () => {\n const storage = this._requireStorage()\n if (this._captureTerminalIntent) await this._readCaptureState(storage)\n const witness = await storage.runTransaction('readonly', async (transaction) =>\n readInstallation(transaction, this._siteKey)\n )\n let initialWall: ReturnType<typeof captureWallNow>\n let initialMonotonicUs: number\n try {\n initialWall = captureWallNow(this._captureClock)\n initialMonotonicUs = captureMonotonicUs(this._captureClock, 'floor')\n } catch {\n return this._persistConfigTerminal(storage, 'capture clock discontinuity during authority submission')\n }\n const evaluation = await this._configManager.evaluateCaptureCandidate(\n configInput,\n privacyInput,\n freezeV1Value({\n contextRevision: witness.identity.contextRevision,\n optedOut: witness.identity.optedOut,\n }),\n initialWall.milliseconds\n )\n if (evaluation.status === 'stale') return evaluation\n this._rememberCaptureSourceIssuance(\n evaluation,\n initialWall.text,\n initialMonotonicUs,\n durableWallFloor(witness.identity)\n )\n\n const candidateSiteId = evaluation.configSiteId\n if (candidateSiteId) {\n if (this._captureConfigSiteId && this._captureConfigSiteId !== candidateSiteId) {\n this._captureAuthority = null\n return freezeV1Value({\n status: 'terminal',\n reason: 'config site identity changed during the owner lifetime',\n })\n } else if (!this._captureConfigSiteId) {\n this._captureConfigSiteId = candidateSiteId\n }\n }\n const queueBytes = this._configManager.activeQueueByteLimit(initialWall.milliseconds)\n const source = evaluation.status === 'authorized' ? evaluation : evaluation.trustedBoundary\n if (queueBytes !== null && source) {\n this._queuePolicy = freezeV1Value({\n configIssuedAt: source.configIssuedAt,\n configSemanticHash: source.configSemanticHash,\n maxBytes: Math.min(this._limits.maxBytes, queueBytes),\n })\n }\n if (evaluation.status === 'authorized' && !this._captureLifecycleProtected) {\n this._captureAuthority = null\n return freezeV1Value({\n status: 'terminal',\n reason: 'browser lifecycle protection is not installed',\n })\n }\n return this._installCaptureEvaluation(storage, witness, evaluation)\n })\n }\n\n captureActivity(command: V1CaptureActivityCommand): Promise<V1DurableAppendResult> {\n return this._snapshotAndSerialize(\n () => normalizeCaptureCommand(command),\n async (normalized) => {\n const storage = this._requireStorage()\n type V1CaptureTransactionResult =\n | Readonly<{ status: 'captured'; value: V1DurableAppendResult }>\n | Readonly<{ status: 'terminal'; row: V1InstallationRow; reason: string }>\n const attempt = (): Promise<V1DurableCommitOutcome<V1CaptureTransactionResult>> =>\n storage.runTransactionWithCommitOutcome<V1CaptureTransactionResult>(async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n let authority: V1ExecutableCaptureAuthority\n try {\n authority = this._requireCaptureAuthority(current)\n } catch (error) {\n if (!(error instanceof V1CaptureConfigTerminalError)) throw error\n const merged = mergeAuthorityBarrier(\n current.authorityBarrier,\n terminalBarrierAtCurrentConfig(current.authorityBarrier, 'config')\n )\n const row: V1InstallationRow = freezeV1Value({ ...current, authorityBarrier: merged })\n if (!sameAuthorityBarrier(current.authorityBarrier, merged)) {\n await transaction.putInstallation(row)\n }\n return freezeV1Value({ status: 'terminal', row, reason: error.message })\n }\n const session = planCaptureSession(\n current.identity,\n normalized.occurredAt,\n authority,\n this._createSessionId\n )\n const identity = validateSessionTransition(current.identity, session, true)\n const eventRow: V1InstallationRow = freezeV1Value({ ...current, identity })\n const draft: V1NormalizedEventDraft = freezeV1Value({\n ...normalized,\n expectedSessionId: session.id,\n })\n const encoded = [encodeEventDraft(draft, eventRow, current.nextSequence)]\n return freezeV1Value({\n status: 'captured',\n value: await this._commitAppend(transaction, current, identity, encoded),\n })\n })\n\n let outcome = await attempt()\n if (outcome.status === 'proven-not-committed' && !(outcome.error instanceof V1DurableStateError)) {\n outcome = await attempt()\n }\n if (outcome.status === 'unknown') {\n this._captureAuthority = null\n throw new V1DurableStateError('commit-unknown', 'capture commit outcome is unknown')\n }\n if (outcome.status === 'proven-not-committed') throw outcome.error\n if (outcome.value.status === 'terminal') {\n this._captureAuthority = null\n this._snapshot = snapshotFromRow(outcome.value.row)\n return fail('capture-not-authorized', outcome.value.reason)\n }\n this._snapshot = outcome.value.value.state\n return outcome.value.value\n }\n )\n }\n\n registerCaptureSuperProperties(properties: V1JsonObject, occurredAt: string): Promise<V1DurableAppendResult> {\n return this.applyLocal({\n type: 'setSuperProperties',\n set: properties,\n setOnce: freezeV1Value({}),\n unset: freezeV1Value([]),\n occurredAt,\n })\n }\n\n unregisterCaptureSuperProperties(\n properties: readonly string[],\n occurredAt: string\n ): Promise<V1DurableAppendResult> {\n return this.applyLocal({\n type: 'setSuperProperties',\n set: freezeV1Value({}),\n setOnce: freezeV1Value({}),\n unset: properties,\n occurredAt,\n })\n }\n\n setCaptureOptedOut(optedOut: boolean, occurredAt: string): Promise<V1DurableAppendResult> {\n return this.applyLocal({ type: 'setOptedOut', optedOut, occurredAt })\n }\n\n appendEvents(\n sessionUpdate: V1DurableEventSessionUpdate,\n eventDrafts: readonly V1DurableEventDraft[]\n ): Promise<V1DurableAppendResult> {\n return this._snapshotAndSerialize(\n () =>\n freezeV1Value({\n sessionUpdate: normalizeSessionUpdate(sessionUpdate),\n drafts: normalizeEventDrafts(eventDrafts),\n }),\n this._appendTransactionOperation<Readonly<{ sessionUpdate: ReturnType<typeof normalizeSessionUpdate>; drafts: readonly V1NormalizedEventDraft[] }>>(\n (transaction, current, { sessionUpdate: normalizedUpdate, drafts }) => {\nif (current.nextSequence > Number.MAX_SAFE_INTEGER - drafts.length) {\n return fail('sequence-exhausted', 'queue sequence space is exhausted')\n }\nlet identity = current.identity\nif (normalizedUpdate.type === 'replace') {\n requireCurrentSessionId(\n current.identity,\n normalizedUpdate.expectedCurrentSessionId,\n 'event session replacement'\n )\n identity = validateSessionTransition(current.identity, normalizedUpdate.session, true)\n }\nvalidateEventCausality(current.identity.updatedAt, identity.session, drafts)\nconst eventRow: V1InstallationRow = freezeV1Value({\n ...current,\n identity,\n })\nconst encoded = drafts.map((draft, index) =>\n encodeEventDraft(draft, eventRow, current.nextSequence + index)\n )\nreturn this._commitAppend(transaction, current, identity, encoded)\n },\n (result) => {\nthis._snapshot = result.state\nthis._captureAuthority = null\nreturn result\n }\n )\n )\n }\n\n appendMutations(mutationDrafts: readonly V1DurableMutationDraft[]): Promise<V1DurableAppendResult> {\n return this._snapshotAndSerialize(\n () => {\n const drafts = normalizeMutationDrafts(mutationDrafts)\n this._captureAdmissionDecision = {}\n this._captureAuthority = null\n return drafts\n },\n this._appendTransactionOperation<readonly V1NormalizedMutationDraft[]>(\n (transaction, current, drafts) => {\nif (current.nextSequence > Number.MAX_SAFE_INTEGER - drafts.length) {\n return fail('sequence-exhausted', 'queue sequence space is exhausted')\n }\nlet identity = current.identity\nconst encoded: V1EncodedRecord[] = []\nfor (let index = 0; index < drafts.length; index += 1) {\n identity = applyMutation(identity, drafts[index])\n const mutationRow: V1InstallationRow = freezeV1Value({ ...current, identity })\n encoded.push(encodeMutationDraft(drafts[index], mutationRow, current.nextSequence + index))\n }\nreturn this._commitAppend(transaction, current, identity, encoded)\n },\n (result) => {\nthis._snapshot = result.state\nthis._captureAuthority = null\nreturn result\n }\n )\n )\n }\n\n applyLocal(change: V1DurableLocalChange): Promise<V1DurableAppendResult> {\n return this._snapshotAndSerialize(\n () => {\n const normalized = normalizeLocalChange(change)\n this._captureAdmissionDecision = {}\n if (\n normalized.type === 'setOptedOut' ||\n normalized.type === 'resetGroups' ||\n normalized.type === 'resetIdentity' ||\n normalized.type === 'setSuperProperties' ||\n normalized.type === 'setFlagContext'\n )\n this._captureAuthority = null\n return normalized\n },\n this._appendTransactionOperation<ReturnType<typeof normalizeLocalChange>>(\n (transaction, current, normalizedChange) => {\nconst identity = applyLocalChange(current.identity, normalizedChange)\nreturn this._commitAppend(transaction, current, identity, [])\n },\n (result, normalizedChange) => {\nthis._snapshot = result.state\nif (\n normalizedChange.type === 'setOptedOut' ||\n normalizedChange.type === 'resetGroups' ||\n normalizedChange.type === 'resetIdentity' ||\n normalizedChange.type === 'setSuperProperties' ||\n normalizedChange.type === 'setFlagContext'\n ) {\n this._captureAuthority = null\n }\nreturn result\n }\n )\n )\n }\n\n peek(maxCount: number, maxRecordBytes: number): Promise<readonly V1DurableQueueRecord[]> {\n return this._serialize(async () => {\n const count = assertInteger(maxCount, 'peek maxCount', 0, 1000, failInput)\n const recordBytes = assertInteger(\n maxRecordBytes,\n 'peek maxRecordBytes',\n 0,\n V1_MAX_DURABLE_QUEUE_BYTES,\n failInput\n )\n const storage = this._requireStorage()\n try {\n const result = await storage.runTransaction('readonly', async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n const prefix: V1DurableQueueRecord[] = []\n let prefixBytes = 0\n let visited = 0\n let stoppedForBytes = false\n const requestedCount = Math.min(count, current.queuedCount)\n await transaction.scanRecordPrefix(\n this._siteKey,\n current.headSequence,\n requestedCount,\n (rawRecord) => {\n const record = validateStoredRecord(rawRecord, current, current.headSequence + visited)\n visited += 1\n if (prefixBytes + record.payloadBytes > recordBytes) {\n if (prefix.length === 0) {\n return fail(\n 'head-record-too-large',\n 'live queue head exceeds the requested record-byte budget'\n )\n }\n stoppedForBytes = true\n return false\n }\n prefix.push(record)\n prefixBytes += record.payloadBytes\n return true\n }\n )\n if (!stoppedForBytes && visited !== requestedCount) {\n return failCorrupt('stored queue ended before its bounded live prefix')\n }\n return freezeV1Value({ state: snapshotFromRow(current), prefix: freezeV1Value(prefix) })\n })\n this._snapshot = result.state\n return result.prefix\n } catch (error) {\n this._rememberFatalFailure(error)\n throw error\n }\n })\n }\n\n acknowledge(value: V1DurableAcknowledgement): Promise<V1DurableStateSnapshot> {\n return this._snapshotAndSerialize(\n () => normalizeAcknowledgement(value),\n async (acknowledgement) => {\n if (acknowledgement.siteKey !== this._siteKey) {\n return failInput('acknowledgement site key does not match this durable runtime')\n }\n const storage = this._requireStorage()\n try {\n const snapshot = await storage.runTransaction('readwrite', async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n if (acknowledgement.streamId !== current.streamId) {\n return failInput('acknowledgement stream does not match the live queue')\n }\n if (acknowledgement.records.length === 0) {\n return snapshotFromRow(current)\n }\n\n for (const reference of acknowledgement.records) {\n if (\n reference.recordId !== recordIdFor(reference.kind, current.streamId, reference.sequence)\n ) {\n return failInput(\n 'acknowledgement contains a record ID not derived from its stream prefix'\n )\n }\n }\n\n const firstSequence = acknowledgement.records[0].sequence\n const lastSequence = acknowledgement.records[acknowledgement.records.length - 1].sequence\n if (lastSequence < current.headSequence) {\n return snapshotFromRow(current)\n }\n if (firstSequence !== current.headSequence) {\n return failInput('acknowledgement is not the exact current queue prefix')\n }\n if (acknowledgement.records.length > current.queuedCount) {\n return failInput('acknowledgement extends beyond the live queue')\n }\n\n let visited = 0\n let deletedBytes = 0\n await transaction.scanRecordPrefix(\n this._siteKey,\n current.headSequence,\n acknowledgement.records.length,\n (rawRecord) => {\n const record = validateStoredRecord(rawRecord, current, current.headSequence + visited)\n if (!sameReference(acknowledgement.records[visited], record)) {\n return failInput('acknowledgement does not exactly match the live queue prefix')\n }\n deletedBytes += record.payloadBytes\n visited += 1\n return true\n }\n )\n if (visited !== acknowledgement.records.length) {\n return failCorrupt('stored queue ended before the acknowledged live prefix')\n }\n if (\n deletedBytes > current.queuedBytes ||\n (acknowledgement.records.length === current.queuedCount &&\n deletedBytes !== current.queuedBytes)\n ) {\n return failCorrupt('stored queue byte count does not match the acknowledged prefix')\n }\n\n for (const reference of acknowledgement.records) {\n await transaction.deleteRecord(this._siteKey, reference.sequence)\n }\n const nextRow: V1InstallationRow = freezeV1Value({\n ...current,\n headSequence: current.headSequence + acknowledgement.records.length,\n queuedCount: current.queuedCount - acknowledgement.records.length,\n queuedBytes: current.queuedBytes - deletedBytes,\n })\n await transaction.putInstallation(nextRow)\n return snapshotFromRow(nextRow)\n })\n this._snapshot = snapshot\n return snapshot\n } catch (error) {\n this._rememberFatalFailure(error)\n throw error\n }\n }\n )\n }\n\n /**\n * Captures this exact local authority decision. The caller supplies the transaction-current\n * installation, never snapshot(). A renewal, lifecycle boundary, close, changed identity or\n * cross-realm barrier invalidates the guard; it does not authorize a chunk's own identity.\n */\n captureAdmissionGuard(): V1CaptureAdmissionGuard {\n const authority = this._captureAuthority\n const decision = this._captureAdmissionDecision\n return (installation) => {\n const denied = freezeV1Value({ authorized: false, terminalInstallation: null })\n if (this._captureTerminalIntent && this._storage && !this._poisoned) {\n const current = validateInstallation(installation, this._siteKey)\n return freezeV1Value({\n authorized: false,\n terminalInstallation: freezeV1Value({\n ...current,\n authorityBarrier: mergeAuthorityBarrier(current.authorityBarrier, this._captureTerminalIntent),\n }),\n })\n }\n if (\n !authority ||\n decision !== this._captureAdmissionDecision ||\n this._poisoned ||\n !this._storage ||\n this._captureAuthority !== authority\n )\n return denied\n const current = validateInstallation(installation, this._siteKey)\n try {\n const authorized = this._requireCaptureAuthority(current) === authority\n const session = current.identity.session\n const now = captureWallNow(this._captureClock).text\n if (\n !session ||\n session.lifecycle !== 'active' ||\n compareV1DateTimes(now, session.lastActivityAt) < 0 ||\n hasV1ElapsedAtLeastSeconds(\n now,\n session.lastActivityAt,\n Math.min(session.timeoutSeconds, authority.idleTimeoutSeconds)\n ) ||\n hasV1ElapsedAtLeastSeconds(now, session.startedAt, authority.maximumDurationSeconds)\n )\n return denied\n return freezeV1Value({\n authorized,\n terminalInstallation: null,\n })\n } catch (error) {\n this._captureAuthority = null\n if (error instanceof V1CaptureConfigTerminalError) {\n this._captureTerminalIntent = terminalBarrierAtCurrentConfig(current.authorityBarrier, 'config')\n return freezeV1Value({\n authorized: false,\n terminalInstallation: freezeV1Value({\n ...current,\n authorityBarrier: mergeAuthorityBarrier(\n current.authorityBarrier,\n this._captureTerminalIntent\n ),\n }),\n })\n }\n if (error instanceof V1DurableStateError && error.code === 'capture-not-authorized') return denied\n throw error\n }\n }\n }\n\n /**\n * Original local source lease only. This consumes the same clock/lifecycle validator as capture\n * admission; it grants no transaction-current identity, session or cross-tab storage authority.\n */\n captureSourceGuard(): () => boolean {\n return this.captureSourceLease().isCurrent\n }\n\n /** Scheduling projection of this exact original lease; it never installs or renews authority. */\n captureSourceLease(): Readonly<{ isCurrent(): boolean; remainingMicroseconds(): number }> {\n const authority = this._captureAuthority\n const decision = this._captureAdmissionDecision\n const remainingMicroseconds = (): number => {\n if (\n !authority ||\n this._captureAuthority !== authority ||\n this._captureAdmissionDecision !== decision ||\n this._captureTerminalIntent ||\n this._poisoned ||\n !this._storage\n )\n return 0\n try {\n this._requireCaptureLifecycle(authority)\n return this._requireCaptureSourceClock(authority)\n } catch (error) {\n this._captureAuthority = null\n if (error instanceof V1CaptureConfigTerminalError) {\n this._captureTerminalIntent = terminalBarrierAtCurrentConfig(authority.barrier, 'config')\n }\n return 0\n }\n }\n return freezeV1Value({ isCurrent: () => remainingMicroseconds() > 0, remainingMicroseconds })\n }\n\n /**\n * Restriction for already-sealed replay, bound to the exact installed source. Local identity or\n * session changes do not extend or revoke that source's clock. This grants no fresh capture or\n * storage authority; the caller must separately check the transaction-current config witness.\n */\n sealedReplaySourceGuard(): () => boolean {\n const authority = this._captureAuthority\n const sourceDecision = this._sealedReplaySourceDecision\n let denied = !authority\n return () => {\n if (\n denied ||\n !authority ||\n this._sealedReplaySourceDecision !== sourceDecision ||\n this._captureTerminalIntent?.terminalScope === 'config' ||\n this._poisoned ||\n !this._storage ||\n this._snapshot?.identity.optedOut ||\n this._snapshot?.streamId !== authority.streamId\n )\n return false\n try {\n this._requireCaptureLifecycle(authority)\n this._requireCaptureSourceClock(authority)\n return true\n } catch (error) {\n denied = true\n if (error instanceof V1CaptureConfigTerminalError) {\n this._captureAuthority = null\n this._captureTerminalIntent = terminalBarrierAtCurrentConfig(authority.barrier, 'config')\n }\n return false\n }\n }\n }\n\n /** Reads the owner inside a transaction; a cached snapshot is never authority for replay. */\n readCaptureState(): Promise<V1DurableCaptureState> {\n return this._serialize(() => this._readCaptureState(this._requireStorage()))\n }\n\n private async _readCaptureState(storage: V1DurableStorage): Promise<V1DurableCaptureState> {\n const authority = this._captureAuthority\n const decision = this._captureAdmissionDecision\n type CaptureRead = V1DurableCaptureState &\n Readonly<{ installation: V1InstallationRow; terminalIntent: V1PersistedAuthorityBarrier | null }>\n const attempt = (): Promise<V1DurableCommitOutcome<CaptureRead>> =>\n storage.runTransactionWithCommitOutcome(async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n if (!this._captureTerminalIntent) {\n try {\n this._requireCaptureAuthority(current)\n return freezeV1Value({\n state: snapshotFromRow(current),\n authorityBarrier: current.authorityBarrier,\n installation: current,\n authorized: true,\n terminalIntent: null,\n })\n } catch (error) {\n if (error instanceof V1CaptureConfigTerminalError) {\n // Close local admission immediately, retaining the exact barrier until commit.\n this._captureAuthority = null\n this._captureTerminalIntent = terminalBarrierAtCurrentConfig(\n current.authorityBarrier,\n 'config'\n )\n } else if (error instanceof V1DurableStateError && error.code === 'capture-not-authorized') {\n return freezeV1Value({\n state: snapshotFromRow(current),\n authorityBarrier: current.authorityBarrier,\n installation: current,\n authorized: false,\n terminalIntent: null,\n })\n } else {\n throw error\n }\n }\n }\n const terminalIntent = this._captureTerminalIntent!\n const barrier = mergeAuthorityBarrier(current.authorityBarrier, terminalIntent)\n const row: V1InstallationRow = freezeV1Value({ ...current, authorityBarrier: barrier })\n if (!sameAuthorityBarrier(current.authorityBarrier, barrier)) await transaction.putInstallation(row)\n return freezeV1Value({\n state: snapshotFromRow(row),\n authorityBarrier: row.authorityBarrier,\n installation: row,\n authorized: false,\n terminalIntent,\n })\n })\n let outcome = await attempt()\n if (outcome.status === 'proven-not-committed' && !(outcome.error instanceof V1DurableStateError)) {\n outcome = await attempt()\n }\n if (outcome.status !== 'committed') {\n this._captureAuthority = null\n if (outcome.status === 'unknown') {\n throw new V1DurableStateError('commit-unknown', 'capture state read commit outcome is unknown')\n }\n this._rememberFatalFailure(outcome.error)\n throw outcome.error\n }\n if (this._captureTerminalIntent === outcome.value.terminalIntent) this._captureTerminalIntent = null\n this._snapshot = outcome.value.state\n let authorized = Boolean(\n outcome.value.authorized &&\n authority &&\n this._captureAuthority === authority &&\n this._captureAdmissionDecision === decision &&\n !this._captureTerminalIntent\n )\n if (authorized) {\n // A commit may wait while consent changes, close is queued, or either clock expires.\n try {\n authorized = this._requireCaptureAuthority(outcome.value.installation) === authority\n } catch (error) {\n this._captureAuthority = null\n if (error instanceof V1CaptureConfigTerminalError) {\n this._captureTerminalIntent = terminalBarrierAtCurrentConfig(\n outcome.value.installation.authorityBarrier,\n 'config'\n )\n // The retained intent makes this a bounded second read which only persists denial.\n return this._readCaptureState(storage)\n }\n if (!(error instanceof V1DurableStateError && error.code === 'capture-not-authorized')) throw error\n authorized = false\n }\n }\n if (!authorized) this._captureAuthority = null\n return freezeV1Value({\n state: outcome.value.state,\n authorityBarrier: outcome.value.authorityBarrier,\n authorized,\n })\n }\n\n snapshot(): V1DurableStateSnapshot {\n if (this._poisoned) {\n throw this._poisoned\n }\n if (!this._snapshot) {\n return fail('not-initialized', 'durable state is not initialized')\n }\n return this._snapshot\n }\n\n close(): Promise<void> {\n this._sealedReplaySourceDecision = {}\n this._captureAdmissionDecision = {}\n this._captureAuthority = null\n return this._serialize(async () => {\n const cleanup = this._captureLifecycleCleanup\n this._captureLifecycleCleanup = null\n this._captureLifecycleProtected = false\n this._captureAuthority = null\n this._captureConfigSiteId = null\n this._configManager.clear()\n try {\n cleanup?.()\n } catch {\n // Best-effort listener cleanup must not prevent storage shutdown.\n }\n this._storage?.close()\n this._storage = null\n this._snapshot = null\n this._poisoned = null\n })\n }\n\n private async _persistConfigTerminal(\n storage: V1DurableStorage,\n reason: string\n ): Promise<V1CaptureAuthorityUpdateResult> {\n const attempt = (): Promise<V1DurableCommitOutcome<V1InstallationRow>> =>\n storage.runTransactionWithCommitOutcome(async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n const merged = mergeAuthorityBarrier(\n current.authorityBarrier,\n terminalBarrierAtCurrentConfig(current.authorityBarrier, 'config')\n )\n const row: V1InstallationRow = freezeV1Value({ ...current, authorityBarrier: merged })\n if (!sameAuthorityBarrier(current.authorityBarrier, merged)) await transaction.putInstallation(row)\n return row\n })\n\n let outcome = await attempt()\n if (outcome.status === 'proven-not-committed' && !(outcome.error instanceof V1DurableStateError)) {\n outcome = await attempt()\n }\n this._captureAuthority = null\n if (outcome.status === 'unknown') {\n throw new V1DurableStateError('commit-unknown', 'config terminal barrier commit outcome is unknown')\n }\n if (outcome.status === 'proven-not-committed') throw outcome.error\n this._snapshot = snapshotFromRow(outcome.value)\n return freezeV1Value({ status: 'terminal', reason })\n }\n\n private _rememberCaptureSourceIssuance(\n evaluation: Exclude<V1CaptureCandidateEvaluation, Readonly<{ status: 'stale'; reason: string }>>,\n installedWallAt: string,\n monotonicStartedAtUs: number,\n observedWallFloor: string\n ): void {\n const boundary = evaluation.status === 'authorized' ? evaluation : evaluation.trustedBoundary\n if (!boundary) return\n const previous = this._captureSourceIssuance\n // Only the config manager's validated newer issuance can replace this clock. Equal\n // timestamps with conflicting content are terminal at that manager, never a new lease.\n if (previous && compareV1DateTimes(boundary.configIssuedAt, previous.configIssuedAt) <= 0) return\n this._captureSourceIssuance = freezeV1Value({\n token: {},\n configIssuedAt: boundary.configIssuedAt,\n configSemanticHash: boundary.configSemanticHash,\n installedWallAt,\n observedWallFloor,\n monotonicStartedAtUs,\n monotonicBudgetUs: null,\n denied: false,\n })\n }\n\n private _sourceClockForCapture(\n evaluation: Extract<V1CaptureCandidateEvaluation, { status: 'authorized' }>,\n identity: V1PersistedIdentityState\n ): V1CaptureSourceClock {\n const source = this._captureSourceIssuance\n if (\n !source ||\n source.denied ||\n source.configIssuedAt !== evaluation.configIssuedAt ||\n source.configSemanticHash !== evaluation.configSemanticHash\n ) {\n throw new V1CaptureConfigTerminalError('capture source issuance is unavailable')\n }\n try {\n const initialBudget = Math.max(\n 0,\n Math.min(\n v1MicrosecondsBetween(evaluation.expiresAt, source.installedWallAt),\n v1MicrosecondsBetween(evaluation.expiresAt, evaluation.configIssuedAt),\n v1MicrosecondsBetween(evaluation.expiresAt, source.observedWallFloor)\n )\n )\n const elapsed = captureMonotonicUs(this._captureClock, 'ceil') - source.monotonicStartedAtUs\n // Translate a newer durable wall floor to an absolute deadline on the ORIGINAL monotonic\n // axis. Subtracting it directly from the total budget would charge normal elapsed time twice.\n const floorDeadline =\n elapsed + Math.max(0, v1MicrosecondsBetween(evaluation.expiresAt, durableWallFloor(identity)))\n if (!Number.isSafeInteger(floorDeadline)) {\n this._captureSourceIssuance = freezeV1Value({ ...source, denied: true })\n throw new V1CaptureConfigTerminalError('capture source clock deadline is unsafe')\n }\n const monotonicBudgetUs = Math.min(source.monotonicBudgetUs ?? initialBudget, initialBudget, floorDeadline)\n this._captureSourceIssuance = freezeV1Value({ ...source, monotonicBudgetUs })\n const clock = freezeV1Value({\n sourceToken: source.token,\n expiresAt: evaluation.expiresAt,\n installedWallAt: source.installedWallAt,\n monotonicStartedAtUs: source.monotonicStartedAtUs,\n monotonicBudgetUs,\n })\n this._requireCaptureSourceClock(clock)\n return clock\n } catch (error) {\n if (this._captureSourceIssuance?.token === source.token) {\n this._captureSourceIssuance = freezeV1Value({ ...this._captureSourceIssuance, denied: true })\n }\n throw error\n }\n }\n\n private async _installCaptureEvaluation(\n storage: V1DurableStorage,\n witness: V1InstallationRow,\n evaluation: Exclude<V1CaptureCandidateEvaluation, Readonly<{ status: 'stale'; reason: string }>>\n ): Promise<V1CaptureAuthorityUpdateResult> {\n interface V1InstallResult {\n readonly row: V1InstallationRow\n readonly authority: V1ExecutableCaptureAuthority | null\n readonly reason: string\n }\n const originalBarrier = barrierForEvaluation(evaluation)\n const prospectiveOwnerEpoch = incrementStateCounter(this._captureOwnerEpoch, 'capture owner epoch')\n const attempt = () =>\n storage.runTransactionWithCommitOutcome<V1InstallResult>(async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n let incoming = originalBarrier\n let executable: V1ExecutableCaptureAuthority | null = null\n let reason = evaluation.status === 'terminal' ? evaluation.reason : 'capture authority conflict'\n\n if (evaluation.status === 'authorized') {\n const witnessMatches =\n current.streamId === witness.streamId &&\n current.identity.revision === witness.identity.revision &&\n current.identity.contextRevision === witness.identity.contextRevision &&\n current.identity.optedOut === witness.identity.optedOut &&\n current.identity.contextRevision === evaluation.contextRevision &&\n !current.identity.optedOut\n if (!witnessMatches) {\n incoming = terminalBarrierFrom(\n {\n configIssuedAt: evaluation.configIssuedAt,\n configSemanticHash: evaluation.configSemanticHash,\n policySourceHash: evaluation.policySourceHash,\n contextRevision: evaluation.contextRevision,\n decisionHash: evaluation.decisionHash,\n },\n 'context'\n )\n reason = 'identity or consent witness changed before authority installation'\n } else if (this._captureConfigSiteId !== evaluation.configSiteId) {\n incoming = terminalBarrierFrom(\n {\n configIssuedAt: evaluation.configIssuedAt,\n configSemanticHash: evaluation.configSemanticHash,\n policySourceHash: evaluation.policySourceHash,\n contextRevision: evaluation.contextRevision,\n decisionHash: evaluation.decisionHash,\n },\n 'config'\n )\n reason = 'config site identity does not match the owner pin'\n } else {\n try {\n const source = this._sourceClockForCapture(evaluation, current.identity)\n executable = freezeV1Value({\n ...source,\n ownerEpoch: prospectiveOwnerEpoch,\n barrier: incoming,\n configSiteId: evaluation.configSiteId,\n streamId: current.streamId,\n identityRevision: current.identity.revision,\n contextRevision: current.identity.contextRevision,\n identityOptedOut: false,\n lifecycleEpoch: this._captureLifecycleEpoch,\n idleTimeoutSeconds: evaluation.idleTimeoutSeconds,\n maximumDurationSeconds: 86_400,\n })\n reason = 'authorized'\n } catch {\n incoming = terminalBarrierFrom(\n {\n configIssuedAt: evaluation.configIssuedAt,\n configSemanticHash: evaluation.configSemanticHash,\n policySourceHash: evaluation.policySourceHash,\n contextRevision: evaluation.contextRevision,\n decisionHash: evaluation.decisionHash,\n },\n 'config'\n )\n reason = 'capture clock discontinuity during authority installation'\n }\n }\n }\n\n const merged = mergeAuthorityBarrier(current.authorityBarrier, incoming)\n if (!sameAuthorityBarrier(merged, incoming) || merged.kind !== 'authorized') executable = null\n if (!sameAuthorityBarrier(merged, incoming) && evaluation.status === 'authorized') {\n reason = 'persisted authority boundary rejected the candidate'\n }\n const row: V1InstallationRow = freezeV1Value({ ...current, authorityBarrier: merged })\n if (!sameAuthorityBarrier(current.authorityBarrier, merged)) await transaction.putInstallation(row)\n return freezeV1Value({ row, authority: executable, reason })\n })\n\n let outcome = await attempt()\n if (outcome.status === 'proven-not-committed' && !(outcome.error instanceof V1DurableStateError)) {\n outcome = await attempt()\n }\n if (outcome.status === 'unknown') {\n this._captureAuthority = null\n throw new V1DurableStateError('commit-unknown', 'authority barrier commit outcome is unknown')\n }\n if (outcome.status === 'proven-not-committed') throw outcome.error\n this._captureOwnerEpoch = prospectiveOwnerEpoch\n this._captureAuthority = outcome.value.authority\n this._snapshot = snapshotFromRow(outcome.value.row)\n if (outcome.value.authority) {\n try {\n this._requireCaptureSourceClock(outcome.value.authority)\n } catch (error) {\n if (!(error instanceof V1CaptureConfigTerminalError)) throw error\n this._captureAuthority = null\n this._captureTerminalIntent = terminalBarrierAtCurrentConfig(\n outcome.value.row.authorityBarrier,\n 'config'\n )\n await this._readCaptureState(storage)\n return freezeV1Value({ status: 'terminal', reason: 'capture source expired before publication' })\n }\n }\n return outcome.value.authority\n ? freezeV1Value({ status: 'authorized', ownerEpoch: prospectiveOwnerEpoch })\n : freezeV1Value({ status: 'terminal', reason: outcome.value.reason })\n }\n\n private _requireCaptureAuthority(current: V1InstallationRow): V1ExecutableCaptureAuthority {\n const authority = this._captureAuthority\n const reject = (message: string): never => {\n this._captureAuthority = null\n return fail('capture-not-authorized', message)\n }\n if (!authority) return reject('capture authority is absent')\n this._requireCaptureLifecycle(authority)\n if (\n authority.configSiteId !== this._captureConfigSiteId ||\n authority.streamId !== current.streamId ||\n authority.identityRevision !== current.identity.revision ||\n authority.contextRevision !== current.identity.contextRevision ||\n authority.identityOptedOut !== current.identity.optedOut ||\n current.identity.optedOut\n ) {\n return reject('capture authority identity, consent, site, or stream witness changed')\n }\n if (!sameAuthorityBarrier(authority.barrier, current.authorityBarrier)) {\n return reject('capture authority no longer matches the persisted cross-tab barrier')\n }\n\n this._requireCaptureSourceClock(authority)\n return authority\n }\n\n private _requireCaptureLifecycle(authority: V1ExecutableCaptureAuthority): void {\n if (!this._captureLifecycleProtected || authority.lifecycleEpoch !== this._captureLifecycleEpoch) {\n this._captureAuthority = null\n return fail('capture-not-authorized', 'capture authority crossed a page lifecycle boundary')\n }\n }\n\n private _requireCaptureSourceClock(authority: V1CaptureSourceClock): number {\n const configTerminal = (message: string): never => {\n const source = this._captureSourceIssuance\n if (source?.token === authority.sourceToken) {\n this._captureSourceIssuance = freezeV1Value({ ...source, denied: true })\n }\n throw new V1CaptureConfigTerminalError(message)\n }\n if (this._captureSourceIssuance?.token === authority.sourceToken && this._captureSourceIssuance.denied) {\n return configTerminal('capture source clock was already denied')\n }\n let wall: ReturnType<typeof captureWallNow>\n let monotonicNowUs: number\n try {\n wall = captureWallNow(this._captureClock)\n monotonicNowUs = captureMonotonicUs(this._captureClock, 'ceil')\n } catch {\n return configTerminal('capture clock discontinuity while validating authority')\n }\n if (compareV1DateTimes(wall.text, authority.expiresAt) >= 0) {\n return configTerminal('capture authority reached its wall-clock expiry')\n }\n if (monotonicNowUs < authority.monotonicStartedAtUs) {\n return configTerminal('capture monotonic clock moved backward')\n }\n const monotonicElapsed = monotonicNowUs - authority.monotonicStartedAtUs\n if (monotonicElapsed >= authority.monotonicBudgetUs) {\n return configTerminal('capture authority reached its monotonic expiry')\n }\n let wallElapsed: number\n try {\n wallElapsed = v1MicrosecondsBetween(wall.text, authority.installedWallAt)\n } catch {\n return configTerminal('capture wall-clock duration is unsafe')\n }\n if (wallElapsed < 0 || Math.abs(wallElapsed - monotonicElapsed) > 1_000_000) {\n return configTerminal('capture wall and monotonic clocks observed a lifecycle discontinuity')\n }\n return Math.max(\n 0,\n Math.min(\n authority.monotonicBudgetUs - monotonicElapsed,\n v1MicrosecondsBetween(authority.expiresAt, wall.text)\n )\n )\n }\n\n private async _commitAppend(\n transaction: V1DurableStorageTransaction,\n current: V1InstallationRow,\n identity: V1PersistedIdentityState,\n encoded: readonly V1EncodedRecord[]\n ): Promise<V1DurableAppendResult> {\n let addedBytes = 0\n for (const record of encoded) {\n if (!Number.isSafeInteger(addedBytes + record.stored.payloadBytes)) {\n return fail('queue-full', 'record bytes exceed the queue counter range')\n }\n addedBytes += record.stored.payloadBytes\n }\n const nextCount = current.queuedCount + encoded.length\n const nextBytes = current.queuedBytes + addedBytes\n // The limits are aggregate per site: live replay chunks share the count and byte budget.\n const aggregateCount = nextCount + (current.replay?.replayQueuedCount ?? 0)\n const aggregateBytes = nextBytes + (current.replay?.replayQueuedBytes ?? 0)\n const policy = this._queuePolicy\n if (\n encoded.length > 0 &&\n (policy || current.authorityBarrier) &&\n (!policy || current.authorityBarrier?.configIssuedAt !== policy.configIssuedAt ||\n current.authorityBarrier?.configSemanticHash !== policy.configSemanticHash)\n ) {\n return fail('queue-full', 'queue policy no longer matches the transaction-current configuration')\n }\n if (\n encoded.length > 0 &&\n (!Number.isSafeInteger(aggregateCount) ||\n !Number.isSafeInteger(aggregateBytes) ||\n aggregateCount > this._limits.maxCount ||\n aggregateBytes > (policy?.maxBytes ?? this._limits.maxBytes))\n ) {\n return fail('queue-full', 'newest append exceeds the durable queue limit')\n }\n\n const nextRow: V1InstallationRow = freezeV1Value({\n ...current,\n identity,\n nextSequence: current.nextSequence + encoded.length,\n queuedCount: nextCount,\n queuedBytes: nextBytes,\n })\n for (const record of encoded) {\n await transaction.addRecord(record.stored)\n }\n await transaction.putInstallation(nextRow)\n return freezeV1Value({\n state: snapshotFromRow(nextRow),\n records: freezeV1Value(encoded.map((record) => record.exposed)),\n })\n }\n\n private _appendTransactionOperation<TInput>(\n encode: (transaction: V1DurableStorageTransaction, current: V1InstallationRow, input: TInput) => Promise<V1DurableAppendResult>,\n publish: (result: V1DurableAppendResult, input: TInput) => V1DurableAppendResult\n ): (input: TInput) => Promise<V1DurableAppendResult> {\n // A callback factory shares the existing two await boundaries. It does not wrap\n // an awaited prefix in another async helper or extend transaction lifetime.\n return async (input) => {\n const storage = this._requireStorage()\n try {\n const result = await storage.runTransaction('readwrite', async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n return encode(transaction, current, input)\n })\n return publish(result, input)\n } catch (error) {\n this._rememberFatalFailure(error)\n throw error\n }\n }\n }\n\n private _requireStorage(): V1DurableStorage {\n if (this._poisoned) {\n throw this._poisoned\n }\n if (!this._storage) {\n return fail('not-initialized', 'durable state is not initialized')\n }\n return this._storage\n }\n\n private _invalidateCaptureAuthorityForLifecycleNow(): void {\n this._captureAuthority = null\n this._captureLifecycleEpoch = incrementStateCounter(this._captureLifecycleEpoch, 'capture lifecycle epoch')\n }\n\n private _rememberFatalFailure(error: unknown): void {\n const isFatalStateError =\n error instanceof V1DurableStateError &&\n (error.code === 'corrupt-storage' || error.code === 'unsupported-storage')\n const isFatalStorageError =\n error instanceof V1DurableStorageError && (error.code === 'unsupported' || error.code === 'closed')\n if (isFatalStateError || isFatalStorageError) {\n this._storage?.close()\n this._storage = null\n this._snapshot = null\n this._poisoned = error instanceof Error ? error : new Error('durable state failed closed')\n }\n }\n\n private _serialize<T>(operation: () => Promise<T>): Promise<T> {\n const result = this._tail.then(operation)\n this._tail = result.then(\n () => undefined,\n () => undefined\n )\n return result\n }\n\n private _snapshotAndSerialize<TInput, TResult>(\n snapshot: () => TInput,\n operation: (input: TInput) => Promise<TResult>\n ): Promise<TResult> {\n let input: TInput\n try {\n input = snapshot()\n } catch (error) {\n return this._serialize(async () => {\n throw error\n })\n }\n return this._serialize(() => operation(input))\n }\n}\n","// DEFLATE is a complex format; to read this code, you should probably check the RFC first:\n// https://tools.ietf.org/html/rfc1951\n// You may also wish to take a look at the guide I made about this program:\n// https://gist.github.com/101arrowz/253f31eb5abc3d9275ab943003ffecad\n// Much of the following code is similar to that of UZIP.js:\n// https://github.com/photopea/UZIP.js\n// Many optimizations have been made, so the bundle size is ultimately smaller but performance is similar.\n// Sometimes 0 will appear where -1 would be more appropriate. This is because using a uint\n// is better for memory in most engines (I *think*).\nvar ch2 = {};\nvar wk = (function (c, id, msg, transfer, cb) {\n var u = ch2[id] || (ch2[id] = URL.createObjectURL(new Blob([c], { type: 'text/javascript' })));\n var w = new Worker(u);\n w.onerror = function (e) { return cb(e.error, null); };\n w.onmessage = function (e) { return cb(null, e.data); };\n w.postMessage(msg, transfer);\n return w;\n});\n\n// aliases for shorter compressed code (most minifers don't do this)\nvar u8 = Uint8Array, u16 = Uint16Array, u32 = Uint32Array;\n// fixed length extra bits\nvar fleb = new u8([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, /* unused */ 0, 0, /* impossible */ 0]);\n// fixed distance extra bits\n// see fleb note\nvar fdeb = new u8([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* unused */ 0, 0]);\n// code length index map\nvar clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);\n// get base, reverse index map from extra bits\nvar freb = function (eb, start) {\n var b = new u16(31);\n for (var i = 0; i < 31; ++i) {\n b[i] = start += 1 << eb[i - 1];\n }\n // numbers here are at max 18 bits\n var r = new u32(b[30]);\n for (var i = 1; i < 30; ++i) {\n for (var j = b[i]; j < b[i + 1]; ++j) {\n r[j] = ((j - b[i]) << 5) | i;\n }\n }\n return [b, r];\n};\nvar _a = freb(fleb, 2), fl = _a[0], revfl = _a[1];\n// we can ignore the fact that the other numbers are wrong; they never happen anyway\nfl[28] = 258, revfl[258] = 28;\nvar _b = freb(fdeb, 0), fd = _b[0], revfd = _b[1];\n// map of value to reverse (assuming 16 bits)\nvar rev = new u16(32768);\nfor (var i = 0; i < 32768; ++i) {\n // reverse table algorithm from SO\n var x = ((i & 0xAAAA) >>> 1) | ((i & 0x5555) << 1);\n x = ((x & 0xCCCC) >>> 2) | ((x & 0x3333) << 2);\n x = ((x & 0xF0F0) >>> 4) | ((x & 0x0F0F) << 4);\n rev[i] = (((x & 0xFF00) >>> 8) | ((x & 0x00FF) << 8)) >>> 1;\n}\n// create huffman tree from u8 \"map\": index -> code length for code index\n// mb (max bits) must be at most 15\n// TODO: optimize/split up?\nvar hMap = (function (cd, mb, r) {\n var s = cd.length;\n // index\n var i = 0;\n // u16 \"map\": index -> # of codes with bit length = index\n var l = new u16(mb);\n // length of cd must be 288 (total # of codes)\n for (; i < s; ++i)\n ++l[cd[i] - 1];\n // u16 \"map\": index -> minimum code for bit length = index\n var le = new u16(mb);\n for (i = 0; i < mb; ++i) {\n le[i] = (le[i - 1] + l[i - 1]) << 1;\n }\n var co;\n if (r) {\n // u16 \"map\": index -> number of actual bits, symbol for code\n co = new u16(1 << mb);\n // bits to remove for reverser\n var rvb = 15 - mb;\n for (i = 0; i < s; ++i) {\n // ignore 0 lengths\n if (cd[i]) {\n // num encoding both symbol and bits read\n var sv = (i << 4) | cd[i];\n // free bits\n var r_1 = mb - cd[i];\n // start value\n var v = le[cd[i] - 1]++ << r_1;\n // m is end value\n for (var m = v | ((1 << r_1) - 1); v <= m; ++v) {\n // every 16 bit value starting with the code yields the same result\n co[rev[v] >>> rvb] = sv;\n }\n }\n }\n }\n else {\n co = new u16(s);\n for (i = 0; i < s; ++i)\n co[i] = rev[le[cd[i] - 1]++] >>> (15 - cd[i]);\n }\n return co;\n});\n// fixed length tree\nvar flt = new u8(288);\nfor (var i = 0; i < 144; ++i)\n flt[i] = 8;\nfor (var i = 144; i < 256; ++i)\n flt[i] = 9;\nfor (var i = 256; i < 280; ++i)\n flt[i] = 7;\nfor (var i = 280; i < 288; ++i)\n flt[i] = 8;\n// fixed distance tree\nvar fdt = new u8(32);\nfor (var i = 0; i < 32; ++i)\n fdt[i] = 5;\n// fixed length map\nvar flm = /*#__PURE__*/ hMap(flt, 9, 0), flrm = /*#__PURE__*/ hMap(flt, 9, 1);\n// fixed distance map\nvar fdm = /*#__PURE__*/ hMap(fdt, 5, 0), fdrm = /*#__PURE__*/ hMap(fdt, 5, 1);\n// find max of array\nvar max = function (a) {\n var m = a[0];\n for (var i = 1; i < a.length; ++i) {\n if (a[i] > m)\n m = a[i];\n }\n return m;\n};\n// read d, starting at bit p and mask with m\nvar bits = function (d, p, m) {\n var o = (p / 8) >> 0;\n return ((d[o] | (d[o + 1] << 8)) >>> (p & 7)) & m;\n};\n// read d, starting at bit p continuing for at least 16 bits\nvar bits16 = function (d, p) {\n var o = (p / 8) >> 0;\n return ((d[o] | (d[o + 1] << 8) | (d[o + 2] << 16)) >>> (p & 7));\n};\n// get end of byte\nvar shft = function (p) { return ((p / 8) >> 0) + (p & 7 && 1); };\n// typed array slice - allows garbage collector to free original reference,\n// while being more compatible than .slice\nvar slc = function (v, s, e) {\n if (s == null || s < 0)\n s = 0;\n if (e == null || e > v.length)\n e = v.length;\n // can't use .constructor in case user-supplied\n var n = new (v instanceof u16 ? u16 : v instanceof u32 ? u32 : u8)(e - s);\n n.set(v.subarray(s, e));\n return n;\n};\n// expands raw DEFLATE data\nvar inflt = function (dat, buf, st) {\n // source length\n var sl = dat.length;\n // have to estimate size\n var noBuf = !buf || st;\n // no state\n var noSt = !st || st.i;\n if (!st)\n st = {};\n // Assumes roughly 33% compression ratio average\n if (!buf)\n buf = new u8(sl * 3);\n // ensure buffer can fit at least l elements\n var cbuf = function (l) {\n var bl = buf.length;\n // need to increase size to fit\n if (l > bl) {\n // Double or set to necessary, whichever is greater\n var nbuf = new u8(Math.max(bl * 2, l));\n nbuf.set(buf);\n buf = nbuf;\n }\n };\n // last chunk bitpos bytes\n var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;\n // total bits\n var tbts = sl * 8;\n do {\n if (!lm) {\n // BFINAL - this is only 1 when last chunk is next\n st.f = final = bits(dat, pos, 1);\n // type: 0 = no compression, 1 = fixed huffman, 2 = dynamic huffman\n var type = bits(dat, pos + 1, 3);\n pos += 3;\n if (!type) {\n // go to end of byte boundary\n var s = shft(pos) + 4, l = dat[s - 4] | (dat[s - 3] << 8), t = s + l;\n if (t > sl) {\n if (noSt)\n throw 'unexpected EOF';\n break;\n }\n // ensure size\n if (noBuf)\n cbuf(bt + l);\n // Copy over uncompressed data\n buf.set(dat.subarray(s, t), bt);\n // Get new bitpos, update byte count\n st.b = bt += l, st.p = pos = t * 8;\n continue;\n }\n else if (type == 1)\n lm = flrm, dm = fdrm, lbt = 9, dbt = 5;\n else if (type == 2) {\n // literal lengths\n var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;\n var tl = hLit + bits(dat, pos + 5, 31) + 1;\n pos += 14;\n // length+distance tree\n var ldt = new u8(tl);\n // code length tree\n var clt = new u8(19);\n for (var i = 0; i < hcLen; ++i) {\n // use index map to get real code\n clt[clim[i]] = bits(dat, pos + i * 3, 7);\n }\n pos += hcLen * 3;\n // code lengths bits\n var clb = max(clt), clbmsk = (1 << clb) - 1;\n if (!noSt && pos + tl * (clb + 7) > tbts)\n break;\n // code lengths map\n var clm = hMap(clt, clb, 1);\n for (var i = 0; i < tl;) {\n var r = clm[bits(dat, pos, clbmsk)];\n // bits read\n pos += r & 15;\n // symbol\n var s = r >>> 4;\n // code length to copy\n if (s < 16) {\n ldt[i++] = s;\n }\n else {\n // copy count\n var c = 0, n = 0;\n if (s == 16)\n n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1];\n else if (s == 17)\n n = 3 + bits(dat, pos, 7), pos += 3;\n else if (s == 18)\n n = 11 + bits(dat, pos, 127), pos += 7;\n while (n--)\n ldt[i++] = c;\n }\n }\n // length tree distance tree\n var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);\n // max length bits\n lbt = max(lt);\n // max dist bits\n dbt = max(dt);\n lm = hMap(lt, lbt, 1);\n dm = hMap(dt, dbt, 1);\n }\n else\n throw 'invalid block type';\n if (pos > tbts)\n throw 'unexpected EOF';\n }\n // Make sure the buffer can hold this + the largest possible addition\n // Maximum chunk size (practically, theoretically infinite) is 2^17;\n if (noBuf)\n cbuf(bt + 131072);\n var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;\n var mxa = lbt + dbt + 18;\n while (noSt || pos + mxa < tbts) {\n // bits read, code\n var c = lm[bits16(dat, pos) & lms], sym = c >>> 4;\n pos += c & 15;\n if (pos > tbts)\n throw 'unexpected EOF';\n if (!c)\n throw 'invalid length/literal';\n if (sym < 256)\n buf[bt++] = sym;\n else if (sym == 256) {\n lm = null;\n break;\n }\n else {\n var add = sym - 254;\n // no extra bits needed if less\n if (sym > 264) {\n // index\n var i = sym - 257, b = fleb[i];\n add = bits(dat, pos, (1 << b) - 1) + fl[i];\n pos += b;\n }\n // dist\n var d = dm[bits16(dat, pos) & dms], dsym = d >>> 4;\n if (!d)\n throw 'invalid distance';\n pos += d & 15;\n var dt = fd[dsym];\n if (dsym > 3) {\n var b = fdeb[dsym];\n dt += bits16(dat, pos) & ((1 << b) - 1), pos += b;\n }\n if (pos > tbts)\n throw 'unexpected EOF';\n if (noBuf)\n cbuf(bt + 131072);\n var end = bt + add;\n for (; bt < end; bt += 4) {\n buf[bt] = buf[bt - dt];\n buf[bt + 1] = buf[bt + 1 - dt];\n buf[bt + 2] = buf[bt + 2 - dt];\n buf[bt + 3] = buf[bt + 3 - dt];\n }\n bt = end;\n }\n }\n st.l = lm, st.p = pos, st.b = bt;\n if (lm)\n final = 1, st.m = lbt, st.d = dm, st.n = dbt;\n } while (!final);\n return bt == buf.length ? buf : slc(buf, 0, bt);\n};\n// starting at p, write the minimum number of bits that can hold v to d\nvar wbits = function (d, p, v) {\n v <<= p & 7;\n var o = (p / 8) >> 0;\n d[o] |= v;\n d[o + 1] |= v >>> 8;\n};\n// starting at p, write the minimum number of bits (>8) that can hold v to d\nvar wbits16 = function (d, p, v) {\n v <<= p & 7;\n var o = (p / 8) >> 0;\n d[o] |= v;\n d[o + 1] |= v >>> 8;\n d[o + 2] |= v >>> 16;\n};\n// creates code lengths from a frequency table\nvar hTree = function (d, mb) {\n // Need extra info to make a tree\n var t = [];\n for (var i = 0; i < d.length; ++i) {\n if (d[i])\n t.push({ s: i, f: d[i] });\n }\n var s = t.length;\n var t2 = t.slice();\n if (!s)\n return [new u8(0), 0];\n if (s == 1) {\n var v = new u8(t[0].s + 1);\n v[t[0].s] = 1;\n return [v, 1];\n }\n t.sort(function (a, b) { return a.f - b.f; });\n // after i2 reaches last ind, will be stopped\n // freq must be greater than largest possible number of symbols\n t.push({ s: -1, f: 25001 });\n var l = t[0], r = t[1], i0 = 0, i1 = 1, i2 = 2;\n t[0] = { s: -1, f: l.f + r.f, l: l, r: r };\n // efficient algorithm from UZIP.js\n // i0 is lookbehind, i2 is lookahead - after processing two low-freq\n // symbols that combined have high freq, will start processing i2 (high-freq,\n // non-composite) symbols instead\n // see https://reddit.com/r/photopea/comments/ikekht/uzipjs_questions/\n while (i1 != s - 1) {\n l = t[t[i0].f < t[i2].f ? i0++ : i2++];\n r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];\n t[i1++] = { s: -1, f: l.f + r.f, l: l, r: r };\n }\n var maxSym = t2[0].s;\n for (var i = 1; i < s; ++i) {\n if (t2[i].s > maxSym)\n maxSym = t2[i].s;\n }\n // code lengths\n var tr = new u16(maxSym + 1);\n // max bits in tree\n var mbt = ln(t[i1 - 1], tr, 0);\n if (mbt > mb) {\n // more algorithms from UZIP.js\n // TODO: find out how this code works (debt)\n // ind debt\n var i = 0, dt = 0;\n // left cost\n var lft = mbt - mb, cst = 1 << lft;\n t2.sort(function (a, b) { return tr[b.s] - tr[a.s] || a.f - b.f; });\n for (; i < s; ++i) {\n var i2_1 = t2[i].s;\n if (tr[i2_1] > mb) {\n dt += cst - (1 << (mbt - tr[i2_1]));\n tr[i2_1] = mb;\n }\n else\n break;\n }\n dt >>>= lft;\n while (dt > 0) {\n var i2_2 = t2[i].s;\n if (tr[i2_2] < mb)\n dt -= 1 << (mb - tr[i2_2]++ - 1);\n else\n ++i;\n }\n for (; i >= 0 && dt; --i) {\n var i2_3 = t2[i].s;\n if (tr[i2_3] == mb) {\n --tr[i2_3];\n ++dt;\n }\n }\n mbt = mb;\n }\n return [new u8(tr), mbt];\n};\n// get the max length and assign length codes\nvar ln = function (n, l, d) {\n return n.s == -1\n ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1))\n : (l[n.s] = d);\n};\n// length codes generation\nvar lc = function (c) {\n var s = c.length;\n // Note that the semicolon was intentional\n while (s && !c[--s])\n ;\n var cl = new u16(++s);\n // ind num streak\n var cli = 0, cln = c[0], cls = 1;\n var w = function (v) { cl[cli++] = v; };\n for (var i = 1; i <= s; ++i) {\n if (c[i] == cln && i != s)\n ++cls;\n else {\n if (!cln && cls > 2) {\n for (; cls > 138; cls -= 138)\n w(32754);\n if (cls > 2) {\n w(cls > 10 ? ((cls - 11) << 5) | 28690 : ((cls - 3) << 5) | 12305);\n cls = 0;\n }\n }\n else if (cls > 3) {\n w(cln), --cls;\n for (; cls > 6; cls -= 6)\n w(8304);\n if (cls > 2)\n w(((cls - 3) << 5) | 8208), cls = 0;\n }\n while (cls--)\n w(cln);\n cls = 1;\n cln = c[i];\n }\n }\n return [cl.subarray(0, cli), s];\n};\n// calculate the length of output from tree, code lengths\nvar clen = function (cf, cl) {\n var l = 0;\n for (var i = 0; i < cl.length; ++i)\n l += cf[i] * cl[i];\n return l;\n};\n// writes a fixed block\n// returns the new bit pos\nvar wfblk = function (out, pos, dat) {\n // no need to write 00 as type: TypedArray defaults to 0\n var s = dat.length;\n var o = shft(pos + 2);\n out[o] = s & 255;\n out[o + 1] = s >>> 8;\n out[o + 2] = out[o] ^ 255;\n out[o + 3] = out[o + 1] ^ 255;\n for (var i = 0; i < s; ++i)\n out[o + i + 4] = dat[i];\n return (o + 4 + s) * 8;\n};\n// writes a block\nvar wblk = function (dat, out, final, syms, lf, df, eb, li, bs, bl, p) {\n wbits(out, p++, final);\n ++lf[256];\n var _a = hTree(lf, 15), dlt = _a[0], mlb = _a[1];\n var _b = hTree(df, 15), ddt = _b[0], mdb = _b[1];\n var _c = lc(dlt), lclt = _c[0], nlc = _c[1];\n var _d = lc(ddt), lcdt = _d[0], ndc = _d[1];\n var lcfreq = new u16(19);\n for (var i = 0; i < lclt.length; ++i)\n lcfreq[lclt[i] & 31]++;\n for (var i = 0; i < lcdt.length; ++i)\n lcfreq[lcdt[i] & 31]++;\n var _e = hTree(lcfreq, 7), lct = _e[0], mlcb = _e[1];\n var nlcc = 19;\n for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)\n ;\n var flen = (bl + 5) << 3;\n var ftlen = clen(lf, flt) + clen(df, fdt) + eb;\n var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + (2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18]);\n if (flen <= ftlen && flen <= dtlen)\n return wfblk(out, p, dat.subarray(bs, bs + bl));\n var lm, ll, dm, dl;\n wbits(out, p, 1 + (dtlen < ftlen)), p += 2;\n if (dtlen < ftlen) {\n lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;\n var llm = hMap(lct, mlcb, 0);\n wbits(out, p, nlc - 257);\n wbits(out, p + 5, ndc - 1);\n wbits(out, p + 10, nlcc - 4);\n p += 14;\n for (var i = 0; i < nlcc; ++i)\n wbits(out, p + 3 * i, lct[clim[i]]);\n p += 3 * nlcc;\n var lcts = [lclt, lcdt];\n for (var it = 0; it < 2; ++it) {\n var clct = lcts[it];\n for (var i = 0; i < clct.length; ++i) {\n var len = clct[i] & 31;\n wbits(out, p, llm[len]), p += lct[len];\n if (len > 15)\n wbits(out, p, (clct[i] >>> 5) & 127), p += clct[i] >>> 12;\n }\n }\n }\n else {\n lm = flm, ll = flt, dm = fdm, dl = fdt;\n }\n for (var i = 0; i < li; ++i) {\n if (syms[i] > 255) {\n var len = (syms[i] >>> 18) & 31;\n wbits16(out, p, lm[len + 257]), p += ll[len + 257];\n if (len > 7)\n wbits(out, p, (syms[i] >>> 23) & 31), p += fleb[len];\n var dst = syms[i] & 31;\n wbits16(out, p, dm[dst]), p += dl[dst];\n if (dst > 3)\n wbits16(out, p, (syms[i] >>> 5) & 8191), p += fdeb[dst];\n }\n else {\n wbits16(out, p, lm[syms[i]]), p += ll[syms[i]];\n }\n }\n wbits16(out, p, lm[256]);\n return p + ll[256];\n};\n// deflate options (nice << 13) | chain\nvar deo = /*#__PURE__*/ new u32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);\n// empty\nvar et = /*#__PURE__*/ new u8(0);\n// compresses data into a raw DEFLATE buffer\nvar dflt = function (dat, lvl, plvl, pre, post, lst) {\n var s = dat.length;\n var o = new u8(pre + s + 5 * (1 + Math.floor(s / 7000)) + post);\n // writing to this writes to the output buffer\n var w = o.subarray(pre, o.length - post);\n var pos = 0;\n if (!lvl || s < 8) {\n for (var i = 0; i <= s; i += 65535) {\n // end\n var e = i + 65535;\n if (e < s) {\n // write full block\n pos = wfblk(w, pos, dat.subarray(i, e));\n }\n else {\n // write final block\n w[i] = lst;\n pos = wfblk(w, pos, dat.subarray(i, s));\n }\n }\n }\n else {\n var opt = deo[lvl - 1];\n var n = opt >>> 13, c = opt & 8191;\n var msk_1 = (1 << plvl) - 1;\n // prev 2-byte val map curr 2-byte val map\n var prev = new u16(32768), head = new u16(msk_1 + 1);\n var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;\n var hsh = function (i) { return (dat[i] ^ (dat[i + 1] << bs1_1) ^ (dat[i + 2] << bs2_1)) & msk_1; };\n // 24576 is an arbitrary number of maximum symbols per block\n // 424 buffer for last block\n var syms = new u32(25000);\n // length/literal freq distance freq\n var lf = new u16(288), df = new u16(32);\n // l/lcnt exbits index l/lind waitdx bitpos\n var lc_1 = 0, eb = 0, i = 0, li = 0, wi = 0, bs = 0;\n for (; i < s; ++i) {\n // hash value\n var hv = hsh(i);\n // index mod 32768\n var imod = i & 32767;\n // previous index with this value\n var pimod = head[hv];\n prev[imod] = pimod;\n head[hv] = imod;\n // We always should modify head and prev, but only add symbols if\n // this data is not yet processed (\"wait\" for wait index)\n if (wi <= i) {\n // bytes remaining\n var rem = s - i;\n if ((lc_1 > 7000 || li > 24576) && rem > 423) {\n pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i - bs, pos);\n li = lc_1 = eb = 0, bs = i;\n for (var j = 0; j < 286; ++j)\n lf[j] = 0;\n for (var j = 0; j < 30; ++j)\n df[j] = 0;\n }\n // len dist chain\n var l = 2, d = 0, ch_1 = c, dif = (imod - pimod) & 32767;\n if (rem > 2 && hv == hsh(i - dif)) {\n var maxn = Math.min(n, rem) - 1;\n var maxd = Math.min(32767, i);\n // max possible length\n // not capped at dif because decompressors implement \"rolling\" index population\n var ml = Math.min(258, rem);\n while (dif <= maxd && --ch_1 && imod != pimod) {\n if (dat[i + l] == dat[i + l - dif]) {\n var nl = 0;\n for (; nl < ml && dat[i + nl] == dat[i + nl - dif]; ++nl)\n ;\n if (nl > l) {\n l = nl, d = dif;\n // break out early when we reach \"nice\" (we are satisfied enough)\n if (nl > maxn)\n break;\n // now, find the rarest 2-byte sequence within this\n // length of literals and search for that instead.\n // Much faster than just using the start\n var mmd = Math.min(dif, nl - 2);\n var md = 0;\n for (var j = 0; j < mmd; ++j) {\n var ti = (i - dif + j + 32768) & 32767;\n var pti = prev[ti];\n var cd = (ti - pti + 32768) & 32767;\n if (cd > md)\n md = cd, pimod = ti;\n }\n }\n }\n // check the previous match\n imod = pimod, pimod = prev[imod];\n dif += (imod - pimod + 32768) & 32767;\n }\n }\n // d will be nonzero only when a match was found\n if (d) {\n // store both dist and len data in one Uint32\n // Make sure this is recognized as a len/dist with 28th bit (2^28)\n syms[li++] = 268435456 | (revfl[l] << 18) | revfd[d];\n var lin = revfl[l] & 31, din = revfd[d] & 31;\n eb += fleb[lin] + fdeb[din];\n ++lf[257 + lin];\n ++df[din];\n wi = i + l;\n ++lc_1;\n }\n else {\n syms[li++] = dat[i];\n ++lf[dat[i]];\n }\n }\n }\n pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);\n // this is the easiest way to avoid needing to maintain state\n if (!lst)\n pos = wfblk(w, pos, et);\n }\n return slc(o, 0, pre + shft(pos) + post);\n};\n// CRC32 table\nvar crct = /*#__PURE__*/ (function () {\n var t = new u32(256);\n for (var i = 0; i < 256; ++i) {\n var c = i, k = 9;\n while (--k)\n c = ((c & 1) && 0xEDB88320) ^ (c >>> 1);\n t[i] = c;\n }\n return t;\n})();\n// CRC32\nvar crc = function () {\n var c = 0xFFFFFFFF;\n return {\n p: function (d) {\n // closures have awful performance\n var cr = c;\n for (var i = 0; i < d.length; ++i)\n cr = crct[(cr & 255) ^ d[i]] ^ (cr >>> 8);\n c = cr;\n },\n d: function () { return c ^ 0xFFFFFFFF; }\n };\n};\n// Alder32\nvar adler = function () {\n var a = 1, b = 0;\n return {\n p: function (d) {\n // closures have awful performance\n var n = a, m = b;\n var l = d.length;\n for (var i = 0; i != l;) {\n var e = Math.min(i + 5552, l);\n for (; i < e; ++i)\n n += d[i], m += n;\n n %= 65521, m %= 65521;\n }\n a = n, b = m;\n },\n d: function () { return ((a >>> 8) << 16 | (b & 255) << 8 | (b >>> 8)) + ((a & 255) << 23) * 2; }\n };\n};\n;\n// deflate with opts\nvar dopt = function (dat, opt, pre, post, st) {\n return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : (12 + opt.mem), pre, post, !st);\n};\n// Walmart object spread\nvar mrg = function (a, b) {\n var o = {};\n for (var k in a)\n o[k] = a[k];\n for (var k in b)\n o[k] = b[k];\n return o;\n};\n// worker clone\n// This is possibly the craziest part of the entire codebase, despite how simple it may seem.\n// The only parameter to this function is a closure that returns an array of variables outside of the function scope.\n// We're going to try to figure out the variable names used in the closure as strings because that is crucial for workerization.\n// We will return an object mapping of true variable name to value (basically, the current scope as a JS object).\n// The reason we can't just use the original variable names is minifiers mangling the toplevel scope.\n// This took me three weeks to figure out how to do.\nvar wcln = function (fn, fnStr, td) {\n var dt = fn();\n var st = fn.toString();\n var ks = st.slice(st.indexOf('[') + 1, st.lastIndexOf(']')).replace(/ /g, '').split(',');\n for (var i = 0; i < dt.length; ++i) {\n var v = dt[i], k = ks[i];\n if (typeof v == 'function') {\n fnStr += ';' + k + '=';\n var st_1 = v.toString();\n if (v.prototype) {\n // for global objects\n if (st_1.indexOf('[native code]') != -1) {\n var spInd = st_1.indexOf(' ', 8) + 1;\n fnStr += st_1.slice(spInd, st_1.indexOf('(', spInd));\n }\n else {\n fnStr += st_1;\n for (var t in v.prototype)\n fnStr += ';' + k + '.prototype.' + t + '=' + v.prototype[t].toString();\n }\n }\n else\n fnStr += st_1;\n }\n else\n td[k] = v;\n }\n return [fnStr, td];\n};\nvar ch = [];\n// clone bufs\nvar cbfs = function (v) {\n var tl = [];\n for (var k in v) {\n if (v[k] instanceof u8 || v[k] instanceof u16 || v[k] instanceof u32)\n tl.push((v[k] = new v[k].constructor(v[k])).buffer);\n }\n return tl;\n};\n// use a worker to execute code\nvar wrkr = function (fns, init, id, cb) {\n var _a;\n if (!ch[id]) {\n var fnStr = '', td_1 = {}, m = fns.length - 1;\n for (var i = 0; i < m; ++i)\n _a = wcln(fns[i], fnStr, td_1), fnStr = _a[0], td_1 = _a[1];\n ch[id] = wcln(fns[m], fnStr, td_1);\n }\n var td = mrg({}, ch[id][1]);\n return wk(ch[id][0] + ';onmessage=function(e){for(var k in e.data)self[k]=e.data[k];onmessage=' + init.toString() + '}', id, td, cbfs(td), cb);\n};\n// base async inflate fn\nvar bInflt = function () { return [u8, u16, u32, fleb, fdeb, clim, fl, fd, flrm, fdrm, rev, hMap, max, bits, bits16, shft, slc, inflt, inflateSync, pbf, gu8]; };\nvar bDflt = function () { return [u8, u16, u32, fleb, fdeb, clim, revfl, revfd, flm, flt, fdm, fdt, rev, deo, et, hMap, wbits, wbits16, hTree, ln, lc, clen, wfblk, wblk, shft, slc, dflt, dopt, deflateSync, pbf]; };\n// gzip extra\nvar gze = function () { return [gzh, gzhl, wbytes, crc, crct]; };\n// gunzip extra\nvar guze = function () { return [gzs, gzl]; };\n// zlib extra\nvar zle = function () { return [zlh, wbytes, adler]; };\n// unzlib extra\nvar zule = function () { return [zlv]; };\n// post buf\nvar pbf = function (msg) { return postMessage(msg, [msg.buffer]); };\n// get u8\nvar gu8 = function (o) { return o && o.size && new u8(o.size); };\n// async helper\nvar cbify = function (dat, opts, fns, init, id, cb) {\n var w = wrkr(fns, init, id, function (err, dat) {\n w.terminate();\n cb(err, dat);\n });\n if (!opts.consume)\n dat = new u8(dat);\n w.postMessage([dat, opts], [dat.buffer]);\n return function () { w.terminate(); };\n};\n// auto stream\nvar astrm = function (strm) {\n strm.ondata = function (dat, final) { return postMessage([dat, final], [dat.buffer]); };\n return function (ev) { return strm.push(ev.data[0], ev.data[1]); };\n};\n// async stream attach\nvar astrmify = function (fns, strm, opts, init, id) {\n var t;\n var w = wrkr(fns, init, id, function (err, dat) {\n if (err)\n w.terminate(), strm.ondata.call(strm, err);\n else {\n if (dat[1])\n w.terminate();\n strm.ondata.call(strm, err, dat[0], dat[1]);\n }\n });\n w.postMessage(opts);\n strm.push = function (d, f) {\n if (t)\n throw 'stream finished';\n if (!strm.ondata)\n throw 'no stream handler';\n w.postMessage([d, t = f], [d.buffer]);\n };\n strm.terminate = function () { w.terminate(); };\n};\n// read 2 bytes\nvar b2 = function (d, b) { return d[b] | (d[b + 1] << 8); };\n// read 4 bytes\nvar b4 = function (d, b) { return (d[b] | (d[b + 1] << 8) | (d[b + 2] << 16)) + (d[b + 3] << 23) * 2; };\n// write bytes\nvar wbytes = function (d, b, v) {\n for (; v; ++b)\n d[b] = v, v >>>= 8;\n};\n// gzip header\nvar gzh = function (c, o) {\n var fn = o.filename;\n c[0] = 31, c[1] = 139, c[2] = 8, c[8] = o.level < 2 ? 4 : o.level == 9 ? 2 : 0, c[9] = 3; // assume Unix\n if (o.mtime != 0)\n wbytes(c, 4, Math.floor(new Date(o.mtime || Date.now()) / 1000));\n if (fn) {\n c[3] = 8;\n for (var i = 0; i <= fn.length; ++i)\n c[i + 10] = fn.charCodeAt(i);\n }\n};\n// gzip footer: -8 to -4 = CRC, -4 to -0 is length\n// gzip start\nvar gzs = function (d) {\n if (d[0] != 31 || d[1] != 139 || d[2] != 8)\n throw 'invalid gzip data';\n var flg = d[3];\n var st = 10;\n if (flg & 4)\n st += d[10] | (d[11] << 8) + 2;\n for (var zs = (flg >> 3 & 1) + (flg >> 4 & 1); zs > 0; zs -= !d[st++])\n ;\n return st + (flg & 2);\n};\n// gzip length\nvar gzl = function (d) {\n var l = d.length;\n return (d[l - 4] | d[l - 3] << 8 | d[l - 2] << 16) + (2 * (d[l - 1] << 23));\n};\n// gzip header length\nvar gzhl = function (o) { return 10 + ((o.filename && (o.filename.length + 1)) || 0); };\n// zlib header\nvar zlh = function (c, o) {\n var lv = o.level, fl = lv == 0 ? 0 : lv < 6 ? 1 : lv == 9 ? 3 : 2;\n c[0] = 120, c[1] = (fl << 6) | (fl ? (32 - 2 * fl) : 1);\n};\n// zlib valid\nvar zlv = function (d) {\n if ((d[0] & 15) != 8 || (d[0] >>> 4) > 7 || ((d[0] << 8 | d[1]) % 31))\n throw 'invalid zlib data';\n if (d[1] & 32)\n throw 'invalid zlib data: preset dictionaries not supported';\n};\nfunction AsyncCmpStrm(opts, cb) {\n if (!cb && typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n return opts;\n}\n// zlib footer: -4 to -0 is Adler32\n/**\n * Streaming DEFLATE compression\n */\nvar Deflate = /*#__PURE__*/ (function () {\n function Deflate(opts, cb) {\n if (!cb && typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n this.o = opts || {};\n }\n Deflate.prototype.p = function (c, f) {\n this.ondata(dopt(c, this.o, 0, 0, !f), f);\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Deflate.prototype.push = function (chunk, final) {\n if (this.d)\n throw 'stream finished';\n if (!this.ondata)\n throw 'no stream handler';\n this.d = final;\n this.p(chunk, final || false);\n };\n return Deflate;\n}());\nexport { Deflate };\n/**\n * Asynchronous streaming DEFLATE compression\n */\nvar AsyncDeflate = /*#__PURE__*/ (function () {\n function AsyncDeflate(opts, cb) {\n astrmify([\n bDflt,\n function () { return [astrm, Deflate]; }\n ], this, AsyncCmpStrm.call(this, opts, cb), function (ev) {\n var strm = new Deflate(ev.data);\n onmessage = astrm(strm);\n }, 6);\n }\n return AsyncDeflate;\n}());\nexport { AsyncDeflate };\nexport function deflate(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return cbify(data, opts, [\n bDflt,\n ], function (ev) { return pbf(deflateSync(ev.data[0], ev.data[1])); }, 0, cb);\n}\n/**\n * Compresses data with DEFLATE without any wrapper\n * @param data The data to compress\n * @param opts The compression options\n * @returns The deflated version of the data\n */\nexport function deflateSync(data, opts) {\n if (opts === void 0) { opts = {}; }\n return dopt(data, opts, 0, 0);\n}\n/**\n * Streaming DEFLATE decompression\n */\nvar Inflate = /*#__PURE__*/ (function () {\n /**\n * Creates an inflation stream\n * @param cb The callback to call whenever data is inflated\n */\n function Inflate(cb) {\n this.s = {};\n this.p = new u8(0);\n this.ondata = cb;\n }\n Inflate.prototype.e = function (c) {\n if (this.d)\n throw 'stream finished';\n if (!this.ondata)\n throw 'no stream handler';\n var l = this.p.length;\n var n = new u8(l + c.length);\n n.set(this.p), n.set(c, l), this.p = n;\n };\n Inflate.prototype.c = function (final) {\n this.d = this.s.i = final || false;\n var bts = this.s.b;\n var dt = inflt(this.p, this.o, this.s);\n this.ondata(slc(dt, bts, this.s.b), this.d);\n this.o = slc(dt, this.s.b - 32768), this.s.b = this.o.length;\n this.p = slc(this.p, (this.s.p / 8) >> 0), this.s.p &= 7;\n };\n /**\n * Pushes a chunk to be inflated\n * @param chunk The chunk to push\n * @param final Whether this is the final chunk\n */\n Inflate.prototype.push = function (chunk, final) {\n this.e(chunk), this.c(final);\n };\n return Inflate;\n}());\nexport { Inflate };\n/**\n * Asynchronous streaming DEFLATE decompression\n */\nvar AsyncInflate = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous inflation stream\n * @param cb The callback to call whenever data is deflated\n */\n function AsyncInflate(cb) {\n this.ondata = cb;\n astrmify([\n bInflt,\n function () { return [astrm, Inflate]; }\n ], this, 0, function () {\n var strm = new Inflate();\n onmessage = astrm(strm);\n }, 7);\n }\n return AsyncInflate;\n}());\nexport { AsyncInflate };\nexport function inflate(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return cbify(data, opts, [\n bInflt\n ], function (ev) { return pbf(inflateSync(ev.data[0], gu8(ev.data[1]))); }, 1, cb);\n}\n/**\n * Expands DEFLATE data with no wrapper\n * @param data The data to decompress\n * @param out Where to write the data. Saves memory if you know the decompressed size and provide an output buffer of that length.\n * @returns The decompressed version of the data\n */\nexport function inflateSync(data, out) {\n return inflt(data, out);\n}\n// before you yell at me for not just using extends, my reason is that TS inheritance is hard to workerize.\n/**\n * Streaming GZIP compression\n */\nvar Gzip = /*#__PURE__*/ (function () {\n function Gzip(opts, cb) {\n this.c = crc();\n this.l = 0;\n this.v = 1;\n Deflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be GZIPped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Gzip.prototype.push = function (chunk, final) {\n Deflate.prototype.push.call(this, chunk, final);\n };\n Gzip.prototype.p = function (c, f) {\n this.c.p(c);\n this.l += c.length;\n var raw = dopt(c, this.o, this.v && gzhl(this.o), f && 8, !f);\n if (this.v)\n gzh(raw, this.o), this.v = 0;\n if (f)\n wbytes(raw, raw.length - 8, this.c.d()), wbytes(raw, raw.length - 4, this.l);\n this.ondata(raw, f);\n };\n return Gzip;\n}());\nexport { Gzip };\n/**\n * Asynchronous streaming GZIP compression\n */\nvar AsyncGzip = /*#__PURE__*/ (function () {\n function AsyncGzip(opts, cb) {\n astrmify([\n bDflt,\n gze,\n function () { return [astrm, Deflate, Gzip]; }\n ], this, AsyncCmpStrm.call(this, opts, cb), function (ev) {\n var strm = new Gzip(ev.data);\n onmessage = astrm(strm);\n }, 8);\n }\n return AsyncGzip;\n}());\nexport { AsyncGzip };\nexport function gzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return cbify(data, opts, [\n bDflt,\n gze,\n function () { return [gzipSync]; }\n ], function (ev) { return pbf(gzipSync(ev.data[0], ev.data[1])); }, 2, cb);\n}\n/**\n * Compresses data with GZIP\n * @param data The data to compress\n * @param opts The compression options\n * @returns The gzipped version of the data\n */\nexport function gzipSync(data, opts) {\n if (opts === void 0) { opts = {}; }\n var c = crc(), l = data.length;\n c.p(data);\n var d = dopt(data, opts, gzhl(opts), 8), s = d.length;\n return gzh(d, opts), wbytes(d, s - 8, c.d()), wbytes(d, s - 4, l), d;\n}\n/**\n * Streaming GZIP decompression\n */\nvar Gunzip = /*#__PURE__*/ (function () {\n /**\n * Creates a GUNZIP stream\n * @param cb The callback to call whenever data is inflated\n */\n function Gunzip(cb) {\n this.v = 1;\n Inflate.call(this, cb);\n }\n /**\n * Pushes a chunk to be GUNZIPped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Gunzip.prototype.push = function (chunk, final) {\n Inflate.prototype.e.call(this, chunk);\n if (this.v) {\n var s = gzs(this.p);\n if (s >= this.p.length && !final)\n return;\n this.p = this.p.subarray(s), this.v = 0;\n }\n if (final) {\n if (this.p.length < 8)\n throw 'invalid gzip stream';\n this.p = this.p.subarray(0, -8);\n }\n // necessary to prevent TS from using the closure value\n // This allows for workerization to function correctly\n Inflate.prototype.c.call(this, final);\n };\n return Gunzip;\n}());\nexport { Gunzip };\n/**\n * Asynchronous streaming GZIP decompression\n */\nvar AsyncGunzip = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous GUNZIP stream\n * @param cb The callback to call whenever data is deflated\n */\n function AsyncGunzip(cb) {\n this.ondata = cb;\n astrmify([\n bInflt,\n guze,\n function () { return [astrm, Inflate, Gunzip]; }\n ], this, 0, function () {\n var strm = new Gunzip();\n onmessage = astrm(strm);\n }, 9);\n }\n return AsyncGunzip;\n}());\nexport { AsyncGunzip };\nexport function gunzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return cbify(data, opts, [\n bInflt,\n guze,\n function () { return [gunzipSync]; }\n ], function (ev) { return pbf(gunzipSync(ev.data[0])); }, 3, cb);\n}\n/**\n * Expands GZIP data\n * @param data The data to decompress\n * @param out Where to write the data. GZIP already encodes the output size, so providing this doesn't save memory.\n * @returns The decompressed version of the data\n */\nexport function gunzipSync(data, out) {\n return inflt(data.subarray(gzs(data), -8), out || new u8(gzl(data)));\n}\n/**\n * Streaming Zlib compression\n */\nvar Zlib = /*#__PURE__*/ (function () {\n function Zlib(opts, cb) {\n this.c = adler();\n this.v = 1;\n Deflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be zlibbed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Zlib.prototype.push = function (chunk, final) {\n Deflate.prototype.push.call(this, chunk, final);\n };\n Zlib.prototype.p = function (c, f) {\n this.c.p(c);\n var raw = dopt(c, this.o, this.v && 2, f && 4, !f);\n if (this.v)\n zlh(raw, this.o), this.v = 0;\n if (f)\n wbytes(raw, raw.length - 4, this.c.d());\n this.ondata(raw, f);\n };\n return Zlib;\n}());\nexport { Zlib };\n/**\n * Asynchronous streaming Zlib compression\n */\nvar AsyncZlib = /*#__PURE__*/ (function () {\n function AsyncZlib(opts, cb) {\n astrmify([\n bDflt,\n zle,\n function () { return [astrm, Deflate, Zlib]; }\n ], this, AsyncCmpStrm.call(this, opts, cb), function (ev) {\n var strm = new Zlib(ev.data);\n onmessage = astrm(strm);\n }, 10);\n }\n return AsyncZlib;\n}());\nexport { AsyncZlib };\nexport function zlib(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return cbify(data, opts, [\n bDflt,\n zle,\n function () { return [zlibSync]; }\n ], function (ev) { return pbf(zlibSync(ev.data[0], ev.data[1])); }, 4, cb);\n}\n/**\n * Compress data with Zlib\n * @param data The data to compress\n * @param opts The compression options\n * @returns The zlib-compressed version of the data\n */\nexport function zlibSync(data, opts) {\n if (opts === void 0) { opts = {}; }\n var a = adler();\n a.p(data);\n var d = dopt(data, opts, 2, 4);\n return zlh(d, opts), wbytes(d, d.length - 4, a.d()), d;\n}\n/**\n * Streaming Zlib decompression\n */\nvar Unzlib = /*#__PURE__*/ (function () {\n /**\n * Creates a Zlib decompression stream\n * @param cb The callback to call whenever data is inflated\n */\n function Unzlib(cb) {\n this.v = 1;\n Inflate.call(this, cb);\n }\n /**\n * Pushes a chunk to be unzlibbed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Unzlib.prototype.push = function (chunk, final) {\n Inflate.prototype.e.call(this, chunk);\n if (this.v) {\n if (this.p.length < 2 && !final)\n return;\n this.p = this.p.subarray(2), this.v = 0;\n }\n if (final) {\n if (this.p.length < 4)\n throw 'invalid zlib stream';\n this.p = this.p.subarray(0, -4);\n }\n // necessary to prevent TS from using the closure value\n // This allows for workerization to function correctly\n Inflate.prototype.c.call(this, final);\n };\n return Unzlib;\n}());\nexport { Unzlib };\n/**\n * Asynchronous streaming Zlib decompression\n */\nvar AsyncUnzlib = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous Zlib decompression stream\n * @param cb The callback to call whenever data is deflated\n */\n function AsyncUnzlib(cb) {\n this.ondata = cb;\n astrmify([\n bInflt,\n zule,\n function () { return [astrm, Inflate, Unzlib]; }\n ], this, 0, function () {\n var strm = new Unzlib();\n onmessage = astrm(strm);\n }, 11);\n }\n return AsyncUnzlib;\n}());\nexport { AsyncUnzlib };\nexport function unzlib(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return cbify(data, opts, [\n bInflt,\n zule,\n function () { return [unzlibSync]; }\n ], function (ev) { return pbf(unzlibSync(ev.data[0], gu8(ev.data[1]))); }, 5, cb);\n}\n/**\n * Expands Zlib data\n * @param data The data to decompress\n * @param out Where to write the data. Saves memory if you know the decompressed size and provide an output buffer of that length.\n * @returns The decompressed version of the data\n */\nexport function unzlibSync(data, out) {\n return inflt((zlv(data), data.subarray(2, -4)), out);\n}\n// Default algorithm for compression (used because having a known output size allows faster decompression)\nexport { gzip as compress, AsyncGzip as AsyncCompress };\n// Default algorithm for compression (used because having a known output size allows faster decompression)\nexport { gzipSync as compressSync, Gzip as Compress };\n/**\n * Streaming GZIP, Zlib, or raw DEFLATE decompression\n */\nvar Decompress = /*#__PURE__*/ (function () {\n /**\n * Creates a decompression stream\n * @param cb The callback to call whenever data is decompressed\n */\n function Decompress(cb) {\n this.G = Gunzip;\n this.I = Inflate;\n this.Z = Unzlib;\n this.ondata = cb;\n }\n /**\n * Pushes a chunk to be decompressed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Decompress.prototype.push = function (chunk, final) {\n if (!this.ondata)\n throw 'no stream handler';\n if (!this.s) {\n if (this.p && this.p.length) {\n var n = new u8(this.p.length + chunk.length);\n n.set(this.p), n.set(chunk, this.p.length);\n }\n else\n this.p = chunk;\n if (this.p.length > 2) {\n var _this_1 = this;\n var cb = function () { _this_1.ondata.apply(_this_1, arguments); };\n this.s = (this.p[0] == 31 && this.p[1] == 139 && this.p[2] == 8)\n ? new this.G(cb)\n : ((this.p[0] & 15) != 8 || (this.p[0] >> 4) > 7 || ((this.p[0] << 8 | this.p[1]) % 31))\n ? new this.I(cb)\n : new this.Z(cb);\n this.s.push(this.p, final);\n this.p = null;\n }\n }\n else\n this.s.push(chunk, final);\n };\n return Decompress;\n}());\nexport { Decompress };\n/**\n * Asynchronous streaming GZIP, Zlib, or raw DEFLATE decompression\n */\nvar AsyncDecompress = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous decompression stream\n * @param cb The callback to call whenever data is decompressed\n */\n function AsyncDecompress(cb) {\n this.G = AsyncGunzip;\n this.I = AsyncInflate;\n this.Z = AsyncUnzlib;\n this.ondata = cb;\n }\n /**\n * Pushes a chunk to be decompressed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n AsyncDecompress.prototype.push = function (chunk, final) {\n Decompress.prototype.push.call(this, chunk, final);\n };\n return AsyncDecompress;\n}());\nexport { AsyncDecompress };\nexport function decompress(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return (data[0] == 31 && data[1] == 139 && data[2] == 8)\n ? gunzip(data, opts, cb)\n : ((data[0] & 15) != 8 || (data[0] >> 4) > 7 || ((data[0] << 8 | data[1]) % 31))\n ? inflate(data, opts, cb)\n : unzlib(data, opts, cb);\n}\n/**\n * Expands compressed GZIP, Zlib, or raw DEFLATE data, automatically detecting the format\n * @param data The data to decompress\n * @param out Where to write the data. Saves memory if you know the decompressed size and provide an output buffer of that length.\n * @returns The decompressed version of the data\n */\nexport function decompressSync(data, out) {\n return (data[0] == 31 && data[1] == 139 && data[2] == 8)\n ? gunzipSync(data, out)\n : ((data[0] & 15) != 8 || (data[0] >> 4) > 7 || ((data[0] << 8 | data[1]) % 31))\n ? inflateSync(data, out)\n : unzlibSync(data, out);\n}\n// flatten a directory structure\nvar fltn = function (d, p, t, o) {\n for (var k in d) {\n var val = d[k], n = p + k;\n if (val instanceof u8)\n t[n] = [val, o];\n else if (Array.isArray(val))\n t[n] = [val[0], mrg(o, val[1])];\n else\n fltn(val, n + '/', t, o);\n }\n};\n/**\n * Converts a string into a Uint8Array for use with compression/decompression methods\n * @param str The string to encode\n * @param latin1 Whether or not to interpret the data as Latin-1. This should\n * not need to be true unless decoding a binary string.\n * @returns The string encoded in UTF-8/Latin-1 binary\n */\nexport function strToU8(str, latin1) {\n var l = str.length;\n if (!latin1 && typeof TextEncoder != 'undefined')\n return new TextEncoder().encode(str);\n var ar = new u8(str.length + (str.length >>> 1));\n var ai = 0;\n var w = function (v) { ar[ai++] = v; };\n for (var i = 0; i < l; ++i) {\n if (ai + 5 > ar.length) {\n var n = new u8(ai + 8 + ((l - i) << 1));\n n.set(ar);\n ar = n;\n }\n var c = str.charCodeAt(i);\n if (c < 128 || latin1)\n w(c);\n else if (c < 2048)\n w(192 | (c >>> 6)), w(128 | (c & 63));\n else if (c > 55295 && c < 57344)\n c = 65536 + (c & 1023 << 10) | (str.charCodeAt(++i) & 1023),\n w(240 | (c >>> 18)), w(128 | ((c >>> 12) & 63)), w(128 | ((c >>> 6) & 63)), w(128 | (c & 63));\n else\n w(224 | (c >>> 12)), w(128 | ((c >>> 6) & 63)), w(128 | (c & 63));\n }\n return slc(ar, 0, ai);\n}\n/**\n * Converts a Uint8Array to a string\n * @param dat The data to decode to string\n * @param latin1 Whether or not to interpret the data as Latin-1. This should\n * not need to be true unless encoding to binary string.\n * @returns The original UTF-8/Latin-1 string\n */\nexport function strFromU8(dat, latin1) {\n var r = '';\n if (!latin1 && typeof TextDecoder != 'undefined')\n return new TextDecoder().decode(dat);\n for (var i = 0; i < dat.length;) {\n var c = dat[i++];\n if (c < 128 || latin1)\n r += String.fromCharCode(c);\n else if (c < 224)\n r += String.fromCharCode((c & 31) << 6 | (dat[i++] & 63));\n else if (c < 240)\n r += String.fromCharCode((c & 15) << 12 | (dat[i++] & 63) << 6 | (dat[i++] & 63));\n else\n c = ((c & 15) << 18 | (dat[i++] & 63) << 12 | (dat[i++] & 63) << 6 | (dat[i++] & 63)) - 65536,\n r += String.fromCharCode(55296 | (c >> 10), 56320 | (c & 1023));\n }\n return r;\n}\n;\n// skip local zip header\nvar slzh = function (d, b) { return b + 30 + b2(d, b + 26) + b2(d, b + 28); };\n// read zip header\nvar zh = function (d, b, z) {\n var fnl = b2(d, b + 28), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl;\n var _a = z ? z64e(d, es) : [b4(d, b + 20), b4(d, b + 24), b4(d, b + 42)], sc = _a[0], su = _a[1], off = _a[2];\n return [b2(d, b + 10), sc, su, fn, es + b2(d, b + 30) + b2(d, b + 32), off];\n};\n// read zip64 extra field\nvar z64e = function (d, b) {\n for (; b2(d, b) != 1; b += 4 + b2(d, b + 2))\n ;\n return [b4(d, b + 12), b4(d, b + 4), b4(d, b + 20)];\n};\n// write zip header\nvar wzh = function (d, b, c, cmp, su, fn, u, o, ce, t) {\n var fl = fn.length, l = cmp.length;\n wbytes(d, b, ce != null ? 0x2014B50 : 0x4034B50), b += 4;\n if (ce != null)\n d[b] = 20, b += 2;\n d[b] = 20, b += 2; // spec compliance? what's that?\n d[b++] = (t == 8 && (o.level == 1 ? 6 : o.level < 6 ? 4 : o.level == 9 ? 2 : 0)), d[b++] = u && 8;\n d[b] = t, b += 2;\n var dt = new Date(o.mtime || Date.now()), y = dt.getFullYear() - 1980;\n if (y < 0 || y > 119)\n throw 'date not in range 1980-2099';\n wbytes(d, b, ((y << 24) * 2) | ((dt.getMonth() + 1) << 21) | (dt.getDate() << 16) | (dt.getHours() << 11) | (dt.getMinutes() << 5) | (dt.getSeconds() >>> 1));\n b += 4;\n wbytes(d, b, c);\n wbytes(d, b + 4, l);\n wbytes(d, b + 8, su);\n wbytes(d, b + 12, fl), b += 16; // skip extra field, comment\n if (ce != null)\n wbytes(d, b += 10, ce), b += 4;\n d.set(fn, b);\n b += fl;\n if (ce == null)\n d.set(cmp, b);\n};\n// write zip footer (end of central directory)\nvar wzf = function (o, b, c, d, e) {\n wbytes(o, b, 0x6054B50); // skip disk\n wbytes(o, b + 8, c);\n wbytes(o, b + 10, c);\n wbytes(o, b + 12, d);\n wbytes(o, b + 16, e);\n};\nexport function zip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n var r = {};\n fltn(data, '', r, opts);\n var k = Object.keys(r);\n var lft = k.length, o = 0, tot = 0;\n var slft = lft, files = new Array(lft);\n var term = [];\n var tAll = function () {\n for (var i = 0; i < term.length; ++i)\n term[i]();\n };\n var cbf = function () {\n var out = new u8(tot + 22), oe = o, cdl = tot - o;\n tot = 0;\n for (var i = 0; i < slft; ++i) {\n var f = files[i];\n try {\n wzh(out, tot, f.c, f.d, f.m, f.n, f.u, f.p, null, f.t);\n wzh(out, o, f.c, f.d, f.m, f.n, f.u, f.p, tot, f.t), o += 46 + f.n.length, tot += 30 + f.n.length + f.d.length;\n }\n catch (e) {\n return cb(e, null);\n }\n }\n wzf(out, o, files.length, cdl, oe);\n cb(null, out);\n };\n if (!lft)\n cbf();\n var _loop_1 = function (i) {\n var fn = k[i];\n var _a = r[fn], file = _a[0], p = _a[1];\n var c = crc(), m = file.length;\n c.p(file);\n var n = strToU8(fn), s = n.length;\n var t = p.level == 0 ? 0 : 8;\n var cbl = function (e, d) {\n if (e) {\n tAll();\n cb(e, null);\n }\n else {\n var l = d.length;\n files[i] = {\n t: t,\n d: d,\n m: m,\n c: c.d(),\n u: fn.length != l,\n n: n,\n p: p\n };\n o += 30 + s + l;\n tot += 76 + 2 * s + l;\n if (!--lft)\n cbf();\n }\n };\n if (n.length > 65535)\n cbl('filename too long', null);\n if (!t)\n cbl(null, file);\n else if (m < 160000) {\n try {\n cbl(null, deflateSync(file, p));\n }\n catch (e) {\n cbl(e, null);\n }\n }\n else\n term.push(deflate(file, p, cbl));\n };\n // Cannot use lft because it can decrease\n for (var i = 0; i < slft; ++i) {\n _loop_1(i);\n }\n return tAll;\n}\n/**\n * Synchronously creates a ZIP file. Prefer using `zip` for better performance\n * with more than one file.\n * @param data The directory structure for the ZIP archive\n * @param opts The main options, merged with per-file options\n * @returns The generated ZIP archive\n */\nexport function zipSync(data, opts) {\n if (opts === void 0) { opts = {}; }\n var r = {};\n var files = [];\n fltn(data, '', r, opts);\n var o = 0;\n var tot = 0;\n for (var fn in r) {\n var _a = r[fn], file = _a[0], p = _a[1];\n var t = p.level == 0 ? 0 : 8;\n var n = strToU8(fn), s = n.length;\n if (n.length > 65535)\n throw 'filename too long';\n var d = t ? deflateSync(file, p) : file, l = d.length;\n var c = crc();\n c.p(file);\n files.push({\n t: t,\n d: d,\n m: file.length,\n c: c.d(),\n u: fn.length != s,\n n: n,\n o: o,\n p: p\n });\n o += 30 + s + l;\n tot += 76 + 2 * s + l;\n }\n var out = new u8(tot + 22), oe = o, cdl = tot - o;\n for (var i = 0; i < files.length; ++i) {\n var f = files[i];\n wzh(out, f.o, f.c, f.d, f.m, f.n, f.u, f.p, null, f.t);\n wzh(out, o, f.c, f.d, f.m, f.n, f.u, f.p, f.o, f.t), o += 46 + f.n.length;\n }\n wzf(out, o, files.length, cdl, oe);\n return out;\n}\n/**\n * Asynchronously decompresses a ZIP archive\n * @param data The raw compressed ZIP file\n * @param cb The callback to call with the decompressed files\n * @returns A function that can be used to immediately terminate the unzipping\n */\nexport function unzip(data, cb) {\n if (typeof cb != 'function')\n throw 'no callback';\n var term = [];\n var tAll = function () {\n for (var i = 0; i < term.length; ++i)\n term[i]();\n };\n var files = {};\n var e = data.length - 22;\n for (; b4(data, e) != 0x6054B50; --e) {\n if (!e || data.length - e > 65558) {\n cb('invalid zip file', null);\n return;\n }\n }\n ;\n var lft = b2(data, e + 8);\n if (!lft)\n cb(null, {});\n var c = lft;\n var o = b4(data, e + 16);\n var z = o == 4294967295;\n if (z) {\n e = b4(data, e - 12);\n if (b4(data, e) != 0x6064B50)\n throw 'invalid zip file';\n c = lft = b4(data, e + 32);\n o = b4(data, e + 48);\n }\n var _loop_2 = function (i) {\n var _a = zh(data, o, z), c_1 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);\n o = no;\n var cbl = function (e, d) {\n if (e) {\n tAll();\n cb(e, null);\n }\n else {\n files[fn] = d;\n if (!--lft)\n cb(null, files);\n }\n };\n if (!c_1)\n cbl(null, slc(data, b, b + sc));\n else if (c_1 == 8) {\n var infl = data.subarray(b, b + sc);\n if (sc < 320000) {\n try {\n cbl(null, inflateSync(infl, new u8(su)));\n }\n catch (e) {\n cbl(e, null);\n }\n }\n else\n term.push(inflate(infl, { size: su }, cbl));\n }\n else\n cbl('unknown compression type ' + c_1, null);\n };\n for (var i = 0; i < c; ++i) {\n _loop_2(i);\n }\n return tAll;\n}\n/**\n * Synchronously decompresses a ZIP archive. Prefer using `unzip` for better\n * performance with more than one file.\n * @param data The raw compressed ZIP file\n * @returns The decompressed files\n */\nexport function unzipSync(data) {\n var files = {};\n var e = data.length - 22;\n for (; b4(data, e) != 0x6054B50; --e) {\n if (!e || data.length - e > 65558)\n throw 'invalid zip file';\n }\n ;\n var c = b2(data, e + 8);\n if (!c)\n return {};\n var o = b4(data, e + 16);\n var z = o == 4294967295;\n if (z) {\n e = b4(data, e - 12);\n if (b4(data, e) != 0x6064B50)\n throw 'invalid zip file';\n c = b4(data, e + 32);\n o = b4(data, e + 48);\n }\n for (var i = 0; i < c; ++i) {\n var _a = zh(data, o, z), c_2 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);\n o = no;\n if (!c_2)\n files[fn] = slc(data, b, b + sc);\n else if (c_2 == 8)\n files[fn] = inflateSync(data.subarray(b, b + sc), new u8(su));\n else\n throw 'unknown compression type ' + c_2;\n }\n return files;\n}\n","import type { V2ReplayPayloadAdmissionCode } from './types'\n\n/**\n * A replay payload that cannot be admitted. Every code is a permanent local rejection: the bytes are\n * never persisted, retried, or acknowledged.\n */\nexport class V2ReplayPayloadAdmissionError extends Error {\n readonly code: V2ReplayPayloadAdmissionCode\n\n constructor(code: V2ReplayPayloadAdmissionCode, message: string) {\n super(message)\n this.name = 'V2ReplayPayloadAdmissionError'\n this.code = code\n }\n}\n\nexport class V2MaskingProfileError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'V2MaskingProfileError'\n }\n}\n\nexport class V2MaskingProofError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'V2MaskingProofError'\n }\n}\n\nexport class V2ReplayRequestError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'V2ReplayRequestError'\n }\n}\n\nexport class V2ReplayAcknowledgementError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'V2ReplayAcknowledgementError'\n }\n}\n","import { V2ReplayPayloadAdmissionError } from './errors'\n\nconst ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\nconst CANONICAL_PATTERN = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/\nconst decodeTable = new Int16Array(128).fill(-1)\nfor (let index = 0; index < ALPHABET.length; index += 1) {\n decodeTable[ALPHABET.charCodeAt(index)] = index\n}\n\n/** RFC 4648 standard alphabet with required padding. */\nexport const encodeBase64 = (bytes: Uint8Array): string => {\n let output = ''\n let index = 0\n for (; index + 2 < bytes.length; index += 3) {\n const triple = (bytes[index] << 16) | (bytes[index + 1] << 8) | bytes[index + 2]\n output +=\n ALPHABET[triple >> 18] +\n ALPHABET[(triple >> 12) & 63] +\n ALPHABET[(triple >> 6) & 63] +\n ALPHABET[triple & 63]\n }\n const remaining = bytes.length - index\n if (remaining === 1) {\n const value = bytes[index] << 16\n output += ALPHABET[value >> 18] + ALPHABET[(value >> 12) & 63] + '=='\n } else if (remaining === 2) {\n const value = (bytes[index] << 16) | (bytes[index + 1] << 8)\n output += ALPHABET[value >> 18] + ALPHABET[(value >> 12) & 63] + ALPHABET[(value >> 6) & 63] + '='\n }\n return output\n}\n\n/**\n * Strict decoder: only the standard alphabet, padding exactly where required, and a decoded value whose\n * canonical re-encoding reproduces the input byte for byte.\n */\nexport const decodeCanonicalBase64 = (value: string): Uint8Array => {\n if (typeof value !== 'string' || value.length === 0 || /[^A-Za-z0-9+/=]/.test(value)) {\n throw new V2ReplayPayloadAdmissionError('invalid-base64', 'payload is not standard base64')\n }\n if (!CANONICAL_PATTERN.test(value)) {\n throw new V2ReplayPayloadAdmissionError('noncanonical-base64', 'payload base64 padding is not canonical')\n }\n const padding = value.endsWith('==') ? 2 : value.endsWith('=') ? 1 : 0\n const output = new Uint8Array((value.length / 4) * 3 - padding)\n let offset = 0\n for (let index = 0; index < value.length; index += 4) {\n const first = decodeTable[value.charCodeAt(index)]\n const second = decodeTable[value.charCodeAt(index + 1)]\n const third = decodeTable[value.charCodeAt(index + 2)]\n const fourth = decodeTable[value.charCodeAt(index + 3)]\n const quad = (first << 18) | (second << 12) | ((third < 0 ? 0 : third) << 6) | (fourth < 0 ? 0 : fourth)\n output[offset++] = quad >> 16\n if (third >= 0) output[offset++] = (quad >> 8) & 0xff\n if (fourth >= 0) output[offset++] = quad & 0xff\n }\n if (encodeBase64(output) !== value) {\n throw new V2ReplayPayloadAdmissionError('noncanonical-base64', 'payload base64 has non-zero padding bits')\n }\n return output\n}\n","import { parseV1StrictCanonicalJson, V1StrictJsonError } from '../strict-canonical-json'\nimport {\n freezeV1Value,\n hasOwnIndex,\n isArrayValue,\n isFunctionValue,\n isNullValue,\n unicodeCodePointLength,\n utf8ByteLength,\n} from '../validation'\nimport { utf8Encode } from './digest'\nimport { V2ReplayPayloadAdmissionError } from './errors'\nimport type { V2ReplayPayloadLimits } from './types'\n\n/** Engine-side ceilings for one decoded replay payload. */\nexport const V2_REPLAY_MAX_DECODED_BYTES = 16_777_216\nexport const V2_REPLAY_MAX_LOGICAL_RECORDS = 10_000\nexport const V2_REPLAY_MAX_JSON_NESTING_DEPTH = 256\n\nexport const V2_REPLAY_PAYLOAD_LIMITS: V2ReplayPayloadLimits = freezeV1Value({\n maximumReplayDecodedBytes: V2_REPLAY_MAX_DECODED_BYTES,\n maximumReplayLogicalRecords: V2_REPLAY_MAX_LOGICAL_RECORDS,\n maximumJsonNestingDepth: V2_REPLAY_MAX_JSON_NESTING_DEPTH,\n})\n\nexport interface V2CanonicalReplayRecords {\n readonly canonical: string\n readonly bytes: Uint8Array\n readonly logicalRecords: number\n readonly nestingDepth: number\n}\n\nexport interface V2AdmittedReplayRecords {\n readonly records: readonly unknown[]\n readonly logicalRecords: number\n readonly nestingDepth: number\n}\n\nfunction fail(code: V2ReplayPayloadAdmissionError['code'], message: string): never {\n throw new V2ReplayPayloadAdmissionError(code, message)\n}\n\nconst isPlainObject = (value: object): value is Record<string, unknown> => {\n const prototype = Object.getPrototypeOf(value)\n return isNullValue(prototype) || prototype === Object.prototype\n}\n\nconst assertUnicode = (value: string, path: string): void => {\n if (isNullValue(unicodeCodePointLength(value))) {\n fail('invalid-unicode', `${path} contains an unpaired Unicode surrogate`)\n }\n}\n\n/** Serialization semantics for values JSON cannot carry: omitted as object members, null as array elements. */\nconst isOmittedValue = (value: unknown): boolean =>\n typeof value === 'undefined' || isFunctionValue(value) || typeof value === 'symbol'\n\ninterface CanonicalWalk {\n depth: number\n}\n\nconst walk = (\n value: unknown,\n depth: number,\n path: string,\n maximumNestingDepth: number,\n state: CanonicalWalk\n): string => {\n if (isNullValue(value)) return 'null'\n switch (typeof value) {\n case 'string':\n assertUnicode(value, path)\n return JSON.stringify(value)\n case 'number':\n if (!Number.isFinite(value)) fail('invalid-codec-json', `${path} must be a finite number`)\n // ECMAScript number formatting is the binary64 fallback of elu-canonical-json-v1; -0 prints as 0.\n return JSON.stringify(value)\n case 'boolean':\n return value ? 'true' : 'false'\n case 'object':\n break\n default:\n return fail('invalid-codec-json', `${path} has an unsupported ${typeof value} value`)\n }\n\n const containerDepth = depth + 1\n if (containerDepth > maximumNestingDepth) {\n fail('json-nesting-limit', `${path} exceeds the maximum JSON nesting depth of ${maximumNestingDepth}`)\n }\n if (containerDepth > state.depth) state.depth = containerDepth\n\n if (isArrayValue(value)) {\n const parts: string[] = []\n for (let index = 0; index < value.length; index += 1) {\n if (!hasOwnIndex(value, index)) fail('invalid-codec-json', `${path}[${index}] is a sparse array hole`)\n const element = value[index]\n parts.push(\n isOmittedValue(element)\n ? 'null'\n : walk(element, containerDepth, `${path}[${index}]`, maximumNestingDepth, state)\n )\n }\n return `[${parts.join(',')}]`\n }\n if (!isPlainObject(value as object)) {\n return fail('invalid-codec-json', `${path} must be a plain JSON object or array`)\n }\n const record = value as Record<string, unknown>\n const keys = Object.keys(record).sort()\n const members: string[] = []\n for (const key of keys) {\n const member = record[key]\n if (isOmittedValue(member)) continue\n assertUnicode(key, `${path}.${key}`)\n members.push(\n `${JSON.stringify(key)}:${walk(member, containerDepth, `${path}.${key}`, maximumNestingDepth, state)}`\n )\n }\n return `{${members.join(',')}}`\n}\n\n/**\n * Canonicalizes an in-memory JSON value: members sorted by UTF-16 code unit order, no whitespace,\n * ECMAScript number formatting, and a container nesting ceiling counted from the root container.\n */\nexport const canonicalV2JsonValue = (\n value: unknown,\n field: string,\n maximumNestingDepth: number = V2_REPLAY_MAX_JSON_NESTING_DEPTH\n): string => {\n if (isOmittedValue(value)) fail('invalid-codec-json', `${field} must be a JSON value`)\n return walk(value, 0, field, maximumNestingDepth, { depth: 0 })\n}\n\n/** Canonical UTF-8 bytes of an ordered array of masked replay records, bounded by the payload limits. */\nexport const canonicalV2ReplayRecords = (\n records: unknown,\n field: string,\n limits: V2ReplayPayloadLimits = V2_REPLAY_PAYLOAD_LIMITS\n): V2CanonicalReplayRecords => {\n if (!isArrayValue(records)) fail('invalid-codec-shape', `${field} must be an array of replay records`)\n if (records.length > limits.maximumReplayLogicalRecords) {\n fail(\n 'logical-record-limit',\n `${field} carries ${records.length} records; the maximum is ${limits.maximumReplayLogicalRecords}`\n )\n }\n const state: CanonicalWalk = { depth: 0 }\n const canonical = walk(records, 0, field, limits.maximumJsonNestingDepth, state)\n const byteLength = utf8ByteLength(canonical)\n if (byteLength > limits.maximumReplayDecodedBytes) {\n fail(\n 'decoded-byte-limit',\n `${field} serializes to ${byteLength} bytes; the maximum is ${limits.maximumReplayDecodedBytes}`\n )\n }\n return freezeV1Value({\n canonical,\n bytes: utf8Encode(canonical),\n logicalRecords: records.length,\n nestingDepth: state.depth,\n })\n}\n\nconst measureNestingDepth = (value: unknown, depth: number): number => {\n if (isNullValue(value) || typeof value !== 'object') return depth\n let deepest = depth + 1\n const children = isArrayValue(value)\n ? value\n : Object.keys(value).map((key) => (value as Record<string, unknown>)[key])\n for (const child of children) {\n const measured = measureNestingDepth(child, depth + 1)\n if (measured > deepest) deepest = measured\n }\n return deepest\n}\n\n/**\n * Admits decoded payload text the way the engine does: strict JSON, no duplicate keys, canonical\n * bytes only, a top-level array, and the nesting and record ceilings.\n */\nexport const admitV2CanonicalPayloadText = (\n text: string,\n limits: V2ReplayPayloadLimits = V2_REPLAY_PAYLOAD_LIMITS\n): V2AdmittedReplayRecords => {\n let parsed\n try {\n parsed = parseV1StrictCanonicalJson(\n text,\n limits.maximumReplayDecodedBytes,\n 'replay payload',\n limits.maximumJsonNestingDepth\n )\n } catch (error) {\n const reason = error instanceof V1StrictJsonError ? error.reason : 'syntax'\n const message = error instanceof Error ? error.message : 'replay payload is not strict JSON'\n if (reason === 'nesting-depth') fail('json-nesting-limit', message)\n if (reason === 'unicode') fail('invalid-unicode', message)\n if (reason === 'size') fail('decoded-byte-limit', message)\n return fail('invalid-codec-json', message)\n }\n if (!isArrayValue(parsed.value)) fail('invalid-codec-shape', 'replay payload must be a JSON array')\n if (parsed.canonical !== text) fail('noncanonical-codec-json', 'replay payload is not canonical JSON')\n const records = parsed.value as readonly unknown[]\n if (records.length > limits.maximumReplayLogicalRecords) {\n fail(\n 'logical-record-limit',\n `replay payload carries ${records.length} records; the maximum is ${limits.maximumReplayLogicalRecords}`\n )\n }\n return freezeV1Value({\n records,\n logicalRecords: records.length,\n nestingDepth: measureNestingDepth(records, 0),\n })\n}\n","import {\n freezeV1Value,\n hasOnlyKeys,\n hasOwn,\n hasOwnIndex,\n isArrayValue,\n isBooleanValue,\n isNullValue,\n isRecordValue,\n isStringValue,\n unicodeCodePointLength,\n} from '../validation'\nimport { canonicalV2JsonValue } from './canonical-payload-json'\nimport { sha256Hex, utf8Encode } from './digest'\nimport { V2MaskingProfileError } from './errors'\nimport type {\n V2MaskingImageRule,\n V2MaskingInputRule,\n V2MaskingInvalidationAction,\n V2MaskingProfile,\n V2MaskingRequirementComparison,\n V2MaskingTextRule,\n V2ReplaySha256,\n V2Sha256Hash,\n} from './types'\n\nexport const V2_MASKING_PROFILE_SCHEMA_VERSION = 1\nexport const V2_BROWSER_MASKING_DIALECT = 'elu-css-selector-v1'\nexport const V2_MASKING_MAX_SELECTORS = 128\nexport const V2_MASKING_MAX_SELECTOR_LENGTH = 512\n\nconst PROFILE_KEYS = [\n 'schemaVersion',\n 'targetDialect',\n 'textRule',\n 'inputRule',\n 'imageRule',\n 'secureInputsMasked',\n 'platformFallbackApplied',\n 'resolvedMaskSelectors',\n 'resolvedBlockSelectors',\n] as const\nconst POLICY_MASKING_KEYS = ['text', 'inputs', 'images', 'secureInputsMasked', 'platformRules'] as const\nconst PLATFORM_RULE_KEYS = ['platform', 'action', 'targetDialect', 'target'] as const\nconst TEXT_RULES: readonly V2MaskingTextRule[] = ['all', 'sensitive']\nconst INPUT_RULES: readonly V2MaskingInputRule[] = ['all', 'sensitive']\nconst IMAGE_RULES: readonly V2MaskingImageRule[] = ['block', 'allow']\n\nfunction fail(message: string): never {\n throw new V2MaskingProfileError(message)\n}\n\nconst assertRule = <T extends string>(value: unknown, allowed: readonly T[], field: string): T => {\n if (!isStringValue(value) || !allowed.includes(value as T)) {\n fail(`${field} must be one of ${allowed.join(', ')}`)\n }\n return value as T\n}\n\nconst assertSelector = (value: unknown, field: string): string => {\n const length = isStringValue(value) ? unicodeCodePointLength(value) : null\n if (isNullValue(length) || length < 1 || length > V2_MASKING_MAX_SELECTOR_LENGTH) {\n return fail(`${field} must be a selector of 1 to ${V2_MASKING_MAX_SELECTOR_LENGTH} Unicode scalars`)\n }\n return value as string\n}\n\n/** Sorted by UTF-16 code unit order with duplicates removed, so equal rule sets canonicalize identically. */\nconst normalizeSelectors = (values: unknown, field: string): readonly string[] => {\n if (!isArrayValue(values)) fail(`${field} must be an array of selectors`)\n const selectors = new Set<string>()\n for (let index = 0; index < values.length; index += 1) {\n if (!hasOwnIndex(values, index)) fail(`${field} must not be sparse`)\n selectors.add(assertSelector(values[index], `${field}[${index}]`))\n }\n if (selectors.size > V2_MASKING_MAX_SELECTORS) {\n fail(`${field} may carry at most ${V2_MASKING_MAX_SELECTORS} selectors`)\n }\n return freezeV1Value([...selectors].sort())\n}\n\n/** Validates a closed masking-profile object and returns its frozen normalized form. */\nexport const buildV2MaskingProfile = (input: unknown): V2MaskingProfile => {\n if (!isRecordValue(input) || !hasOnlyKeys(input, PROFILE_KEYS)) {\n fail('masking profile must contain only schema-defined fields')\n }\n for (const key of PROFILE_KEYS) {\n if (!hasOwn(input, key)) fail(`masking profile ${key} is required`)\n }\n if (input.schemaVersion !== V2_MASKING_PROFILE_SCHEMA_VERSION) {\n fail(`masking profile schemaVersion must be ${V2_MASKING_PROFILE_SCHEMA_VERSION}`)\n }\n if (input.targetDialect !== V2_BROWSER_MASKING_DIALECT) {\n fail(`masking profile targetDialect must be ${V2_BROWSER_MASKING_DIALECT}`)\n }\n if (input.secureInputsMasked !== true) fail('masking profile secureInputsMasked must be true')\n if (!isBooleanValue(input.platformFallbackApplied)) {\n fail('masking profile platformFallbackApplied must be a boolean')\n }\n return freezeV1Value({\n schemaVersion: V2_MASKING_PROFILE_SCHEMA_VERSION,\n targetDialect: V2_BROWSER_MASKING_DIALECT,\n textRule: assertRule(input.textRule, TEXT_RULES, 'masking profile textRule'),\n inputRule: assertRule(input.inputRule, INPUT_RULES, 'masking profile inputRule'),\n imageRule: assertRule(input.imageRule, IMAGE_RULES, 'masking profile imageRule'),\n secureInputsMasked: true,\n platformFallbackApplied: input.platformFallbackApplied,\n resolvedMaskSelectors: normalizeSelectors(input.resolvedMaskSelectors, 'masking profile resolvedMaskSelectors'),\n resolvedBlockSelectors: normalizeSelectors(\n input.resolvedBlockSelectors,\n 'masking profile resolvedBlockSelectors'\n ),\n })\n}\n\nexport interface V2MaskingProfileResolution {\n /** True when the recorder had to fall back to a coarser platform masking mode. */\n readonly platformFallbackApplied?: boolean\n}\n\n/**\n * Projects the server privacy policy's masking block onto the browser: the text, input, and image\n * rules plus every browser rule in the recognized selector dialect. Rules for other platforms are not\n * the browser's to apply; a browser rule in an unrecognized dialect cannot be honored and fails closed.\n */\nexport const resolveV2MaskingProfile = (\n masking: unknown,\n resolution: V2MaskingProfileResolution = {}\n): V2MaskingProfile => {\n if (!isRecordValue(masking) || !hasOnlyKeys(masking, POLICY_MASKING_KEYS)) {\n fail('policy masking must contain only schema-defined fields')\n }\n const maskSelectors: string[] = []\n const blockSelectors: string[] = []\n if (hasOwn(masking, 'platformRules')) {\n if (!isArrayValue(masking.platformRules)) fail('policy masking platformRules must be an array')\n masking.platformRules.forEach((rule, index) => {\n const field = `policy masking platformRules[${index}]`\n if (!isRecordValue(rule) || !hasOnlyKeys(rule, PLATFORM_RULE_KEYS)) {\n fail(`${field} must contain only schema-defined fields`)\n }\n if (rule.platform !== 'browser' && rule.platform !== 'android' && rule.platform !== 'ios') {\n fail(`${field} platform is unsupported`)\n }\n if (rule.action !== 'mask' && rule.action !== 'block') fail(`${field} action is unsupported`)\n if (!isStringValue(rule.targetDialect)) fail(`${field} targetDialect must be a string`)\n const target = assertSelector(rule.target, `${field} target`)\n if (rule.platform !== 'browser') return\n if (rule.targetDialect !== V2_BROWSER_MASKING_DIALECT) {\n fail(`${field} uses the unrecognized browser masking dialect ${rule.targetDialect}`)\n }\n ;(rule.action === 'mask' ? maskSelectors : blockSelectors).push(target)\n })\n }\n return buildV2MaskingProfile({\n schemaVersion: V2_MASKING_PROFILE_SCHEMA_VERSION,\n targetDialect: V2_BROWSER_MASKING_DIALECT,\n textRule: masking.text,\n inputRule: masking.inputs,\n imageRule: masking.images,\n secureInputsMasked: masking.secureInputsMasked,\n platformFallbackApplied: resolution.platformFallbackApplied === true,\n resolvedMaskSelectors: maskSelectors,\n resolvedBlockSelectors: blockSelectors,\n })\n}\n\n/** Canonical JSON of the closed profile: sorted members, no whitespace. */\nexport const canonicalV2MaskingProfile = (profile: V2MaskingProfile): string =>\n canonicalV2JsonValue(buildV2MaskingProfile(profile), 'masking profile')\n\nexport const canonicalV2MaskingProfileBytes = (profile: V2MaskingProfile): Uint8Array =>\n utf8Encode(canonicalV2MaskingProfile(profile))\n\n/** The profile hash bound into every replay chunk and its request identity. */\nexport const hashV2MaskingProfile = async (profile: V2MaskingProfile, sha256?: V2ReplaySha256): Promise<V2Sha256Hash> =>\n `sha256:${await sha256Hex(canonicalV2MaskingProfileBytes(profile), sha256)}`\n\nconst RULE_STRENGTH = {\n text: { sensitive: 1, all: 2 },\n input: { sensitive: 1, all: 2 },\n image: { allow: 1, block: 2 },\n} as const\n\ntype DimensionOrder = -1 | 0 | 1\n\nconst orderStrength = (applied: number, required: number): DimensionOrder =>\n applied === required ? 0 : applied > required ? 1 : -1\n\n/** 1 when the applied set covers every required selector, -1 when it is covered, 0 when equal, null otherwise. */\nconst orderSelectors = (applied: readonly string[], required: readonly string[]): DimensionOrder | null => {\n const appliedSet = new Set(applied)\n const requiredSet = new Set(required)\n const appliedCoversRequired = required.every((selector) => appliedSet.has(selector))\n const requiredCoversApplied = applied.every((selector) => requiredSet.has(selector))\n if (appliedCoversRequired && requiredCoversApplied) return 0\n if (appliedCoversRequired) return 1\n if (requiredCoversApplied) return -1\n return null\n}\n\n/**\n * Relates a newly required profile to the profile a sealed chunk was masked with. Serialized bytes\n * cannot be remasked, so a requirement that is stricter on any dimension, or not comparable with the\n * applied rules (a different dialect, fallback mode, or selector sets that neither contain the other),\n * makes the sealed bytes unlawful; an equal or looser requirement leaves them lawful.\n */\nexport const compareV2MaskingRequirement = (\n applied: V2MaskingProfile,\n required: V2MaskingProfile\n): V2MaskingRequirementComparison => {\n const appliedProfile = buildV2MaskingProfile(applied)\n const requiredProfile = buildV2MaskingProfile(required)\n if (\n appliedProfile.schemaVersion !== requiredProfile.schemaVersion ||\n appliedProfile.targetDialect !== requiredProfile.targetDialect ||\n appliedProfile.platformFallbackApplied !== requiredProfile.platformFallbackApplied\n ) {\n return 'incomparable'\n }\n const orders = [\n orderStrength(RULE_STRENGTH.text[appliedProfile.textRule], RULE_STRENGTH.text[requiredProfile.textRule]),\n orderStrength(RULE_STRENGTH.input[appliedProfile.inputRule], RULE_STRENGTH.input[requiredProfile.inputRule]),\n orderStrength(RULE_STRENGTH.image[appliedProfile.imageRule], RULE_STRENGTH.image[requiredProfile.imageRule]),\n orderSelectors(appliedProfile.resolvedMaskSelectors, requiredProfile.resolvedMaskSelectors),\n orderSelectors(appliedProfile.resolvedBlockSelectors, requiredProfile.resolvedBlockSelectors),\n ]\n if (orders.some((order) => isNullValue(order))) return 'incomparable'\n const appliedIsStricterSomewhere = orders.some((order) => order === 1)\n const requiredIsStricterSomewhere = orders.some((order) => order === -1)\n if (appliedIsStricterSomewhere && requiredIsStricterSomewhere) return 'incomparable'\n if (requiredIsStricterSomewhere) return 'stricter'\n if (appliedIsStricterSomewhere) return 'looser'\n return 'equal'\n}\n\nexport const v2MaskingInvalidationAction = (comparison: V2MaskingRequirementComparison): V2MaskingInvalidationAction =>\n comparison === 'equal' || comparison === 'looser' ? 'preserve' : 'purge'\n","import { isArrayValue, isFunctionValue } from '../validation'\nimport { canonicalV2ReplayRecords, V2_REPLAY_PAYLOAD_LIMITS } from './canonical-payload-json'\nimport { isV2Sha256Hash } from './digest'\nimport { V2MaskingProofError } from './errors'\nimport { buildV2MaskingProfile } from './masking-profile'\nimport type { V2MaskingProfile, V2ReplayPayloadLimits, V2Sha256Hash } from './types'\n\n/** Applies the effective masking profile to one raw recorder event and returns the masked event. */\nexport type V2ReplayEventMasker = (event: unknown, profile: V2MaskingProfile) => unknown\n\nexport interface V2MaskingStageInput {\n readonly profile: V2MaskingProfile\n readonly profileHash: V2Sha256Hash\n readonly events: readonly unknown[]\n readonly limits?: V2ReplayPayloadLimits\n}\n\nconst SEAL = Symbol('elu-replay-masking-seal')\n\n/**\n * The sealed output of the masking stage and the only value the payload codec serializes. Masking\n * runs inside the stage, the masked records are canonicalized here, and the resulting text is what\n * gets compressed; nothing that bypassed the stage can reach compression, persistence, or transport.\n */\nexport class V2MaskingProof {\n readonly profile: V2MaskingProfile\n readonly profileHash: V2Sha256Hash\n readonly canonicalPayload: string\n readonly payloadByteLength: number\n readonly logicalRecords: number\n readonly nestingDepth: number\n\n private constructor(\n seal: symbol,\n profile: V2MaskingProfile,\n profileHash: V2Sha256Hash,\n canonicalPayload: string,\n payloadByteLength: number,\n logicalRecords: number,\n nestingDepth: number\n ) {\n if (seal !== SEAL) throw new V2MaskingProofError('masking proofs are sealed by the masking stage only')\n this.profile = profile\n this.profileHash = profileHash\n this.canonicalPayload = canonicalPayload\n this.payloadByteLength = payloadByteLength\n this.logicalRecords = logicalRecords\n this.nestingDepth = nestingDepth\n Object.freeze(this)\n }\n\n /** Runs the masker over every raw event in order and seals the masked, canonicalized result. */\n static seal(masker: V2ReplayEventMasker, input: V2MaskingStageInput): V2MaskingProof {\n if (!isFunctionValue(masker)) throw new V2MaskingProofError('masking stage requires a masker')\n if (!isArrayValue(input.events)) throw new V2MaskingProofError('masking stage requires an event array')\n const profile = buildV2MaskingProfile(input.profile)\n if (!isV2Sha256Hash(input.profileHash)) {\n throw new V2MaskingProofError('masking stage requires the sha256 profile hash')\n }\n const masked = input.events.map((event) => masker(event, profile))\n const canonical = canonicalV2ReplayRecords(\n masked,\n 'masked replay records',\n input.limits ?? V2_REPLAY_PAYLOAD_LIMITS\n )\n return new V2MaskingProof(\n SEAL,\n profile,\n input.profileHash,\n canonical.canonical,\n canonical.bytes.length,\n canonical.logicalRecords,\n canonical.nestingDepth\n )\n }\n}\n\nexport const isV2MaskingProof = (value: unknown): value is V2MaskingProof => value instanceof V2MaskingProof\n","import { gzipSync, Inflate } from 'fflate'\nimport { decodeCanonicalBase64, encodeBase64 } from './base64'\nimport { admitV2CanonicalPayloadText, V2_REPLAY_PAYLOAD_LIMITS } from './canonical-payload-json'\nimport { utf8Encode } from './digest'\nimport { V2ReplayPayloadAdmissionError } from './errors'\nimport { isV2MaskingProof, V2MaskingProof } from './masking-proof'\nimport type { V2ReplayCodec, V2ReplayCompression, V2ReplayContentEncoding, V2ReplayPayloadLimits } from './types'\n\n/**\n * elu-browser-dom-v1: the canonical JSON array of masked DOM recording events, gzip-compressed exactly\n * once, carried as base64. The outer envelope describes the inner payload only; HTTP transfer stays\n * identity-encoded.\n */\nexport const V2_BROWSER_DOM_CODEC: V2ReplayCodec = 'elu-browser-dom-v1'\nexport const V2_BROWSER_DOM_COMPRESSION: V2ReplayCompression = 'gzip'\nexport const V2_REPLAY_CONTENT_ENCODING: V2ReplayContentEncoding = 'base64'\n\nconst GZIP_MAGIC_FIRST = 0x1f\nconst GZIP_MAGIC_SECOND = 0x8b\nconst GZIP_DEFLATE_METHOD = 8\nconst GZIP_FIXED_HEADER_BYTES = 10\nconst GZIP_TRAILER_BYTES = 8\nconst GZIP_MINIMUM_BYTES = GZIP_FIXED_HEADER_BYTES + GZIP_TRAILER_BYTES\nconst GZIP_FLAG_HEADER_CRC = 0x02\nconst GZIP_FLAG_EXTRA = 0x04\nconst GZIP_FLAG_NAME = 0x08\nconst GZIP_FLAG_COMMENT = 0x10\nconst GZIP_RESERVED_FLAGS = 0xe0\n\nexport interface V2EncodedReplayPayload {\n readonly codec: V2ReplayCodec\n readonly compression: V2ReplayCompression\n readonly contentEncoding: V2ReplayContentEncoding\n readonly payload: string\n readonly compressedByteLength: number\n readonly decodedByteLength: number\n readonly logicalRecords: number\n readonly proof: V2MaskingProof\n}\n\nexport interface V2AdmittedReplayPayload {\n readonly decoded: Uint8Array\n readonly decodedByteLength: number\n readonly logicalRecords: number\n readonly nestingDepth: number\n readonly records: readonly unknown[]\n}\n\nfunction fail(code: V2ReplayPayloadAdmissionError['code'], message: string): never {\n throw new V2ReplayPayloadAdmissionError(code, message)\n}\n\n/** Serializes a sealed masking proof: compress once, then base64. Nothing else is accepted. */\nexport const encodeV2BrowserDomPayload = (proof: V2MaskingProof): V2EncodedReplayPayload => {\n if (!isV2MaskingProof(proof)) {\n throw new V2ReplayPayloadAdmissionError(\n 'invalid-codec-shape',\n 'payload serialization requires a sealed masking proof'\n )\n }\n const decoded = utf8Encode(proof.canonicalPayload)\n let compressed: Uint8Array\n try {\n // A zero mtime keeps the member header free of capture-time metadata.\n compressed = gzipSync(decoded, { mtime: 0 })\n } catch {\n return fail('invalid-gzip', 'payload compression failed')\n }\n return Object.freeze({\n codec: V2_BROWSER_DOM_CODEC,\n compression: V2_BROWSER_DOM_COMPRESSION,\n contentEncoding: V2_REPLAY_CONTENT_ENCODING,\n payload: encodeBase64(compressed),\n compressedByteLength: compressed.length,\n decodedByteLength: decoded.length,\n logicalRecords: proof.logicalRecords,\n proof,\n })\n}\n\nconst CRC_TABLE = (() => {\n const table = new Int32Array(256)\n for (let index = 0; index < 256; index += 1) {\n let value = index\n for (let bit = 0; bit < 8; bit += 1) {\n value = value & 1 ? (value >>> 1) ^ 0xedb88320 : value >>> 1\n }\n table[index] = value\n }\n return table\n})()\n\nconst crc32 = (chunks: readonly Uint8Array[]): number => {\n let crc = -1\n for (const chunk of chunks) {\n for (let index = 0; index < chunk.length; index += 1) {\n crc = CRC_TABLE[(crc ^ chunk[index]) & 0xff] ^ (crc >>> 8)\n }\n }\n return (crc ^ -1) >>> 0\n}\n\nconst readUint32LittleEndian = (bytes: Uint8Array, offset: number): number =>\n (bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24)) >>> 0\n\nconst gzipHeaderLength = (payload: Uint8Array): number => {\n if (\n payload.length < GZIP_MINIMUM_BYTES ||\n payload[0] !== GZIP_MAGIC_FIRST ||\n payload[1] !== GZIP_MAGIC_SECOND ||\n payload[2] !== GZIP_DEFLATE_METHOD\n ) {\n fail('invalid-gzip', 'payload is not a gzip member')\n }\n const flags = payload[3]\n if ((flags & GZIP_RESERVED_FLAGS) !== 0) fail('invalid-gzip', 'gzip header uses reserved flags')\n let offset = GZIP_FIXED_HEADER_BYTES\n if ((flags & GZIP_FLAG_EXTRA) !== 0) {\n if (offset + 2 > payload.length) fail('invalid-gzip', 'gzip extra field is truncated')\n offset += 2 + (payload[offset] | (payload[offset + 1] << 8))\n if (offset > payload.length) fail('invalid-gzip', 'gzip extra field is truncated')\n }\n for (const flag of [GZIP_FLAG_NAME, GZIP_FLAG_COMMENT]) {\n if ((flags & flag) === 0) continue\n while (offset < payload.length && payload[offset] !== 0) offset += 1\n if (offset >= payload.length) fail('invalid-gzip', 'gzip header string is unterminated')\n offset += 1\n }\n if ((flags & GZIP_FLAG_HEADER_CRC) !== 0) offset += 2\n if (offset + GZIP_TRAILER_BYTES > payload.length) fail('invalid-gzip', 'gzip member is truncated')\n return offset\n}\n\n/** The inflater's post-completion state: unconsumed input bytes and the bit offset into the first of them. */\ninterface InflateProgress {\n readonly p: Uint8Array\n readonly s: { readonly p?: number }\n}\n\n/**\n * Input slice for the counting pass below. The inflater emits what each slice\n * decodes to before it accepts the next one, so a member that would expand past\n * the decoded-byte ceiling is refused after at most one slice's worth of\n * expansion instead of after the whole member has been materialized.\n */\nconst INFLATE_GUARD_SLICE_BYTES = 4096\n\n/**\n * Refuse a member whose decoded size would exceed the ceiling without holding\n * the decoded bytes. The pass only counts: every slice is pushed as non-final,\n * so a malformed member or trailing data is left for the strict pass to\n * classify, and only the decoded-byte ceiling is reported from here. The\n * inflater may defer a block whose header straddles a slice boundary, so the\n * count can trail the true size by a few hundred kilobytes; the strict pass\n * still applies the exact ceiling to what it decodes.\n */\nconst refuseOversizedMember = (body: Uint8Array, maximumDecodedBytes: number): void => {\n let counted = 0\n const guard = new Inflate((chunk) => {\n counted += chunk.length\n if (counted > maximumDecodedBytes) {\n fail('decoded-byte-limit', `decoded payload exceeds ${maximumDecodedBytes} bytes`)\n }\n })\n try {\n for (let offset = 0; offset < body.length; offset += INFLATE_GUARD_SLICE_BYTES) {\n guard.push(body.subarray(offset, Math.min(offset + INFLATE_GUARD_SLICE_BYTES, body.length)), false)\n }\n } catch (error) {\n if (error instanceof V2ReplayPayloadAdmissionError) throw error\n }\n}\n\nconst inflateSingleMember = (payload: Uint8Array, headerLength: number, maximumDecodedBytes: number): Uint8Array => {\n const body = payload.subarray(headerLength, payload.length - GZIP_TRAILER_BYTES)\n refuseOversizedMember(body, maximumDecodedBytes)\n const chunks: Uint8Array[] = []\n let produced = 0\n const inflater = new Inflate((chunk) => {\n produced += chunk.length\n if (produced > maximumDecodedBytes) {\n fail('decoded-byte-limit', `decoded payload exceeds ${maximumDecodedBytes} bytes`)\n }\n chunks.push(chunk)\n })\n try {\n inflater.push(body, true)\n } catch (error) {\n if (error instanceof V2ReplayPayloadAdmissionError) throw error\n return fail('invalid-gzip', 'gzip member does not inflate')\n }\n\n const progress = inflater as unknown as InflateProgress\n const remaining = progress.p\n const bitOffset = progress.s?.p\n if (!ArrayBuffer.isView(remaining) || typeof bitOffset !== 'number' || remaining.length > body.length) {\n return fail('invalid-gzip', 'gzip member end position is unavailable')\n }\n const consumed = body.length - remaining.length + ((bitOffset & 7) !== 0 ? 1 : 0)\n const trailerOffset = headerLength + consumed\n if (trailerOffset + GZIP_TRAILER_BYTES > payload.length) fail('invalid-gzip', 'gzip trailer is truncated')\n if (readUint32LittleEndian(payload, trailerOffset) !== crc32(chunks)) {\n fail('invalid-gzip', 'gzip CRC-32 does not match the decoded bytes')\n }\n if (readUint32LittleEndian(payload, trailerOffset + 4) !== produced % 0x1_0000_0000) {\n fail('invalid-gzip', 'gzip ISIZE does not match the decoded length')\n }\n if (trailerOffset + GZIP_TRAILER_BYTES !== payload.length) {\n fail('trailing-gzip-data', 'payload carries bytes after the single gzip member')\n }\n\n const decoded = new Uint8Array(produced)\n let offset = 0\n for (const chunk of chunks) {\n decoded.set(chunk, offset)\n offset += chunk.length\n }\n return decoded\n}\n\n/**\n * Admits one base64 payload exactly as the engine does, in this order: strict canonical base64, a\n * single gzip member with the decoded-byte ceiling, no nested compression, strict canonical JSON\n * within the nesting ceiling, and the logical-record ceiling. Any failure is permanent.\n */\nexport const admitV2BrowserDomPayload = (\n payload: string,\n limits: V2ReplayPayloadLimits = V2_REPLAY_PAYLOAD_LIMITS\n): V2AdmittedReplayPayload => {\n const compressed = decodeCanonicalBase64(payload)\n const decoded = inflateSingleMember(compressed, gzipHeaderLength(compressed), limits.maximumReplayDecodedBytes)\n if (decoded.length >= 2 && decoded[0] === GZIP_MAGIC_FIRST && decoded[1] === GZIP_MAGIC_SECOND) {\n fail('nested-compression', 'decoded payload is itself gzip-compressed')\n }\n let text: string\n try {\n text = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(decoded)\n } catch {\n return fail('invalid-unicode', 'decoded payload is not UTF-8')\n }\n const admitted = admitV2CanonicalPayloadText(text, limits)\n return Object.freeze({\n decoded,\n decodedByteLength: decoded.length,\n logicalRecords: admitted.logicalRecords,\n nestingDepth: admitted.nestingDepth,\n records: admitted.records,\n })\n}\n","import type { V2MaskingProfile } from './types'\n\n/**\n * The second redaction, applied to every recorded event before it is sealed.\n *\n * The recorder is already configured from the same profile, so in the ordinary case this finds\n * nothing left to remove. It exists because \"the recorder was configured correctly\" is an\n * assumption, and a sealed payload should not depend on one: this pass removes the categories the\n * profile forbids whatever the recorder did, so the proof describes bytes that were redacted here.\n *\n * It is a deny pass, not a verifier. It cannot tell masked text from text that happens to look\n * masked, and it does not try — it removes.\n */\n\n/** rrweb serialized node kinds. Numeric, because that is what travels on the wire. */\nconst NODE_TYPE_ELEMENT = 2\nconst NODE_TYPE_TEXT = 3\nconst NODE_TYPE_COMMENT = 5\n\n/** rrweb event kinds and mutation sources, likewise numeric on the wire. */\nconst EVENT_TYPE_FULL_SNAPSHOT = 2\nconst EVENT_TYPE_INCREMENTAL = 3\nconst INCREMENTAL_SOURCE_MUTATION = 0\nconst INCREMENTAL_SOURCE_INPUT = 5\n\n/** Input types whose value is sensitive even when the policy only masks sensitive inputs. */\nconst SENSITIVE_INPUT_TYPES = new Set(['password', 'email', 'tel', 'hidden'])\n\n/** Attributes that carry an image's content rather than its layout. */\nconst IMAGE_CONTENT_ATTRIBUTES = ['src', 'srcset']\n\nconst IMAGE_TAG_NAMES = new Set(['img', 'picture', 'source'])\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nconst maskAttributes = (\n tagName: string,\n attributes: Record<string, unknown>,\n profile: V2MaskingProfile\n): Record<string, unknown> => {\n const masked: Record<string, unknown> = { ...attributes }\n const inputType = typeof masked.type === 'string' ? masked.type.toLowerCase() : ''\n const maskValue =\n profile.inputRule === 'all' || SENSITIVE_INPUT_TYPES.has(inputType) || tagName === 'textarea'\n if (maskValue && 'value' in masked) {\n delete masked.value\n }\n if (profile.imageRule === 'block' && IMAGE_TAG_NAMES.has(tagName)) {\n for (const attribute of IMAGE_CONTENT_ATTRIBUTES) {\n if (attribute in masked) delete masked[attribute]\n }\n }\n return masked\n}\n\n/** Recursively redacts one serialized node and everything under it. */\nconst maskNode = (node: unknown, profile: V2MaskingProfile): unknown => {\n if (!isRecord(node)) return node\n const masked: Record<string, unknown> = { ...node }\n const type = masked.type\n\n if (type === NODE_TYPE_COMMENT) {\n // A comment is never replayed and can carry anything a template put there.\n masked.textContent = ''\n } else if (type === NODE_TYPE_TEXT && profile.textRule === 'all') {\n // Style text is not customer content and blanking it breaks the rendered replay.\n if (masked.isStyle !== true) masked.textContent = ''\n } else if (type === NODE_TYPE_ELEMENT) {\n const tagName = typeof masked.tagName === 'string' ? masked.tagName.toLowerCase() : ''\n if (isRecord(masked.attributes)) {\n masked.attributes = maskAttributes(tagName, masked.attributes, profile)\n }\n }\n\n if (Array.isArray(masked.childNodes)) {\n masked.childNodes = masked.childNodes.map((child) => maskNode(child, profile))\n }\n return masked\n}\n\nconst maskMutation = (data: Record<string, unknown>, profile: V2MaskingProfile): Record<string, unknown> => {\n const masked: Record<string, unknown> = { ...data }\n if (profile.textRule === 'all' && Array.isArray(masked.texts)) {\n masked.texts = masked.texts.map((text) => (isRecord(text) ? { ...text, value: '' } : text))\n }\n if (Array.isArray(masked.attributes)) {\n masked.attributes = masked.attributes.map((entry) => {\n if (!isRecord(entry) || !isRecord(entry.attributes)) return entry\n // A mutation names no tag, so the value rule is applied at its broadest here.\n return { ...entry, attributes: maskAttributes('', entry.attributes, profile) }\n })\n }\n if (Array.isArray(masked.adds)) {\n masked.adds = masked.adds.map((add) =>\n isRecord(add) ? { ...add, node: maskNode(add.node, profile) } : add\n )\n }\n return masked\n}\n\n/**\n * The masker the sealing stage runs over every event. Pure: it copies what it changes and returns\n * the input untouched when there is nothing of the forbidden kind in it.\n */\nexport const maskV2BrowserDomEvent = (event: unknown, profile: V2MaskingProfile): unknown => {\n if (!isRecord(event)) return event\n const data = event.data\n if (!isRecord(data)) return event\n\n if (event.type === EVENT_TYPE_FULL_SNAPSHOT) {\n return { ...event, data: { ...data, node: maskNode(data.node, profile) } }\n }\n if (event.type !== EVENT_TYPE_INCREMENTAL) {\n return event\n }\n if (data.source === INCREMENTAL_SOURCE_MUTATION) {\n return { ...event, data: maskMutation(data, profile) }\n }\n if (data.source === INCREMENTAL_SOURCE_INPUT) {\n // The typed value. Masked whenever inputs are, and always for a sensitive field.\n const masked: Record<string, unknown> = { ...data }\n if (profile.inputRule === 'all' && typeof masked.text === 'string') masked.text = ''\n return { ...event, data: masked }\n }\n return event\n}\n","import { V2_REPLAY_REQUEST_ID_PATTERN } from './storage-constants'\nimport { compareV1ExactTimestamps, parseV1ExactTimestamp } from '../exact-timestamp'\nimport {\n freezeV1Value,\n hasOnlyKeys,\n hasOwn,\n isBooleanValue,\n isNonNegativeInteger,\n isNullValue,\n isRecordValue,\n isStringValue,\n unicodeCodePointLength,\n} from '../validation'\nimport { canonicalV2JsonValue } from './canonical-payload-json'\nimport type { V2EncodedReplayPayload } from './codec'\nimport { isV2Sha256Hash, sha256Hex, utf8Encode } from './digest'\nimport { V2ReplayRequestError } from './errors'\nimport { isV2MaskingProof } from './masking-proof'\nimport type {\n V2ReplayChunk,\n V2ReplayIdentity,\n V2ReplayPrivacyProof,\n V2ReplayRequest,\n V2ReplaySha256,\n V2Sha256Hash,\n V2VersionContext,\n} from './types'\n\n/** Domain separator hashed ahead of the canonical chunk to derive a replay request identity. */\nexport const V2_REPLAY_REQUEST_ID_DOMAIN = 'elu-sdk-replay-request-v2'\nexport const V2_REPLAY_CONTRACT_VERSION = '2.0.0'\n/** Hard ceiling on the exact UTF-8 bytes of one complete replay request. */\nexport const V2_MAX_REPLAY_REQUEST_BYTES = 5_242_880\n\nexport { V2_REPLAY_REQUEST_ID_PATTERN } from './storage-constants'\nconst CODEC_PATTERN = /^elu-[a-z0-9][a-z0-9.-]{0,63}$/\nconst RUNTIME_NAME_PATTERN = /^elu-[a-z0-9-]+$/\nconst FACADE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9._-]+$/\nconst BASE64_PATTERN = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/\nconst CHUNK_KEYS = [\n 'schemaVersion',\n 'replayId',\n 'sessionId',\n 'chunkId',\n 'sequence',\n 'startedAt',\n 'endedAt',\n 'identity',\n 'contextRevision',\n 'codec',\n 'compression',\n 'contentEncoding',\n 'payload',\n 'privacy',\n 'versions',\n] as const\nconst IDENTITY_KEYS = ['anonymousId', 'userId', 'revision'] as const\nconst PRIVACY_KEYS = [\n 'policyRevision',\n 'effectivePolicyHash',\n 'maskingProfileHash',\n 'appliedBeforeSerialization',\n 'secureInputsMasked',\n 'platformFallbackApplied',\n] as const\nconst VERSIONS_KEYS = ['schemaVersion', 'contractVersion', 'platform', 'runtime', 'facade', 'build'] as const\nconst NAMED_VERSION_KEYS = ['name', 'version'] as const\nconst REQUEST_KEYS = ['schemaVersion', 'requestId', 'chunk'] as const\n\nfunction fail(message: string): never {\n throw new V2ReplayRequestError(message)\n}\n\nconst assertBoundedString = (value: unknown, field: string, maximum: number, minimum = 1): string => {\n const length = isStringValue(value) ? unicodeCodePointLength(value) : null\n if (isNullValue(length) || length < minimum || length > maximum) {\n return fail(`${field} must be a string of ${minimum} to ${maximum} Unicode scalars`)\n }\n return value as string\n}\n\nconst assertSafeCount = (value: unknown, field: string): number => {\n if (!isNonNegativeInteger(value)) fail(`${field} must be a safe non-negative integer`)\n return value as number\n}\n\nconst assertHash = (value: unknown, field: string): V2Sha256Hash => {\n if (!isV2Sha256Hash(value)) fail(`${field} must be a lowercase sha256 hash`)\n return value as V2Sha256Hash\n}\n\nconst assertKeys = (value: unknown, keys: readonly string[], field: string, optional: readonly string[] = []) => {\n if (!isRecordValue(value) || !hasOnlyKeys(value, keys)) fail(`${field} must contain only schema-defined fields`)\n for (const key of keys) {\n if (!optional.includes(key) && !hasOwn(value, key)) fail(`${field} ${key} is required`)\n }\n return value\n}\n\nconst validateIdentity = (value: unknown, field: string): V2ReplayIdentity => {\n const identity = assertKeys(value, IDENTITY_KEYS, field)\n return freezeV1Value({\n anonymousId: assertBoundedString(identity.anonymousId, `${field} anonymousId`, 256),\n userId: isNullValue(identity.userId) ? null : assertBoundedString(identity.userId, `${field} userId`, 512),\n revision: assertSafeCount(identity.revision, `${field} revision`),\n })\n}\n\nconst validatePrivacy = (value: unknown, field: string): V2ReplayPrivacyProof => {\n const privacy = assertKeys(value, PRIVACY_KEYS, field)\n if (privacy.appliedBeforeSerialization !== true) fail(`${field} appliedBeforeSerialization must be true`)\n if (privacy.secureInputsMasked !== true) fail(`${field} secureInputsMasked must be true`)\n if (!isBooleanValue(privacy.platformFallbackApplied)) fail(`${field} platformFallbackApplied must be a boolean`)\n return freezeV1Value({\n policyRevision: assertBoundedString(privacy.policyRevision, `${field} policyRevision`, 128),\n effectivePolicyHash: assertHash(privacy.effectivePolicyHash, `${field} effectivePolicyHash`),\n maskingProfileHash: assertHash(privacy.maskingProfileHash, `${field} maskingProfileHash`),\n appliedBeforeSerialization: true,\n secureInputsMasked: true,\n platformFallbackApplied: privacy.platformFallbackApplied,\n })\n}\n\nconst validateNamedVersion = (value: unknown, field: string, namePattern: RegExp) => {\n const named = assertKeys(value, NAMED_VERSION_KEYS, field)\n const name = assertBoundedString(named.name, `${field} name`, 256)\n if (!namePattern.test(name)) fail(`${field} name is invalid`)\n return freezeV1Value({ name, version: assertBoundedString(named.version, `${field} version`, 64) })\n}\n\nexport const validateV2VersionContext = (value: unknown, field = 'replay versions'): V2VersionContext => {\n const versions = assertKeys(value, VERSIONS_KEYS, field, ['build'])\n if (versions.schemaVersion !== 2) fail(`${field} schemaVersion must be 2`)\n if (versions.contractVersion !== V2_REPLAY_CONTRACT_VERSION) {\n fail(`${field} contractVersion must be ${V2_REPLAY_CONTRACT_VERSION}`)\n }\n if (versions.platform !== 'browser' && versions.platform !== 'android' && versions.platform !== 'ios') {\n fail(`${field} platform is unsupported`)\n }\n const runtime = validateNamedVersion(versions.runtime, `${field} runtime`, RUNTIME_NAME_PATTERN)\n const facade = validateNamedVersion(versions.facade, `${field} facade`, FACADE_NAME_PATTERN)\n return freezeV1Value({\n schemaVersion: 2,\n contractVersion: V2_REPLAY_CONTRACT_VERSION,\n platform: versions.platform,\n runtime: runtime as V2VersionContext['runtime'],\n facade,\n ...(hasOwn(versions, 'build') ? { build: assertBoundedString(versions.build, `${field} build`, 128) } : {}),\n })\n}\n\n/** Validates a complete replay chunk envelope against the frozen v2 schema and returns a frozen copy. */\nexport const validateV2ReplayChunk = (value: unknown, field = 'replay chunk'): V2ReplayChunk => {\n const chunk = assertKeys(value, CHUNK_KEYS, field)\n if (chunk.schemaVersion !== 2) fail(`${field} schemaVersion must be 2`)\n const startedAt = assertBoundedString(chunk.startedAt, `${field} startedAt`, 64)\n const endedAt = assertBoundedString(chunk.endedAt, `${field} endedAt`, 64)\n const started = parseV1ExactTimestamp(startedAt, `${field} startedAt`, 64)\n const ended = parseV1ExactTimestamp(endedAt, `${field} endedAt`, 64)\n if (compareV1ExactTimestamps(started, ended) > 0) fail(`${field} endedAt must not precede startedAt`)\n const codec = assertBoundedString(chunk.codec, `${field} codec`, 68)\n if (!CODEC_PATTERN.test(codec)) fail(`${field} codec is invalid`)\n if (chunk.compression !== 'none' && chunk.compression !== 'gzip') fail(`${field} compression is unsupported`)\n if (chunk.contentEncoding !== 'base64') fail(`${field} contentEncoding must be base64`)\n if (!isStringValue(chunk.payload) || chunk.payload.length === 0 || !BASE64_PATTERN.test(chunk.payload)) {\n fail(`${field} payload must be non-empty canonical base64`)\n }\n return freezeV1Value({\n schemaVersion: 2,\n replayId: assertBoundedString(chunk.replayId, `${field} replayId`, 256),\n sessionId: assertBoundedString(chunk.sessionId, `${field} sessionId`, 256),\n chunkId: assertBoundedString(chunk.chunkId, `${field} chunkId`, 256),\n sequence: assertSafeCount(chunk.sequence, `${field} sequence`),\n startedAt,\n endedAt,\n identity: validateIdentity(chunk.identity, `${field} identity`),\n contextRevision: assertSafeCount(chunk.contextRevision, `${field} contextRevision`),\n codec: codec as `elu-${string}`,\n compression: chunk.compression,\n contentEncoding: 'base64',\n payload: chunk.payload,\n privacy: validatePrivacy(chunk.privacy, `${field} privacy`),\n versions: validateV2VersionContext(chunk.versions, `${field} versions`),\n })\n}\n\nexport interface V2ReplayChunkInput {\n readonly replayId: string\n readonly sessionId: string\n readonly chunkId: string\n readonly sequence: number\n readonly startedAt: string\n readonly endedAt: string\n readonly identity: V2ReplayIdentity\n readonly contextRevision: number\n readonly payload: V2EncodedReplayPayload\n readonly policyRevision: string\n readonly effectivePolicyHash: V2Sha256Hash\n readonly versions: V2VersionContext\n}\n\n/**\n * Builds the outer envelope around an encoded payload. The privacy proof constants and the masking\n * profile hash come from the sealed proof inside the payload; callers cannot supply them.\n */\nexport const buildV2ReplayChunk = (input: V2ReplayChunkInput): V2ReplayChunk => {\n if (!isRecordValue(input) || !isRecordValue(input.payload) || !isV2MaskingProof(input.payload.proof)) {\n fail('replay chunk requires a payload encoded from a sealed masking proof')\n }\n const { payload } = input\n return validateV2ReplayChunk({\n schemaVersion: 2,\n replayId: input.replayId,\n sessionId: input.sessionId,\n chunkId: input.chunkId,\n sequence: input.sequence,\n startedAt: input.startedAt,\n endedAt: input.endedAt,\n identity: input.identity,\n contextRevision: input.contextRevision,\n codec: payload.codec,\n compression: payload.compression,\n contentEncoding: payload.contentEncoding,\n payload: payload.payload,\n privacy: {\n policyRevision: input.policyRevision,\n effectivePolicyHash: input.effectivePolicyHash,\n maskingProfileHash: payload.proof.profileHash,\n appliedBeforeSerialization: true,\n secureInputsMasked: true,\n platformFallbackApplied: payload.proof.profile.platformFallbackApplied,\n },\n versions: input.versions,\n })\n}\n\nexport const validateV2ReplayRequest = (value: unknown, field = 'replay request'): V2ReplayRequest => {\n const request = assertKeys(value, REQUEST_KEYS, field)\n if (request.schemaVersion !== 2) fail(`${field} schemaVersion must be 2`)\n if (!isStringValue(request.requestId) || !V2_REPLAY_REQUEST_ID_PATTERN.test(request.requestId)) {\n fail(`${field} requestId must be request_ followed by a lowercase sha256 digest`)\n }\n return freezeV1Value({\n schemaVersion: 2,\n requestId: request.requestId,\n chunk: validateV2ReplayChunk(request.chunk, `${field} chunk`),\n })\n}\n\n/** Strict canonical UTF-8 bytes of the complete chunk envelope. */\nexport const canonicalV2ReplayChunkBytes = (chunk: V2ReplayChunk): Uint8Array =>\n utf8Encode(canonicalV2JsonValue(validateV2ReplayChunk(chunk), 'replay chunk'))\n\n/** `UTF8(domain) || 0x00 || uint32be(len(canonicalChunk)) || canonicalChunk` */\nexport const v2ReplayRequestIdentityMaterial = (canonicalChunk: Uint8Array): Uint8Array => {\n const domain = utf8Encode(V2_REPLAY_REQUEST_ID_DOMAIN)\n const material = new Uint8Array(domain.length + 1 + 4 + canonicalChunk.length)\n material.set(domain, 0)\n material[domain.length] = 0\n const length = canonicalChunk.length\n material[domain.length + 1] = (length >>> 24) & 0xff\n material[domain.length + 2] = (length >>> 16) & 0xff\n material[domain.length + 3] = (length >>> 8) & 0xff\n material[domain.length + 4] = length & 0xff\n material.set(canonicalChunk, domain.length + 5)\n return material\n}\n\nexport interface V2ReplayRequestIdentity {\n readonly requestId: string\n readonly canonicalChunk: Uint8Array\n}\n\nexport const deriveV2ReplayRequestId = async (\n chunk: V2ReplayChunk,\n sha256?: V2ReplaySha256\n): Promise<V2ReplayRequestIdentity> => {\n const canonicalChunk = canonicalV2ReplayChunkBytes(chunk)\n const digest = await sha256Hex(v2ReplayRequestIdentityMaterial(canonicalChunk), sha256)\n return freezeV1Value({ requestId: `request_${digest}`, canonicalChunk })\n}\n\nexport interface V2PreparedReplayRequest {\n /** Local capture witness; deliberately excluded from request bytes and request identity. */\n readonly captureProtocolGeneration: string\n readonly request: V2ReplayRequest\n /** The exact bytes every attempt sends; they are persisted and never regenerated on retry. */\n readonly canonicalRequest: Uint8Array\n readonly canonicalRequestSha256: V2Sha256Hash\n readonly byteLength: number\n}\n\nexport interface V2ReplayRequestOptions {\n readonly captureProtocolGeneration: string\n readonly sha256?: V2ReplaySha256\n /** `limits.replayChunkBytes` from the current config; the hard ceiling applies regardless. */\n readonly replayChunkBytes?: number\n}\n\n/** `min(config.replayChunkBytes, 5242880)`; a missing config limit leaves only the hard ceiling. */\nexport const effectiveV2ReplayRequestLimit = (replayChunkBytes?: number): number => {\n if (typeof replayChunkBytes === 'undefined') return V2_MAX_REPLAY_REQUEST_BYTES\n if (!isNonNegativeInteger(replayChunkBytes) || replayChunkBytes < 1) {\n return fail('replayChunkBytes must be a positive safe integer')\n }\n return Math.min(replayChunkBytes, V2_MAX_REPLAY_REQUEST_BYTES)\n}\n\n/** Derives the request identity, canonicalizes the complete request, and applies the exact byte limit. */\nexport const prepareV2ReplayRequest = async (\n chunk: V2ReplayChunk,\n options: V2ReplayRequestOptions\n): Promise<V2PreparedReplayRequest> => {\n if (!isRecordValue(options)) fail('replay request options must include captureProtocolGeneration')\n const captureProtocolGeneration = assertBoundedString(\n options.captureProtocolGeneration,\n 'captureProtocolGeneration',\n 128\n )\n const limit = effectiveV2ReplayRequestLimit(options.replayChunkBytes)\n const validated = validateV2ReplayChunk(chunk)\n const identity = await deriveV2ReplayRequestId(validated, options.sha256)\n const request: V2ReplayRequest = freezeV1Value({\n schemaVersion: 2,\n requestId: identity.requestId,\n chunk: validated,\n })\n const canonicalRequest = utf8Encode(canonicalV2JsonValue(request, 'replay request'))\n if (canonicalRequest.length > limit) {\n fail(`replay request is ${canonicalRequest.length} bytes; the effective limit is ${limit}`)\n }\n const digest = await sha256Hex(canonicalRequest, options.sha256)\n return freezeV1Value({\n captureProtocolGeneration,\n request,\n canonicalRequest,\n canonicalRequestSha256: `sha256:${digest}` as V2Sha256Hash,\n byteLength: canonicalRequest.length,\n })\n}\n","import { encodeV2BrowserDomPayload } from './codec'\nimport { maskV2BrowserDomEvent } from './dom-masker'\nimport { V2MaskingProof } from './masking-proof'\nimport { buildV2ReplayChunk, prepareV2ReplayRequest } from './request'\nimport type { V2MaskingProfile, V2ReplayIdentity, V2ReplaySha256, V2Sha256Hash, V2VersionContext } from './types'\nimport type { V2PreparedReplayRequest } from './request'\n\n/**\n * One recorded stretch of a session, on its way to the queue.\n *\n * This is the whole path from events to a request the store will accept, in one place and in one\n * order: mask, seal, encode, envelope, prepare. Splitting it would let a caller seal a proof over\n * events this module never masked, which is the one sequence the proof exists to rule out.\n */\nexport interface V2SealReplayChunkInput {\n /** Events as the recorder produced them, in order. */\n readonly events: readonly unknown[]\n readonly profile: V2MaskingProfile\n readonly profileHash: V2Sha256Hash\n readonly replayId: string\n readonly sessionId: string\n readonly chunkId: string\n readonly sequence: number\n readonly startedAt: string\n readonly endedAt: string\n readonly identity: V2ReplayIdentity\n readonly contextRevision: number\n readonly captureProtocolGeneration: string\n readonly policyRevision: string\n readonly effectivePolicyHash: V2Sha256Hash\n readonly versions: V2VersionContext\n /** The document's replay chunk ceiling; a chunk over it is refused rather than trimmed. */\n readonly replayChunkBytes?: number\n readonly sha256?: V2ReplaySha256\n}\n\n/**\n * Masks, seals, encodes and prepares one chunk.\n *\n * The masking pass runs here rather than at the call site so a proof can only ever cover events this\n * function masked. `V2MaskingProof.seal` applies the masker to every event itself, so passing the\n * masker rather than pre-masked events is what makes the proof's claim true by construction.\n */\nexport const sealV2ReplayChunk = async (input: V2SealReplayChunkInput): Promise<V2PreparedReplayRequest> => {\n const proof = V2MaskingProof.seal(maskV2BrowserDomEvent, {\n profile: input.profile,\n profileHash: input.profileHash,\n events: input.events,\n })\n const payload = encodeV2BrowserDomPayload(proof)\n const chunk = buildV2ReplayChunk({\n replayId: input.replayId,\n sessionId: input.sessionId,\n chunkId: input.chunkId,\n sequence: input.sequence,\n startedAt: input.startedAt,\n endedAt: input.endedAt,\n identity: input.identity,\n contextRevision: input.contextRevision,\n payload,\n policyRevision: input.policyRevision,\n effectivePolicyHash: input.effectivePolicyHash,\n versions: input.versions,\n })\n return prepareV2ReplayRequest(chunk, {\n captureProtocolGeneration: input.captureProtocolGeneration,\n ...(input.replayChunkBytes === undefined ? {} : { replayChunkBytes: input.replayChunkBytes }),\n ...(input.sha256 === undefined ? {} : { sha256: input.sha256 }),\n })\n}\n","/* eslint-disable compat/compat -- The internal replay driver requires native Promise support. */\n\nimport { advanceV2ReplaySchedule, initialV2ReplayScheduleState } from './schedule'\nimport { V1_MAX_DURABLE_QUEUE_BYTES, V1_MAX_DURABLE_QUEUE_COUNT } from '../durable-state'\nimport { isNullValue } from '../validation'\nimport type { V2ReplaySchedulePolicy, V2ReplayScheduleState } from './schedule'\nimport { sealV2ReplayChunk } from './seal-chunk'\nimport type { V2SealReplayChunkInput } from './seal-chunk'\nimport type { V2PreparedReplayRequest } from './request'\n\n/**\n * Turns a stream of recorded events into sealed chunks.\n *\n * The recorder is injected rather than imported. That is not only for tests: importing it here would\n * pull the whole recorder into the served bundle, which is why the inherited core loads its own\n * lazily. Whoever supplies one decides how it arrives.\n *\n * Nothing here starts recording on its own, and nothing here delivers. It buffers what it is given,\n * asks the schedule where the boundaries fall, and hands sealed chunks to a sink.\n */\n\n/** What the driver needs to name and stamp a chunk. Supplied per chunk, not held. */\nexport interface V2ReplayChunkIdentitySource {\n readonly replayId: () => string\n readonly chunkId: () => string\n readonly instant: (atMs: number) => string\n}\n\nexport type V2SealedChunkSink = (\n prepared: V2PreparedReplayRequest,\n context: { readonly discarded: boolean; readonly sequence: number }\n) => Promise<void>\n\nexport interface V2ReplaySessionDriverOptions {\n readonly policy: V2ReplaySchedulePolicy\n /** Separate memory ceiling, not a reservation of the durable event/replay queue. */\n readonly maximumBufferedBytes: number\n readonly identity: V2ReplayChunkIdentitySource\n /**\n * Everything the sealing stage needs that does not change between chunks. The chunk ceiling is\n * deliberately not among them: it comes from the schedule, so one document cannot schedule to\n * one size and seal against another.\n */\n readonly seal: Omit<\n V2SealReplayChunkInput,\n 'events' | 'replayId' | 'sessionId' | 'chunkId' | 'sequence' | 'startedAt' | 'endedAt' | 'replayChunkBytes'\n >\n readonly sessionId: string\n readonly sink: V2SealedChunkSink\n /** A terminal failure discards the driver; the owner must stop its recorder. */\n readonly onSealFailure?: (error: unknown, terminal: boolean) => void\n /** Original stream's sealing/sink has settled; no later suffix may use this replay identity. */\n readonly onReplayClosed?: (replayId: string) => Promise<void>\n}\n\ninterface OpenChunk {\n readonly events: unknown[]\n readonly startedAtMs: number\n endedAtMs: number\n estimatedBytes: number\n}\n\nexport interface V2ReplaySessionDriver {\n /** Offers one recorded event. Sealing happens when a boundary says so. */\n record(event: unknown, atMs: number, byteLength: number): Promise<void>\n /** Seals what is open, retaining an ineligible prefix until real activity qualifies it. */\n flush(): Promise<void>\n /** Includes held prefixes and work queued behind asynchronous sealing. */\n hasBufferedWork(): boolean\n /**\n * Immediately discards buffered/queued work and prevents any further sink calls. A sink call\n * already in progress is awaited; its persisted rows need the owner's separate purge barrier.\n * Rebinding identity or privacy requires a new driver after that barrier completes.\n */\n close(): Promise<void>\n}\n\nexport const createV2ReplaySessionDriver = (options: V2ReplaySessionDriverOptions): V2ReplaySessionDriver => {\n if (!Number.isSafeInteger(options.maximumBufferedBytes) || options.maximumBufferedBytes < 1) {\n throw new Error('replay buffer ceiling must be a positive safe integer')\n }\n const byteLimit = Math.min(options.maximumBufferedBytes, V1_MAX_DURABLE_QUEUE_BYTES)\n let state: V2ReplayScheduleState = initialV2ReplayScheduleState()\n let open: OpenChunk | null = null\n let closed = false\n let replayId = options.identity.replayId()\n let closedReplay: Promise<void> | null = null\n let closePromise: Promise<void> | null = null\n const closeReplay = (): Promise<void> => {\n if (!closedReplay) {\n const originalReplayId = replayId\n closedReplay = Promise.resolve().then(() => options.onReplayClosed?.(originalReplayId))\n }\n return closedReplay\n }\n let qualified = options.policy.minimumDurationSeconds === 0\n let emitted = false\n let held: Array<{ prepared: V2PreparedReplayRequest; sequence: number }> = []\n let heldBytes = 0\n // Recorder-supplied size is an ingress estimate; prepared bytes below are exact.\n // Counting pending/open events also bounds closures retained behind slow hashing.\n let ingressBytes = 0\n let ingressCount = 0\n // Chunk work is serialized: a boundary must finish sealing what it closed before the next\n // event can open anything, or two chunks could claim one sequence.\n let tail: Promise<void> = Promise.resolve()\n\n const report = (error: unknown, terminal = false): void => {\n try {\n options.onSealFailure?.(error ?? new Error('replay driver failed without an error value'), terminal)\n } catch {\n // A diagnostic observer cannot strand queued work.\n }\n }\n const discardHeld = (): void => {\n held = []\n heldBytes = 0\n }\n const stop = (): void => {\n closed = true\n open = null\n discardHeld()\n ingressBytes = 0\n ingressCount = 0\n }\n const fail = (error: unknown): void => {\n stop()\n report(error, true)\n }\n const drainHeld = async (): Promise<void> => {\n while (!closed && qualified && held.length > 0) {\n const next = held.shift()!\n heldBytes -= next.prepared.byteLength\n try {\n await options.sink(next.prepared, { discarded: false, sequence: next.sequence })\n emitted = true\n } catch (error) {\n // Never release later deltas after losing the held prefix's baseline.\n fail(error)\n }\n }\n }\n\n const sealOpen = async (discarded: boolean, sequence: number): Promise<void> => {\n const closing = open\n open = null\n if (closed || !closing || closing.events.length === 0) return\n if (discarded) {\n ingressBytes -= closing.estimatedBytes\n ingressCount -= closing.events.length\n return\n }\n try {\n const prepared = await sealV2ReplayChunk({\n ...options.seal,\n // One ceiling, the schedule's, so a chunk is sealed against the size it was cut to.\n replayChunkBytes: options.policy.replayChunkBytes,\n events: closing.events,\n replayId,\n sessionId: options.sessionId,\n chunkId: options.identity.chunkId(),\n sequence,\n startedAt: options.identity.instant(closing.startedAtMs),\n endedAt: options.identity.instant(closing.endedAtMs),\n })\n // Closing while hashing/sealing must not hand stale identity or privacy to the sink.\n if (!closed && !discarded) {\n if (!qualified) {\n if (prepared.byteLength > byteLimit - heldBytes || held.length >= V1_MAX_DURABLE_QUEUE_COUNT) {\n fail(new Error('replay minimum-duration buffer capacity exceeded'))\n } else {\n held.push({ prepared, sequence })\n heldBytes += prepared.byteLength\n }\n } else {\n await options.sink(prepared, { discarded: false, sequence })\n emitted = true\n }\n }\n } catch (error) {\n // A chunk that cannot be sealed is lost, and the session goes on. Failing the whole\n // recorder over one unsendable stretch would lose the rest of the session too.\n if (!closed) {\n if (!qualified || (options.policy.minimumDurationSeconds > 0 && !emitted)) fail(error)\n else report(error)\n }\n } finally {\n ingressBytes = Math.max(0, ingressBytes - closing.estimatedBytes)\n ingressCount = Math.max(0, ingressCount - closing.events.length)\n }\n }\n\n const append = (event: unknown, atMs: number, byteLength: number): void => {\n if (open) {\n open.events.push(event)\n open.endedAtMs = atMs\n open.estimatedBytes += byteLength\n return\n }\n open = { events: [event], startedAtMs: atMs, endedAtMs: atMs, estimatedBytes: byteLength }\n }\n\n return {\n record(event: unknown, atMs: number, byteLength: number): Promise<void> {\n if (closed) return Promise.resolve()\n if (\n !Number.isFinite(atMs) ||\n atMs < 0 ||\n (!isNullValue(state.lastActivityAtMs) && atMs < state.lastActivityAtMs) ||\n !Number.isSafeInteger(byteLength) ||\n byteLength < 1\n ) {\n fail(new Error('invalid replay ingress observation'))\n return tail\n }\n if (byteLength > byteLimit - ingressBytes || ingressCount >= V1_MAX_DURABLE_QUEUE_COUNT) {\n fail(new Error('replay ingress buffer capacity exceeded'))\n return tail\n }\n ingressBytes += byteLength\n ingressCount += 1\n const decision = advanceV2ReplaySchedule(state, { atMs, byteLength }, options.policy)\n const sealedSequence = state.sequence\n state = decision.state\n tail = tail.then(async () => {\n if (closed) return\n if (decision.action !== 'buffer') {\n await sealOpen(decision.discardSealedChunk, sealedSequence)\n if (closed) return\n // A session boundary starts a new replay; a chunk boundary stays in this one.\n if (decision.action === 'cut-session') {\n try {\n await closeReplay()\n } catch (error) {\n fail(error)\n throw error\n }\n if (closed) return\n discardHeld()\n qualified = options.policy.minimumDurationSeconds === 0\n emitted = false\n replayId = options.identity.replayId()\n closedReplay = null\n }\n }\n append(event, atMs, byteLength)\n // Use this queued event's immutable decision, never the globally advanced state.\n if (\n !qualified &&\n !isNullValue(decision.state.sessionStartedAtMs) &&\n atMs - decision.state.sessionStartedAtMs >= options.policy.minimumDurationSeconds * 1000\n ) {\n qualified = true\n await drainHeld()\n }\n })\n return tail\n },\n flush(): Promise<void> {\n if (closed) return Promise.resolve()\n const sequence = state.sequence\n if (state.chunkEvents > 0) {\n // Reserve the next sequence at invocation, like record(), so events queued after\n // this flush cannot reuse the identity of the chunk it is about to seal.\n state = Object.freeze({\n ...state,\n chunkStartedAtMs: null,\n chunkBytes: 0,\n chunkEvents: 0,\n sequence: state.sequence + 1,\n })\n }\n tail = tail.then(() => sealOpen(false, sequence))\n return tail\n },\n hasBufferedWork(): boolean {\n return !closed && (ingressCount > 0 || held.length > 0 || !isNullValue(open))\n },\n close(): Promise<void> {\n // This barrier is synchronous even when prior digest or sink work is still pending.\n stop()\n if (!closePromise) closePromise = tail.then(closeReplay, async (error) => {\n await closeReplay()\n throw error\n })\n return closePromise\n },\n }\n}\n"],"names":["values","unpackV1DeflatedTextData","encoded","expectedBytes","expectedCount","invalid","Error","Number","isSafeInteger","length","padding","endsWith","bytes","Uint8Array","byteCount","bits","value","index","digit","indexOf","charAt","fixedLiterals","fixedDistances","output","written","position","read","count","offset","result","tree","lengths","single","empty","counts","Uint16Array","maximum","Math","max","remaining","offsets","symbols","symbol","codes","code","first","final","blocks","kind","set","subarray","literals","distances","distanceLengths","literalCount","distanceCount","codeCount","order","codeLengths","codeTree","repeated","end","next","extra","distanceSymbol","distanceExtra","distance","ceil","decoded","String","fromCharCode","apply","min","texts","JSON","parse","Array","isArray","some","text","Object","freeze","readV1SharedText","v1MemberKeys","v1MemberKey0","v1MemberKey2","v1MemberKey3","v1MemberKey7","v1MemberKey8","v1MemberKey22","v1MemberKey23","v1MemberKey34","v1MemberKey37","v1MemberKey38","v1MemberKey45","v1MemberKey48","v1MemberKey52","v1MemberKey53","v1MemberKey60","v1MemberKey65","v1MemberKey83","v1MemberKey85","v1MemberKey86","v1MemberKey91","v1MemberKey95","v1MemberKey111","v1MemberKey113","v1MemberKey118","v1MemberKey132","v1MemberKey133","v1MemberKey134","v1MemberKey135","v1MemberKey136","v1MemberKey139","v1MemberKey147","v1MemberKey151","v1MemberKey171","v1MemberKey180","v1MemberKey183","v1MemberKey195","v1MemberKey196","v1MemberKey203","v1MemberKey212","v1MemberKey215","v1MemberKey216","v1MemberKey217","v1MemberKey220","v1MemberKey221","v1MemberKey225","v1MemberKey228","v1MemberKey230","v1MemberKey232","v1MemberKey233","v1MemberKey236","v1MemberKey238","v1MemberKey244","v1MemberKey252","v1MemberKey253","v1MemberKey262","v1MemberKey269","v1MemberKey272","v1MemberKey275","v1MemberKey289","v1MemberKey305","v1MemberKey308","v1MemberKey317","v1MemberKey323","v1MemberKey324","v1MemberKey334","v1MemberKey348","v1MemberKey351","isArrayValue","isRecordValue","isStringValue","isBooleanValue","isFunctionValue","unicodeCodePointLength","isNumberValue","isNonNegativeInteger","isNullValue","is","hasOwn","key","prototype","call","hasOwnIndex","hasOnlyKeys","allowedKeys","allowed","Set","keys","every","has","freezeV1Value","openChunk","state","atMs","sequence","chunkStartedAtMs","chunkBytes","chunkEvents","V1_SHARED_LITERAL_020","V1_SHARED_LITERAL_024","INGEST_ORIGIN","assets","V1_ENDPOINT_PATHS","V2_ENDPOINT_PATHS","RFC3339_PATTERN","fail","field","message","parseV1ExactTimestamp","maximumUnicodeScalars","_match$","scalarLength","match","exec","year","month","day","hour","minute","second","fractionalDigits","replace","offsetHour","offsetMinute","daysInMonth","date","Date","getUTCDate","local","setUTCHours","epochSecond","getTime","leapSecond","nextUtcSecond","getUTCHours","getUTCMinutes","getUTCSeconds","getUTCMonth","split","epochDay","floor","splitEpochSecond","_extends","source","V2_REPLAY_SEQUENCE_INDEX","RECORDS_STORE","base","name","keyPath","indexes","unique","SHA256_HEX_PATTERN","defaultV2ReplaySha256","_ref","_asyncToGenerator","cryptoApi","globalThis","undefined","crypto","subtle","digest","hexadecimal","byte","toString","padStart","_unused","_x","this","arguments","sha256Hex","_ref2","sha256","test","_x2","_x3","isV2Sha256Hash","utf8Encode","TextEncoder","encode","wallNow","monotonicNowMs","clock","performance","now","NaN","u8","u16","u32","Uint32Array","fleb","fdeb","clim","freb","eb","start","b","i","r","j","_a","fl","revfl","revfd","rev","x","hMap","cd","mb","s","l","co","le","rvb","sv","r_1","v","m","flt","fdt","flm","fdm","shft","p","wbits","d","o","wbits16","hTree","t","push","f","t2","slice","sort","a","i0","i1","i2","maxSym","tr","mbt","ln","dt","lft","cst","i2_1","i2_2","i2_3","n","lc","c","cl","cli","cln","cls","w","clen","cf","wfblk","out","pos","dat","wblk","syms","lf","df","li","bs","bl","dlt","mlb","_b","ddt","mdb","_c","lclt","nlc","_d","lcdt","ndc","lcfreq","_e","lct","mlcb","nlcc","lm","ll","dm","dl","flen","ftlen","dtlen","llm","lcts","it","clct","len","dst","deo","crct","k","wbytes","gzipSync","data","opts","cr","crc","opt","pre","filename","lvl","plvl","post","lst","e","msk_1","prev","head","bs1_1","bs2_1","hsh","lc_1","wi","hv","imod","pimod","rem","ch_1","dif","maxn","maxd","ml","nl","mmd","md","ti","lin","din","slc","dflt","level","mem","log","fn","mtime","charCodeAt","gzh","V2ReplayPayloadAdmissionError","constructor","super","V2MaskingProfileError","V2MaskingProofError","V2ReplayRequestError","ALPHABET","decodeTable","Int16Array","fill","encodeBase64","triple","V2_REPLAY_PAYLOAD_LIMITS","maximumReplayDecodedBytes","maximumJsonNestingDepth","assertUnicode","path","isOmittedValue","walk","depth","maximumNestingDepth","containerDepth","parts","element","join","isPlainObject","record","members","member","canonicalV2JsonValue","V2_BROWSER_MASKING_DIALECT","PROFILE_KEYS","TEXT_RULES","INPUT_RULES","IMAGE_RULES","assertRule","includes","assertSelector","normalizeSelectors","selectors","add","size","SEAL","Symbol","V2MaskingProof","seal","profile","profileHash","canonicalPayload","payloadByteLength","logicalRecords","nestingDepth","masker","input","_input$v1MemberKey","textRule","inputRule","imageRule","buildV2MaskingProfile","canonical","records","limits","byteLength","utf8ByteLength","canonicalV2ReplayRecords","map","event","isV2MaskingProof","table","Int32Array","bit","SENSITIVE_INPUT_TYPES","IMAGE_CONTENT_ATTRIBUTES","IMAGE_TAG_NAMES","isRecord","maskAttributes","tagName","attributes","masked","inputType","type","attribute","maskNode","node","textContent","isStyle","childNodes","child","maskMutation","entry","adds","maskV2BrowserDomEvent","V2_REPLAY_CONTRACT_VERSION","V2_MAX_REPLAY_REQUEST_BYTES","CODEC_PATTERN","RUNTIME_NAME_PATTERN","FACADE_NAME_PATTERN","BASE64_PATTERN","CHUNK_KEYS","IDENTITY_KEYS","PRIVACY_KEYS","VERSIONS_KEYS","NAMED_VERSION_KEYS","assertBoundedString","minimum","assertSafeCount","assertHash","assertKeys","optional","validateIdentity","identity","validatePrivacy","privacy","appliedBeforeSerialization","validateNamedVersion","namePattern","named","validateV2VersionContext","versions","runtime","facade","build","validateV2ReplayChunk","chunk","startedAt","endedAt","compareV1ExactTimestamps","left","right","maximumDigits","leftDigit","rightDigit","codec","contentEncoding","payload","deriveV2ReplayRequestId","canonicalChunk","canonicalV2ReplayChunkBytes","domain","material","v2ReplayRequestIdentityMaterial","prepareV2ReplayRequest","options","replayChunkBytes","captureProtocolGeneration","limit","validated","request","canonicalRequest","canonicalRequestSha256","_x4","sealV2ReplayChunk","proof","compressed","compressedByteLength","decodedByteLength","encodeV2BrowserDomPayload","buildV2ReplayChunk","createV2ReplaySessionDriver","byteLimit","open","closed","replayId","closedReplay","closePromise","closeReplay","originalReplayId","Promise","resolve","then","onReplayClosed","qualified","policy","emitted","held","heldBytes","ingressBytes","ingressCount","tail","report","error","terminal","onSealFailure","discardHeld","stop","drainHeld","shift","prepared","sink","discarded","sealOpen","closing","estimatedBytes","instant","startedAtMs","endedAtMs","decision","advanceV2ReplaySchedule","_state$chunkStartedAt","action","discardSealedChunk","idleForMs","sessionAgeMs","_state$v1MemberKey","sealedMs","sealedSequence","append","flush","hasBufferedWork","close","_ref4"],"mappings":"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,IACHtC,IAAYrB,GAAeE,IAC/BkB,EAAOC,KAAasC,MACjB,CACCA,EAAO,KAAKzD,IAChB,IAAMY,EAAQ6C,EAAO,IACfC,GAAS9C,IAAU,GAAK,EACxBR,EAASQ,EAAQ,EAAIA,EAAQ,EAAc,KAAVA,EAAe,IAAqC,GAAf,GAAL,EAARA,IAAmB8C,GAAarC,EAAKqC,GAC9FC,EAAiBtB,EAAOU,GAC1BY,EAAiB,IAAI3D,IACzB,IAAM4D,GAAiBD,IAAmB,GAAK,EACzCE,EAAWF,EAAiB,EAAIA,EAAiB,EAAoD,GAAvB,GAAL,EAAjBA,IAA4BC,GAAqBvC,EAAKuC,IAChHC,EAAW1C,GAAWA,EAAUf,EAASN,IAAeE,IAE5D,IAAK,IAAIY,EAAQ,EAAGA,EAAQR,EAAQQ,IAAWM,EAAOC,GAAWD,EAAOC,EAAU0C,GAAW1C,GACjG,CACJ,CACJ,CACJ,CAEIA,IAAYrB,GAAiBkC,KAAK8B,KAAK1C,EAAW,KAAOb,EAAMH,QAAQJ,IAG3E,IAFA,IAAI+D,EAAU,GAELxC,EAAS,EAAGA,EAASJ,EAASI,GAAU,KAAMwC,GAAWC,OAAOC,aAAaC,MAAM,KAAMhD,EAAO2B,SAAStB,EAAQS,KAAKmC,IAAI5C,EAAS,KAAMJ,KAClJ,IAAMiD,EAAiBC,KAAKC,MAAMP,GAElC,OADKQ,MAAMC,QAAQJ,IAAUA,EAAMhE,SAAWL,IAAiBqE,EAAMK,MAAKC,GAAwB,iBAATA,KAAoB1E,IACtG2E,OAAOC,OAAOR,EAAkB,E,E,I,I,E,E,2gD,K,K,E,I,E,E,miG,K,K,E,E,G,E,G,I,E,E,G,E,E,G,E,G,E,G,E,G,I,E,E,G,E,E,G,E,G,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,I,E,I,I,E,E,I,E,E,I,E,I,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,I,E,I,I,E,E,I,E,I,E,I,E,I,I,E,E,I,E,E,I,E,I,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,I,I,E,E,I,E,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,I,E,I,E,I,I,E,E,I,E,I,E,I,E,I,E,I,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,E,K,E,E,K,E,E,K,E,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,I,E,E,K,E,E,K,E,E,K,E,K,E,K,I,E,E,K,E,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,I,E,E,K,E,K,I,E,E,K,E,K,I,G,E,K,G,E,K,E,K,E,K,I,G,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,I,G,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,I,G,E,K,E,KC1J3C,IAAQI,GAAYD,MAAZC,QACK0E,GAAgBvI,GAAuC6D,GAAQ7D,GAE/DwI,GAAiBxI,GACT,iBAAVA,GAA+B,MAATA,IAAkBuI,GAAavI,GAEnDyI,GAAiBzI,GAAqD,iBAAVA,EAE5D0I,GAAkB1I,IAA+C,IAAVA,IAA4B,IAAVA,EAEzE2I,GAAmB3I,GAEnB,mBADMA,EAQN4I,GAA0B5I,IAEnC,IADA,IAAIP,EAAS,EACJQ,EAAQ,EAAGA,EAAQD,E,IAAcC,GAAS,EAAG,CAClD,IAAM2B,EAAO5B,E,GAAiBC,GAC9B,GAAI2B,GAAQ,OAAUA,GAAQ,MAAQ,CAClC,IAAMkB,EAAO9C,E,GAAiBC,EAAQ,GACtC,GAAIA,EAAQ,GAAKD,E,KAAgB8C,EAAO,OAAUA,EAAO,MACrD,OAAO,KAEX7C,GAAS,CACb,MAAO,GAAI2B,GAAQ,OAAUA,GAAQ,MACjC,OAAO,KAEXnC,GAAU,CACd,CACA,OAAOA,CAAM,EAGJoJ,GAAiB7I,GAEjB,iBADMA,GAEAT,O,IAAgBS,GAMtB8I,GAAwB9I,GACjC6I,GAAc7I,IAAUT,O,GAAqBS,IAAUA,GAAS,EAEvD+I,GAAe/I,GAAkCgE,OAAOgF,GAAGhJ,EAAO,MAElEiJ,GAASA,CAACjJ,EAAgCkJ,IACnDlF,OAAOmF,U,GAAyBC,KAAKpJ,EAAOkJ,GAEnCG,GAAcA,CAACrJ,EAA2BC,IACnD+D,OAAOmF,U,GAAyBC,KAAKpJ,EAAOC,GAEnCqJ,GAAcA,CAACtJ,EAAgCuJ,KACxD,IAAMC,EAAU,IAAIC,IAAIF,GACxB,OAAOvF,OAAO0F,KAAK1J,GAAO2J,OAAOT,GAAQM,EAAQI,IAAIV,IAAK,EAwDjDW,GAA0C7J,GAA0BgE,OAAOC,OAAOjE,GC7DzF8J,GAAYA,CAACC,EAA8BC,EAAcpK,EAAeqK,IAC1EJ,GAAc,C,C,GACUE,E,G,C,GACFC,EAClBE,iBAAkBF,EAClBG,WAAYvK,EACZwK,YAAa,E,C,G,I,G,E,2nD,K,I,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,I,G,G,I,G,I,G,I,G,I,I,G,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,ICpCrB,IAAMG,G,yBAkB2FV,GAAc,C,C,IACnGA,GAAc,CAACU,K,C,IACfV,GAAc,CAACU,K,C,IAChBV,GAAc,CAACU,KACtBC,OAAQX,GAAc,C,6BAEnB,IAAMY,GAA8DZ,GAAc,C,C,IAC7E,a,C,IACA,a,C,IACD,YACPW,OAAQ,UAOCE,GAA8Db,GAAc,C,C,IAC7E,a,C,IACA,a,C,IACD,YACPW,OAAQ,UAIRX,GAAc,CAAE,EAAGY,GAAmB,EAAGC,KAC4Cb,GAAc,CACnG,EAAG,WACH,EAAG,aCpEP,IAAMc,GAAkB,iGAYlBC,GAAOA,CAACC,EAAeC,KACzB,MAAM,IAAIxL,MAASuL,EAAK,IAAIC,EAAU,EAc7BC,GAAwB,SACjC/K,EACA6K,EACAG,GACmB,IAAAC,EACnB,IAAKxC,GAAczI,GAAQ,OAAO4K,GAAKC,E,E,KACvC,IAAMK,EAAetC,GAAuB5I,GAC5C,GAAI+I,GAAYmC,IAAiBA,EAAe,GAAKA,EAAeF,EAChE,OAAOJ,GAAKC,EAAK,8BAAgCG,sBAErD,IAAMG,EAAQR,GAAgBS,KAAKpL,GACnC,IAAKmL,EAAO,OAAOP,GAAKC,E,E,KACxB,IAAMQ,EAAO9L,OAAO4L,EAAM,IACpBG,EAAQ/L,OAAO4L,EAAM,IACrBI,EAAMhM,OAAO4L,EAAM,IACnBK,EAAOjM,OAAO4L,EAAM,IACpBM,EAASlM,OAAO4L,EAAM,IACtBO,EAASnM,OAAO4L,EAAM,IACtBQ,UAAmBV,EAACE,EAAM,IAAEF,EAAI,IAAIW,Q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","x_google_ignoreList":[8]}
|
|
1
|
+
{"version":3,"file":"cold-session.mjs","sources":["elu-js/packages/browser/scripts/runtime/deflated-text-data.ts","elu-js/packages/browser/src/elu-http-v1/validation.ts","elu-js/packages/browser/src/elu-http-v1/replay/schedule.ts","elu-js/packages/browser/src/elu-http-v1/transport.ts","elu-js/packages/browser/src/elu-http-v1/exact-timestamp.ts","elu-js/packages/browser/src/elu-http-v1/indexeddb-storage.ts","elu-js/packages/browser/src/elu-http-v1/replay/digest.ts","elu-js/packages/browser/src/elu-http-v1/durable-state.ts","../dependencies/fflate/0.4.8/fflate/esm/browser.js","elu-js/packages/browser/src/elu-http-v1/replay/errors.ts","elu-js/packages/browser/src/elu-http-v1/replay/base64.ts","elu-js/packages/browser/src/elu-http-v1/replay/canonical-payload-json.ts","elu-js/packages/browser/src/elu-http-v1/replay/masking-profile.ts","elu-js/packages/browser/src/elu-http-v1/replay/masking-proof.ts","elu-js/packages/browser/src/elu-http-v1/replay/codec.ts","elu-js/packages/browser/src/elu-http-v1/replay/dom-masker.ts","elu-js/packages/browser/src/elu-http-v1/replay/request.ts","elu-js/packages/browser/src/elu-http-v1/replay/seal-chunk.ts","elu-js/packages/browser/src/elu-http-v1/replay/session-driver.ts"],"sourcesContent":["/** Decodes build-owned ASCII JSON data. It never evaluates or imports source code. */\nexport const unpackV1DeflatedTextData = (encoded: string, expectedBytes: number, expectedCount: number): readonly string[] => {\n const invalid = (): never => { throw new Error('Invalid internal text data') }\n if (!Number.isSafeInteger(expectedBytes) || expectedBytes < 1 || expectedBytes > 1_000_000 || !Number.isSafeInteger(expectedCount) || expectedCount < 1 || expectedCount > 10_000 || encoded.length > 2_000_000 || encoded.length % 4 !== 0) invalid()\n const padding = encoded.endsWith('==') ? 2 : encoded.endsWith('=') ? 1 : 0\n const bytes = new Uint8Array((encoded.length / 4) * 3 - padding)\n const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\n let byteCount = 0\n let bits = 0\n let value = 0\n for (let index = 0; index < encoded.length - padding; index++) {\n const digit = alphabet.indexOf(encoded.charAt(index))\n if (digit < 0) invalid()\n value = (value << 6) | digit\n bits += 6\n if (bits >= 8) { bits -= 8; bytes[byteCount++] = (value >>> bits) & 255 }\n }\n if (bits && (value & ((1 << bits) - 1))) invalid()\n\n const output = new Uint8Array(expectedBytes)\n let written = 0\n let position = 0\n const read = (count: number): number => {\n if (position + count > bytes.length * 8) invalid()\n const offset = position >>> 3\n const result = ((bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16)) >>> (position & 7)) & ((1 << count) - 1)\n position += count\n return result\n }\n type Tree = { counts: Uint16Array; symbols: Uint16Array; maximum: number }\n // Code-length trees must be complete. Data trees may have one one-bit symbol;\n // the distance tree alone may be empty when the block contains no match.\n const tree = (lengths: Uint8Array, single: boolean, empty: boolean): Tree => {\n const counts = new Uint16Array(16)\n let maximum = 0\n for (let index = 0; index < lengths.length; index++) {\n const length = lengths[index]\n if (length > 15) invalid()\n if (length) { counts[length]++; maximum = Math.max(maximum, length) }\n }\n let remaining = 1\n for (let length = 1; length <= 15; length++) {\n remaining = remaining * 2 - counts[length]\n if (remaining < 0) invalid()\n }\n if (!maximum ? !empty : remaining && !(single && maximum === 1 && counts[1] === 1)) invalid()\n const offsets = new Uint16Array(16)\n for (let length = 1; length < 15; length++) offsets[length + 1] = offsets[length] + counts[length]\n const symbols = new Uint16Array(lengths.length)\n for (let symbol = 0; symbol < lengths.length; symbol++) {\n const length = lengths[symbol]\n if (length) symbols[offsets[length]++] = symbol\n }\n return { counts, symbols, maximum }\n }\n const symbol = (codes: Tree): number => {\n let code = 0\n let first = 0\n let index = 0\n for (let length = 1; length <= codes.maximum; length++) {\n code = (code << 1) | read(1)\n const count = codes.counts[length]\n if (code >= first && code - first < count) return codes.symbols[index + code - first]\n index += count\n first = (first + count) << 1\n }\n return invalid()\n }\n let final = 0\n let blocks = 0\n let fixedLiterals: Tree | undefined\n let fixedDistances: Tree | undefined\n while (!final) {\n // Build-owned tables allow at most 1,024 blocks, including empty blocks.\n if (++blocks > 1024) invalid()\n final = read(1)\n const kind = read(2)\n if (kind === 0) {\n // Stored blocks start at the next byte; these alignment bits carry no data.\n position = (position + 7) & ~7\n const length = read(16)\n if ((length ^ read(16)) !== 65535 || written + length > expectedBytes || position + length * 8 > bytes.length * 8) invalid()\n output.set(bytes.subarray(position >>> 3, (position >>> 3) + length), written)\n written += length\n position += length * 8\n } else {\n let literals: Tree\n let distances: Tree\n if (kind === 1) {\n if (!fixedLiterals || !fixedDistances) {\n const lengths = new Uint8Array(288)\n for (let index = 0; index < lengths.length; index++) lengths[index] = index < 144 ? 8 : index < 256 ? 9 : index < 280 ? 7 : 8\n fixedLiterals = tree(lengths, false, false)\n const distanceLengths = new Uint8Array(32)\n for (let index = 0; index < distanceLengths.length; index++) distanceLengths[index] = 5\n fixedDistances = tree(distanceLengths, false, false)\n }\n literals = fixedLiterals\n distances = fixedDistances\n } else if (kind === 2) {\n const literalCount = read(5) + 257\n const distanceCount = read(5) + 1\n const codeCount = read(4) + 4\n if (literalCount > 286) invalid()\n const order = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]\n const codeLengths = new Uint8Array(19)\n for (let index = 0; index < codeCount; index++) codeLengths[order[index]] = read(3)\n const codeTree = tree(codeLengths, false, false)\n const lengths = new Uint8Array(literalCount + distanceCount)\n let index = 0\n while (index < lengths.length) {\n const length = symbol(codeTree)\n if (length < 16) lengths[index++] = length\n else {\n if (length === 16 && !index) invalid()\n const repeated = length === 16 ? lengths[index - 1] : 0\n const count = length === 16 ? read(2) + 3 : length === 17 ? read(3) + 3 : read(7) + 11\n if (index + count > lengths.length) invalid()\n for (let end = index + count; index < end; index++) lengths[index] = repeated\n }\n }\n if (!lengths[256]) invalid()\n literals = tree(lengths.subarray(0, literalCount), true, false)\n distances = tree(lengths.subarray(literalCount), true, true)\n } else return invalid()\n for (;;) {\n const next = symbol(literals)\n if (next === 256) break\n if (next < 256) {\n if (written === expectedBytes) invalid()\n output[written++] = next\n } else {\n if (next > 285) invalid()\n const index = next - 257\n const extra = (index >>> 2) - 1\n const length = index < 8 ? index + 3 : index === 28 ? 258 : (((index & 3) + 4) << extra) + 3 + read(extra)\n const distanceSymbol = symbol(distances)\n if (distanceSymbol > 29) invalid()\n const distanceExtra = (distanceSymbol >>> 1) - 1\n const distance = distanceSymbol < 4 ? distanceSymbol + 1 : (((distanceSymbol & 1) + 2) << distanceExtra) + 1 + read(distanceExtra)\n if (distance > written || written + length > expectedBytes) invalid()\n // Overlapping matches deliberately read the bytes just written.\n for (let index = 0; index < length; index++) { output[written] = output[written - distance]; written++ }\n }\n }\n }\n }\n // A raw stream has no trailer. Ignore only its final byte's unused alignment bits.\n if (written !== expectedBytes || Math.ceil(position / 8) !== bytes.length) invalid()\n let decoded = ''\n // Keep native apply argument lists bounded on older supported browsers.\n for (let offset = 0; offset < written; offset += 4096) decoded += String.fromCharCode.apply(null, output.subarray(offset, Math.min(offset + 4096, written)) as unknown as number[])\n const texts: unknown = JSON.parse(decoded)\n if (!Array.isArray(texts) || texts.length !== expectedCount || texts.some(text => typeof text !== 'string')) invalid()\n return Object.freeze(texts as string[])\n}\n","const { isArray } = Array\nexport const isArrayValue = (value: unknown): value is unknown[] => isArray(value)\n\nexport const isRecordValue = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value != null && !isArrayValue(value)\n\nexport const isStringValue = (value: unknown): value is string => typeof value === 'string'\n\nexport const isBooleanValue = (value: unknown): value is boolean => value === true || value === false\n\nexport const isFunctionValue = (value: unknown): value is (...args: never[]) => unknown => {\n switch (typeof value) {\n case 'function':\n return true\n default:\n return false\n }\n}\n\nexport const unicodeCodePointLength = (value: string): number | null => {\n let length = 0\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index)\n if (code >= 0xd800 && code <= 0xdbff) {\n const next = value.charCodeAt(index + 1)\n if (index + 1 >= value.length || next < 0xdc00 || next > 0xdfff) {\n return null\n }\n index += 1\n } else if (code >= 0xdc00 && code <= 0xdfff) {\n return null\n }\n length += 1\n }\n return length\n}\n\nexport const isNumberValue = (value: unknown): value is number => {\n switch (typeof value) {\n case 'number':\n return Number.isFinite(value)\n default:\n return false\n }\n}\n\nexport const isNonNegativeInteger = (value: unknown): value is number =>\n isNumberValue(value) && Number.isSafeInteger(value) && value >= 0\n\nexport const isNullValue = (value: unknown): value is null => Object.is(value, null)\n\nexport const hasOwn = (value: Record<string, unknown>, key: string): boolean =>\n Object.prototype.hasOwnProperty.call(value, key)\n\nexport const hasOwnIndex = (value: readonly unknown[], index: number): boolean =>\n Object.prototype.hasOwnProperty.call(value, index)\n\nexport const hasOnlyKeys = (value: Record<string, unknown>, allowedKeys: readonly string[]): boolean => {\n const allowed = new Set(allowedKeys)\n return Object.keys(value).every((key) => allowed.has(key))\n}\n\nexport const utf8ByteLength = (value: string): number => {\n let bytes = 0\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index)\n if (code <= 0x7f) {\n bytes += 1\n } else if (code <= 0x7ff) {\n bytes += 2\n } else if (code >= 0xd800 && code <= 0xdbff && index + 1 < value.length) {\n const next = value.charCodeAt(index + 1)\n if (next >= 0xdc00 && next <= 0xdfff) {\n bytes += 4\n index += 1\n } else {\n bytes += 3\n }\n } else {\n bytes += 3\n }\n }\n return bytes\n}\n\nexport const parseBoundedJson = (\n rawJson: string,\n maximumBytes: number,\n field: string\n): { readonly value: unknown; readonly serialized: string } => {\n if (!isStringValue(rawJson)) {\n throw new Error(`${field} must be raw JSON text`)\n }\n if (utf8ByteLength(rawJson) > maximumBytes) {\n throw new Error(`${field} exceeds the ${maximumBytes}-byte limit`)\n }\n\n let value: unknown\n try {\n value = JSON.parse(rawJson)\n } catch {\n throw new Error(`${field} must be valid JSON`)\n }\n\n const serialized = JSON.stringify(value)\n if (!isStringValue(serialized)) {\n throw new Error(`${field} must be a JSON value`)\n }\n return { value, serialized }\n}\n\n/**\n * Shares native freezing without caching the intrinsic or changing its overloads.\n * Assumes native Object.freeze semantics; every value is still frozen exactly once.\n */\nexport const freezeV1Value: typeof Object.freeze = <T>(value: T): Readonly<T> => Object.freeze(value)\n","import { freezeV1Value } from '../validation'\n\n/**\n * When one recorded stretch ends and the next begins.\n *\n * Every boundary here comes from the configuration document rather than from a constant: how long a\n * session may idle, how long it may run, how short a replay is worth keeping, and how many bytes a\n * chunk may carry. The state machine is pure — it is given each event's instant and size and returns\n * what to do — so a boundary is a decision that can be read back rather than a timer that fired.\n */\n\nexport interface V2ReplaySchedulePolicy {\n /** No activity for this long ends the session. */\n readonly idleTimeoutSeconds: number\n /** A session may not run longer than this, however active it is. */\n readonly maximumDurationSeconds: number\n /** A replay shorter than this is not worth delivering. */\n readonly minimumDurationSeconds: number\n /** The chunk ceiling from the document, in bytes. */\n readonly replayChunkBytes: number\n}\n\nexport interface V2ReplayScheduleState {\n /** Null before the first event of a session. */\n readonly sessionStartedAtMs: number | null\n readonly lastActivityAtMs: number | null\n readonly chunkStartedAtMs: number | null\n readonly chunkBytes: number\n readonly chunkEvents: number\n readonly sequence: number\n}\n\nexport type V2ReplayScheduleAction =\n | /** Add the event to the open chunk. */ 'buffer'\n | /** Seal the open chunk, then start a new one holding this event. */ 'cut-chunk'\n | /** Seal the open chunk and end the session; this event opens a new replay. */ 'cut-session'\n\nexport interface V2ReplayScheduleDecision {\n readonly action: V2ReplayScheduleAction\n /** True when the stretch being sealed is too short to be worth delivering. */\n readonly discardSealedChunk: boolean\n readonly state: V2ReplayScheduleState\n}\n\nexport const initialV2ReplayScheduleState = (): V2ReplayScheduleState =>\n freezeV1Value({\n sessionStartedAtMs: null,\n lastActivityAtMs: null,\n chunkStartedAtMs: null,\n chunkBytes: 0,\n chunkEvents: 0,\n sequence: 0,\n })\n\nconst openChunk = (state: V2ReplayScheduleState, atMs: number, bytes: number, sequence: number) =>\n freezeV1Value({\n sessionStartedAtMs: state.sessionStartedAtMs,\n lastActivityAtMs: atMs,\n chunkStartedAtMs: atMs,\n chunkBytes: bytes,\n chunkEvents: 1,\n sequence,\n })\n\n/**\n * Decides where one event falls.\n *\n * The order of the checks is the policy: a session that has idled out or run its maximum ends before\n * anything else is considered, because a chunk cut inside a session that should already have closed\n * would carry the new activity under the old replay's identity.\n */\nexport const advanceV2ReplaySchedule = (\n state: V2ReplayScheduleState,\n event: { readonly atMs: number; readonly byteLength: number },\n policy: V2ReplaySchedulePolicy\n): V2ReplayScheduleDecision => {\n const { atMs, byteLength } = event\n\n // The first event of a session opens it; there is nothing to seal.\n if (state.sessionStartedAtMs === null || state.lastActivityAtMs === null) {\n return freezeV1Value({\n action: 'buffer' as const,\n discardSealedChunk: false,\n state: freezeV1Value({\n ...openChunk(state, atMs, byteLength, 0),\n sessionStartedAtMs: atMs,\n }),\n })\n }\n\n const idleForMs = atMs - state.lastActivityAtMs\n const sessionAgeMs = atMs - state.sessionStartedAtMs\n const endsSession =\n idleForMs >= policy.idleTimeoutSeconds * 1000 || sessionAgeMs >= policy.maximumDurationSeconds * 1000\n\n if (endsSession) {\n // What is sealed is the stretch that just ended, so its length decides whether it is kept.\n const sealedMs = state.lastActivityAtMs - (state.sessionStartedAtMs ?? state.lastActivityAtMs)\n return freezeV1Value({\n action: 'cut-session' as const,\n discardSealedChunk: sealedMs < policy.minimumDurationSeconds * 1000,\n state: freezeV1Value({\n ...openChunk(state, atMs, byteLength, 0),\n sessionStartedAtMs: atMs,\n }),\n })\n }\n\n // A chunk that cannot hold this event is sealed and the event opens the next one. An event\n // larger than the whole ceiling still gets its own chunk rather than being dropped here; the\n // sealing stage refuses it, which is where a payload that cannot be sent belongs.\n if (state.chunkEvents > 0 && state.chunkBytes + byteLength > policy.replayChunkBytes) {\n return freezeV1Value({\n action: 'cut-chunk' as const,\n discardSealedChunk: false,\n state: openChunk(state, atMs, byteLength, state.sequence + 1),\n })\n }\n\n return freezeV1Value({\n action: 'buffer' as const,\n discardSealedChunk: false,\n state: freezeV1Value({\n ...state,\n lastActivityAtMs: atMs,\n chunkStartedAtMs: state.chunkStartedAtMs ?? atMs,\n chunkBytes: state.chunkBytes + byteLength,\n chunkEvents: state.chunkEvents + 1,\n }),\n })\n}\n","import type {\n V1BatchRequest,\n V1BeaconPostRequest,\n V1ConfigDocumentVersion,\n V1ConfigHttpRequest,\n V1EndpointName,\n V1EndpointSet,\n V1FlagsRequest,\n V1NormalPostRequest,\n V1PostRequest,\n V1ReplayRequest,\n V1ValidatedEndpointSet,\n} from './types'\nimport {\n freezeV1Value,\n hasOnlyKeys,\n hasOwn,\n isNullValue,\n isRecordValue,\n isStringValue,\n unicodeCodePointLength,\n} from './validation'\n\nexport const V1_CONFIG_ORIGIN = 'https://elu.dev'\nconst INGEST_ORIGIN = 'https://ingest.elu.dev'\nconst ASSETS_ORIGIN = 'https://assets.elu.dev'\nimport { instanceOrigins } from './instance-origin'\n\nconst SITE_KEY_QUERY_PARAMETER = 'site_key'\nconst BEACON_CONTENT_TYPE = 'text/plain;charset=UTF-8'\nconst MALFORMED_PERCENT_ESCAPE_PATTERN = /%(?![0-9A-Fa-f]{2})/\nconst RFC3986_FORBIDDEN_RAW_CHARACTER_PATTERN = /[\"<>\\\\^`{|}]/\nconst ABSOLUTE_URL_RAW_PATH_PATTERN = /^[A-Za-z][A-Za-z0-9+.-]*:\\/\\/[^/?#]+([^?#]*)/\n\nconst hasCharacterOutsideVisibleAscii = (value: string): boolean => {\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index)\n if (code <= 0x20 || code >= 0x7f) {\n return true\n }\n }\n return false\n}\n\nconst DEFAULT_ENDPOINT_ORIGIN_ALLOWLIST: Readonly<Record<V1EndpointName, readonly string[]>> = freezeV1Value({\n events: freezeV1Value([INGEST_ORIGIN]),\n replay: freezeV1Value([INGEST_ORIGIN]),\n flags: freezeV1Value([INGEST_ORIGIN]),\n assets: freezeV1Value([ASSETS_ORIGIN]),\n})\n\n/**\n * The origins each endpoint role may use, for the instance this page is\n * configured against (ELU Cloud unless the application passed `api_host`).\n * Still an allowlist of exactly one origin per role — see instance-origin.ts\n * for why that matters — but one the application chose in its own source.\n */\nexport const v1EndpointOriginAllowlist = (): Readonly<Record<V1EndpointName, readonly string[]>> => {\n const origins = instanceOrigins()\n if (origins.ingest === INGEST_ORIGIN && origins.assets === ASSETS_ORIGIN) return DEFAULT_ENDPOINT_ORIGIN_ALLOWLIST\n return freezeV1Value({\n events: freezeV1Value([origins.ingest]),\n replay: freezeV1Value([origins.ingest]),\n flags: freezeV1Value([origins.ingest]),\n assets: freezeV1Value([origins.assets]),\n })\n}\nexport const V1_ENDPOINT_PATHS: Readonly<Record<V1EndpointName, string>> = freezeV1Value({\n events: '/v1/events',\n replay: '/v1/replay',\n flags: '/v1/flags',\n assets: '/sdk/',\n})\n/**\n * Config v2 keeps the v1 event and flag roles and moves replay to its own major. The standalone\n * replay endpoint does not accept replay v1, so a v2 document must never advertise the v1 path and a\n * v1 document must never advertise the v2 path.\n */\nexport const V2_ENDPOINT_PATHS: Readonly<Record<V1EndpointName, string>> = freezeV1Value({\n events: '/v1/events',\n replay: '/v2/replay',\n flags: '/v1/flags',\n assets: '/sdk/',\n})\nconst ENDPOINT_PATHS_BY_CONFIG_VERSION: Readonly<\n Record<V1ConfigDocumentVersion, Readonly<Record<V1EndpointName, string>>>\n> = freezeV1Value({ 1: V1_ENDPOINT_PATHS, 2: V2_ENDPOINT_PATHS })\nconst CONFIG_PATH_PREFIX_BY_VERSION: Readonly<Record<V1ConfigDocumentVersion, string>> = freezeV1Value({\n 1: '/sdk/v1/',\n 2: '/sdk/v2/',\n})\n\nexport class V1TransportConfigurationError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'V1TransportConfigurationError'\n }\n}\n\nconst assertSiteKey = (siteKey: string): void => {\n if (!isStringValue(siteKey) || siteKey.length === 0) {\n throw new V1TransportConfigurationError('site key must be a non-empty string')\n }\n if (isNullValue(unicodeCodePointLength(siteKey))) {\n throw new V1TransportConfigurationError('site key must not contain an unpaired Unicode surrogate')\n }\n if (siteKey === '.' || siteKey === '..') {\n throw new V1TransportConfigurationError('site key must not be a URL path dot segment')\n }\n for (let index = 0; index < siteKey.length; index += 1) {\n const codePoint = siteKey.charCodeAt(index)\n if (codePoint <= 31 || codePoint === 127) {\n throw new V1TransportConfigurationError('site key must not contain ASCII control characters')\n }\n }\n}\n\n/** Validates a site key from an authorized delivery snapshot. */\nexport const validateV1SiteKey = (value: unknown): string => {\n if (!isStringValue(value)) {\n throw new V1TransportConfigurationError('site key must be a string')\n }\n assertSiteKey(value)\n return value\n}\n\nconst validateEndpoint = (\n name: V1EndpointName,\n value: unknown,\n expectedPath: string = V1_ENDPOINT_PATHS[name]\n): string => {\n if (!isStringValue(value)) {\n throw new V1TransportConfigurationError(`${name} endpoint must be a string`)\n }\n if (isNullValue(unicodeCodePointLength(value))) {\n throw new V1TransportConfigurationError(`${name} endpoint must not contain an unpaired Unicode surrogate`)\n }\n if (hasCharacterOutsideVisibleAscii(value)) {\n throw new V1TransportConfigurationError(\n `${name} endpoint must contain only visible ASCII characters; encode non-ASCII bytes`\n )\n }\n if (MALFORMED_PERCENT_ESCAPE_PATTERN.test(value)) {\n throw new V1TransportConfigurationError(`${name} endpoint contains a malformed percent escape`)\n }\n if (RFC3986_FORBIDDEN_RAW_CHARACTER_PATTERN.test(value)) {\n throw new V1TransportConfigurationError(`${name} endpoint contains a character forbidden in an RFC 3986 URI`)\n }\n\n // URL parsers normalize dot segments before exposing pathname. Compare the configured bytes so\n // a path such as /v1/../v1/events cannot acquire an allowlisted path through normalization.\n const rawPath = ABSOLUTE_URL_RAW_PATH_PATTERN.exec(value)?.[1]\n if (rawPath !== expectedPath) {\n throw new V1TransportConfigurationError(`${name} endpoint path is not allowlisted`)\n }\n\n let parsed: URL\n try {\n // The trust check requires native absolute-URL parsing and fails closed when it is unavailable.\n // eslint-disable-next-line compat/compat\n parsed = new URL(value)\n } catch {\n throw new V1TransportConfigurationError(`${name} endpoint must be an absolute URL`)\n }\n\n if (parsed.protocol !== 'https:') {\n throw new V1TransportConfigurationError(`${name} endpoint must use HTTPS`)\n }\n if (parsed.username || parsed.password) {\n throw new V1TransportConfigurationError(`${name} endpoint must not contain user information`)\n }\n if (!v1EndpointOriginAllowlist()[name].includes(parsed.origin)) {\n throw new V1TransportConfigurationError(`${name} endpoint origin is not allowlisted`)\n }\n if (parsed.pathname !== expectedPath) {\n throw new V1TransportConfigurationError(`${name} endpoint path is not allowlisted`)\n }\n if (parsed.searchParams.has(SITE_KEY_QUERY_PARAMETER)) {\n throw new V1TransportConfigurationError(`${name} endpoint must not define the reserved site_key parameter`)\n }\n if (value.includes('#')) {\n throw new V1TransportConfigurationError(`${name} endpoint must not contain a fragment`)\n }\n\n return parsed.href\n}\n\n/** Validates an event endpoint from an authorized delivery snapshot. */\nexport const validateV1EventsEndpoint = (value: unknown): string => validateEndpoint('events', value)\n\n/** Validates the standalone replay role of a v2 config; the v1 replay path is not interchangeable. */\nexport const validateV2ReplayEndpoint = (value: unknown): string =>\n validateEndpoint('replay', value, V2_ENDPOINT_PATHS.replay)\n\n/** Validates only the flags transport role for the independent flag authorization projection. */\nexport const validateV1FlagsEndpoint = (value: unknown): string => validateEndpoint('flags', value)\n\n/** Validates the configured asset base; this is an endpoint check, not loading authority. */\nexport const validateV1AssetsEndpoint = (value: unknown): string => validateEndpoint('assets', value)\n\n/**\n * Resolves transport endpoints from a config that has already passed the config manager's\n * complete schema, status, expiry, privacy, and feature-gate checks. This function validates\n * only exact endpoint shape, role path, and origin; it is not a config authorization decision.\n */\nconst validateEnabledConfigEndpoints = (\n value: unknown,\n configVersion: V1ConfigDocumentVersion\n): V1ValidatedEndpointSet => {\n if (!isRecordValue(value)) {\n throw new V1TransportConfigurationError('enabled config endpoint set must be an object')\n }\n if (!hasOnlyKeys(value, ['events', 'replay', 'flags', 'assets'])) {\n throw new V1TransportConfigurationError('enabled config endpoint set contains an unsupported endpoint')\n }\n const paths = ENDPOINT_PATHS_BY_CONFIG_VERSION[configVersion]\n\n const endpoints: V1EndpointSet = {\n events: validateEndpoint('events', value.events, paths.events),\n flags: validateEndpoint('flags', value.flags, paths.flags),\n ...(hasOwn(value, 'replay') ? { replay: validateEndpoint('replay', value.replay, paths.replay) } : {}),\n ...(hasOwn(value, 'assets') ? { assets: validateEndpoint('assets', value.assets, paths.assets) } : {}),\n }\n return freezeV1Value(endpoints) as V1ValidatedEndpointSet\n}\n\nexport const validateV1EnabledConfigEndpoints = (value: unknown): V1ValidatedEndpointSet =>\n validateEnabledConfigEndpoints(value, 1)\n\n/** The v2 endpoint set differs from v1 only in the replay role, which must use the v2 replay path. */\nexport const validateV2EnabledConfigEndpoints = (value: unknown): V1ValidatedEndpointSet =>\n validateEnabledConfigEndpoints(value, 2)\n\nconst serializeBody = (body: unknown): string => {\n const serialized = JSON.stringify(body)\n if (!isStringValue(serialized)) {\n throw new V1TransportConfigurationError('request body must be JSON serializable')\n }\n return serialized\n}\n\nconst resolvePostEndpoint = (endpoints: V1ValidatedEndpointSet, name: 'events' | 'replay' | 'flags'): string => {\n const endpoint = endpoints[name]\n if (!endpoint) {\n throw new V1TransportConfigurationError(`${name} endpoint is not advertised by the enabled config`)\n }\n return endpoint\n}\n\nconst buildNormalPostRequest = (siteKey: string, endpoint: string, body: unknown): V1NormalPostRequest => {\n assertSiteKey(siteKey)\n const headers: V1NormalPostRequest['init']['headers'] = freezeV1Value({\n Authorization: `Bearer ${siteKey}`,\n 'Content-Type': 'application/json',\n })\n const init: V1NormalPostRequest['init'] = freezeV1Value({\n method: 'POST',\n mode: 'cors',\n credentials: 'omit',\n headers,\n body: serializeBody(body),\n })\n return freezeV1Value({ delivery: 'normal', url: endpoint, init })\n}\n\nconst buildBeaconPostRequest = (siteKey: string, endpoint: string, body: unknown): V1BeaconPostRequest => {\n assertSiteKey(siteKey)\n // Config endpoint validation already required native URL parsing and an exact allowlisted origin.\n // eslint-disable-next-line compat/compat\n const url = new URL(endpoint)\n url.searchParams.set(SITE_KEY_QUERY_PARAMETER, siteKey)\n return freezeV1Value({\n delivery: 'beacon',\n url: url.href,\n body: new Blob([serializeBody(body)], { type: BEACON_CONTENT_TYPE }),\n })\n}\n\nconst buildPostRequest = (\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n endpointName: 'events' | 'replay',\n body: unknown,\n delivery: 'normal' | 'beacon'\n): V1PostRequest => {\n const endpoint = resolvePostEndpoint(endpoints, endpointName)\n return delivery === 'beacon'\n ? buildBeaconPostRequest(siteKey, endpoint, body)\n : buildNormalPostRequest(siteKey, endpoint, body)\n}\n\nconst buildConfigRequest = (siteKey: string, configVersion: V1ConfigDocumentVersion): V1ConfigHttpRequest => {\n assertSiteKey(siteKey)\n const encodedSiteKey = encodeURIComponent(siteKey)\n const init: V1ConfigHttpRequest['init'] = freezeV1Value({\n method: 'GET',\n mode: 'cors',\n credentials: 'omit',\n })\n return freezeV1Value({\n delivery: 'normal',\n url: `${V1_CONFIG_ORIGIN}${CONFIG_PATH_PREFIX_BY_VERSION[configVersion]}${encodedSiteKey}/config`,\n init,\n })\n}\n\nexport const buildV1ConfigRequest = (siteKey: string): V1ConfigHttpRequest => buildConfigRequest(siteKey, 1)\n\n/** The owned standalone handshake. The site key is the only credential and travels in the path. */\nexport const buildV2ConfigRequest = (siteKey: string): V1ConfigHttpRequest => buildConfigRequest(siteKey, 2)\n\nexport function buildV1EventsRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1BatchRequest\n): V1NormalPostRequest\nexport function buildV1EventsRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1BatchRequest,\n delivery: 'normal'\n): V1NormalPostRequest\nexport function buildV1EventsRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1BatchRequest,\n delivery: 'beacon'\n): V1BeaconPostRequest\nexport function buildV1EventsRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1BatchRequest,\n delivery: 'normal' | 'beacon' = 'normal'\n): V1PostRequest {\n return buildPostRequest(siteKey, endpoints, 'events', request, delivery)\n}\n\n/** Builds a request from an already-authorized event endpoint snapshot. */\nexport function buildV1EventsRequestForEndpoint(\n siteKey: string,\n eventsEndpoint: string,\n request: V1BatchRequest,\n delivery: 'normal'\n): V1NormalPostRequest\nexport function buildV1EventsRequestForEndpoint(\n siteKey: string,\n eventsEndpoint: string,\n request: V1BatchRequest,\n delivery: 'beacon'\n): V1BeaconPostRequest\nexport function buildV1EventsRequestForEndpoint(\n siteKey: string,\n eventsEndpoint: string,\n request: V1BatchRequest,\n delivery: 'normal' | 'beacon'\n): V1PostRequest {\n const endpoint = validateV1EventsEndpoint(eventsEndpoint)\n return delivery === 'beacon'\n ? buildBeaconPostRequest(siteKey, endpoint, request)\n : buildNormalPostRequest(siteKey, endpoint, request)\n}\n\nexport function buildV1ReplayRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1ReplayRequest\n): V1NormalPostRequest\nexport function buildV1ReplayRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1ReplayRequest,\n delivery: 'normal'\n): V1NormalPostRequest\nexport function buildV1ReplayRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1ReplayRequest,\n delivery: 'beacon'\n): V1BeaconPostRequest\nexport function buildV1ReplayRequest(\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1ReplayRequest,\n delivery: 'normal' | 'beacon' = 'normal'\n): V1PostRequest {\n return buildPostRequest(siteKey, endpoints, 'replay', request, delivery)\n}\n\nexport const buildV1FlagsRequest = (\n siteKey: string,\n endpoints: V1ValidatedEndpointSet,\n request: V1FlagsRequest\n): V1NormalPostRequest => buildNormalPostRequest(siteKey, resolvePostEndpoint(endpoints, 'flags'), request)\n","import { freezeV1Value, isNullValue, isNumberValue, isStringValue, unicodeCodePointLength } from './validation'\n\nconst RFC3339_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})[Tt](\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?(?:[Zz]|([+-])(\\d{2}):(\\d{2}))$/\nconst SECONDS_PER_DAY = 86_400\n\nexport interface V1ExactTimestamp {\n readonly source: string\n readonly epochDay: number\n readonly secondOfDay: number\n /** True when source used RFC 3339 second 60; secondOfDay names the preceding :59 second. */\n readonly leapSecond: boolean\n readonly fractionalDigits: string\n}\n\nconst fail = (field: string, message: string): never => {\n throw new Error(`${field} ${message}`)\n}\n\nconst daysInMonth = (year: number, month: number): number => {\n const date = new Date(0)\n date.setUTCFullYear(year, month, 0)\n return date.getUTCDate()\n}\n\nconst splitEpochSecond = (epochSecond: number): Readonly<{ epochDay: number; secondOfDay: number }> => {\n const epochDay = Math.floor(epochSecond / SECONDS_PER_DAY)\n return freezeV1Value({ epochDay, secondOfDay: epochSecond - epochDay * SECONDS_PER_DAY })\n}\n\nexport const parseV1ExactTimestamp = (\n value: unknown,\n field: string,\n maximumUnicodeScalars = 65_536\n): V1ExactTimestamp => {\n if (!isStringValue(value)) return fail(field, 'must be an RFC 3339 date-time string')\n const scalarLength = unicodeCodePointLength(value)\n if (isNullValue(scalarLength) || scalarLength < 1 || scalarLength > maximumUnicodeScalars) {\n return fail(field, `must contain between 1 and ${maximumUnicodeScalars} Unicode scalars`)\n }\n const match = RFC3339_PATTERN.exec(value)\n if (!match) return fail(field, 'must be an RFC 3339 date-time')\n const year = Number(match[1])\n const month = Number(match[2])\n const day = Number(match[3])\n const hour = Number(match[4])\n const minute = Number(match[5])\n const second = Number(match[6])\n const fractionalDigits = (match[7] ?? '').replace(/0+$/, '')\n const offsetHour = match[9] ? Number(match[9]) : 0\n const offsetMinute = match[10] ? Number(match[10]) : 0\n if (\n month < 1 ||\n month > 12 ||\n day < 1 ||\n day > daysInMonth(year, month) ||\n hour > 23 ||\n minute > 59 ||\n second > 60 ||\n offsetHour > 23 ||\n offsetMinute > 59\n ) {\n fail(field, 'must be a valid RFC 3339 date-time')\n }\n\n const local = new Date(0)\n local.setUTCFullYear(year, month - 1, day)\n local.setUTCHours(hour, minute, Math.min(second, 59), 0)\n const localEpochSecond = local.getTime() / 1000\n const offsetDirection = match[8] === '-' ? -1 : 1\n const offsetSeconds = offsetDirection * (offsetHour * 60 + offsetMinute) * 60\n const epochSecond = localEpochSecond - offsetSeconds\n if (!isNumberValue(epochSecond) || !Number.isSafeInteger(epochSecond)) {\n fail(field, 'is outside the supported epoch range')\n }\n\n const leapSecond = second === 60\n if (leapSecond) {\n const nextUtcSecond = new Date((epochSecond + 1) * 1000)\n const isLeapBoundary =\n nextUtcSecond.getUTCHours() === 0 &&\n nextUtcSecond.getUTCMinutes() === 0 &&\n nextUtcSecond.getUTCSeconds() === 0 &&\n nextUtcSecond.getUTCDate() === 1 &&\n (nextUtcSecond.getUTCMonth() === 0 || nextUtcSecond.getUTCMonth() === 6)\n if (!isLeapBoundary) fail(field, 'must use second 60 only at a shifted UTC June or December leap boundary')\n }\n\n const split = splitEpochSecond(epochSecond)\n return freezeV1Value({ source: value, ...split, leapSecond, fractionalDigits })\n}\n\nexport const compareV1ExactTimestamps = (left: V1ExactTimestamp, right: V1ExactTimestamp): number => {\n if (left.epochDay !== right.epochDay) return left.epochDay < right.epochDay ? -1 : 1\n if (left.secondOfDay !== right.secondOfDay) return left.secondOfDay < right.secondOfDay ? -1 : 1\n if (left.leapSecond !== right.leapSecond) return left.leapSecond ? 1 : -1\n const maximumDigits = Math.max(left.fractionalDigits.length, right.fractionalDigits.length)\n for (let index = 0; index < maximumDigits; index += 1) {\n const leftDigit = left.fractionalDigits.charCodeAt(index) || 48\n const rightDigit = right.fractionalDigits.charCodeAt(index) || 48\n if (leftDigit !== rightDigit) return leftDigit < rightDigit ? -1 : 1\n }\n return 0\n}\n\nexport const v1ExactTimestampToApproximateMs = (value: V1ExactTimestamp): number =>\n (value.epochDay * SECONDS_PER_DAY + value.secondOfDay + (value.leapSecond ? 1 : 0)) * 1000 +\n Number((value.fractionalDigits + '000').slice(0, 3))\n\nexport const assertV1WallNow = (nowMs: number, field = 'nowMs'): void => {\n if (!isNumberValue(nowMs) || !Number.isSafeInteger(nowMs) || !Number.isFinite(new Date(nowMs).getTime())) {\n fail(field, 'must be a valid integer epoch timestamp')\n }\n}\n\nexport const v1ExactTimestampFromEpochMs = (nowMs: number, field = 'nowMs'): V1ExactTimestamp => {\n assertV1WallNow(nowMs, field)\n const epochSecond = Math.floor(nowMs / 1000)\n const milliseconds = nowMs - epochSecond * 1000\n const split = splitEpochSecond(epochSecond)\n return freezeV1Value({\n source: new Date(nowMs).toISOString(),\n ...split,\n leapSecond: false,\n fractionalDigits: String(milliseconds).padStart(3, '0').replace(/0+$/, ''),\n })\n}\n\nexport const sameV1ExactTimestamp = (left: V1ExactTimestamp, right: V1ExactTimestamp): boolean =>\n compareV1ExactTimestamps(left, right) === 0\n","/* eslint-disable compat/compat, elu-client/no-direct-null-check -- Native IndexedDB is asynchronous and this runtime fails closed without Promise; its open state uses exact null checks. */\n\nimport type {\n V1DurableCommitOutcome,\n V1DurableRecordVisitor,\n V1DurableStorage,\n V1DurableStorageMode,\n V1DurableStorageTransaction,\n} from './durable-storage'\nimport { V1DurableStorageError } from './durable-storage'\nimport { freezeV1Value, isArrayValue } from './validation'\nimport { initializeV1IndexedDBConnection } from './indexeddb-initialization'\n\nexport const V1_INDEXED_DB_NAME = 'elu-sdk-v1'\n/** Schema versions of the shared database: base stores, plus the flag cache, plus the replay stores. */\nexport const V1_INDEXED_DB_VERSION = 1\nexport const V1_INDEXED_DB_FLAG_VERSION = 2\nexport const V1_INDEXED_DB_REPLAY_VERSION = 3\n\nexport const V1_INSTALLATIONS_STORE = 'installations'\nexport const V1_RECORDS_STORE = 'records'\nexport const V1_FLAG_CACHE_STORE = 'flagCache'\nexport const V2_REPLAY_CHUNKS_STORE = 'replayChunks'\nexport const V2_REPLAY_STREAMS_STORE = 'replayStreams'\nexport const V2_REPLAY_RESOLUTIONS_STORE = 'replayResolutions'\n/** Unique index on [siteKey, replayId, chunkId] over live replay chunks. */\nexport const V2_REPLAY_CHUNK_INDEX = 'chunk'\n/** Unique index on [siteKey, replayId, sequence] over live chunks and over retained resolutions. */\nexport const V2_REPLAY_SEQUENCE_INDEX = 'sequence'\nconst INSTALLATIONS_STORE = V1_INSTALLATIONS_STORE\nconst RECORDS_STORE = V1_RECORDS_STORE\nconst STORE_NAMES = [INSTALLATIONS_STORE, RECORDS_STORE] as const\n\nconst requestResult = <T>(request: IDBRequest<T>): Promise<T> =>\n new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result)\n request.onerror = () => reject(request.error ?? new Error('IndexedDB request failed'))\n })\n\ntype V1IndexedDBCompletion = Readonly<{ status: 'committed' }> | Readonly<{ status: 'aborted'; error: Error }>\n\nconst transactionCompletion = (transaction: IDBTransaction): Promise<V1IndexedDBCompletion> =>\n new Promise((resolve) => {\n let observedError: Error | null = null\n transaction.oncomplete = () => resolve(freezeV1Value({ status: 'committed' }))\n transaction.onerror = () => {\n observedError = transaction.error ?? new Error('IndexedDB transaction failed')\n }\n transaction.onabort = () =>\n resolve(\n freezeV1Value({\n status: 'aborted',\n error: transaction.error ?? observedError ?? new Error('IndexedDB transaction aborted'),\n })\n )\n })\n\nconst keyPathMatches = (actual: string | string[] | null, expected: string | readonly string[]): boolean => {\n if (typeof expected === 'string') {\n return actual === expected\n }\n if (!isArrayValue(actual) || actual.length !== expected.length) {\n return false\n }\n return expected.every((part, index) => actual[index] === part)\n}\n\nclass V1NativeIndexedDBTransaction implements V1DurableStorageTransaction {\n private readonly _installations: IDBObjectStore\n private readonly _records: IDBObjectStore\n private readonly _keyRange: typeof IDBKeyRange\n\n constructor(transaction: IDBTransaction, keyRange: typeof IDBKeyRange) {\n this._installations = transaction.objectStore(INSTALLATIONS_STORE)\n this._records = transaction.objectStore(RECORDS_STORE)\n this._keyRange = keyRange\n }\n\n getInstallation(siteKey: string): Promise<unknown | undefined> {\n return requestResult(this._installations.get(siteKey))\n }\n\n async addInstallation(value: unknown): Promise<void> {\n await requestResult(this._installations.add(value))\n }\n\n async putInstallation(value: unknown): Promise<void> {\n await requestResult(this._installations.put(value))\n }\n\n scanAllRecords(siteKey: string, visit: V1DurableRecordVisitor): Promise<void> {\n return this._scan(\n this._records.openCursor(this._keyRange.bound([siteKey], [siteKey, []])),\n Number.MAX_SAFE_INTEGER,\n visit\n )\n }\n\n scanRecordPrefix(\n siteKey: string,\n startSequence: number,\n maximumCount: number,\n visit: V1DurableRecordVisitor\n ): Promise<void> {\n if (maximumCount === 0) {\n return Promise.resolve()\n }\n return this._scan(\n this._records.openCursor(\n this._keyRange.bound([siteKey, startSequence], [siteKey, Number.MAX_SAFE_INTEGER])\n ),\n maximumCount,\n visit\n )\n }\n\n async addRecord(value: unknown): Promise<void> {\n await requestResult(this._records.add(value))\n }\n\n async deleteRecord(siteKey: string, sequence: number): Promise<void> {\n await requestResult(this._records.delete([siteKey, sequence]))\n }\n\n private _scan(\n request: IDBRequest<IDBCursorWithValue | null>,\n maximumCount: number,\n visit: V1DurableRecordVisitor\n ): Promise<void> {\n return new Promise((resolve, reject) => {\n let visited = 0\n request.onerror = () => reject(request.error ?? new Error('IndexedDB cursor failed'))\n request.onsuccess = () => {\n const cursor = request.result\n if (!cursor || visited >= maximumCount) {\n resolve()\n return\n }\n try {\n visited += 1\n if (!visit(cursor.value) || visited >= maximumCount) {\n resolve()\n return\n }\n cursor.continue()\n } catch (error) {\n reject(error)\n }\n }\n })\n }\n}\n\nclass V1NativeIndexedDBStorage implements V1DurableStorage {\n private readonly _database: IDBDatabase\n private readonly _keyRange: typeof IDBKeyRange\n private _closed = false\n\n constructor(database: IDBDatabase, keyRange: typeof IDBKeyRange) {\n this._database = database\n this._keyRange = keyRange\n this._database.onversionchange = () => this.close()\n }\n\n async runTransaction<T>(\n mode: V1DurableStorageMode,\n operation: (transaction: V1DurableStorageTransaction) => Promise<T>\n ): Promise<T> {\n if (this._closed) {\n throw new V1DurableStorageError('closed', 'IndexedDB storage is closed')\n }\n\n if (mode === 'readwrite') {\n const outcome = await this.runTransactionWithCommitOutcome(operation)\n if (outcome.status === 'committed') return outcome.value\n throw outcome.error\n }\n\n let transaction: IDBTransaction\n try {\n transaction = this._database.transaction([...STORE_NAMES], mode)\n } catch {\n throw new V1DurableStorageError('unavailable', 'IndexedDB transaction could not be opened')\n }\n const completion = transactionCompletion(transaction)\n try {\n const result = await operation(new V1NativeIndexedDBTransaction(transaction, this._keyRange))\n const outcome = await completion\n if (outcome.status === 'committed') return result\n throw outcome.error\n } catch (error) {\n try {\n transaction.abort()\n } catch {\n // The transaction may already have aborted or completed.\n }\n await completion\n throw error\n }\n }\n\n async runTransactionWithCommitOutcome<T>(\n operation: (transaction: V1DurableStorageTransaction) => Promise<T>\n ): Promise<V1DurableCommitOutcome<T>> {\n if (this._closed) {\n return freezeV1Value({\n status: 'proven-not-committed',\n error: new V1DurableStorageError('closed', 'IndexedDB storage is closed'),\n })\n }\n\n let transaction: IDBTransaction\n try {\n transaction = this._database.transaction([...STORE_NAMES], 'readwrite')\n } catch {\n return freezeV1Value({\n status: 'proven-not-committed',\n error: new V1DurableStorageError('unavailable', 'IndexedDB transaction could not be opened'),\n })\n }\n const completion = transactionCompletion(transaction)\n let value: T\n try {\n value = await operation(new V1NativeIndexedDBTransaction(transaction, this._keyRange))\n } catch (error) {\n try {\n transaction.abort()\n } catch {\n // A transaction that completed while the callback awaited unrelated work is ambiguous.\n }\n const completed = await completion\n const normalized = error instanceof Error ? error : new Error('IndexedDB operation failed')\n return completed.status === 'aborted'\n ? freezeV1Value({ status: 'proven-not-committed', error: normalized })\n : freezeV1Value({ status: 'unknown', error: normalized })\n }\n\n const completed = await completion\n return completed.status === 'committed'\n ? freezeV1Value({ status: 'committed', value })\n : freezeV1Value({ status: 'proven-not-committed', error: completed.error })\n }\n\n close(): void {\n if (!this._closed) {\n this._closed = true\n this._database.close()\n }\n }\n}\n\nexport type V1IndexedDBSchema = 'base' | 'flags' | 'replay'\n\nexport interface V1OpenIndexedDBDatabaseOptions {\n /** The schema the caller needs; the database is upgraded to that version only when it is older. */\n readonly schema: V1IndexedDBSchema\n /** Lazy owners may inspect an existing schema, but must never close active peers with an upgrade. */\n readonly upgradePolicy?: 'allow' | 'require-existing'\n readonly factory?: IDBFactory | null\n /** How long a version upgrade may stay blocked by other connections before it fails closed. */\n readonly upgradeTimeoutMs?: number\n}\n\ninterface V1IndexedDBIndexDefinition {\n readonly name: string\n readonly keyPath: readonly string[]\n readonly unique: boolean\n}\n\ninterface V1IndexedDBStoreDefinition {\n readonly name: string\n readonly keyPath: string | readonly string[]\n /** The schema version that introduces the store. */\n readonly version: number\n readonly indexes: readonly V1IndexedDBIndexDefinition[]\n}\n\nconst SCHEMA_VERSIONS: Readonly<Record<V1IndexedDBSchema, number>> = freezeV1Value({\n base: V1_INDEXED_DB_VERSION,\n flags: V1_INDEXED_DB_FLAG_VERSION,\n replay: V1_INDEXED_DB_REPLAY_VERSION,\n})\n\nconst STORE_DEFINITIONS: readonly V1IndexedDBStoreDefinition[] = freezeV1Value([\n { name: INSTALLATIONS_STORE, keyPath: 'siteKey', version: 1, indexes: [] },\n { name: RECORDS_STORE, keyPath: ['siteKey', 'sequence'], version: 1, indexes: [] },\n { name: V1_FLAG_CACHE_STORE, keyPath: ['siteNamespaceDigest', 'recordKind'], version: 2, indexes: [] },\n {\n name: V2_REPLAY_CHUNKS_STORE,\n keyPath: ['siteKey', 'enqueueOrdinal'],\n version: 3,\n indexes: [\n { name: V2_REPLAY_CHUNK_INDEX, keyPath: ['siteKey', 'replayId', 'chunkId'], unique: true },\n { name: V2_REPLAY_SEQUENCE_INDEX, keyPath: ['siteKey', 'replayId', 'sequence'], unique: true },\n ],\n },\n { name: V2_REPLAY_STREAMS_STORE, keyPath: ['siteKey', 'replayId'], version: 3, indexes: [] },\n {\n name: V2_REPLAY_RESOLUTIONS_STORE,\n keyPath: ['siteKey', 'replayId', 'chunkId'],\n version: 3,\n indexes: [{ name: V2_REPLAY_SEQUENCE_INDEX, keyPath: ['siteKey', 'replayId', 'sequence'], unique: true }],\n },\n])\n\nconst DEFAULT_UPGRADE_TIMEOUT_MS = 5_000\nconst MAXIMUM_UPGRADE_TIMEOUT_MS = 60_000\n\nconst storeKeyPath = (definition: V1IndexedDBStoreDefinition): string | string[] =>\n typeof definition.keyPath === 'string' ? definition.keyPath : [...definition.keyPath]\n\nconst storesAtVersion = (version: number): readonly V1IndexedDBStoreDefinition[] =>\n STORE_DEFINITIONS.filter((store) => store.version <= version)\n\nconst unsupported = (message: string): V1DurableStorageError => new V1DurableStorageError('unsupported', message)\n\nconst storeNames = (names: DOMStringList): string[] => {\n const result: string[] = []\n for (let index = 0; index < names.length; index += 1) {\n const name = names.item(index)\n if (name !== null) result.push(name)\n }\n return result.sort()\n}\n\n/**\n * Checks that the stores the transaction can see are exactly the stores of the given schema version,\n * with the expected key paths and indexes. Used both on an open connection and inside an upgrade\n * transaction, where it proves the pre-upgrade schema before anything is added.\n */\nconst validateStores = (\n names: DOMStringList,\n objectStore: (name: string) => IDBObjectStore,\n version: number,\n what: string\n): void => {\n const expected = storesAtVersion(version)\n const actual = storeNames(names)\n const expectedNames = expected.map((store) => store.name).sort()\n if (actual.length !== expectedNames.length || actual.some((name, index) => name !== expectedNames[index])) {\n throw unsupported(`IndexedDB ${what} schema does not match version ${version}`)\n }\n for (const definition of expected) {\n const store = objectStore(definition.name)\n if (!keyPathMatches(store.keyPath, definition.keyPath)) {\n throw unsupported(`IndexedDB ${what} key paths are incompatible`)\n }\n const indexNames = storeNames(store.indexNames)\n const expectedIndexNames = definition.indexes.map((index) => index.name).sort()\n if (\n indexNames.length !== expectedIndexNames.length ||\n indexNames.some((name, position) => name !== expectedIndexNames[position])\n ) {\n throw unsupported(`IndexedDB ${what} indexes are incompatible`)\n }\n for (const definitionIndex of definition.indexes) {\n const index = store.index(definitionIndex.name)\n if (!keyPathMatches(index.keyPath, definitionIndex.keyPath) || index.unique !== definitionIndex.unique) {\n throw unsupported(`IndexedDB ${what} index key paths are incompatible`)\n }\n }\n }\n}\n\n/** Validates a connection against the closed schema of its own version; any other version fails closed. */\nexport const validateV1IndexedDBSchema = (database: IDBDatabase): void => {\n if (\n database.version !== V1_INDEXED_DB_VERSION &&\n database.version !== V1_INDEXED_DB_FLAG_VERSION &&\n database.version !== V1_INDEXED_DB_REPLAY_VERSION\n ) {\n throw unsupported('IndexedDB schema version is unsupported')\n }\n const expectedNames = storesAtVersion(database.version).map((store) => store.name)\n if (storeNames(database.objectStoreNames).join(',') !== [...expectedNames].sort().join(',')) {\n throw unsupported(`IndexedDB schema does not match version ${database.version}`)\n }\n let transaction: IDBTransaction\n try {\n transaction = database.transaction(expectedNames, 'readonly')\n } catch {\n throw unsupported('IndexedDB stores cannot be opened')\n }\n try {\n validateStores(database.objectStoreNames, (name) => transaction.objectStore(name), database.version, 'stored')\n } catch (error) {\n try {\n transaction.abort()\n } catch {\n // A completed read transaction needs no cleanup.\n }\n throw error\n }\n}\n\nconst applyUpgrade = (\n database: IDBDatabase,\n transaction: IDBTransaction,\n oldVersion: number,\n newVersion: number\n): void => {\n if (\n (oldVersion !== V1_INDEXED_DB_VERSION && oldVersion !== V1_INDEXED_DB_FLAG_VERSION) ||\n newVersion <= oldVersion ||\n newVersion > V1_INDEXED_DB_REPLAY_VERSION\n ) {\n throw unsupported('IndexedDB upgrade path is unsupported')\n }\n validateStores(database.objectStoreNames, (name) => transaction.objectStore(name), oldVersion, 'pre-upgrade')\n for (const definition of STORE_DEFINITIONS) {\n if (definition.version <= oldVersion || definition.version > newVersion) continue\n const store = database.createObjectStore(definition.name, { keyPath: storeKeyPath(definition) })\n for (const index of definition.indexes) {\n store.createIndex(index.name, [...index.keyPath], { unique: index.unique })\n }\n }\n}\n\nconst resolveFactory = (factory: IDBFactory | null | undefined): IDBFactory | undefined =>\n typeof factory === 'undefined'\n ? typeof globalThis === 'undefined'\n ? undefined\n : globalThis.indexedDB\n : (factory ?? undefined)\n\ntype V1InspectOutcome =\n | Readonly<{ status: 'ready'; database: IDBDatabase }>\n | Readonly<{ status: 'older'; version: number }>\n\n/**\n * Opens the database without a version and reports whether it already satisfies the requested schema.\n * A missing database is created at version 1 for the base schema only; the flag and replay schemas\n * need an installation to exist first, so they abort the creation and fail closed. Inspection never\n * requests a version change, so a blocked open here is unexpected and fails immediately.\n */\nconst inspectDatabase = (\n factory: IDBFactory,\n schema: V1IndexedDBSchema,\n allowCreation = true\n): Promise<V1InspectOutcome> =>\n new Promise((resolve, reject) => {\n let request: IDBOpenDBRequest\n try {\n request = factory.open(V1_INDEXED_DB_NAME)\n } catch {\n reject(new V1DurableStorageError('unavailable', 'IndexedDB open was denied'))\n return\n }\n let settled = false\n let upgradeFailure: V1DurableStorageError | null = null\n const rejectOnce = (error: V1DurableStorageError): void => {\n if (!settled) {\n settled = true\n reject(error)\n }\n }\n request.onupgradeneeded = (event) => {\n const database = request.result\n if (event.oldVersion !== 0 || schema !== 'base' || !allowCreation) {\n upgradeFailure = unsupported(\n schema === 'base'\n ? 'IndexedDB schema version is unsupported'\n : 'IndexedDB has no base schema to extend'\n )\n request.transaction?.abort()\n return\n }\n if (database.objectStoreNames.length !== 0) {\n upgradeFailure = unsupported('IndexedDB contains an unexpected v1 schema')\n request.transaction?.abort()\n return\n }\n for (const definition of storesAtVersion(V1_INDEXED_DB_VERSION)) {\n database.createObjectStore(definition.name, { keyPath: storeKeyPath(definition) })\n }\n }\n request.onblocked = () => rejectOnce(new V1DurableStorageError('blocked', 'IndexedDB open is blocked'))\n request.onerror = () => {\n const code = request.error?.name === 'VersionError' ? 'unsupported' : 'unavailable'\n rejectOnce(upgradeFailure ?? new V1DurableStorageError(code, 'IndexedDB could not be opened'))\n }\n request.onsuccess = () => {\n const database = request.result\n if (settled) {\n database.close()\n return\n }\n settled = true\n try {\n validateV1IndexedDBSchema(database)\n } catch (error) {\n database.close()\n reject(\n error instanceof V1DurableStorageError ? error : unsupported('IndexedDB schema validation failed')\n )\n return\n }\n if (database.version >= SCHEMA_VERSIONS[schema]) {\n database.onversionchange = () => database.close()\n resolve(freezeV1Value({ status: 'ready', database }))\n return\n }\n database.close()\n resolve(freezeV1Value({ status: 'older', version: database.version }))\n }\n })\n\ntype V1UpgradeOutcome = Readonly<{ status: 'ready'; database: IDBDatabase }> | Readonly<{ status: 'overtaken' }>\n\n/**\n * Requests exactly the schema version the caller needs. The upgrade callback trusts oldVersion, not\n * the earlier inspection, so a concurrent lower upgrade by another connection is extended rather\n * than raced. A VersionError means another connection already moved past the requested version;\n * the caller inspects once more and accepts it when it is a known schema.\n */\nconst upgradeDatabase = (\n factory: IDBFactory,\n schema: V1IndexedDBSchema,\n upgradeTimeoutMs: number\n): Promise<V1UpgradeOutcome> =>\n new Promise((resolve, reject) => {\n const version = SCHEMA_VERSIONS[schema]\n let request: IDBOpenDBRequest\n try {\n request = factory.open(V1_INDEXED_DB_NAME, version)\n } catch {\n reject(new V1DurableStorageError('unavailable', 'IndexedDB open was denied'))\n return\n }\n let settled = false\n let upgradeFailure: V1DurableStorageError | null = null\n let blockedTimer: ReturnType<typeof setTimeout> | null = null\n const clearBlockedTimer = (): void => {\n if (blockedTimer !== null) {\n clearTimeout(blockedTimer)\n blockedTimer = null\n }\n }\n const settle = (outcome: () => void): void => {\n if (settled) return\n settled = true\n clearBlockedTimer()\n outcome()\n }\n const abortUpgrade = (): void => {\n try {\n request.transaction?.abort()\n } catch {\n // The upgrade transaction may not exist yet or may already have completed.\n }\n }\n request.onblocked = () => {\n if (settled || blockedTimer !== null) return\n blockedTimer = setTimeout(() => {\n abortUpgrade()\n settle(() => reject(new V1DurableStorageError('blocked', 'IndexedDB upgrade remained blocked')))\n }, upgradeTimeoutMs)\n }\n request.onupgradeneeded = (event) => {\n clearBlockedTimer()\n if (settled) {\n // The caller already gave up; a late upgrade must not change the schema behind it.\n abortUpgrade()\n return\n }\n try {\n const transaction = request.transaction ?? fail(unsupported('IndexedDB upgrade transaction is missing'))\n applyUpgrade(request.result, transaction, event.oldVersion, event.newVersion ?? version)\n } catch (error) {\n upgradeFailure =\n error instanceof V1DurableStorageError ? error : unsupported('IndexedDB upgrade failed')\n abortUpgrade()\n }\n }\n request.onerror = () => {\n if (upgradeFailure === null && request.error?.name === 'VersionError') {\n settle(() => resolve(freezeV1Value({ status: 'overtaken' })))\n return\n }\n settle(() =>\n reject(upgradeFailure ?? new V1DurableStorageError('unavailable', 'IndexedDB could not be opened'))\n )\n }\n request.onsuccess = () => {\n const database = request.result\n if (settled) {\n database.close()\n return\n }\n try {\n validateV1IndexedDBSchema(database)\n } catch (error) {\n database.close()\n settle(() =>\n reject(\n error instanceof V1DurableStorageError\n ? error\n : unsupported('IndexedDB schema validation failed')\n )\n )\n return\n }\n database.onversionchange = () => database.close()\n settle(() => resolve(freezeV1Value({ status: 'ready', database })))\n }\n })\n\nfunction fail(error: V1DurableStorageError): never {\n throw error\n}\n\n/**\n * The one opener every internal owner of `elu-sdk-v1` goes through. It inspects the current version\n * first, without requesting one, and upgrades only when the database is older than the schema the\n * caller needs: v1 to v2 adds the flag cache, v1 or v2 to v3 adds the replay stores, and every\n * existing store keeps its rows. It never requests a lower version, a future or foreign schema fails\n * closed, and a blocked upgrade or an unexpected version change leaves the database untouched.\n */\nexport const openV1IndexedDBDatabase = async (options: V1OpenIndexedDBDatabaseOptions): Promise<IDBDatabase> => {\n const schema = options.schema\n if (schema !== 'base' && schema !== 'flags' && schema !== 'replay') {\n throw new V1DurableStorageError('unavailable', 'IndexedDB schema request is invalid')\n }\n const upgradePolicy = options.upgradePolicy ?? 'allow'\n if (upgradePolicy !== 'allow' && upgradePolicy !== 'require-existing') {\n throw new V1DurableStorageError('unavailable', 'IndexedDB upgrade policy is invalid')\n }\n const upgradeTimeoutMs = options.upgradeTimeoutMs ?? DEFAULT_UPGRADE_TIMEOUT_MS\n if (\n !Number.isSafeInteger(upgradeTimeoutMs) ||\n upgradeTimeoutMs < 1 ||\n upgradeTimeoutMs > MAXIMUM_UPGRADE_TIMEOUT_MS\n ) {\n throw new V1DurableStorageError('unavailable', 'IndexedDB upgrade timeout is invalid')\n }\n const factory = resolveFactory(options.factory)\n if (!factory) {\n throw new V1DurableStorageError('unavailable', 'IndexedDB is unavailable')\n }\n\n const inspected = await inspectDatabase(factory, schema, upgradePolicy === 'allow')\n if (inspected.status === 'ready') return inspected.database\n if (upgradePolicy === 'require-existing') {\n throw unsupported('IndexedDB required schema has not been prepared')\n }\n\n const upgraded = await upgradeDatabase(factory, schema, upgradeTimeoutMs)\n if (upgraded.status === 'ready') return upgraded.database\n\n const reinspected = await inspectDatabase(factory, schema)\n if (reinspected.status === 'ready') return reinspected.database\n throw unsupported('IndexedDB schema changed during upgrade')\n}\n\n/**\n * Initializes an owner after overlapping opens have settled. The connection lifecycle helper keeps\n * its version-change observer independent of the owner's handler and only retries proven loss.\n */\nexport const initializeV1IndexedDBDatabase = async <T>(\n options: V1OpenIndexedDBDatabaseOptions,\n initialize: (database: IDBDatabase) => Promise<T>,\n lostConnection: (error: unknown) => boolean\n): Promise<T> => {\n const factory = resolveFactory(options.factory)\n if (!factory) throw new V1DurableStorageError('unavailable', 'IndexedDB is unavailable')\n return initializeV1IndexedDBConnection({\n factory,\n open: () => openV1IndexedDBDatabase({ ...options, factory }),\n initialize,\n lostConnection,\n })\n}\n\nexport const openV1IndexedDBStorage = async (\n factory?: IDBFactory | null,\n keyRangeFactory?: typeof IDBKeyRange | null\n): Promise<V1DurableStorage> => {\n const indexedDBKeyRange =\n typeof keyRangeFactory === 'undefined'\n ? typeof globalThis === 'undefined'\n ? undefined\n : globalThis.IDBKeyRange\n : (keyRangeFactory ?? undefined)\n if (!resolveFactory(factory) || !indexedDBKeyRange) {\n throw new V1DurableStorageError('unavailable', 'IndexedDB is unavailable')\n }\n const database = await openV1IndexedDBDatabase({ schema: 'base', factory })\n return new V1NativeIndexedDBStorage(database, indexedDBKeyRange)\n}\n","import { isStringValue } from '../validation'\nimport type { V2ReplaySha256, V2Sha256Hash } from './types'\n\nconst SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/\n\n/** Lowercase-hex SHA-256 over the Web Crypto API; null when no digest provider is available. */\nexport const defaultV2ReplaySha256: V2ReplaySha256 = async (value) => {\n const cryptoApi = typeof globalThis === 'undefined' ? undefined : globalThis.crypto\n if (!cryptoApi?.subtle) {\n return null\n }\n try {\n const digest = await cryptoApi.subtle.digest('SHA-256', value)\n let hexadecimal = ''\n for (const byte of new Uint8Array(digest)) {\n hexadecimal += byte.toString(16).padStart(2, '0')\n }\n return hexadecimal\n } catch {\n return null\n }\n}\n\nexport const sha256Hex = async (value: Uint8Array, sha256: V2ReplaySha256 = defaultV2ReplaySha256): Promise<string> => {\n const digest = await sha256(value)\n if (!isStringValue(digest) || !SHA256_HEX_PATTERN.test(digest.toLowerCase())) {\n throw new Error('SHA-256 digest generation is unavailable')\n }\n return digest.toLowerCase()\n}\n\nexport const isV2Sha256Hash = (value: unknown): value is V2Sha256Hash =>\n isStringValue(value) && /^sha256:[a-f0-9]{64}$/.test(value)\n\nexport const utf8Encode = (value: string): Uint8Array => new TextEncoder().encode(value)\n\n/** Strict UTF-8 decode; malformed bytes throw rather than becoming replacement characters. */\nexport const utf8Decode = (value: Uint8Array): string => new TextDecoder('utf-8', { fatal: true }).decode(value)\n","/* eslint-disable compat/compat -- Durable storage requires native Promise and IndexedDB support. */\n\nimport type {\n V1BatchRecord,\n V1Event,\n V1IdentityReference,\n V1JsonObject,\n V1JsonValue,\n V1Mutation,\n V1MutationChange,\n V1MutationSubject,\n V1VersionContext,\n} from './types'\nimport type {\n V1DurableCommitOutcome,\n V1DurableStorage,\n V1DurableStorageFactory,\n V1DurableStorageTransaction,\n} from './durable-storage'\nimport { V1DurableStorageError } from './durable-storage'\nimport type { V1CaptureCandidateEvaluation, V1Sha256 } from './config-manager'\nimport { V1ConfigManager } from './config-manager'\nimport { openV1IndexedDBStorage } from './indexeddb-storage'\nimport { initialV2ReplayInstallationCounters, validateV2ReplayInstallationCounters } from './replay/installation'\nimport type { V2ReplayInstallationCounters } from './replay/installation'\nimport { v1FlagJsonFromUnknown } from './feature-flags/json'\nimport type { V1FlagEvaluationContext } from './feature-flags/types'\nimport {\n freezeV1Value,\n hasOnlyKeys,\n hasOwn,\n hasOwnIndex,\n isArrayValue,\n isBooleanValue,\n isFunctionValue,\n isNonNegativeInteger,\n isNullValue,\n isNumberValue,\n isRecordValue,\n isStringValue,\n parseBoundedJson,\n unicodeCodePointLength,\n utf8ByteLength,\n} from './validation'\n\nexport const V1_DURABLE_STORAGE_SCHEMA_VERSION = 1\nexport const V1_MAX_DURABLE_QUEUE_COUNT = 10_000\nexport const V1_MAX_DURABLE_QUEUE_BYTES = 268_435_456\n\nconst V1_MAX_CAPTURE_PROPERTY_COUNT = 256\nconst V1_MAX_CAPTURE_PROPERTIES_BYTES = 1_048_576\n\nconst IDENTITY_KEYS = [\n 'schemaVersion',\n 'revision',\n 'contextRevision',\n 'anonymousId',\n 'userId',\n 'groups',\n 'superProperties',\n 'session',\n 'optedOut',\n 'updatedAt',\n 'migration',\n 'flagContext',\n] as const\nconst IDENTITY_REQUIRED_KEYS = IDENTITY_KEYS.filter((key) => key !== 'migration' && key !== 'flagContext')\nconst SESSION_KEYS = [\n 'id',\n 'startedAt',\n 'lastActivityAt',\n 'timeoutSeconds',\n 'maximumDurationSeconds',\n 'lifecycle',\n 'backgroundedAt',\n] as const\nconst MIGRATION_KEYS = ['sourceSchema', 'completedAt'] as const\nconst INSTALLATION_KEYS = [\n 'storageSchemaVersion',\n 'siteKey',\n 'identity',\n 'streamId',\n 'nextSequence',\n 'headSequence',\n 'queuedCount',\n 'queuedBytes',\n 'authorityBarrier',\n 'flagAuthorityInitialized',\n 'replay',\n] as const\nconst INSTALLATION_REQUIRED_KEYS = INSTALLATION_KEYS.filter(\n (key) => key !== 'authorityBarrier' && key !== 'flagAuthorityInitialized' && key !== 'replay'\n)\nconst FLAG_CONTEXT_KEYS = ['schemaVersion', 'personProperties', 'groupProperties'] as const\nconst AUTHORITY_BARRIER_KEYS = [\n 'kind',\n 'terminalScope',\n 'configIssuedAt',\n 'configSemanticHash',\n 'policySourceHash',\n 'contextRevision',\n 'decisionHash',\n] as const\nconst AUTHORITY_BARRIER_REQUIRED_KEYS = AUTHORITY_BARRIER_KEYS.filter((key) => key !== 'terminalScope')\nconst STORED_RECORD_KEYS = [\n 'storageSchemaVersion',\n 'siteKey',\n 'streamId',\n 'sequence',\n 'kind',\n 'recordId',\n 'captureVersions',\n 'payloadJson',\n 'payloadBytes',\n] as const\nconst EVENT_KEYS = [\n 'schemaVersion',\n 'eventId',\n 'streamId',\n 'sequence',\n 'contextRevision',\n 'kind',\n 'name',\n 'occurredAt',\n 'identity',\n 'sessionId',\n 'properties',\n 'groups',\n 'versions',\n] as const\nconst EVENT_DRAFT_KEYS = ['kind', 'name', 'occurredAt', 'expectedSessionId', 'properties', 'versions'] as const\nconst MUTATION_KEYS = ['mutationId', 'sequence', 'contextRevision', 'occurredAt', 'subject', 'change'] as const\nconst MUTATION_DRAFT_KEYS = ['occurredAt', 'change', 'versions'] as const\nconst IDENTITY_REFERENCE_KEYS = ['anonymousId', 'userId', 'revision'] as const\nconst MUTATION_SUBJECT_KEYS = ['anonymousId', 'userId', 'identityRevision'] as const\nconst VERSION_KEYS = ['schemaVersion', 'contractVersion', 'platform', 'runtime', 'facade', 'build'] as const\nconst VERSION_REQUIRED_KEYS = VERSION_KEYS.filter((key) => key !== 'build')\nconst RUNTIME_VERSION_KEYS = ['name', 'version'] as const\nconst FACADE_VERSION_KEYS = ['name', 'version'] as const\nconst ACKNOWLEDGEMENT_KEYS = ['siteKey', 'streamId', 'records'] as const\nconst SESSION_UPDATE_KEYS = ['type', 'expectedCurrentSessionId', 'session'] as const\nconst ACKNOWLEDGEMENT_REFERENCE_KEYS = ['sequence', 'kind', 'recordId'] as const\nconst RFC3339_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})[Tt](\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?(?:[Zz]|([+-])(\\d{2}):(\\d{2}))$/\nconst SHA256_PATTERN = /^sha256:[a-f0-9]{64}$/\nconst RUNTIME_NAME_PATTERN = /^elu-[a-z0-9-]+$/\nconst FACADE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9._-]+$/\n\nexport interface V1PersistedSessionState {\n readonly id: string\n readonly startedAt: string\n readonly lastActivityAt: string\n readonly timeoutSeconds: number\n readonly maximumDurationSeconds: 86_400\n readonly lifecycle: 'active' | 'background'\n readonly backgroundedAt: string | null\n}\n\nexport interface V1PersistedMigrationState {\n readonly sourceSchema: string\n readonly completedAt: string\n}\n\nexport interface V1PersistedIdentityState {\n readonly schemaVersion: 1\n readonly revision: number\n readonly contextRevision: number\n readonly anonymousId: string\n readonly userId: string | null\n readonly groups: Readonly<Record<string, string>>\n readonly superProperties: V1JsonObject\n readonly session: V1PersistedSessionState | null\n readonly optedOut: boolean\n readonly updatedAt: string\n readonly migration?: V1PersistedMigrationState\n readonly flagContext?: V1PersistedFlagContext\n}\n\nexport interface V1PersistedFlagContext {\n readonly schemaVersion: 1\n readonly personProperties: V1JsonObject\n readonly groupProperties: Readonly<Record<string, V1JsonObject>>\n}\n\nexport interface V1DurableEventDraft {\n readonly kind: V1Event['kind']\n readonly name: string\n readonly occurredAt: string\n /** Causality assertion only; the persisted session remains authoritative. */\n readonly expectedSessionId: string\n readonly properties: V1JsonObject\n readonly versions: V1VersionContext\n}\n\nexport interface V1DurableMutationDraft {\n readonly occurredAt: string\n readonly change: V1MutationChange\n readonly versions: V1VersionContext\n}\n\nexport type V1DurableEventSessionUpdate =\n | Readonly<{ type: 'preserve' }>\n | Readonly<{\n type: 'replace'\n /** Compare-and-swap assertion for the transaction-current pre-transition session. */\n expectedCurrentSessionId: string | null\n session: V1PersistedSessionState\n }>\n\nexport type V1DurableLocalChange =\n | Readonly<{ type: 'setOptedOut'; optedOut: boolean; occurredAt: string }>\n | Readonly<{ type: 'resetGroups'; occurredAt: string }>\n | Readonly<{ type: 'resetIdentity'; anonymousId: string; occurredAt: string }>\n | Readonly<{\n type: 'setSuperProperties'\n set: V1JsonObject\n setOnce: V1JsonObject\n unset: readonly string[]\n occurredAt: string\n }>\n | Readonly<{\n type: 'replaceSession'\n expectedCurrentSessionId: string | null\n session: V1PersistedSessionState\n }>\n | Readonly<{ type: 'clearSession'; expectedCurrentSessionId: string | null; occurredAt: string }>\n /** Replaces the persisted flag evaluation context; a null member leaves that member unchanged. */\n | Readonly<{\n type: 'setFlagContext'\n personProperties: V1JsonObject | null\n groupProperties: Readonly<Record<string, V1JsonObject>> | null\n /** Merges the person properties into the transaction-current member instead of replacing it. */\n mergePersonProperties?: boolean\n /** Merges each named group type into the transaction-current member instead of replacing it. */\n mergeGroupProperties?: boolean\n /** Group types dropped from the transaction-current member before the group properties apply. */\n unsetGroupTypes?: readonly string[]\n occurredAt: string\n }>\n\nexport interface V1DurableRecordReference {\n readonly sequence: number\n readonly kind: 'event' | 'mutation'\n readonly recordId: string\n}\n\nexport interface V1DurableQueueRecord extends V1DurableRecordReference {\n readonly siteKey: string\n readonly streamId: string\n /** Capture-time SDK/facade metadata; deliberately excluded from the canonical payload bytes. */\n readonly captureVersions: V1VersionContext\n readonly batchRecord: V1BatchRecord\n readonly payloadJson: string\n /** Exact canonical V1BatchRecord bytes. Request-envelope overhead is intentionally separate. */\n readonly payloadBytes: number\n}\n\nexport interface V1DurableStateSnapshot {\n readonly siteKey: string\n readonly identity: V1PersistedIdentityState\n readonly streamId: string\n readonly nextSequence: number\n readonly headSequence: number\n readonly queuedCount: number\n readonly queuedBytes: number\n}\n\nexport interface V1DurableCaptureState {\n readonly state: V1DurableStateSnapshot\n readonly authorized: boolean\n /** Validated transaction observation; later replay writes must compare it to their current row. */\n readonly authorityBarrier: V1InstallationRow['authorityBarrier']\n}\n\nexport interface V1CaptureAdmissionDecision {\n readonly authorized: boolean\n /** A clock-terminal barrier must commit in the caller's current transaction before it returns. */\n readonly terminalInstallation: V1InstallationRow | null\n}\n\n/** Synchronous so replay can check authority inside its existing owner-row transaction. */\nexport type V1CaptureAdmissionGuard = (installation: unknown) => V1CaptureAdmissionDecision\n\nexport interface V1DurableAppendResult {\n readonly state: V1DurableStateSnapshot\n readonly records: readonly V1DurableQueueRecord[]\n}\n\nexport interface V1DurableRuntimeLimits {\n readonly maxCount: number\n readonly maxBytes: number\n}\n\nexport interface V1DurableRuntimeOptions {\n readonly siteKey: string\n readonly limits: V1DurableRuntimeLimits\n readonly storageFactory?: V1DurableStorageFactory\n readonly captureRuntime?: V1CaptureRuntimeOptions\n}\n\nexport interface V1CaptureRuntimeClock {\n readonly wallNow: () => string\n readonly monotonicNowMs: () => number\n}\n\nexport interface V1CaptureRuntimeOptions {\n readonly sha256?: V1Sha256\n readonly clock?: V1CaptureRuntimeClock\n readonly createSessionId?: () => string\n}\n\nexport interface V1CaptureActivityCommand {\n /** Session-eligible activity kinds; diagnostics never touch the capture session. */\n readonly kind: 'capture' | 'screen' | 'exception'\n readonly name: string\n readonly occurredAt: string\n readonly properties: V1JsonObject\n readonly versions: V1VersionContext\n}\n\nexport type V1CaptureAuthorityUpdateResult =\n | Readonly<{ status: 'authorized'; ownerEpoch: number }>\n | Readonly<{ status: 'terminal'; reason: string }>\n | Readonly<{ status: 'stale'; reason: string }>\n\nexport interface V1CaptureLifecycleWindowTarget {\n addEventListener(type: string, listener: () => void): void\n removeEventListener(type: string, listener: () => void): void\n}\n\nexport interface V1CaptureLifecycleDocumentTarget extends V1CaptureLifecycleWindowTarget {\n readonly hidden: boolean\n}\n\nexport interface V1DurableInitialState {\n readonly identity: V1PersistedIdentityState\n readonly streamId: string\n}\n\nexport interface V1DurableAcknowledgement {\n readonly siteKey: string\n readonly streamId: string\n readonly records: readonly V1DurableRecordReference[]\n}\n\nexport type V1DurableStateErrorCode =\n | 'invalid-input'\n | 'not-initialized'\n | 'queue-full'\n | 'head-record-too-large'\n | 'sequence-exhausted'\n | 'corrupt-storage'\n | 'unsupported-storage'\n | 'capture-not-authorized'\n | 'commit-unknown'\n\nexport class V1DurableStateError extends Error {\n readonly code: V1DurableStateErrorCode\n\n constructor(code: V1DurableStateErrorCode, message: string) {\n super(message)\n this.name = 'V1DurableStateError'\n this.code = code\n }\n}\n\nclass V1CaptureConfigTerminalError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'V1CaptureConfigTerminalError'\n }\n}\n\nexport interface V1InstallationRow extends V1DurableStateSnapshot {\n readonly storageSchemaVersion: 1\n readonly authorityBarrier: V1PersistedAuthorityBarrier | null\n readonly flagAuthorityInitialized?: true\n /** Present once the replay store has activated; counted with the event queue at admission. */\n readonly replay?: V2ReplayInstallationCounters\n}\n\ninterface V1PersistedAuthorityBarrier {\n readonly kind: 'authorized' | 'terminal'\n readonly terminalScope: 'config' | 'context' | null\n readonly configIssuedAt: string | null\n readonly configSemanticHash: `sha256:${string}` | null\n readonly policySourceHash: `sha256:${string}` | null\n readonly contextRevision: number | null\n readonly decisionHash: `sha256:${string}` | null\n}\n\ninterface V1CaptureSourceClock {\n readonly sourceToken: object\n readonly expiresAt: string\n readonly installedWallAt: string\n readonly monotonicStartedAtUs: number\n readonly monotonicBudgetUs: number\n}\n\n/** Owner-local issuance age survives executable identity and lifecycle replacement. */\ninterface V1CaptureSourceIssuance {\n readonly token: object\n readonly configIssuedAt: string\n readonly configSemanticHash: `sha256:${string}`\n readonly installedWallAt: string\n readonly observedWallFloor: string\n readonly monotonicStartedAtUs: number\n readonly monotonicBudgetUs: number | null\n readonly denied: boolean\n}\n\ninterface V1ExecutableCaptureAuthority extends V1CaptureSourceClock {\n readonly ownerEpoch: number\n readonly barrier: V1PersistedAuthorityBarrier\n readonly configSiteId: string\n readonly streamId: string\n readonly identityRevision: number\n readonly contextRevision: number\n readonly identityOptedOut: false\n readonly lifecycleEpoch: number\n readonly idleTimeoutSeconds: number\n readonly maximumDurationSeconds: 86_400\n}\n\ninterface V1StoredRecordRow {\n readonly storageSchemaVersion: 1\n readonly siteKey: string\n readonly streamId: string\n readonly sequence: number\n readonly kind: 'event' | 'mutation'\n readonly recordId: string\n readonly captureVersions: V1VersionContext\n readonly payloadJson: string\n readonly payloadBytes: number\n}\n\ninterface V1EncodedRecord {\n readonly stored: V1StoredRecordRow\n readonly exposed: V1DurableQueueRecord\n}\n\ntype V1NormalizedEventDraft = V1DurableEventDraft\n\ntype V1NormalizedMutationDraft = V1DurableMutationDraft\n\nconst fail = (code: V1DurableStateErrorCode, message: string): never => {\n throw new V1DurableStateError(code, message)\n}\n\nconst failInput = (message: string): never => fail('invalid-input', message)\nconst failCorrupt = (message: string): never => fail('corrupt-storage', message)\n\nconst assertExactRecord = (\n value: unknown,\n allowedKeys: readonly string[],\n requiredKeys: readonly string[],\n field: string,\n failure: (message: string) => never\n): Record<string, unknown> => {\n if (!isRecordValue(value) || Object.getPrototypeOf(value) !== Object.prototype) {\n failure(`${field} must be a plain object`)\n }\n if (!hasOnlyKeys(value, allowedKeys)) {\n failure(`${field} contains an unsupported field`)\n }\n for (const key of requiredKeys) {\n if (!hasOwn(value, key)) {\n failure(`${field}.${key} is required`)\n }\n }\n return value\n}\n\nconst assertString = (\n value: unknown,\n field: string,\n minimumLength: number,\n maximumLength: number,\n failure: (message: string) => never\n): string => {\n if (!isStringValue(value)) {\n failure(`${field} must be a string`)\n }\n const length = unicodeCodePointLength(value)\n if (isNullValue(length)) {\n failure(`${field} contains an unpaired Unicode surrogate`)\n }\n if (length < minimumLength || length > maximumLength) {\n failure(`${field} must contain between ${minimumLength} and ${maximumLength} Unicode code points`)\n }\n return value\n}\n\nconst assertIdentifier = (\n value: unknown,\n field: string,\n maximumLength: number,\n failure: (message: string) => never\n): string => assertString(value, field, 1, maximumLength, failure)\n\n// Closed field delegation retains the original validator and evaluation order.\nconst assertIdentifierField = (value: unknown, key: string, field: string, maximumLength: number, failure: (message: string) => never): string =>\n assertIdentifier(value, `${field}.${key}`, maximumLength, failure)\n\nconst assertInteger = (\n value: unknown,\n field: string,\n minimum: number,\n maximum: number,\n failure: (message: string) => never\n): number => {\n if (!isNonNegativeInteger(value) || value < minimum || value > maximum) {\n failure(`${field} must be an integer between ${minimum} and ${maximum}`)\n }\n return value\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst assertIntegerField = (value: unknown, key: string, field: string, minimum: number, maximum: number, failure: (message: string) => never): number =>\n assertInteger(value, `${field}.${key}`, minimum, maximum, failure)\n\nconst assertBoolean = (value: unknown, field: string, failure: (message: string) => never): boolean => {\n if (!isBooleanValue(value)) {\n failure(`${field} must be a boolean`)\n }\n return value\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst assertBooleanField = (value: unknown, key: string, field: string, failure: (message: string) => never): boolean =>\n assertBoolean(value, `${field}.${key}`, failure)\n\nconst daysInMonth = (year: number, month: number): number => {\n const date = new Date(0)\n date.setUTCFullYear(year, month, 0)\n return date.getUTCDate()\n}\n\ninterface V1ParsedDateTime {\n readonly source: string\n /** Ordinary POSIX second, or the preceding :59 second when leapSecond is true. */\n readonly epochSecond: number\n readonly leapSecond: boolean\n readonly fractionalDigits: string\n}\n\nconst parseDateTime = (value: unknown, field: string, failure: (message: string) => never): V1ParsedDateTime => {\n const source = assertString(value, field, 1, 128, failure)\n const match = RFC3339_PATTERN.exec(source)\n if (!match) {\n failure(`${field} must be an RFC 3339 date-time`)\n }\n const year = Number(match[1])\n const month = Number(match[2])\n const day = Number(match[3])\n const hour = Number(match[4])\n const minute = Number(match[5])\n const second = Number(match[6])\n const offsetHour = match[9] ? Number(match[9]) : 0\n const offsetMinute = match[10] ? Number(match[10]) : 0\n if (\n month < 1 ||\n month > 12 ||\n day < 1 ||\n day > daysInMonth(year, month) ||\n hour > 23 ||\n minute > 59 ||\n second > 60 ||\n offsetHour > 23 ||\n offsetMinute > 59\n ) {\n failure(`${field} must be a valid RFC 3339 date-time`)\n }\n const local = new Date(0)\n local.setUTCFullYear(year, month - 1, day)\n local.setUTCHours(hour, minute, Math.min(second, 59), 0)\n const localEpochSecond = local.getTime() / 1000\n const offsetDirection = match[8] === '-' ? -1 : 1\n const offsetSeconds = offsetDirection * (offsetHour * 60 + offsetMinute) * 60\n const epochSecond = localEpochSecond - offsetSeconds\n if (!isNumberValue(epochSecond) || !Number.isSafeInteger(epochSecond)) {\n failure(`${field} is outside the supported epoch range`)\n }\n if (second === 60) {\n const nextUtcSecond = new Date((epochSecond + 1) * 1000)\n const isLeapBoundary =\n nextUtcSecond.getUTCHours() === 0 &&\n nextUtcSecond.getUTCMinutes() === 0 &&\n nextUtcSecond.getUTCSeconds() === 0 &&\n nextUtcSecond.getUTCDate() === 1 &&\n (nextUtcSecond.getUTCMonth() === 0 || nextUtcSecond.getUTCMonth() === 6)\n if (!isLeapBoundary) {\n failure(`${field} must use second 60 only at a shifted UTC June or December leap boundary`)\n }\n }\n return freezeV1Value({\n source,\n epochSecond,\n leapSecond: second === 60,\n fractionalDigits: match[7] ?? '',\n })\n}\n\nconst assertDateTime = (value: unknown, field: string, failure: (message: string) => never): string =>\n parseDateTime(value, field, failure).source\n\n// Closed field delegation retains the original validator and evaluation order.\nconst assertDateTimeField = (value: unknown, key: string, field: string, failure: (message: string) => never): string =>\n assertDateTime(value, `${field}.${key}`, failure)\n\n/** Exact RFC 3339 comparison shared by durable state and batch delivery. */\nexport const compareV1DateTimes = (left: string, right: string): number => {\n const parsedLeft = parseDateTime(left, 'left date-time', failInput)\n const parsedRight = parseDateTime(right, 'right date-time', failInput)\n if (parsedLeft.epochSecond !== parsedRight.epochSecond) {\n return parsedLeft.epochSecond < parsedRight.epochSecond ? -1 : 1\n }\n if (parsedLeft.leapSecond !== parsedRight.leapSecond) {\n return parsedLeft.leapSecond ? 1 : -1\n }\n const width = Math.max(parsedLeft.fractionalDigits.length, parsedRight.fractionalDigits.length)\n const leftFraction = parsedLeft.fractionalDigits.padEnd(width, '0')\n const rightFraction = parsedRight.fractionalDigits.padEnd(width, '0')\n return leftFraction === rightFraction ? 0 : leftFraction < rightFraction ? -1 : 1\n}\n\n/** Exact age predicate shared by durable state and batch delivery. */\nexport const hasV1ElapsedAtLeastSeconds = (later: string, earlier: string, minimumSeconds: number): boolean => {\n const parsedLater = parseDateTime(later, 'later date-time', failInput)\n const parsedEarlier = parseDateTime(earlier, 'earlier date-time', failInput)\n const wholeSecondDifference = parsedLater.epochSecond - parsedEarlier.epochSecond + (parsedLater.leapSecond ? 1 : 0)\n if (wholeSecondDifference !== minimumSeconds) {\n return wholeSecondDifference > minimumSeconds\n }\n const width = Math.max(parsedLater.fractionalDigits.length, parsedEarlier.fractionalDigits.length)\n return parsedLater.fractionalDigits.padEnd(width, '0') >= parsedEarlier.fractionalDigits.padEnd(width, '0')\n}\n\n/**\n * Conservative whole-millisecond duration for a strict later/earlier pair. The later fractional\n * component is floored so a monotonic authorization cap never extends past exact RFC 3339 expiry.\n */\nexport const v1MillisecondsBetween = (later: string, earlier: string): number => {\n const parsedLater = parseDateTime(later, 'later date-time', failInput)\n const parsedEarlier = parseDateTime(earlier, 'earlier date-time', failInput)\n const millisecondFloor = (value: V1ParsedDateTime): number =>\n (value.epochSecond + (value.leapSecond ? 1 : 0)) * 1_000 +\n Number(value.fractionalDigits.slice(0, 3).padEnd(3, '0'))\n return millisecondFloor(parsedLater) - millisecondFloor(parsedEarlier)\n}\n\nexport const v1MicrosecondsBetween = (later: string, earlier: string): number => {\n const parsedLater = parseDateTime(later, 'later date-time', failInput)\n const parsedEarlier = parseDateTime(earlier, 'earlier date-time', failInput)\n const laterSecond = parsedLater.epochSecond + (parsedLater.leapSecond ? 1 : 0)\n const earlierSecond = parsedEarlier.epochSecond + (parsedEarlier.leapSecond ? 1 : 0)\n const secondDifference = laterSecond - earlierSecond\n if (!Number.isSafeInteger(secondDifference) || !Number.isSafeInteger(secondDifference * 1_000_000)) {\n return failInput('date-time duration exceeds the exact microsecond range')\n }\n const laterMicroseconds = Number(parsedLater.fractionalDigits.slice(0, 6).padEnd(6, '0'))\n const earlierMicroseconds = Number(parsedEarlier.fractionalDigits.slice(0, 6).padEnd(6, '0'))\n const residualWidth = Math.max(\n Math.max(0, parsedLater.fractionalDigits.length - 6),\n Math.max(0, parsedEarlier.fractionalDigits.length - 6)\n )\n const laterResidual = parsedLater.fractionalDigits.slice(6).padEnd(residualWidth, '0')\n const earlierResidual = parsedEarlier.fractionalDigits.slice(6).padEnd(residualWidth, '0')\n const residualFloorAdjustment = laterResidual < earlierResidual ? -1 : 0\n const result = secondDifference * 1_000_000 + laterMicroseconds - earlierMicroseconds + residualFloorAdjustment\n return Number.isSafeInteger(result) ? result : failInput('date-time duration exceeds the exact microsecond range')\n}\n\nconst validateHash = (\n value: unknown,\n field: string,\n nullable: boolean,\n failure: (message: string) => never\n): `sha256:${string}` | null => {\n if (nullable && isNullValue(value)) return null\n const hash = assertString(value, field, 71, 71, failure)\n if (!SHA256_PATTERN.test(hash)) failure(`${field} must be a lowercase SHA-256 identity`)\n return hash as `sha256:${string}`\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst validateHashField = (value: unknown, key: string, field: string, nullable: boolean, failure: (message: string) => never): `sha256:${string}` | null =>\n validateHash(value, `${field}.${key}`, nullable, failure)\n\nconst validateAuthorityBarrier = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1PersistedAuthorityBarrier | null => {\n if (isNullValue(value)) return null\n const barrier = assertExactRecord(value, AUTHORITY_BARRIER_KEYS, AUTHORITY_BARRIER_REQUIRED_KEYS, field, failure)\n const kind =\n barrier.kind === 'authorized'\n ? 'authorized'\n : barrier.kind === 'terminal'\n ? 'terminal'\n : failure(`${field}.kind is unsupported`)\n const terminalScope = hasOwn(barrier, 'terminalScope')\n ? isNullValue(barrier.terminalScope)\n ? null\n : barrier.terminalScope === 'config' || barrier.terminalScope === 'context'\n ? barrier.terminalScope\n : failure(`${field}.terminalScope is unsupported`)\n : kind === 'terminal'\n ? 'config'\n : null\n const configIssuedAt = isNullValue(barrier.configIssuedAt)\n ? null\n : assertDateTimeField(barrier.configIssuedAt, \"configIssuedAt\", field, failure)\n const configSemanticHash = validateHashField(barrier.configSemanticHash, \"configSemanticHash\", field, true, failure)\n const policySourceHash = validateHashField(barrier.policySourceHash, \"policySourceHash\", field, true, failure)\n const contextRevision = isNullValue(barrier.contextRevision)\n ? null\n : assertIntegerField(barrier.contextRevision, \"contextRevision\", field, 0, Number.MAX_SAFE_INTEGER, failure)\n const decisionHash = validateHashField(barrier.decisionHash, \"decisionHash\", field, true, failure)\n if (isNullValue(configIssuedAt) !== isNullValue(configSemanticHash)) {\n failure(`${field} config boundary must be wholly present or absent`)\n }\n if (\n kind === 'authorized' &&\n (!configIssuedAt || !configSemanticHash || !policySourceHash || isNullValue(contextRevision) || !decisionHash)\n ) {\n failure(`${field} authorized state must contain the complete decision fingerprint`)\n }\n if ((kind === 'authorized' && !isNullValue(terminalScope)) || (kind === 'terminal' && isNullValue(terminalScope))) {\n failure(`${field} terminal scope does not match its barrier kind`)\n }\n return freezeV1Value({\n kind,\n terminalScope,\n configIssuedAt,\n configSemanticHash,\n policySourceHash,\n contextRevision,\n decisionHash,\n })\n}\n\nconst normalizeJsonValue = (\n value: unknown,\n field: string,\n failure: (message: string) => never,\n ancestors: WeakSet<object> = new WeakSet(),\n depth = 0\n): V1JsonValue => {\n if (depth > 64) {\n failure(`${field} exceeds the maximum JSON nesting depth`)\n }\n if (isNullValue(value) || isBooleanValue(value) || isStringValue(value)) {\n if (isStringValue(value) && isNullValue(unicodeCodePointLength(value))) {\n failure(`${field} contains an unpaired Unicode surrogate`)\n }\n return value\n }\n if (isNumberValue(value)) {\n return Object.is(value, -0) ? 0 : value\n }\n if (typeof value !== 'object' || isNullValue(value)) {\n failure(`${field} is outside the JSON value domain`)\n }\n if (ancestors.has(value)) {\n failure(`${field} contains a JSON cycle`)\n }\n ancestors.add(value)\n\n if (isArrayValue(value)) {\n const ownNames = Object.getOwnPropertyNames(value).filter((name) => name !== 'length')\n if (ownNames.length !== value.length) {\n failure(`${field} must be a dense JSON array without extra properties`)\n }\n const result: V1JsonValue[] = []\n for (let index = 0; index < value.length; index += 1) {\n if (!hasOwnIndex(value, index)) {\n failure(`${field} must not be sparse`)\n }\n const descriptor = Object.getOwnPropertyDescriptor(value, String(index))\n if (!descriptor || !hasOwn(descriptor as unknown as Record<string, unknown>, 'value')) {\n failure(`${field}[${index}] must be a data property`)\n }\n result.push(normalizeJsonValue(value[index], `${field}[${index}]`, failure, ancestors, depth + 1))\n }\n ancestors.delete(value)\n return freezeV1Value(result)\n }\n\n if (Object.getPrototypeOf(value) !== Object.prototype) {\n failure(`${field} must be a plain JSON object`)\n }\n const objectValue = value as Record<string, unknown>\n if (Object.getOwnPropertySymbols(value).length !== 0) {\n failure(`${field} must not contain symbol properties`)\n }\n const keys = Object.keys(value)\n if (Object.getOwnPropertyNames(value).length !== keys.length) {\n failure(`${field} must not contain hidden properties`)\n }\n const result: Record<string, V1JsonValue> = {}\n for (const key of keys) {\n if (isNullValue(unicodeCodePointLength(key))) {\n failure(`${field} contains an invalid Unicode property name`)\n }\n const descriptor = Object.getOwnPropertyDescriptor(value, key)\n if (!descriptor || !hasOwn(descriptor as unknown as Record<string, unknown>, 'value')) {\n failure(`${field}.${key} must be a data property`)\n }\n Object.defineProperty(result, key, {\n value: normalizeJsonValue(objectValue[key], `${field}.${key}`, failure, ancestors, depth + 1),\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n ancestors.delete(value)\n return freezeV1Value(result)\n}\n\nconst normalizeJsonObject = (value: unknown, field: string, failure: (message: string) => never): V1JsonObject => {\n const normalized = normalizeJsonValue(value, field, failure)\n if (!isRecordValue(normalized)) {\n failure(`${field} must be a JSON object`)\n }\n return normalized as V1JsonObject\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst normalizeJsonObjectField = (value: unknown, key: string, field: string, failure: (message: string) => never): V1JsonObject =>\n normalizeJsonObject(value, `${field}.${key}`, failure)\n\nconst validateGroups = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): Readonly<Record<string, string>> => {\n const groups = normalizeJsonObject(value, field, failure)\n const keys = Object.keys(groups)\n if (keys.length > 64) {\n failure(`${field} must not contain more than 64 groups`)\n }\n const result: Record<string, string> = {}\n for (const key of keys) {\n Object.defineProperty(result, key, {\n value: assertIdentifier(groups[key], `${field}.${key}`, 512, failure),\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n return freezeV1Value(result)\n}\n\nconst validateFlagProperties = (value: unknown, field: string, failure: (message: string) => never): V1JsonObject => {\n const properties = normalizeJsonObject(value, field, failure)\n if (Object.keys(properties).length > 1_024) failure(`${field} must not contain more than 1024 properties`)\n try {\n if (v1FlagJsonFromUnknown(properties, field).kind !== 'object') failure(`${field} must be an object`)\n } catch (error) {\n failure(error instanceof Error ? error.message : `${field} is outside the flag JSON domain`)\n }\n return properties\n}\n\nconst validateFlagContext = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1PersistedFlagContext => {\n const context = assertExactRecord(value, FLAG_CONTEXT_KEYS, FLAG_CONTEXT_KEYS, field, failure)\n if (context.schemaVersion !== 1) failure(`${field}.schemaVersion must be 1`)\n const rawGroups = normalizeJsonObjectField(context.groupProperties, \"groupProperties\", field, failure)\n const groupTypes = Object.keys(rawGroups)\n if (groupTypes.length > 64) failure(`${field}.groupProperties must not contain more than 64 group types`)\n const groupProperties: Record<string, V1JsonObject> = {}\n for (const groupType of groupTypes) {\n Object.defineProperty(groupProperties, groupType, {\n value: validateFlagProperties(rawGroups[groupType], `${field}.groupProperties.${groupType}`, failure),\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n return freezeV1Value({\n schemaVersion: 1,\n personProperties: validateFlagProperties(context.personProperties, `${field}.personProperties`, failure),\n groupProperties: freezeV1Value(groupProperties),\n })\n}\n\nconst emptyFlagContext = (): V1PersistedFlagContext =>\n freezeV1Value({\n schemaVersion: 1,\n personProperties: freezeV1Value({}),\n groupProperties: freezeV1Value({}),\n })\n\nconst flagContextHasValues = (context: V1PersistedFlagContext | undefined): boolean =>\n Boolean(\n context && (Object.keys(context.personProperties).length > 0 || Object.keys(context.groupProperties).length > 0)\n )\n\nconst validateSession = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1PersistedSessionState | null => {\n if (isNullValue(value)) {\n return null\n }\n const session = assertExactRecord(value, SESSION_KEYS, SESSION_KEYS, field, failure)\n const lifecycle = session.lifecycle\n if (lifecycle !== 'active' && lifecycle !== 'background') {\n failure(`${field}.lifecycle is unsupported`)\n }\n const backgroundedAt = isNullValue(session.backgroundedAt)\n ? null\n : assertDateTimeField(session.backgroundedAt, \"backgroundedAt\", field, failure)\n const result: V1PersistedSessionState = {\n id: assertIdentifierField(session.id, \"id\", field, 256, failure),\n startedAt: assertDateTimeField(session.startedAt, \"startedAt\", field, failure),\n lastActivityAt: assertDateTimeField(session.lastActivityAt, \"lastActivityAt\", field, failure),\n timeoutSeconds: assertIntegerField(session.timeoutSeconds, \"timeoutSeconds\", field, 60, 36_000, failure),\n maximumDurationSeconds: assertIntegerField(session.maximumDurationSeconds, \"maximumDurationSeconds\", field, 86_400, 86_400, failure) as 86_400,\n lifecycle,\n backgroundedAt,\n }\n if (compareV1DateTimes(result.lastActivityAt, result.startedAt) < 0) {\n failure(`${field}.lastActivityAt must not precede ${field}.startedAt`)\n }\n if (hasV1ElapsedAtLeastSeconds(result.lastActivityAt, result.startedAt, result.maximumDurationSeconds)) {\n failure(`${field}.lastActivityAt must precede the session maximum-duration boundary`)\n }\n if (result.lifecycle === 'active' && result.backgroundedAt) {\n failure(`${field}.backgroundedAt must be null while the session is active`)\n }\n if (result.lifecycle === 'background') {\n if (!result.backgroundedAt) {\n failure(`${field}.backgroundedAt is required while the session is backgrounded`)\n }\n if (compareV1DateTimes(result.backgroundedAt, result.lastActivityAt) < 0) {\n failure(`${field}.backgroundedAt must not precede ${field}.lastActivityAt`)\n }\n }\n return freezeV1Value(result)\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst validateSessionField = (value: unknown, key: string, field: string, failure: (message: string) => never): V1PersistedSessionState | null =>\n validateSession(value, `${field}.${key}`, failure)\n\nconst validateMigration = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1PersistedMigrationState => {\n const migration = assertExactRecord(value, MIGRATION_KEYS, MIGRATION_KEYS, field, failure)\n return freezeV1Value({\n sourceSchema: assertIdentifierField(migration.sourceSchema, \"sourceSchema\", field, 128, failure),\n completedAt: assertDateTimeField(migration.completedAt, \"completedAt\", field, failure),\n })\n}\n\nconst validateIdentity = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1PersistedIdentityState => {\n const normalized = normalizeJsonObject(value, field, failure)\n const identity = assertExactRecord(normalized, IDENTITY_KEYS, IDENTITY_REQUIRED_KEYS, field, failure)\n if (identity.schemaVersion !== 1) {\n failure(`${field}.schemaVersion must be 1`)\n }\n const userId = isNullValue(identity.userId)\n ? null\n : assertIdentifierField(identity.userId, \"userId\", field, 512, failure)\n const result: V1PersistedIdentityState = {\n schemaVersion: 1,\n revision: assertIntegerField(identity.revision, \"revision\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n contextRevision: assertIntegerField(identity.contextRevision, \"contextRevision\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n anonymousId: assertIdentifierField(identity.anonymousId, \"anonymousId\", field, 256, failure),\n userId,\n groups: validateGroups(identity.groups, `${field}.groups`, failure),\n superProperties: normalizeJsonObjectField(identity.superProperties, \"superProperties\", field, failure),\n session: validateSessionField(identity.session, \"session\", field, failure),\n optedOut: assertBooleanField(identity.optedOut, \"optedOut\", field, failure),\n updatedAt: assertDateTimeField(identity.updatedAt, \"updatedAt\", field, failure),\n ...(hasOwn(identity, 'migration')\n ? { migration: validateMigration(identity.migration, `${field}.migration`, failure) }\n : {}),\n ...(hasOwn(identity, 'flagContext')\n ? { flagContext: validateFlagContext(identity.flagContext, `${field}.flagContext`, failure) }\n : {}),\n }\n if (result.revision > result.contextRevision) {\n failure(`${field}.revision must not exceed ${field}.contextRevision`)\n }\n if (\n result.session &&\n (compareV1DateTimes(result.updatedAt, result.session.lastActivityAt) < 0 ||\n (result.session.backgroundedAt && compareV1DateTimes(result.updatedAt, result.session.backgroundedAt) < 0))\n ) {\n failure(`${field}.updatedAt must not precede session activity`)\n }\n return freezeV1Value(result)\n}\n\nconst validateIdentityReference = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1IdentityReference => {\n const identity = assertExactRecord(value, IDENTITY_REFERENCE_KEYS, IDENTITY_REFERENCE_KEYS, field, failure)\n return freezeV1Value({\n anonymousId: assertIdentifierField(identity.anonymousId, \"anonymousId\", field, 256, failure),\n userId: isNullValue(identity.userId)\n ? null\n : assertIdentifierField(identity.userId, \"userId\", field, 512, failure),\n revision: assertIntegerField(identity.revision, \"revision\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n })\n}\n\nconst validateMutationSubject = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1MutationSubject => {\n const subject = assertExactRecord(value, MUTATION_SUBJECT_KEYS, MUTATION_SUBJECT_KEYS, field, failure)\n return freezeV1Value({\n anonymousId: assertIdentifierField(subject.anonymousId, \"anonymousId\", field, 256, failure),\n userId: isNullValue(subject.userId) ? null : assertIdentifierField(subject.userId, \"userId\", field, 512, failure),\n identityRevision: assertIntegerField(subject.identityRevision, \"identityRevision\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n })\n}\n\nconst validatePropertyNames = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): readonly string[] => {\n if (!isArrayValue(value) || value.length > 256) {\n failure(`${field} must be an array of at most 256 property names`)\n }\n const names: string[] = []\n const seen = new Set<string>()\n for (let index = 0; index < value.length; index += 1) {\n if (!hasOwnIndex(value, index)) {\n failure(`${field} must not be sparse`)\n }\n const name = assertIdentifier(value[index], `${field}[${index}]`, 512, failure)\n if (seen.has(name)) {\n failure(`${field} must contain unique property names`)\n }\n seen.add(name)\n names.push(name)\n }\n return freezeV1Value(names)\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst validatePropertyNamesField = (value: unknown, key: string, field: string, failure: (message: string) => never): readonly string[] =>\n validatePropertyNames(value, `${field}.${key}`, failure)\n\nconst validateMutationChange = (\n value: unknown,\n field: string,\n failure: (message: string) => never\n): V1MutationChange => {\n const change = normalizeJsonObject(value, field, failure)\n switch (change.type) {\n case 'identify': {\n const parsed = assertExactRecord(\n change,\n ['type', 'userId', 'set', 'setOnce'],\n ['type', 'userId', 'set', 'setOnce'],\n field,\n failure\n )\n return freezeV1Value({\n type: 'identify',\n userId: assertIdentifierField(parsed.userId, \"userId\", field, 512, failure),\n set: normalizeJsonObjectField(parsed.set, \"set\", field, failure),\n setOnce: normalizeJsonObjectField(parsed.setOnce, \"setOnce\", field, failure),\n })\n }\n case 'linkAlias': {\n const parsed = assertExactRecord(\n change,\n ['type', 'aliasId', 'canonicalId'],\n ['type', 'aliasId', 'canonicalId'],\n field,\n failure\n )\n return freezeV1Value({\n type: 'linkAlias',\n aliasId: assertIdentifierField(parsed.aliasId, \"aliasId\", field, 512, failure),\n canonicalId: assertIdentifierField(parsed.canonicalId, \"canonicalId\", field, 512, failure),\n })\n }\n case 'setPersonProperties': {\n const parsed = assertExactRecord(\n change,\n ['type', 'set', 'setOnce', 'unset'],\n ['type', 'set', 'setOnce', 'unset'],\n field,\n failure\n )\n return freezeV1Value({\n type: 'setPersonProperties',\n set: normalizeJsonObjectField(parsed.set, \"set\", field, failure),\n setOnce: normalizeJsonObjectField(parsed.setOnce, \"setOnce\", field, failure),\n unset: validatePropertyNamesField(parsed.unset, \"unset\", field, failure),\n })\n }\n case 'associateGroup': {\n const parsed = assertExactRecord(\n change,\n ['type', 'groupType', 'groupKey'],\n ['type', 'groupType', 'groupKey'],\n field,\n failure\n )\n return freezeV1Value({\n type: 'associateGroup',\n groupType: assertIdentifierField(parsed.groupType, \"groupType\", field, 256, failure),\n groupKey: assertIdentifierField(parsed.groupKey, \"groupKey\", field, 512, failure),\n })\n }\n case 'setGroupProperties': {\n const parsed = assertExactRecord(\n change,\n ['type', 'groupType', 'groupKey', 'set', 'setOnce', 'unset'],\n ['type', 'groupType', 'groupKey', 'set', 'setOnce', 'unset'],\n field,\n failure\n )\n return freezeV1Value({\n type: 'setGroupProperties',\n groupType: assertIdentifierField(parsed.groupType, \"groupType\", field, 256, failure),\n groupKey: assertIdentifierField(parsed.groupKey, \"groupKey\", field, 512, failure),\n set: normalizeJsonObjectField(parsed.set, \"set\", field, failure),\n setOnce: normalizeJsonObjectField(parsed.setOnce, \"setOnce\", field, failure),\n unset: validatePropertyNamesField(parsed.unset, \"unset\", field, failure),\n })\n }\n default:\n return failure(`${field}.type is unsupported`)\n }\n}\n\nconst validateVersions = (value: unknown, field: string, failure: (message: string) => never): V1VersionContext => {\n const versions = assertExactRecord(value, VERSION_KEYS, VERSION_REQUIRED_KEYS, field, failure)\n if (versions.schemaVersion !== 1 || versions.contractVersion !== '1.0.0' || versions.platform !== 'browser') {\n failure(`${field} is not a browser v1 version context`)\n }\n const runtime = assertExactRecord(\n versions.runtime,\n RUNTIME_VERSION_KEYS,\n RUNTIME_VERSION_KEYS,\n `${field}.runtime`,\n failure\n )\n const facade = assertExactRecord(\n versions.facade,\n FACADE_VERSION_KEYS,\n FACADE_VERSION_KEYS,\n `${field}.facade`,\n failure\n )\n const runtimeName = assertIdentifier(runtime.name, `${field}.runtime.name`, V1_MAX_DURABLE_QUEUE_BYTES, failure)\n if (!RUNTIME_NAME_PATTERN.test(runtimeName)) {\n failure(`${field}.runtime.name must match the v1 runtime identifier pattern`)\n }\n const facadeName = assertIdentifier(facade.name, `${field}.facade.name`, V1_MAX_DURABLE_QUEUE_BYTES, failure)\n if (!FACADE_NAME_PATTERN.test(facadeName)) {\n failure(`${field}.facade.name must match the v1 facade identifier pattern`)\n }\n const result: V1VersionContext = {\n schemaVersion: 1,\n contractVersion: '1.0.0',\n platform: 'browser',\n runtime: freezeV1Value({\n name: runtimeName as `elu-${string}`,\n version: assertIdentifier(runtime.version, `${field}.runtime.version`, 64, failure),\n }),\n facade: freezeV1Value({\n name: facadeName,\n version: assertIdentifier(facade.version, `${field}.facade.version`, 64, failure),\n }),\n ...(hasOwn(versions, 'build')\n ? { build: assertIdentifierField(versions.build, \"build\", field, 128, failure) }\n : {}),\n }\n return freezeV1Value(result)\n}\n\n// Closed field delegation retains the original validator and evaluation order.\nconst validateVersionsField = (value: unknown, key: string, field: string, failure: (message: string) => never): V1VersionContext =>\n validateVersions(value, `${field}.${key}`, failure)\n\nconst sameVersionContext = (left: V1VersionContext, right: V1VersionContext): boolean =>\n left.schemaVersion === right.schemaVersion &&\n left.contractVersion === right.contractVersion &&\n left.platform === right.platform &&\n left.runtime.name === right.runtime.name &&\n left.runtime.version === right.runtime.version &&\n left.facade.name === right.facade.name &&\n left.facade.version === right.facade.version &&\n left.build === right.build\n\nconst validateEvent = (\n value: unknown,\n expected: V1DurableRecordReference & Readonly<{ streamId: string }>,\n field: string,\n failure: (message: string) => never\n): V1Event => {\n const event = assertExactRecord(value, EVENT_KEYS, EVENT_KEYS, field, failure)\n if (event.schemaVersion !== 1) {\n failure(`${field}.schemaVersion must be 1`)\n }\n if (\n event.eventId !== expected.recordId ||\n event.streamId !== expected.streamId ||\n event.sequence !== expected.sequence\n ) {\n failure(`${field} identity does not match its stored queue row`)\n }\n if (\n event.kind !== 'capture' &&\n event.kind !== 'screen' &&\n event.kind !== 'exception' &&\n event.kind !== 'diagnostic'\n ) {\n failure(`${field}.kind is unsupported`)\n }\n return freezeV1Value({\n schemaVersion: 1,\n eventId: assertIdentifierField(event.eventId, \"eventId\", field, 256, failure),\n streamId: assertIdentifierField(event.streamId, \"streamId\", field, 256, failure),\n sequence: assertIntegerField(event.sequence, \"sequence\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n contextRevision: assertIntegerField(event.contextRevision, \"contextRevision\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n kind: event.kind,\n name: assertIdentifierField(event.name, \"name\", field, 512, failure),\n occurredAt: assertDateTimeField(event.occurredAt, \"occurredAt\", field, failure),\n identity: validateIdentityReference(event.identity, `${field}.identity`, failure),\n sessionId: assertIdentifierField(event.sessionId, \"sessionId\", field, 256, failure),\n properties: normalizeJsonObjectField(event.properties, \"properties\", field, failure),\n groups: validateGroups(event.groups, `${field}.groups`, failure),\n versions: validateVersionsField(event.versions, \"versions\", field, failure),\n })\n}\n\nconst validateMutation = (\n value: unknown,\n expected: V1DurableRecordReference,\n field: string,\n failure: (message: string) => never\n): V1Mutation => {\n const mutation = assertExactRecord(value, MUTATION_KEYS, MUTATION_KEYS, field, failure)\n if (mutation.mutationId !== expected.recordId || mutation.sequence !== expected.sequence) {\n failure(`${field} identity does not match its stored queue row`)\n }\n return freezeV1Value({\n mutationId: assertIdentifierField(mutation.mutationId, \"mutationId\", field, 256, failure),\n sequence: assertIntegerField(mutation.sequence, \"sequence\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n contextRevision: assertIntegerField(mutation.contextRevision, \"contextRevision\", field, 0, Number.MAX_SAFE_INTEGER, failure),\n occurredAt: assertDateTimeField(mutation.occurredAt, \"occurredAt\", field, failure),\n subject: validateMutationSubject(mutation.subject, `${field}.subject`, failure),\n change: validateMutationChange(mutation.change, `${field}.change`, failure),\n })\n}\n\nconst validateInstallation = (value: unknown, siteKey: string): V1InstallationRow => {\n const installation = assertExactRecord(\n value,\n INSTALLATION_KEYS,\n INSTALLATION_REQUIRED_KEYS,\n 'stored installation',\n failCorrupt\n )\n if (installation.storageSchemaVersion !== V1_DURABLE_STORAGE_SCHEMA_VERSION) {\n fail('unsupported-storage', 'stored installation schema version is unsupported')\n }\n if (installation.siteKey !== siteKey) {\n failCorrupt('stored installation site key does not match its storage scope')\n }\n const nextSequence = assertInteger(\n installation.nextSequence,\n 'stored installation nextSequence',\n 0,\n Number.MAX_SAFE_INTEGER,\n failCorrupt\n )\n const headSequence = assertInteger(\n installation.headSequence,\n 'stored installation headSequence',\n 0,\n Number.MAX_SAFE_INTEGER,\n failCorrupt\n )\n const queuedCount = assertInteger(\n installation.queuedCount,\n 'stored installation queuedCount',\n 0,\n V1_MAX_DURABLE_QUEUE_COUNT,\n failCorrupt\n )\n if (headSequence > nextSequence || nextSequence - headSequence !== queuedCount) {\n failCorrupt('stored installation sequence counters are inconsistent')\n }\n const row: V1InstallationRow = freezeV1Value({\n storageSchemaVersion: 1,\n siteKey: assertIdentifier(installation.siteKey, 'stored installation siteKey', 128, failCorrupt),\n identity: validateIdentity(installation.identity, 'stored installation identity', failCorrupt),\n streamId: assertIdentifier(installation.streamId, 'stored installation streamId', 256, failCorrupt),\n nextSequence,\n headSequence,\n queuedCount,\n queuedBytes: assertInteger(\n installation.queuedBytes,\n 'stored installation queuedBytes',\n 0,\n V1_MAX_DURABLE_QUEUE_BYTES,\n failCorrupt\n ),\n authorityBarrier: hasOwn(installation, 'authorityBarrier')\n ? validateAuthorityBarrier(\n installation.authorityBarrier,\n 'stored installation authorityBarrier',\n failCorrupt\n )\n : null,\n ...(hasOwn(installation, 'flagAuthorityInitialized')\n ? installation.flagAuthorityInitialized === true\n ? { flagAuthorityInitialized: true as const }\n : failCorrupt('stored installation flagAuthorityInitialized must be true when present')\n : {}),\n ...(hasOwn(installation, 'replay')\n ? {\n replay: validateV2ReplayInstallationCounters(\n installation.replay,\n 'stored installation replay',\n failCorrupt,\n { siteKey: installation.siteKey, streamId: installation.streamId }\n ),\n }\n : {}),\n })\n return row\n}\n\n/** Internal bridge used by the unwired flag store to share the durable identity authority. */\nexport const validateV1FlagInstallation = (value: unknown, siteKey: string): V1InstallationRow =>\n validateInstallation(value, siteKey)\n\n/** Internal bridge used by the unwired replay store to read the shared installation row. */\nexport const validateV1ReplayInstallation = (value: unknown, siteKey: string): V1InstallationRow =>\n validateInstallation(value, siteKey)\n\n/** Adds only the zeroed replay counters; every other installation field is preserved. */\nexport const initializeV1ReplayInstallation = (value: unknown, siteKey: string): V1InstallationRow => {\n const current = validateInstallation(value, siteKey)\n return current.replay\n ? current\n : validateInstallation({ ...current, replay: initialV2ReplayInstallationCounters() }, siteKey)\n}\n\n/** Adds only the lazy empty flag context and marker; all queue/session fields are preserved. */\nexport const initializeV1FlagInstallation = (value: unknown, siteKey: string): V1InstallationRow => {\n const current = validateInstallation(value, siteKey)\n return validateInstallation(\n {\n ...current,\n identity: current.identity.flagContext\n ? current.identity\n : freezeV1Value({ ...current.identity, flagContext: emptyFlagContext() }),\n flagAuthorityInitialized: true,\n },\n siteKey\n )\n}\n\nconst requireFlagJsonObject = (\n value: unknown,\n field: string\n): ReturnType<typeof v1FlagJsonFromUnknown> & {\n readonly kind: 'object'\n} => {\n const node = v1FlagJsonFromUnknown(value, field)\n if (node.kind !== 'object') return failCorrupt(`${field} must be an object`)\n return node\n}\n\n/** Builds a detached flag context from the transaction-current installation row. */\nexport const v1FlagEvaluationContextFromInstallation = (\n value: unknown,\n siteKey: string,\n versionsValue: unknown\n): V1FlagEvaluationContext => {\n const installation = validateInstallation(value, siteKey)\n const versions = validateVersions(versionsValue, 'flag versions', failCorrupt)\n const context = installation.identity.flagContext ?? emptyFlagContext()\n const associatedGroupProperties: Record<string, V1JsonObject> = {}\n for (const groupType of Object.keys(installation.identity.groups)) {\n const properties = context.groupProperties[groupType]\n if (properties) {\n Object.defineProperty(associatedGroupProperties, groupType, {\n value: properties,\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n }\n return freezeV1Value({\n identity: freezeV1Value({\n anonymousId: installation.identity.anonymousId,\n userId: installation.identity.userId,\n revision: installation.identity.revision,\n }),\n contextRevision: installation.identity.contextRevision,\n optedOut: installation.identity.optedOut,\n personProperties: requireFlagJsonObject(context.personProperties, 'flag personProperties'),\n groups: requireFlagJsonObject(installation.identity.groups, 'flag groups'),\n groupProperties: requireFlagJsonObject(freezeV1Value(associatedGroupProperties), 'flag groupProperties'),\n versions: requireFlagJsonObject(versions, 'flag versions'),\n })\n}\n\nconst validateStoredRecord = (\n value: unknown,\n installation: V1InstallationRow,\n expectedSequence: number\n): V1DurableQueueRecord => {\n const stored = assertExactRecord(value, STORED_RECORD_KEYS, STORED_RECORD_KEYS, 'stored record', failCorrupt)\n if (stored.storageSchemaVersion !== V1_DURABLE_STORAGE_SCHEMA_VERSION) {\n fail('unsupported-storage', 'stored record schema version is unsupported')\n }\n if (stored.siteKey !== installation.siteKey || stored.streamId !== installation.streamId) {\n failCorrupt('stored record scope does not match its installation')\n }\n if (stored.sequence !== expectedSequence) {\n failCorrupt('stored records are not one contiguous ordered prefix')\n }\n const kind =\n stored.kind === 'event'\n ? 'event'\n : stored.kind === 'mutation'\n ? 'mutation'\n : failCorrupt('stored record kind is unsupported')\n const recordId = assertIdentifier(stored.recordId, 'stored record recordId', 256, failCorrupt)\n const sequence = assertInteger(stored.sequence, 'stored record sequence', 0, Number.MAX_SAFE_INTEGER, failCorrupt)\n const captureVersions = validateVersions(stored.captureVersions, 'stored record captureVersions', failCorrupt)\n const payloadBytes = assertInteger(\n stored.payloadBytes,\n 'stored record payloadBytes',\n 1,\n V1_MAX_DURABLE_QUEUE_BYTES,\n failCorrupt\n )\n const payloadJson = assertString(\n stored.payloadJson,\n 'stored record payloadJson',\n 1,\n V1_MAX_DURABLE_QUEUE_BYTES,\n failCorrupt\n )\n if (utf8ByteLength(payloadJson) !== payloadBytes) {\n failCorrupt('stored record payload byte count is inconsistent')\n }\n\n let parsed: { readonly value: unknown; readonly serialized: string }\n try {\n parsed = parseBoundedJson(payloadJson, V1_MAX_DURABLE_QUEUE_BYTES, 'stored record payload')\n } catch {\n return failCorrupt('stored record payload is not valid bounded JSON')\n }\n if (parsed.serialized !== payloadJson) {\n failCorrupt('stored record payload is not in the stable JSON encoding')\n }\n const wrapper = assertExactRecord(\n parsed.value,\n kind === 'event' ? ['kind', 'event'] : ['kind', 'mutation'],\n kind === 'event' ? ['kind', 'event'] : ['kind', 'mutation'],\n 'stored batch record',\n failCorrupt\n )\n if (wrapper.kind !== kind) {\n failCorrupt('stored batch record kind does not match its queue row')\n }\n const reference: V1DurableRecordReference = freezeV1Value({ sequence, kind, recordId })\n const batchRecord: V1BatchRecord =\n kind === 'event'\n ? freezeV1Value({\n kind: 'event',\n event: validateEvent(\n wrapper.event,\n { ...reference, streamId: installation.streamId },\n 'stored event',\n failCorrupt\n ),\n })\n : freezeV1Value({\n kind: 'mutation',\n mutation: validateMutation(wrapper.mutation, reference, 'stored mutation', failCorrupt),\n })\n if (JSON.stringify(batchRecord) !== payloadJson) {\n failCorrupt('stored batch record is not in the canonical v1 encoding')\n }\n if (batchRecord.kind === 'event' && !sameVersionContext(captureVersions, batchRecord.event.versions)) {\n failCorrupt('stored event capture versions do not match its payload versions')\n }\n return freezeV1Value({\n ...reference,\n siteKey: installation.siteKey,\n streamId: installation.streamId,\n captureVersions,\n batchRecord,\n payloadJson,\n payloadBytes,\n })\n}\n\nconst snapshotFromRow = (row: V1InstallationRow): V1DurableStateSnapshot =>\n freezeV1Value({\n siteKey: row.siteKey,\n identity: row.identity,\n streamId: row.streamId,\n nextSequence: row.nextSequence,\n headSequence: row.headSequence,\n queuedCount: row.queuedCount,\n queuedBytes: row.queuedBytes,\n })\n\nconst auditStoredQueue = async (transaction: V1DurableStorageTransaction, row: V1InstallationRow): Promise<void> => {\n let queuedCount = 0\n let queuedBytes = 0\n await transaction.scanAllRecords(row.siteKey, (rawRecord) => {\n if (queuedCount >= row.queuedCount) {\n return failCorrupt('stored queue contains more records than its counter')\n }\n const record = validateStoredRecord(rawRecord, row, row.headSequence + queuedCount)\n if (!Number.isSafeInteger(queuedBytes + record.payloadBytes)) {\n failCorrupt('stored queue byte counter is exhausted')\n }\n queuedBytes += record.payloadBytes\n queuedCount += 1\n return true\n })\n if (queuedCount !== row.queuedCount) {\n failCorrupt('stored queue count does not match its records')\n }\n if (queuedBytes !== row.queuedBytes) {\n failCorrupt('stored queue byte count does not match its records')\n }\n}\n\ninterface V1AuditedInstallation {\n readonly row: V1InstallationRow\n readonly requiresMigrationWrite: boolean\n}\n\nconst readAuditedState = async (\n transaction: V1DurableStorageTransaction,\n siteKey: string\n): Promise<V1AuditedInstallation | null> => {\n const installation = await transaction.getInstallation(siteKey)\n if (typeof installation === 'undefined') {\n let hasOrphanRecord = false\n await transaction.scanAllRecords(siteKey, () => {\n hasOrphanRecord = true\n return false\n })\n if (hasOrphanRecord) {\n failCorrupt('stored records exist without an installation')\n }\n return null\n }\n let row = validateInstallation(installation, siteKey)\n await auditStoredQueue(transaction, row)\n const legacyBarrier = !hasOwn(installation as Record<string, unknown>, 'authorityBarrier')\n const rawBarrier = (installation as Record<string, unknown>).authorityBarrier\n const legacyBarrierScope = isRecordValue(rawBarrier) && !hasOwn(rawBarrier, 'terminalScope')\n const legacyOptedOutSession = row.identity.optedOut && !isNullValue(row.identity.session)\n if (legacyOptedOutSession) {\n row = freezeV1Value({\n ...row,\n identity: validateIdentity({ ...row.identity, session: null }, 'migrated opted-out identity', failCorrupt),\n })\n }\n return freezeV1Value({\n row,\n requiresMigrationWrite: legacyBarrier || legacyBarrierScope || legacyOptedOutSession,\n })\n}\n\nconst readInstallation = async (\n transaction: V1DurableStorageTransaction,\n siteKey: string\n): Promise<V1InstallationRow> => {\n const installation = await transaction.getInstallation(siteKey)\n if (typeof installation === 'undefined') {\n return failCorrupt('installation disappeared after initialization')\n }\n return validateInstallation(installation, siteKey)\n}\n\nconst normalizeDraftArray = (value: unknown, field: string): readonly unknown[] => {\n const values = isArrayValue(value) ? value : failInput(`${field} must be an array of 1 to 100 records`)\n if (values.length < 1 || values.length > 100) {\n failInput(`${field} must be an array of 1 to 100 records`)\n }\n for (let index = 0; index < values.length; index += 1) {\n if (!hasOwnIndex(values, index)) {\n failInput(`${field} must not be sparse`)\n }\n }\n return values\n}\n\nconst normalizeEventDrafts = (value: unknown): readonly V1NormalizedEventDraft[] => {\n const values = normalizeDraftArray(value, 'event drafts')\n return freezeV1Value(\n values.map((value, index) => {\n const field = `event draft ${index}`\n const normalized = normalizeJsonObject(value, field, failInput)\n const draft = assertExactRecord(normalized, EVENT_DRAFT_KEYS, EVENT_DRAFT_KEYS, field, failInput)\n const kind: V1Event['kind'] =\n draft.kind === 'capture' ||\n draft.kind === 'screen' ||\n draft.kind === 'exception' ||\n draft.kind === 'diagnostic'\n ? draft.kind\n : failInput(`${field}.kind is unsupported`)\n return freezeV1Value({\n kind,\n name: assertIdentifierField(draft.name, \"name\", field, 512, failInput),\n occurredAt: assertDateTimeField(draft.occurredAt, \"occurredAt\", field, failInput),\n expectedSessionId: assertIdentifierField(draft.expectedSessionId, \"expectedSessionId\", field, 256, failInput),\n properties: normalizeJsonObjectField(draft.properties, \"properties\", field, failInput),\n versions: validateVersionsField(draft.versions, \"versions\", field, failInput),\n })\n })\n )\n}\n\nconst normalizeMutationDrafts = (value: unknown): readonly V1NormalizedMutationDraft[] => {\n const values = normalizeDraftArray(value, 'mutation drafts')\n return freezeV1Value(\n values.map((value, index) => {\n const field = `mutation draft ${index}`\n const normalized = normalizeJsonObject(value, field, failInput)\n const draft = assertExactRecord(normalized, MUTATION_DRAFT_KEYS, MUTATION_DRAFT_KEYS, field, failInput)\n return freezeV1Value({\n occurredAt: assertDateTimeField(draft.occurredAt, \"occurredAt\", field, failInput),\n change: validateMutationChange(draft.change, `${field}.change`, failInput),\n versions: validateVersionsField(draft.versions, \"versions\", field, failInput),\n })\n })\n )\n}\n\nconst normalizeSessionUpdate = (value: unknown): V1DurableEventSessionUpdate => {\n const normalized = normalizeJsonObject(value, 'event session update', failInput)\n if (normalized.type === 'preserve') {\n assertExactRecord(normalized, ['type'], ['type'], 'event session update', failInput)\n return freezeV1Value({ type: 'preserve' })\n }\n if (normalized.type === 'replace') {\n const update = assertExactRecord(\n normalized,\n SESSION_UPDATE_KEYS,\n SESSION_UPDATE_KEYS,\n 'event session update',\n failInput\n )\n const session = validateSessionField(update.session, \"session\", \"event session update\", failInput)\n if (!session) {\n return failInput('event session update.session must not be null')\n }\n const expectedCurrentSessionId = isNullValue(update.expectedCurrentSessionId)\n ? null\n : assertIdentifierField(update.expectedCurrentSessionId, \"expectedCurrentSessionId\", \"event session update\", 256, failInput)\n return freezeV1Value({ type: 'replace', expectedCurrentSessionId, session })\n }\n return failInput('event session update.type is unsupported')\n}\n\nconst normalizeLocalChange = (value: unknown): V1DurableLocalChange => {\n const normalized = normalizeJsonObject(value, 'local change', failInput)\n const occurredAt = (): string => assertDateTimeField(normalized.occurredAt, \"occurredAt\", \"local change\", failInput)\n switch (normalized.type) {\n case 'setOptedOut': {\n const change = assertExactRecord(\n normalized,\n ['type', 'optedOut', 'occurredAt'],\n ['type', 'optedOut', 'occurredAt'],\n 'local change',\n failInput\n )\n return freezeV1Value({\n type: 'setOptedOut',\n optedOut: assertBooleanField(change.optedOut, \"optedOut\", \"local change\", failInput),\n occurredAt: occurredAt(),\n })\n }\n case 'resetGroups':\n assertExactRecord(normalized, ['type', 'occurredAt'], ['type', 'occurredAt'], 'local change', failInput)\n return freezeV1Value({ type: 'resetGroups', occurredAt: occurredAt() })\n case 'resetIdentity': {\n const change = assertExactRecord(\n normalized,\n ['type', 'anonymousId', 'occurredAt'],\n ['type', 'anonymousId', 'occurredAt'],\n 'local change',\n failInput\n )\n return freezeV1Value({\n type: 'resetIdentity',\n anonymousId: assertIdentifierField(change.anonymousId, \"anonymousId\", \"local change\", 256, failInput),\n occurredAt: occurredAt(),\n })\n }\n case 'setSuperProperties': {\n const change = assertExactRecord(\n normalized,\n ['type', 'set', 'setOnce', 'unset', 'occurredAt'],\n ['type', 'set', 'setOnce', 'unset', 'occurredAt'],\n 'local change',\n failInput\n )\n return freezeV1Value({\n type: 'setSuperProperties',\n set: normalizeJsonObjectField(change.set, \"set\", \"local change\", failInput),\n setOnce: normalizeJsonObjectField(change.setOnce, \"setOnce\", \"local change\", failInput),\n unset: validatePropertyNamesField(change.unset, \"unset\", \"local change\", failInput),\n occurredAt: occurredAt(),\n })\n }\n case 'replaceSession': {\n const change = assertExactRecord(\n normalized,\n ['type', 'expectedCurrentSessionId', 'session'],\n ['type', 'expectedCurrentSessionId', 'session'],\n 'local change',\n failInput\n )\n const session = validateSessionField(change.session, \"session\", \"local change\", failInput)\n if (!session) {\n return failInput('local change.session must not be null')\n }\n return freezeV1Value({\n type: 'replaceSession',\n expectedCurrentSessionId: isNullValue(change.expectedCurrentSessionId)\n ? null\n : assertIdentifier(\n change.expectedCurrentSessionId,\n 'local change.expectedCurrentSessionId',\n 256,\n failInput\n ),\n session,\n })\n }\n case 'clearSession': {\n const change = assertExactRecord(\n normalized,\n ['type', 'expectedCurrentSessionId', 'occurredAt'],\n ['type', 'expectedCurrentSessionId', 'occurredAt'],\n 'local change',\n failInput\n )\n return freezeV1Value({\n type: 'clearSession',\n expectedCurrentSessionId: isNullValue(change.expectedCurrentSessionId)\n ? null\n : assertIdentifier(\n change.expectedCurrentSessionId,\n 'local change.expectedCurrentSessionId',\n 256,\n failInput\n ),\n occurredAt: occurredAt(),\n })\n }\n case 'setFlagContext': {\n const change = assertExactRecord(\n normalized,\n [\n 'type',\n 'personProperties',\n 'groupProperties',\n 'mergePersonProperties',\n 'mergeGroupProperties',\n 'unsetGroupTypes',\n 'occurredAt',\n ],\n ['type', 'personProperties', 'groupProperties', 'occurredAt'],\n 'local change',\n failInput\n )\n const groupProperties = isNullValue(change.groupProperties)\n ? null\n : validateFlagContext(\n { schemaVersion: 1, personProperties: {}, groupProperties: change.groupProperties },\n 'local change',\n failInput\n ).groupProperties\n return freezeV1Value({\n type: 'setFlagContext',\n personProperties: isNullValue(change.personProperties)\n ? null\n : validateFlagProperties(change.personProperties, 'local change.personProperties', failInput),\n groupProperties,\n ...(hasOwn(change, 'mergePersonProperties')\n ? {\n mergePersonProperties: assertBooleanField(change.mergePersonProperties, \"mergePersonProperties\", \"local change\", failInput),\n }\n : {}),\n ...(hasOwn(change, 'mergeGroupProperties')\n ? {\n mergeGroupProperties: assertBooleanField(change.mergeGroupProperties, \"mergeGroupProperties\", \"local change\", failInput),\n }\n : {}),\n ...(hasOwn(change, 'unsetGroupTypes')\n ? {\n unsetGroupTypes: validatePropertyNamesField(change.unsetGroupTypes, \"unsetGroupTypes\", \"local change\", failInput),\n }\n : {}),\n occurredAt: occurredAt(),\n })\n }\n default:\n return failInput('local change.type is unsupported')\n }\n}\n\nconst streamFingerprint = (streamId: string): string => {\n const moduli = [4_294_967_291, 4_294_967_279, 4_294_967_231, 4_294_967_197] as const\n const multipliers = [131, 137, 139, 149] as const\n const hashes = [2_166_136_261, 2_166_136_247, 2_166_136_229, 2_166_136_213]\n for (let index = 0; index < streamId.length; index += 1) {\n const code = streamId.charCodeAt(index)\n for (let part = 0; part < hashes.length; part += 1) {\n hashes[part] = (hashes[part] * multipliers[part] + code + part) % moduli[part]\n }\n }\n return hashes.map((hash) => hash.toString(16).padStart(8, '0')).join('')\n}\n\nconst recordIdFor = (kind: 'event' | 'mutation', streamId: string, sequence: number): string =>\n `elu_${kind === 'event' ? 'e' : 'm'}_${streamFingerprint(streamId)}_${sequence.toString(36)}`\n\nconst incrementStateCounter = (value: number, field: string): number => {\n if (value >= Number.MAX_SAFE_INTEGER) {\n return fail('sequence-exhausted', `${field} is exhausted`)\n }\n return value + 1\n}\n\nconst requireTimestampNotBefore = (candidate: string, current: string, field: string): void => {\n if (compareV1DateTimes(candidate, current) < 0) {\n failInput(`${field} must not move persisted time backward`)\n }\n}\n\nconst requireCurrentSessionId = (\n current: V1PersistedIdentityState,\n expectedCurrentSessionId: string | null,\n field: string\n): void => {\n if (expectedCurrentSessionId !== (current.session?.id ?? null)) {\n failInput(`${field} lost its current-session compare-and-swap`)\n }\n}\n\nconst copyGroupsWithAssociation = (\n groups: Readonly<Record<string, string>>,\n groupType: string,\n groupKey: string\n): Readonly<Record<string, string>> => {\n const result: Record<string, string> = {}\n for (const key of Object.keys(groups)) {\n Object.defineProperty(result, key, {\n value: groups[key],\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n Object.defineProperty(result, groupType, {\n value: groupKey,\n enumerable: true,\n configurable: true,\n writable: true,\n })\n return freezeV1Value(result)\n}\n\nconst applyJsonProperties = (\n current: V1JsonObject,\n set: V1JsonObject,\n setOnce: V1JsonObject,\n unset: readonly string[]\n): V1JsonObject => {\n const result: Record<string, V1JsonValue> = {}\n const define = (key: string, value: V1JsonValue): void => {\n Object.defineProperty(result, key, { value, enumerable: true, configurable: true, writable: true })\n }\n for (const key of Object.keys(current)) define(key, current[key])\n for (const key of unset) delete result[key]\n for (const key of Object.keys(setOnce)) {\n if (!hasOwn(result, key)) define(key, setOnce[key])\n }\n for (const key of Object.keys(set)) define(key, set[key])\n return freezeV1Value(result)\n}\n\nconst copyFlagGroupProperties = (\n groups: Readonly<Record<string, V1JsonObject>>,\n groupType: string,\n properties: V1JsonObject | null\n): Readonly<Record<string, V1JsonObject>> => {\n const result: Record<string, V1JsonObject> = {}\n for (const key of Object.keys(groups)) {\n if (key !== groupType) {\n Object.defineProperty(result, key, {\n value: groups[key],\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n }\n if (properties) {\n Object.defineProperty(result, groupType, {\n value: properties,\n enumerable: true,\n configurable: true,\n writable: true,\n })\n }\n return freezeV1Value(result)\n}\n\nconst mergeFlagGroupProperties = (\n current: Readonly<Record<string, V1JsonObject>>,\n overlay: Readonly<Record<string, V1JsonObject>>\n): Readonly<Record<string, V1JsonObject>> => {\n let result = current\n for (const groupType of Object.keys(overlay)) {\n result = copyFlagGroupProperties(\n result,\n groupType,\n applyJsonProperties(result[groupType] ?? freezeV1Value({}), overlay[groupType], freezeV1Value({}), [])\n )\n }\n return result\n}\n\nconst withFlagContext = (\n current: V1PersistedIdentityState,\n update: (context: V1PersistedFlagContext) => V1PersistedFlagContext\n): Readonly<{ flagContext?: V1PersistedFlagContext }> =>\n current.flagContext ? freezeV1Value({ flagContext: update(current.flagContext) }) : freezeV1Value({})\n\nconst updatePersonFlagContext = (\n context: V1PersistedFlagContext,\n set: V1JsonObject,\n setOnce: V1JsonObject,\n unset: readonly string[]\n): V1PersistedFlagContext =>\n freezeV1Value({\n ...context,\n personProperties: applyJsonProperties(context.personProperties, set, setOnce, unset),\n })\n\nconst mergeEventProperties = (\n superProperties: V1JsonObject,\n eventProperties: V1JsonObject,\n versions: V1VersionContext\n): V1JsonObject => {\n const merged = applyJsonProperties(superProperties, eventProperties, freezeV1Value({}), freezeV1Value([]))\n return applyJsonProperties(\n merged,\n freezeV1Value({\n $elu_contract_version: versions.contractVersion,\n $elu_sdk_version: versions.runtime.version,\n $elu_facade_version: versions.facade.version,\n }),\n freezeV1Value({}),\n freezeV1Value([])\n )\n}\n\nconst validateSessionTransition = (\n current: V1PersistedIdentityState,\n session: V1PersistedSessionState,\n eventEligible: boolean\n): V1PersistedIdentityState => {\n if (eventEligible && session.lifecycle !== 'active') {\n failInput('event session must be active')\n }\n if ((!current.session || current.session.id !== session.id) && session.lifecycle !== 'active') {\n failInput('a replacement session must begin in the active lifecycle')\n }\n const transitionAt = session.backgroundedAt ?? session.lastActivityAt\n requireTimestampNotBefore(transitionAt, current.updatedAt, 'session activity')\n if (current.session) {\n if (current.session.id === session.id) {\n if (current.session.startedAt !== session.startedAt) {\n failInput('a retained session ID must keep its original startedAt')\n }\n requireTimestampNotBefore(session.lastActivityAt, current.session.lastActivityAt, 'session lastActivityAt')\n if (\n hasV1ElapsedAtLeastSeconds(\n session.lastActivityAt,\n current.session.lastActivityAt,\n Math.min(current.session.timeoutSeconds, session.timeoutSeconds)\n )\n ) {\n failInput('a retained session must remain before its idle timeout')\n }\n } else {\n requireTimestampNotBefore(\n session.startedAt,\n current.session.lastActivityAt,\n 'replacement session startedAt'\n )\n }\n }\n return validateIdentity({ ...current, session, updatedAt: transitionAt }, 'derived session identity', failInput)\n}\n\nconst applyMutation = (\n current: V1PersistedIdentityState,\n draft: V1NormalizedMutationDraft\n): V1PersistedIdentityState => {\n requireTimestampNotBefore(draft.occurredAt, current.updatedAt, 'mutation occurredAt')\n const contextRevision = incrementStateCounter(current.contextRevision, 'identity context revision')\n const base = { ...current, contextRevision, updatedAt: draft.occurredAt }\n const change = draft.change\n switch (change.type) {\n case 'identify': {\n const identityChanged = change.userId !== current.userId\n return validateIdentity(\n {\n ...base,\n ...withFlagContext(current, (context) =>\n updatePersonFlagContext(context, change.set, change.setOnce, freezeV1Value([]))\n ),\n userId: change.userId,\n revision: identityChanged\n ? incrementStateCounter(current.revision, 'identity revision')\n : current.revision,\n },\n 'derived identify identity',\n failInput\n )\n }\n case 'linkAlias':\n return validateIdentity(base, 'derived mutation identity', failInput)\n case 'setPersonProperties':\n return validateIdentity(\n {\n ...base,\n ...withFlagContext(current, (context) =>\n updatePersonFlagContext(context, change.set, change.setOnce, change.unset)\n ),\n },\n 'derived person properties identity',\n failInput\n )\n case 'associateGroup':\n return validateIdentity(\n {\n ...base,\n groups: copyGroupsWithAssociation(current.groups, change.groupType, change.groupKey),\n ...(current.groups[change.groupType] !== change.groupKey\n ? withFlagContext(current, (context) =>\n freezeV1Value({\n ...context,\n groupProperties: copyFlagGroupProperties(\n context.groupProperties,\n change.groupType,\n null\n ),\n })\n )\n : {}),\n },\n 'derived group identity',\n failInput\n )\n case 'setGroupProperties':\n if (current.groups[change.groupType] !== change.groupKey) {\n return failInput('group properties mutation must match the persisted group association')\n }\n return validateIdentity(\n {\n ...base,\n ...withFlagContext(current, (context) => {\n const properties = applyJsonProperties(\n context.groupProperties[change.groupType] ?? freezeV1Value({}),\n change.set,\n change.setOnce,\n change.unset\n )\n return freezeV1Value({\n ...context,\n groupProperties: copyFlagGroupProperties(\n context.groupProperties,\n change.groupType,\n properties\n ),\n })\n }),\n },\n 'derived group properties identity',\n failInput\n )\n }\n}\n\nconst applyLocalChange = (\n current: V1PersistedIdentityState,\n change: V1DurableLocalChange\n): V1PersistedIdentityState => {\n switch (change.type) {\n case 'replaceSession':\n requireCurrentSessionId(current, change.expectedCurrentSessionId, 'local session replacement')\n return validateSessionTransition(current, change.session, false)\n case 'clearSession':\n requireCurrentSessionId(current, change.expectedCurrentSessionId, 'local session clear')\n requireTimestampNotBefore(change.occurredAt, current.updatedAt, 'local change occurredAt')\n return validateIdentity(\n { ...current, session: null, updatedAt: change.occurredAt },\n 'derived local identity',\n failInput\n )\n case 'setOptedOut': {\n requireTimestampNotBefore(change.occurredAt, current.updatedAt, 'local change occurredAt')\n const optChanged = change.optedOut !== current.optedOut\n const flagContextWasCleared = change.optedOut && flagContextHasValues(current.flagContext)\n return validateIdentity(\n {\n ...current,\n optedOut: change.optedOut,\n session: change.optedOut ? null : current.session,\n contextRevision:\n optChanged || flagContextWasCleared\n ? incrementStateCounter(current.contextRevision, 'identity context revision')\n : current.contextRevision,\n ...(change.optedOut && current.flagContext ? { flagContext: emptyFlagContext() } : {}),\n updatedAt: change.occurredAt,\n },\n 'derived local identity',\n failInput\n )\n }\n case 'resetGroups':\n requireTimestampNotBefore(change.occurredAt, current.updatedAt, 'local change occurredAt')\n return validateIdentity(\n {\n ...current,\n groups: freezeV1Value({}),\n ...(current.flagContext\n ? { flagContext: freezeV1Value({ ...current.flagContext, groupProperties: freezeV1Value({}) }) }\n : {}),\n contextRevision: incrementStateCounter(current.contextRevision, 'identity context revision'),\n updatedAt: change.occurredAt,\n },\n 'derived local identity',\n failInput\n )\n case 'resetIdentity':\n requireTimestampNotBefore(change.occurredAt, current.updatedAt, 'local change occurredAt')\n if (change.anonymousId === current.anonymousId) {\n return failInput('resetIdentity must rotate the anonymous ID')\n }\n return validateIdentity(\n {\n ...current,\n revision: incrementStateCounter(current.revision, 'identity revision'),\n contextRevision: incrementStateCounter(current.contextRevision, 'identity context revision'),\n anonymousId: change.anonymousId,\n userId: null,\n groups: freezeV1Value({}),\n superProperties: freezeV1Value({}),\n ...(current.flagContext ? { flagContext: emptyFlagContext() } : {}),\n session: null,\n updatedAt: change.occurredAt,\n },\n 'derived reset identity',\n failInput\n )\n case 'setSuperProperties':\n requireTimestampNotBefore(change.occurredAt, current.updatedAt, 'local change occurredAt')\n return validateIdentity(\n {\n ...current,\n contextRevision: incrementStateCounter(current.contextRevision, 'identity context revision'),\n superProperties: applyJsonProperties(\n current.superProperties,\n change.set,\n change.setOnce,\n change.unset\n ),\n updatedAt: change.occurredAt,\n },\n 'derived local identity',\n failInput\n )\n case 'setFlagContext': {\n requireTimestampNotBefore(change.occurredAt, current.updatedAt, 'local change occurredAt')\n const context = current.flagContext ?? emptyFlagContext()\n let personProperties = context.personProperties\n if (change.personProperties) {\n personProperties = change.mergePersonProperties\n ? applyJsonProperties(context.personProperties, change.personProperties, freezeV1Value({}), [])\n : change.personProperties\n }\n let groupProperties = context.groupProperties\n if (change.unsetGroupTypes) {\n for (const groupType of change.unsetGroupTypes) {\n groupProperties = copyFlagGroupProperties(groupProperties, groupType, null)\n }\n }\n if (change.groupProperties) {\n groupProperties = change.mergeGroupProperties\n ? mergeFlagGroupProperties(groupProperties, change.groupProperties)\n : change.groupProperties\n }\n return validateIdentity(\n {\n ...current,\n contextRevision: incrementStateCounter(current.contextRevision, 'identity context revision'),\n flagContext: freezeV1Value({\n schemaVersion: 1,\n personProperties,\n groupProperties,\n }),\n updatedAt: change.occurredAt,\n },\n 'derived local identity',\n failInput\n )\n }\n }\n}\n\nconst storedRecordFromBatch = (\n batchRecord: V1BatchRecord,\n kind: 'event' | 'mutation',\n recordId: string,\n captureVersions: V1VersionContext,\n installation: V1InstallationRow,\n sequence: number\n): { readonly stored: V1StoredRecordRow; readonly exposed: V1DurableQueueRecord } => {\n const reference: V1DurableRecordReference = freezeV1Value({\n sequence,\n kind,\n recordId,\n })\n const payloadJson = JSON.stringify(batchRecord)\n const payloadBytes = utf8ByteLength(payloadJson)\n const stored: V1StoredRecordRow = freezeV1Value({\n storageSchemaVersion: 1,\n siteKey: installation.siteKey,\n streamId: installation.streamId,\n sequence,\n kind,\n recordId,\n captureVersions,\n payloadJson,\n payloadBytes,\n })\n const exposed: V1DurableQueueRecord = freezeV1Value({\n ...reference,\n siteKey: installation.siteKey,\n streamId: installation.streamId,\n captureVersions,\n batchRecord,\n payloadJson,\n payloadBytes,\n })\n return freezeV1Value({ stored, exposed })\n}\n\nconst encodeEventDraft = (\n draft: V1NormalizedEventDraft,\n installation: V1InstallationRow,\n sequence: number\n): { readonly stored: V1StoredRecordRow; readonly exposed: V1DurableQueueRecord } => {\n const session = installation.identity.session\n if (!session || session.lifecycle !== 'active') {\n return failInput('events require an active persisted session')\n }\n if (draft.expectedSessionId !== session.id) {\n return failInput('event session expectation does not match the persisted session')\n }\n if (\n compareV1DateTimes(draft.occurredAt, session.startedAt) < 0 ||\n compareV1DateTimes(draft.occurredAt, session.lastActivityAt) > 0\n ) {\n return failInput('event occurredAt must fall within the persisted session activity window')\n }\n const recordId = recordIdFor('event', installation.streamId, sequence)\n const reference = freezeV1Value({ sequence, kind: 'event' as const, recordId })\n const event = validateEvent(\n {\n schemaVersion: 1,\n eventId: recordId,\n streamId: installation.streamId,\n sequence,\n contextRevision: installation.identity.contextRevision,\n kind: draft.kind,\n name: draft.name,\n occurredAt: draft.occurredAt,\n identity: {\n anonymousId: installation.identity.anonymousId,\n userId: installation.identity.userId,\n revision: installation.identity.revision,\n },\n sessionId: session.id,\n properties: mergeEventProperties(installation.identity.superProperties, draft.properties, draft.versions),\n groups: installation.identity.groups,\n versions: draft.versions,\n },\n { ...reference, streamId: installation.streamId },\n 'derived event',\n failInput\n )\n return storedRecordFromBatch(\n freezeV1Value({ kind: 'event', event }),\n 'event',\n recordId,\n event.versions,\n installation,\n sequence\n )\n}\n\nconst validateEventCausality = (\n lowerBound: string,\n session: V1PersistedSessionState | null,\n drafts: readonly V1NormalizedEventDraft[]\n): void => {\n if (!session || session.lifecycle !== 'active') {\n return failInput('events require an active persisted session')\n }\n let previousOccurredAt = lowerBound\n for (let index = 0; index < drafts.length; index += 1) {\n const draft = drafts[index]\n if (draft.expectedSessionId !== session.id) {\n failInput(`event draft ${index} session expectation does not match the persisted session`)\n }\n requireTimestampNotBefore(draft.occurredAt, previousOccurredAt, `event draft ${index} occurredAt`)\n if (\n compareV1DateTimes(draft.occurredAt, session.startedAt) < 0 ||\n compareV1DateTimes(draft.occurredAt, session.lastActivityAt) > 0\n ) {\n failInput(`event draft ${index} occurredAt must fall within the persisted session activity window`)\n }\n previousOccurredAt = draft.occurredAt\n }\n}\n\nconst encodeMutationDraft = (\n draft: V1NormalizedMutationDraft,\n installation: V1InstallationRow,\n sequence: number\n): { readonly stored: V1StoredRecordRow; readonly exposed: V1DurableQueueRecord } => {\n const recordId = recordIdFor('mutation', installation.streamId, sequence)\n const mutation = validateMutation(\n {\n mutationId: recordId,\n sequence,\n contextRevision: installation.identity.contextRevision,\n occurredAt: draft.occurredAt,\n subject: {\n anonymousId: installation.identity.anonymousId,\n userId: installation.identity.userId,\n identityRevision: installation.identity.revision,\n },\n change: draft.change,\n },\n freezeV1Value({ sequence, kind: 'mutation', recordId }),\n 'derived mutation',\n failInput\n )\n return storedRecordFromBatch(\n freezeV1Value({ kind: 'mutation', mutation }),\n 'mutation',\n recordId,\n draft.versions,\n installation,\n sequence\n )\n}\n\nconst normalizeReferences = (value: unknown): readonly V1DurableRecordReference[] => {\n const values = isArrayValue(value)\n ? value\n : failInput('acknowledgement references must be an array of at most 1000 records')\n if (values.length > 1000) failInput('acknowledgement references must be an array of at most 1000 records')\n const references: V1DurableRecordReference[] = []\n for (let index = 0; index < values.length; index += 1) {\n if (!hasOwnIndex(values, index)) {\n failInput('acknowledgement references must not be sparse')\n }\n const reference = assertExactRecord(\n values[index],\n ACKNOWLEDGEMENT_REFERENCE_KEYS,\n ACKNOWLEDGEMENT_REFERENCE_KEYS,\n `acknowledgement reference ${index}`,\n failInput\n )\n const kind =\n reference.kind === 'event'\n ? 'event'\n : reference.kind === 'mutation'\n ? 'mutation'\n : failInput(`acknowledgement reference ${index}.kind is unsupported`)\n const sequence = assertInteger(\n reference.sequence,\n `acknowledgement reference ${index}.sequence`,\n 0,\n Number.MAX_SAFE_INTEGER,\n failInput\n )\n if (index > 0 && sequence !== references[index - 1].sequence + 1) {\n failInput('acknowledgement references must be contiguous and ordered')\n }\n references.push(\n freezeV1Value({\n sequence,\n kind,\n recordId: assertIdentifier(\n reference.recordId,\n `acknowledgement reference ${index}.recordId`,\n 256,\n failInput\n ),\n })\n )\n }\n return freezeV1Value(references)\n}\n\nconst normalizeAcknowledgement = (value: unknown): V1DurableAcknowledgement => {\n const normalized = normalizeJsonObject(value, 'acknowledgement', failInput)\n const acknowledgement = assertExactRecord(\n normalized,\n ACKNOWLEDGEMENT_KEYS,\n ACKNOWLEDGEMENT_KEYS,\n 'acknowledgement',\n failInput\n )\n return freezeV1Value({\n siteKey: assertIdentifierField(acknowledgement.siteKey, \"siteKey\", \"acknowledgement\", 128, failInput),\n streamId: assertIdentifierField(acknowledgement.streamId, \"streamId\", \"acknowledgement\", 256, failInput),\n records: normalizeReferences(acknowledgement.records),\n })\n}\n\nconst sameReference = (left: V1DurableRecordReference, right: V1DurableRecordReference): boolean =>\n left.sequence === right.sequence && left.kind === right.kind && left.recordId === right.recordId\n\ninterface V1NormalizedRuntimeOptions {\n readonly siteKey: string\n readonly limits: V1DurableRuntimeLimits\n readonly storageFactory: V1DurableStorageFactory\n readonly captureRuntime: Readonly<{\n readonly sha256?: V1Sha256\n readonly clock: V1CaptureRuntimeClock\n readonly createSessionId: () => string\n }>\n}\n\nconst defaultCaptureClock: V1CaptureRuntimeClock = freezeV1Value({\n wallNow: (): string => new Date().toISOString(),\n monotonicNowMs: (): number => {\n const clock = typeof globalThis === 'undefined' ? undefined : globalThis.performance\n return clock ? clock.now() : Number.NaN\n },\n})\n\n/** Lowercase hex from platform randomness; fails closed when the platform offers none. */\nexport const v1SecureRandomHex = (byteCount: number, purpose: string): string => {\n const cryptoApi = typeof globalThis === 'undefined' ? undefined : globalThis.crypto\n if (!cryptoApi || !isFunctionValue(cryptoApi.getRandomValues)) {\n return failInput(`secure randomness is unavailable for ${purpose}`)\n }\n const bytes = new Uint8Array(byteCount)\n cryptoApi.getRandomValues(bytes)\n let hexadecimal = ''\n for (const byte of bytes) hexadecimal += byte.toString(16).padStart(2, '0')\n return hexadecimal\n}\n\nconst defaultSessionId = (): string => `elu_s_${v1SecureRandomHex(16, 'capture session creation')}`\n\n/** Mints a fresh anonymous identity from 128 bits of platform randomness; never derived from other state. */\nexport const createV1AnonymousId = (): string => `anon_${v1SecureRandomHex(16, 'anonymous identity creation')}`\n\n/** Mints the installation stream identity that events and mutations share for the life of the install. */\nexport const createV1StreamId = (): string => `stream_${v1SecureRandomHex(16, 'stream identity creation')}`\n\n/**\n * The identity a fresh install persists before its first event: no user, groups, super properties,\n * or session, opted in, at revision zero. Callers mint the anonymous ID once and persist this row\n * atomically; later constructions read the persisted row instead of minting again.\n */\nexport const createV1FreshInstallIdentity = (anonymousId: string, updatedAt: string): V1PersistedIdentityState =>\n validateIdentity(\n {\n schemaVersion: 1,\n revision: 0,\n contextRevision: 0,\n anonymousId,\n userId: null,\n groups: {},\n superProperties: {},\n session: null,\n optedOut: false,\n updatedAt,\n },\n 'fresh install identity',\n failInput\n )\n\nconst validateOptions = (value: V1DurableRuntimeOptions): V1NormalizedRuntimeOptions => {\n const options = assertExactRecord(\n value,\n ['siteKey', 'limits', 'storageFactory', 'captureRuntime'],\n ['siteKey', 'limits'],\n 'options',\n failInput\n )\n const limits = assertExactRecord(\n options.limits,\n ['maxCount', 'maxBytes'],\n ['maxCount', 'maxBytes'],\n 'options.limits',\n failInput\n )\n const storageFactoryCandidate = hasOwn(options, 'storageFactory') ? options.storageFactory : openV1IndexedDBStorage\n const storageFactory: V1DurableStorageFactory = isFunctionValue(storageFactoryCandidate)\n ? (storageFactoryCandidate as V1DurableStorageFactory)\n : failInput('options.storageFactory must be a function')\n const captureRuntime = hasOwn(options, 'captureRuntime')\n ? assertExactRecord(\n options.captureRuntime,\n ['sha256', 'clock', 'createSessionId'],\n [],\n 'options.captureRuntime',\n failInput\n )\n : {}\n const clock = hasOwn(captureRuntime, 'clock')\n ? assertExactRecord(\n captureRuntime.clock,\n ['wallNow', 'monotonicNowMs'],\n ['wallNow', 'monotonicNowMs'],\n 'options.captureRuntime.clock',\n failInput\n )\n : defaultCaptureClock\n if (!isFunctionValue(clock.wallNow) || !isFunctionValue(clock.monotonicNowMs)) {\n failInput('options.captureRuntime.clock functions are required')\n }\n if (hasOwn(captureRuntime, 'sha256') && !isFunctionValue(captureRuntime.sha256)) {\n failInput('options.captureRuntime.sha256 must be a function')\n }\n const createSessionId = hasOwn(captureRuntime, 'createSessionId')\n ? captureRuntime.createSessionId\n : defaultSessionId\n if (!isFunctionValue(createSessionId)) failInput('options.captureRuntime.createSessionId must be a function')\n return freezeV1Value({\n siteKey: assertIdentifierField(options.siteKey, \"siteKey\", \"options\", 128, failInput),\n limits: freezeV1Value({\n maxCount: assertIntegerField(limits.maxCount, \"maxCount\", \"options.limits\", 1, V1_MAX_DURABLE_QUEUE_COUNT, failInput),\n maxBytes: assertIntegerField(limits.maxBytes, \"maxBytes\", \"options.limits\", 1, V1_MAX_DURABLE_QUEUE_BYTES, failInput),\n }),\n storageFactory,\n captureRuntime: freezeV1Value({\n ...(hasOwn(captureRuntime, 'sha256') ? { sha256: captureRuntime.sha256 as V1Sha256 } : {}),\n clock: freezeV1Value({\n wallNow: clock.wallNow as () => string,\n monotonicNowMs: clock.monotonicNowMs as () => number,\n }),\n createSessionId: createSessionId as () => string,\n }),\n })\n}\n\nconst sameAuthorityBarrier = (\n left: V1PersistedAuthorityBarrier | null,\n right: V1PersistedAuthorityBarrier | null\n): boolean =>\n left === right ||\n (!!left &&\n !!right &&\n left.kind === right.kind &&\n left.terminalScope === right.terminalScope &&\n left.configIssuedAt === right.configIssuedAt &&\n left.configSemanticHash === right.configSemanticHash &&\n left.policySourceHash === right.policySourceHash &&\n left.contextRevision === right.contextRevision &&\n left.decisionHash === right.decisionHash)\n\nconst terminalBarrierFrom = (\n boundary: Pick<\n V1PersistedAuthorityBarrier,\n 'configIssuedAt' | 'configSemanticHash' | 'policySourceHash' | 'contextRevision' | 'decisionHash'\n >,\n terminalScope: 'config' | 'context'\n): V1PersistedAuthorityBarrier => freezeV1Value({ kind: 'terminal', terminalScope, ...boundary })\n\nconst terminalBarrierAtCurrentConfig = (\n current: V1PersistedAuthorityBarrier | null,\n terminalScope: 'config' | 'context' = 'config'\n): V1PersistedAuthorityBarrier =>\n terminalBarrierFrom(\n {\n configIssuedAt: current?.configIssuedAt ?? null,\n configSemanticHash: current?.configSemanticHash ?? null,\n policySourceHash: current?.policySourceHash ?? null,\n contextRevision: current?.contextRevision ?? null,\n decisionHash: current?.decisionHash ?? null,\n },\n terminalScope\n )\n\nconst mergeAuthorityBarrier = (\n current: V1PersistedAuthorityBarrier | null,\n incoming: V1PersistedAuthorityBarrier\n): V1PersistedAuthorityBarrier => {\n if (!current) return incoming\n if (!incoming.configIssuedAt || !incoming.configSemanticHash) {\n return terminalBarrierAtCurrentConfig(current, 'config')\n }\n if (!current.configIssuedAt || !current.configSemanticHash) return incoming\n const configOrder = compareV1DateTimes(incoming.configIssuedAt, current.configIssuedAt)\n if (configOrder < 0) return current\n if (configOrder > 0) return incoming\n if (incoming.configSemanticHash !== current.configSemanticHash) {\n return terminalBarrierAtCurrentConfig(current, 'config')\n }\n if (current.kind === 'terminal' && current.terminalScope === 'config') return current\n if (incoming.kind === 'terminal' && incoming.terminalScope === 'config') {\n return terminalBarrierFrom(\n {\n configIssuedAt: current.configIssuedAt,\n configSemanticHash: current.configSemanticHash,\n policySourceHash: current.policySourceHash,\n contextRevision: current.contextRevision,\n decisionHash: incoming.decisionHash ?? current.decisionHash,\n },\n 'config'\n )\n }\n if (incoming.policySourceHash !== current.policySourceHash) {\n return terminalBarrierAtCurrentConfig(current, 'config')\n }\n const currentContext = current.contextRevision ?? -1\n const incomingContext = incoming.contextRevision ?? currentContext\n if (incomingContext < currentContext) return current\n if (incomingContext > currentContext) return freezeV1Value({ ...incoming, contextRevision: incomingContext })\n if (current.kind === 'terminal' || incoming.kind === 'terminal') {\n return terminalBarrierFrom(\n {\n configIssuedAt: current.configIssuedAt,\n configSemanticHash: current.configSemanticHash,\n policySourceHash: current.policySourceHash,\n contextRevision: current.contextRevision,\n decisionHash:\n incoming.kind === 'terminal'\n ? (incoming.decisionHash ?? current.decisionHash)\n : current.decisionHash,\n },\n 'context'\n )\n }\n if (incoming.decisionHash !== current.decisionHash) {\n return terminalBarrierFrom(\n {\n configIssuedAt: current.configIssuedAt,\n configSemanticHash: current.configSemanticHash,\n policySourceHash: current.policySourceHash,\n contextRevision: current.contextRevision,\n decisionHash: current.decisionHash,\n },\n 'context'\n )\n }\n return incoming\n}\n\nconst barrierForEvaluation = (evaluation: V1CaptureCandidateEvaluation): V1PersistedAuthorityBarrier => {\n if (evaluation.status === 'authorized') {\n return freezeV1Value({\n kind: 'authorized',\n terminalScope: null,\n configIssuedAt: evaluation.configIssuedAt,\n configSemanticHash: evaluation.configSemanticHash,\n policySourceHash: evaluation.policySourceHash,\n contextRevision: evaluation.contextRevision,\n decisionHash: evaluation.decisionHash,\n })\n }\n if (evaluation.status === 'stale') return failInput('stale evaluation has no authority barrier')\n return freezeV1Value({\n kind: 'terminal',\n terminalScope: evaluation.terminalScope,\n configIssuedAt: evaluation.trustedBoundary?.configIssuedAt ?? null,\n configSemanticHash: evaluation.trustedBoundary?.configSemanticHash ?? null,\n policySourceHash: evaluation.trustedBoundary?.policySourceHash ?? null,\n contextRevision: evaluation.contextRevision,\n decisionHash: evaluation.decisionHash,\n })\n}\n\nconst normalizeCaptureCommand = (value: V1CaptureActivityCommand): V1CaptureActivityCommand => {\n const normalized = normalizeJsonObject(value, 'capture command', failInput)\n const command = assertExactRecord(\n normalized,\n ['kind', 'name', 'occurredAt', 'properties', 'versions'],\n ['kind', 'name', 'occurredAt', 'properties', 'versions'],\n 'capture command',\n failInput\n )\n const kind =\n command.kind === 'capture'\n ? 'capture'\n : command.kind === 'screen'\n ? 'screen'\n : command.kind === 'exception'\n ? 'exception'\n : failInput('capture command.kind must be capture, screen, or exception')\n const properties = normalizeJsonObjectField(command.properties, \"properties\", \"capture command\", failInput)\n if (Object.keys(properties).length > V1_MAX_CAPTURE_PROPERTY_COUNT) {\n return failInput(`capture command.properties must contain at most ${V1_MAX_CAPTURE_PROPERTY_COUNT} properties`)\n }\n if (utf8ByteLength(JSON.stringify(properties)) > V1_MAX_CAPTURE_PROPERTIES_BYTES) {\n return failInput(\n `capture command.properties must serialize to at most ${V1_MAX_CAPTURE_PROPERTIES_BYTES} UTF-8 bytes`\n )\n }\n return freezeV1Value({\n kind,\n name: assertIdentifierField(command.name, \"name\", \"capture command\", 512, failInput),\n occurredAt: assertDateTimeField(command.occurredAt, \"occurredAt\", \"capture command\", failInput),\n properties,\n versions: validateVersionsField(command.versions, \"versions\", \"capture command\", failInput),\n })\n}\n\nconst captureWallNow = (clock: V1CaptureRuntimeClock): { readonly text: string; readonly milliseconds: number } => {\n let raw: unknown\n try {\n raw = clock.wallNow()\n } catch {\n return failInput('capture wall clock threw while being sampled')\n }\n const text = assertDateTime(raw, 'capture wall clock', failInput)\n const milliseconds = Date.parse(text)\n if (!Number.isSafeInteger(milliseconds)) failInput('capture wall clock cannot be represented safely')\n return freezeV1Value({ text, milliseconds })\n}\n\nconst captureMonotonicUs = (clock: V1CaptureRuntimeClock, rounding: 'floor' | 'ceil'): number => {\n let milliseconds: unknown\n try {\n milliseconds = clock.monotonicNowMs()\n } catch {\n return failInput('capture monotonic clock threw while being sampled')\n }\n if (!isNumberValue(milliseconds) || milliseconds < 0) {\n failInput('capture monotonic clock is unavailable')\n }\n const sampledMilliseconds = milliseconds as number\n const microseconds =\n rounding === 'floor' ? Math.floor(sampledMilliseconds * 1000) : Math.ceil(sampledMilliseconds * 1000)\n if (!Number.isSafeInteger(microseconds)) failInput('capture monotonic clock exceeds the exact range')\n return microseconds\n}\n\nconst durableWallFloor = (identity: V1PersistedIdentityState): string => {\n let floor = identity.updatedAt\n if (identity.session && compareV1DateTimes(identity.session.lastActivityAt, floor) > 0) {\n floor = identity.session.lastActivityAt\n }\n if (identity.session?.backgroundedAt && compareV1DateTimes(identity.session.backgroundedAt, floor) > 0) {\n floor = identity.session.backgroundedAt\n }\n return floor\n}\n\nconst planCaptureSession = (\n identity: V1PersistedIdentityState,\n occurredAt: string,\n authority: V1ExecutableCaptureAuthority,\n createSessionId: () => string\n): V1PersistedSessionState => {\n requireTimestampNotBefore(occurredAt, identity.updatedAt, 'capture occurredAt')\n const current = identity.session\n const mustRotate =\n !current ||\n hasV1ElapsedAtLeastSeconds(\n occurredAt,\n current.lastActivityAt,\n Math.min(current.timeoutSeconds, authority.idleTimeoutSeconds)\n ) ||\n hasV1ElapsedAtLeastSeconds(occurredAt, current.startedAt, authority.maximumDurationSeconds)\n if (mustRotate) {\n return freezeV1Value({\n id: assertIdentifier(createSessionId(), 'generated capture session ID', 256, failInput),\n startedAt: occurredAt,\n lastActivityAt: occurredAt,\n timeoutSeconds: authority.idleTimeoutSeconds,\n maximumDurationSeconds: 86_400,\n lifecycle: 'active',\n backgroundedAt: null,\n })\n }\n return freezeV1Value({\n ...current,\n lastActivityAt: occurredAt,\n timeoutSeconds: Math.min(current.timeoutSeconds, authority.idleTimeoutSeconds),\n lifecycle: 'active',\n backgroundedAt: null,\n })\n}\n\n/** Durable queue implementation used by the browser SDK. */\nexport class V1DurableState {\n private readonly _siteKey: string\n private readonly _limits: V1DurableRuntimeLimits\n private readonly _storageFactory: V1DurableStorageFactory\n private readonly _configManager: V1ConfigManager\n private readonly _captureClock: V1CaptureRuntimeClock\n private readonly _createSessionId: () => string\n private _storage: V1DurableStorage | null = null\n private _snapshot: V1DurableStateSnapshot | null = null\n private _poisoned: Error | null = null\n private _tail: Promise<void> = Promise.resolve()\n private _captureAuthority: V1ExecutableCaptureAuthority | null = null\n private _captureAdmissionDecision: object = {}\n private _sealedReplaySourceDecision: object = {}\n private _captureSourceIssuance: V1CaptureSourceIssuance | null = null\n private _captureTerminalIntent: V1PersistedAuthorityBarrier | null = null\n private _captureOwnerEpoch = 0\n private _captureLifecycleEpoch = 0\n private _captureLifecycleProtected = false\n private _captureLifecycleCleanup: (() => void) | null = null\n private _captureConfigSiteId: string | null = null\n // A lost/expired source never restores the larger constructor default. This restriction is\n // not capture authority; every append also compares its exact source with the current row.\n private _queuePolicy: Readonly<{ configIssuedAt: string; configSemanticHash: string; maxBytes: number }> | null = null\n\n constructor(options: V1DurableRuntimeOptions) {\n const normalized = validateOptions(options)\n this._siteKey = normalized.siteKey\n this._limits = normalized.limits\n this._storageFactory = normalized.storageFactory\n this._configManager = new V1ConfigManager(\n normalized.captureRuntime.sha256 ? { sha256: normalized.captureRuntime.sha256 } : {}\n )\n this._captureClock = normalized.captureRuntime.clock\n this._createSessionId = normalized.captureRuntime.createSessionId\n }\n\n initialize(createInitialState: () => V1DurableInitialState): Promise<V1DurableStateSnapshot> {\n return this._serialize(async () => {\n if (this._storage && this._snapshot) {\n return this._snapshot\n }\n // Validate the initializer without invoking it before durable storage is known to be available.\n if (!isFunctionValue(createInitialState)) {\n return failInput('initial state factory must be a function')\n }\n\n this._poisoned = null\n const storage = await this._storageFactory()\n\n try {\n const snapshot = await storage.runTransaction('readwrite', async (transaction) => {\n const existing = await readAuditedState(transaction, this._siteKey)\n if (existing) {\n if (existing.requiresMigrationWrite) await transaction.putInstallation(existing.row)\n return snapshotFromRow(existing.row)\n }\n\n const initial = assertExactRecord(\n createInitialState(),\n ['identity', 'streamId'],\n ['identity', 'streamId'],\n 'initial state',\n failInput\n )\n const row: V1InstallationRow = freezeV1Value({\n storageSchemaVersion: 1,\n siteKey: this._siteKey,\n identity: validateIdentity(initial.identity, 'initial state identity', failInput),\n streamId: assertIdentifier(initial.streamId, 'initial state streamId', 256, failInput),\n nextSequence: 0,\n headSequence: 0,\n queuedCount: 0,\n queuedBytes: 0,\n authorityBarrier: null,\n })\n await transaction.addInstallation(row)\n return snapshotFromRow(row)\n })\n this._storage = storage\n this._snapshot = snapshot\n return snapshot\n } catch (error) {\n storage.close()\n this._rememberFatalFailure(error)\n throw error\n }\n })\n }\n\n installCaptureLifecycleProtection(\n windowTarget: V1CaptureLifecycleWindowTarget,\n documentTarget: V1CaptureLifecycleDocumentTarget\n ): Promise<boolean> {\n return this._serialize(async () => {\n const previousCleanup = this._captureLifecycleCleanup\n this._captureLifecycleCleanup = null\n this._captureLifecycleProtected = false\n this._captureAuthority = null\n try {\n previousCleanup?.()\n } catch {\n // Authority is already cleared; stale observer cleanup is best effort.\n }\n\n if (documentTarget.hidden) return false\n\n const lifecycleEpochBeforeInstall = this._captureLifecycleEpoch\n const invalidate = (): void => {\n this._invalidateCaptureAuthorityForLifecycleNow()\n }\n const invalidateWhenHidden = (): void => {\n if (documentTarget.hidden) invalidate()\n }\n const windowEvents = ['freeze', 'pagehide', 'pageshow', 'resume'] as const\n try {\n for (const event of windowEvents) {\n // Internal security-only target abstraction; the public browser helper is deliberately not wired.\n // eslint-disable-next-line elu-client/no-add-event-listener\n windowTarget.addEventListener(event, invalidate)\n }\n // eslint-disable-next-line elu-client/no-add-event-listener\n documentTarget.addEventListener('visibilitychange', invalidateWhenHidden)\n } catch {\n for (const event of windowEvents) {\n try {\n windowTarget.removeEventListener(event, invalidate)\n } catch {\n // Best-effort cleanup after a partially installed security observer.\n }\n }\n try {\n documentTarget.removeEventListener('visibilitychange', invalidateWhenHidden)\n } catch {\n // Best-effort cleanup after a partially installed security observer.\n }\n return false\n }\n const cleanup = (): void => {\n for (const event of windowEvents) {\n try {\n windowTarget.removeEventListener(event, invalidate)\n } catch {\n // Best-effort cleanup; authority is invalidated before this callback runs.\n }\n }\n try {\n documentTarget.removeEventListener('visibilitychange', invalidateWhenHidden)\n } catch {\n // Best-effort cleanup; authority is invalidated before this callback runs.\n }\n }\n if (documentTarget.hidden || this._captureLifecycleEpoch !== lifecycleEpochBeforeInstall) {\n cleanup()\n return false\n }\n this._captureLifecycleCleanup = cleanup\n this._captureLifecycleProtected = true\n return true\n })\n }\n\n invalidateCaptureAuthorityForLifecycle(): Promise<void> {\n try {\n this._invalidateCaptureAuthorityForLifecycleNow()\n return Promise.resolve()\n } catch (error) {\n return Promise.reject(error)\n }\n }\n\n submitCaptureAuthority(\n configInput: string,\n privacyInput: string | undefined\n ): Promise<V1CaptureAuthorityUpdateResult> {\n this._captureAdmissionDecision = {}\n this._sealedReplaySourceDecision = {}\n return this._serialize(async () => {\n const storage = this._requireStorage()\n if (this._captureTerminalIntent) await this._readCaptureState(storage)\n const witness = await storage.runTransaction('readonly', async (transaction) =>\n readInstallation(transaction, this._siteKey)\n )\n let initialWall: ReturnType<typeof captureWallNow>\n let initialMonotonicUs: number\n try {\n initialWall = captureWallNow(this._captureClock)\n initialMonotonicUs = captureMonotonicUs(this._captureClock, 'floor')\n } catch {\n return this._persistConfigTerminal(storage, 'capture clock discontinuity during authority submission')\n }\n const evaluation = await this._configManager.evaluateCaptureCandidate(\n configInput,\n privacyInput,\n freezeV1Value({\n contextRevision: witness.identity.contextRevision,\n optedOut: witness.identity.optedOut,\n }),\n initialWall.milliseconds\n )\n if (evaluation.status === 'stale') return evaluation\n this._rememberCaptureSourceIssuance(\n evaluation,\n initialWall.text,\n initialMonotonicUs,\n durableWallFloor(witness.identity)\n )\n\n const candidateSiteId = evaluation.configSiteId\n if (candidateSiteId) {\n if (this._captureConfigSiteId && this._captureConfigSiteId !== candidateSiteId) {\n this._captureAuthority = null\n return freezeV1Value({\n status: 'terminal',\n reason: 'config site identity changed during the owner lifetime',\n })\n } else if (!this._captureConfigSiteId) {\n this._captureConfigSiteId = candidateSiteId\n }\n }\n const queueBytes = this._configManager.activeQueueByteLimit(initialWall.milliseconds)\n const source = evaluation.status === 'authorized' ? evaluation : evaluation.trustedBoundary\n if (queueBytes !== null && source) {\n this._queuePolicy = freezeV1Value({\n configIssuedAt: source.configIssuedAt,\n configSemanticHash: source.configSemanticHash,\n maxBytes: Math.min(this._limits.maxBytes, queueBytes),\n })\n }\n if (evaluation.status === 'authorized' && !this._captureLifecycleProtected) {\n this._captureAuthority = null\n return freezeV1Value({\n status: 'terminal',\n reason: 'browser lifecycle protection is not installed',\n })\n }\n return this._installCaptureEvaluation(storage, witness, evaluation)\n })\n }\n\n captureActivity(command: V1CaptureActivityCommand): Promise<V1DurableAppendResult> {\n return this._snapshotAndSerialize(\n () => normalizeCaptureCommand(command),\n async (normalized) => {\n const storage = this._requireStorage()\n type V1CaptureTransactionResult =\n | Readonly<{ status: 'captured'; value: V1DurableAppendResult }>\n | Readonly<{ status: 'terminal'; row: V1InstallationRow; reason: string }>\n const attempt = (): Promise<V1DurableCommitOutcome<V1CaptureTransactionResult>> =>\n storage.runTransactionWithCommitOutcome<V1CaptureTransactionResult>(async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n let authority: V1ExecutableCaptureAuthority\n try {\n authority = this._requireCaptureAuthority(current)\n } catch (error) {\n if (!(error instanceof V1CaptureConfigTerminalError)) throw error\n const merged = mergeAuthorityBarrier(\n current.authorityBarrier,\n terminalBarrierAtCurrentConfig(current.authorityBarrier, 'config')\n )\n const row: V1InstallationRow = freezeV1Value({ ...current, authorityBarrier: merged })\n if (!sameAuthorityBarrier(current.authorityBarrier, merged)) {\n await transaction.putInstallation(row)\n }\n return freezeV1Value({ status: 'terminal', row, reason: error.message })\n }\n const session = planCaptureSession(\n current.identity,\n normalized.occurredAt,\n authority,\n this._createSessionId\n )\n const identity = validateSessionTransition(current.identity, session, true)\n const eventRow: V1InstallationRow = freezeV1Value({ ...current, identity })\n const draft: V1NormalizedEventDraft = freezeV1Value({\n ...normalized,\n expectedSessionId: session.id,\n })\n const encoded = [encodeEventDraft(draft, eventRow, current.nextSequence)]\n return freezeV1Value({\n status: 'captured',\n value: await this._commitAppend(transaction, current, identity, encoded),\n })\n })\n\n let outcome = await attempt()\n if (outcome.status === 'proven-not-committed' && !(outcome.error instanceof V1DurableStateError)) {\n outcome = await attempt()\n }\n if (outcome.status === 'unknown') {\n this._captureAuthority = null\n throw new V1DurableStateError('commit-unknown', 'capture commit outcome is unknown')\n }\n if (outcome.status === 'proven-not-committed') throw outcome.error\n if (outcome.value.status === 'terminal') {\n this._captureAuthority = null\n this._snapshot = snapshotFromRow(outcome.value.row)\n return fail('capture-not-authorized', outcome.value.reason)\n }\n this._snapshot = outcome.value.value.state\n return outcome.value.value\n }\n )\n }\n\n registerCaptureSuperProperties(properties: V1JsonObject, occurredAt: string): Promise<V1DurableAppendResult> {\n return this.applyLocal({\n type: 'setSuperProperties',\n set: properties,\n setOnce: freezeV1Value({}),\n unset: freezeV1Value([]),\n occurredAt,\n })\n }\n\n unregisterCaptureSuperProperties(\n properties: readonly string[],\n occurredAt: string\n ): Promise<V1DurableAppendResult> {\n return this.applyLocal({\n type: 'setSuperProperties',\n set: freezeV1Value({}),\n setOnce: freezeV1Value({}),\n unset: properties,\n occurredAt,\n })\n }\n\n setCaptureOptedOut(optedOut: boolean, occurredAt: string): Promise<V1DurableAppendResult> {\n return this.applyLocal({ type: 'setOptedOut', optedOut, occurredAt })\n }\n\n appendEvents(\n sessionUpdate: V1DurableEventSessionUpdate,\n eventDrafts: readonly V1DurableEventDraft[]\n ): Promise<V1DurableAppendResult> {\n return this._snapshotAndSerialize(\n () =>\n freezeV1Value({\n sessionUpdate: normalizeSessionUpdate(sessionUpdate),\n drafts: normalizeEventDrafts(eventDrafts),\n }),\n this._appendTransactionOperation<Readonly<{ sessionUpdate: ReturnType<typeof normalizeSessionUpdate>; drafts: readonly V1NormalizedEventDraft[] }>>(\n (transaction, current, { sessionUpdate: normalizedUpdate, drafts }) => {\nif (current.nextSequence > Number.MAX_SAFE_INTEGER - drafts.length) {\n return fail('sequence-exhausted', 'queue sequence space is exhausted')\n }\nlet identity = current.identity\nif (normalizedUpdate.type === 'replace') {\n requireCurrentSessionId(\n current.identity,\n normalizedUpdate.expectedCurrentSessionId,\n 'event session replacement'\n )\n identity = validateSessionTransition(current.identity, normalizedUpdate.session, true)\n }\nvalidateEventCausality(current.identity.updatedAt, identity.session, drafts)\nconst eventRow: V1InstallationRow = freezeV1Value({\n ...current,\n identity,\n })\nconst encoded = drafts.map((draft, index) =>\n encodeEventDraft(draft, eventRow, current.nextSequence + index)\n )\nreturn this._commitAppend(transaction, current, identity, encoded)\n },\n (result) => {\nthis._snapshot = result.state\nthis._captureAuthority = null\nreturn result\n }\n )\n )\n }\n\n appendMutations(mutationDrafts: readonly V1DurableMutationDraft[]): Promise<V1DurableAppendResult> {\n return this._snapshotAndSerialize(\n () => {\n const drafts = normalizeMutationDrafts(mutationDrafts)\n this._captureAdmissionDecision = {}\n this._captureAuthority = null\n return drafts\n },\n this._appendTransactionOperation<readonly V1NormalizedMutationDraft[]>(\n (transaction, current, drafts) => {\nif (current.nextSequence > Number.MAX_SAFE_INTEGER - drafts.length) {\n return fail('sequence-exhausted', 'queue sequence space is exhausted')\n }\nlet identity = current.identity\nconst encoded: V1EncodedRecord[] = []\nfor (let index = 0; index < drafts.length; index += 1) {\n identity = applyMutation(identity, drafts[index])\n const mutationRow: V1InstallationRow = freezeV1Value({ ...current, identity })\n encoded.push(encodeMutationDraft(drafts[index], mutationRow, current.nextSequence + index))\n }\nreturn this._commitAppend(transaction, current, identity, encoded)\n },\n (result) => {\nthis._snapshot = result.state\nthis._captureAuthority = null\nreturn result\n }\n )\n )\n }\n\n applyLocal(change: V1DurableLocalChange): Promise<V1DurableAppendResult> {\n return this._snapshotAndSerialize(\n () => {\n const normalized = normalizeLocalChange(change)\n this._captureAdmissionDecision = {}\n if (\n normalized.type === 'setOptedOut' ||\n normalized.type === 'resetGroups' ||\n normalized.type === 'resetIdentity' ||\n normalized.type === 'setSuperProperties' ||\n normalized.type === 'setFlagContext'\n )\n this._captureAuthority = null\n return normalized\n },\n this._appendTransactionOperation<ReturnType<typeof normalizeLocalChange>>(\n (transaction, current, normalizedChange) => {\nconst identity = applyLocalChange(current.identity, normalizedChange)\nreturn this._commitAppend(transaction, current, identity, [])\n },\n (result, normalizedChange) => {\nthis._snapshot = result.state\nif (\n normalizedChange.type === 'setOptedOut' ||\n normalizedChange.type === 'resetGroups' ||\n normalizedChange.type === 'resetIdentity' ||\n normalizedChange.type === 'setSuperProperties' ||\n normalizedChange.type === 'setFlagContext'\n ) {\n this._captureAuthority = null\n }\nreturn result\n }\n )\n )\n }\n\n peek(maxCount: number, maxRecordBytes: number): Promise<readonly V1DurableQueueRecord[]> {\n return this._serialize(async () => {\n const count = assertInteger(maxCount, 'peek maxCount', 0, 1000, failInput)\n const recordBytes = assertInteger(\n maxRecordBytes,\n 'peek maxRecordBytes',\n 0,\n V1_MAX_DURABLE_QUEUE_BYTES,\n failInput\n )\n const storage = this._requireStorage()\n try {\n const result = await storage.runTransaction('readonly', async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n const prefix: V1DurableQueueRecord[] = []\n let prefixBytes = 0\n let visited = 0\n let stoppedForBytes = false\n const requestedCount = Math.min(count, current.queuedCount)\n await transaction.scanRecordPrefix(\n this._siteKey,\n current.headSequence,\n requestedCount,\n (rawRecord) => {\n const record = validateStoredRecord(rawRecord, current, current.headSequence + visited)\n visited += 1\n if (prefixBytes + record.payloadBytes > recordBytes) {\n if (prefix.length === 0) {\n return fail(\n 'head-record-too-large',\n 'live queue head exceeds the requested record-byte budget'\n )\n }\n stoppedForBytes = true\n return false\n }\n prefix.push(record)\n prefixBytes += record.payloadBytes\n return true\n }\n )\n if (!stoppedForBytes && visited !== requestedCount) {\n return failCorrupt('stored queue ended before its bounded live prefix')\n }\n return freezeV1Value({ state: snapshotFromRow(current), prefix: freezeV1Value(prefix) })\n })\n this._snapshot = result.state\n return result.prefix\n } catch (error) {\n this._rememberFatalFailure(error)\n throw error\n }\n })\n }\n\n acknowledge(value: V1DurableAcknowledgement): Promise<V1DurableStateSnapshot> {\n return this._snapshotAndSerialize(\n () => normalizeAcknowledgement(value),\n async (acknowledgement) => {\n if (acknowledgement.siteKey !== this._siteKey) {\n return failInput('acknowledgement site key does not match this durable runtime')\n }\n const storage = this._requireStorage()\n try {\n const snapshot = await storage.runTransaction('readwrite', async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n if (acknowledgement.streamId !== current.streamId) {\n return failInput('acknowledgement stream does not match the live queue')\n }\n if (acknowledgement.records.length === 0) {\n return snapshotFromRow(current)\n }\n\n for (const reference of acknowledgement.records) {\n if (\n reference.recordId !== recordIdFor(reference.kind, current.streamId, reference.sequence)\n ) {\n return failInput(\n 'acknowledgement contains a record ID not derived from its stream prefix'\n )\n }\n }\n\n const firstSequence = acknowledgement.records[0].sequence\n const lastSequence = acknowledgement.records[acknowledgement.records.length - 1].sequence\n if (lastSequence < current.headSequence) {\n return snapshotFromRow(current)\n }\n if (firstSequence !== current.headSequence) {\n return failInput('acknowledgement is not the exact current queue prefix')\n }\n if (acknowledgement.records.length > current.queuedCount) {\n return failInput('acknowledgement extends beyond the live queue')\n }\n\n let visited = 0\n let deletedBytes = 0\n await transaction.scanRecordPrefix(\n this._siteKey,\n current.headSequence,\n acknowledgement.records.length,\n (rawRecord) => {\n const record = validateStoredRecord(rawRecord, current, current.headSequence + visited)\n if (!sameReference(acknowledgement.records[visited], record)) {\n return failInput('acknowledgement does not exactly match the live queue prefix')\n }\n deletedBytes += record.payloadBytes\n visited += 1\n return true\n }\n )\n if (visited !== acknowledgement.records.length) {\n return failCorrupt('stored queue ended before the acknowledged live prefix')\n }\n if (\n deletedBytes > current.queuedBytes ||\n (acknowledgement.records.length === current.queuedCount &&\n deletedBytes !== current.queuedBytes)\n ) {\n return failCorrupt('stored queue byte count does not match the acknowledged prefix')\n }\n\n for (const reference of acknowledgement.records) {\n await transaction.deleteRecord(this._siteKey, reference.sequence)\n }\n const nextRow: V1InstallationRow = freezeV1Value({\n ...current,\n headSequence: current.headSequence + acknowledgement.records.length,\n queuedCount: current.queuedCount - acknowledgement.records.length,\n queuedBytes: current.queuedBytes - deletedBytes,\n })\n await transaction.putInstallation(nextRow)\n return snapshotFromRow(nextRow)\n })\n this._snapshot = snapshot\n return snapshot\n } catch (error) {\n this._rememberFatalFailure(error)\n throw error\n }\n }\n )\n }\n\n /**\n * Captures this exact local authority decision. The caller supplies the transaction-current\n * installation, never snapshot(). A renewal, lifecycle boundary, close, changed identity or\n * cross-realm barrier invalidates the guard; it does not authorize a chunk's own identity.\n */\n captureAdmissionGuard(): V1CaptureAdmissionGuard {\n const authority = this._captureAuthority\n const decision = this._captureAdmissionDecision\n return (installation) => {\n const denied = freezeV1Value({ authorized: false, terminalInstallation: null })\n if (this._captureTerminalIntent && this._storage && !this._poisoned) {\n const current = validateInstallation(installation, this._siteKey)\n return freezeV1Value({\n authorized: false,\n terminalInstallation: freezeV1Value({\n ...current,\n authorityBarrier: mergeAuthorityBarrier(current.authorityBarrier, this._captureTerminalIntent),\n }),\n })\n }\n if (\n !authority ||\n decision !== this._captureAdmissionDecision ||\n this._poisoned ||\n !this._storage ||\n this._captureAuthority !== authority\n )\n return denied\n const current = validateInstallation(installation, this._siteKey)\n try {\n const authorized = this._requireCaptureAuthority(current) === authority\n const session = current.identity.session\n const now = captureWallNow(this._captureClock).text\n if (\n !session ||\n session.lifecycle !== 'active' ||\n compareV1DateTimes(now, session.lastActivityAt) < 0 ||\n hasV1ElapsedAtLeastSeconds(\n now,\n session.lastActivityAt,\n Math.min(session.timeoutSeconds, authority.idleTimeoutSeconds)\n ) ||\n hasV1ElapsedAtLeastSeconds(now, session.startedAt, authority.maximumDurationSeconds)\n )\n return denied\n return freezeV1Value({\n authorized,\n terminalInstallation: null,\n })\n } catch (error) {\n this._captureAuthority = null\n if (error instanceof V1CaptureConfigTerminalError) {\n this._captureTerminalIntent = terminalBarrierAtCurrentConfig(current.authorityBarrier, 'config')\n return freezeV1Value({\n authorized: false,\n terminalInstallation: freezeV1Value({\n ...current,\n authorityBarrier: mergeAuthorityBarrier(\n current.authorityBarrier,\n this._captureTerminalIntent\n ),\n }),\n })\n }\n if (error instanceof V1DurableStateError && error.code === 'capture-not-authorized') return denied\n throw error\n }\n }\n }\n\n /**\n * Original local source lease only. This consumes the same clock/lifecycle validator as capture\n * admission; it grants no transaction-current identity, session or cross-tab storage authority.\n */\n captureSourceGuard(): () => boolean {\n return this.captureSourceLease().isCurrent\n }\n\n /** Scheduling projection of this exact original lease; it never installs or renews authority. */\n captureSourceLease(): Readonly<{ isCurrent(): boolean; remainingMicroseconds(): number }> {\n const authority = this._captureAuthority\n const decision = this._captureAdmissionDecision\n const remainingMicroseconds = (): number => {\n if (\n !authority ||\n this._captureAuthority !== authority ||\n this._captureAdmissionDecision !== decision ||\n this._captureTerminalIntent ||\n this._poisoned ||\n !this._storage\n )\n return 0\n try {\n this._requireCaptureLifecycle(authority)\n return this._requireCaptureSourceClock(authority)\n } catch (error) {\n this._captureAuthority = null\n if (error instanceof V1CaptureConfigTerminalError) {\n this._captureTerminalIntent = terminalBarrierAtCurrentConfig(authority.barrier, 'config')\n }\n return 0\n }\n }\n return freezeV1Value({ isCurrent: () => remainingMicroseconds() > 0, remainingMicroseconds })\n }\n\n /**\n * Restriction for already-sealed replay, bound to the exact installed source. Local identity or\n * session changes do not extend or revoke that source's clock. This grants no fresh capture or\n * storage authority; the caller must separately check the transaction-current config witness.\n */\n sealedReplaySourceGuard(): () => boolean {\n const authority = this._captureAuthority\n const sourceDecision = this._sealedReplaySourceDecision\n let denied = !authority\n return () => {\n if (\n denied ||\n !authority ||\n this._sealedReplaySourceDecision !== sourceDecision ||\n this._captureTerminalIntent?.terminalScope === 'config' ||\n this._poisoned ||\n !this._storage ||\n this._snapshot?.identity.optedOut ||\n this._snapshot?.streamId !== authority.streamId\n )\n return false\n try {\n this._requireCaptureLifecycle(authority)\n this._requireCaptureSourceClock(authority)\n return true\n } catch (error) {\n denied = true\n if (error instanceof V1CaptureConfigTerminalError) {\n this._captureAuthority = null\n this._captureTerminalIntent = terminalBarrierAtCurrentConfig(authority.barrier, 'config')\n }\n return false\n }\n }\n }\n\n /** Reads the owner inside a transaction; a cached snapshot is never authority for replay. */\n readCaptureState(): Promise<V1DurableCaptureState> {\n return this._serialize(() => this._readCaptureState(this._requireStorage()))\n }\n\n private async _readCaptureState(storage: V1DurableStorage): Promise<V1DurableCaptureState> {\n const authority = this._captureAuthority\n const decision = this._captureAdmissionDecision\n type CaptureRead = V1DurableCaptureState &\n Readonly<{ installation: V1InstallationRow; terminalIntent: V1PersistedAuthorityBarrier | null }>\n const attempt = (): Promise<V1DurableCommitOutcome<CaptureRead>> =>\n storage.runTransactionWithCommitOutcome(async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n if (!this._captureTerminalIntent) {\n try {\n this._requireCaptureAuthority(current)\n return freezeV1Value({\n state: snapshotFromRow(current),\n authorityBarrier: current.authorityBarrier,\n installation: current,\n authorized: true,\n terminalIntent: null,\n })\n } catch (error) {\n if (error instanceof V1CaptureConfigTerminalError) {\n // Close local admission immediately, retaining the exact barrier until commit.\n this._captureAuthority = null\n this._captureTerminalIntent = terminalBarrierAtCurrentConfig(\n current.authorityBarrier,\n 'config'\n )\n } else if (error instanceof V1DurableStateError && error.code === 'capture-not-authorized') {\n return freezeV1Value({\n state: snapshotFromRow(current),\n authorityBarrier: current.authorityBarrier,\n installation: current,\n authorized: false,\n terminalIntent: null,\n })\n } else {\n throw error\n }\n }\n }\n const terminalIntent = this._captureTerminalIntent!\n const barrier = mergeAuthorityBarrier(current.authorityBarrier, terminalIntent)\n const row: V1InstallationRow = freezeV1Value({ ...current, authorityBarrier: barrier })\n if (!sameAuthorityBarrier(current.authorityBarrier, barrier)) await transaction.putInstallation(row)\n return freezeV1Value({\n state: snapshotFromRow(row),\n authorityBarrier: row.authorityBarrier,\n installation: row,\n authorized: false,\n terminalIntent,\n })\n })\n let outcome = await attempt()\n if (outcome.status === 'proven-not-committed' && !(outcome.error instanceof V1DurableStateError)) {\n outcome = await attempt()\n }\n if (outcome.status !== 'committed') {\n this._captureAuthority = null\n if (outcome.status === 'unknown') {\n throw new V1DurableStateError('commit-unknown', 'capture state read commit outcome is unknown')\n }\n this._rememberFatalFailure(outcome.error)\n throw outcome.error\n }\n if (this._captureTerminalIntent === outcome.value.terminalIntent) this._captureTerminalIntent = null\n this._snapshot = outcome.value.state\n let authorized = Boolean(\n outcome.value.authorized &&\n authority &&\n this._captureAuthority === authority &&\n this._captureAdmissionDecision === decision &&\n !this._captureTerminalIntent\n )\n if (authorized) {\n // A commit may wait while consent changes, close is queued, or either clock expires.\n try {\n authorized = this._requireCaptureAuthority(outcome.value.installation) === authority\n } catch (error) {\n this._captureAuthority = null\n if (error instanceof V1CaptureConfigTerminalError) {\n this._captureTerminalIntent = terminalBarrierAtCurrentConfig(\n outcome.value.installation.authorityBarrier,\n 'config'\n )\n // The retained intent makes this a bounded second read which only persists denial.\n return this._readCaptureState(storage)\n }\n if (!(error instanceof V1DurableStateError && error.code === 'capture-not-authorized')) throw error\n authorized = false\n }\n }\n if (!authorized) this._captureAuthority = null\n return freezeV1Value({\n state: outcome.value.state,\n authorityBarrier: outcome.value.authorityBarrier,\n authorized,\n })\n }\n\n snapshot(): V1DurableStateSnapshot {\n if (this._poisoned) {\n throw this._poisoned\n }\n if (!this._snapshot) {\n return fail('not-initialized', 'durable state is not initialized')\n }\n return this._snapshot\n }\n\n close(): Promise<void> {\n this._sealedReplaySourceDecision = {}\n this._captureAdmissionDecision = {}\n this._captureAuthority = null\n return this._serialize(async () => {\n const cleanup = this._captureLifecycleCleanup\n this._captureLifecycleCleanup = null\n this._captureLifecycleProtected = false\n this._captureAuthority = null\n this._captureConfigSiteId = null\n this._configManager.clear()\n try {\n cleanup?.()\n } catch {\n // Best-effort listener cleanup must not prevent storage shutdown.\n }\n this._storage?.close()\n this._storage = null\n this._snapshot = null\n this._poisoned = null\n })\n }\n\n private async _persistConfigTerminal(\n storage: V1DurableStorage,\n reason: string\n ): Promise<V1CaptureAuthorityUpdateResult> {\n const attempt = (): Promise<V1DurableCommitOutcome<V1InstallationRow>> =>\n storage.runTransactionWithCommitOutcome(async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n const merged = mergeAuthorityBarrier(\n current.authorityBarrier,\n terminalBarrierAtCurrentConfig(current.authorityBarrier, 'config')\n )\n const row: V1InstallationRow = freezeV1Value({ ...current, authorityBarrier: merged })\n if (!sameAuthorityBarrier(current.authorityBarrier, merged)) await transaction.putInstallation(row)\n return row\n })\n\n let outcome = await attempt()\n if (outcome.status === 'proven-not-committed' && !(outcome.error instanceof V1DurableStateError)) {\n outcome = await attempt()\n }\n this._captureAuthority = null\n if (outcome.status === 'unknown') {\n throw new V1DurableStateError('commit-unknown', 'config terminal barrier commit outcome is unknown')\n }\n if (outcome.status === 'proven-not-committed') throw outcome.error\n this._snapshot = snapshotFromRow(outcome.value)\n return freezeV1Value({ status: 'terminal', reason })\n }\n\n private _rememberCaptureSourceIssuance(\n evaluation: Exclude<V1CaptureCandidateEvaluation, Readonly<{ status: 'stale'; reason: string }>>,\n installedWallAt: string,\n monotonicStartedAtUs: number,\n observedWallFloor: string\n ): void {\n const boundary = evaluation.status === 'authorized' ? evaluation : evaluation.trustedBoundary\n if (!boundary) return\n const previous = this._captureSourceIssuance\n // Only the config manager's validated newer issuance can replace this clock. Equal\n // timestamps with conflicting content are terminal at that manager, never a new lease.\n if (previous && compareV1DateTimes(boundary.configIssuedAt, previous.configIssuedAt) <= 0) return\n this._captureSourceIssuance = freezeV1Value({\n token: {},\n configIssuedAt: boundary.configIssuedAt,\n configSemanticHash: boundary.configSemanticHash,\n installedWallAt,\n observedWallFloor,\n monotonicStartedAtUs,\n monotonicBudgetUs: null,\n denied: false,\n })\n }\n\n private _sourceClockForCapture(\n evaluation: Extract<V1CaptureCandidateEvaluation, { status: 'authorized' }>,\n identity: V1PersistedIdentityState\n ): V1CaptureSourceClock {\n const source = this._captureSourceIssuance\n if (\n !source ||\n source.denied ||\n source.configIssuedAt !== evaluation.configIssuedAt ||\n source.configSemanticHash !== evaluation.configSemanticHash\n ) {\n throw new V1CaptureConfigTerminalError('capture source issuance is unavailable')\n }\n try {\n const initialBudget = Math.max(\n 0,\n Math.min(\n v1MicrosecondsBetween(evaluation.expiresAt, source.installedWallAt),\n v1MicrosecondsBetween(evaluation.expiresAt, evaluation.configIssuedAt),\n v1MicrosecondsBetween(evaluation.expiresAt, source.observedWallFloor)\n )\n )\n const elapsed = captureMonotonicUs(this._captureClock, 'ceil') - source.monotonicStartedAtUs\n // Translate a newer durable wall floor to an absolute deadline on the ORIGINAL monotonic\n // axis. Subtracting it directly from the total budget would charge normal elapsed time twice.\n const floorDeadline =\n elapsed + Math.max(0, v1MicrosecondsBetween(evaluation.expiresAt, durableWallFloor(identity)))\n if (!Number.isSafeInteger(floorDeadline)) {\n this._captureSourceIssuance = freezeV1Value({ ...source, denied: true })\n throw new V1CaptureConfigTerminalError('capture source clock deadline is unsafe')\n }\n const monotonicBudgetUs = Math.min(source.monotonicBudgetUs ?? initialBudget, initialBudget, floorDeadline)\n this._captureSourceIssuance = freezeV1Value({ ...source, monotonicBudgetUs })\n const clock = freezeV1Value({\n sourceToken: source.token,\n expiresAt: evaluation.expiresAt,\n installedWallAt: source.installedWallAt,\n monotonicStartedAtUs: source.monotonicStartedAtUs,\n monotonicBudgetUs,\n })\n this._requireCaptureSourceClock(clock)\n return clock\n } catch (error) {\n if (this._captureSourceIssuance?.token === source.token) {\n this._captureSourceIssuance = freezeV1Value({ ...this._captureSourceIssuance, denied: true })\n }\n throw error\n }\n }\n\n private async _installCaptureEvaluation(\n storage: V1DurableStorage,\n witness: V1InstallationRow,\n evaluation: Exclude<V1CaptureCandidateEvaluation, Readonly<{ status: 'stale'; reason: string }>>\n ): Promise<V1CaptureAuthorityUpdateResult> {\n interface V1InstallResult {\n readonly row: V1InstallationRow\n readonly authority: V1ExecutableCaptureAuthority | null\n readonly reason: string\n }\n const originalBarrier = barrierForEvaluation(evaluation)\n const prospectiveOwnerEpoch = incrementStateCounter(this._captureOwnerEpoch, 'capture owner epoch')\n const attempt = () =>\n storage.runTransactionWithCommitOutcome<V1InstallResult>(async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n let incoming = originalBarrier\n let executable: V1ExecutableCaptureAuthority | null = null\n let reason = evaluation.status === 'terminal' ? evaluation.reason : 'capture authority conflict'\n\n if (evaluation.status === 'authorized') {\n const witnessMatches =\n current.streamId === witness.streamId &&\n current.identity.revision === witness.identity.revision &&\n current.identity.contextRevision === witness.identity.contextRevision &&\n current.identity.optedOut === witness.identity.optedOut &&\n current.identity.contextRevision === evaluation.contextRevision &&\n !current.identity.optedOut\n if (!witnessMatches) {\n incoming = terminalBarrierFrom(\n {\n configIssuedAt: evaluation.configIssuedAt,\n configSemanticHash: evaluation.configSemanticHash,\n policySourceHash: evaluation.policySourceHash,\n contextRevision: evaluation.contextRevision,\n decisionHash: evaluation.decisionHash,\n },\n 'context'\n )\n reason = 'identity or consent witness changed before authority installation'\n } else if (this._captureConfigSiteId !== evaluation.configSiteId) {\n incoming = terminalBarrierFrom(\n {\n configIssuedAt: evaluation.configIssuedAt,\n configSemanticHash: evaluation.configSemanticHash,\n policySourceHash: evaluation.policySourceHash,\n contextRevision: evaluation.contextRevision,\n decisionHash: evaluation.decisionHash,\n },\n 'config'\n )\n reason = 'config site identity does not match the owner pin'\n } else {\n try {\n const source = this._sourceClockForCapture(evaluation, current.identity)\n executable = freezeV1Value({\n ...source,\n ownerEpoch: prospectiveOwnerEpoch,\n barrier: incoming,\n configSiteId: evaluation.configSiteId,\n streamId: current.streamId,\n identityRevision: current.identity.revision,\n contextRevision: current.identity.contextRevision,\n identityOptedOut: false,\n lifecycleEpoch: this._captureLifecycleEpoch,\n idleTimeoutSeconds: evaluation.idleTimeoutSeconds,\n maximumDurationSeconds: 86_400,\n })\n reason = 'authorized'\n } catch {\n incoming = terminalBarrierFrom(\n {\n configIssuedAt: evaluation.configIssuedAt,\n configSemanticHash: evaluation.configSemanticHash,\n policySourceHash: evaluation.policySourceHash,\n contextRevision: evaluation.contextRevision,\n decisionHash: evaluation.decisionHash,\n },\n 'config'\n )\n reason = 'capture clock discontinuity during authority installation'\n }\n }\n }\n\n const merged = mergeAuthorityBarrier(current.authorityBarrier, incoming)\n if (!sameAuthorityBarrier(merged, incoming) || merged.kind !== 'authorized') executable = null\n if (!sameAuthorityBarrier(merged, incoming) && evaluation.status === 'authorized') {\n reason = 'persisted authority boundary rejected the candidate'\n }\n const row: V1InstallationRow = freezeV1Value({ ...current, authorityBarrier: merged })\n if (!sameAuthorityBarrier(current.authorityBarrier, merged)) await transaction.putInstallation(row)\n return freezeV1Value({ row, authority: executable, reason })\n })\n\n let outcome = await attempt()\n if (outcome.status === 'proven-not-committed' && !(outcome.error instanceof V1DurableStateError)) {\n outcome = await attempt()\n }\n if (outcome.status === 'unknown') {\n this._captureAuthority = null\n throw new V1DurableStateError('commit-unknown', 'authority barrier commit outcome is unknown')\n }\n if (outcome.status === 'proven-not-committed') throw outcome.error\n this._captureOwnerEpoch = prospectiveOwnerEpoch\n this._captureAuthority = outcome.value.authority\n this._snapshot = snapshotFromRow(outcome.value.row)\n if (outcome.value.authority) {\n try {\n this._requireCaptureSourceClock(outcome.value.authority)\n } catch (error) {\n if (!(error instanceof V1CaptureConfigTerminalError)) throw error\n this._captureAuthority = null\n this._captureTerminalIntent = terminalBarrierAtCurrentConfig(\n outcome.value.row.authorityBarrier,\n 'config'\n )\n await this._readCaptureState(storage)\n return freezeV1Value({ status: 'terminal', reason: 'capture source expired before publication' })\n }\n }\n return outcome.value.authority\n ? freezeV1Value({ status: 'authorized', ownerEpoch: prospectiveOwnerEpoch })\n : freezeV1Value({ status: 'terminal', reason: outcome.value.reason })\n }\n\n private _requireCaptureAuthority(current: V1InstallationRow): V1ExecutableCaptureAuthority {\n const authority = this._captureAuthority\n const reject = (message: string): never => {\n this._captureAuthority = null\n return fail('capture-not-authorized', message)\n }\n if (!authority) return reject('capture authority is absent')\n this._requireCaptureLifecycle(authority)\n if (\n authority.configSiteId !== this._captureConfigSiteId ||\n authority.streamId !== current.streamId ||\n authority.identityRevision !== current.identity.revision ||\n authority.contextRevision !== current.identity.contextRevision ||\n authority.identityOptedOut !== current.identity.optedOut ||\n current.identity.optedOut\n ) {\n return reject('capture authority identity, consent, site, or stream witness changed')\n }\n if (!sameAuthorityBarrier(authority.barrier, current.authorityBarrier)) {\n return reject('capture authority no longer matches the persisted cross-tab barrier')\n }\n\n this._requireCaptureSourceClock(authority)\n return authority\n }\n\n private _requireCaptureLifecycle(authority: V1ExecutableCaptureAuthority): void {\n if (!this._captureLifecycleProtected || authority.lifecycleEpoch !== this._captureLifecycleEpoch) {\n this._captureAuthority = null\n return fail('capture-not-authorized', 'capture authority crossed a page lifecycle boundary')\n }\n }\n\n private _requireCaptureSourceClock(authority: V1CaptureSourceClock): number {\n const configTerminal = (message: string): never => {\n const source = this._captureSourceIssuance\n if (source?.token === authority.sourceToken) {\n this._captureSourceIssuance = freezeV1Value({ ...source, denied: true })\n }\n throw new V1CaptureConfigTerminalError(message)\n }\n if (this._captureSourceIssuance?.token === authority.sourceToken && this._captureSourceIssuance.denied) {\n return configTerminal('capture source clock was already denied')\n }\n let wall: ReturnType<typeof captureWallNow>\n let monotonicNowUs: number\n try {\n wall = captureWallNow(this._captureClock)\n monotonicNowUs = captureMonotonicUs(this._captureClock, 'ceil')\n } catch {\n return configTerminal('capture clock discontinuity while validating authority')\n }\n if (compareV1DateTimes(wall.text, authority.expiresAt) >= 0) {\n return configTerminal('capture authority reached its wall-clock expiry')\n }\n if (monotonicNowUs < authority.monotonicStartedAtUs) {\n return configTerminal('capture monotonic clock moved backward')\n }\n const monotonicElapsed = monotonicNowUs - authority.monotonicStartedAtUs\n if (monotonicElapsed >= authority.monotonicBudgetUs) {\n return configTerminal('capture authority reached its monotonic expiry')\n }\n let wallElapsed: number\n try {\n wallElapsed = v1MicrosecondsBetween(wall.text, authority.installedWallAt)\n } catch {\n return configTerminal('capture wall-clock duration is unsafe')\n }\n if (wallElapsed < 0 || Math.abs(wallElapsed - monotonicElapsed) > 1_000_000) {\n return configTerminal('capture wall and monotonic clocks observed a lifecycle discontinuity')\n }\n return Math.max(\n 0,\n Math.min(\n authority.monotonicBudgetUs - monotonicElapsed,\n v1MicrosecondsBetween(authority.expiresAt, wall.text)\n )\n )\n }\n\n private async _commitAppend(\n transaction: V1DurableStorageTransaction,\n current: V1InstallationRow,\n identity: V1PersistedIdentityState,\n encoded: readonly V1EncodedRecord[]\n ): Promise<V1DurableAppendResult> {\n let addedBytes = 0\n for (const record of encoded) {\n if (!Number.isSafeInteger(addedBytes + record.stored.payloadBytes)) {\n return fail('queue-full', 'record bytes exceed the queue counter range')\n }\n addedBytes += record.stored.payloadBytes\n }\n const nextCount = current.queuedCount + encoded.length\n const nextBytes = current.queuedBytes + addedBytes\n // The limits are aggregate per site: live replay chunks share the count and byte budget.\n const aggregateCount = nextCount + (current.replay?.replayQueuedCount ?? 0)\n const aggregateBytes = nextBytes + (current.replay?.replayQueuedBytes ?? 0)\n const policy = this._queuePolicy\n if (\n encoded.length > 0 &&\n (policy || current.authorityBarrier) &&\n (!policy || current.authorityBarrier?.configIssuedAt !== policy.configIssuedAt ||\n current.authorityBarrier?.configSemanticHash !== policy.configSemanticHash)\n ) {\n return fail('queue-full', 'queue policy no longer matches the transaction-current configuration')\n }\n if (\n encoded.length > 0 &&\n (!Number.isSafeInteger(aggregateCount) ||\n !Number.isSafeInteger(aggregateBytes) ||\n aggregateCount > this._limits.maxCount ||\n aggregateBytes > (policy?.maxBytes ?? this._limits.maxBytes))\n ) {\n return fail('queue-full', 'newest append exceeds the durable queue limit')\n }\n\n const nextRow: V1InstallationRow = freezeV1Value({\n ...current,\n identity,\n nextSequence: current.nextSequence + encoded.length,\n queuedCount: nextCount,\n queuedBytes: nextBytes,\n })\n for (const record of encoded) {\n await transaction.addRecord(record.stored)\n }\n await transaction.putInstallation(nextRow)\n return freezeV1Value({\n state: snapshotFromRow(nextRow),\n records: freezeV1Value(encoded.map((record) => record.exposed)),\n })\n }\n\n private _appendTransactionOperation<TInput>(\n encode: (transaction: V1DurableStorageTransaction, current: V1InstallationRow, input: TInput) => Promise<V1DurableAppendResult>,\n publish: (result: V1DurableAppendResult, input: TInput) => V1DurableAppendResult\n ): (input: TInput) => Promise<V1DurableAppendResult> {\n // A callback factory shares the existing two await boundaries. It does not wrap\n // an awaited prefix in another async helper or extend transaction lifetime.\n return async (input) => {\n const storage = this._requireStorage()\n try {\n const result = await storage.runTransaction('readwrite', async (transaction) => {\n const current = await readInstallation(transaction, this._siteKey)\n return encode(transaction, current, input)\n })\n return publish(result, input)\n } catch (error) {\n this._rememberFatalFailure(error)\n throw error\n }\n }\n }\n\n private _requireStorage(): V1DurableStorage {\n if (this._poisoned) {\n throw this._poisoned\n }\n if (!this._storage) {\n return fail('not-initialized', 'durable state is not initialized')\n }\n return this._storage\n }\n\n private _invalidateCaptureAuthorityForLifecycleNow(): void {\n this._captureAuthority = null\n this._captureLifecycleEpoch = incrementStateCounter(this._captureLifecycleEpoch, 'capture lifecycle epoch')\n }\n\n private _rememberFatalFailure(error: unknown): void {\n const isFatalStateError =\n error instanceof V1DurableStateError &&\n (error.code === 'corrupt-storage' || error.code === 'unsupported-storage')\n const isFatalStorageError =\n error instanceof V1DurableStorageError && (error.code === 'unsupported' || error.code === 'closed')\n if (isFatalStateError || isFatalStorageError) {\n this._storage?.close()\n this._storage = null\n this._snapshot = null\n this._poisoned = error instanceof Error ? error : new Error('durable state failed closed')\n }\n }\n\n private _serialize<T>(operation: () => Promise<T>): Promise<T> {\n const result = this._tail.then(operation)\n this._tail = result.then(\n () => undefined,\n () => undefined\n )\n return result\n }\n\n private _snapshotAndSerialize<TInput, TResult>(\n snapshot: () => TInput,\n operation: (input: TInput) => Promise<TResult>\n ): Promise<TResult> {\n let input: TInput\n try {\n input = snapshot()\n } catch (error) {\n return this._serialize(async () => {\n throw error\n })\n }\n return this._serialize(() => operation(input))\n }\n}\n","// DEFLATE is a complex format; to read this code, you should probably check the RFC first:\n// https://tools.ietf.org/html/rfc1951\n// You may also wish to take a look at the guide I made about this program:\n// https://gist.github.com/101arrowz/253f31eb5abc3d9275ab943003ffecad\n// Much of the following code is similar to that of UZIP.js:\n// https://github.com/photopea/UZIP.js\n// Many optimizations have been made, so the bundle size is ultimately smaller but performance is similar.\n// Sometimes 0 will appear where -1 would be more appropriate. This is because using a uint\n// is better for memory in most engines (I *think*).\nvar ch2 = {};\nvar wk = (function (c, id, msg, transfer, cb) {\n var u = ch2[id] || (ch2[id] = URL.createObjectURL(new Blob([c], { type: 'text/javascript' })));\n var w = new Worker(u);\n w.onerror = function (e) { return cb(e.error, null); };\n w.onmessage = function (e) { return cb(null, e.data); };\n w.postMessage(msg, transfer);\n return w;\n});\n\n// aliases for shorter compressed code (most minifers don't do this)\nvar u8 = Uint8Array, u16 = Uint16Array, u32 = Uint32Array;\n// fixed length extra bits\nvar fleb = new u8([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, /* unused */ 0, 0, /* impossible */ 0]);\n// fixed distance extra bits\n// see fleb note\nvar fdeb = new u8([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* unused */ 0, 0]);\n// code length index map\nvar clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);\n// get base, reverse index map from extra bits\nvar freb = function (eb, start) {\n var b = new u16(31);\n for (var i = 0; i < 31; ++i) {\n b[i] = start += 1 << eb[i - 1];\n }\n // numbers here are at max 18 bits\n var r = new u32(b[30]);\n for (var i = 1; i < 30; ++i) {\n for (var j = b[i]; j < b[i + 1]; ++j) {\n r[j] = ((j - b[i]) << 5) | i;\n }\n }\n return [b, r];\n};\nvar _a = freb(fleb, 2), fl = _a[0], revfl = _a[1];\n// we can ignore the fact that the other numbers are wrong; they never happen anyway\nfl[28] = 258, revfl[258] = 28;\nvar _b = freb(fdeb, 0), fd = _b[0], revfd = _b[1];\n// map of value to reverse (assuming 16 bits)\nvar rev = new u16(32768);\nfor (var i = 0; i < 32768; ++i) {\n // reverse table algorithm from SO\n var x = ((i & 0xAAAA) >>> 1) | ((i & 0x5555) << 1);\n x = ((x & 0xCCCC) >>> 2) | ((x & 0x3333) << 2);\n x = ((x & 0xF0F0) >>> 4) | ((x & 0x0F0F) << 4);\n rev[i] = (((x & 0xFF00) >>> 8) | ((x & 0x00FF) << 8)) >>> 1;\n}\n// create huffman tree from u8 \"map\": index -> code length for code index\n// mb (max bits) must be at most 15\n// TODO: optimize/split up?\nvar hMap = (function (cd, mb, r) {\n var s = cd.length;\n // index\n var i = 0;\n // u16 \"map\": index -> # of codes with bit length = index\n var l = new u16(mb);\n // length of cd must be 288 (total # of codes)\n for (; i < s; ++i)\n ++l[cd[i] - 1];\n // u16 \"map\": index -> minimum code for bit length = index\n var le = new u16(mb);\n for (i = 0; i < mb; ++i) {\n le[i] = (le[i - 1] + l[i - 1]) << 1;\n }\n var co;\n if (r) {\n // u16 \"map\": index -> number of actual bits, symbol for code\n co = new u16(1 << mb);\n // bits to remove for reverser\n var rvb = 15 - mb;\n for (i = 0; i < s; ++i) {\n // ignore 0 lengths\n if (cd[i]) {\n // num encoding both symbol and bits read\n var sv = (i << 4) | cd[i];\n // free bits\n var r_1 = mb - cd[i];\n // start value\n var v = le[cd[i] - 1]++ << r_1;\n // m is end value\n for (var m = v | ((1 << r_1) - 1); v <= m; ++v) {\n // every 16 bit value starting with the code yields the same result\n co[rev[v] >>> rvb] = sv;\n }\n }\n }\n }\n else {\n co = new u16(s);\n for (i = 0; i < s; ++i)\n co[i] = rev[le[cd[i] - 1]++] >>> (15 - cd[i]);\n }\n return co;\n});\n// fixed length tree\nvar flt = new u8(288);\nfor (var i = 0; i < 144; ++i)\n flt[i] = 8;\nfor (var i = 144; i < 256; ++i)\n flt[i] = 9;\nfor (var i = 256; i < 280; ++i)\n flt[i] = 7;\nfor (var i = 280; i < 288; ++i)\n flt[i] = 8;\n// fixed distance tree\nvar fdt = new u8(32);\nfor (var i = 0; i < 32; ++i)\n fdt[i] = 5;\n// fixed length map\nvar flm = /*#__PURE__*/ hMap(flt, 9, 0), flrm = /*#__PURE__*/ hMap(flt, 9, 1);\n// fixed distance map\nvar fdm = /*#__PURE__*/ hMap(fdt, 5, 0), fdrm = /*#__PURE__*/ hMap(fdt, 5, 1);\n// find max of array\nvar max = function (a) {\n var m = a[0];\n for (var i = 1; i < a.length; ++i) {\n if (a[i] > m)\n m = a[i];\n }\n return m;\n};\n// read d, starting at bit p and mask with m\nvar bits = function (d, p, m) {\n var o = (p / 8) >> 0;\n return ((d[o] | (d[o + 1] << 8)) >>> (p & 7)) & m;\n};\n// read d, starting at bit p continuing for at least 16 bits\nvar bits16 = function (d, p) {\n var o = (p / 8) >> 0;\n return ((d[o] | (d[o + 1] << 8) | (d[o + 2] << 16)) >>> (p & 7));\n};\n// get end of byte\nvar shft = function (p) { return ((p / 8) >> 0) + (p & 7 && 1); };\n// typed array slice - allows garbage collector to free original reference,\n// while being more compatible than .slice\nvar slc = function (v, s, e) {\n if (s == null || s < 0)\n s = 0;\n if (e == null || e > v.length)\n e = v.length;\n // can't use .constructor in case user-supplied\n var n = new (v instanceof u16 ? u16 : v instanceof u32 ? u32 : u8)(e - s);\n n.set(v.subarray(s, e));\n return n;\n};\n// expands raw DEFLATE data\nvar inflt = function (dat, buf, st) {\n // source length\n var sl = dat.length;\n // have to estimate size\n var noBuf = !buf || st;\n // no state\n var noSt = !st || st.i;\n if (!st)\n st = {};\n // Assumes roughly 33% compression ratio average\n if (!buf)\n buf = new u8(sl * 3);\n // ensure buffer can fit at least l elements\n var cbuf = function (l) {\n var bl = buf.length;\n // need to increase size to fit\n if (l > bl) {\n // Double or set to necessary, whichever is greater\n var nbuf = new u8(Math.max(bl * 2, l));\n nbuf.set(buf);\n buf = nbuf;\n }\n };\n // last chunk bitpos bytes\n var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;\n // total bits\n var tbts = sl * 8;\n do {\n if (!lm) {\n // BFINAL - this is only 1 when last chunk is next\n st.f = final = bits(dat, pos, 1);\n // type: 0 = no compression, 1 = fixed huffman, 2 = dynamic huffman\n var type = bits(dat, pos + 1, 3);\n pos += 3;\n if (!type) {\n // go to end of byte boundary\n var s = shft(pos) + 4, l = dat[s - 4] | (dat[s - 3] << 8), t = s + l;\n if (t > sl) {\n if (noSt)\n throw 'unexpected EOF';\n break;\n }\n // ensure size\n if (noBuf)\n cbuf(bt + l);\n // Copy over uncompressed data\n buf.set(dat.subarray(s, t), bt);\n // Get new bitpos, update byte count\n st.b = bt += l, st.p = pos = t * 8;\n continue;\n }\n else if (type == 1)\n lm = flrm, dm = fdrm, lbt = 9, dbt = 5;\n else if (type == 2) {\n // literal lengths\n var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;\n var tl = hLit + bits(dat, pos + 5, 31) + 1;\n pos += 14;\n // length+distance tree\n var ldt = new u8(tl);\n // code length tree\n var clt = new u8(19);\n for (var i = 0; i < hcLen; ++i) {\n // use index map to get real code\n clt[clim[i]] = bits(dat, pos + i * 3, 7);\n }\n pos += hcLen * 3;\n // code lengths bits\n var clb = max(clt), clbmsk = (1 << clb) - 1;\n if (!noSt && pos + tl * (clb + 7) > tbts)\n break;\n // code lengths map\n var clm = hMap(clt, clb, 1);\n for (var i = 0; i < tl;) {\n var r = clm[bits(dat, pos, clbmsk)];\n // bits read\n pos += r & 15;\n // symbol\n var s = r >>> 4;\n // code length to copy\n if (s < 16) {\n ldt[i++] = s;\n }\n else {\n // copy count\n var c = 0, n = 0;\n if (s == 16)\n n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1];\n else if (s == 17)\n n = 3 + bits(dat, pos, 7), pos += 3;\n else if (s == 18)\n n = 11 + bits(dat, pos, 127), pos += 7;\n while (n--)\n ldt[i++] = c;\n }\n }\n // length tree distance tree\n var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);\n // max length bits\n lbt = max(lt);\n // max dist bits\n dbt = max(dt);\n lm = hMap(lt, lbt, 1);\n dm = hMap(dt, dbt, 1);\n }\n else\n throw 'invalid block type';\n if (pos > tbts)\n throw 'unexpected EOF';\n }\n // Make sure the buffer can hold this + the largest possible addition\n // Maximum chunk size (practically, theoretically infinite) is 2^17;\n if (noBuf)\n cbuf(bt + 131072);\n var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;\n var mxa = lbt + dbt + 18;\n while (noSt || pos + mxa < tbts) {\n // bits read, code\n var c = lm[bits16(dat, pos) & lms], sym = c >>> 4;\n pos += c & 15;\n if (pos > tbts)\n throw 'unexpected EOF';\n if (!c)\n throw 'invalid length/literal';\n if (sym < 256)\n buf[bt++] = sym;\n else if (sym == 256) {\n lm = null;\n break;\n }\n else {\n var add = sym - 254;\n // no extra bits needed if less\n if (sym > 264) {\n // index\n var i = sym - 257, b = fleb[i];\n add = bits(dat, pos, (1 << b) - 1) + fl[i];\n pos += b;\n }\n // dist\n var d = dm[bits16(dat, pos) & dms], dsym = d >>> 4;\n if (!d)\n throw 'invalid distance';\n pos += d & 15;\n var dt = fd[dsym];\n if (dsym > 3) {\n var b = fdeb[dsym];\n dt += bits16(dat, pos) & ((1 << b) - 1), pos += b;\n }\n if (pos > tbts)\n throw 'unexpected EOF';\n if (noBuf)\n cbuf(bt + 131072);\n var end = bt + add;\n for (; bt < end; bt += 4) {\n buf[bt] = buf[bt - dt];\n buf[bt + 1] = buf[bt + 1 - dt];\n buf[bt + 2] = buf[bt + 2 - dt];\n buf[bt + 3] = buf[bt + 3 - dt];\n }\n bt = end;\n }\n }\n st.l = lm, st.p = pos, st.b = bt;\n if (lm)\n final = 1, st.m = lbt, st.d = dm, st.n = dbt;\n } while (!final);\n return bt == buf.length ? buf : slc(buf, 0, bt);\n};\n// starting at p, write the minimum number of bits that can hold v to d\nvar wbits = function (d, p, v) {\n v <<= p & 7;\n var o = (p / 8) >> 0;\n d[o] |= v;\n d[o + 1] |= v >>> 8;\n};\n// starting at p, write the minimum number of bits (>8) that can hold v to d\nvar wbits16 = function (d, p, v) {\n v <<= p & 7;\n var o = (p / 8) >> 0;\n d[o] |= v;\n d[o + 1] |= v >>> 8;\n d[o + 2] |= v >>> 16;\n};\n// creates code lengths from a frequency table\nvar hTree = function (d, mb) {\n // Need extra info to make a tree\n var t = [];\n for (var i = 0; i < d.length; ++i) {\n if (d[i])\n t.push({ s: i, f: d[i] });\n }\n var s = t.length;\n var t2 = t.slice();\n if (!s)\n return [new u8(0), 0];\n if (s == 1) {\n var v = new u8(t[0].s + 1);\n v[t[0].s] = 1;\n return [v, 1];\n }\n t.sort(function (a, b) { return a.f - b.f; });\n // after i2 reaches last ind, will be stopped\n // freq must be greater than largest possible number of symbols\n t.push({ s: -1, f: 25001 });\n var l = t[0], r = t[1], i0 = 0, i1 = 1, i2 = 2;\n t[0] = { s: -1, f: l.f + r.f, l: l, r: r };\n // efficient algorithm from UZIP.js\n // i0 is lookbehind, i2 is lookahead - after processing two low-freq\n // symbols that combined have high freq, will start processing i2 (high-freq,\n // non-composite) symbols instead\n // see https://reddit.com/r/photopea/comments/ikekht/uzipjs_questions/\n while (i1 != s - 1) {\n l = t[t[i0].f < t[i2].f ? i0++ : i2++];\n r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];\n t[i1++] = { s: -1, f: l.f + r.f, l: l, r: r };\n }\n var maxSym = t2[0].s;\n for (var i = 1; i < s; ++i) {\n if (t2[i].s > maxSym)\n maxSym = t2[i].s;\n }\n // code lengths\n var tr = new u16(maxSym + 1);\n // max bits in tree\n var mbt = ln(t[i1 - 1], tr, 0);\n if (mbt > mb) {\n // more algorithms from UZIP.js\n // TODO: find out how this code works (debt)\n // ind debt\n var i = 0, dt = 0;\n // left cost\n var lft = mbt - mb, cst = 1 << lft;\n t2.sort(function (a, b) { return tr[b.s] - tr[a.s] || a.f - b.f; });\n for (; i < s; ++i) {\n var i2_1 = t2[i].s;\n if (tr[i2_1] > mb) {\n dt += cst - (1 << (mbt - tr[i2_1]));\n tr[i2_1] = mb;\n }\n else\n break;\n }\n dt >>>= lft;\n while (dt > 0) {\n var i2_2 = t2[i].s;\n if (tr[i2_2] < mb)\n dt -= 1 << (mb - tr[i2_2]++ - 1);\n else\n ++i;\n }\n for (; i >= 0 && dt; --i) {\n var i2_3 = t2[i].s;\n if (tr[i2_3] == mb) {\n --tr[i2_3];\n ++dt;\n }\n }\n mbt = mb;\n }\n return [new u8(tr), mbt];\n};\n// get the max length and assign length codes\nvar ln = function (n, l, d) {\n return n.s == -1\n ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1))\n : (l[n.s] = d);\n};\n// length codes generation\nvar lc = function (c) {\n var s = c.length;\n // Note that the semicolon was intentional\n while (s && !c[--s])\n ;\n var cl = new u16(++s);\n // ind num streak\n var cli = 0, cln = c[0], cls = 1;\n var w = function (v) { cl[cli++] = v; };\n for (var i = 1; i <= s; ++i) {\n if (c[i] == cln && i != s)\n ++cls;\n else {\n if (!cln && cls > 2) {\n for (; cls > 138; cls -= 138)\n w(32754);\n if (cls > 2) {\n w(cls > 10 ? ((cls - 11) << 5) | 28690 : ((cls - 3) << 5) | 12305);\n cls = 0;\n }\n }\n else if (cls > 3) {\n w(cln), --cls;\n for (; cls > 6; cls -= 6)\n w(8304);\n if (cls > 2)\n w(((cls - 3) << 5) | 8208), cls = 0;\n }\n while (cls--)\n w(cln);\n cls = 1;\n cln = c[i];\n }\n }\n return [cl.subarray(0, cli), s];\n};\n// calculate the length of output from tree, code lengths\nvar clen = function (cf, cl) {\n var l = 0;\n for (var i = 0; i < cl.length; ++i)\n l += cf[i] * cl[i];\n return l;\n};\n// writes a fixed block\n// returns the new bit pos\nvar wfblk = function (out, pos, dat) {\n // no need to write 00 as type: TypedArray defaults to 0\n var s = dat.length;\n var o = shft(pos + 2);\n out[o] = s & 255;\n out[o + 1] = s >>> 8;\n out[o + 2] = out[o] ^ 255;\n out[o + 3] = out[o + 1] ^ 255;\n for (var i = 0; i < s; ++i)\n out[o + i + 4] = dat[i];\n return (o + 4 + s) * 8;\n};\n// writes a block\nvar wblk = function (dat, out, final, syms, lf, df, eb, li, bs, bl, p) {\n wbits(out, p++, final);\n ++lf[256];\n var _a = hTree(lf, 15), dlt = _a[0], mlb = _a[1];\n var _b = hTree(df, 15), ddt = _b[0], mdb = _b[1];\n var _c = lc(dlt), lclt = _c[0], nlc = _c[1];\n var _d = lc(ddt), lcdt = _d[0], ndc = _d[1];\n var lcfreq = new u16(19);\n for (var i = 0; i < lclt.length; ++i)\n lcfreq[lclt[i] & 31]++;\n for (var i = 0; i < lcdt.length; ++i)\n lcfreq[lcdt[i] & 31]++;\n var _e = hTree(lcfreq, 7), lct = _e[0], mlcb = _e[1];\n var nlcc = 19;\n for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)\n ;\n var flen = (bl + 5) << 3;\n var ftlen = clen(lf, flt) + clen(df, fdt) + eb;\n var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + (2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18]);\n if (flen <= ftlen && flen <= dtlen)\n return wfblk(out, p, dat.subarray(bs, bs + bl));\n var lm, ll, dm, dl;\n wbits(out, p, 1 + (dtlen < ftlen)), p += 2;\n if (dtlen < ftlen) {\n lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;\n var llm = hMap(lct, mlcb, 0);\n wbits(out, p, nlc - 257);\n wbits(out, p + 5, ndc - 1);\n wbits(out, p + 10, nlcc - 4);\n p += 14;\n for (var i = 0; i < nlcc; ++i)\n wbits(out, p + 3 * i, lct[clim[i]]);\n p += 3 * nlcc;\n var lcts = [lclt, lcdt];\n for (var it = 0; it < 2; ++it) {\n var clct = lcts[it];\n for (var i = 0; i < clct.length; ++i) {\n var len = clct[i] & 31;\n wbits(out, p, llm[len]), p += lct[len];\n if (len > 15)\n wbits(out, p, (clct[i] >>> 5) & 127), p += clct[i] >>> 12;\n }\n }\n }\n else {\n lm = flm, ll = flt, dm = fdm, dl = fdt;\n }\n for (var i = 0; i < li; ++i) {\n if (syms[i] > 255) {\n var len = (syms[i] >>> 18) & 31;\n wbits16(out, p, lm[len + 257]), p += ll[len + 257];\n if (len > 7)\n wbits(out, p, (syms[i] >>> 23) & 31), p += fleb[len];\n var dst = syms[i] & 31;\n wbits16(out, p, dm[dst]), p += dl[dst];\n if (dst > 3)\n wbits16(out, p, (syms[i] >>> 5) & 8191), p += fdeb[dst];\n }\n else {\n wbits16(out, p, lm[syms[i]]), p += ll[syms[i]];\n }\n }\n wbits16(out, p, lm[256]);\n return p + ll[256];\n};\n// deflate options (nice << 13) | chain\nvar deo = /*#__PURE__*/ new u32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);\n// empty\nvar et = /*#__PURE__*/ new u8(0);\n// compresses data into a raw DEFLATE buffer\nvar dflt = function (dat, lvl, plvl, pre, post, lst) {\n var s = dat.length;\n var o = new u8(pre + s + 5 * (1 + Math.floor(s / 7000)) + post);\n // writing to this writes to the output buffer\n var w = o.subarray(pre, o.length - post);\n var pos = 0;\n if (!lvl || s < 8) {\n for (var i = 0; i <= s; i += 65535) {\n // end\n var e = i + 65535;\n if (e < s) {\n // write full block\n pos = wfblk(w, pos, dat.subarray(i, e));\n }\n else {\n // write final block\n w[i] = lst;\n pos = wfblk(w, pos, dat.subarray(i, s));\n }\n }\n }\n else {\n var opt = deo[lvl - 1];\n var n = opt >>> 13, c = opt & 8191;\n var msk_1 = (1 << plvl) - 1;\n // prev 2-byte val map curr 2-byte val map\n var prev = new u16(32768), head = new u16(msk_1 + 1);\n var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;\n var hsh = function (i) { return (dat[i] ^ (dat[i + 1] << bs1_1) ^ (dat[i + 2] << bs2_1)) & msk_1; };\n // 24576 is an arbitrary number of maximum symbols per block\n // 424 buffer for last block\n var syms = new u32(25000);\n // length/literal freq distance freq\n var lf = new u16(288), df = new u16(32);\n // l/lcnt exbits index l/lind waitdx bitpos\n var lc_1 = 0, eb = 0, i = 0, li = 0, wi = 0, bs = 0;\n for (; i < s; ++i) {\n // hash value\n var hv = hsh(i);\n // index mod 32768\n var imod = i & 32767;\n // previous index with this value\n var pimod = head[hv];\n prev[imod] = pimod;\n head[hv] = imod;\n // We always should modify head and prev, but only add symbols if\n // this data is not yet processed (\"wait\" for wait index)\n if (wi <= i) {\n // bytes remaining\n var rem = s - i;\n if ((lc_1 > 7000 || li > 24576) && rem > 423) {\n pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i - bs, pos);\n li = lc_1 = eb = 0, bs = i;\n for (var j = 0; j < 286; ++j)\n lf[j] = 0;\n for (var j = 0; j < 30; ++j)\n df[j] = 0;\n }\n // len dist chain\n var l = 2, d = 0, ch_1 = c, dif = (imod - pimod) & 32767;\n if (rem > 2 && hv == hsh(i - dif)) {\n var maxn = Math.min(n, rem) - 1;\n var maxd = Math.min(32767, i);\n // max possible length\n // not capped at dif because decompressors implement \"rolling\" index population\n var ml = Math.min(258, rem);\n while (dif <= maxd && --ch_1 && imod != pimod) {\n if (dat[i + l] == dat[i + l - dif]) {\n var nl = 0;\n for (; nl < ml && dat[i + nl] == dat[i + nl - dif]; ++nl)\n ;\n if (nl > l) {\n l = nl, d = dif;\n // break out early when we reach \"nice\" (we are satisfied enough)\n if (nl > maxn)\n break;\n // now, find the rarest 2-byte sequence within this\n // length of literals and search for that instead.\n // Much faster than just using the start\n var mmd = Math.min(dif, nl - 2);\n var md = 0;\n for (var j = 0; j < mmd; ++j) {\n var ti = (i - dif + j + 32768) & 32767;\n var pti = prev[ti];\n var cd = (ti - pti + 32768) & 32767;\n if (cd > md)\n md = cd, pimod = ti;\n }\n }\n }\n // check the previous match\n imod = pimod, pimod = prev[imod];\n dif += (imod - pimod + 32768) & 32767;\n }\n }\n // d will be nonzero only when a match was found\n if (d) {\n // store both dist and len data in one Uint32\n // Make sure this is recognized as a len/dist with 28th bit (2^28)\n syms[li++] = 268435456 | (revfl[l] << 18) | revfd[d];\n var lin = revfl[l] & 31, din = revfd[d] & 31;\n eb += fleb[lin] + fdeb[din];\n ++lf[257 + lin];\n ++df[din];\n wi = i + l;\n ++lc_1;\n }\n else {\n syms[li++] = dat[i];\n ++lf[dat[i]];\n }\n }\n }\n pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);\n // this is the easiest way to avoid needing to maintain state\n if (!lst)\n pos = wfblk(w, pos, et);\n }\n return slc(o, 0, pre + shft(pos) + post);\n};\n// CRC32 table\nvar crct = /*#__PURE__*/ (function () {\n var t = new u32(256);\n for (var i = 0; i < 256; ++i) {\n var c = i, k = 9;\n while (--k)\n c = ((c & 1) && 0xEDB88320) ^ (c >>> 1);\n t[i] = c;\n }\n return t;\n})();\n// CRC32\nvar crc = function () {\n var c = 0xFFFFFFFF;\n return {\n p: function (d) {\n // closures have awful performance\n var cr = c;\n for (var i = 0; i < d.length; ++i)\n cr = crct[(cr & 255) ^ d[i]] ^ (cr >>> 8);\n c = cr;\n },\n d: function () { return c ^ 0xFFFFFFFF; }\n };\n};\n// Alder32\nvar adler = function () {\n var a = 1, b = 0;\n return {\n p: function (d) {\n // closures have awful performance\n var n = a, m = b;\n var l = d.length;\n for (var i = 0; i != l;) {\n var e = Math.min(i + 5552, l);\n for (; i < e; ++i)\n n += d[i], m += n;\n n %= 65521, m %= 65521;\n }\n a = n, b = m;\n },\n d: function () { return ((a >>> 8) << 16 | (b & 255) << 8 | (b >>> 8)) + ((a & 255) << 23) * 2; }\n };\n};\n;\n// deflate with opts\nvar dopt = function (dat, opt, pre, post, st) {\n return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : (12 + opt.mem), pre, post, !st);\n};\n// Walmart object spread\nvar mrg = function (a, b) {\n var o = {};\n for (var k in a)\n o[k] = a[k];\n for (var k in b)\n o[k] = b[k];\n return o;\n};\n// worker clone\n// This is possibly the craziest part of the entire codebase, despite how simple it may seem.\n// The only parameter to this function is a closure that returns an array of variables outside of the function scope.\n// We're going to try to figure out the variable names used in the closure as strings because that is crucial for workerization.\n// We will return an object mapping of true variable name to value (basically, the current scope as a JS object).\n// The reason we can't just use the original variable names is minifiers mangling the toplevel scope.\n// This took me three weeks to figure out how to do.\nvar wcln = function (fn, fnStr, td) {\n var dt = fn();\n var st = fn.toString();\n var ks = st.slice(st.indexOf('[') + 1, st.lastIndexOf(']')).replace(/ /g, '').split(',');\n for (var i = 0; i < dt.length; ++i) {\n var v = dt[i], k = ks[i];\n if (typeof v == 'function') {\n fnStr += ';' + k + '=';\n var st_1 = v.toString();\n if (v.prototype) {\n // for global objects\n if (st_1.indexOf('[native code]') != -1) {\n var spInd = st_1.indexOf(' ', 8) + 1;\n fnStr += st_1.slice(spInd, st_1.indexOf('(', spInd));\n }\n else {\n fnStr += st_1;\n for (var t in v.prototype)\n fnStr += ';' + k + '.prototype.' + t + '=' + v.prototype[t].toString();\n }\n }\n else\n fnStr += st_1;\n }\n else\n td[k] = v;\n }\n return [fnStr, td];\n};\nvar ch = [];\n// clone bufs\nvar cbfs = function (v) {\n var tl = [];\n for (var k in v) {\n if (v[k] instanceof u8 || v[k] instanceof u16 || v[k] instanceof u32)\n tl.push((v[k] = new v[k].constructor(v[k])).buffer);\n }\n return tl;\n};\n// use a worker to execute code\nvar wrkr = function (fns, init, id, cb) {\n var _a;\n if (!ch[id]) {\n var fnStr = '', td_1 = {}, m = fns.length - 1;\n for (var i = 0; i < m; ++i)\n _a = wcln(fns[i], fnStr, td_1), fnStr = _a[0], td_1 = _a[1];\n ch[id] = wcln(fns[m], fnStr, td_1);\n }\n var td = mrg({}, ch[id][1]);\n return wk(ch[id][0] + ';onmessage=function(e){for(var k in e.data)self[k]=e.data[k];onmessage=' + init.toString() + '}', id, td, cbfs(td), cb);\n};\n// base async inflate fn\nvar bInflt = function () { return [u8, u16, u32, fleb, fdeb, clim, fl, fd, flrm, fdrm, rev, hMap, max, bits, bits16, shft, slc, inflt, inflateSync, pbf, gu8]; };\nvar bDflt = function () { return [u8, u16, u32, fleb, fdeb, clim, revfl, revfd, flm, flt, fdm, fdt, rev, deo, et, hMap, wbits, wbits16, hTree, ln, lc, clen, wfblk, wblk, shft, slc, dflt, dopt, deflateSync, pbf]; };\n// gzip extra\nvar gze = function () { return [gzh, gzhl, wbytes, crc, crct]; };\n// gunzip extra\nvar guze = function () { return [gzs, gzl]; };\n// zlib extra\nvar zle = function () { return [zlh, wbytes, adler]; };\n// unzlib extra\nvar zule = function () { return [zlv]; };\n// post buf\nvar pbf = function (msg) { return postMessage(msg, [msg.buffer]); };\n// get u8\nvar gu8 = function (o) { return o && o.size && new u8(o.size); };\n// async helper\nvar cbify = function (dat, opts, fns, init, id, cb) {\n var w = wrkr(fns, init, id, function (err, dat) {\n w.terminate();\n cb(err, dat);\n });\n if (!opts.consume)\n dat = new u8(dat);\n w.postMessage([dat, opts], [dat.buffer]);\n return function () { w.terminate(); };\n};\n// auto stream\nvar astrm = function (strm) {\n strm.ondata = function (dat, final) { return postMessage([dat, final], [dat.buffer]); };\n return function (ev) { return strm.push(ev.data[0], ev.data[1]); };\n};\n// async stream attach\nvar astrmify = function (fns, strm, opts, init, id) {\n var t;\n var w = wrkr(fns, init, id, function (err, dat) {\n if (err)\n w.terminate(), strm.ondata.call(strm, err);\n else {\n if (dat[1])\n w.terminate();\n strm.ondata.call(strm, err, dat[0], dat[1]);\n }\n });\n w.postMessage(opts);\n strm.push = function (d, f) {\n if (t)\n throw 'stream finished';\n if (!strm.ondata)\n throw 'no stream handler';\n w.postMessage([d, t = f], [d.buffer]);\n };\n strm.terminate = function () { w.terminate(); };\n};\n// read 2 bytes\nvar b2 = function (d, b) { return d[b] | (d[b + 1] << 8); };\n// read 4 bytes\nvar b4 = function (d, b) { return (d[b] | (d[b + 1] << 8) | (d[b + 2] << 16)) + (d[b + 3] << 23) * 2; };\n// write bytes\nvar wbytes = function (d, b, v) {\n for (; v; ++b)\n d[b] = v, v >>>= 8;\n};\n// gzip header\nvar gzh = function (c, o) {\n var fn = o.filename;\n c[0] = 31, c[1] = 139, c[2] = 8, c[8] = o.level < 2 ? 4 : o.level == 9 ? 2 : 0, c[9] = 3; // assume Unix\n if (o.mtime != 0)\n wbytes(c, 4, Math.floor(new Date(o.mtime || Date.now()) / 1000));\n if (fn) {\n c[3] = 8;\n for (var i = 0; i <= fn.length; ++i)\n c[i + 10] = fn.charCodeAt(i);\n }\n};\n// gzip footer: -8 to -4 = CRC, -4 to -0 is length\n// gzip start\nvar gzs = function (d) {\n if (d[0] != 31 || d[1] != 139 || d[2] != 8)\n throw 'invalid gzip data';\n var flg = d[3];\n var st = 10;\n if (flg & 4)\n st += d[10] | (d[11] << 8) + 2;\n for (var zs = (flg >> 3 & 1) + (flg >> 4 & 1); zs > 0; zs -= !d[st++])\n ;\n return st + (flg & 2);\n};\n// gzip length\nvar gzl = function (d) {\n var l = d.length;\n return (d[l - 4] | d[l - 3] << 8 | d[l - 2] << 16) + (2 * (d[l - 1] << 23));\n};\n// gzip header length\nvar gzhl = function (o) { return 10 + ((o.filename && (o.filename.length + 1)) || 0); };\n// zlib header\nvar zlh = function (c, o) {\n var lv = o.level, fl = lv == 0 ? 0 : lv < 6 ? 1 : lv == 9 ? 3 : 2;\n c[0] = 120, c[1] = (fl << 6) | (fl ? (32 - 2 * fl) : 1);\n};\n// zlib valid\nvar zlv = function (d) {\n if ((d[0] & 15) != 8 || (d[0] >>> 4) > 7 || ((d[0] << 8 | d[1]) % 31))\n throw 'invalid zlib data';\n if (d[1] & 32)\n throw 'invalid zlib data: preset dictionaries not supported';\n};\nfunction AsyncCmpStrm(opts, cb) {\n if (!cb && typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n return opts;\n}\n// zlib footer: -4 to -0 is Adler32\n/**\n * Streaming DEFLATE compression\n */\nvar Deflate = /*#__PURE__*/ (function () {\n function Deflate(opts, cb) {\n if (!cb && typeof opts == 'function')\n cb = opts, opts = {};\n this.ondata = cb;\n this.o = opts || {};\n }\n Deflate.prototype.p = function (c, f) {\n this.ondata(dopt(c, this.o, 0, 0, !f), f);\n };\n /**\n * Pushes a chunk to be deflated\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Deflate.prototype.push = function (chunk, final) {\n if (this.d)\n throw 'stream finished';\n if (!this.ondata)\n throw 'no stream handler';\n this.d = final;\n this.p(chunk, final || false);\n };\n return Deflate;\n}());\nexport { Deflate };\n/**\n * Asynchronous streaming DEFLATE compression\n */\nvar AsyncDeflate = /*#__PURE__*/ (function () {\n function AsyncDeflate(opts, cb) {\n astrmify([\n bDflt,\n function () { return [astrm, Deflate]; }\n ], this, AsyncCmpStrm.call(this, opts, cb), function (ev) {\n var strm = new Deflate(ev.data);\n onmessage = astrm(strm);\n }, 6);\n }\n return AsyncDeflate;\n}());\nexport { AsyncDeflate };\nexport function deflate(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return cbify(data, opts, [\n bDflt,\n ], function (ev) { return pbf(deflateSync(ev.data[0], ev.data[1])); }, 0, cb);\n}\n/**\n * Compresses data with DEFLATE without any wrapper\n * @param data The data to compress\n * @param opts The compression options\n * @returns The deflated version of the data\n */\nexport function deflateSync(data, opts) {\n if (opts === void 0) { opts = {}; }\n return dopt(data, opts, 0, 0);\n}\n/**\n * Streaming DEFLATE decompression\n */\nvar Inflate = /*#__PURE__*/ (function () {\n /**\n * Creates an inflation stream\n * @param cb The callback to call whenever data is inflated\n */\n function Inflate(cb) {\n this.s = {};\n this.p = new u8(0);\n this.ondata = cb;\n }\n Inflate.prototype.e = function (c) {\n if (this.d)\n throw 'stream finished';\n if (!this.ondata)\n throw 'no stream handler';\n var l = this.p.length;\n var n = new u8(l + c.length);\n n.set(this.p), n.set(c, l), this.p = n;\n };\n Inflate.prototype.c = function (final) {\n this.d = this.s.i = final || false;\n var bts = this.s.b;\n var dt = inflt(this.p, this.o, this.s);\n this.ondata(slc(dt, bts, this.s.b), this.d);\n this.o = slc(dt, this.s.b - 32768), this.s.b = this.o.length;\n this.p = slc(this.p, (this.s.p / 8) >> 0), this.s.p &= 7;\n };\n /**\n * Pushes a chunk to be inflated\n * @param chunk The chunk to push\n * @param final Whether this is the final chunk\n */\n Inflate.prototype.push = function (chunk, final) {\n this.e(chunk), this.c(final);\n };\n return Inflate;\n}());\nexport { Inflate };\n/**\n * Asynchronous streaming DEFLATE decompression\n */\nvar AsyncInflate = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous inflation stream\n * @param cb The callback to call whenever data is deflated\n */\n function AsyncInflate(cb) {\n this.ondata = cb;\n astrmify([\n bInflt,\n function () { return [astrm, Inflate]; }\n ], this, 0, function () {\n var strm = new Inflate();\n onmessage = astrm(strm);\n }, 7);\n }\n return AsyncInflate;\n}());\nexport { AsyncInflate };\nexport function inflate(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return cbify(data, opts, [\n bInflt\n ], function (ev) { return pbf(inflateSync(ev.data[0], gu8(ev.data[1]))); }, 1, cb);\n}\n/**\n * Expands DEFLATE data with no wrapper\n * @param data The data to decompress\n * @param out Where to write the data. Saves memory if you know the decompressed size and provide an output buffer of that length.\n * @returns The decompressed version of the data\n */\nexport function inflateSync(data, out) {\n return inflt(data, out);\n}\n// before you yell at me for not just using extends, my reason is that TS inheritance is hard to workerize.\n/**\n * Streaming GZIP compression\n */\nvar Gzip = /*#__PURE__*/ (function () {\n function Gzip(opts, cb) {\n this.c = crc();\n this.l = 0;\n this.v = 1;\n Deflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be GZIPped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Gzip.prototype.push = function (chunk, final) {\n Deflate.prototype.push.call(this, chunk, final);\n };\n Gzip.prototype.p = function (c, f) {\n this.c.p(c);\n this.l += c.length;\n var raw = dopt(c, this.o, this.v && gzhl(this.o), f && 8, !f);\n if (this.v)\n gzh(raw, this.o), this.v = 0;\n if (f)\n wbytes(raw, raw.length - 8, this.c.d()), wbytes(raw, raw.length - 4, this.l);\n this.ondata(raw, f);\n };\n return Gzip;\n}());\nexport { Gzip };\n/**\n * Asynchronous streaming GZIP compression\n */\nvar AsyncGzip = /*#__PURE__*/ (function () {\n function AsyncGzip(opts, cb) {\n astrmify([\n bDflt,\n gze,\n function () { return [astrm, Deflate, Gzip]; }\n ], this, AsyncCmpStrm.call(this, opts, cb), function (ev) {\n var strm = new Gzip(ev.data);\n onmessage = astrm(strm);\n }, 8);\n }\n return AsyncGzip;\n}());\nexport { AsyncGzip };\nexport function gzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return cbify(data, opts, [\n bDflt,\n gze,\n function () { return [gzipSync]; }\n ], function (ev) { return pbf(gzipSync(ev.data[0], ev.data[1])); }, 2, cb);\n}\n/**\n * Compresses data with GZIP\n * @param data The data to compress\n * @param opts The compression options\n * @returns The gzipped version of the data\n */\nexport function gzipSync(data, opts) {\n if (opts === void 0) { opts = {}; }\n var c = crc(), l = data.length;\n c.p(data);\n var d = dopt(data, opts, gzhl(opts), 8), s = d.length;\n return gzh(d, opts), wbytes(d, s - 8, c.d()), wbytes(d, s - 4, l), d;\n}\n/**\n * Streaming GZIP decompression\n */\nvar Gunzip = /*#__PURE__*/ (function () {\n /**\n * Creates a GUNZIP stream\n * @param cb The callback to call whenever data is inflated\n */\n function Gunzip(cb) {\n this.v = 1;\n Inflate.call(this, cb);\n }\n /**\n * Pushes a chunk to be GUNZIPped\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Gunzip.prototype.push = function (chunk, final) {\n Inflate.prototype.e.call(this, chunk);\n if (this.v) {\n var s = gzs(this.p);\n if (s >= this.p.length && !final)\n return;\n this.p = this.p.subarray(s), this.v = 0;\n }\n if (final) {\n if (this.p.length < 8)\n throw 'invalid gzip stream';\n this.p = this.p.subarray(0, -8);\n }\n // necessary to prevent TS from using the closure value\n // This allows for workerization to function correctly\n Inflate.prototype.c.call(this, final);\n };\n return Gunzip;\n}());\nexport { Gunzip };\n/**\n * Asynchronous streaming GZIP decompression\n */\nvar AsyncGunzip = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous GUNZIP stream\n * @param cb The callback to call whenever data is deflated\n */\n function AsyncGunzip(cb) {\n this.ondata = cb;\n astrmify([\n bInflt,\n guze,\n function () { return [astrm, Inflate, Gunzip]; }\n ], this, 0, function () {\n var strm = new Gunzip();\n onmessage = astrm(strm);\n }, 9);\n }\n return AsyncGunzip;\n}());\nexport { AsyncGunzip };\nexport function gunzip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return cbify(data, opts, [\n bInflt,\n guze,\n function () { return [gunzipSync]; }\n ], function (ev) { return pbf(gunzipSync(ev.data[0])); }, 3, cb);\n}\n/**\n * Expands GZIP data\n * @param data The data to decompress\n * @param out Where to write the data. GZIP already encodes the output size, so providing this doesn't save memory.\n * @returns The decompressed version of the data\n */\nexport function gunzipSync(data, out) {\n return inflt(data.subarray(gzs(data), -8), out || new u8(gzl(data)));\n}\n/**\n * Streaming Zlib compression\n */\nvar Zlib = /*#__PURE__*/ (function () {\n function Zlib(opts, cb) {\n this.c = adler();\n this.v = 1;\n Deflate.call(this, opts, cb);\n }\n /**\n * Pushes a chunk to be zlibbed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Zlib.prototype.push = function (chunk, final) {\n Deflate.prototype.push.call(this, chunk, final);\n };\n Zlib.prototype.p = function (c, f) {\n this.c.p(c);\n var raw = dopt(c, this.o, this.v && 2, f && 4, !f);\n if (this.v)\n zlh(raw, this.o), this.v = 0;\n if (f)\n wbytes(raw, raw.length - 4, this.c.d());\n this.ondata(raw, f);\n };\n return Zlib;\n}());\nexport { Zlib };\n/**\n * Asynchronous streaming Zlib compression\n */\nvar AsyncZlib = /*#__PURE__*/ (function () {\n function AsyncZlib(opts, cb) {\n astrmify([\n bDflt,\n zle,\n function () { return [astrm, Deflate, Zlib]; }\n ], this, AsyncCmpStrm.call(this, opts, cb), function (ev) {\n var strm = new Zlib(ev.data);\n onmessage = astrm(strm);\n }, 10);\n }\n return AsyncZlib;\n}());\nexport { AsyncZlib };\nexport function zlib(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return cbify(data, opts, [\n bDflt,\n zle,\n function () { return [zlibSync]; }\n ], function (ev) { return pbf(zlibSync(ev.data[0], ev.data[1])); }, 4, cb);\n}\n/**\n * Compress data with Zlib\n * @param data The data to compress\n * @param opts The compression options\n * @returns The zlib-compressed version of the data\n */\nexport function zlibSync(data, opts) {\n if (opts === void 0) { opts = {}; }\n var a = adler();\n a.p(data);\n var d = dopt(data, opts, 2, 4);\n return zlh(d, opts), wbytes(d, d.length - 4, a.d()), d;\n}\n/**\n * Streaming Zlib decompression\n */\nvar Unzlib = /*#__PURE__*/ (function () {\n /**\n * Creates a Zlib decompression stream\n * @param cb The callback to call whenever data is inflated\n */\n function Unzlib(cb) {\n this.v = 1;\n Inflate.call(this, cb);\n }\n /**\n * Pushes a chunk to be unzlibbed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Unzlib.prototype.push = function (chunk, final) {\n Inflate.prototype.e.call(this, chunk);\n if (this.v) {\n if (this.p.length < 2 && !final)\n return;\n this.p = this.p.subarray(2), this.v = 0;\n }\n if (final) {\n if (this.p.length < 4)\n throw 'invalid zlib stream';\n this.p = this.p.subarray(0, -4);\n }\n // necessary to prevent TS from using the closure value\n // This allows for workerization to function correctly\n Inflate.prototype.c.call(this, final);\n };\n return Unzlib;\n}());\nexport { Unzlib };\n/**\n * Asynchronous streaming Zlib decompression\n */\nvar AsyncUnzlib = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous Zlib decompression stream\n * @param cb The callback to call whenever data is deflated\n */\n function AsyncUnzlib(cb) {\n this.ondata = cb;\n astrmify([\n bInflt,\n zule,\n function () { return [astrm, Inflate, Unzlib]; }\n ], this, 0, function () {\n var strm = new Unzlib();\n onmessage = astrm(strm);\n }, 11);\n }\n return AsyncUnzlib;\n}());\nexport { AsyncUnzlib };\nexport function unzlib(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return cbify(data, opts, [\n bInflt,\n zule,\n function () { return [unzlibSync]; }\n ], function (ev) { return pbf(unzlibSync(ev.data[0], gu8(ev.data[1]))); }, 5, cb);\n}\n/**\n * Expands Zlib data\n * @param data The data to decompress\n * @param out Where to write the data. Saves memory if you know the decompressed size and provide an output buffer of that length.\n * @returns The decompressed version of the data\n */\nexport function unzlibSync(data, out) {\n return inflt((zlv(data), data.subarray(2, -4)), out);\n}\n// Default algorithm for compression (used because having a known output size allows faster decompression)\nexport { gzip as compress, AsyncGzip as AsyncCompress };\n// Default algorithm for compression (used because having a known output size allows faster decompression)\nexport { gzipSync as compressSync, Gzip as Compress };\n/**\n * Streaming GZIP, Zlib, or raw DEFLATE decompression\n */\nvar Decompress = /*#__PURE__*/ (function () {\n /**\n * Creates a decompression stream\n * @param cb The callback to call whenever data is decompressed\n */\n function Decompress(cb) {\n this.G = Gunzip;\n this.I = Inflate;\n this.Z = Unzlib;\n this.ondata = cb;\n }\n /**\n * Pushes a chunk to be decompressed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n Decompress.prototype.push = function (chunk, final) {\n if (!this.ondata)\n throw 'no stream handler';\n if (!this.s) {\n if (this.p && this.p.length) {\n var n = new u8(this.p.length + chunk.length);\n n.set(this.p), n.set(chunk, this.p.length);\n }\n else\n this.p = chunk;\n if (this.p.length > 2) {\n var _this_1 = this;\n var cb = function () { _this_1.ondata.apply(_this_1, arguments); };\n this.s = (this.p[0] == 31 && this.p[1] == 139 && this.p[2] == 8)\n ? new this.G(cb)\n : ((this.p[0] & 15) != 8 || (this.p[0] >> 4) > 7 || ((this.p[0] << 8 | this.p[1]) % 31))\n ? new this.I(cb)\n : new this.Z(cb);\n this.s.push(this.p, final);\n this.p = null;\n }\n }\n else\n this.s.push(chunk, final);\n };\n return Decompress;\n}());\nexport { Decompress };\n/**\n * Asynchronous streaming GZIP, Zlib, or raw DEFLATE decompression\n */\nvar AsyncDecompress = /*#__PURE__*/ (function () {\n /**\n * Creates an asynchronous decompression stream\n * @param cb The callback to call whenever data is decompressed\n */\n function AsyncDecompress(cb) {\n this.G = AsyncGunzip;\n this.I = AsyncInflate;\n this.Z = AsyncUnzlib;\n this.ondata = cb;\n }\n /**\n * Pushes a chunk to be decompressed\n * @param chunk The chunk to push\n * @param final Whether this is the last chunk\n */\n AsyncDecompress.prototype.push = function (chunk, final) {\n Decompress.prototype.push.call(this, chunk, final);\n };\n return AsyncDecompress;\n}());\nexport { AsyncDecompress };\nexport function decompress(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n return (data[0] == 31 && data[1] == 139 && data[2] == 8)\n ? gunzip(data, opts, cb)\n : ((data[0] & 15) != 8 || (data[0] >> 4) > 7 || ((data[0] << 8 | data[1]) % 31))\n ? inflate(data, opts, cb)\n : unzlib(data, opts, cb);\n}\n/**\n * Expands compressed GZIP, Zlib, or raw DEFLATE data, automatically detecting the format\n * @param data The data to decompress\n * @param out Where to write the data. Saves memory if you know the decompressed size and provide an output buffer of that length.\n * @returns The decompressed version of the data\n */\nexport function decompressSync(data, out) {\n return (data[0] == 31 && data[1] == 139 && data[2] == 8)\n ? gunzipSync(data, out)\n : ((data[0] & 15) != 8 || (data[0] >> 4) > 7 || ((data[0] << 8 | data[1]) % 31))\n ? inflateSync(data, out)\n : unzlibSync(data, out);\n}\n// flatten a directory structure\nvar fltn = function (d, p, t, o) {\n for (var k in d) {\n var val = d[k], n = p + k;\n if (val instanceof u8)\n t[n] = [val, o];\n else if (Array.isArray(val))\n t[n] = [val[0], mrg(o, val[1])];\n else\n fltn(val, n + '/', t, o);\n }\n};\n/**\n * Converts a string into a Uint8Array for use with compression/decompression methods\n * @param str The string to encode\n * @param latin1 Whether or not to interpret the data as Latin-1. This should\n * not need to be true unless decoding a binary string.\n * @returns The string encoded in UTF-8/Latin-1 binary\n */\nexport function strToU8(str, latin1) {\n var l = str.length;\n if (!latin1 && typeof TextEncoder != 'undefined')\n return new TextEncoder().encode(str);\n var ar = new u8(str.length + (str.length >>> 1));\n var ai = 0;\n var w = function (v) { ar[ai++] = v; };\n for (var i = 0; i < l; ++i) {\n if (ai + 5 > ar.length) {\n var n = new u8(ai + 8 + ((l - i) << 1));\n n.set(ar);\n ar = n;\n }\n var c = str.charCodeAt(i);\n if (c < 128 || latin1)\n w(c);\n else if (c < 2048)\n w(192 | (c >>> 6)), w(128 | (c & 63));\n else if (c > 55295 && c < 57344)\n c = 65536 + (c & 1023 << 10) | (str.charCodeAt(++i) & 1023),\n w(240 | (c >>> 18)), w(128 | ((c >>> 12) & 63)), w(128 | ((c >>> 6) & 63)), w(128 | (c & 63));\n else\n w(224 | (c >>> 12)), w(128 | ((c >>> 6) & 63)), w(128 | (c & 63));\n }\n return slc(ar, 0, ai);\n}\n/**\n * Converts a Uint8Array to a string\n * @param dat The data to decode to string\n * @param latin1 Whether or not to interpret the data as Latin-1. This should\n * not need to be true unless encoding to binary string.\n * @returns The original UTF-8/Latin-1 string\n */\nexport function strFromU8(dat, latin1) {\n var r = '';\n if (!latin1 && typeof TextDecoder != 'undefined')\n return new TextDecoder().decode(dat);\n for (var i = 0; i < dat.length;) {\n var c = dat[i++];\n if (c < 128 || latin1)\n r += String.fromCharCode(c);\n else if (c < 224)\n r += String.fromCharCode((c & 31) << 6 | (dat[i++] & 63));\n else if (c < 240)\n r += String.fromCharCode((c & 15) << 12 | (dat[i++] & 63) << 6 | (dat[i++] & 63));\n else\n c = ((c & 15) << 18 | (dat[i++] & 63) << 12 | (dat[i++] & 63) << 6 | (dat[i++] & 63)) - 65536,\n r += String.fromCharCode(55296 | (c >> 10), 56320 | (c & 1023));\n }\n return r;\n}\n;\n// skip local zip header\nvar slzh = function (d, b) { return b + 30 + b2(d, b + 26) + b2(d, b + 28); };\n// read zip header\nvar zh = function (d, b, z) {\n var fnl = b2(d, b + 28), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl;\n var _a = z ? z64e(d, es) : [b4(d, b + 20), b4(d, b + 24), b4(d, b + 42)], sc = _a[0], su = _a[1], off = _a[2];\n return [b2(d, b + 10), sc, su, fn, es + b2(d, b + 30) + b2(d, b + 32), off];\n};\n// read zip64 extra field\nvar z64e = function (d, b) {\n for (; b2(d, b) != 1; b += 4 + b2(d, b + 2))\n ;\n return [b4(d, b + 12), b4(d, b + 4), b4(d, b + 20)];\n};\n// write zip header\nvar wzh = function (d, b, c, cmp, su, fn, u, o, ce, t) {\n var fl = fn.length, l = cmp.length;\n wbytes(d, b, ce != null ? 0x2014B50 : 0x4034B50), b += 4;\n if (ce != null)\n d[b] = 20, b += 2;\n d[b] = 20, b += 2; // spec compliance? what's that?\n d[b++] = (t == 8 && (o.level == 1 ? 6 : o.level < 6 ? 4 : o.level == 9 ? 2 : 0)), d[b++] = u && 8;\n d[b] = t, b += 2;\n var dt = new Date(o.mtime || Date.now()), y = dt.getFullYear() - 1980;\n if (y < 0 || y > 119)\n throw 'date not in range 1980-2099';\n wbytes(d, b, ((y << 24) * 2) | ((dt.getMonth() + 1) << 21) | (dt.getDate() << 16) | (dt.getHours() << 11) | (dt.getMinutes() << 5) | (dt.getSeconds() >>> 1));\n b += 4;\n wbytes(d, b, c);\n wbytes(d, b + 4, l);\n wbytes(d, b + 8, su);\n wbytes(d, b + 12, fl), b += 16; // skip extra field, comment\n if (ce != null)\n wbytes(d, b += 10, ce), b += 4;\n d.set(fn, b);\n b += fl;\n if (ce == null)\n d.set(cmp, b);\n};\n// write zip footer (end of central directory)\nvar wzf = function (o, b, c, d, e) {\n wbytes(o, b, 0x6054B50); // skip disk\n wbytes(o, b + 8, c);\n wbytes(o, b + 10, c);\n wbytes(o, b + 12, d);\n wbytes(o, b + 16, e);\n};\nexport function zip(data, opts, cb) {\n if (!cb)\n cb = opts, opts = {};\n if (typeof cb != 'function')\n throw 'no callback';\n var r = {};\n fltn(data, '', r, opts);\n var k = Object.keys(r);\n var lft = k.length, o = 0, tot = 0;\n var slft = lft, files = new Array(lft);\n var term = [];\n var tAll = function () {\n for (var i = 0; i < term.length; ++i)\n term[i]();\n };\n var cbf = function () {\n var out = new u8(tot + 22), oe = o, cdl = tot - o;\n tot = 0;\n for (var i = 0; i < slft; ++i) {\n var f = files[i];\n try {\n wzh(out, tot, f.c, f.d, f.m, f.n, f.u, f.p, null, f.t);\n wzh(out, o, f.c, f.d, f.m, f.n, f.u, f.p, tot, f.t), o += 46 + f.n.length, tot += 30 + f.n.length + f.d.length;\n }\n catch (e) {\n return cb(e, null);\n }\n }\n wzf(out, o, files.length, cdl, oe);\n cb(null, out);\n };\n if (!lft)\n cbf();\n var _loop_1 = function (i) {\n var fn = k[i];\n var _a = r[fn], file = _a[0], p = _a[1];\n var c = crc(), m = file.length;\n c.p(file);\n var n = strToU8(fn), s = n.length;\n var t = p.level == 0 ? 0 : 8;\n var cbl = function (e, d) {\n if (e) {\n tAll();\n cb(e, null);\n }\n else {\n var l = d.length;\n files[i] = {\n t: t,\n d: d,\n m: m,\n c: c.d(),\n u: fn.length != l,\n n: n,\n p: p\n };\n o += 30 + s + l;\n tot += 76 + 2 * s + l;\n if (!--lft)\n cbf();\n }\n };\n if (n.length > 65535)\n cbl('filename too long', null);\n if (!t)\n cbl(null, file);\n else if (m < 160000) {\n try {\n cbl(null, deflateSync(file, p));\n }\n catch (e) {\n cbl(e, null);\n }\n }\n else\n term.push(deflate(file, p, cbl));\n };\n // Cannot use lft because it can decrease\n for (var i = 0; i < slft; ++i) {\n _loop_1(i);\n }\n return tAll;\n}\n/**\n * Synchronously creates a ZIP file. Prefer using `zip` for better performance\n * with more than one file.\n * @param data The directory structure for the ZIP archive\n * @param opts The main options, merged with per-file options\n * @returns The generated ZIP archive\n */\nexport function zipSync(data, opts) {\n if (opts === void 0) { opts = {}; }\n var r = {};\n var files = [];\n fltn(data, '', r, opts);\n var o = 0;\n var tot = 0;\n for (var fn in r) {\n var _a = r[fn], file = _a[0], p = _a[1];\n var t = p.level == 0 ? 0 : 8;\n var n = strToU8(fn), s = n.length;\n if (n.length > 65535)\n throw 'filename too long';\n var d = t ? deflateSync(file, p) : file, l = d.length;\n var c = crc();\n c.p(file);\n files.push({\n t: t,\n d: d,\n m: file.length,\n c: c.d(),\n u: fn.length != s,\n n: n,\n o: o,\n p: p\n });\n o += 30 + s + l;\n tot += 76 + 2 * s + l;\n }\n var out = new u8(tot + 22), oe = o, cdl = tot - o;\n for (var i = 0; i < files.length; ++i) {\n var f = files[i];\n wzh(out, f.o, f.c, f.d, f.m, f.n, f.u, f.p, null, f.t);\n wzh(out, o, f.c, f.d, f.m, f.n, f.u, f.p, f.o, f.t), o += 46 + f.n.length;\n }\n wzf(out, o, files.length, cdl, oe);\n return out;\n}\n/**\n * Asynchronously decompresses a ZIP archive\n * @param data The raw compressed ZIP file\n * @param cb The callback to call with the decompressed files\n * @returns A function that can be used to immediately terminate the unzipping\n */\nexport function unzip(data, cb) {\n if (typeof cb != 'function')\n throw 'no callback';\n var term = [];\n var tAll = function () {\n for (var i = 0; i < term.length; ++i)\n term[i]();\n };\n var files = {};\n var e = data.length - 22;\n for (; b4(data, e) != 0x6054B50; --e) {\n if (!e || data.length - e > 65558) {\n cb('invalid zip file', null);\n return;\n }\n }\n ;\n var lft = b2(data, e + 8);\n if (!lft)\n cb(null, {});\n var c = lft;\n var o = b4(data, e + 16);\n var z = o == 4294967295;\n if (z) {\n e = b4(data, e - 12);\n if (b4(data, e) != 0x6064B50)\n throw 'invalid zip file';\n c = lft = b4(data, e + 32);\n o = b4(data, e + 48);\n }\n var _loop_2 = function (i) {\n var _a = zh(data, o, z), c_1 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);\n o = no;\n var cbl = function (e, d) {\n if (e) {\n tAll();\n cb(e, null);\n }\n else {\n files[fn] = d;\n if (!--lft)\n cb(null, files);\n }\n };\n if (!c_1)\n cbl(null, slc(data, b, b + sc));\n else if (c_1 == 8) {\n var infl = data.subarray(b, b + sc);\n if (sc < 320000) {\n try {\n cbl(null, inflateSync(infl, new u8(su)));\n }\n catch (e) {\n cbl(e, null);\n }\n }\n else\n term.push(inflate(infl, { size: su }, cbl));\n }\n else\n cbl('unknown compression type ' + c_1, null);\n };\n for (var i = 0; i < c; ++i) {\n _loop_2(i);\n }\n return tAll;\n}\n/**\n * Synchronously decompresses a ZIP archive. Prefer using `unzip` for better\n * performance with more than one file.\n * @param data The raw compressed ZIP file\n * @returns The decompressed files\n */\nexport function unzipSync(data) {\n var files = {};\n var e = data.length - 22;\n for (; b4(data, e) != 0x6054B50; --e) {\n if (!e || data.length - e > 65558)\n throw 'invalid zip file';\n }\n ;\n var c = b2(data, e + 8);\n if (!c)\n return {};\n var o = b4(data, e + 16);\n var z = o == 4294967295;\n if (z) {\n e = b4(data, e - 12);\n if (b4(data, e) != 0x6064B50)\n throw 'invalid zip file';\n c = b4(data, e + 32);\n o = b4(data, e + 48);\n }\n for (var i = 0; i < c; ++i) {\n var _a = zh(data, o, z), c_2 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);\n o = no;\n if (!c_2)\n files[fn] = slc(data, b, b + sc);\n else if (c_2 == 8)\n files[fn] = inflateSync(data.subarray(b, b + sc), new u8(su));\n else\n throw 'unknown compression type ' + c_2;\n }\n return files;\n}\n","import type { V2ReplayPayloadAdmissionCode } from './types'\n\n/**\n * A replay payload that cannot be admitted. Every code is a permanent local rejection: the bytes are\n * never persisted, retried, or acknowledged.\n */\nexport class V2ReplayPayloadAdmissionError extends Error {\n readonly code: V2ReplayPayloadAdmissionCode\n\n constructor(code: V2ReplayPayloadAdmissionCode, message: string) {\n super(message)\n this.name = 'V2ReplayPayloadAdmissionError'\n this.code = code\n }\n}\n\nexport class V2MaskingProfileError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'V2MaskingProfileError'\n }\n}\n\nexport class V2MaskingProofError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'V2MaskingProofError'\n }\n}\n\nexport class V2ReplayRequestError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'V2ReplayRequestError'\n }\n}\n\nexport class V2ReplayAcknowledgementError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'V2ReplayAcknowledgementError'\n }\n}\n","import { V2ReplayPayloadAdmissionError } from './errors'\n\nconst ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\nconst CANONICAL_PATTERN = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/\nconst decodeTable = new Int16Array(128).fill(-1)\nfor (let index = 0; index < ALPHABET.length; index += 1) {\n decodeTable[ALPHABET.charCodeAt(index)] = index\n}\n\n/** RFC 4648 standard alphabet with required padding. */\nexport const encodeBase64 = (bytes: Uint8Array): string => {\n let output = ''\n let index = 0\n for (; index + 2 < bytes.length; index += 3) {\n const triple = (bytes[index] << 16) | (bytes[index + 1] << 8) | bytes[index + 2]\n output +=\n ALPHABET[triple >> 18] +\n ALPHABET[(triple >> 12) & 63] +\n ALPHABET[(triple >> 6) & 63] +\n ALPHABET[triple & 63]\n }\n const remaining = bytes.length - index\n if (remaining === 1) {\n const value = bytes[index] << 16\n output += ALPHABET[value >> 18] + ALPHABET[(value >> 12) & 63] + '=='\n } else if (remaining === 2) {\n const value = (bytes[index] << 16) | (bytes[index + 1] << 8)\n output += ALPHABET[value >> 18] + ALPHABET[(value >> 12) & 63] + ALPHABET[(value >> 6) & 63] + '='\n }\n return output\n}\n\n/**\n * Strict decoder: only the standard alphabet, padding exactly where required, and a decoded value whose\n * canonical re-encoding reproduces the input byte for byte.\n */\nexport const decodeCanonicalBase64 = (value: string): Uint8Array => {\n if (typeof value !== 'string' || value.length === 0 || /[^A-Za-z0-9+/=]/.test(value)) {\n throw new V2ReplayPayloadAdmissionError('invalid-base64', 'payload is not standard base64')\n }\n if (!CANONICAL_PATTERN.test(value)) {\n throw new V2ReplayPayloadAdmissionError('noncanonical-base64', 'payload base64 padding is not canonical')\n }\n const padding = value.endsWith('==') ? 2 : value.endsWith('=') ? 1 : 0\n const output = new Uint8Array((value.length / 4) * 3 - padding)\n let offset = 0\n for (let index = 0; index < value.length; index += 4) {\n const first = decodeTable[value.charCodeAt(index)]\n const second = decodeTable[value.charCodeAt(index + 1)]\n const third = decodeTable[value.charCodeAt(index + 2)]\n const fourth = decodeTable[value.charCodeAt(index + 3)]\n const quad = (first << 18) | (second << 12) | ((third < 0 ? 0 : third) << 6) | (fourth < 0 ? 0 : fourth)\n output[offset++] = quad >> 16\n if (third >= 0) output[offset++] = (quad >> 8) & 0xff\n if (fourth >= 0) output[offset++] = quad & 0xff\n }\n if (encodeBase64(output) !== value) {\n throw new V2ReplayPayloadAdmissionError('noncanonical-base64', 'payload base64 has non-zero padding bits')\n }\n return output\n}\n","import { parseV1StrictCanonicalJson, V1StrictJsonError } from '../strict-canonical-json'\nimport {\n freezeV1Value,\n hasOwnIndex,\n isArrayValue,\n isFunctionValue,\n isNullValue,\n unicodeCodePointLength,\n utf8ByteLength,\n} from '../validation'\nimport { utf8Encode } from './digest'\nimport { V2ReplayPayloadAdmissionError } from './errors'\nimport type { V2ReplayPayloadLimits } from './types'\n\n/** Engine-side ceilings for one decoded replay payload. */\nexport const V2_REPLAY_MAX_DECODED_BYTES = 16_777_216\nexport const V2_REPLAY_MAX_LOGICAL_RECORDS = 10_000\nexport const V2_REPLAY_MAX_JSON_NESTING_DEPTH = 256\n\nexport const V2_REPLAY_PAYLOAD_LIMITS: V2ReplayPayloadLimits = freezeV1Value({\n maximumReplayDecodedBytes: V2_REPLAY_MAX_DECODED_BYTES,\n maximumReplayLogicalRecords: V2_REPLAY_MAX_LOGICAL_RECORDS,\n maximumJsonNestingDepth: V2_REPLAY_MAX_JSON_NESTING_DEPTH,\n})\n\nexport interface V2CanonicalReplayRecords {\n readonly canonical: string\n readonly bytes: Uint8Array\n readonly logicalRecords: number\n readonly nestingDepth: number\n}\n\nexport interface V2AdmittedReplayRecords {\n readonly records: readonly unknown[]\n readonly logicalRecords: number\n readonly nestingDepth: number\n}\n\nfunction fail(code: V2ReplayPayloadAdmissionError['code'], message: string): never {\n throw new V2ReplayPayloadAdmissionError(code, message)\n}\n\nconst isPlainObject = (value: object): value is Record<string, unknown> => {\n const prototype = Object.getPrototypeOf(value)\n return isNullValue(prototype) || prototype === Object.prototype\n}\n\nconst assertUnicode = (value: string, path: string): void => {\n if (isNullValue(unicodeCodePointLength(value))) {\n fail('invalid-unicode', `${path} contains an unpaired Unicode surrogate`)\n }\n}\n\n/** Serialization semantics for values JSON cannot carry: omitted as object members, null as array elements. */\nconst isOmittedValue = (value: unknown): boolean =>\n typeof value === 'undefined' || isFunctionValue(value) || typeof value === 'symbol'\n\ninterface CanonicalWalk {\n depth: number\n}\n\nconst walk = (\n value: unknown,\n depth: number,\n path: string,\n maximumNestingDepth: number,\n state: CanonicalWalk\n): string => {\n if (isNullValue(value)) return 'null'\n switch (typeof value) {\n case 'string':\n assertUnicode(value, path)\n return JSON.stringify(value)\n case 'number':\n if (!Number.isFinite(value)) fail('invalid-codec-json', `${path} must be a finite number`)\n // ECMAScript number formatting is the binary64 fallback of elu-canonical-json-v1; -0 prints as 0.\n return JSON.stringify(value)\n case 'boolean':\n return value ? 'true' : 'false'\n case 'object':\n break\n default:\n return fail('invalid-codec-json', `${path} has an unsupported ${typeof value} value`)\n }\n\n const containerDepth = depth + 1\n if (containerDepth > maximumNestingDepth) {\n fail('json-nesting-limit', `${path} exceeds the maximum JSON nesting depth of ${maximumNestingDepth}`)\n }\n if (containerDepth > state.depth) state.depth = containerDepth\n\n if (isArrayValue(value)) {\n const parts: string[] = []\n for (let index = 0; index < value.length; index += 1) {\n if (!hasOwnIndex(value, index)) fail('invalid-codec-json', `${path}[${index}] is a sparse array hole`)\n const element = value[index]\n parts.push(\n isOmittedValue(element)\n ? 'null'\n : walk(element, containerDepth, `${path}[${index}]`, maximumNestingDepth, state)\n )\n }\n return `[${parts.join(',')}]`\n }\n if (!isPlainObject(value as object)) {\n return fail('invalid-codec-json', `${path} must be a plain JSON object or array`)\n }\n const record = value as Record<string, unknown>\n const keys = Object.keys(record).sort()\n const members: string[] = []\n for (const key of keys) {\n const member = record[key]\n if (isOmittedValue(member)) continue\n assertUnicode(key, `${path}.${key}`)\n members.push(\n `${JSON.stringify(key)}:${walk(member, containerDepth, `${path}.${key}`, maximumNestingDepth, state)}`\n )\n }\n return `{${members.join(',')}}`\n}\n\n/**\n * Canonicalizes an in-memory JSON value: members sorted by UTF-16 code unit order, no whitespace,\n * ECMAScript number formatting, and a container nesting ceiling counted from the root container.\n */\nexport const canonicalV2JsonValue = (\n value: unknown,\n field: string,\n maximumNestingDepth: number = V2_REPLAY_MAX_JSON_NESTING_DEPTH\n): string => {\n if (isOmittedValue(value)) fail('invalid-codec-json', `${field} must be a JSON value`)\n return walk(value, 0, field, maximumNestingDepth, { depth: 0 })\n}\n\n/** Canonical UTF-8 bytes of an ordered array of masked replay records, bounded by the payload limits. */\nexport const canonicalV2ReplayRecords = (\n records: unknown,\n field: string,\n limits: V2ReplayPayloadLimits = V2_REPLAY_PAYLOAD_LIMITS\n): V2CanonicalReplayRecords => {\n if (!isArrayValue(records)) fail('invalid-codec-shape', `${field} must be an array of replay records`)\n if (records.length > limits.maximumReplayLogicalRecords) {\n fail(\n 'logical-record-limit',\n `${field} carries ${records.length} records; the maximum is ${limits.maximumReplayLogicalRecords}`\n )\n }\n const state: CanonicalWalk = { depth: 0 }\n const canonical = walk(records, 0, field, limits.maximumJsonNestingDepth, state)\n const byteLength = utf8ByteLength(canonical)\n if (byteLength > limits.maximumReplayDecodedBytes) {\n fail(\n 'decoded-byte-limit',\n `${field} serializes to ${byteLength} bytes; the maximum is ${limits.maximumReplayDecodedBytes}`\n )\n }\n return freezeV1Value({\n canonical,\n bytes: utf8Encode(canonical),\n logicalRecords: records.length,\n nestingDepth: state.depth,\n })\n}\n\nconst measureNestingDepth = (value: unknown, depth: number): number => {\n if (isNullValue(value) || typeof value !== 'object') return depth\n let deepest = depth + 1\n const children = isArrayValue(value)\n ? value\n : Object.keys(value).map((key) => (value as Record<string, unknown>)[key])\n for (const child of children) {\n const measured = measureNestingDepth(child, depth + 1)\n if (measured > deepest) deepest = measured\n }\n return deepest\n}\n\n/**\n * Admits decoded payload text the way the engine does: strict JSON, no duplicate keys, canonical\n * bytes only, a top-level array, and the nesting and record ceilings.\n */\nexport const admitV2CanonicalPayloadText = (\n text: string,\n limits: V2ReplayPayloadLimits = V2_REPLAY_PAYLOAD_LIMITS\n): V2AdmittedReplayRecords => {\n let parsed\n try {\n parsed = parseV1StrictCanonicalJson(\n text,\n limits.maximumReplayDecodedBytes,\n 'replay payload',\n limits.maximumJsonNestingDepth\n )\n } catch (error) {\n const reason = error instanceof V1StrictJsonError ? error.reason : 'syntax'\n const message = error instanceof Error ? error.message : 'replay payload is not strict JSON'\n if (reason === 'nesting-depth') fail('json-nesting-limit', message)\n if (reason === 'unicode') fail('invalid-unicode', message)\n if (reason === 'size') fail('decoded-byte-limit', message)\n return fail('invalid-codec-json', message)\n }\n if (!isArrayValue(parsed.value)) fail('invalid-codec-shape', 'replay payload must be a JSON array')\n if (parsed.canonical !== text) fail('noncanonical-codec-json', 'replay payload is not canonical JSON')\n const records = parsed.value as readonly unknown[]\n if (records.length > limits.maximumReplayLogicalRecords) {\n fail(\n 'logical-record-limit',\n `replay payload carries ${records.length} records; the maximum is ${limits.maximumReplayLogicalRecords}`\n )\n }\n return freezeV1Value({\n records,\n logicalRecords: records.length,\n nestingDepth: measureNestingDepth(records, 0),\n })\n}\n","import {\n freezeV1Value,\n hasOnlyKeys,\n hasOwn,\n hasOwnIndex,\n isArrayValue,\n isBooleanValue,\n isNullValue,\n isRecordValue,\n isStringValue,\n unicodeCodePointLength,\n} from '../validation'\nimport { canonicalV2JsonValue } from './canonical-payload-json'\nimport { sha256Hex, utf8Encode } from './digest'\nimport { V2MaskingProfileError } from './errors'\nimport type {\n V2MaskingImageRule,\n V2MaskingInputRule,\n V2MaskingInvalidationAction,\n V2MaskingProfile,\n V2MaskingRequirementComparison,\n V2MaskingTextRule,\n V2ReplaySha256,\n V2Sha256Hash,\n} from './types'\n\nexport const V2_MASKING_PROFILE_SCHEMA_VERSION = 1\nexport const V2_BROWSER_MASKING_DIALECT = 'elu-css-selector-v1'\nexport const V2_MASKING_MAX_SELECTORS = 128\nexport const V2_MASKING_MAX_SELECTOR_LENGTH = 512\n\nconst PROFILE_KEYS = [\n 'schemaVersion',\n 'targetDialect',\n 'textRule',\n 'inputRule',\n 'imageRule',\n 'secureInputsMasked',\n 'platformFallbackApplied',\n 'resolvedMaskSelectors',\n 'resolvedBlockSelectors',\n] as const\nconst POLICY_MASKING_KEYS = ['text', 'inputs', 'images', 'secureInputsMasked', 'platformRules'] as const\nconst PLATFORM_RULE_KEYS = ['platform', 'action', 'targetDialect', 'target'] as const\nconst TEXT_RULES: readonly V2MaskingTextRule[] = ['all', 'sensitive']\nconst INPUT_RULES: readonly V2MaskingInputRule[] = ['all', 'sensitive']\nconst IMAGE_RULES: readonly V2MaskingImageRule[] = ['block', 'allow']\n\nfunction fail(message: string): never {\n throw new V2MaskingProfileError(message)\n}\n\nconst assertRule = <T extends string>(value: unknown, allowed: readonly T[], field: string): T => {\n if (!isStringValue(value) || !allowed.includes(value as T)) {\n fail(`${field} must be one of ${allowed.join(', ')}`)\n }\n return value as T\n}\n\nconst assertSelector = (value: unknown, field: string): string => {\n const length = isStringValue(value) ? unicodeCodePointLength(value) : null\n if (isNullValue(length) || length < 1 || length > V2_MASKING_MAX_SELECTOR_LENGTH) {\n return fail(`${field} must be a selector of 1 to ${V2_MASKING_MAX_SELECTOR_LENGTH} Unicode scalars`)\n }\n return value as string\n}\n\n/** Sorted by UTF-16 code unit order with duplicates removed, so equal rule sets canonicalize identically. */\nconst normalizeSelectors = (values: unknown, field: string): readonly string[] => {\n if (!isArrayValue(values)) fail(`${field} must be an array of selectors`)\n const selectors = new Set<string>()\n for (let index = 0; index < values.length; index += 1) {\n if (!hasOwnIndex(values, index)) fail(`${field} must not be sparse`)\n selectors.add(assertSelector(values[index], `${field}[${index}]`))\n }\n if (selectors.size > V2_MASKING_MAX_SELECTORS) {\n fail(`${field} may carry at most ${V2_MASKING_MAX_SELECTORS} selectors`)\n }\n return freezeV1Value([...selectors].sort())\n}\n\n/** Validates a closed masking-profile object and returns its frozen normalized form. */\nexport const buildV2MaskingProfile = (input: unknown): V2MaskingProfile => {\n if (!isRecordValue(input) || !hasOnlyKeys(input, PROFILE_KEYS)) {\n fail('masking profile must contain only schema-defined fields')\n }\n for (const key of PROFILE_KEYS) {\n if (!hasOwn(input, key)) fail(`masking profile ${key} is required`)\n }\n if (input.schemaVersion !== V2_MASKING_PROFILE_SCHEMA_VERSION) {\n fail(`masking profile schemaVersion must be ${V2_MASKING_PROFILE_SCHEMA_VERSION}`)\n }\n if (input.targetDialect !== V2_BROWSER_MASKING_DIALECT) {\n fail(`masking profile targetDialect must be ${V2_BROWSER_MASKING_DIALECT}`)\n }\n if (input.secureInputsMasked !== true) fail('masking profile secureInputsMasked must be true')\n if (!isBooleanValue(input.platformFallbackApplied)) {\n fail('masking profile platformFallbackApplied must be a boolean')\n }\n return freezeV1Value({\n schemaVersion: V2_MASKING_PROFILE_SCHEMA_VERSION,\n targetDialect: V2_BROWSER_MASKING_DIALECT,\n textRule: assertRule(input.textRule, TEXT_RULES, 'masking profile textRule'),\n inputRule: assertRule(input.inputRule, INPUT_RULES, 'masking profile inputRule'),\n imageRule: assertRule(input.imageRule, IMAGE_RULES, 'masking profile imageRule'),\n secureInputsMasked: true,\n platformFallbackApplied: input.platformFallbackApplied,\n resolvedMaskSelectors: normalizeSelectors(input.resolvedMaskSelectors, 'masking profile resolvedMaskSelectors'),\n resolvedBlockSelectors: normalizeSelectors(\n input.resolvedBlockSelectors,\n 'masking profile resolvedBlockSelectors'\n ),\n })\n}\n\nexport interface V2MaskingProfileResolution {\n /** True when the recorder had to fall back to a coarser platform masking mode. */\n readonly platformFallbackApplied?: boolean\n}\n\n/**\n * Projects the server privacy policy's masking block onto the browser: the text, input, and image\n * rules plus every browser rule in the recognized selector dialect. Rules for other platforms are not\n * the browser's to apply; a browser rule in an unrecognized dialect cannot be honored and fails closed.\n */\nexport const resolveV2MaskingProfile = (\n masking: unknown,\n resolution: V2MaskingProfileResolution = {}\n): V2MaskingProfile => {\n if (!isRecordValue(masking) || !hasOnlyKeys(masking, POLICY_MASKING_KEYS)) {\n fail('policy masking must contain only schema-defined fields')\n }\n const maskSelectors: string[] = []\n const blockSelectors: string[] = []\n if (hasOwn(masking, 'platformRules')) {\n if (!isArrayValue(masking.platformRules)) fail('policy masking platformRules must be an array')\n masking.platformRules.forEach((rule, index) => {\n const field = `policy masking platformRules[${index}]`\n if (!isRecordValue(rule) || !hasOnlyKeys(rule, PLATFORM_RULE_KEYS)) {\n fail(`${field} must contain only schema-defined fields`)\n }\n if (rule.platform !== 'browser' && rule.platform !== 'android' && rule.platform !== 'ios') {\n fail(`${field} platform is unsupported`)\n }\n if (rule.action !== 'mask' && rule.action !== 'block') fail(`${field} action is unsupported`)\n if (!isStringValue(rule.targetDialect)) fail(`${field} targetDialect must be a string`)\n const target = assertSelector(rule.target, `${field} target`)\n if (rule.platform !== 'browser') return\n if (rule.targetDialect !== V2_BROWSER_MASKING_DIALECT) {\n fail(`${field} uses the unrecognized browser masking dialect ${rule.targetDialect}`)\n }\n ;(rule.action === 'mask' ? maskSelectors : blockSelectors).push(target)\n })\n }\n return buildV2MaskingProfile({\n schemaVersion: V2_MASKING_PROFILE_SCHEMA_VERSION,\n targetDialect: V2_BROWSER_MASKING_DIALECT,\n textRule: masking.text,\n inputRule: masking.inputs,\n imageRule: masking.images,\n secureInputsMasked: masking.secureInputsMasked,\n platformFallbackApplied: resolution.platformFallbackApplied === true,\n resolvedMaskSelectors: maskSelectors,\n resolvedBlockSelectors: blockSelectors,\n })\n}\n\n/** Canonical JSON of the closed profile: sorted members, no whitespace. */\nexport const canonicalV2MaskingProfile = (profile: V2MaskingProfile): string =>\n canonicalV2JsonValue(buildV2MaskingProfile(profile), 'masking profile')\n\nexport const canonicalV2MaskingProfileBytes = (profile: V2MaskingProfile): Uint8Array =>\n utf8Encode(canonicalV2MaskingProfile(profile))\n\n/** The profile hash bound into every replay chunk and its request identity. */\nexport const hashV2MaskingProfile = async (profile: V2MaskingProfile, sha256?: V2ReplaySha256): Promise<V2Sha256Hash> =>\n `sha256:${await sha256Hex(canonicalV2MaskingProfileBytes(profile), sha256)}`\n\nconst RULE_STRENGTH = {\n text: { sensitive: 1, all: 2 },\n input: { sensitive: 1, all: 2 },\n image: { allow: 1, block: 2 },\n} as const\n\ntype DimensionOrder = -1 | 0 | 1\n\nconst orderStrength = (applied: number, required: number): DimensionOrder =>\n applied === required ? 0 : applied > required ? 1 : -1\n\n/** 1 when the applied set covers every required selector, -1 when it is covered, 0 when equal, null otherwise. */\nconst orderSelectors = (applied: readonly string[], required: readonly string[]): DimensionOrder | null => {\n const appliedSet = new Set(applied)\n const requiredSet = new Set(required)\n const appliedCoversRequired = required.every((selector) => appliedSet.has(selector))\n const requiredCoversApplied = applied.every((selector) => requiredSet.has(selector))\n if (appliedCoversRequired && requiredCoversApplied) return 0\n if (appliedCoversRequired) return 1\n if (requiredCoversApplied) return -1\n return null\n}\n\n/**\n * Relates a newly required profile to the profile a sealed chunk was masked with. Serialized bytes\n * cannot be remasked, so a requirement that is stricter on any dimension, or not comparable with the\n * applied rules (a different dialect, fallback mode, or selector sets that neither contain the other),\n * makes the sealed bytes unlawful; an equal or looser requirement leaves them lawful.\n */\nexport const compareV2MaskingRequirement = (\n applied: V2MaskingProfile,\n required: V2MaskingProfile\n): V2MaskingRequirementComparison => {\n const appliedProfile = buildV2MaskingProfile(applied)\n const requiredProfile = buildV2MaskingProfile(required)\n if (\n appliedProfile.schemaVersion !== requiredProfile.schemaVersion ||\n appliedProfile.targetDialect !== requiredProfile.targetDialect ||\n appliedProfile.platformFallbackApplied !== requiredProfile.platformFallbackApplied\n ) {\n return 'incomparable'\n }\n const orders = [\n orderStrength(RULE_STRENGTH.text[appliedProfile.textRule], RULE_STRENGTH.text[requiredProfile.textRule]),\n orderStrength(RULE_STRENGTH.input[appliedProfile.inputRule], RULE_STRENGTH.input[requiredProfile.inputRule]),\n orderStrength(RULE_STRENGTH.image[appliedProfile.imageRule], RULE_STRENGTH.image[requiredProfile.imageRule]),\n orderSelectors(appliedProfile.resolvedMaskSelectors, requiredProfile.resolvedMaskSelectors),\n orderSelectors(appliedProfile.resolvedBlockSelectors, requiredProfile.resolvedBlockSelectors),\n ]\n if (orders.some((order) => isNullValue(order))) return 'incomparable'\n const appliedIsStricterSomewhere = orders.some((order) => order === 1)\n const requiredIsStricterSomewhere = orders.some((order) => order === -1)\n if (appliedIsStricterSomewhere && requiredIsStricterSomewhere) return 'incomparable'\n if (requiredIsStricterSomewhere) return 'stricter'\n if (appliedIsStricterSomewhere) return 'looser'\n return 'equal'\n}\n\nexport const v2MaskingInvalidationAction = (comparison: V2MaskingRequirementComparison): V2MaskingInvalidationAction =>\n comparison === 'equal' || comparison === 'looser' ? 'preserve' : 'purge'\n","import { isArrayValue, isFunctionValue } from '../validation'\nimport { canonicalV2ReplayRecords, V2_REPLAY_PAYLOAD_LIMITS } from './canonical-payload-json'\nimport { isV2Sha256Hash } from './digest'\nimport { V2MaskingProofError } from './errors'\nimport { buildV2MaskingProfile } from './masking-profile'\nimport type { V2MaskingProfile, V2ReplayPayloadLimits, V2Sha256Hash } from './types'\n\n/** Applies the effective masking profile to one raw recorder event and returns the masked event. */\nexport type V2ReplayEventMasker = (event: unknown, profile: V2MaskingProfile) => unknown\n\nexport interface V2MaskingStageInput {\n readonly profile: V2MaskingProfile\n readonly profileHash: V2Sha256Hash\n readonly events: readonly unknown[]\n readonly limits?: V2ReplayPayloadLimits\n}\n\nconst SEAL = Symbol('elu-replay-masking-seal')\n\n/**\n * The sealed output of the masking stage and the only value the payload codec serializes. Masking\n * runs inside the stage, the masked records are canonicalized here, and the resulting text is what\n * gets compressed; nothing that bypassed the stage can reach compression, persistence, or transport.\n */\nexport class V2MaskingProof {\n readonly profile: V2MaskingProfile\n readonly profileHash: V2Sha256Hash\n readonly canonicalPayload: string\n readonly payloadByteLength: number\n readonly logicalRecords: number\n readonly nestingDepth: number\n\n private constructor(\n seal: symbol,\n profile: V2MaskingProfile,\n profileHash: V2Sha256Hash,\n canonicalPayload: string,\n payloadByteLength: number,\n logicalRecords: number,\n nestingDepth: number\n ) {\n if (seal !== SEAL) throw new V2MaskingProofError('masking proofs are sealed by the masking stage only')\n this.profile = profile\n this.profileHash = profileHash\n this.canonicalPayload = canonicalPayload\n this.payloadByteLength = payloadByteLength\n this.logicalRecords = logicalRecords\n this.nestingDepth = nestingDepth\n Object.freeze(this)\n }\n\n /** Runs the masker over every raw event in order and seals the masked, canonicalized result. */\n static seal(masker: V2ReplayEventMasker, input: V2MaskingStageInput): V2MaskingProof {\n if (!isFunctionValue(masker)) throw new V2MaskingProofError('masking stage requires a masker')\n if (!isArrayValue(input.events)) throw new V2MaskingProofError('masking stage requires an event array')\n const profile = buildV2MaskingProfile(input.profile)\n if (!isV2Sha256Hash(input.profileHash)) {\n throw new V2MaskingProofError('masking stage requires the sha256 profile hash')\n }\n const masked = input.events.map((event) => masker(event, profile))\n const canonical = canonicalV2ReplayRecords(\n masked,\n 'masked replay records',\n input.limits ?? V2_REPLAY_PAYLOAD_LIMITS\n )\n return new V2MaskingProof(\n SEAL,\n profile,\n input.profileHash,\n canonical.canonical,\n canonical.bytes.length,\n canonical.logicalRecords,\n canonical.nestingDepth\n )\n }\n}\n\nexport const isV2MaskingProof = (value: unknown): value is V2MaskingProof => value instanceof V2MaskingProof\n","import { gzipSync, Inflate } from 'fflate'\nimport { decodeCanonicalBase64, encodeBase64 } from './base64'\nimport { admitV2CanonicalPayloadText, V2_REPLAY_PAYLOAD_LIMITS } from './canonical-payload-json'\nimport { utf8Encode } from './digest'\nimport { V2ReplayPayloadAdmissionError } from './errors'\nimport { isV2MaskingProof, V2MaskingProof } from './masking-proof'\nimport type { V2ReplayCodec, V2ReplayCompression, V2ReplayContentEncoding, V2ReplayPayloadLimits } from './types'\n\n/**\n * elu-browser-dom-v1: the canonical JSON array of masked DOM recording events, gzip-compressed exactly\n * once, carried as base64. The outer envelope describes the inner payload only; HTTP transfer stays\n * identity-encoded.\n */\nexport const V2_BROWSER_DOM_CODEC: V2ReplayCodec = 'elu-browser-dom-v1'\nexport const V2_BROWSER_DOM_COMPRESSION: V2ReplayCompression = 'gzip'\nexport const V2_REPLAY_CONTENT_ENCODING: V2ReplayContentEncoding = 'base64'\n\nconst GZIP_MAGIC_FIRST = 0x1f\nconst GZIP_MAGIC_SECOND = 0x8b\nconst GZIP_DEFLATE_METHOD = 8\nconst GZIP_FIXED_HEADER_BYTES = 10\nconst GZIP_TRAILER_BYTES = 8\nconst GZIP_MINIMUM_BYTES = GZIP_FIXED_HEADER_BYTES + GZIP_TRAILER_BYTES\nconst GZIP_FLAG_HEADER_CRC = 0x02\nconst GZIP_FLAG_EXTRA = 0x04\nconst GZIP_FLAG_NAME = 0x08\nconst GZIP_FLAG_COMMENT = 0x10\nconst GZIP_RESERVED_FLAGS = 0xe0\n\nexport interface V2EncodedReplayPayload {\n readonly codec: V2ReplayCodec\n readonly compression: V2ReplayCompression\n readonly contentEncoding: V2ReplayContentEncoding\n readonly payload: string\n readonly compressedByteLength: number\n readonly decodedByteLength: number\n readonly logicalRecords: number\n readonly proof: V2MaskingProof\n}\n\nexport interface V2AdmittedReplayPayload {\n readonly decoded: Uint8Array\n readonly decodedByteLength: number\n readonly logicalRecords: number\n readonly nestingDepth: number\n readonly records: readonly unknown[]\n}\n\nfunction fail(code: V2ReplayPayloadAdmissionError['code'], message: string): never {\n throw new V2ReplayPayloadAdmissionError(code, message)\n}\n\n/** Serializes a sealed masking proof: compress once, then base64. Nothing else is accepted. */\nexport const encodeV2BrowserDomPayload = (proof: V2MaskingProof): V2EncodedReplayPayload => {\n if (!isV2MaskingProof(proof)) {\n throw new V2ReplayPayloadAdmissionError(\n 'invalid-codec-shape',\n 'payload serialization requires a sealed masking proof'\n )\n }\n const decoded = utf8Encode(proof.canonicalPayload)\n let compressed: Uint8Array\n try {\n // A zero mtime keeps the member header free of capture-time metadata.\n compressed = gzipSync(decoded, { mtime: 0 })\n } catch {\n return fail('invalid-gzip', 'payload compression failed')\n }\n return Object.freeze({\n codec: V2_BROWSER_DOM_CODEC,\n compression: V2_BROWSER_DOM_COMPRESSION,\n contentEncoding: V2_REPLAY_CONTENT_ENCODING,\n payload: encodeBase64(compressed),\n compressedByteLength: compressed.length,\n decodedByteLength: decoded.length,\n logicalRecords: proof.logicalRecords,\n proof,\n })\n}\n\nconst CRC_TABLE = (() => {\n const table = new Int32Array(256)\n for (let index = 0; index < 256; index += 1) {\n let value = index\n for (let bit = 0; bit < 8; bit += 1) {\n value = value & 1 ? (value >>> 1) ^ 0xedb88320 : value >>> 1\n }\n table[index] = value\n }\n return table\n})()\n\nconst crc32 = (chunks: readonly Uint8Array[]): number => {\n let crc = -1\n for (const chunk of chunks) {\n for (let index = 0; index < chunk.length; index += 1) {\n crc = CRC_TABLE[(crc ^ chunk[index]) & 0xff] ^ (crc >>> 8)\n }\n }\n return (crc ^ -1) >>> 0\n}\n\nconst readUint32LittleEndian = (bytes: Uint8Array, offset: number): number =>\n (bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24)) >>> 0\n\nconst gzipHeaderLength = (payload: Uint8Array): number => {\n if (\n payload.length < GZIP_MINIMUM_BYTES ||\n payload[0] !== GZIP_MAGIC_FIRST ||\n payload[1] !== GZIP_MAGIC_SECOND ||\n payload[2] !== GZIP_DEFLATE_METHOD\n ) {\n fail('invalid-gzip', 'payload is not a gzip member')\n }\n const flags = payload[3]\n if ((flags & GZIP_RESERVED_FLAGS) !== 0) fail('invalid-gzip', 'gzip header uses reserved flags')\n let offset = GZIP_FIXED_HEADER_BYTES\n if ((flags & GZIP_FLAG_EXTRA) !== 0) {\n if (offset + 2 > payload.length) fail('invalid-gzip', 'gzip extra field is truncated')\n offset += 2 + (payload[offset] | (payload[offset + 1] << 8))\n if (offset > payload.length) fail('invalid-gzip', 'gzip extra field is truncated')\n }\n for (const flag of [GZIP_FLAG_NAME, GZIP_FLAG_COMMENT]) {\n if ((flags & flag) === 0) continue\n while (offset < payload.length && payload[offset] !== 0) offset += 1\n if (offset >= payload.length) fail('invalid-gzip', 'gzip header string is unterminated')\n offset += 1\n }\n if ((flags & GZIP_FLAG_HEADER_CRC) !== 0) offset += 2\n if (offset + GZIP_TRAILER_BYTES > payload.length) fail('invalid-gzip', 'gzip member is truncated')\n return offset\n}\n\n/** The inflater's post-completion state: unconsumed input bytes and the bit offset into the first of them. */\ninterface InflateProgress {\n readonly p: Uint8Array\n readonly s: { readonly p?: number }\n}\n\n/**\n * Input slice for the counting pass below. The inflater emits what each slice\n * decodes to before it accepts the next one, so a member that would expand past\n * the decoded-byte ceiling is refused after at most one slice's worth of\n * expansion instead of after the whole member has been materialized.\n */\nconst INFLATE_GUARD_SLICE_BYTES = 4096\n\n/**\n * Refuse a member whose decoded size would exceed the ceiling without holding\n * the decoded bytes. The pass only counts: every slice is pushed as non-final,\n * so a malformed member or trailing data is left for the strict pass to\n * classify, and only the decoded-byte ceiling is reported from here. The\n * inflater may defer a block whose header straddles a slice boundary, so the\n * count can trail the true size by a few hundred kilobytes; the strict pass\n * still applies the exact ceiling to what it decodes.\n */\nconst refuseOversizedMember = (body: Uint8Array, maximumDecodedBytes: number): void => {\n let counted = 0\n const guard = new Inflate((chunk) => {\n counted += chunk.length\n if (counted > maximumDecodedBytes) {\n fail('decoded-byte-limit', `decoded payload exceeds ${maximumDecodedBytes} bytes`)\n }\n })\n try {\n for (let offset = 0; offset < body.length; offset += INFLATE_GUARD_SLICE_BYTES) {\n guard.push(body.subarray(offset, Math.min(offset + INFLATE_GUARD_SLICE_BYTES, body.length)), false)\n }\n } catch (error) {\n if (error instanceof V2ReplayPayloadAdmissionError) throw error\n }\n}\n\nconst inflateSingleMember = (payload: Uint8Array, headerLength: number, maximumDecodedBytes: number): Uint8Array => {\n const body = payload.subarray(headerLength, payload.length - GZIP_TRAILER_BYTES)\n refuseOversizedMember(body, maximumDecodedBytes)\n const chunks: Uint8Array[] = []\n let produced = 0\n const inflater = new Inflate((chunk) => {\n produced += chunk.length\n if (produced > maximumDecodedBytes) {\n fail('decoded-byte-limit', `decoded payload exceeds ${maximumDecodedBytes} bytes`)\n }\n chunks.push(chunk)\n })\n try {\n inflater.push(body, true)\n } catch (error) {\n if (error instanceof V2ReplayPayloadAdmissionError) throw error\n return fail('invalid-gzip', 'gzip member does not inflate')\n }\n\n const progress = inflater as unknown as InflateProgress\n const remaining = progress.p\n const bitOffset = progress.s?.p\n if (!ArrayBuffer.isView(remaining) || typeof bitOffset !== 'number' || remaining.length > body.length) {\n return fail('invalid-gzip', 'gzip member end position is unavailable')\n }\n const consumed = body.length - remaining.length + ((bitOffset & 7) !== 0 ? 1 : 0)\n const trailerOffset = headerLength + consumed\n if (trailerOffset + GZIP_TRAILER_BYTES > payload.length) fail('invalid-gzip', 'gzip trailer is truncated')\n if (readUint32LittleEndian(payload, trailerOffset) !== crc32(chunks)) {\n fail('invalid-gzip', 'gzip CRC-32 does not match the decoded bytes')\n }\n if (readUint32LittleEndian(payload, trailerOffset + 4) !== produced % 0x1_0000_0000) {\n fail('invalid-gzip', 'gzip ISIZE does not match the decoded length')\n }\n if (trailerOffset + GZIP_TRAILER_BYTES !== payload.length) {\n fail('trailing-gzip-data', 'payload carries bytes after the single gzip member')\n }\n\n const decoded = new Uint8Array(produced)\n let offset = 0\n for (const chunk of chunks) {\n decoded.set(chunk, offset)\n offset += chunk.length\n }\n return decoded\n}\n\n/**\n * Admits one base64 payload exactly as the engine does, in this order: strict canonical base64, a\n * single gzip member with the decoded-byte ceiling, no nested compression, strict canonical JSON\n * within the nesting ceiling, and the logical-record ceiling. Any failure is permanent.\n */\nexport const admitV2BrowserDomPayload = (\n payload: string,\n limits: V2ReplayPayloadLimits = V2_REPLAY_PAYLOAD_LIMITS\n): V2AdmittedReplayPayload => {\n const compressed = decodeCanonicalBase64(payload)\n const decoded = inflateSingleMember(compressed, gzipHeaderLength(compressed), limits.maximumReplayDecodedBytes)\n if (decoded.length >= 2 && decoded[0] === GZIP_MAGIC_FIRST && decoded[1] === GZIP_MAGIC_SECOND) {\n fail('nested-compression', 'decoded payload is itself gzip-compressed')\n }\n let text: string\n try {\n text = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(decoded)\n } catch {\n return fail('invalid-unicode', 'decoded payload is not UTF-8')\n }\n const admitted = admitV2CanonicalPayloadText(text, limits)\n return Object.freeze({\n decoded,\n decodedByteLength: decoded.length,\n logicalRecords: admitted.logicalRecords,\n nestingDepth: admitted.nestingDepth,\n records: admitted.records,\n })\n}\n","import type { V2MaskingProfile } from './types'\n\n/**\n * The second redaction, applied to every recorded event before it is sealed.\n *\n * The recorder is already configured from the same profile, so in the ordinary case this finds\n * nothing left to remove. It exists because \"the recorder was configured correctly\" is an\n * assumption, and a sealed payload should not depend on one: this pass removes the categories the\n * profile forbids whatever the recorder did, so the proof describes bytes that were redacted here.\n *\n * It is a deny pass, not a verifier. It cannot tell masked text from text that happens to look\n * masked, and it does not try — it removes.\n */\n\n/** rrweb serialized node kinds. Numeric, because that is what travels on the wire. */\nconst NODE_TYPE_ELEMENT = 2\nconst NODE_TYPE_TEXT = 3\nconst NODE_TYPE_COMMENT = 5\n\n/** rrweb event kinds and mutation sources, likewise numeric on the wire. */\nconst EVENT_TYPE_FULL_SNAPSHOT = 2\nconst EVENT_TYPE_INCREMENTAL = 3\nconst INCREMENTAL_SOURCE_MUTATION = 0\nconst INCREMENTAL_SOURCE_INPUT = 5\n\n/** Input types whose value is sensitive even when the policy only masks sensitive inputs. */\nconst SENSITIVE_INPUT_TYPES = new Set(['password', 'email', 'tel', 'hidden'])\n\n/** Attributes that carry an image's content rather than its layout. */\nconst IMAGE_CONTENT_ATTRIBUTES = ['src', 'srcset']\n\nconst IMAGE_TAG_NAMES = new Set(['img', 'picture', 'source'])\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nconst maskAttributes = (\n tagName: string,\n attributes: Record<string, unknown>,\n profile: V2MaskingProfile\n): Record<string, unknown> => {\n const masked: Record<string, unknown> = { ...attributes }\n const inputType = typeof masked.type === 'string' ? masked.type.toLowerCase() : ''\n const maskValue =\n profile.inputRule === 'all' || SENSITIVE_INPUT_TYPES.has(inputType) || tagName === 'textarea'\n if (maskValue && 'value' in masked) {\n delete masked.value\n }\n if (profile.imageRule === 'block' && IMAGE_TAG_NAMES.has(tagName)) {\n for (const attribute of IMAGE_CONTENT_ATTRIBUTES) {\n if (attribute in masked) delete masked[attribute]\n }\n }\n return masked\n}\n\n/** Recursively redacts one serialized node and everything under it. */\nconst maskNode = (node: unknown, profile: V2MaskingProfile): unknown => {\n if (!isRecord(node)) return node\n const masked: Record<string, unknown> = { ...node }\n const type = masked.type\n\n if (type === NODE_TYPE_COMMENT) {\n // A comment is never replayed and can carry anything a template put there.\n masked.textContent = ''\n } else if (type === NODE_TYPE_TEXT && profile.textRule === 'all') {\n // Style text is not customer content and blanking it breaks the rendered replay.\n if (masked.isStyle !== true) masked.textContent = ''\n } else if (type === NODE_TYPE_ELEMENT) {\n const tagName = typeof masked.tagName === 'string' ? masked.tagName.toLowerCase() : ''\n if (isRecord(masked.attributes)) {\n masked.attributes = maskAttributes(tagName, masked.attributes, profile)\n }\n }\n\n if (Array.isArray(masked.childNodes)) {\n masked.childNodes = masked.childNodes.map((child) => maskNode(child, profile))\n }\n return masked\n}\n\nconst maskMutation = (data: Record<string, unknown>, profile: V2MaskingProfile): Record<string, unknown> => {\n const masked: Record<string, unknown> = { ...data }\n if (profile.textRule === 'all' && Array.isArray(masked.texts)) {\n masked.texts = masked.texts.map((text) => (isRecord(text) ? { ...text, value: '' } : text))\n }\n if (Array.isArray(masked.attributes)) {\n masked.attributes = masked.attributes.map((entry) => {\n if (!isRecord(entry) || !isRecord(entry.attributes)) return entry\n // A mutation names no tag, so the value rule is applied at its broadest here.\n return { ...entry, attributes: maskAttributes('', entry.attributes, profile) }\n })\n }\n if (Array.isArray(masked.adds)) {\n masked.adds = masked.adds.map((add) =>\n isRecord(add) ? { ...add, node: maskNode(add.node, profile) } : add\n )\n }\n return masked\n}\n\n/**\n * The masker the sealing stage runs over every event. Pure: it copies what it changes and returns\n * the input untouched when there is nothing of the forbidden kind in it.\n */\nexport const maskV2BrowserDomEvent = (event: unknown, profile: V2MaskingProfile): unknown => {\n if (!isRecord(event)) return event\n const data = event.data\n if (!isRecord(data)) return event\n\n if (event.type === EVENT_TYPE_FULL_SNAPSHOT) {\n return { ...event, data: { ...data, node: maskNode(data.node, profile) } }\n }\n if (event.type !== EVENT_TYPE_INCREMENTAL) {\n return event\n }\n if (data.source === INCREMENTAL_SOURCE_MUTATION) {\n return { ...event, data: maskMutation(data, profile) }\n }\n if (data.source === INCREMENTAL_SOURCE_INPUT) {\n // The typed value. Masked whenever inputs are, and always for a sensitive field.\n const masked: Record<string, unknown> = { ...data }\n if (profile.inputRule === 'all' && typeof masked.text === 'string') masked.text = ''\n return { ...event, data: masked }\n }\n return event\n}\n","import { V2_REPLAY_REQUEST_ID_PATTERN } from './storage-constants'\nimport { compareV1ExactTimestamps, parseV1ExactTimestamp } from '../exact-timestamp'\nimport {\n freezeV1Value,\n hasOnlyKeys,\n hasOwn,\n isBooleanValue,\n isNonNegativeInteger,\n isNullValue,\n isRecordValue,\n isStringValue,\n unicodeCodePointLength,\n} from '../validation'\nimport { canonicalV2JsonValue } from './canonical-payload-json'\nimport type { V2EncodedReplayPayload } from './codec'\nimport { isV2Sha256Hash, sha256Hex, utf8Encode } from './digest'\nimport { V2ReplayRequestError } from './errors'\nimport { isV2MaskingProof } from './masking-proof'\nimport type {\n V2ReplayChunk,\n V2ReplayIdentity,\n V2ReplayPrivacyProof,\n V2ReplayRequest,\n V2ReplaySha256,\n V2Sha256Hash,\n V2VersionContext,\n} from './types'\n\n/** Domain separator hashed ahead of the canonical chunk to derive a replay request identity. */\nexport const V2_REPLAY_REQUEST_ID_DOMAIN = 'elu-sdk-replay-request-v2'\nexport const V2_REPLAY_CONTRACT_VERSION = '2.0.0'\n/** Hard ceiling on the exact UTF-8 bytes of one complete replay request. */\nexport const V2_MAX_REPLAY_REQUEST_BYTES = 5_242_880\n\nexport { V2_REPLAY_REQUEST_ID_PATTERN } from './storage-constants'\nconst CODEC_PATTERN = /^elu-[a-z0-9][a-z0-9.-]{0,63}$/\nconst RUNTIME_NAME_PATTERN = /^elu-[a-z0-9-]+$/\nconst FACADE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9._-]+$/\nconst BASE64_PATTERN = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/\nconst CHUNK_KEYS = [\n 'schemaVersion',\n 'replayId',\n 'sessionId',\n 'chunkId',\n 'sequence',\n 'startedAt',\n 'endedAt',\n 'identity',\n 'contextRevision',\n 'codec',\n 'compression',\n 'contentEncoding',\n 'payload',\n 'privacy',\n 'versions',\n] as const\nconst IDENTITY_KEYS = ['anonymousId', 'userId', 'revision'] as const\nconst PRIVACY_KEYS = [\n 'policyRevision',\n 'effectivePolicyHash',\n 'maskingProfileHash',\n 'appliedBeforeSerialization',\n 'secureInputsMasked',\n 'platformFallbackApplied',\n] as const\nconst VERSIONS_KEYS = ['schemaVersion', 'contractVersion', 'platform', 'runtime', 'facade', 'build'] as const\nconst NAMED_VERSION_KEYS = ['name', 'version'] as const\nconst REQUEST_KEYS = ['schemaVersion', 'requestId', 'chunk'] as const\n\nfunction fail(message: string): never {\n throw new V2ReplayRequestError(message)\n}\n\nconst assertBoundedString = (value: unknown, field: string, maximum: number, minimum = 1): string => {\n const length = isStringValue(value) ? unicodeCodePointLength(value) : null\n if (isNullValue(length) || length < minimum || length > maximum) {\n return fail(`${field} must be a string of ${minimum} to ${maximum} Unicode scalars`)\n }\n return value as string\n}\n\nconst assertSafeCount = (value: unknown, field: string): number => {\n if (!isNonNegativeInteger(value)) fail(`${field} must be a safe non-negative integer`)\n return value as number\n}\n\nconst assertHash = (value: unknown, field: string): V2Sha256Hash => {\n if (!isV2Sha256Hash(value)) fail(`${field} must be a lowercase sha256 hash`)\n return value as V2Sha256Hash\n}\n\nconst assertKeys = (value: unknown, keys: readonly string[], field: string, optional: readonly string[] = []) => {\n if (!isRecordValue(value) || !hasOnlyKeys(value, keys)) fail(`${field} must contain only schema-defined fields`)\n for (const key of keys) {\n if (!optional.includes(key) && !hasOwn(value, key)) fail(`${field} ${key} is required`)\n }\n return value\n}\n\nconst validateIdentity = (value: unknown, field: string): V2ReplayIdentity => {\n const identity = assertKeys(value, IDENTITY_KEYS, field)\n return freezeV1Value({\n anonymousId: assertBoundedString(identity.anonymousId, `${field} anonymousId`, 256),\n userId: isNullValue(identity.userId) ? null : assertBoundedString(identity.userId, `${field} userId`, 512),\n revision: assertSafeCount(identity.revision, `${field} revision`),\n })\n}\n\nconst validatePrivacy = (value: unknown, field: string): V2ReplayPrivacyProof => {\n const privacy = assertKeys(value, PRIVACY_KEYS, field)\n if (privacy.appliedBeforeSerialization !== true) fail(`${field} appliedBeforeSerialization must be true`)\n if (privacy.secureInputsMasked !== true) fail(`${field} secureInputsMasked must be true`)\n if (!isBooleanValue(privacy.platformFallbackApplied)) fail(`${field} platformFallbackApplied must be a boolean`)\n return freezeV1Value({\n policyRevision: assertBoundedString(privacy.policyRevision, `${field} policyRevision`, 128),\n effectivePolicyHash: assertHash(privacy.effectivePolicyHash, `${field} effectivePolicyHash`),\n maskingProfileHash: assertHash(privacy.maskingProfileHash, `${field} maskingProfileHash`),\n appliedBeforeSerialization: true,\n secureInputsMasked: true,\n platformFallbackApplied: privacy.platformFallbackApplied,\n })\n}\n\nconst validateNamedVersion = (value: unknown, field: string, namePattern: RegExp) => {\n const named = assertKeys(value, NAMED_VERSION_KEYS, field)\n const name = assertBoundedString(named.name, `${field} name`, 256)\n if (!namePattern.test(name)) fail(`${field} name is invalid`)\n return freezeV1Value({ name, version: assertBoundedString(named.version, `${field} version`, 64) })\n}\n\nexport const validateV2VersionContext = (value: unknown, field = 'replay versions'): V2VersionContext => {\n const versions = assertKeys(value, VERSIONS_KEYS, field, ['build'])\n if (versions.schemaVersion !== 2) fail(`${field} schemaVersion must be 2`)\n if (versions.contractVersion !== V2_REPLAY_CONTRACT_VERSION) {\n fail(`${field} contractVersion must be ${V2_REPLAY_CONTRACT_VERSION}`)\n }\n if (versions.platform !== 'browser' && versions.platform !== 'android' && versions.platform !== 'ios') {\n fail(`${field} platform is unsupported`)\n }\n const runtime = validateNamedVersion(versions.runtime, `${field} runtime`, RUNTIME_NAME_PATTERN)\n const facade = validateNamedVersion(versions.facade, `${field} facade`, FACADE_NAME_PATTERN)\n return freezeV1Value({\n schemaVersion: 2,\n contractVersion: V2_REPLAY_CONTRACT_VERSION,\n platform: versions.platform,\n runtime: runtime as V2VersionContext['runtime'],\n facade,\n ...(hasOwn(versions, 'build') ? { build: assertBoundedString(versions.build, `${field} build`, 128) } : {}),\n })\n}\n\n/** Validates a complete replay chunk envelope against the frozen v2 schema and returns a frozen copy. */\nexport const validateV2ReplayChunk = (value: unknown, field = 'replay chunk'): V2ReplayChunk => {\n const chunk = assertKeys(value, CHUNK_KEYS, field)\n if (chunk.schemaVersion !== 2) fail(`${field} schemaVersion must be 2`)\n const startedAt = assertBoundedString(chunk.startedAt, `${field} startedAt`, 64)\n const endedAt = assertBoundedString(chunk.endedAt, `${field} endedAt`, 64)\n const started = parseV1ExactTimestamp(startedAt, `${field} startedAt`, 64)\n const ended = parseV1ExactTimestamp(endedAt, `${field} endedAt`, 64)\n if (compareV1ExactTimestamps(started, ended) > 0) fail(`${field} endedAt must not precede startedAt`)\n const codec = assertBoundedString(chunk.codec, `${field} codec`, 68)\n if (!CODEC_PATTERN.test(codec)) fail(`${field} codec is invalid`)\n if (chunk.compression !== 'none' && chunk.compression !== 'gzip') fail(`${field} compression is unsupported`)\n if (chunk.contentEncoding !== 'base64') fail(`${field} contentEncoding must be base64`)\n if (!isStringValue(chunk.payload) || chunk.payload.length === 0 || !BASE64_PATTERN.test(chunk.payload)) {\n fail(`${field} payload must be non-empty canonical base64`)\n }\n return freezeV1Value({\n schemaVersion: 2,\n replayId: assertBoundedString(chunk.replayId, `${field} replayId`, 256),\n sessionId: assertBoundedString(chunk.sessionId, `${field} sessionId`, 256),\n chunkId: assertBoundedString(chunk.chunkId, `${field} chunkId`, 256),\n sequence: assertSafeCount(chunk.sequence, `${field} sequence`),\n startedAt,\n endedAt,\n identity: validateIdentity(chunk.identity, `${field} identity`),\n contextRevision: assertSafeCount(chunk.contextRevision, `${field} contextRevision`),\n codec: codec as `elu-${string}`,\n compression: chunk.compression,\n contentEncoding: 'base64',\n payload: chunk.payload,\n privacy: validatePrivacy(chunk.privacy, `${field} privacy`),\n versions: validateV2VersionContext(chunk.versions, `${field} versions`),\n })\n}\n\nexport interface V2ReplayChunkInput {\n readonly replayId: string\n readonly sessionId: string\n readonly chunkId: string\n readonly sequence: number\n readonly startedAt: string\n readonly endedAt: string\n readonly identity: V2ReplayIdentity\n readonly contextRevision: number\n readonly payload: V2EncodedReplayPayload\n readonly policyRevision: string\n readonly effectivePolicyHash: V2Sha256Hash\n readonly versions: V2VersionContext\n}\n\n/**\n * Builds the outer envelope around an encoded payload. The privacy proof constants and the masking\n * profile hash come from the sealed proof inside the payload; callers cannot supply them.\n */\nexport const buildV2ReplayChunk = (input: V2ReplayChunkInput): V2ReplayChunk => {\n if (!isRecordValue(input) || !isRecordValue(input.payload) || !isV2MaskingProof(input.payload.proof)) {\n fail('replay chunk requires a payload encoded from a sealed masking proof')\n }\n const { payload } = input\n return validateV2ReplayChunk({\n schemaVersion: 2,\n replayId: input.replayId,\n sessionId: input.sessionId,\n chunkId: input.chunkId,\n sequence: input.sequence,\n startedAt: input.startedAt,\n endedAt: input.endedAt,\n identity: input.identity,\n contextRevision: input.contextRevision,\n codec: payload.codec,\n compression: payload.compression,\n contentEncoding: payload.contentEncoding,\n payload: payload.payload,\n privacy: {\n policyRevision: input.policyRevision,\n effectivePolicyHash: input.effectivePolicyHash,\n maskingProfileHash: payload.proof.profileHash,\n appliedBeforeSerialization: true,\n secureInputsMasked: true,\n platformFallbackApplied: payload.proof.profile.platformFallbackApplied,\n },\n versions: input.versions,\n })\n}\n\nexport const validateV2ReplayRequest = (value: unknown, field = 'replay request'): V2ReplayRequest => {\n const request = assertKeys(value, REQUEST_KEYS, field)\n if (request.schemaVersion !== 2) fail(`${field} schemaVersion must be 2`)\n if (!isStringValue(request.requestId) || !V2_REPLAY_REQUEST_ID_PATTERN.test(request.requestId)) {\n fail(`${field} requestId must be request_ followed by a lowercase sha256 digest`)\n }\n return freezeV1Value({\n schemaVersion: 2,\n requestId: request.requestId,\n chunk: validateV2ReplayChunk(request.chunk, `${field} chunk`),\n })\n}\n\n/** Strict canonical UTF-8 bytes of the complete chunk envelope. */\nexport const canonicalV2ReplayChunkBytes = (chunk: V2ReplayChunk): Uint8Array =>\n utf8Encode(canonicalV2JsonValue(validateV2ReplayChunk(chunk), 'replay chunk'))\n\n/** `UTF8(domain) || 0x00 || uint32be(len(canonicalChunk)) || canonicalChunk` */\nexport const v2ReplayRequestIdentityMaterial = (canonicalChunk: Uint8Array): Uint8Array => {\n const domain = utf8Encode(V2_REPLAY_REQUEST_ID_DOMAIN)\n const material = new Uint8Array(domain.length + 1 + 4 + canonicalChunk.length)\n material.set(domain, 0)\n material[domain.length] = 0\n const length = canonicalChunk.length\n material[domain.length + 1] = (length >>> 24) & 0xff\n material[domain.length + 2] = (length >>> 16) & 0xff\n material[domain.length + 3] = (length >>> 8) & 0xff\n material[domain.length + 4] = length & 0xff\n material.set(canonicalChunk, domain.length + 5)\n return material\n}\n\nexport interface V2ReplayRequestIdentity {\n readonly requestId: string\n readonly canonicalChunk: Uint8Array\n}\n\nexport const deriveV2ReplayRequestId = async (\n chunk: V2ReplayChunk,\n sha256?: V2ReplaySha256\n): Promise<V2ReplayRequestIdentity> => {\n const canonicalChunk = canonicalV2ReplayChunkBytes(chunk)\n const digest = await sha256Hex(v2ReplayRequestIdentityMaterial(canonicalChunk), sha256)\n return freezeV1Value({ requestId: `request_${digest}`, canonicalChunk })\n}\n\nexport interface V2PreparedReplayRequest {\n /** Local capture witness; deliberately excluded from request bytes and request identity. */\n readonly captureProtocolGeneration: string\n readonly request: V2ReplayRequest\n /** The exact bytes every attempt sends; they are persisted and never regenerated on retry. */\n readonly canonicalRequest: Uint8Array\n readonly canonicalRequestSha256: V2Sha256Hash\n readonly byteLength: number\n}\n\nexport interface V2ReplayRequestOptions {\n readonly captureProtocolGeneration: string\n readonly sha256?: V2ReplaySha256\n /** `limits.replayChunkBytes` from the current config; the hard ceiling applies regardless. */\n readonly replayChunkBytes?: number\n}\n\n/** `min(config.replayChunkBytes, 5242880)`; a missing config limit leaves only the hard ceiling. */\nexport const effectiveV2ReplayRequestLimit = (replayChunkBytes?: number): number => {\n if (typeof replayChunkBytes === 'undefined') return V2_MAX_REPLAY_REQUEST_BYTES\n if (!isNonNegativeInteger(replayChunkBytes) || replayChunkBytes < 1) {\n return fail('replayChunkBytes must be a positive safe integer')\n }\n return Math.min(replayChunkBytes, V2_MAX_REPLAY_REQUEST_BYTES)\n}\n\n/** Derives the request identity, canonicalizes the complete request, and applies the exact byte limit. */\nexport const prepareV2ReplayRequest = async (\n chunk: V2ReplayChunk,\n options: V2ReplayRequestOptions\n): Promise<V2PreparedReplayRequest> => {\n if (!isRecordValue(options)) fail('replay request options must include captureProtocolGeneration')\n const captureProtocolGeneration = assertBoundedString(\n options.captureProtocolGeneration,\n 'captureProtocolGeneration',\n 128\n )\n const limit = effectiveV2ReplayRequestLimit(options.replayChunkBytes)\n const validated = validateV2ReplayChunk(chunk)\n const identity = await deriveV2ReplayRequestId(validated, options.sha256)\n const request: V2ReplayRequest = freezeV1Value({\n schemaVersion: 2,\n requestId: identity.requestId,\n chunk: validated,\n })\n const canonicalRequest = utf8Encode(canonicalV2JsonValue(request, 'replay request'))\n if (canonicalRequest.length > limit) {\n fail(`replay request is ${canonicalRequest.length} bytes; the effective limit is ${limit}`)\n }\n const digest = await sha256Hex(canonicalRequest, options.sha256)\n return freezeV1Value({\n captureProtocolGeneration,\n request,\n canonicalRequest,\n canonicalRequestSha256: `sha256:${digest}` as V2Sha256Hash,\n byteLength: canonicalRequest.length,\n })\n}\n","import { encodeV2BrowserDomPayload } from './codec'\nimport { maskV2BrowserDomEvent } from './dom-masker'\nimport { V2MaskingProof } from './masking-proof'\nimport { buildV2ReplayChunk, prepareV2ReplayRequest } from './request'\nimport type { V2MaskingProfile, V2ReplayIdentity, V2ReplaySha256, V2Sha256Hash, V2VersionContext } from './types'\nimport type { V2PreparedReplayRequest } from './request'\n\n/**\n * One recorded stretch of a session, on its way to the queue.\n *\n * This is the whole path from events to a request the store will accept, in one place and in one\n * order: mask, seal, encode, envelope, prepare. Splitting it would let a caller seal a proof over\n * events this module never masked, which is the one sequence the proof exists to rule out.\n */\nexport interface V2SealReplayChunkInput {\n /** Events as the recorder produced them, in order. */\n readonly events: readonly unknown[]\n readonly profile: V2MaskingProfile\n readonly profileHash: V2Sha256Hash\n readonly replayId: string\n readonly sessionId: string\n readonly chunkId: string\n readonly sequence: number\n readonly startedAt: string\n readonly endedAt: string\n readonly identity: V2ReplayIdentity\n readonly contextRevision: number\n readonly captureProtocolGeneration: string\n readonly policyRevision: string\n readonly effectivePolicyHash: V2Sha256Hash\n readonly versions: V2VersionContext\n /** The document's replay chunk ceiling; a chunk over it is refused rather than trimmed. */\n readonly replayChunkBytes?: number\n readonly sha256?: V2ReplaySha256\n}\n\n/**\n * Masks, seals, encodes and prepares one chunk.\n *\n * The masking pass runs here rather than at the call site so a proof can only ever cover events this\n * function masked. `V2MaskingProof.seal` applies the masker to every event itself, so passing the\n * masker rather than pre-masked events is what makes the proof's claim true by construction.\n */\nexport const sealV2ReplayChunk = async (input: V2SealReplayChunkInput): Promise<V2PreparedReplayRequest> => {\n const proof = V2MaskingProof.seal(maskV2BrowserDomEvent, {\n profile: input.profile,\n profileHash: input.profileHash,\n events: input.events,\n })\n const payload = encodeV2BrowserDomPayload(proof)\n const chunk = buildV2ReplayChunk({\n replayId: input.replayId,\n sessionId: input.sessionId,\n chunkId: input.chunkId,\n sequence: input.sequence,\n startedAt: input.startedAt,\n endedAt: input.endedAt,\n identity: input.identity,\n contextRevision: input.contextRevision,\n payload,\n policyRevision: input.policyRevision,\n effectivePolicyHash: input.effectivePolicyHash,\n versions: input.versions,\n })\n return prepareV2ReplayRequest(chunk, {\n captureProtocolGeneration: input.captureProtocolGeneration,\n ...(input.replayChunkBytes === undefined ? {} : { replayChunkBytes: input.replayChunkBytes }),\n ...(input.sha256 === undefined ? {} : { sha256: input.sha256 }),\n })\n}\n","/* eslint-disable compat/compat -- The internal replay driver requires native Promise support. */\n\nimport { advanceV2ReplaySchedule, initialV2ReplayScheduleState } from './schedule'\nimport { V1_MAX_DURABLE_QUEUE_BYTES, V1_MAX_DURABLE_QUEUE_COUNT } from '../durable-state'\nimport { isNullValue } from '../validation'\nimport type { V2ReplaySchedulePolicy, V2ReplayScheduleState } from './schedule'\nimport { sealV2ReplayChunk } from './seal-chunk'\nimport type { V2SealReplayChunkInput } from './seal-chunk'\nimport type { V2PreparedReplayRequest } from './request'\n\n/**\n * Turns a stream of recorded events into sealed chunks.\n *\n * The recorder is injected rather than imported. That is not only for tests: importing it here would\n * pull the whole recorder into the served bundle, which is why the inherited core loads its own\n * lazily. Whoever supplies one decides how it arrives.\n *\n * Nothing here starts recording on its own, and nothing here delivers. It buffers what it is given,\n * asks the schedule where the boundaries fall, and hands sealed chunks to a sink.\n */\n\n/** What the driver needs to name and stamp a chunk. Supplied per chunk, not held. */\nexport interface V2ReplayChunkIdentitySource {\n readonly replayId: () => string\n readonly chunkId: () => string\n readonly instant: (atMs: number) => string\n}\n\nexport type V2SealedChunkSink = (\n prepared: V2PreparedReplayRequest,\n context: { readonly discarded: boolean; readonly sequence: number }\n) => Promise<void>\n\nexport interface V2ReplaySessionDriverOptions {\n readonly policy: V2ReplaySchedulePolicy\n /** Separate memory ceiling, not a reservation of the durable event/replay queue. */\n readonly maximumBufferedBytes: number\n readonly identity: V2ReplayChunkIdentitySource\n /**\n * Everything the sealing stage needs that does not change between chunks. The chunk ceiling is\n * deliberately not among them: it comes from the schedule, so one document cannot schedule to\n * one size and seal against another.\n */\n readonly seal: Omit<\n V2SealReplayChunkInput,\n 'events' | 'replayId' | 'sessionId' | 'chunkId' | 'sequence' | 'startedAt' | 'endedAt' | 'replayChunkBytes'\n >\n readonly sessionId: string\n readonly sink: V2SealedChunkSink\n /** A terminal failure discards the driver; the owner must stop its recorder. */\n readonly onSealFailure?: (error: unknown, terminal: boolean) => void\n /** Original stream's sealing/sink has settled; no later suffix may use this replay identity. */\n readonly onReplayClosed?: (replayId: string) => Promise<void>\n}\n\ninterface OpenChunk {\n readonly events: unknown[]\n readonly startedAtMs: number\n endedAtMs: number\n estimatedBytes: number\n}\n\nexport interface V2ReplaySessionDriver {\n /** Offers one recorded event. Sealing happens when a boundary says so. */\n record(event: unknown, atMs: number, byteLength: number): Promise<void>\n /** Seals what is open, retaining an ineligible prefix until real activity qualifies it. */\n flush(): Promise<void>\n /** Includes held prefixes and work queued behind asynchronous sealing. */\n hasBufferedWork(): boolean\n /**\n * Immediately discards buffered/queued work and prevents any further sink calls. A sink call\n * already in progress is awaited; its persisted rows need the owner's separate purge barrier.\n * Rebinding identity or privacy requires a new driver after that barrier completes.\n */\n close(): Promise<void>\n}\n\nexport const createV2ReplaySessionDriver = (options: V2ReplaySessionDriverOptions): V2ReplaySessionDriver => {\n if (!Number.isSafeInteger(options.maximumBufferedBytes) || options.maximumBufferedBytes < 1) {\n throw new Error('replay buffer ceiling must be a positive safe integer')\n }\n const byteLimit = Math.min(options.maximumBufferedBytes, V1_MAX_DURABLE_QUEUE_BYTES)\n let state: V2ReplayScheduleState = initialV2ReplayScheduleState()\n let open: OpenChunk | null = null\n let closed = false\n let replayId = options.identity.replayId()\n let closedReplay: Promise<void> | null = null\n let closePromise: Promise<void> | null = null\n const closeReplay = (): Promise<void> => {\n if (!closedReplay) {\n const originalReplayId = replayId\n closedReplay = Promise.resolve().then(() => options.onReplayClosed?.(originalReplayId))\n }\n return closedReplay\n }\n let qualified = options.policy.minimumDurationSeconds === 0\n let emitted = false\n let held: Array<{ prepared: V2PreparedReplayRequest; sequence: number }> = []\n let heldBytes = 0\n // Recorder-supplied size is an ingress estimate; prepared bytes below are exact.\n // Counting pending/open events also bounds closures retained behind slow hashing.\n let ingressBytes = 0\n let ingressCount = 0\n // Chunk work is serialized: a boundary must finish sealing what it closed before the next\n // event can open anything, or two chunks could claim one sequence.\n let tail: Promise<void> = Promise.resolve()\n\n const report = (error: unknown, terminal = false): void => {\n try {\n options.onSealFailure?.(error ?? new Error('replay driver failed without an error value'), terminal)\n } catch {\n // A diagnostic observer cannot strand queued work.\n }\n }\n const discardHeld = (): void => {\n held = []\n heldBytes = 0\n }\n const stop = (): void => {\n closed = true\n open = null\n discardHeld()\n ingressBytes = 0\n ingressCount = 0\n }\n const fail = (error: unknown): void => {\n stop()\n report(error, true)\n }\n const drainHeld = async (): Promise<void> => {\n while (!closed && qualified && held.length > 0) {\n const next = held.shift()!\n heldBytes -= next.prepared.byteLength\n try {\n await options.sink(next.prepared, { discarded: false, sequence: next.sequence })\n emitted = true\n } catch (error) {\n // Never release later deltas after losing the held prefix's baseline.\n fail(error)\n }\n }\n }\n\n const sealOpen = async (discarded: boolean, sequence: number): Promise<void> => {\n const closing = open\n open = null\n if (closed || !closing || closing.events.length === 0) return\n if (discarded) {\n ingressBytes -= closing.estimatedBytes\n ingressCount -= closing.events.length\n return\n }\n try {\n const prepared = await sealV2ReplayChunk({\n ...options.seal,\n // One ceiling, the schedule's, so a chunk is sealed against the size it was cut to.\n replayChunkBytes: options.policy.replayChunkBytes,\n events: closing.events,\n replayId,\n sessionId: options.sessionId,\n chunkId: options.identity.chunkId(),\n sequence,\n startedAt: options.identity.instant(closing.startedAtMs),\n endedAt: options.identity.instant(closing.endedAtMs),\n })\n // Closing while hashing/sealing must not hand stale identity or privacy to the sink.\n if (!closed && !discarded) {\n if (!qualified) {\n if (prepared.byteLength > byteLimit - heldBytes || held.length >= V1_MAX_DURABLE_QUEUE_COUNT) {\n fail(new Error('replay minimum-duration buffer capacity exceeded'))\n } else {\n held.push({ prepared, sequence })\n heldBytes += prepared.byteLength\n }\n } else {\n await options.sink(prepared, { discarded: false, sequence })\n emitted = true\n }\n }\n } catch (error) {\n // A chunk that cannot be sealed is lost, and the session goes on. Failing the whole\n // recorder over one unsendable stretch would lose the rest of the session too.\n if (!closed) {\n if (!qualified || (options.policy.minimumDurationSeconds > 0 && !emitted)) fail(error)\n else report(error)\n }\n } finally {\n ingressBytes = Math.max(0, ingressBytes - closing.estimatedBytes)\n ingressCount = Math.max(0, ingressCount - closing.events.length)\n }\n }\n\n const append = (event: unknown, atMs: number, byteLength: number): void => {\n if (open) {\n open.events.push(event)\n open.endedAtMs = atMs\n open.estimatedBytes += byteLength\n return\n }\n open = { events: [event], startedAtMs: atMs, endedAtMs: atMs, estimatedBytes: byteLength }\n }\n\n return {\n record(event: unknown, atMs: number, byteLength: number): Promise<void> {\n if (closed) return Promise.resolve()\n if (\n !Number.isFinite(atMs) ||\n atMs < 0 ||\n (!isNullValue(state.lastActivityAtMs) && atMs < state.lastActivityAtMs) ||\n !Number.isSafeInteger(byteLength) ||\n byteLength < 1\n ) {\n fail(new Error('invalid replay ingress observation'))\n return tail\n }\n if (byteLength > byteLimit - ingressBytes || ingressCount >= V1_MAX_DURABLE_QUEUE_COUNT) {\n fail(new Error('replay ingress buffer capacity exceeded'))\n return tail\n }\n ingressBytes += byteLength\n ingressCount += 1\n const decision = advanceV2ReplaySchedule(state, { atMs, byteLength }, options.policy)\n const sealedSequence = state.sequence\n state = decision.state\n tail = tail.then(async () => {\n if (closed) return\n if (decision.action !== 'buffer') {\n await sealOpen(decision.discardSealedChunk, sealedSequence)\n if (closed) return\n // A session boundary starts a new replay; a chunk boundary stays in this one.\n if (decision.action === 'cut-session') {\n try {\n await closeReplay()\n } catch (error) {\n fail(error)\n throw error\n }\n if (closed) return\n discardHeld()\n qualified = options.policy.minimumDurationSeconds === 0\n emitted = false\n replayId = options.identity.replayId()\n closedReplay = null\n }\n }\n append(event, atMs, byteLength)\n // Use this queued event's immutable decision, never the globally advanced state.\n if (\n !qualified &&\n !isNullValue(decision.state.sessionStartedAtMs) &&\n atMs - decision.state.sessionStartedAtMs >= options.policy.minimumDurationSeconds * 1000\n ) {\n qualified = true\n await drainHeld()\n }\n })\n return tail\n },\n flush(): Promise<void> {\n if (closed) return Promise.resolve()\n const sequence = state.sequence\n if (state.chunkEvents > 0) {\n // Reserve the next sequence at invocation, like record(), so events queued after\n // this flush cannot reuse the identity of the chunk it is about to seal.\n state = Object.freeze({\n ...state,\n chunkStartedAtMs: null,\n chunkBytes: 0,\n chunkEvents: 0,\n sequence: state.sequence + 1,\n })\n }\n tail = tail.then(() => sealOpen(false, sequence))\n return tail\n },\n hasBufferedWork(): boolean {\n return !closed && (ingressCount > 0 || held.length > 0 || !isNullValue(open))\n },\n close(): Promise<void> {\n // This barrier is synchronous even when prior digest or sink work is still pending.\n stop()\n if (!closePromise) closePromise = tail.then(closeReplay, async (error) => {\n await closeReplay()\n throw error\n })\n return closePromise\n },\n }\n}\n"],"names":["values","unpackV1DeflatedTextData","encoded","expectedBytes","expectedCount","invalid","Error","Number","isSafeInteger","length","padding","endsWith","bytes","Uint8Array","byteCount","bits","value","index","digit","indexOf","charAt","fixedLiterals","fixedDistances","output","written","position","read","count","offset","result","tree","lengths","single","empty","counts","Uint16Array","maximum","Math","max","remaining","offsets","symbols","symbol","codes","code","first","final","blocks","kind","set","subarray","literals","distances","distanceLengths","literalCount","distanceCount","codeCount","order","codeLengths","codeTree","repeated","end","next","extra","distanceSymbol","distanceExtra","distance","ceil","decoded","String","fromCharCode","apply","min","texts","JSON","parse","Array","isArray","some","text","Object","freeze","readV1SharedText","v1MemberKeys","v1MemberKey0","v1MemberKey2","v1MemberKey3","v1MemberKey7","v1MemberKey8","v1MemberKey22","v1MemberKey23","v1MemberKey34","v1MemberKey37","v1MemberKey38","v1MemberKey45","v1MemberKey48","v1MemberKey52","v1MemberKey53","v1MemberKey60","v1MemberKey65","v1MemberKey83","v1MemberKey85","v1MemberKey86","v1MemberKey91","v1MemberKey95","v1MemberKey111","v1MemberKey113","v1MemberKey118","v1MemberKey132","v1MemberKey133","v1MemberKey134","v1MemberKey135","v1MemberKey136","v1MemberKey139","v1MemberKey147","v1MemberKey151","v1MemberKey171","v1MemberKey180","v1MemberKey183","v1MemberKey195","v1MemberKey196","v1MemberKey203","v1MemberKey212","v1MemberKey215","v1MemberKey216","v1MemberKey217","v1MemberKey220","v1MemberKey221","v1MemberKey225","v1MemberKey228","v1MemberKey230","v1MemberKey232","v1MemberKey233","v1MemberKey236","v1MemberKey238","v1MemberKey244","v1MemberKey252","v1MemberKey253","v1MemberKey262","v1MemberKey269","v1MemberKey272","v1MemberKey275","v1MemberKey289","v1MemberKey305","v1MemberKey308","v1MemberKey317","v1MemberKey323","v1MemberKey324","v1MemberKey334","v1MemberKey348","v1MemberKey351","isArrayValue","isRecordValue","isStringValue","isBooleanValue","isFunctionValue","unicodeCodePointLength","isNumberValue","isNonNegativeInteger","isNullValue","is","hasOwn","key","prototype","call","hasOwnIndex","hasOnlyKeys","allowedKeys","allowed","Set","keys","every","has","freezeV1Value","openChunk","state","atMs","sequence","chunkStartedAtMs","chunkBytes","chunkEvents","V1_SHARED_LITERAL_020","V1_SHARED_LITERAL_024","INGEST_ORIGIN","assets","V1_ENDPOINT_PATHS","V2_ENDPOINT_PATHS","RFC3339_PATTERN","fail","field","message","parseV1ExactTimestamp","maximumUnicodeScalars","_match$","scalarLength","match","exec","year","month","day","hour","minute","second","fractionalDigits","replace","offsetHour","offsetMinute","daysInMonth","date","Date","getUTCDate","local","setUTCHours","epochSecond","getTime","leapSecond","nextUtcSecond","getUTCHours","getUTCMinutes","getUTCSeconds","getUTCMonth","split","epochDay","floor","splitEpochSecond","_extends","source","V2_REPLAY_SEQUENCE_INDEX","RECORDS_STORE","base","name","keyPath","indexes","unique","SHA256_HEX_PATTERN","defaultV2ReplaySha256","_ref","_asyncToGenerator","cryptoApi","globalThis","undefined","crypto","subtle","digest","hexadecimal","byte","toString","padStart","_unused","_x","this","arguments","sha256Hex","_ref2","sha256","test","_x2","_x3","isV2Sha256Hash","utf8Encode","TextEncoder","encode","wallNow","monotonicNowMs","clock","performance","now","NaN","u8","u16","u32","Uint32Array","fleb","fdeb","clim","freb","eb","start","b","i","r","j","_a","fl","revfl","revfd","rev","x","hMap","cd","mb","s","l","co","le","rvb","sv","r_1","v","m","flt","fdt","flm","fdm","shft","p","wbits","d","o","wbits16","hTree","t","push","f","t2","slice","sort","a","i0","i1","i2","maxSym","tr","mbt","ln","dt","lft","cst","i2_1","i2_2","i2_3","n","lc","c","cl","cli","cln","cls","w","clen","cf","wfblk","out","pos","dat","wblk","syms","lf","df","li","bs","bl","dlt","mlb","_b","ddt","mdb","_c","lclt","nlc","_d","lcdt","ndc","lcfreq","_e","lct","mlcb","nlcc","lm","ll","dm","dl","flen","ftlen","dtlen","llm","lcts","it","clct","len","dst","deo","crct","k","wbytes","gzipSync","data","opts","cr","crc","opt","pre","filename","lvl","plvl","post","lst","e","msk_1","prev","head","bs1_1","bs2_1","hsh","lc_1","wi","hv","imod","pimod","rem","ch_1","dif","maxn","maxd","ml","nl","mmd","md","ti","lin","din","slc","dflt","level","mem","log","fn","mtime","charCodeAt","gzh","V2ReplayPayloadAdmissionError","constructor","super","V2MaskingProfileError","V2MaskingProofError","V2ReplayRequestError","ALPHABET","decodeTable","Int16Array","fill","encodeBase64","triple","V2_REPLAY_PAYLOAD_LIMITS","maximumReplayDecodedBytes","maximumJsonNestingDepth","assertUnicode","path","isOmittedValue","walk","depth","maximumNestingDepth","containerDepth","parts","element","join","isPlainObject","record","members","member","canonicalV2JsonValue","V2_BROWSER_MASKING_DIALECT","PROFILE_KEYS","TEXT_RULES","INPUT_RULES","IMAGE_RULES","assertRule","includes","assertSelector","normalizeSelectors","selectors","add","size","SEAL","Symbol","V2MaskingProof","seal","profile","profileHash","canonicalPayload","payloadByteLength","logicalRecords","nestingDepth","masker","input","_input$v1MemberKey","textRule","inputRule","imageRule","buildV2MaskingProfile","canonical","records","limits","byteLength","utf8ByteLength","canonicalV2ReplayRecords","map","event","isV2MaskingProof","table","Int32Array","bit","SENSITIVE_INPUT_TYPES","IMAGE_CONTENT_ATTRIBUTES","IMAGE_TAG_NAMES","isRecord","maskAttributes","tagName","attributes","masked","inputType","type","attribute","maskNode","node","textContent","isStyle","childNodes","child","maskMutation","entry","adds","maskV2BrowserDomEvent","V2_REPLAY_CONTRACT_VERSION","V2_MAX_REPLAY_REQUEST_BYTES","CODEC_PATTERN","RUNTIME_NAME_PATTERN","FACADE_NAME_PATTERN","BASE64_PATTERN","CHUNK_KEYS","IDENTITY_KEYS","PRIVACY_KEYS","VERSIONS_KEYS","NAMED_VERSION_KEYS","assertBoundedString","minimum","assertSafeCount","assertHash","assertKeys","optional","validateIdentity","identity","validatePrivacy","privacy","appliedBeforeSerialization","validateNamedVersion","namePattern","named","validateV2VersionContext","versions","runtime","facade","build","validateV2ReplayChunk","chunk","startedAt","endedAt","compareV1ExactTimestamps","left","right","maximumDigits","leftDigit","rightDigit","codec","contentEncoding","payload","deriveV2ReplayRequestId","canonicalChunk","canonicalV2ReplayChunkBytes","domain","material","v2ReplayRequestIdentityMaterial","prepareV2ReplayRequest","options","replayChunkBytes","captureProtocolGeneration","limit","validated","request","canonicalRequest","canonicalRequestSha256","_x4","sealV2ReplayChunk","proof","compressed","compressedByteLength","decodedByteLength","encodeV2BrowserDomPayload","buildV2ReplayChunk","createV2ReplaySessionDriver","byteLimit","open","closed","replayId","closedReplay","closePromise","closeReplay","originalReplayId","Promise","resolve","then","onReplayClosed","qualified","policy","emitted","held","heldBytes","ingressBytes","ingressCount","tail","report","error","terminal","onSealFailure","discardHeld","stop","drainHeld","shift","prepared","sink","discarded","sealOpen","closing","estimatedBytes","instant","startedAtMs","endedAtMs","decision","advanceV2ReplaySchedule","_state$chunkStartedAt","action","discardSealedChunk","idleForMs","sessionAgeMs","_state$v1MemberKey","sealedMs","sealedSequence","append","flush","hasBufferedWork","close","_ref4"],"mappings":";ohBACO,I,EAAMC,EAA2BA,CAACC,EAAiBC,EAAuBC,KAC7E,IAAMC,EAAUA,KAAe,MAAM,IAAIC,MAAM,6BAA6B,IACvEC,OAAOC,cAAcL,IAAkBA,EAAgB,GAAKA,EAAgB,MAAcI,OAAOC,cAAcJ,IAAkBA,EAAgB,GAAKA,EAAgB,KAAUF,EAAQO,OAAS,KAAaP,EAAQO,OAAS,GAAM,IAAGJ,IAO7O,IANA,IAAMK,EAAUR,EAAQS,SAAS,MAAQ,EAAIT,EAAQS,SAAS,KAAO,EAAI,EACnEC,EAAQ,IAAIC,WAAYX,EAAQO,OAAS,EAAK,EAAIC,GAEpDI,EAAY,EACZC,EAAO,EACPC,EAAQ,EACHC,EAAQ,EAAGA,EAAQf,EAAQO,OAASC,EAASO,IAAS,CAC3D,IAAMC,EALO,mEAKUC,QAAQjB,EAAQkB,OAAOH,IAC1CC,EAAQ,GAAGb,IACfW,EAASA,GAAS,EAAKE,GACvBH,GAAQ,IACI,IAAKA,GAAQ,EAAGH,EAAME,KAAgBE,IAAUD,EAAQ,IACxE,CACIA,GAASC,GAAU,GAAKD,GAAQ,GAAKV,IAuDzC,IArDA,IAmDIgB,EACAC,EApDEC,EAAS,IAAIV,WAAWV,GAC1BqB,EAAU,EACVC,EAAW,EACTC,EAAQC,IACNF,EAAWE,EAAuB,EAAff,EAAMH,QAAYJ,IACzC,IAAMuB,EAASH,IAAa,EACtBI,GAAWjB,EAAMgB,GAAWhB,EAAMgB,EAAS,IAAM,EAAMhB,EAAMgB,EAAS,IAAM,OAAqB,EAAXH,IAAmB,GAAKE,GAAS,EAE7H,OADAF,GAAYE,EACLE,CAAM,EAKXC,EAAOA,CAACC,EAAqBC,EAAiBC,KAGhD,IAFA,IAAMC,EAAS,IAAIC,YAAY,IAC3BC,EAAU,EACLnB,EAAQ,EAAGA,EAAQc,EAAQtB,OAAQQ,IAAS,CACjD,IAAMR,EAASsB,EAAQd,GACnBR,EAAS,IAAIJ,IACbI,IAAUyB,EAAOzB,KAAW2B,EAAUC,KAAKC,IAAIF,EAAS3B,GAChE,CAEA,IADA,IAAI8B,EAAY,EACP9B,EAAS,EAAGA,GAAU,GAAIA,KAC/B8B,EAAwB,EAAZA,EAAgBL,EAAOzB,IACnB,GAAGJ,KAElB+B,GAAmBG,GAAeP,GAAsB,IAAZI,GAA+B,IAAdF,EAAO,GAAzDD,IAAoE5B,IAEpF,IADA,IAAMmC,EAAU,IAAIL,YAAY,IACvB1B,EAAS,EAAGA,EAAS,GAAIA,IAAU+B,EAAQ/B,EAAS,GAAK+B,EAAQ/B,GAAUyB,EAAOzB,GAE3F,IADA,IAAMgC,EAAU,IAAIN,YAAYJ,EAAQtB,QAC/BiC,EAAS,EAAGA,EAASX,EAAQtB,OAAQiC,IAAU,CACpD,IAAMjC,EAASsB,EAAQW,GACnBjC,IAAQgC,EAAQD,EAAQ/B,MAAaiC,EAC7C,CACA,MAAO,CAAER,SAAQO,UAASL,UAAS,EAEjCM,EAAUC,IAIZ,IAHA,IAAIC,EAAO,EACPC,EAAQ,EACR5B,EAAQ,EACHR,EAAS,EAAGA,GAAUkC,EAAMP,QAAS3B,IAAU,CACpDmC,EAAQA,GAAQ,EAAKlB,EAAK,GAC1B,IAAMC,EAAQgB,EAAMT,OAAOzB,GAC3B,GAAImC,GAAQC,GAASD,EAAOC,EAAQlB,EAAO,OAAOgB,EAAMF,QAAQxB,EAAQ2B,EAAOC,GAC/E5B,GAASU,EACTkB,EAASA,EAAQlB,GAAU,CAC/B,CACA,OAAOtB,GAAS,EAEhByC,EAAQ,EACRC,EAAS,GAGLD,GAAO,GAELC,EAAS,MAAM1C,IACrByC,EAAQpB,EAAK,GACb,IAAMsB,EAAOtB,EAAK,GAClB,GAAa,IAATsB,EAAY,CAEZvB,EAAYA,EAAW,GAAK,EAC5B,IAAMhB,EAASiB,EAAK,KACQ,QAAvBjB,EAASiB,EAAK,MAAkBF,EAAUf,EAASN,GAAiBsB,EAAoB,EAAThB,EAA4B,EAAfG,EAAMH,SAAYJ,IACnHkB,EAAO0B,IAAIrC,EAAMsC,SAASzB,IAAa,GAAIA,IAAa,GAAKhB,GAASe,GACtEA,GAAWf,EACXgB,GAAqB,EAAThB,CAChB,KAAO,CACH,IAAI0C,OAAc,EACdC,OAAe,EACnB,GAAa,IAATJ,EAAY,CACZ,IAAK3B,IAAkBC,EAAgB,CAEnC,IADA,IAAMS,EAAU,IAAIlB,WAAW,KACtBI,EAAQ,EAAGA,EAAQc,EAAQtB,OAAQQ,IAASc,EAAQd,GAASA,EAAQ,IAAM,EAAIA,EAAQ,IAAM,EAAIA,EAAQ,IAAM,EAAI,EAC5HI,EAAgBS,EAAKC,GAAS,GAAO,GAErC,IADA,IAAMsB,EAAkB,IAAIxC,WAAW,IAC9BI,EAAQ,EAAGA,EAAQoC,EAAgB5C,OAAQQ,IAASoC,EAAgBpC,GAAS,EACtFK,EAAiBQ,EAAKuB,GAAiB,GAAO,EAClD,CACAF,EAAW9B,EACX+B,EAAY9B,CAChB,KAAO,IAAa,IAAT0B,EAyBJ,OAAO3C,IAxBV,IAAMiD,EAAe5B,EAAK,GAAK,IACzB6B,EAAgB7B,EAAK,GAAK,EAC1B8B,EAAY9B,EAAK,GAAK,EACxB4B,EAAe,KAAKjD,IAGxB,IAFA,IAAMoD,EAAQ,CAAC,GAAI,GAAI,GAAI,EAAG,EAAG,EAAG,EAAG,EAAG,GAAI,EAAG,GAAI,EAAG,GAAI,EAAG,GAAI,EAAG,GAAI,EAAG,IACvEC,EAAc,IAAI7C,WAAW,IAC1BI,EAAQ,EAAGA,EAAQuC,EAAWvC,IAASyC,EAAYD,EAAMxC,IAAUS,EAAK,GAIjF,IAHA,IAAMiC,EAAW7B,EAAK4B,GAAa,GAAO,GACpC3B,EAAU,IAAIlB,WAAWyC,EAAeC,GAC1CtC,EAAQ,EACLA,EAAQc,EAAQtB,QAAQ,CAC3B,IAAMA,EAASiC,EAAOiB,GACtB,GAAIlD,EAAS,GAAIsB,EAAQd,KAAWR,MAC/B,CACc,KAAXA,GAAkBQ,GAAOZ,IAC7B,IAAMuD,EAAsB,KAAXnD,EAAgBsB,EAAQd,EAAQ,GAAK,EAChDU,EAAmB,KAAXlB,EAAgBiB,EAAK,GAAK,EAAe,KAAXjB,EAAgBiB,EAAK,GAAK,EAAIA,EAAK,GAAK,GAChFT,EAAQU,EAAQI,EAAQtB,QAAQJ,IACpC,IAAK,IAAIwD,EAAM5C,EAAQU,EAAOV,EAAQ4C,EAAK5C,IAASc,EAAQd,GAAS2C,CACzE,CACJ,CACK7B,EAAQ,MAAM1B,IACnB8C,EAAWrB,EAAKC,EAAQmB,SAAS,EAAGI,IAAe,GAAM,GACzDF,EAAYtB,EAAKC,EAAQmB,SAASI,IAAe,GAAM,EACpC,CACvB,OAAS,CACL,IAAMQ,EAAOpB,EAAOS,GACpB,GAAa,MAATW,EAAc,MAClB,GAAIA,EAAO,IACHtC,IAAYrB,GAAeE,IAC/BkB,EAAOC,KAAasC,MACjB,CACCA,EAAO,KAAKzD,IAChB,IAAMY,EAAQ6C,EAAO,IACfC,GAAS9C,IAAU,GAAK,EACxBR,EAASQ,EAAQ,EAAIA,EAAQ,EAAc,KAAVA,EAAe,IAAqC,GAAf,GAAL,EAARA,IAAmB8C,GAAarC,EAAKqC,GAC9FC,EAAiBtB,EAAOU,GAC1BY,EAAiB,IAAI3D,IACzB,IAAM4D,GAAiBD,IAAmB,GAAK,EACzCE,EAAWF,EAAiB,EAAIA,EAAiB,EAAoD,GAAvB,GAAL,EAAjBA,IAA4BC,GAAqBvC,EAAKuC,IAChHC,EAAW1C,GAAWA,EAAUf,EAASN,IAAeE,IAE5D,IAAK,IAAIY,EAAQ,EAAGA,EAAQR,EAAQQ,IAAWM,EAAOC,GAAWD,EAAOC,EAAU0C,GAAW1C,GACjG,CACJ,CACJ,CACJ,CAEIA,IAAYrB,GAAiBkC,KAAK8B,KAAK1C,EAAW,KAAOb,EAAMH,QAAQJ,IAG3E,IAFA,IAAI+D,EAAU,GAELxC,EAAS,EAAGA,EAASJ,EAASI,GAAU,KAAMwC,GAAWC,OAAOC,aAAaC,MAAM,KAAMhD,EAAO2B,SAAStB,EAAQS,KAAKmC,IAAI5C,EAAS,KAAMJ,KAClJ,IAAMiD,EAAiBC,KAAKC,MAAMP,GAElC,OADKQ,MAAMC,QAAQJ,IAAUA,EAAMhE,SAAWL,IAAiBqE,EAAMK,MAAKC,GAAwB,iBAATA,KAAoB1E,IACtG2E,OAAOC,OAAOR,EAAkB,E,E,I,I,E,E,2gD,K,K,E,I,E,E,miG,K,K,E,E,G,E,G,I,E,E,G,E,E,G,E,G,E,G,E,G,I,E,E,G,E,E,G,E,G,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,I,E,I,I,E,E,I,E,E,I,E,I,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,I,E,I,I,E,E,I,E,I,E,I,E,I,I,E,E,I,E,E,I,E,I,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,I,I,E,E,I,E,E,I,E,I,E,I,E,I,E,I,I,E,E,I,E,I,E,I,E,I,I,E,E,I,E,I,E,I,E,I,E,I,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,E,K,E,E,K,E,E,K,E,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,I,E,E,K,E,E,K,E,E,K,E,K,E,K,I,E,E,K,E,E,K,E,K,E,K,E,K,I,E,E,K,E,K,E,K,I,E,E,K,E,K,I,E,E,K,E,K,I,G,E,K,G,E,K,E,K,E,K,I,G,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,I,G,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,E,K,I,G,E,K,E,K,E,K,I,G,E,K,E,KC1J3C,IAAQI,GAAYD,MAAZC,QACK0E,GAAgBvI,GAAuC6D,GAAQ7D,GAE/DwI,GAAiBxI,GACT,iBAAVA,GAA+B,MAATA,IAAkBuI,GAAavI,GAEnDyI,GAAiBzI,GAAqD,iBAAVA,EAE5D0I,GAAkB1I,IAA+C,IAAVA,IAA4B,IAAVA,EAEzE2I,GAAmB3I,GAEnB,mBADMA,EAQN4I,GAA0B5I,IAEnC,IADA,IAAIP,EAAS,EACJQ,EAAQ,EAAGA,EAAQD,E,IAAcC,GAAS,EAAG,CAClD,IAAM2B,EAAO5B,E,GAAiBC,GAC9B,GAAI2B,GAAQ,OAAUA,GAAQ,MAAQ,CAClC,IAAMkB,EAAO9C,E,GAAiBC,EAAQ,GACtC,GAAIA,EAAQ,GAAKD,E,KAAgB8C,EAAO,OAAUA,EAAO,MACrD,OAAO,KAEX7C,GAAS,CACb,MAAO,GAAI2B,GAAQ,OAAUA,GAAQ,MACjC,OAAO,KAEXnC,GAAU,CACd,CACA,OAAOA,CAAM,EAGJoJ,GAAiB7I,GAEjB,iBADMA,GAEAT,O,IAAgBS,GAMtB8I,GAAwB9I,GACjC6I,GAAc7I,IAAUT,O,GAAqBS,IAAUA,GAAS,EAEvD+I,GAAe/I,GAAkCgE,OAAOgF,GAAGhJ,EAAO,MAElEiJ,GAASA,CAACjJ,EAAgCkJ,IACnDlF,OAAOmF,U,GAAyBC,KAAKpJ,EAAOkJ,GAEnCG,GAAcA,CAACrJ,EAA2BC,IACnD+D,OAAOmF,U,GAAyBC,KAAKpJ,EAAOC,GAEnCqJ,GAAcA,CAACtJ,EAAgCuJ,KACxD,IAAMC,EAAU,IAAIC,IAAIF,GACxB,OAAOvF,OAAO0F,KAAK1J,GAAO2J,OAAOT,GAAQM,EAAQI,IAAIV,IAAK,EAwDjDW,GAA0C7J,GAA0BgE,OAAOC,OAAOjE,GC7DzF8J,GAAYA,CAACC,EAA8BC,EAAcpK,EAAeqK,IAC1EJ,GAAc,C,C,GACUE,E,G,C,GACFC,EAClBE,iBAAkBF,EAClBG,WAAYvK,EACZwK,YAAa,E,C,G,I,G,E,2nD,K,I,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,I,G,G,I,G,I,G,I,G,I,I,G,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,I,G,ICpCrB,IAAMG,G,yBAoByFV,GAAc,C,C,IACjGA,GAAc,CAACU,K,C,IACfV,GAAc,CAACU,K,C,IAChBV,GAAc,CAACU,KACtBC,OAAQX,GAAc,C,6BAmBnB,IAAMY,GAA8DZ,GAAc,C,C,IAC7E,a,C,IACA,a,C,IACD,YACPW,OAAQ,UAOCE,GAA8Db,GAAc,C,C,IAC7E,a,C,IACA,a,C,IACD,YACPW,OAAQ,UAIRX,GAAc,CAAE,EAAGY,GAAmB,EAAGC,KAC4Cb,GAAc,CACnG,EAAG,WACH,EAAG,aCvFP,IAAMc,GAAkB,iGAYlBC,GAAOA,CAACC,EAAeC,KACzB,MAAM,IAAIxL,MAASuL,EAAK,IAAIC,EAAU,EAc7BC,GAAwB,SACjC/K,EACA6K,EACAG,GACmB,IAAAC,EACnB,IAAKxC,GAAczI,GAAQ,OAAO4K,GAAKC,E,E,KACvC,IAAMK,EAAetC,GAAuB5I,GAC5C,GAAI+I,GAAYmC,IAAiBA,EAAe,GAAKA,EAAeF,EAChE,OAAOJ,GAAKC,EAAK,8BAAgCG,sBAErD,IAAMG,EAAQR,GAAgBS,KAAKpL,GACnC,IAAKmL,EAAO,OAAOP,GAAKC,E,E,KACxB,IAAMQ,EAAO9L,OAAO4L,EAAM,IACpBG,EAAQ/L,OAAO4L,EAAM,IACrBI,EAAMhM,OAAO4L,EAAM,IACnBK,EAAOjM,OAAO4L,EAAM,IACpBM,EAASlM,OAAO4L,EAAM,IACtBO,EAASnM,OAAO4L,EAAM,IACtBQ,UAAmBV,EAACE,EAAM,IAAEF,EAAI,IAAIW,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","x_google_ignoreList":[8]}
|