{"version":3,"sources":["../src/error.ts","../src/parser.ts","../src/column-mapper.ts","../src/helpers.ts","../src/constants.ts","../src/fetch.ts","../src/expression-compiler.ts","../../../node_modules/.pnpm/@microsoft+fetch-event-source@2.0.1_patch_hash=46f4e76dd960e002a542732bb4323817a24fce1673cb71e2f458fe09776fa188/node_modules/@microsoft/fetch-event-source/src/parse.ts","../../../node_modules/.pnpm/@microsoft+fetch-event-source@2.0.1_patch_hash=46f4e76dd960e002a542732bb4323817a24fce1673cb71e2f458fe09776fa188/node_modules/@microsoft/fetch-event-source/src/fetch.ts","../src/expired-shapes-cache.ts","../src/up-to-date-tracker.ts","../src/snapshot-tracker.ts","../src/shape-stream-state.ts","../src/pause-lock.ts","../src/client.ts","../src/shape.ts"],"sourcesContent":["export class FetchError extends Error {\n  status: number\n  text?: string\n  json?: object\n  headers: Record<string, string>\n\n  constructor(\n    status: number,\n    text: string | undefined,\n    json: object | undefined,\n    headers: Record<string, string>,\n    public url: string,\n    message?: string\n  ) {\n    super(\n      message ||\n        `HTTP Error ${status} at ${url}: ${text ?? JSON.stringify(json)}`\n    )\n    this.name = `FetchError`\n    this.status = status\n    this.text = text\n    this.json = json\n    this.headers = headers\n  }\n\n  static async fromResponse(\n    response: Response,\n    url: string\n  ): Promise<FetchError> {\n    const status = response.status\n    const headers = Object.fromEntries([...response.headers.entries()])\n    let text: string | undefined = undefined\n    let json: object | undefined = undefined\n\n    const contentType = response.headers.get(`content-type`)\n    if (!response.bodyUsed) {\n      if (contentType && contentType.includes(`application/json`)) {\n        json = (await response.json()) as object\n      } else {\n        text = await response.text()\n      }\n    }\n\n    return new FetchError(status, text, json, headers, url)\n  }\n}\n\nexport class FetchBackoffAbortError extends Error {\n  constructor() {\n    super(`Fetch with backoff aborted`)\n    this.name = `FetchBackoffAbortError`\n  }\n}\n\nexport class InvalidShapeOptionsError extends Error {\n  constructor(message: string) {\n    super(message)\n    this.name = `InvalidShapeOptionsError`\n  }\n}\n\nexport class MissingShapeUrlError extends Error {\n  constructor() {\n    super(`Invalid shape options: missing required url parameter`)\n    this.name = `MissingShapeUrlError`\n  }\n}\n\nexport class InvalidSignalError extends Error {\n  constructor() {\n    super(`Invalid signal option. It must be an instance of AbortSignal.`)\n    this.name = `InvalidSignalError`\n  }\n}\n\nexport class MissingShapeHandleError extends Error {\n  constructor() {\n    super(\n      `shapeHandle is required if this isn't an initial fetch (i.e. offset > -1)`\n    )\n    this.name = `MissingShapeHandleError`\n  }\n}\n\nexport class ReservedParamError extends Error {\n  constructor(reservedParams: string[]) {\n    super(\n      `Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`\n    )\n    this.name = `ReservedParamError`\n  }\n}\n\nexport class ParserNullValueError extends Error {\n  constructor(columnName: string) {\n    super(`Column \"${columnName ?? `unknown`}\" does not allow NULL values`)\n    this.name = `ParserNullValueError`\n  }\n}\n\nexport class ShapeStreamAlreadyRunningError extends Error {\n  constructor() {\n    super(`ShapeStream is already running`)\n    this.name = `ShapeStreamAlreadyRunningError`\n  }\n}\n\nexport class MissingHeadersError extends Error {\n  constructor(url: string, missingHeaders: Array<string>) {\n    let msg = `The response for the shape request to ${url} didn't include the following required headers:\\n`\n    missingHeaders.forEach((h) => {\n      msg += `- ${h}\\n`\n    })\n    msg += `\\nThis is often due to a proxy not setting CORS correctly so that all Electric headers can be read by the client.`\n    msg += `\\nFor more information visit the troubleshooting guide: /docs/guides/troubleshooting/missing-headers`\n    super(msg)\n  }\n}\n\nexport class StaleCacheError extends Error {\n  constructor(message: string) {\n    super(message)\n    this.name = `StaleCacheError`\n  }\n}\n","import { ColumnInfo, GetExtensions, Row, Schema, Value } from './types'\nimport { ParserNullValueError } from './error'\n\ntype Token = string\ntype NullableToken = Token | null\nexport type ParseFunction<Extensions = never> = (\n  value: Token,\n  additionalInfo?: Omit<ColumnInfo, `type` | `dims`>\n) => Value<Extensions>\ntype NullableParseFunction<Extensions = never> = (\n  value: NullableToken,\n  additionalInfo?: Omit<ColumnInfo, `type` | `dims`>\n) => Value<Extensions>\n/**\n * @typeParam Extensions - Additional types that can be parsed by this parser beyond the standard SQL types.\n *                         Defaults to no additional types.\n */\nexport type Parser<Extensions = never> = {\n  [key: string]: ParseFunction<Extensions>\n}\n\nexport type TransformFunction<Extensions = never> = (\n  message: Row<Extensions>\n) => Row<Extensions>\n\nconst parseNumber = (value: string) => Number(value)\nconst parseBool = (value: string) => value === `true` || value === `t`\nconst parseBigInt = (value: string) => BigInt(value)\nconst parseJson = (value: string) => JSON.parse(value)\nconst identityParser: ParseFunction = (v: string) => v\n\nexport const defaultParser: Parser = {\n  int2: parseNumber,\n  int4: parseNumber,\n  int8: parseBigInt,\n  bool: parseBool,\n  float4: parseNumber,\n  float8: parseNumber,\n  json: parseJson,\n  jsonb: parseJson,\n}\n\n// Taken from: https://github.com/electric-sql/pglite/blob/main/packages/pglite/src/types.ts#L233-L279\nexport function pgArrayParser<Extensions>(\n  value: Token,\n  parser?: NullableParseFunction<Extensions>\n): Value<Extensions> {\n  let i = 0\n  let char = null\n  let str = ``\n  let quoted = false\n  let last = 0\n  let p: string | undefined = undefined\n\n  function extractValue(x: Token, start: number, end: number) {\n    let val: Token | null = x.slice(start, end)\n    val = val === `NULL` ? null : val\n    return parser ? parser(val) : val\n  }\n\n  function loop(x: string): Array<Value<Extensions>> {\n    const xs = []\n    for (; i < x.length; i++) {\n      char = x[i]\n      if (quoted) {\n        if (char === `\\\\`) {\n          str += x[++i]\n        } else if (char === `\"`) {\n          xs.push(parser ? parser(str) : str)\n          str = ``\n          quoted = x[i + 1] === `\"`\n          last = i + 2\n        } else {\n          str += char\n        }\n      } else if (char === `\"`) {\n        quoted = true\n      } else if (char === `{`) {\n        last = ++i\n        xs.push(loop(x))\n      } else if (char === `}`) {\n        quoted = false\n        last < i && xs.push(extractValue(x, last, i))\n        last = i + 1\n        break\n      } else if (char === `,` && p !== `}` && p !== `\"`) {\n        xs.push(extractValue(x, last, i))\n        last = i + 1\n      }\n      p = char\n    }\n    last < i && xs.push(xs.push(extractValue(x, last, i + 1)))\n    return xs\n  }\n\n  return loop(value)[0]\n}\n\nexport class MessageParser<T extends Row<unknown>> {\n  private parser: Parser<GetExtensions<T>>\n  private transformer?: TransformFunction<GetExtensions<T>>\n  constructor(\n    parser?: Parser<GetExtensions<T>>,\n    transformer?: TransformFunction<GetExtensions<T>>\n  ) {\n    // Merge the provided parser with the default parser\n    // to use the provided parser whenever defined\n    // and otherwise fall back to the default parser\n    this.parser = { ...defaultParser, ...parser }\n    this.transformer = transformer\n  }\n\n  parse<Result>(messages: string, schema: Schema): Result {\n    return JSON.parse(messages, (key, value) => {\n      // typeof value === `object` && value !== null\n      // is needed because there could be a column named `value`\n      // and the value associated to that column will be a string or null.\n      // But `typeof null === 'object'` so we need to make an explicit check.\n      // We also parse the `old_value`, which appears on updates when `replica=full`.\n      if (\n        (key === `value` || key === `old_value`) &&\n        typeof value === `object` &&\n        value !== null\n      ) {\n        return this.transformMessageValue(value, schema)\n      }\n      return value\n    }) as Result\n  }\n\n  /**\n   * Parse an array of ChangeMessages from a snapshot response.\n   * Applies type parsing and transformations to the value and old_value properties.\n   */\n  parseSnapshotData<Result>(\n    messages: Array<unknown>,\n    schema: Schema\n  ): Array<Result> {\n    return messages.map((message) => {\n      const msg = message as Record<string, unknown>\n\n      // Transform the value property if it exists\n      if (msg.value && typeof msg.value === `object` && msg.value !== null) {\n        msg.value = this.transformMessageValue(msg.value, schema)\n      }\n\n      // Transform the old_value property if it exists\n      if (\n        msg.old_value &&\n        typeof msg.old_value === `object` &&\n        msg.old_value !== null\n      ) {\n        msg.old_value = this.transformMessageValue(msg.old_value, schema)\n      }\n\n      return msg as Result\n    })\n  }\n\n  /**\n   * Transform a message value or old_value object by parsing its columns.\n   */\n  private transformMessageValue(\n    value: unknown,\n    schema: Schema\n  ): Row<GetExtensions<T>> {\n    const row = value as Record<string, Value<GetExtensions<T>>>\n    Object.keys(row).forEach((key) => {\n      row[key] = this.parseRow(key, row[key] as NullableToken, schema)\n    })\n\n    return this.transformer ? this.transformer(row) : row\n  }\n\n  // Parses the message values using the provided parser based on the schema information\n  private parseRow(\n    key: string,\n    value: NullableToken,\n    schema: Schema\n  ): Value<GetExtensions<T>> {\n    const columnInfo = schema[key]\n    if (!columnInfo) {\n      // We don't have information about the value\n      // so we just return it\n      return value\n    }\n\n    // Copy the object but don't include `dimensions` and `type`\n    const { type: typ, dims: dimensions, ...additionalInfo } = columnInfo\n\n    // Pick the right parser for the type\n    // and support parsing null values if needed\n    // if no parser is provided for the given type, just return the value as is\n    const typeParser = this.parser[typ] ?? identityParser\n    const parser = makeNullableParser(typeParser, columnInfo, key)\n\n    if (dimensions && dimensions > 0) {\n      // It's an array\n      const nullablePgArrayParser = makeNullableParser(\n        (value, _) => pgArrayParser(value, parser),\n        columnInfo,\n        key\n      )\n      return nullablePgArrayParser(value)\n    }\n\n    return parser(value, additionalInfo)\n  }\n}\n\nfunction makeNullableParser<Extensions>(\n  parser: ParseFunction<Extensions>,\n  columnInfo: ColumnInfo,\n  columnName?: string\n): NullableParseFunction<Extensions> {\n  const isNullable = !(columnInfo.not_null ?? false)\n  // The sync service contains `null` value for a column whose value is NULL\n  // but if the column value is an array that contains a NULL value\n  // then it will be included in the array string as `NULL`, e.g.: `\"{1,NULL,3}\"`\n  return (value: NullableToken) => {\n    if (value === null) {\n      if (!isNullable) {\n        throw new ParserNullValueError(columnName ?? `unknown`)\n      }\n      return null\n    }\n    return parser(value, columnInfo)\n  }\n}\n","import { Schema } from './types'\n\ntype DbColumnName = string\ntype AppColumnName = string\n\n/**\n * Quote a PostgreSQL identifier for safe use in query parameters.\n *\n * Wraps the identifier in double quotes and escapes any internal\n * double quotes by doubling them. This ensures identifiers with\n * special characters (commas, spaces, etc.) are handled correctly.\n *\n * @param identifier - The identifier to quote\n * @returns The quoted identifier\n *\n * @example\n * ```typescript\n * quoteIdentifier('user_id')        // '\"user_id\"'\n * quoteIdentifier('foo,bar')        // '\"foo,bar\"'\n * quoteIdentifier('has\"quote')      // '\"has\"\"quote\"'\n * ```\n *\n * @internal\n */\nexport function quoteIdentifier(identifier: string): string {\n  // Escape internal double quotes by doubling them\n  const escaped = identifier.replace(/\"/g, `\"\"`)\n  return `\"${escaped}\"`\n}\n\n/**\n * A bidirectional column mapper that handles transforming column **names**\n * between database format (e.g., snake_case) and application format (e.g., camelCase).\n *\n * **Important**: ColumnMapper only transforms column names, not column values or types.\n * For type conversions (e.g., string → Date), use the `parser` option.\n * For value transformations (e.g., encryption), use the `transformer` option.\n *\n * @example\n * ```typescript\n * const mapper = snakeCamelMapper()\n * mapper.decode('user_id') // 'userId'\n * mapper.encode('userId') // 'user_id'\n * ```\n */\nexport interface ColumnMapper {\n  /**\n   * Transform a column name from database format to application format.\n   * Applied to column names in query results.\n   */\n  decode: (dbColumnName: DbColumnName) => AppColumnName\n\n  /**\n   * Transform a column name from application format to database format.\n   * Applied to column names in WHERE clauses and other query parameters.\n   */\n  encode: (appColumnName: AppColumnName) => DbColumnName\n}\n\n/**\n * Converts a snake_case string to camelCase.\n *\n * Handles edge cases:\n * - Preserves leading underscores: `_user_id` → `_userId`\n * - Preserves trailing underscores: `user_id_` → `userId_`\n * - Preserves multi-underscore count for injectivity:\n *     `user__id` → `user_Id`, `user___id` → `user__Id`\n * - Normalizes to lowercase first: `user_Column` → `userColumn`\n *\n * The transform is designed to be injective on lowercase input and\n * round-trippable via `camelToSnake`, so distinct db columns never\n * collide on the same app key.\n *\n * @example\n * snakeToCamel('user_id') // 'userId'\n * snakeToCamel('project_id') // 'projectId'\n * snakeToCamel('created_at') // 'createdAt'\n * snakeToCamel('_private') // '_private'\n * snakeToCamel('user__id') // 'user_Id'\n * snakeToCamel('user___id') // 'user__Id'\n * snakeToCamel('user_id_') // 'userId_'\n */\nexport function snakeToCamel(str: string): string {\n  // Preserve leading underscores\n  const leadingUnderscores = str.match(/^_+/)?.[0] ?? ``\n  const withoutLeading = str.slice(leadingUnderscores.length)\n\n  // Preserve trailing underscores for round-trip safety\n  const trailingUnderscores = withoutLeading.match(/_+$/)?.[0] ?? ``\n  const core = trailingUnderscores\n    ? withoutLeading.slice(\n        0,\n        withoutLeading.length - trailingUnderscores.length\n      )\n    : withoutLeading\n\n  // Convert to lowercase\n  const normalized = core.toLowerCase()\n\n  // Convert snake_case to camelCase. For a run of n underscores before\n  // a letter, keep (n-1) literal underscores and uppercase the letter.\n  // This preserves underscore count so distinct inputs never collide.\n  const camelCased = normalized.replace(/_+([a-z])/g, (match, letter) => {\n    const extraUnderscores = `_`.repeat(match.length - 2)\n    return extraUnderscores + letter.toUpperCase()\n  })\n\n  return leadingUnderscores + camelCased + trailingUnderscores\n}\n\n/**\n * Converts a camelCase string to snake_case.\n *\n * Handles consecutive capitals (acronyms) properly:\n * - `userID` → `user_id`\n * - `userHTTPSURL` → `user_https_url`\n *\n * @example\n * camelToSnake('userId') // 'user_id'\n * camelToSnake('projectId') // 'project_id'\n * camelToSnake('createdAt') // 'created_at'\n * camelToSnake('userID') // 'user_id'\n * camelToSnake('parseHTMLString') // 'parse_html_string'\n */\nexport function camelToSnake(str: string): string {\n  return (\n    str\n      // Insert underscore before uppercase letters that follow a\n      // lowercase letter or an underscore. The `_` case preserves the\n      // injective encoding from snakeToCamel: `user_Id` → `user__id`.\n      .replace(/([a-z_])([A-Z])/g, `$1_$2`)\n      // Insert underscore before uppercase letters that are followed by lowercase letters\n      // This handles acronyms: userID -> user_ID, but parseHTMLString -> parse_HTML_String\n      .replace(/([A-Z]+)([A-Z][a-z])/g, `$1_$2`)\n      .toLowerCase()\n  )\n}\n\n/**\n * Creates a column mapper from an explicit mapping of database columns to application columns.\n *\n * @param mapping - Object mapping database column names (keys) to application column names (values)\n * @returns A ColumnMapper that can encode and decode column names bidirectionally\n *\n * @example\n * const mapper = createColumnMapper({\n *   user_id: 'userId',\n *   project_id: 'projectId',\n *   created_at: 'createdAt'\n * })\n *\n * // Use with ShapeStream\n * const stream = new ShapeStream({\n *   url: 'http://localhost:3000/v1/shape',\n *   params: { table: 'todos' },\n *   columnMapper: mapper\n * })\n */\nexport function createColumnMapper(\n  mapping: Record<string, string>\n): ColumnMapper {\n  // Build reverse mapping: app name -> db name\n  const reverseMapping: Record<string, string> = {}\n  for (const [dbName, appName] of Object.entries(mapping)) {\n    reverseMapping[appName] = dbName\n  }\n\n  return {\n    decode: (dbColumnName: string) => {\n      return mapping[dbColumnName] ?? dbColumnName\n    },\n\n    encode: (appColumnName: string) => {\n      return reverseMapping[appColumnName] ?? appColumnName\n    },\n  }\n}\n\n/**\n * Encodes column names in a WHERE clause using the provided encoder function.\n * Uses regex to identify column references and replace them.\n *\n * Handles common SQL patterns:\n * - Simple comparisons: columnName = $1\n * - Function calls: LOWER(columnName)\n * - Qualified names: table.columnName\n * - Operators: columnName IS NULL, columnName IN (...)\n * - Quoted strings: Preserves string literals unchanged\n *\n * Note: This uses regex-based replacement which works for most common cases\n * but may not handle all complex SQL expressions perfectly. For complex queries,\n * test thoroughly or use database column names directly in WHERE clauses.\n *\n * @param whereClause - The WHERE clause string to encode\n * @param encode - Optional encoder function. If undefined, returns whereClause unchanged.\n * @returns The encoded WHERE clause\n *\n * @internal\n */\nexport function encodeWhereClause(\n  whereClause: string | undefined,\n  encode?: (columnName: string) => string\n): string {\n  if (!whereClause || !encode) return whereClause ?? ``\n\n  // SQL keywords that should not be transformed (common ones)\n  const sqlKeywords = new Set([\n    `SELECT`,\n    `FROM`,\n    `WHERE`,\n    `AND`,\n    `OR`,\n    `NOT`,\n    `IN`,\n    `IS`,\n    `NULL`,\n    `NULLS`,\n    `FIRST`,\n    `LAST`,\n    `TRUE`,\n    `FALSE`,\n    `LIKE`,\n    `ILIKE`,\n    `BETWEEN`,\n    `ASC`,\n    `DESC`,\n    `LIMIT`,\n    `OFFSET`,\n    `ORDER`,\n    `BY`,\n    `GROUP`,\n    `HAVING`,\n    `DISTINCT`,\n    `AS`,\n    `ON`,\n    `JOIN`,\n    `LEFT`,\n    `RIGHT`,\n    `INNER`,\n    `OUTER`,\n    `CROSS`,\n    `CASE`,\n    `WHEN`,\n    `THEN`,\n    `ELSE`,\n    `END`,\n    `CAST`,\n    `LOWER`,\n    `UPPER`,\n    `COALESCE`,\n    `NULLIF`,\n  ])\n\n  // Track positions of quoted strings and double-quoted identifiers to skip them\n  const quotedRanges: Array<{ start: number; end: number }> = []\n\n  // Find all single-quoted strings and double-quoted identifiers\n  let pos = 0\n  while (pos < whereClause.length) {\n    const ch = whereClause[pos]\n    if (ch === `'` || ch === `\"`) {\n      const start = pos\n      const quoteChar = ch\n      pos++ // Skip opening quote\n      // Find closing quote, handling escaped quotes ('' or \"\")\n      while (pos < whereClause.length) {\n        if (whereClause[pos] === quoteChar) {\n          if (whereClause[pos + 1] === quoteChar) {\n            pos += 2 // Skip escaped quote\n          } else {\n            pos++ // Skip closing quote\n            break\n          }\n        } else {\n          pos++\n        }\n      }\n      quotedRanges.push({ start, end: pos })\n    } else {\n      pos++\n    }\n  }\n\n  // Helper to check if position is within a quoted string or double-quoted identifier\n  const isInQuotedString = (pos: number): boolean => {\n    return quotedRanges.some((range) => pos >= range.start && pos < range.end)\n  }\n\n  // Pattern explanation:\n  // (?<![a-zA-Z0-9_]) - negative lookbehind: not preceded by identifier char\n  // ([a-zA-Z_][a-zA-Z0-9_]*) - capture: valid SQL identifier\n  // (?![a-zA-Z0-9_]) - negative lookahead: not followed by identifier char\n  //\n  // This avoids matching:\n  // - Parts of longer identifiers\n  // - SQL keywords (handled by checking if result differs from input)\n  const identifierPattern =\n    /(?<![a-zA-Z0-9_])([a-zA-Z_][a-zA-Z0-9_]*)(?![a-zA-Z0-9_])/g\n\n  return whereClause.replace(identifierPattern, (match, _p1, offset) => {\n    // Don't transform if inside quoted string\n    if (isInQuotedString(offset)) {\n      return match\n    }\n\n    // Don't transform SQL keywords\n    if (sqlKeywords.has(match.toUpperCase())) {\n      return match\n    }\n\n    // Don't transform parameter placeholders ($1, $2, etc.)\n    // This regex won't match them anyway, but being explicit\n    if (match.startsWith(`$`)) {\n      return match\n    }\n\n    // Apply encoding\n    const encoded = encode(match)\n    return encoded\n  })\n}\n\n/**\n * Creates a column mapper that automatically converts between snake_case and camelCase.\n * This is the most common use case for column mapping.\n *\n * When a schema is provided, it will only map columns that exist in the schema.\n * Otherwise, it will map any column name it encounters.\n *\n * **⚠️ Limitations and Edge Cases:**\n * - **WHERE clause encoding**: Uses regex-based parsing which may not handle all complex\n *   SQL expressions. Test thoroughly with your queries, especially those with:\n *   - Complex nested expressions\n *   - Custom operators or functions\n *   - Column names that conflict with SQL keywords\n *   - Quoted identifiers (e.g., `\"$price\"`, `\"user-id\"`) - not supported\n *   - Column names with special characters (non-alphanumeric except underscore)\n * - **Acronym ambiguity**: `userID` → `user_id` → `userId` (ID becomes Id after roundtrip)\n *   Use `createColumnMapper()` with explicit mapping if you need exact control\n * - **Type conversion**: This only renames columns, not values. Use `parser` for type conversion\n *\n * **When to use explicit mapping instead:**\n * - You have column names that don't follow snake_case/camelCase patterns\n * - You need exact control over mappings (e.g., `id` → `identifier`)\n * - Your WHERE clauses are complex and automatic encoding fails\n * - You have quoted identifiers or column names with special characters\n *\n * @param schema - Optional database schema to constrain mapping to known columns\n * @returns A ColumnMapper for snake_case ↔ camelCase conversion\n *\n * @example\n * // Basic usage\n * const mapper = snakeCamelMapper()\n *\n * // With schema - only maps columns in schema (recommended)\n * const mapper = snakeCamelMapper(schema)\n *\n * // Use with ShapeStream\n * const stream = new ShapeStream({\n *   url: 'http://localhost:3000/v1/shape',\n *   params: { table: 'todos' },\n *   columnMapper: snakeCamelMapper()\n * })\n *\n * @example\n * // If automatic encoding fails, fall back to manual column names in WHERE clauses:\n * stream.requestSnapshot({\n *   where: \"user_id = $1\", // Use database column names directly if needed\n *   params: { \"1\": \"123\" }\n * })\n */\nexport function snakeCamelMapper(schema?: Schema): ColumnMapper {\n  // If schema provided, build explicit mapping\n  if (schema) {\n    const mapping: Record<string, string> = {}\n    for (const dbColumn of Object.keys(schema)) {\n      mapping[dbColumn] = snakeToCamel(dbColumn)\n    }\n    return createColumnMapper(mapping)\n  }\n\n  // Otherwise, map dynamically\n  return {\n    decode: (dbColumnName: string) => {\n      return snakeToCamel(dbColumnName)\n    },\n\n    encode: (appColumnName: string) => {\n      return camelToSnake(appColumnName)\n    },\n  }\n}\n","import {\n  ChangeMessage,\n  ControlMessage,\n  Message,\n  NormalizedPgSnapshot,\n  Offset,\n  PostgresSnapshot,\n  Row,\n} from './types'\n\n/**\n * Type guard for checking {@link Message} is {@link ChangeMessage}.\n *\n * See [TS docs](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)\n * for information on how to use type guards.\n *\n * @param message - the message to check\n * @returns true if the message is a {@link ChangeMessage}\n *\n * @example\n * ```ts\n * if (isChangeMessage(message)) {\n *   const msgChng: ChangeMessage = message // Ok\n *   const msgCtrl: ControlMessage = message // Err, type mismatch\n * }\n * ```\n */\nexport function isChangeMessage<T extends Row<unknown> = Row>(\n  message: Message<T>\n): message is ChangeMessage<T> {\n  return message != null && `key` in message\n}\n\n/**\n * Type guard for checking {@link Message} is {@link ControlMessage}.\n *\n * See [TS docs](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)\n * for information on how to use type guards.\n *\n * @param message - the message to check\n * @returns true if the message is a {@link ControlMessage}\n *\n *  * @example\n * ```ts\n * if (isControlMessage(message)) {\n *   const msgChng: ChangeMessage = message // Err, type mismatch\n *   const msgCtrl: ControlMessage = message // Ok\n * }\n * ```\n */\nexport function isControlMessage<T extends Row<unknown> = Row>(\n  message: Message<T>\n): message is ControlMessage {\n  return message != null && `headers` in message && `control` in message.headers\n}\n\nexport function isUpToDateMessage<T extends Row<unknown> = Row>(\n  message: Message<T>\n): message is ControlMessage & { up_to_date: true } {\n  return isControlMessage(message) && message.headers.control === `up-to-date`\n}\n\n/**\n * Parses the LSN from the up-to-date message and turns it into an offset.\n * The LSN is only present in the up-to-date control message when in SSE mode.\n * If we are not in SSE mode this function will return undefined.\n */\nexport function getOffset(message: ControlMessage): Offset | undefined {\n  if (message.headers.control != `up-to-date`) return\n  const lsn = message.headers.global_last_seen_lsn\n  return lsn ? (`${lsn}_0` as Offset) : undefined\n}\n\nfunction bigintReplacer(_key: string, value: unknown): unknown {\n  return typeof value === `bigint` ? value.toString() : value\n}\n\n/**\n * BigInt-safe version of JSON.stringify.\n * Converts BigInt values to their string representation (as JSON strings,\n * e.g. `{ id: 42n }` becomes `{\"id\":\"42\"}`) instead of throwing.\n * Assumes input is a JSON-serializable value — passing `undefined` at the\n * top level will return `undefined` (matching `JSON.stringify` behavior).\n */\nexport function bigintSafeStringify(value: unknown): string {\n  return JSON.stringify(value, bigintReplacer)\n}\n\n/**\n * Canonical, BigInt-safe stringify. Recursively sorts object keys so that\n * permutation-equivalent inputs produce identical output. Suitable for\n * dedup keys and cache lookups.\n */\nexport function canonicalBigintSafeStringify(value: unknown): string {\n  return JSON.stringify(canonicalize(value))\n}\n\nfunction canonicalize(value: unknown): unknown {\n  if (typeof value === `bigint`) return value.toString()\n  if (value === null || typeof value !== `object`) return value\n  if (Array.isArray(value)) return value.map(canonicalize)\n  const sorted: Record<string, unknown> = {}\n  for (const k of Object.keys(value as Record<string, unknown>).sort()) {\n    sorted[k] = canonicalize((value as Record<string, unknown>)[k])\n  }\n  return sorted\n}\n\n/**\n * Checks if a transaction is visible in a snapshot.\n *\n * @param txid - the transaction id to check\n * @param snapshot - the information about the snapshot\n * @returns true if the transaction is visible in the snapshot\n */\nexport function isVisibleInSnapshot(\n  txid: number | bigint | `${bigint}`,\n  snapshot: PostgresSnapshot | NormalizedPgSnapshot\n): boolean {\n  const xid = BigInt(txid)\n  const xmin = BigInt(snapshot.xmin)\n  const xmax = BigInt(snapshot.xmax)\n  const xip = snapshot.xip_list.map(BigInt)\n\n  // If the transaction id is less than the minimum transaction id, it is visible in the snapshot.\n  // If the transaction id is less than the maximum transaction id and not in the list of active\n  //   transactions at the time of the snapshot, it has been committed before the snapshot was taken\n  //   and is therefore visible in the snapshot.\n  // Otherwise, it is not visible in the snapshot.\n\n  return xid < xmin || (xid < xmax && !xip.includes(xid))\n}\n","export const LIVE_CACHE_BUSTER_HEADER = `electric-cursor`\nexport const SHAPE_HANDLE_HEADER = `electric-handle`\nexport const CHUNK_LAST_OFFSET_HEADER = `electric-offset`\nexport const SHAPE_SCHEMA_HEADER = `electric-schema`\nexport const CHUNK_UP_TO_DATE_HEADER = `electric-up-to-date`\nexport const COLUMNS_QUERY_PARAM = `columns`\nexport const LIVE_CACHE_BUSTER_QUERY_PARAM = `cursor`\nexport const EXPIRED_HANDLE_QUERY_PARAM = `expired_handle`\nexport const SHAPE_HANDLE_QUERY_PARAM = `handle`\nexport const LIVE_QUERY_PARAM = `live`\nexport const OFFSET_QUERY_PARAM = `offset`\nexport const TABLE_QUERY_PARAM = `table`\nexport const WHERE_QUERY_PARAM = `where`\nexport const REPLICA_PARAM = `replica`\nexport const WHERE_PARAMS_PARAM = `params`\n/**\n * @deprecated Use {@link LIVE_SSE_QUERY_PARAM} instead.\n */\nexport const EXPERIMENTAL_LIVE_SSE_QUERY_PARAM = `experimental_live_sse`\nexport const LIVE_SSE_QUERY_PARAM = `live_sse`\nexport const FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`\nexport const PAUSE_STREAM = `pause-stream`\nexport const SYSTEM_WAKE = `system-wake`\nexport const LOG_MODE_QUERY_PARAM = `log`\nexport const SUBSET_PARAM_WHERE = `subset__where`\nexport const SUBSET_PARAM_LIMIT = `subset__limit`\nexport const SUBSET_PARAM_OFFSET = `subset__offset`\nexport const SUBSET_PARAM_ORDER_BY = `subset__order_by`\nexport const SUBSET_PARAM_WHERE_PARAMS = `subset__params`\nexport const SUBSET_PARAM_WHERE_EXPR = `subset__where_expr`\nexport const SUBSET_PARAM_ORDER_BY_EXPR = `subset__order_by_expr`\nexport const CACHE_BUSTER_QUERY_PARAM = `cache-buster` // Random cache buster to bypass stale CDN responses\n\n// Query parameters that should be passed through when proxying Electric requests\nexport const ELECTRIC_PROTOCOL_QUERY_PARAMS: Array<string> = [\n  LIVE_QUERY_PARAM,\n  LIVE_SSE_QUERY_PARAM,\n  EXPERIMENTAL_LIVE_SSE_QUERY_PARAM,\n  SHAPE_HANDLE_QUERY_PARAM,\n  OFFSET_QUERY_PARAM,\n  LIVE_CACHE_BUSTER_QUERY_PARAM,\n  EXPIRED_HANDLE_QUERY_PARAM,\n  LOG_MODE_QUERY_PARAM,\n  SUBSET_PARAM_WHERE,\n  SUBSET_PARAM_LIMIT,\n  SUBSET_PARAM_OFFSET,\n  SUBSET_PARAM_ORDER_BY,\n  SUBSET_PARAM_WHERE_PARAMS,\n  SUBSET_PARAM_WHERE_EXPR,\n  SUBSET_PARAM_ORDER_BY_EXPR,\n  CACHE_BUSTER_QUERY_PARAM,\n]\n","import {\n  CHUNK_LAST_OFFSET_HEADER,\n  CHUNK_UP_TO_DATE_HEADER,\n  EXPIRED_HANDLE_QUERY_PARAM,\n  LIVE_CACHE_BUSTER_HEADER,\n  LIVE_QUERY_PARAM,\n  OFFSET_QUERY_PARAM,\n  SHAPE_SCHEMA_HEADER,\n  SHAPE_HANDLE_HEADER,\n  SHAPE_HANDLE_QUERY_PARAM,\n  SUBSET_PARAM_LIMIT,\n  SUBSET_PARAM_OFFSET,\n  SUBSET_PARAM_ORDER_BY,\n  SUBSET_PARAM_WHERE,\n  SUBSET_PARAM_WHERE_PARAMS,\n} from './constants'\nimport {\n  FetchError,\n  FetchBackoffAbortError,\n  MissingHeadersError,\n} from './error'\n\n// Some specific 4xx and 5xx HTTP status codes that we definitely\n// want to retry\nconst HTTP_RETRY_STATUS_CODES = [429]\n\nexport interface BackoffOptions {\n  /**\n   * Initial delay before retrying in milliseconds\n   */\n  initialDelay: number\n  /**\n   * Maximum retry delay in milliseconds\n   * After reaching this, delay stays constant (e.g., retry every 60s)\n   */\n  maxDelay: number\n  multiplier: number\n  onFailedAttempt?: () => void\n  debug?: boolean\n  /**\n   * Maximum number of retry attempts before giving up.\n   * Set to Infinity (default) for indefinite retries - needed for offline scenarios\n   * where clients may go offline and come back later.\n   */\n  maxRetries?: number\n}\n\nexport const BackoffDefaults = {\n  initialDelay: 1_000,\n  maxDelay: 32_000,\n  multiplier: 2,\n  maxRetries: Infinity, // Retry forever - clients may go offline and come back\n}\n\n/**\n * Parse Retry-After header value and return delay in milliseconds\n * Supports both delta-seconds format and HTTP-date format\n * Returns 0 if header is not present or invalid\n */\nexport function parseRetryAfterHeader(retryAfter: string | undefined): number {\n  if (!retryAfter) return 0\n\n  // Try parsing as seconds (delta-seconds format)\n  const retryAfterSec = Number(retryAfter)\n  if (Number.isFinite(retryAfterSec) && retryAfterSec > 0) {\n    return retryAfterSec * 1000\n  }\n\n  // Try parsing as HTTP-date\n  const retryDate = Date.parse(retryAfter)\n  if (!isNaN(retryDate)) {\n    // Handle clock skew: clamp to non-negative, cap at reasonable max\n    const deltaMs = retryDate - Date.now()\n    return Math.max(0, Math.min(deltaMs, 3600_000)) // Cap at 1 hour\n  }\n\n  return 0\n}\n\nexport function createFetchWithBackoff(\n  fetchClient: typeof fetch,\n  backoffOptions: BackoffOptions = BackoffDefaults\n): typeof fetch {\n  const {\n    initialDelay,\n    maxDelay,\n    multiplier,\n    debug = false,\n    onFailedAttempt,\n    maxRetries = Infinity,\n  } = backoffOptions\n  return async (...args: Parameters<typeof fetch>): Promise<Response> => {\n    const url = args[0]\n    const options = args[1]\n\n    let delay = initialDelay\n    let attempt = 0\n\n    while (true) {\n      try {\n        const result = await fetchClient(...args)\n        if (result.ok) {\n          return result\n        }\n\n        const err = await FetchError.fromResponse(result, url.toString())\n\n        throw err\n      } catch (e) {\n        onFailedAttempt?.()\n        if (options?.signal?.aborted) {\n          throw new FetchBackoffAbortError()\n        } else if (\n          e instanceof FetchError &&\n          !HTTP_RETRY_STATUS_CODES.includes(e.status) &&\n          e.status >= 400 &&\n          e.status < 500\n        ) {\n          // Any client errors cannot be backed off on, leave it to the caller to handle.\n          throw e\n        } else {\n          // Check max retries\n          attempt++\n          if (attempt > maxRetries) {\n            if (debug) {\n              console.log(\n                `Max retries reached (${attempt}/${maxRetries}), giving up`\n              )\n            }\n            throw e\n          }\n\n          // Calculate wait time honoring server-driven backoff as a floor\n          // Precedence: max(serverMinimum, min(clientMaxDelay, backoffWithJitter))\n\n          // 1. Parse server-provided Retry-After (if present)\n          const serverMinimumMs =\n            e instanceof FetchError && e.headers\n              ? parseRetryAfterHeader(e.headers[`retry-after`])\n              : 0\n\n          // 2. Calculate client backoff with full jitter strategy\n          // Full jitter: random_between(0, min(cap, exponential_backoff))\n          // See: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/\n          const jitter = Math.random() * delay // random value between 0 and current delay\n          const clientBackoffMs = Math.min(jitter, maxDelay) // cap at maxDelay\n\n          // 3. Server minimum is the floor, client cap is the ceiling\n          const waitMs = Math.max(serverMinimumMs, clientBackoffMs)\n\n          if (debug) {\n            const source = serverMinimumMs > 0 ? `server+client` : `client`\n            console.log(\n              `Retry attempt #${attempt} after ${waitMs}ms (${source}, serverMin=${serverMinimumMs}ms, clientBackoff=${clientBackoffMs}ms)`\n            )\n          }\n\n          // Wait for the calculated duration\n          await new Promise((resolve) => setTimeout(resolve, waitMs))\n\n          // Increase the delay for the next attempt (capped at maxDelay)\n          delay = Math.min(delay * multiplier, maxDelay)\n        }\n      }\n    }\n  }\n}\n\nconst NO_BODY_STATUS_CODES = [201, 204, 205]\n\n// Ensure body can actually be read in its entirety\nexport function createFetchWithConsumedMessages(fetchClient: typeof fetch) {\n  return async (...args: Parameters<typeof fetch>): Promise<Response> => {\n    const url = args[0]\n    const res = await fetchClient(...args)\n    try {\n      if (res.status < 200 || NO_BODY_STATUS_CODES.includes(res.status)) {\n        return res\n      }\n\n      const text = await res.text()\n      return new Response(text, res)\n    } catch (err) {\n      if (args[1]?.signal?.aborted) {\n        throw new FetchBackoffAbortError()\n      }\n\n      throw new FetchError(\n        res.status,\n        undefined,\n        undefined,\n        Object.fromEntries([...res.headers.entries()]),\n        url.toString(),\n        err instanceof Error\n          ? err.message\n          : typeof err === `string`\n            ? err\n            : `failed to read body`\n      )\n    }\n  }\n}\n\ninterface ChunkPrefetchOptions {\n  maxChunksToPrefetch: number\n}\n\nconst ChunkPrefetchDefaults = {\n  maxChunksToPrefetch: 2,\n}\n\n/**\n * Creates a fetch client that prefetches subsequent log chunks for\n * consumption by the shape stream without waiting for the chunk bodies\n * themselves to be loaded.\n *\n * @param fetchClient the client to wrap\n * @param prefetchOptions options to configure prefetching\n * @returns wrapped client with prefetch capabilities\n */\nexport function createFetchWithChunkBuffer(\n  fetchClient: typeof fetch,\n  prefetchOptions: ChunkPrefetchOptions = ChunkPrefetchDefaults\n): typeof fetch {\n  const { maxChunksToPrefetch } = prefetchOptions\n\n  let prefetchQueue: PrefetchQueue | undefined\n\n  const prefetchClient = async (...args: Parameters<typeof fetchClient>) => {\n    const url = args[0].toString()\n    const method = getRequestMethod(args[0], args[1])\n\n    // Prefetch is only valid for GET requests. The prefetch queue matches\n    // requests by URL alone and ignores HTTP method/body, so a POST request\n    // with the same URL would incorrectly consume the prefetched stream\n    // response instead of making its own request.\n    if (method !== `GET`) {\n      prefetchQueue?.abort()\n      prefetchQueue = undefined\n      return fetchClient(...args)\n    }\n\n    // try to consume from the prefetch queue first, and if request is\n    // not present abort the prefetch queue as it must no longer be valid\n    const prefetchedRequest = prefetchQueue?.consume(...args)\n    if (prefetchedRequest) {\n      return prefetchedRequest\n    }\n\n    // Clear the prefetch queue after aborting to prevent returning\n    // stale/aborted requests on future calls with the same URL\n    prefetchQueue?.abort()\n    prefetchQueue = undefined\n\n    // perform request and fire off prefetch queue if request is eligible\n    const response = await fetchClient(...args)\n    const nextUrl = getNextChunkUrl(url, response)\n    if (nextUrl) {\n      prefetchQueue = new PrefetchQueue({\n        fetchClient,\n        maxPrefetchedRequests: maxChunksToPrefetch,\n        url: nextUrl,\n        requestInit: args[1],\n      })\n    }\n\n    return response\n  }\n\n  return prefetchClient\n}\n\nexport const requiredElectricResponseHeaders = [\n  CHUNK_LAST_OFFSET_HEADER,\n  SHAPE_HANDLE_HEADER,\n]\n\nexport const requiredLiveResponseHeaders = [LIVE_CACHE_BUSTER_HEADER]\n\nexport const requiredNonLiveResponseHeaders = [SHAPE_SCHEMA_HEADER]\n\nexport function createFetchWithResponseHeadersCheck(\n  fetchClient: typeof fetch\n): typeof fetch {\n  return async (...args: Parameters<typeof fetchClient>) => {\n    const response = await fetchClient(...args)\n\n    if (response.ok) {\n      // Check that the necessary Electric headers are present on the response\n      const headers = response.headers\n      const missingHeaders: Array<string> = []\n\n      const addMissingHeaders = (requiredHeaders: Array<string>) =>\n        missingHeaders.push(...requiredHeaders.filter((h) => !headers.has(h)))\n\n      const input = args[0]\n      const urlString = input.toString()\n      const url = new URL(urlString)\n\n      // Snapshot responses (subset params) return a JSON object and do not include Electric chunk headers\n      const isSnapshotRequest = [\n        SUBSET_PARAM_WHERE,\n        SUBSET_PARAM_WHERE_PARAMS,\n        SUBSET_PARAM_LIMIT,\n        SUBSET_PARAM_OFFSET,\n        SUBSET_PARAM_ORDER_BY,\n      ].some((p) => url.searchParams.has(p))\n      if (isSnapshotRequest) {\n        return response\n      }\n\n      addMissingHeaders(requiredElectricResponseHeaders)\n      if (url.searchParams.get(LIVE_QUERY_PARAM) === `true`) {\n        addMissingHeaders(requiredLiveResponseHeaders)\n      }\n\n      if (\n        !url.searchParams.has(LIVE_QUERY_PARAM) ||\n        url.searchParams.get(LIVE_QUERY_PARAM) === `false`\n      ) {\n        addMissingHeaders(requiredNonLiveResponseHeaders)\n      }\n\n      if (missingHeaders.length > 0) {\n        throw new MissingHeadersError(urlString, missingHeaders)\n      }\n    }\n\n    return response\n  }\n}\n\nclass PrefetchQueue {\n  readonly #fetchClient: typeof fetch\n  readonly #maxPrefetchedRequests: number\n  readonly #prefetchQueue = new Map<\n    string,\n    [Promise<Response>, AbortController]\n  >()\n  #queueHeadUrl: string | void\n  #queueTailUrl: string | void\n\n  constructor(options: {\n    url: Parameters<typeof fetch>[0]\n    requestInit: Parameters<typeof fetch>[1]\n    maxPrefetchedRequests: number\n    fetchClient?: typeof fetch\n  }) {\n    this.#fetchClient =\n      options.fetchClient ??\n      ((...args: Parameters<typeof fetch>) => fetch(...args))\n    this.#maxPrefetchedRequests = options.maxPrefetchedRequests\n    this.#queueHeadUrl = options.url.toString()\n    this.#queueTailUrl = this.#queueHeadUrl\n    this.#prefetch(options.url, options.requestInit)\n  }\n\n  abort(): void {\n    this.#prefetchQueue.forEach(([_, aborter]) => aborter.abort())\n    this.#prefetchQueue.clear()\n  }\n\n  consume(...args: Parameters<typeof fetch>): Promise<Response> | void {\n    const url = args[0].toString()\n\n    const entry = this.#prefetchQueue.get(url)\n    // only consume if request is in queue and is the queue \"head\"\n    // if request is in the queue but not the head, the queue is being\n    // consumed out of order and should be restarted\n    if (!entry || url !== this.#queueHeadUrl) return\n\n    const [request, aborter] = entry\n    // Don't return aborted requests - they will reject with AbortError\n    if (aborter.signal.aborted) {\n      this.#prefetchQueue.delete(url)\n      return\n    }\n    this.#prefetchQueue.delete(url)\n\n    // fire off new prefetch since request has been consumed\n    request\n      .then((response) => {\n        const nextUrl = getNextChunkUrl(url, response)\n        this.#queueHeadUrl = nextUrl\n        if (\n          this.#queueTailUrl &&\n          !this.#prefetchQueue.has(this.#queueTailUrl)\n        ) {\n          this.#prefetch(this.#queueTailUrl, args[1])\n        }\n      })\n      .catch(() => {})\n\n    return request\n  }\n\n  #prefetch(...args: Parameters<typeof fetch>): void {\n    const url = args[0].toString()\n\n    // only prefetch when queue is not full\n    if (this.#prefetchQueue.size >= this.#maxPrefetchedRequests) return\n\n    // initialize aborter per request, to avoid aborting consumed requests that\n    // are still streaming their bodies to the consumer\n    const aborter = new AbortController()\n\n    try {\n      const { signal, cleanup } = chainAborter(aborter, args[1]?.signal)\n      const request = this.#fetchClient(url, { ...(args[1] ?? {}), signal })\n      this.#prefetchQueue.set(url, [request, aborter])\n      request\n        .then((response) => {\n          // only keep prefetching if response chain is uninterrupted\n          if (!response.ok || aborter.signal.aborted) return\n\n          const nextUrl = getNextChunkUrl(url, response)\n\n          // only prefetch when there is a next URL\n          if (!nextUrl || nextUrl === url) {\n            this.#queueTailUrl = undefined\n            return\n          }\n\n          this.#queueTailUrl = nextUrl\n          return this.#prefetch(nextUrl, args[1])\n        })\n        .catch(() => {})\n        .finally(cleanup)\n    } catch (_) {\n      // ignore prefetch errors\n    }\n  }\n}\n\n/**\n * Generate the next chunk's URL if the url and response are valid\n */\nfunction getNextChunkUrl(url: string, res: Response): string | void {\n  const shapeHandle = res.headers.get(SHAPE_HANDLE_HEADER)\n  const lastOffset = res.headers.get(CHUNK_LAST_OFFSET_HEADER)\n  const isUpToDate = res.headers.has(CHUNK_UP_TO_DATE_HEADER)\n\n  // only prefetch if shape handle and offset for next chunk are available, and\n  // response is not already up-to-date\n  if (!shapeHandle || !lastOffset || isUpToDate) return\n\n  const nextUrl = new URL(url)\n\n  // don't prefetch live requests, rushing them will only\n  // potentially miss more recent data\n  if (nextUrl.searchParams.has(LIVE_QUERY_PARAM)) return\n\n  // don't prefetch if the response handle is the expired handle from the request\n  // this can happen when a proxy serves a stale cached response despite the\n  // expired_handle cache buster parameter\n  const expiredHandle = nextUrl.searchParams.get(EXPIRED_HANDLE_QUERY_PARAM)\n  if (expiredHandle && shapeHandle === expiredHandle) {\n    console.warn(\n      `[Electric] Received stale cached response with expired shape handle. ` +\n        `This should not happen and indicates a proxy/CDN caching misconfiguration. ` +\n        `The response contained handle \"${shapeHandle}\" which was previously marked as expired. ` +\n        `Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key. ` +\n        `Skipping prefetch to prevent infinite 409 loop.`\n    )\n    return\n  }\n\n  nextUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, shapeHandle)\n  nextUrl.searchParams.set(OFFSET_QUERY_PARAM, lastOffset)\n  nextUrl.searchParams.sort()\n  return nextUrl.toString()\n}\n\n/**\n * Chains an abort controller on an optional source signal's\n * aborted state - if the source signal is aborted, the provided abort\n * controller will also abort\n */\nfunction chainAborter(\n  aborter: AbortController,\n  sourceSignal?: AbortSignal | null\n): {\n  signal: AbortSignal\n  cleanup: () => void\n} {\n  let cleanup = noop\n  if (!sourceSignal) {\n    // no-op, nothing to chain to\n  } else if (sourceSignal.aborted) {\n    // source signal is already aborted, abort immediately\n    aborter.abort()\n  } else {\n    // chain to source signal abort event, and add callback to unlink\n    // the aborter to avoid memory leaks\n    const abortParent = () => aborter.abort()\n    sourceSignal.addEventListener(`abort`, abortParent, {\n      once: true,\n      signal: aborter.signal,\n    })\n    cleanup = () => sourceSignal.removeEventListener(`abort`, abortParent)\n  }\n\n  return {\n    signal: aborter.signal,\n    cleanup,\n  }\n}\n\nfunction noop() {}\n\nfunction getRequestMethod(\n  input: Parameters<typeof fetch>[0],\n  init?: Parameters<typeof fetch>[1]\n): string {\n  if (init?.method) {\n    return init.method.toUpperCase()\n  }\n\n  if (typeof Request !== `undefined` && input instanceof Request) {\n    return input.method.toUpperCase()\n  }\n\n  return `GET`\n}\n","import { SerializedExpression, SerializedOrderByClause } from './types'\nimport { quoteIdentifier } from './column-mapper'\n\n/**\n * Compiles a serialized expression into a SQL string.\n * Applies columnMapper transformations to column references.\n *\n * @param expr - The serialized expression to compile\n * @param columnMapper - Optional function to transform column names (e.g., camelCase to snake_case)\n * @returns The compiled SQL string\n *\n * @example\n * ```typescript\n * const expr = { type: 'ref', column: 'userId' }\n * compileExpression(expr, camelToSnake) // '\"user_id\"'\n * ```\n */\nexport function compileExpression(\n  expr: SerializedExpression,\n  columnMapper?: (col: string) => string\n): string {\n  switch (expr.type) {\n    case `ref`: {\n      // Apply columnMapper, then quote\n      const mappedColumn = columnMapper\n        ? columnMapper(expr.column)\n        : expr.column\n      return quoteIdentifier(mappedColumn)\n    }\n    case `val`:\n      return `$${expr.paramIndex}`\n    case `func`:\n      return compileFunction(expr, columnMapper)\n    default: {\n      // TypeScript exhaustiveness check\n      const _exhaustive: never = expr\n      throw new Error(`Unknown expression type: ${JSON.stringify(_exhaustive)}`)\n    }\n  }\n}\n\n/**\n * Compiles a function expression into SQL.\n */\nfunction compileFunction(\n  expr: { type: `func`; name: string; args: SerializedExpression[] },\n  columnMapper?: (col: string) => string\n): string {\n  const args = expr.args.map((arg) => compileExpression(arg, columnMapper))\n\n  switch (expr.name) {\n    // Binary comparison operators\n    case `eq`:\n      return `${args[0]} = ${args[1]}`\n    case `gt`:\n      return `${args[0]} > ${args[1]}`\n    case `gte`:\n      return `${args[0]} >= ${args[1]}`\n    case `lt`:\n      return `${args[0]} < ${args[1]}`\n    case `lte`:\n      return `${args[0]} <= ${args[1]}`\n\n    // Logical operators\n    case `and`:\n      return args.map((a) => `(${a})`).join(` AND `)\n    case `or`:\n      return args.map((a) => `(${a})`).join(` OR `)\n    case `not`:\n      return `NOT (${args[0]})`\n\n    // Special operators\n    case `in`:\n      return `${args[0]} = ANY(${args[1]})`\n    case `like`:\n      return `${args[0]} LIKE ${args[1]}`\n    case `ilike`:\n      return `${args[0]} ILIKE ${args[1]}`\n    case `isNull`:\n    case `isUndefined`:\n      return `${args[0]} IS NULL`\n\n    // String functions\n    case `upper`:\n      return `UPPER(${args[0]})`\n    case `lower`:\n      return `LOWER(${args[0]})`\n    case `length`:\n      return `LENGTH(${args[0]})`\n    case `concat`:\n      return `CONCAT(${args.join(`, `)})`\n\n    // Other functions\n    case `coalesce`:\n      return `COALESCE(${args.join(`, `)})`\n\n    default:\n      throw new Error(`Unknown function: ${expr.name}`)\n  }\n}\n\n/**\n * Compiles serialized ORDER BY clauses into a SQL string.\n * Applies columnMapper transformations to column references.\n *\n * @param clauses - The serialized ORDER BY clauses to compile\n * @param columnMapper - Optional function to transform column names\n * @returns The compiled SQL ORDER BY string\n *\n * @example\n * ```typescript\n * const clauses = [{ column: 'createdAt', direction: 'desc', nulls: 'first' }]\n * compileOrderBy(clauses, camelToSnake) // '\"created_at\" DESC NULLS FIRST'\n * ```\n */\nexport function compileOrderBy(\n  clauses: SerializedOrderByClause[],\n  columnMapper?: (col: string) => string\n): string {\n  return clauses\n    .map((clause) => {\n      const mappedColumn = columnMapper\n        ? columnMapper(clause.column)\n        : clause.column\n      let sql = quoteIdentifier(mappedColumn)\n      if (clause.direction === `desc`) sql += ` DESC`\n      if (clause.nulls === `first`) sql += ` NULLS FIRST`\n      if (clause.nulls === `last`) sql += ` NULLS LAST`\n      return sql\n    })\n    .join(`, `)\n}\n",null,null,"interface ExpiredShapeCacheEntry {\n  expiredHandle: string\n  lastUsed: number\n}\n\n/**\n * LRU cache for tracking expired shapes with automatic cleanup\n */\nexport class ExpiredShapesCache {\n  private data: Record<string, ExpiredShapeCacheEntry> = {}\n  private max: number = 250\n  private readonly storageKey = `electric_expired_shapes`\n\n  getExpiredHandle(shapeUrl: string): string | null {\n    const entry = this.data[shapeUrl]\n    if (entry) {\n      // Update last used time when accessed\n      entry.lastUsed = Date.now()\n      this.save()\n      return entry.expiredHandle\n    }\n    return null\n  }\n\n  markExpired(shapeUrl: string, handle: string): void {\n    this.data[shapeUrl] = { expiredHandle: handle, lastUsed: Date.now() }\n\n    const keys = Object.keys(this.data)\n    if (keys.length > this.max) {\n      const oldest = keys.reduce((min, k) =>\n        this.data[k].lastUsed < this.data[min].lastUsed ? k : min\n      )\n      delete this.data[oldest]\n    }\n\n    this.save()\n  }\n\n  private save(): void {\n    if (typeof localStorage === `undefined`) return\n    try {\n      localStorage.setItem(this.storageKey, JSON.stringify(this.data))\n    } catch {\n      // Ignore localStorage errors\n    }\n  }\n\n  private load(): void {\n    if (typeof localStorage === `undefined`) return\n    try {\n      const stored = localStorage.getItem(this.storageKey)\n      if (stored) {\n        this.data = JSON.parse(stored)\n      }\n    } catch {\n      // Ignore localStorage errors, start fresh\n      this.data = {}\n    }\n  }\n\n  constructor() {\n    this.load()\n  }\n\n  clear(): void {\n    this.data = {}\n    this.save()\n  }\n\n  delete(shapeUrl: string): void {\n    delete this.data[shapeUrl]\n    this.save()\n  }\n}\n\n// Module-level singleton instance\nexport const expiredShapesCache = new ExpiredShapesCache()\n","interface UpToDateEntry {\n  timestamp: number\n  cursor: string\n}\n\n/**\n * Tracks up-to-date messages to detect when we're replaying cached responses.\n *\n * When a shape receives an up-to-date, we record the timestamp and cursor in localStorage.\n * On page refresh, if we find a recent timestamp (< 60s), we know we'll be replaying\n * cached responses. We suppress their up-to-date notifications until we see a NEW cursor\n * (different from the last recorded one), which indicates fresh data from the server.\n *\n * localStorage writes are throttled to once per 60 seconds to avoid performance issues\n * with frequent updates. In-memory data is always kept current.\n */\nexport class UpToDateTracker {\n  private data: Record<string, UpToDateEntry> = {}\n  private readonly storageKey = `electric_up_to_date_tracker`\n  private readonly cacheTTL = 60_000 // 60s to match typical CDN s-maxage cache duration\n  private readonly maxEntries = 250\n  private readonly writeThrottleMs = 60_000 // Throttle localStorage writes to once per 60s\n  private lastWriteTime = 0\n  private pendingSaveTimer?: ReturnType<typeof setTimeout>\n\n  constructor() {\n    this.load()\n    this.cleanup()\n  }\n\n  /**\n   * Records that a shape received an up-to-date message with a specific cursor.\n   * This timestamp and cursor are used to detect cache replay scenarios.\n   * Updates in-memory immediately, but throttles localStorage writes.\n   */\n  recordUpToDate(shapeKey: string, cursor: string): void {\n    this.data[shapeKey] = {\n      timestamp: Date.now(),\n      cursor,\n    }\n\n    // Implement LRU eviction if we exceed max entries\n    const keys = Object.keys(this.data)\n    if (keys.length > this.maxEntries) {\n      const oldest = keys.reduce((min, k) =>\n        this.data[k].timestamp < this.data[min].timestamp ? k : min\n      )\n      delete this.data[oldest]\n    }\n\n    this.scheduleSave()\n  }\n\n  /**\n   * Schedules a throttled save to localStorage.\n   * Writes immediately if enough time has passed, otherwise schedules for later.\n   */\n  private scheduleSave(): void {\n    const now = Date.now()\n    const timeSinceLastWrite = now - this.lastWriteTime\n\n    if (timeSinceLastWrite >= this.writeThrottleMs) {\n      // Enough time has passed, write immediately\n      this.lastWriteTime = now\n      this.save()\n    } else if (!this.pendingSaveTimer) {\n      // Schedule a write for when the throttle period expires\n      const delay = this.writeThrottleMs - timeSinceLastWrite\n      this.pendingSaveTimer = setTimeout(() => {\n        this.lastWriteTime = Date.now()\n        this.pendingSaveTimer = undefined\n        this.save()\n      }, delay)\n    }\n    // else: a save is already scheduled, no need to do anything\n  }\n\n  /**\n   * Checks if we should enter replay mode for this shape.\n   * Returns the last seen cursor if there's a recent up-to-date (< 60s),\n   * which means we'll likely be replaying cached responses.\n   * Returns null if no recent up-to-date exists.\n   */\n  shouldEnterReplayMode(shapeKey: string): string | null {\n    const entry = this.data[shapeKey]\n    if (!entry) {\n      return null\n    }\n\n    const age = Date.now() - entry.timestamp\n    if (age >= this.cacheTTL) {\n      return null\n    }\n\n    return entry.cursor\n  }\n\n  /**\n   * Cleans up expired entries from the cache.\n   * Called on initialization and can be called periodically.\n   */\n  private cleanup(): void {\n    const now = Date.now()\n    const keys = Object.keys(this.data)\n    let modified = false\n\n    for (const key of keys) {\n      const age = now - this.data[key].timestamp\n      if (age > this.cacheTTL) {\n        delete this.data[key]\n        modified = true\n      }\n    }\n\n    if (modified) {\n      this.save()\n    }\n  }\n\n  private save(): void {\n    if (typeof localStorage === `undefined`) return\n    try {\n      localStorage.setItem(this.storageKey, JSON.stringify(this.data))\n    } catch {\n      // Ignore localStorage errors (quota exceeded, etc.)\n    }\n  }\n\n  private load(): void {\n    if (typeof localStorage === `undefined`) return\n    try {\n      const stored = localStorage.getItem(this.storageKey)\n      if (stored) {\n        this.data = JSON.parse(stored)\n      }\n    } catch {\n      // Ignore localStorage errors, start fresh\n      this.data = {}\n    }\n  }\n\n  /**\n   * Clears all tracked up-to-date timestamps.\n   * Useful for testing or manual cache invalidation.\n   */\n  clear(): void {\n    this.data = {}\n    if (this.pendingSaveTimer) {\n      clearTimeout(this.pendingSaveTimer)\n      this.pendingSaveTimer = undefined\n    }\n    this.save()\n  }\n\n  delete(shapeKey: string): void {\n    delete this.data[shapeKey]\n    this.save()\n  }\n}\n\n// Module-level singleton instance\nexport const upToDateTracker = new UpToDateTracker()\n","import { isVisibleInSnapshot } from './helpers'\nimport { Row, SnapshotMetadata } from './types'\nimport { ChangeMessage } from './types'\n\n/**\n * Tracks active snapshots and filters out duplicate change messages that are already included in snapshots.\n *\n * When requesting a snapshot in changes_only mode, we need to track which transactions were included in the\n * snapshot to avoid processing duplicate changes that arrive via the live stream. This class maintains that\n * tracking state and provides methods to:\n *\n * - Add new snapshots for tracking via addSnapshot()\n * - Remove completed snapshots via removeSnapshot()\n * - Check if incoming changes should be filtered via shouldRejectMessage()\n */\nexport class SnapshotTracker {\n  private activeSnapshots: Map<\n    number,\n    {\n      xmin: bigint\n      xmax: bigint\n      xip_list: bigint[]\n      keys: Set<string>\n      databaseLsn: bigint\n    }\n  > = new Map()\n  private xmaxSnapshots: Map<bigint, Set<number>> = new Map()\n  private snapshotsByDatabaseLsn: Map<bigint, Set<number>> = new Map()\n\n  /**\n   * Add a new snapshot for tracking\n   */\n  addSnapshot(metadata: SnapshotMetadata, keys: Set<string>): void {\n    // If this mark already exists, drop its reverse-index entries first\n    // so they don't linger with the old (xmax, database_lsn) coordinates.\n    this.#detachFromReverseIndexes(metadata.snapshot_mark)\n\n    const xmax = BigInt(metadata.xmax)\n    const databaseLsn = BigInt(metadata.database_lsn)\n    this.activeSnapshots.set(metadata.snapshot_mark, {\n      xmin: BigInt(metadata.xmin),\n      xmax,\n      xip_list: metadata.xip_list.map(BigInt),\n      keys,\n      databaseLsn,\n    })\n    this.#addToSet(this.xmaxSnapshots, xmax, metadata.snapshot_mark)\n    this.#addToSet(\n      this.snapshotsByDatabaseLsn,\n      databaseLsn,\n      metadata.snapshot_mark\n    )\n  }\n\n  /**\n   * Remove a snapshot from tracking\n   */\n  removeSnapshot(snapshotMark: number): void {\n    this.#detachFromReverseIndexes(snapshotMark)\n    this.activeSnapshots.delete(snapshotMark)\n  }\n\n  #detachFromReverseIndexes(snapshotMark: number): void {\n    const existing = this.activeSnapshots.get(snapshotMark)\n    if (!existing) return\n    this.#removeFromSet(this.xmaxSnapshots, existing.xmax, snapshotMark)\n    this.#removeFromSet(\n      this.snapshotsByDatabaseLsn,\n      existing.databaseLsn,\n      snapshotMark\n    )\n  }\n\n  #addToSet(map: Map<bigint, Set<number>>, key: bigint, value: number): void {\n    const set = map.get(key)\n    if (set) {\n      set.add(value)\n    } else {\n      map.set(key, new Set([value]))\n    }\n  }\n\n  #removeFromSet(\n    map: Map<bigint, Set<number>>,\n    key: bigint,\n    value: number\n  ): void {\n    const set = map.get(key)\n    if (!set) return\n    set.delete(value)\n    if (set.size === 0) map.delete(key)\n  }\n\n  /**\n   * Check if a change message should be filtered because its already in an active snapshot\n   * Returns true if the message should be filtered out (not processed)\n   */\n  shouldRejectMessage(message: ChangeMessage<Row<unknown>>): boolean {\n    const txids = message.headers.txids || []\n    if (txids.length === 0) return false\n\n    const xid = Math.max(...txids) // Use the maximum transaction ID\n\n    for (const [xmax, snapshots] of this.xmaxSnapshots.entries()) {\n      if (xid >= xmax) {\n        for (const snapshot of snapshots) {\n          this.removeSnapshot(snapshot)\n        }\n      }\n    }\n\n    return [...this.activeSnapshots.values()].some(\n      (x) => x.keys.has(message.key) && isVisibleInSnapshot(xid, x)\n    )\n  }\n\n  lastSeenUpdate(newDatabaseLsn: bigint): void {\n    for (const [dbLsn, snapshots] of this.snapshotsByDatabaseLsn.entries()) {\n      if (dbLsn <= newDatabaseLsn) {\n        for (const snapshot of snapshots) {\n          this.removeSnapshot(snapshot)\n        }\n      }\n    }\n  }\n}\n","/*\n * Shape stream state machine.\n *\n * Class hierarchy:\n *\n *   ShapeStreamState (abstract base)\n *   ├── ActiveState (abstract — shared field storage & helpers)\n *   │   ├── FetchingState (abstract — shared Initial/Syncing/StaleRetry behavior)\n *   │   │   ├── InitialState\n *   │   │   ├── SyncingState\n *   │   │   └── StaleRetryState\n *   │   ├── LiveState\n *   │   └── ReplayingState\n *   ├── PausedState   (delegates to previousState)\n *   └── ErrorState    (delegates to previousState)\n *\n * State transitions:\n *\n *   Initial ─response─► Syncing ─up-to-date─► Live\n *                            │                  │\n *                            └──stale──► StaleRetry\n *                                           │\n *                            Syncing ◄──response──┘\n *\n *   Any state ─pause─► Paused ─resume─► (previous state)\n *   Any state ─error─► Error  ─retry──► (previous state)\n *   Any state ─markMustRefetch─► Initial (offset reset)\n */\nimport { Offset, Schema } from './types'\nimport {\n  OFFSET_QUERY_PARAM,\n  SHAPE_HANDLE_QUERY_PARAM,\n  LIVE_CACHE_BUSTER_QUERY_PARAM,\n  LIVE_QUERY_PARAM,\n  CACHE_BUSTER_QUERY_PARAM,\n} from './constants'\n\nexport type ShapeStreamStateKind =\n  | `initial`\n  | `syncing`\n  | `live`\n  | `replaying`\n  | `stale-retry`\n  | `paused`\n  | `error`\n\n/**\n * Shared fields carried by all active (non-paused, non-error) states.\n */\nexport interface SharedStateFields {\n  readonly handle?: string\n  readonly offset: Offset\n  readonly schema?: Schema\n  readonly liveCacheBuster: string\n  readonly lastSyncedAt?: number\n}\n\ntype ResponseBaseInput = {\n  status: number\n  responseHandle: string | null\n  responseOffset: Offset | null\n  responseCursor: string | null\n  responseSchema?: Schema\n  expiredHandle?: string | null\n  now: number\n}\n\nexport type ResponseMetadataInput = ResponseBaseInput & {\n  maxStaleCacheRetries: number\n  createCacheBuster: () => string\n}\n\nexport type ResponseMetadataTransition =\n  | { action: `accepted`; state: ShapeStreamState }\n  | { action: `ignored`; state: ShapeStreamState }\n  | {\n      action: `stale-retry`\n      state: ShapeStreamState\n      exceededMaxRetries: boolean\n    }\n\nexport interface MessageBatchInput {\n  hasMessages: boolean\n  hasUpToDateMessage: boolean\n  isSse: boolean\n  upToDateOffset?: Offset\n  now: number\n  currentCursor: string\n}\n\nexport interface MessageBatchTransition {\n  state: ShapeStreamState\n  suppressBatch: boolean\n  becameUpToDate: boolean\n}\n\nexport interface SseCloseInput {\n  connectionDuration: number\n  wasAborted: boolean\n  minConnectionDuration: number\n  maxShortConnections: number\n}\n\nexport interface SseCloseTransition {\n  state: ShapeStreamState\n  fellBackToLongPolling: boolean\n  wasShortConnection: boolean\n}\n\nexport interface UrlParamsContext {\n  isSnapshotRequest: boolean\n  canLongPoll: boolean\n}\n\n// ---------------------------------------------------------------------------\n// Abstract base — shared by ALL states (including Paused/Error)\n// ---------------------------------------------------------------------------\n\n/**\n * Abstract base class for all shape stream states.\n *\n * Each concrete state carries only its relevant fields — there is no shared\n * flat context bag. Transitions create new immutable state objects.\n *\n * `isUpToDate` returns true for LiveState and delegating states wrapping LiveState.\n */\nexport abstract class ShapeStreamState {\n  abstract readonly kind: ShapeStreamStateKind\n\n  // --- Shared field getters (all states expose these) ---\n  abstract get handle(): string | undefined\n  abstract get offset(): Offset\n  abstract get schema(): Schema | undefined\n  abstract get liveCacheBuster(): string\n  abstract get lastSyncedAt(): number | undefined\n\n  // --- Derived booleans ---\n  get isUpToDate(): boolean {\n    return false\n  }\n\n  // --- Per-state field defaults ---\n  get staleCacheBuster(): string | undefined {\n    return undefined\n  }\n  get staleCacheRetryCount(): number {\n    return 0\n  }\n  get sseFallbackToLongPolling(): boolean {\n    return false\n  }\n  get consecutiveShortSseConnections(): number {\n    return 0\n  }\n  get replayCursor(): string | undefined {\n    return undefined\n  }\n\n  // --- Default no-op methods ---\n\n  canEnterReplayMode(): boolean {\n    return false\n  }\n\n  enterReplayMode(_cursor: string): ShapeStreamState {\n    return this\n  }\n\n  shouldUseSse(_opts: {\n    liveSseEnabled: boolean\n    isRefreshing: boolean\n    resumingFromPause: boolean\n  }): boolean {\n    return false\n  }\n\n  handleSseConnectionClosed(_input: SseCloseInput): SseCloseTransition {\n    return {\n      state: this,\n      fellBackToLongPolling: false,\n      wasShortConnection: false,\n    }\n  }\n\n  // --- URL param application ---\n\n  /** Adds state-specific query parameters to the fetch URL. */\n  applyUrlParams(_url: URL, _context: UrlParamsContext): void {}\n\n  // --- Default response/message handlers (Paused/Error never receive these) ---\n\n  handleResponseMetadata(\n    _input: ResponseMetadataInput\n  ): ResponseMetadataTransition {\n    return { action: `ignored`, state: this }\n  }\n\n  handleMessageBatch(_input: MessageBatchInput): MessageBatchTransition {\n    return { state: this, suppressBatch: false, becameUpToDate: false }\n  }\n\n  // --- Universal transitions ---\n\n  /** Returns a new state identical to this one but with the handle changed. */\n  abstract withHandle(handle: string): ShapeStreamState\n\n  pause(): PausedState {\n    return new PausedState(this)\n  }\n\n  toErrorState(error: Error): ErrorState {\n    return new ErrorState(this, error)\n  }\n\n  markMustRefetch(handle?: string): InitialState {\n    return new InitialState({\n      handle,\n      offset: `-1`,\n      liveCacheBuster: ``,\n      lastSyncedAt: this.lastSyncedAt,\n      schema: undefined,\n    })\n  }\n}\n\n// ---------------------------------------------------------------------------\n// ActiveState — intermediate base for all non-paused, non-error states\n// ---------------------------------------------------------------------------\n\n/**\n * Holds shared field storage and provides helpers for response/message\n * handling. All five active states extend this (via FetchingState or directly).\n */\nabstract class ActiveState extends ShapeStreamState {\n  readonly #shared: SharedStateFields\n\n  constructor(shared: SharedStateFields) {\n    super()\n    this.#shared = shared\n  }\n\n  get handle() {\n    return this.#shared.handle\n  }\n  get offset() {\n    return this.#shared.offset\n  }\n  get schema() {\n    return this.#shared.schema\n  }\n  get liveCacheBuster() {\n    return this.#shared.liveCacheBuster\n  }\n  get lastSyncedAt() {\n    return this.#shared.lastSyncedAt\n  }\n\n  /** Expose shared fields to subclasses for spreading into new instances. */\n  protected get currentFields(): SharedStateFields {\n    return this.#shared\n  }\n\n  // --- URL param application ---\n\n  applyUrlParams(url: URL, _context: UrlParamsContext): void {\n    url.searchParams.set(OFFSET_QUERY_PARAM, this.#shared.offset)\n    if (this.#shared.handle) {\n      url.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, this.#shared.handle)\n    }\n  }\n\n  // --- Helpers for subclass handleResponseMetadata implementations ---\n\n  /** Extracts updated SharedStateFields from response headers. */\n  protected parseResponseFields(\n    input: ResponseMetadataInput\n  ): SharedStateFields {\n    const responseHandle = input.responseHandle\n    const handle =\n      responseHandle && responseHandle !== input.expiredHandle\n        ? responseHandle\n        : this.#shared.handle\n    const offset = input.responseOffset ?? this.#shared.offset\n    const liveCacheBuster = input.responseCursor ?? this.#shared.liveCacheBuster\n    const schema = this.#shared.schema ?? input.responseSchema\n    const lastSyncedAt =\n      input.status === 204 ? input.now : this.#shared.lastSyncedAt\n\n    return { handle, offset, schema, liveCacheBuster, lastSyncedAt }\n  }\n\n  /**\n   * Stale detection. Returns a transition if the response is stale,\n   * or null if it is not stale and the caller should proceed normally.\n   */\n  protected checkStaleResponse(\n    input: ResponseMetadataInput\n  ): ResponseMetadataTransition | null {\n    const responseHandle = input.responseHandle\n    const expiredHandle = input.expiredHandle\n\n    if (!responseHandle || responseHandle !== expiredHandle) {\n      return null // not stale\n    }\n\n    // Stale response detected — always enter stale-retry to get a cache buster.\n    // Without a cache buster, the CDN will keep serving the same stale response\n    // and the client loops infinitely (the URL never changes).\n    // currentFields preserves the valid local handle when we already have one.\n    const retryCount = this.staleCacheRetryCount + 1\n    return {\n      action: `stale-retry`,\n      state: new StaleRetryState({\n        ...this.currentFields,\n        staleCacheBuster: input.createCacheBuster(),\n        staleCacheRetryCount: retryCount,\n      }),\n      exceededMaxRetries: retryCount > input.maxStaleCacheRetries,\n    }\n  }\n\n  // --- handleMessageBatch: template method with onUpToDate override point ---\n\n  handleMessageBatch(input: MessageBatchInput): MessageBatchTransition {\n    if (!input.hasMessages || !input.hasUpToDateMessage) {\n      return { state: this, suppressBatch: false, becameUpToDate: false }\n    }\n\n    // Has up-to-date message — compute shared fields for the transition\n    let offset = this.#shared.offset\n    if (input.isSse && input.upToDateOffset) {\n      offset = input.upToDateOffset\n    }\n\n    const shared: SharedStateFields = {\n      handle: this.#shared.handle,\n      offset,\n      schema: this.#shared.schema,\n      liveCacheBuster: this.#shared.liveCacheBuster,\n      lastSyncedAt: input.now,\n    }\n\n    return this.onUpToDate(shared, input)\n  }\n\n  /** Override point for up-to-date handling. Default → LiveState. */\n  protected onUpToDate(\n    shared: SharedStateFields,\n    _input: MessageBatchInput\n  ): MessageBatchTransition {\n    return {\n      state: new LiveState(shared),\n      suppressBatch: false,\n      becameUpToDate: true,\n    }\n  }\n}\n\n// ---------------------------------------------------------------------------\n// FetchingState — Common behavior for Initial/Syncing/StaleRetry\n// ---------------------------------------------------------------------------\n\n/**\n * Captures shared behavior of InitialState, SyncingState, StaleRetryState:\n * - handleResponseMetadata: stale check → parse fields → new SyncingState (or LiveState for 204)\n * - enterReplayMode(cursor) → new ReplayingState\n * - canEnterReplayMode(): boolean — returns true (StaleRetryState overrides to return false,\n *   because entering replay would lose the stale-retry count; see C1 in SPEC.md)\n */\nabstract class FetchingState extends ActiveState {\n  handleResponseMetadata(\n    input: ResponseMetadataInput\n  ): ResponseMetadataTransition {\n    const staleResult = this.checkStaleResponse(input)\n    if (staleResult) return staleResult\n\n    const shared = this.parseResponseFields(input)\n\n    // NOTE: 204s are deprecated, the Electric server should not send these\n    // in latest versions but this is here for backwards compatibility.\n    // A 204 means \"no content, you're caught up\" — transition to live.\n    // Skip SSE detection: a 204 gives no indication SSE will work, and\n    // the 3-attempt fallback cycle adds unnecessary latency.\n    if (input.status === 204) {\n      return {\n        action: `accepted`,\n        state: new LiveState(shared, { sseFallbackToLongPolling: true }),\n      }\n    }\n\n    return { action: `accepted`, state: new SyncingState(shared) }\n  }\n\n  canEnterReplayMode(): boolean {\n    return true\n  }\n\n  enterReplayMode(cursor: string): ReplayingState {\n    return new ReplayingState({\n      ...this.currentFields,\n      replayCursor: cursor,\n    })\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Concrete states\n// ---------------------------------------------------------------------------\n\nexport class InitialState extends FetchingState {\n  readonly kind = `initial` as const\n\n  constructor(shared: SharedStateFields) {\n    super(shared)\n  }\n\n  withHandle(handle: string): InitialState {\n    return new InitialState({ ...this.currentFields, handle })\n  }\n}\n\nexport class SyncingState extends FetchingState {\n  readonly kind = `syncing` as const\n\n  constructor(shared: SharedStateFields) {\n    super(shared)\n  }\n\n  withHandle(handle: string): SyncingState {\n    return new SyncingState({ ...this.currentFields, handle })\n  }\n}\n\nexport class StaleRetryState extends FetchingState {\n  readonly kind = `stale-retry` as const\n  readonly #staleCacheBuster: string\n  readonly #staleCacheRetryCount: number\n\n  constructor(\n    fields: SharedStateFields & {\n      staleCacheBuster: string\n      staleCacheRetryCount: number\n    }\n  ) {\n    const { staleCacheBuster, staleCacheRetryCount, ...shared } = fields\n    super(shared)\n    this.#staleCacheBuster = staleCacheBuster\n    this.#staleCacheRetryCount = staleCacheRetryCount\n  }\n\n  get staleCacheBuster() {\n    return this.#staleCacheBuster\n  }\n  get staleCacheRetryCount() {\n    return this.#staleCacheRetryCount\n  }\n\n  // StaleRetryState must not enter replay mode — it would lose the retry count\n  canEnterReplayMode(): boolean {\n    return false\n  }\n\n  withHandle(handle: string): StaleRetryState {\n    return new StaleRetryState({\n      ...this.currentFields,\n      handle,\n      staleCacheBuster: this.#staleCacheBuster,\n      staleCacheRetryCount: this.#staleCacheRetryCount,\n    })\n  }\n\n  applyUrlParams(url: URL, context: UrlParamsContext): void {\n    super.applyUrlParams(url, context)\n    url.searchParams.set(CACHE_BUSTER_QUERY_PARAM, this.#staleCacheBuster)\n  }\n}\n\nexport class LiveState extends ActiveState {\n  readonly kind = `live` as const\n  readonly #consecutiveShortSseConnections: number\n  readonly #sseFallbackToLongPolling: boolean\n\n  constructor(\n    shared: SharedStateFields,\n    sseState?: {\n      consecutiveShortSseConnections?: number\n      sseFallbackToLongPolling?: boolean\n    }\n  ) {\n    super(shared)\n    this.#consecutiveShortSseConnections =\n      sseState?.consecutiveShortSseConnections ?? 0\n    this.#sseFallbackToLongPolling = sseState?.sseFallbackToLongPolling ?? false\n  }\n\n  get isUpToDate(): boolean {\n    return true\n  }\n\n  get consecutiveShortSseConnections(): number {\n    return this.#consecutiveShortSseConnections\n  }\n\n  get sseFallbackToLongPolling(): boolean {\n    return this.#sseFallbackToLongPolling\n  }\n\n  withHandle(handle: string): LiveState {\n    return new LiveState({ ...this.currentFields, handle }, this.sseState)\n  }\n\n  applyUrlParams(url: URL, context: UrlParamsContext): void {\n    super.applyUrlParams(url, context)\n    // Snapshot requests (with subsetParams) should never use live polling\n    if (!context.isSnapshotRequest) {\n      url.searchParams.set(LIVE_CACHE_BUSTER_QUERY_PARAM, this.liveCacheBuster)\n      if (context.canLongPoll) {\n        url.searchParams.set(LIVE_QUERY_PARAM, `true`)\n      }\n    }\n  }\n\n  private get sseState() {\n    return {\n      consecutiveShortSseConnections: this.#consecutiveShortSseConnections,\n      sseFallbackToLongPolling: this.#sseFallbackToLongPolling,\n    }\n  }\n\n  handleResponseMetadata(\n    input: ResponseMetadataInput\n  ): ResponseMetadataTransition {\n    const staleResult = this.checkStaleResponse(input)\n    if (staleResult) return staleResult\n\n    const shared = this.parseResponseFields(input)\n    return {\n      action: `accepted`,\n      state: new LiveState(shared, this.sseState),\n    }\n  }\n\n  protected onUpToDate(\n    shared: SharedStateFields,\n    _input: MessageBatchInput\n  ): MessageBatchTransition {\n    return {\n      state: new LiveState(shared, this.sseState),\n      suppressBatch: false,\n      becameUpToDate: true,\n    }\n  }\n\n  shouldUseSse(opts: {\n    liveSseEnabled: boolean\n    isRefreshing: boolean\n    resumingFromPause: boolean\n  }): boolean {\n    return (\n      opts.liveSseEnabled &&\n      !opts.isRefreshing &&\n      !opts.resumingFromPause &&\n      !this.#sseFallbackToLongPolling\n    )\n  }\n\n  handleSseConnectionClosed(input: SseCloseInput): SseCloseTransition {\n    let nextConsecutiveShort = this.#consecutiveShortSseConnections\n    let nextFallback = this.#sseFallbackToLongPolling\n    let fellBackToLongPolling = false\n    let wasShortConnection = false\n\n    if (\n      input.connectionDuration < input.minConnectionDuration &&\n      !input.wasAborted\n    ) {\n      wasShortConnection = true\n      nextConsecutiveShort = nextConsecutiveShort + 1\n\n      if (nextConsecutiveShort >= input.maxShortConnections) {\n        nextFallback = true\n        fellBackToLongPolling = true\n      }\n    } else if (input.connectionDuration >= input.minConnectionDuration) {\n      nextConsecutiveShort = 0\n    }\n\n    return {\n      state: new LiveState(this.currentFields, {\n        consecutiveShortSseConnections: nextConsecutiveShort,\n        sseFallbackToLongPolling: nextFallback,\n      }),\n      fellBackToLongPolling,\n      wasShortConnection,\n    }\n  }\n}\n\nexport class ReplayingState extends ActiveState {\n  readonly kind = `replaying` as const\n  readonly #replayCursor: string\n\n  constructor(fields: SharedStateFields & { replayCursor: string }) {\n    const { replayCursor, ...shared } = fields\n    super(shared)\n    this.#replayCursor = replayCursor\n  }\n\n  get replayCursor() {\n    return this.#replayCursor\n  }\n\n  withHandle(handle: string): ReplayingState {\n    return new ReplayingState({\n      ...this.currentFields,\n      handle,\n      replayCursor: this.#replayCursor,\n    })\n  }\n\n  handleResponseMetadata(\n    input: ResponseMetadataInput\n  ): ResponseMetadataTransition {\n    const staleResult = this.checkStaleResponse(input)\n    if (staleResult) return staleResult\n\n    const shared = this.parseResponseFields(input)\n    return {\n      action: `accepted`,\n      state: new ReplayingState({\n        ...shared,\n        replayCursor: this.#replayCursor,\n      }),\n    }\n  }\n\n  protected onUpToDate(\n    shared: SharedStateFields,\n    input: MessageBatchInput\n  ): MessageBatchTransition {\n    // Suppress replayed cache data when cursor has not moved since\n    // the previous session (non-SSE only).\n    const suppressBatch =\n      !input.isSse && this.#replayCursor === input.currentCursor\n    return {\n      state: new LiveState(shared),\n      suppressBatch,\n      becameUpToDate: true,\n    }\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Delegating states (Paused / Error)\n// ---------------------------------------------------------------------------\n\n// Union of all non-delegating states — used to type previousState fields so\n// that PausedState.previousState is never another PausedState, and\n// ErrorState.previousState is never another ErrorState.\nexport type ShapeStreamActiveState =\n  | InitialState\n  | SyncingState\n  | LiveState\n  | ReplayingState\n  | StaleRetryState\n\nexport class PausedState extends ShapeStreamState {\n  readonly kind = `paused` as const\n  readonly previousState: ShapeStreamActiveState | ErrorState\n\n  constructor(previousState: ShapeStreamState) {\n    super()\n    this.previousState = (\n      previousState instanceof PausedState\n        ? previousState.previousState\n        : previousState\n    ) as ShapeStreamActiveState | ErrorState\n  }\n\n  get handle(): string | undefined {\n    return this.previousState.handle\n  }\n  get offset(): Offset {\n    return this.previousState.offset\n  }\n  get schema(): Schema | undefined {\n    return this.previousState.schema\n  }\n  get liveCacheBuster(): string {\n    return this.previousState.liveCacheBuster\n  }\n  get lastSyncedAt(): number | undefined {\n    return this.previousState.lastSyncedAt\n  }\n  get isUpToDate(): boolean {\n    return this.previousState.isUpToDate\n  }\n  get staleCacheBuster(): string | undefined {\n    return this.previousState.staleCacheBuster\n  }\n  get staleCacheRetryCount(): number {\n    return this.previousState.staleCacheRetryCount\n  }\n  get sseFallbackToLongPolling(): boolean {\n    return this.previousState.sseFallbackToLongPolling\n  }\n  get consecutiveShortSseConnections(): number {\n    return this.previousState.consecutiveShortSseConnections\n  }\n  get replayCursor(): string | undefined {\n    return this.previousState.replayCursor\n  }\n\n  handleResponseMetadata(\n    input: ResponseMetadataInput\n  ): ResponseMetadataTransition {\n    const transition = this.previousState.handleResponseMetadata(input)\n    if (transition.action === `accepted`) {\n      return { action: `accepted`, state: new PausedState(transition.state) }\n    }\n    if (transition.action === `ignored`) {\n      return { action: `ignored`, state: this }\n    }\n    if (transition.action === `stale-retry`) {\n      return {\n        action: `stale-retry`,\n        state: new PausedState(transition.state),\n        exceededMaxRetries: transition.exceededMaxRetries,\n      }\n    }\n    const _exhaustive: never = transition\n    throw new Error(\n      `PausedState.handleResponseMetadata: unhandled transition action \"${(_exhaustive as ResponseMetadataTransition).action}\"`\n    )\n  }\n\n  withHandle(handle: string): PausedState {\n    return new PausedState(this.previousState.withHandle(handle))\n  }\n\n  applyUrlParams(url: URL, context: UrlParamsContext): void {\n    this.previousState.applyUrlParams(url, context)\n  }\n\n  pause(): PausedState {\n    return this\n  }\n\n  resume(): ShapeStreamState {\n    return this.previousState\n  }\n}\n\nexport class ErrorState extends ShapeStreamState {\n  readonly kind = `error` as const\n  readonly previousState: ShapeStreamActiveState | PausedState\n  readonly error: Error\n\n  constructor(previousState: ShapeStreamState, error: Error) {\n    super()\n    this.previousState = (\n      previousState instanceof ErrorState\n        ? previousState.previousState\n        : previousState\n    ) as ShapeStreamActiveState | PausedState\n    this.error = error\n  }\n\n  get handle(): string | undefined {\n    return this.previousState.handle\n  }\n  get offset(): Offset {\n    return this.previousState.offset\n  }\n  get schema(): Schema | undefined {\n    return this.previousState.schema\n  }\n  get liveCacheBuster(): string {\n    return this.previousState.liveCacheBuster\n  }\n  get lastSyncedAt(): number | undefined {\n    return this.previousState.lastSyncedAt\n  }\n  get isUpToDate(): boolean {\n    return this.previousState.isUpToDate\n  }\n  get staleCacheBuster(): string | undefined {\n    return this.previousState.staleCacheBuster\n  }\n  get staleCacheRetryCount(): number {\n    return this.previousState.staleCacheRetryCount\n  }\n  get sseFallbackToLongPolling(): boolean {\n    return this.previousState.sseFallbackToLongPolling\n  }\n  get consecutiveShortSseConnections(): number {\n    return this.previousState.consecutiveShortSseConnections\n  }\n  get replayCursor(): string | undefined {\n    return this.previousState.replayCursor\n  }\n\n  withHandle(handle: string): ErrorState {\n    return new ErrorState(this.previousState.withHandle(handle), this.error)\n  }\n\n  applyUrlParams(url: URL, context: UrlParamsContext): void {\n    this.previousState.applyUrlParams(url, context)\n  }\n\n  retry(): ShapeStreamState {\n    return this.previousState\n  }\n\n  reset(handle?: string): InitialState {\n    return this.previousState.markMustRefetch(handle)\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Type alias & factory\n// ---------------------------------------------------------------------------\n\nexport function createInitialState(opts: {\n  offset: Offset\n  handle?: string\n}): InitialState {\n  return new InitialState({\n    handle: opts.handle,\n    offset: opts.offset,\n    liveCacheBuster: ``,\n    lastSyncedAt: undefined,\n    schema: undefined,\n  })\n}\n","/**\n * A set-based counting lock for coordinating multiple pause reasons.\n *\n * Multiple independent subsystems (tab visibility, snapshot requests, etc.)\n * may each need the stream paused. A simple boolean flag or counter can't\n * track *why* the stream is paused, leading to bugs where one subsystem's\n * resume overrides another's pause.\n *\n * PauseLock uses a Set of reason strings. The stream is paused when any\n * reason is held, and only resumes when all reasons are released.\n *\n * @example\n * ```ts\n * const lock = new PauseLock({\n *   onAcquired: () => abortController.abort(),\n *   onReleased: () => startRequestLoop(),\n * })\n *\n * // Tab hidden\n * lock.acquire('visibility')  // → onAcquired fires, stream pauses\n *\n * // Snapshot starts while tab hidden\n * lock.acquire('snapshot-1')  // → no-op, already paused\n *\n * // Snapshot finishes\n * lock.release('snapshot-1')  // → no-op, 'visibility' still held\n *\n * // Tab visible\n * lock.release('visibility')  // → onReleased fires, stream resumes\n * ```\n */\nexport class PauseLock {\n  #holders = new Set<string>()\n  #onAcquired: () => void\n  #onReleased: () => void\n\n  constructor(callbacks: { onAcquired: () => void; onReleased: () => void }) {\n    this.#onAcquired = callbacks.onAcquired\n    this.#onReleased = callbacks.onReleased\n  }\n\n  /**\n   * Acquire the lock for a given reason. Idempotent — acquiring the same\n   * reason twice is a no-op (but logs a warning since it likely indicates\n   * a caller bug).\n   *\n   * Fires `onAcquired` when the first reason is acquired (transition from\n   * unlocked to locked).\n   */\n  acquire(reason: string): void {\n    if (this.#holders.has(reason)) {\n      console.warn(\n        `[Electric] PauseLock: \"${reason}\" already held — ignoring duplicate acquire`\n      )\n      return\n    }\n    const wasUnlocked = this.#holders.size === 0\n    this.#holders.add(reason)\n    if (wasUnlocked) {\n      this.#onAcquired()\n    }\n  }\n\n  /**\n   * Release the lock for a given reason. Releasing a reason that isn't\n   * held logs a warning (likely indicates an acquire/release mismatch).\n   *\n   * Fires `onReleased` when the last reason is released (transition from\n   * locked to unlocked).\n   */\n  release(reason: string): void {\n    if (!this.#holders.delete(reason)) {\n      console.warn(\n        `[Electric] PauseLock: \"${reason}\" not held — ignoring release (possible acquire/release mismatch)`\n      )\n      return\n    }\n    if (this.#holders.size === 0) {\n      this.#onReleased()\n    }\n  }\n\n  /**\n   * Whether the lock is currently held by any reason.\n   */\n  get isPaused(): boolean {\n    return this.#holders.size > 0\n  }\n\n  /**\n   * Check if a specific reason is holding the lock.\n   */\n  isHeldBy(reason: string): boolean {\n    return this.#holders.has(reason)\n  }\n\n  /**\n   * Release all reasons matching a prefix. Does NOT fire `onReleased` —\n   * this is for cleanup/reset paths where the stream state is being\n   * managed separately.\n   *\n   * This preserves reasons with different prefixes (e.g., 'visibility'\n   * is preserved when clearing 'snapshot-*' reasons).\n   */\n  releaseAllMatching(prefix: string): void {\n    for (const reason of this.#holders) {\n      if (reason.startsWith(prefix)) {\n        this.#holders.delete(reason)\n      }\n    }\n  }\n}\n","import {\n  Message,\n  Offset,\n  Schema,\n  Row,\n  MaybePromise,\n  GetExtensions,\n  ChangeMessage,\n  SnapshotMetadata,\n  SubsetParams,\n} from './types'\nimport { MessageParser, Parser, TransformFunction } from './parser'\nimport {\n  ColumnMapper,\n  encodeWhereClause,\n  quoteIdentifier,\n} from './column-mapper'\nimport {\n  getOffset,\n  isUpToDateMessage,\n  isChangeMessage,\n  bigintSafeStringify,\n} from './helpers'\nimport {\n  FetchError,\n  FetchBackoffAbortError,\n  MissingShapeUrlError,\n  InvalidSignalError,\n  MissingShapeHandleError,\n  ReservedParamError,\n  MissingHeadersError,\n  StaleCacheError,\n} from './error'\nimport {\n  BackoffDefaults,\n  BackoffOptions,\n  createFetchWithBackoff,\n  createFetchWithChunkBuffer,\n  createFetchWithConsumedMessages,\n  createFetchWithResponseHeadersCheck,\n} from './fetch'\nimport {\n  CHUNK_LAST_OFFSET_HEADER,\n  LIVE_CACHE_BUSTER_HEADER,\n  LIVE_CACHE_BUSTER_QUERY_PARAM,\n  EXPIRED_HANDLE_QUERY_PARAM,\n  COLUMNS_QUERY_PARAM,\n  LIVE_QUERY_PARAM,\n  OFFSET_QUERY_PARAM,\n  SHAPE_HANDLE_HEADER,\n  SHAPE_HANDLE_QUERY_PARAM,\n  SHAPE_SCHEMA_HEADER,\n  WHERE_QUERY_PARAM,\n  WHERE_PARAMS_PARAM,\n  TABLE_QUERY_PARAM,\n  REPLICA_PARAM,\n  FORCE_DISCONNECT_AND_REFRESH,\n  PAUSE_STREAM,\n  SYSTEM_WAKE,\n  EXPERIMENTAL_LIVE_SSE_QUERY_PARAM,\n  LIVE_SSE_QUERY_PARAM,\n  ELECTRIC_PROTOCOL_QUERY_PARAMS,\n  LOG_MODE_QUERY_PARAM,\n  SUBSET_PARAM_WHERE,\n  SUBSET_PARAM_WHERE_PARAMS,\n  SUBSET_PARAM_LIMIT,\n  SUBSET_PARAM_OFFSET,\n  SUBSET_PARAM_ORDER_BY,\n  SUBSET_PARAM_WHERE_EXPR,\n  SUBSET_PARAM_ORDER_BY_EXPR,\n  CACHE_BUSTER_QUERY_PARAM,\n} from './constants'\nimport { compileExpression, compileOrderBy } from './expression-compiler'\nimport {\n  EventSourceMessage,\n  fetchEventSource,\n} from '@microsoft/fetch-event-source'\nimport { expiredShapesCache } from './expired-shapes-cache'\nimport { upToDateTracker } from './up-to-date-tracker'\nimport { SnapshotTracker } from './snapshot-tracker'\nimport {\n  createInitialState,\n  ErrorState,\n  PausedState,\n  ShapeStreamState,\n} from './shape-stream-state'\nimport { PauseLock } from './pause-lock'\n\nconst RESERVED_PARAMS: Set<ReservedParamKeys> = new Set([\n  LIVE_CACHE_BUSTER_QUERY_PARAM,\n  SHAPE_HANDLE_QUERY_PARAM,\n  LIVE_QUERY_PARAM,\n  OFFSET_QUERY_PARAM,\n  CACHE_BUSTER_QUERY_PARAM,\n])\n\nconst TROUBLESHOOTING_URL = `https://electric-sql.com/docs/guides/troubleshooting`\n\nfunction createCacheBuster(): string {\n  return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n}\n\ntype Replica = `full` | `default`\nexport type LogMode = `changes_only` | `full`\n\n/**\n * PostgreSQL-specific shape parameters that can be provided externally\n */\nexport interface PostgresParams<T extends Row<unknown> = Row> {\n  /** The root table for the shape. Not required if you set the table in your proxy. */\n  table?: string\n\n  /**\n   * The columns to include in the shape.\n   * Must include primary keys, and can only include valid columns.\n   * Defaults to all columns of the type `T`. If provided, must include primary keys, and can only include valid columns.\n\n   */\n  columns?: (keyof T)[]\n\n  /** The where clauses for the shape */\n  where?: string\n\n  /**\n   * Positional where clause paramater values. These will be passed to the server\n   * and will substitute `$i` parameters in the where clause.\n   *\n   * It can be an array (note that positional arguments start at 1, the array will be mapped\n   * accordingly), or an object with keys matching the used positional parameters in the where clause.\n   *\n   * If where clause is `id = $1 or id = $2`, params must have keys `\"1\"` and `\"2\"`, or be an array with length 2.\n   */\n  params?: Record<`${number}`, string> | string[]\n\n  /**\n   * If `replica` is `default` (the default) then Electric will only send the\n   * changed columns in an update.\n   *\n   * If it's `full` Electric will send the entire row with both changed and\n   * unchanged values. `old_value` will also be present on update messages,\n   * containing the previous value for changed columns.\n   *\n   * Setting `replica` to `full` will result in higher bandwidth\n   * usage and so is not generally recommended.\n   */\n  replica?: Replica\n}\ntype SerializableParamValue = string | string[] | Record<string, string>\ntype ParamValue =\n  | SerializableParamValue\n  | (() => SerializableParamValue | Promise<SerializableParamValue>)\n\n/**\n * External params type - what users provide.\n * Excludes reserved parameters to prevent dynamic variations that could cause stream shape changes.\n */\nexport type ExternalParamsRecord<T extends Row<unknown> = Row> = {\n  [K in string]: ParamValue | undefined\n} & Partial<PostgresParams<T>> & { [K in ReservedParamKeys]?: never }\n\ntype ReservedParamKeys =\n  | typeof LIVE_CACHE_BUSTER_QUERY_PARAM\n  | typeof SHAPE_HANDLE_QUERY_PARAM\n  | typeof LIVE_QUERY_PARAM\n  | typeof OFFSET_QUERY_PARAM\n  | typeof CACHE_BUSTER_QUERY_PARAM\n  | `subset__${string}`\n\n/**\n * External headers type - what users provide.\n * Allows string or function values for any header.\n */\nexport type ExternalHeadersRecord = {\n  [key: string]: string | (() => string | Promise<string>)\n}\n\n/**\n * Internal params type - used within the library.\n * All values are converted to strings.\n */\ntype InternalParamsRecord = {\n  [K in string as K extends ReservedParamKeys ? never : K]:\n    | string\n    | Record<string, string>\n}\n\n/**\n * Helper function to resolve a function or value to its final value\n */\nexport async function resolveValue<T>(\n  value: T | (() => T | Promise<T>)\n): Promise<T> {\n  if (typeof value === `function`) {\n    return (value as () => T | Promise<T>)()\n  }\n  return value\n}\n\n/**\n * Helper function to convert external params to internal format\n */\nasync function toInternalParams(\n  params: ExternalParamsRecord<Row>\n): Promise<InternalParamsRecord> {\n  const entries = Object.entries(params)\n  const resolvedEntries = await Promise.all(\n    entries.map(async ([key, value]) => {\n      if (value === undefined) return [key, undefined]\n      const resolvedValue = await resolveValue(value)\n      return [\n        key,\n        Array.isArray(resolvedValue) ? resolvedValue.join(`,`) : resolvedValue,\n      ]\n    })\n  )\n\n  return Object.fromEntries(\n    resolvedEntries.filter(([_, value]) => value !== undefined)\n  )\n}\n\n/**\n * Helper function to resolve headers\n */\nasync function resolveHeaders(\n  headers?: ExternalHeadersRecord\n): Promise<Record<string, string>> {\n  if (!headers) return {}\n\n  const entries = Object.entries(headers)\n  const resolvedEntries = await Promise.all(\n    entries.map(async ([key, value]) => [key, await resolveValue(value)])\n  )\n\n  return Object.fromEntries(resolvedEntries)\n}\n\ntype RetryOpts = {\n  params?: ExternalParamsRecord\n  headers?: ExternalHeadersRecord\n}\n\ntype ShapeStreamErrorHandler = (\n  error: Error\n) => void | RetryOpts | Promise<void | RetryOpts>\n\n/**\n * Options for constructing a ShapeStream.\n */\nexport interface ShapeStreamOptions<T = never> {\n  /**\n   * The full URL to where the Shape is served. This can either be the Electric server\n   * directly or a proxy. E.g. for a local Electric instance, you might set `http://localhost:3000/v1/shape`\n   */\n  url: string\n\n  /**\n   * The \"offset\" on the shape log. This is typically not set as the ShapeStream\n   * will handle this automatically. A common scenario where you might pass an offset\n   * is if you're maintaining a local cache of the log. If you've gone offline\n   * and are re-starting a ShapeStream to catch-up to the latest state of the Shape,\n   * you'd pass in the last offset and shapeHandle you'd seen from the Electric server\n   * so it knows at what point in the shape to catch you up from.\n   */\n  offset?: Offset\n\n  /**\n   * Similar to `offset`, this isn't typically used unless you're maintaining\n   * a cache of the shape log.\n   */\n  handle?: string\n\n  /**\n   * HTTP headers to attach to requests made by the client.\n   * Values can be strings or functions (sync or async) that return strings.\n   * Function values are resolved in parallel when needed, making this useful\n   * for authentication tokens or other dynamic headers.\n   */\n  headers?: ExternalHeadersRecord\n\n  /**\n   * Additional request parameters to attach to the URL.\n   * Values can be strings, string arrays, or functions (sync or async) that return these types.\n   * Function values are resolved in parallel when needed, making this useful\n   * for user-specific parameters or dynamic filters.\n   *\n   * These will be merged with Electric's standard parameters.\n   * Note: You cannot use Electric's reserved parameter names\n   * (offset, handle, live, cursor).\n   *\n   * PostgreSQL-specific options like table, where, columns, and replica\n   * should be specified here.\n   */\n  params?: ExternalParamsRecord\n\n  /**\n   * Automatically fetch updates to the Shape. If you just want to sync the current\n   * shape and stop, pass false.\n   */\n  subscribe?: boolean\n\n  /**\n   * @deprecated No longer experimental, use {@link liveSse} instead.\n   */\n  experimentalLiveSse?: boolean\n\n  /**\n   * Use Server-Sent Events (SSE) for live updates.\n   */\n  liveSse?: boolean\n\n  /**\n   * Initial data loading mode\n   */\n  log?: LogMode\n\n  signal?: AbortSignal\n  fetchClient?: typeof fetch\n  backoffOptions?: BackoffOptions\n  parser?: Parser<T>\n\n  /**\n   * Function to transform rows after parsing (e.g., for encryption, type coercion).\n   * Applied to data received from Electric.\n   *\n   * **Note**: If you're using `transformer` solely for column name transformation\n   * (e.g., snake_case → camelCase), consider using `columnMapper` instead, which\n   * provides bidirectional transformation and automatically encodes WHERE clauses.\n   *\n   * **Execution order** when both are provided:\n   * 1. `columnMapper.decode` runs first (renames columns)\n   * 2. `transformer` runs second (transforms values)\n   *\n   * @example\n   * ```typescript\n   * // For column renaming only - use columnMapper\n   * import { snakeCamelMapper } from '@electric-sql/client'\n   * const stream = new ShapeStream({ columnMapper: snakeCamelMapper() })\n   * ```\n   *\n   * @example\n   * ```typescript\n   * // For value transformation (encryption, etc.) - use transformer\n   * const stream = new ShapeStream({\n   *   transformer: (row) => ({\n   *     ...row,\n   *     encrypted_field: decrypt(row.encrypted_field)\n   *   })\n   * })\n   * ```\n   *\n   * @example\n   * ```typescript\n   * // Use both together\n   * const stream = new ShapeStream({\n   *   columnMapper: snakeCamelMapper(), // Runs first: renames columns\n   *   transformer: (row) => ({         // Runs second: transforms values\n   *     ...row,\n   *     encryptedData: decrypt(row.encryptedData)\n   *   })\n   * })\n   * ```\n   */\n  transformer?: TransformFunction<T>\n\n  /**\n   * Bidirectional column name mapper for transforming between database column names\n   * (e.g., snake_case) and application column names (e.g., camelCase).\n   *\n   * The mapper handles both:\n   * - **Decoding**: Database → Application (applied to query results)\n   * - **Encoding**: Application → Database (applied to WHERE clauses)\n   *\n   * @example\n   * ```typescript\n   * // Most common case: snake_case ↔ camelCase\n   * import { snakeCamelMapper } from '@electric-sql/client'\n   *\n   * const stream = new ShapeStream({\n   *   url: 'http://localhost:3000/v1/shape',\n   *   params: { table: 'todos' },\n   *   columnMapper: snakeCamelMapper()\n   * })\n   * ```\n   *\n   * @example\n   * ```typescript\n   * // Custom mapping\n   * import { createColumnMapper } from '@electric-sql/client'\n   *\n   * const stream = new ShapeStream({\n   *   columnMapper: createColumnMapper({\n   *     user_id: 'userId',\n   *     project_id: 'projectId',\n   *     created_at: 'createdAt'\n   *   })\n   * })\n   * ```\n   */\n  columnMapper?: ColumnMapper\n\n  /**\n   * A function for handling shapestream errors.\n   *\n   * **Automatic retries**: The client automatically retries 5xx server errors, network\n   * errors, and 429 rate limits with exponential backoff. The `onError` callback is\n   * only invoked after these automatic retries are exhausted, or for non-retryable\n   * errors like 4xx client errors.\n   *\n   * When not provided, non-retryable errors will be thrown and syncing will stop.\n   *\n   * **Return value behavior**:\n   * - Return an **object** (RetryOpts or empty `{}`) to retry syncing:\n   *   - `{}` - Retry with the same params and headers\n   *   - `{ params }` - Retry with modified params\n   *   - `{ headers }` - Retry with modified headers (e.g., refreshed auth token)\n   *   - `{ params, headers }` - Retry with both modified\n   * - Return **void** or **undefined** to stop the stream permanently\n   *\n   * **Important**: If you want syncing to continue after an error (e.g., to retry\n   * on network failures), you MUST return at least an empty object `{}`. Simply\n   * logging the error and returning nothing will stop syncing.\n   *\n   * Supports async functions that return `Promise<void | RetryOpts>`.\n   *\n   * @example\n   * ```typescript\n   * // Retry on network errors, stop on others\n   * onError: (error) => {\n   *   console.error('Stream error:', error)\n   *   if (error instanceof FetchError && error.status >= 500) {\n   *     return {} // Retry with same params\n   *   }\n   *   // Return void to stop on other errors\n   * }\n   * ```\n   *\n   * @example\n   * ```typescript\n   * // Refresh auth token on 401\n   * onError: async (error) => {\n   *   if (error instanceof FetchError && error.status === 401) {\n   *     const newToken = await refreshAuthToken()\n   *     return { headers: { Authorization: `Bearer ${newToken}` } }\n   *   }\n   *   return {} // Retry other errors\n   * }\n   * ```\n   */\n  onError?: ShapeStreamErrorHandler\n\n  /**\n   * HTTP method to use for subset snapshot requests (`requestSnapshot`/`fetchSnapshot`).\n   *\n   * - `'GET'` (default): Sends subset params as URL query parameters. May fail with\n   *   HTTP 414 errors for large queries with many parameters.\n   * - `'POST'`: Sends subset params in request body as JSON. Recommended for queries\n   *   with large parameter lists (e.g., `WHERE id = ANY($1)` with hundreds of IDs).\n   *\n   * This can be overridden per-request by passing `method` in the subset params.\n   *\n   * @example\n   * ```typescript\n   * const stream = new ShapeStream({\n   *   url: 'http://localhost:3000/v1/shape',\n   *   params: { table: 'items' },\n   *   subsetMethod: 'POST', // Use POST for all subset requests\n   * })\n   * ```\n   */\n  subsetMethod?: `GET` | `POST`\n}\n\nexport interface ShapeStreamInterface<T extends Row<unknown> = Row> {\n  subscribe(\n    callback: (\n      messages: Message<T>[]\n    ) => MaybePromise<void> | { columns?: (keyof T)[] },\n    onError?: (error: FetchError | Error) => void\n  ): () => void\n  unsubscribeAll(): void\n\n  isLoading(): boolean\n  lastSyncedAt(): number | undefined\n  lastSynced(): number\n  isConnected(): boolean\n  hasStarted(): boolean\n\n  isUpToDate: boolean\n  lastOffset: Offset\n  shapeHandle?: string\n  error?: unknown\n  mode: LogMode\n\n  forceDisconnectAndRefresh(): Promise<void>\n\n  requestSnapshot(params: SubsetParams): Promise<{\n    metadata: SnapshotMetadata\n    data: Array<Message<T>>\n  }>\n\n  fetchSnapshot(opts: SubsetParams): Promise<{\n    metadata: SnapshotMetadata\n    data: Array<ChangeMessage<T>>\n  }>\n}\n\n/**\n * Creates a canonical shape key from a URL excluding only Electric protocol parameters\n */\nexport function canonicalShapeKey(url: URL): string {\n  const cleanUrl = new URL(url.origin + url.pathname)\n\n  // Copy all params except Electric protocol ones that vary between requests.\n  // Use append() so duplicate keys (e.g. ?table=a&table=b) are preserved.\n  for (const [key, value] of url.searchParams) {\n    if (!ELECTRIC_PROTOCOL_QUERY_PARAMS.includes(key)) {\n      cleanUrl.searchParams.append(key, value)\n    }\n  }\n\n  cleanUrl.searchParams.sort()\n  return cleanUrl.toString()\n}\n\n/**\n * Reads updates to a shape from Electric using HTTP requests and long polling or\n * Server-Sent Events (SSE).\n * Notifies subscribers when new messages come in. Doesn't maintain any history of the\n * log but does keep track of the offset position and is the best way\n * to consume the HTTP `GET /v1/shape` api.\n *\n * @constructor\n * @param {ShapeStreamOptions} options - configure the shape stream\n * @example\n * Register a callback function to subscribe to the messages.\n * ```\n * const stream = new ShapeStream(options)\n * stream.subscribe(messages => {\n *   // messages is 1 or more row updates\n * })\n * ```\n *\n * To use Server-Sent Events (SSE) for real-time updates:\n * ```\n * const stream = new ShapeStream({\n *   url: `http://localhost:3000/v1/shape`,\n *   liveSse: true\n * })\n * ```\n *\n * To abort the stream, abort the `signal`\n * passed in via the `ShapeStreamOptions`.\n * ```\n * const aborter = new AbortController()\n * const issueStream = new ShapeStream({\n *   url: `${BASE_URL}/${table}`\n *   subscribe: true,\n *   signal: aborter.signal,\n * })\n * // Later...\n * aborter.abort()\n * ```\n */\n\nexport class ShapeStream<T extends Row<unknown> = Row>\n  implements ShapeStreamInterface<T>\n{\n  static readonly Replica = {\n    FULL: `full` as Replica,\n    DEFAULT: `default` as Replica,\n  }\n\n  readonly options: ShapeStreamOptions<GetExtensions<T>>\n  #error: unknown = null\n\n  readonly #fetchClient: typeof fetch\n  readonly #sseFetchClient: typeof fetch\n  readonly #messageParser: MessageParser<T>\n\n  readonly #subscribers = new Map<\n    object,\n    [\n      (messages: Message<T>[]) => MaybePromise<void>,\n      ((error: Error) => void) | undefined,\n    ]\n  >()\n\n  #started = false\n  #syncState: ShapeStreamState\n  #connected: boolean = false\n  #mode: LogMode\n  #onError?: ShapeStreamErrorHandler\n  #requestAbortController?: AbortController\n  #refreshCount = 0\n  #snapshotCounter = 0\n\n  get #isRefreshing(): boolean {\n    return this.#refreshCount > 0\n  }\n  #tickPromise?: Promise<void>\n  #tickPromiseResolver?: () => void\n  #tickPromiseRejecter?: (reason?: unknown) => void\n  #messageChain = Promise.resolve<void[]>([]) // promise chain for incoming messages\n  #snapshotTracker = new SnapshotTracker()\n  #pauseLock: PauseLock\n  #currentFetchUrl?: URL // Current fetch URL for computing shape key\n  #lastSseConnectionStartTime?: number\n  #minSseConnectionDuration = 1000 // Minimum expected SSE connection duration (1 second)\n  #maxShortSseConnections = 3 // Fall back to long polling after this many short connections\n  #sseBackoffBaseDelay = 100 // Base delay for exponential backoff (ms)\n  #sseBackoffMaxDelay = 5000 // Maximum delay cap (ms)\n  #unsubscribeFromVisibilityChanges?: () => void\n  #unsubscribeFromWakeDetection?: () => void\n  #maxStaleCacheRetries = 3\n  // Fast-loop detection: track recent non-live requests to detect tight retry\n  // loops caused by proxy/CDN misconfiguration or stale client-side caches\n  #recentRequestEntries: Array<{ timestamp: number; offset: string }> = []\n  #fastLoopWindowMs = 500\n  #fastLoopThreshold = 5\n  #fastLoopBackoffBaseMs = 100\n  #fastLoopBackoffMaxMs = 5_000\n  #fastLoopConsecutiveCount = 0\n  #fastLoopMaxCount = 5\n  #pendingRequestShapeCacheBuster?: string\n  #maxSnapshotRetries = 5\n  #expiredShapeRecoveryKey: string | null = null\n  #pendingSelfHealCheck: { shapeKey: string; staleHandle: string } | null = null\n  #consecutiveErrorRetries = 0\n  #maxConsecutiveErrorRetries = 50\n\n  constructor(options: ShapeStreamOptions<GetExtensions<T>>) {\n    this.options = { subscribe: true, ...options }\n    validateOptions(this.options)\n    this.#syncState = createInitialState({\n      offset: this.options.offset ?? `-1`,\n      handle: this.options.handle,\n    })\n\n    this.#pauseLock = new PauseLock({\n      onAcquired: () => {\n        this.#syncState = this.#syncState.pause()\n        if (this.#started) {\n          this.#requestAbortController?.abort(PAUSE_STREAM)\n        }\n      },\n      onReleased: () => {\n        if (!this.#started) return\n        if (this.options.signal?.aborted) return\n        // Don't transition syncState here — let #requestShape handle\n        // the PausedState→previous transition so it can detect\n        // resumingFromPause and avoid live long-polling.\n        this.#start().catch(() => {\n          // Errors from #start are handled internally via onError.\n          // This catch prevents unhandled promise rejection in Node/Bun.\n        })\n      },\n    })\n\n    // Build transformer chain: columnMapper.decode -> transformer\n    // columnMapper transforms column names, transformer transforms values\n    let transformer: TransformFunction<GetExtensions<T>> | undefined\n\n    if (options.columnMapper) {\n      const applyColumnMapper = (\n        row: Row<GetExtensions<T>>\n      ): Row<GetExtensions<T>> => {\n        const result: Record<string, unknown> = {}\n        for (const [dbKey, value] of Object.entries(row)) {\n          const appKey = options.columnMapper!.decode(dbKey)\n          result[appKey] = value\n        }\n        return result as Row<GetExtensions<T>>\n      }\n\n      transformer = options.transformer\n        ? (row: Row<GetExtensions<T>>) =>\n            options.transformer!(applyColumnMapper(row))\n        : applyColumnMapper\n    } else {\n      transformer = options.transformer\n    }\n\n    this.#messageParser = new MessageParser<T>(options.parser, transformer)\n\n    this.#onError = this.options.onError\n    this.#mode = this.options.log ?? `full`\n\n    const baseFetchClient =\n      options.fetchClient ??\n      ((...args: Parameters<typeof fetch>) => fetch(...args))\n\n    const backOffOpts = {\n      ...(options.backoffOptions ?? BackoffDefaults),\n      onFailedAttempt: () => {\n        this.#connected = false\n        options.backoffOptions?.onFailedAttempt?.()\n      },\n    }\n    const fetchWithBackoffClient = createFetchWithBackoff(\n      baseFetchClient,\n      backOffOpts\n    )\n\n    this.#sseFetchClient = createFetchWithResponseHeadersCheck(\n      createFetchWithChunkBuffer(fetchWithBackoffClient)\n    )\n\n    this.#fetchClient = createFetchWithConsumedMessages(this.#sseFetchClient)\n\n    this.#subscribeToVisibilityChanges()\n  }\n\n  get shapeHandle() {\n    return this.#syncState.handle\n  }\n\n  get error() {\n    return this.#error\n  }\n\n  get isUpToDate() {\n    return this.#syncState.isUpToDate\n  }\n\n  get lastOffset() {\n    return this.#syncState.offset\n  }\n\n  get mode() {\n    return this.#mode\n  }\n\n  async #start(): Promise<void> {\n    this.#started = true\n    this.#subscribeToWakeDetection()\n\n    try {\n      await this.#requestShape()\n    } catch (err) {\n      this.#error = err\n      if (err instanceof Error) {\n        this.#syncState = this.#syncState.toErrorState(err)\n      }\n\n      // Check if onError handler wants to retry\n      if (this.#onError) {\n        const retryOpts = await this.#onError(err as Error)\n        // Guard against null (typeof null === \"object\" in JavaScript)\n        const isRetryable = !(err instanceof MissingHeadersError)\n        if (retryOpts && typeof retryOpts === `object` && isRetryable) {\n          // Update params/headers but don't reset offset\n          // We want to continue from where we left off, not refetch everything\n          if (retryOpts.params) {\n            // Merge new params with existing params to preserve other parameters\n            this.options.params = {\n              ...(this.options.params ?? {}),\n              ...retryOpts.params,\n            }\n          }\n\n          if (retryOpts.headers) {\n            // Merge new headers with existing headers to preserve other headers\n            this.options.headers = {\n              ...(this.options.headers ?? {}),\n              ...retryOpts.headers,\n            }\n          }\n\n          // Bound the onError retry loop to prevent unbounded retries\n          this.#consecutiveErrorRetries++\n          if (\n            this.#consecutiveErrorRetries > this.#maxConsecutiveErrorRetries\n          ) {\n            console.warn(\n              `[Electric] onError retry loop exhausted after ${this.#maxConsecutiveErrorRetries} consecutive retries. ` +\n                `The error was never resolved by the onError handler. ` +\n                `Error: ${err instanceof Error ? err.message : String(err)}`,\n              new Error(`stack trace`)\n            )\n            if (err instanceof Error) {\n              this.#sendErrorToSubscribers(err)\n            }\n            this.#teardown()\n            return\n          }\n\n          // Clear the error since we're retrying\n          this.#error = null\n          if (this.#syncState instanceof ErrorState) {\n            this.#syncState = this.#syncState.retry()\n          }\n          this.#fastLoopConsecutiveCount = 0\n          this.#recentRequestEntries = []\n\n          // Restart from current offset\n          this.#started = false\n          return this.#start()\n        }\n        // onError returned void, meaning it doesn't want to retry\n        // This is an unrecoverable error, notify subscribers\n        if (err instanceof Error) {\n          this.#sendErrorToSubscribers(err)\n        }\n        this.#teardown()\n        return\n      }\n\n      // No onError handler provided, this is an unrecoverable error\n      // Notify subscribers and throw\n      if (err instanceof Error) {\n        this.#sendErrorToSubscribers(err)\n      }\n      this.#teardown()\n      throw err\n    }\n\n    this.#teardown()\n  }\n\n  #teardown() {\n    this.#connected = false\n    this.#tickPromiseRejecter?.()\n    this.#unsubscribeFromWakeDetection?.()\n  }\n\n  async #requestShape(requestShapeCacheBuster?: string): Promise<void> {\n    // ErrorState should never reach the request loop — re-throw so\n    // #start's catch block can route it through onError properly.\n    if (this.#syncState instanceof ErrorState) {\n      throw this.#syncState.error\n    }\n\n    const activeCacheBuster =\n      requestShapeCacheBuster ?? this.#pendingRequestShapeCacheBuster\n\n    if (this.#pauseLock.isPaused) {\n      if (activeCacheBuster) {\n        this.#pendingRequestShapeCacheBuster = activeCacheBuster\n      }\n      return\n    }\n\n    if (\n      !this.options.subscribe &&\n      (this.options.signal?.aborted || this.#syncState.isUpToDate)\n    ) {\n      return\n    }\n\n    // Only check for fast loops on non-live requests; live polling is expected to be rapid\n    if (!this.#syncState.isUpToDate) {\n      await this.#checkFastLoop()\n    } else {\n      this.#fastLoopConsecutiveCount = 0\n      this.#recentRequestEntries = []\n    }\n\n    let resumingFromPause = false\n    if (this.#syncState instanceof PausedState) {\n      resumingFromPause = true\n      this.#syncState = this.#syncState.resume()\n    }\n\n    const { url, signal } = this.options\n    const { fetchUrl, requestHeaders } = await this.#constructUrl(\n      url,\n      resumingFromPause\n    )\n\n    if (activeCacheBuster) {\n      fetchUrl.searchParams.set(CACHE_BUSTER_QUERY_PARAM, activeCacheBuster)\n      fetchUrl.searchParams.sort()\n    }\n    const abortListener = await this.#createAbortListener(signal)\n    const requestAbortController = this.#requestAbortController! // we know that it is not undefined because it is set by `this.#createAbortListener`\n\n    // Re-check after async setup — the lock may have been acquired\n    // during URL construction or abort controller creation (e.g., by\n    // requestSnapshot), when the abort controller didn't exist yet.\n    if (this.#pauseLock.isPaused) {\n      if (abortListener && signal) {\n        signal.removeEventListener(`abort`, abortListener)\n      }\n      if (activeCacheBuster) {\n        this.#pendingRequestShapeCacheBuster = activeCacheBuster\n      }\n      this.#requestAbortController = undefined\n      return\n    }\n\n    this.#pendingRequestShapeCacheBuster = undefined\n\n    try {\n      await this.#fetchShape({\n        fetchUrl,\n        requestAbortController,\n        headers: requestHeaders,\n        resumingFromPause,\n      })\n    } catch (e) {\n      const abortReason = requestAbortController.signal.reason\n      const isRestartAbort =\n        requestAbortController.signal.aborted &&\n        (abortReason === FORCE_DISCONNECT_AND_REFRESH ||\n          abortReason === SYSTEM_WAKE)\n\n      if (\n        (e instanceof FetchError || e instanceof FetchBackoffAbortError) &&\n        isRestartAbort\n      ) {\n        return this.#requestShape()\n      }\n\n      if (e instanceof FetchBackoffAbortError) {\n        return // interrupted\n      }\n\n      if (e instanceof StaleCacheError) {\n        // Two paths throw StaleCacheError:\n        // 1. Normal stale-retry: response handle matched expired handle,\n        //    #staleCacheBuster set to bypass CDN cache on next request.\n        // 2. Self-healing: stale retries exhausted, expired entry cleared,\n        //    stream reset — retry without expired_handle param.\n        return this.#requestShape()\n      }\n\n      if (!(e instanceof FetchError)) throw e // should never happen\n\n      if (e.status == 409) {\n        // Upon receiving a 409, start from scratch with the newly\n        // provided shape handle (if present). An unconditional cache\n        // buster ensures the retry URL is always unique regardless of\n        // whether the server returns a new, same, or missing handle.\n\n        // Store the current shape URL as expired to avoid future 409s\n        if (this.#syncState.handle) {\n          const shapeKey = canonicalShapeKey(fetchUrl)\n          expiredShapesCache.markExpired(shapeKey, this.#syncState.handle)\n        }\n\n        const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER]\n        if (!newShapeHandle) {\n          console.warn(\n            `[Electric] Received 409 response without a shape handle header. ` +\n              `This likely indicates a proxy or CDN stripping required headers.`,\n            new Error(`stack trace`)\n          )\n        }\n        const nextRequestShapeCacheBuster = createCacheBuster()\n        this.#reset(newShapeHandle)\n\n        // Notify subscribers that data must be re-fetched so they can\n        // clear accumulated state (e.g., Shape clears its row map).\n        // We publish a synthetic control message rather than the raw 409\n        // body to avoid delivering stale data rows to subscribers.\n        await this.#publish([{ headers: { control: `must-refetch` } }])\n\n        return this.#requestShape(nextRequestShapeCacheBuster)\n      } else {\n        // errors that have reached this point are not actionable without\n        // additional user input, such as 400s or failures to read the\n        // body of a response, so we exit the loop and let #start handle it\n        // Note: We don't notify subscribers here because onError might recover\n        throw e\n      }\n    } finally {\n      if (abortListener && signal) {\n        signal.removeEventListener(`abort`, abortListener)\n      }\n      this.#requestAbortController = undefined\n    }\n\n    this.#tickPromiseResolver?.()\n    return this.#requestShape()\n  }\n\n  /**\n   * Detects tight retry loops (e.g., from stale client-side caches or\n   * proxy/CDN misconfiguration) and attempts recovery. On first detection,\n   * clears client-side caches (in-memory and localStorage) and resets the\n   * stream to fetch from scratch.\n   * If the loop persists, applies exponential backoff and eventually throws.\n   */\n  async #checkFastLoop(): Promise<void> {\n    const now = Date.now()\n    const currentOffset = this.#syncState.offset\n\n    this.#recentRequestEntries = this.#recentRequestEntries.filter(\n      (e) => now - e.timestamp < this.#fastLoopWindowMs\n    )\n    this.#recentRequestEntries.push({ timestamp: now, offset: currentOffset })\n\n    // Only flag as a fast loop if requests are stuck at the same offset.\n    // Normal rapid syncing advances the offset with each response.\n    const sameOffsetCount = this.#recentRequestEntries.filter(\n      (e) => e.offset === currentOffset\n    ).length\n\n    if (sameOffsetCount < this.#fastLoopThreshold) return\n\n    this.#fastLoopConsecutiveCount++\n\n    if (this.#fastLoopConsecutiveCount >= this.#fastLoopMaxCount) {\n      throw new FetchError(\n        502,\n        undefined,\n        undefined,\n        {},\n        this.options.url,\n        `Client is stuck in a fast retry loop ` +\n          `(${this.#fastLoopThreshold} requests in ${this.#fastLoopWindowMs}ms at the same offset, ` +\n          `repeated ${this.#fastLoopMaxCount} times). ` +\n          `Client-side caches were cleared automatically on first detection, but the loop persists. ` +\n          `This usually indicates a proxy or CDN misconfiguration. ` +\n          `Common causes:\\n` +\n          `  - Proxy is not including query parameters (handle, offset) in its cache key\\n` +\n          `  - CDN is serving stale 409 responses\\n` +\n          `  - Proxy is stripping required Electric headers from responses\\n` +\n          `For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL}`\n      )\n    }\n\n    if (this.#fastLoopConsecutiveCount === 1) {\n      console.warn(\n        `[Electric] Detected fast retry loop ` +\n          `(${this.#fastLoopThreshold} requests in ${this.#fastLoopWindowMs}ms at the same offset). ` +\n          `Clearing client-side caches and resetting stream to recover. ` +\n          `If this persists, check that your proxy includes all query parameters ` +\n          `(especially 'handle' and 'offset') in its cache key, ` +\n          `and that required Electric headers are forwarded to the client. ` +\n          `For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL}`,\n        new Error(`stack trace`)\n      )\n\n      if (this.#currentFetchUrl) {\n        const shapeKey = canonicalShapeKey(this.#currentFetchUrl)\n        expiredShapesCache.delete(shapeKey)\n        upToDateTracker.delete(shapeKey)\n      } else {\n        expiredShapesCache.clear()\n        upToDateTracker.clear()\n      }\n      this.#reset()\n      this.#recentRequestEntries = []\n      return\n    }\n\n    // Exponential backoff with full jitter\n    const maxDelay = Math.min(\n      this.#fastLoopBackoffMaxMs,\n      this.#fastLoopBackoffBaseMs * Math.pow(2, this.#fastLoopConsecutiveCount)\n    )\n    const delayMs = Math.floor(Math.random() * maxDelay)\n\n    await new Promise((resolve) => setTimeout(resolve, delayMs))\n\n    this.#recentRequestEntries = []\n  }\n\n  async #constructUrl(\n    url: string,\n    resumingFromPause: boolean,\n    subsetParams?: SubsetParams\n  ) {\n    // Resolve headers and params in parallel\n    const [requestHeaders, params] = await Promise.all([\n      resolveHeaders(this.options.headers),\n      this.options.params\n        ? toInternalParams(convertWhereParamsToObj(this.options.params))\n        : undefined,\n    ])\n\n    // Validate params after resolution\n    if (params) validateParams(params)\n\n    const fetchUrl = new URL(url)\n\n    // Add PostgreSQL-specific parameters\n    if (params) {\n      if (params.table) setQueryParam(fetchUrl, TABLE_QUERY_PARAM, params.table)\n      if (params.where && typeof params.where === `string`) {\n        const encodedWhere = encodeWhereClause(\n          params.where,\n          this.options.columnMapper?.encode\n        )\n        setQueryParam(fetchUrl, WHERE_QUERY_PARAM, encodedWhere)\n      }\n      if (params.columns) {\n        // Get original columns array from options (before toInternalParams converted to string)\n        const originalColumns = await resolveValue(this.options.params?.columns)\n        if (Array.isArray(originalColumns)) {\n          // Apply columnMapper encoding if present\n          let encodedColumns = originalColumns.map(String)\n          if (this.options.columnMapper) {\n            encodedColumns = encodedColumns.map(\n              this.options.columnMapper.encode\n            )\n          }\n          // Quote each column name to handle special characters (commas, etc.)\n          const serializedColumns = encodedColumns\n            .map(quoteIdentifier)\n            .join(`,`)\n          setQueryParam(fetchUrl, COLUMNS_QUERY_PARAM, serializedColumns)\n        } else {\n          // Fallback: columns was already a string\n          setQueryParam(fetchUrl, COLUMNS_QUERY_PARAM, params.columns)\n        }\n      }\n      if (params.replica) setQueryParam(fetchUrl, REPLICA_PARAM, params.replica)\n      if (params.params)\n        setQueryParam(fetchUrl, WHERE_PARAMS_PARAM, params.params)\n\n      // Add any remaining custom parameters\n      const customParams = { ...params }\n      delete customParams.table\n      delete customParams.where\n      delete customParams.columns\n      delete customParams.replica\n      delete customParams.params\n\n      for (const [key, value] of Object.entries(customParams)) {\n        setQueryParam(fetchUrl, key, value)\n      }\n    }\n\n    if (subsetParams) {\n      // Prefer structured expressions when available (allows proper columnMapper application)\n      // Fall back to legacy string format for backwards compatibility\n      if (subsetParams.whereExpr) {\n        // Compile structured expression with columnMapper applied\n        const compiledWhere = compileExpression(\n          subsetParams.whereExpr,\n          this.options.columnMapper?.encode\n        )\n        setQueryParam(fetchUrl, SUBSET_PARAM_WHERE, compiledWhere)\n        // Also send the structured expression for servers that support it\n        fetchUrl.searchParams.set(\n          SUBSET_PARAM_WHERE_EXPR,\n          JSON.stringify(subsetParams.whereExpr)\n        )\n      } else if (subsetParams.where && typeof subsetParams.where === `string`) {\n        // Legacy string format (no columnMapper applied to already-compiled SQL)\n        const encodedWhere = encodeWhereClause(\n          subsetParams.where,\n          this.options.columnMapper?.encode\n        )\n        setQueryParam(fetchUrl, SUBSET_PARAM_WHERE, encodedWhere)\n      }\n\n      if (subsetParams.params)\n        // Serialize params as JSON to keep the parameter name constant for proxy configs\n        fetchUrl.searchParams.set(\n          SUBSET_PARAM_WHERE_PARAMS,\n          bigintSafeStringify(subsetParams.params)\n        )\n      if (subsetParams.limit !== undefined)\n        setQueryParam(fetchUrl, SUBSET_PARAM_LIMIT, subsetParams.limit)\n      if (subsetParams.offset !== undefined)\n        setQueryParam(fetchUrl, SUBSET_PARAM_OFFSET, subsetParams.offset)\n\n      // Prefer structured ORDER BY expressions when available\n      if (subsetParams.orderByExpr) {\n        // Compile structured ORDER BY with columnMapper applied\n        const compiledOrderBy = compileOrderBy(\n          subsetParams.orderByExpr,\n          this.options.columnMapper?.encode\n        )\n        setQueryParam(fetchUrl, SUBSET_PARAM_ORDER_BY, compiledOrderBy)\n        // Also send the structured expression for servers that support it\n        fetchUrl.searchParams.set(\n          SUBSET_PARAM_ORDER_BY_EXPR,\n          JSON.stringify(subsetParams.orderByExpr)\n        )\n      } else if (\n        subsetParams.orderBy &&\n        typeof subsetParams.orderBy === `string`\n      ) {\n        // Legacy string format\n        const encodedOrderBy = encodeWhereClause(\n          subsetParams.orderBy,\n          this.options.columnMapper?.encode\n        )\n        setQueryParam(fetchUrl, SUBSET_PARAM_ORDER_BY, encodedOrderBy)\n      }\n    }\n\n    // Add state-specific parameters (offset, handle, live cache busters, etc.)\n    this.#syncState.applyUrlParams(fetchUrl, {\n      isSnapshotRequest: subsetParams !== undefined,\n      // Don't long-poll when resuming from pause or refreshing — avoids\n      // a 20s hold during which `isConnected` would be false\n      canLongPoll: !this.#isRefreshing && !resumingFromPause,\n    })\n    fetchUrl.searchParams.set(LOG_MODE_QUERY_PARAM, this.#mode)\n\n    // Add cache buster for shapes known to be expired to prevent 409s\n    const shapeKey = canonicalShapeKey(fetchUrl)\n    const expiredHandle = expiredShapesCache.getExpiredHandle(shapeKey)\n    if (expiredHandle) {\n      fetchUrl.searchParams.set(EXPIRED_HANDLE_QUERY_PARAM, expiredHandle)\n    }\n\n    // sort query params in-place for stable URLs and improved cache hits\n    fetchUrl.searchParams.sort()\n\n    return {\n      fetchUrl,\n      requestHeaders,\n    }\n  }\n\n  async #createAbortListener(signal?: AbortSignal) {\n    // Create a new AbortController for this request\n    this.#requestAbortController = new AbortController()\n\n    // If user provided a signal, listen to it and pass on the reason for the abort\n    if (signal) {\n      const abortListener = () => {\n        this.#requestAbortController?.abort(signal.reason)\n      }\n\n      signal.addEventListener(`abort`, abortListener, { once: true })\n\n      if (signal.aborted) {\n        // If the signal is already aborted, abort the request immediately\n        this.#requestAbortController?.abort(signal.reason)\n      }\n\n      return abortListener\n    }\n  }\n\n  /**\n   * Processes response metadata (headers, status) and updates sync state.\n   * Returns `true` if the response body should be processed by the caller,\n   * or `false` if the response was ignored (stale) and the body should be skipped.\n   * Throws on stale-retry (to trigger a retry with cache buster).\n   */\n  async #onInitialResponse(response: Response): Promise<boolean> {\n    const { headers, status } = response\n    const shapeHandle = headers.get(SHAPE_HANDLE_HEADER)\n    const shapeKey = this.#currentFetchUrl\n      ? canonicalShapeKey(this.#currentFetchUrl)\n      : null\n    const expiredHandle = shapeKey\n      ? expiredShapesCache.getExpiredHandle(shapeKey)\n      : null\n\n    // If this response is the first one after a self-healing retry, check\n    // whether the proxy/CDN returned the exact handle we just marked expired.\n    // If so, the client is about to accept stale data silently — loudly warn\n    // so operators can detect and fix the proxy misconfiguration.\n    if (this.#pendingSelfHealCheck) {\n      const { shapeKey: healedKey, staleHandle } = this.#pendingSelfHealCheck\n      this.#pendingSelfHealCheck = null\n      if (shapeKey === healedKey && shapeHandle === staleHandle) {\n        console.warn(\n          `[Electric] Self-healing retry received the same handle \"${staleHandle}\" that was just marked expired. ` +\n            `This means your proxy/CDN is serving a stale cached response and ignoring cache-buster query params. ` +\n            `The client will proceed with this stale data to avoid a permanent failure, but it may be out of date until the cache refreshes. ` +\n            `Fix: configure your proxy/CDN to include all query parameters (especially 'handle' and 'offset') in its cache key. ` +\n            `For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL}`,\n          new Error(`stack trace`)\n        )\n      }\n    }\n\n    const transition = this.#syncState.handleResponseMetadata({\n      status,\n      responseHandle: shapeHandle,\n      responseOffset: headers.get(CHUNK_LAST_OFFSET_HEADER) as Offset | null,\n      responseCursor: headers.get(LIVE_CACHE_BUSTER_HEADER),\n      responseSchema: getSchemaFromHeaders(headers),\n      expiredHandle,\n      now: Date.now(),\n      maxStaleCacheRetries: this.#maxStaleCacheRetries,\n      createCacheBuster,\n    })\n\n    this.#syncState = transition.state\n\n    // Clear recovery guard on 204 (no-content), since the empty body means\n    // #onMessages won't run to clear it via the up-to-date path.\n    if (status === 204) {\n      this.#expiredShapeRecoveryKey = null\n    }\n\n    if (transition.action === `accepted` && status === 204) {\n      this.#consecutiveErrorRetries = 0\n    }\n\n    if (transition.action === `stale-retry`) {\n      // Cancel the response body to release the connection before retrying.\n      await response.body?.cancel()\n      if (transition.exceededMaxRetries) {\n        if (shapeKey) {\n          // Clear the expired entry — keeping it only poisons future sessions.\n          expiredShapesCache.delete(shapeKey)\n\n          // Try one self-healing retry per shape: reset the stream and\n          // retry without the expired_handle param. Since handles are never\n          // reused (see SPEC.md S0), the fresh response will have a new\n          // handle and won't trigger stale detection.\n          if (this.#expiredShapeRecoveryKey !== shapeKey) {\n            console.warn(\n              `[Electric] Stale cache retries exhausted (${this.#maxStaleCacheRetries} attempts). ` +\n                `Clearing expired handle entry and attempting self-healing retry without the expired_handle parameter. ` +\n                `For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL}`,\n              new Error(`stack trace`)\n            )\n            this.#expiredShapeRecoveryKey = shapeKey\n            // Arm a post-self-heal check: if the next response comes back\n            // with the same handle we just marked expired, the proxy/CDN is\n            // still serving stale data and we'll warn loudly instead of\n            // accepting it silently.\n            if (shapeHandle) {\n              this.#pendingSelfHealCheck = {\n                shapeKey,\n                staleHandle: shapeHandle,\n              }\n            }\n            this.#reset()\n            throw new StaleCacheError(\n              `Expired handle entry evicted for self-healing retry`\n            )\n          }\n        }\n        throw new FetchError(\n          502,\n          undefined,\n          undefined,\n          {},\n          this.#currentFetchUrl?.toString() ?? ``,\n          `CDN continues serving stale cached responses after ${this.#maxStaleCacheRetries} retry attempts. ` +\n            `This indicates a severe proxy/CDN misconfiguration. ` +\n            `Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key. ` +\n            `For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL}`\n        )\n      }\n      console.warn(\n        `[Electric] Received stale cached response with expired shape handle. ` +\n          `This should not happen and indicates a proxy/CDN caching misconfiguration. ` +\n          `The response contained handle \"${shapeHandle}\" which was previously marked as expired. ` +\n          `Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key. ` +\n          `For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL} ` +\n          `Retrying with a random cache buster to bypass the stale cache (attempt ${this.#syncState.staleCacheRetryCount}/${this.#maxStaleCacheRetries}).`,\n        new Error(`stack trace`)\n      )\n      throw new StaleCacheError(\n        `Received stale cached response with expired handle \"${shapeHandle}\". ` +\n          `This indicates a proxy/CDN caching misconfiguration. ` +\n          `Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key.`\n      )\n    }\n\n    if (transition.action === `ignored`) {\n      console.warn(\n        `[Electric] Response was ignored by state \"${this.#syncState.kind}\". ` +\n          `The response body will be skipped. ` +\n          `This may indicate a proxy/CDN caching issue or a client state machine bug.`,\n        new Error(`stack trace`)\n      )\n      return false\n    }\n\n    return true\n  }\n\n  async #onMessages(batch: Array<Message<T>>, isSseMessage = false) {\n    if (!Array.isArray(batch)) {\n      console.warn(\n        `[Electric] #onMessages called with non-array argument (${typeof batch}). ` +\n          `This is a client bug — please report it.`,\n        new Error(`stack trace`)\n      )\n      return\n    }\n    if (batch.length === 0) return\n\n    this.#consecutiveErrorRetries = 0\n\n    const lastMessage = batch[batch.length - 1]\n    const hasUpToDateMessage = isUpToDateMessage(lastMessage)\n    const upToDateOffset = hasUpToDateMessage\n      ? getOffset(lastMessage)\n      : undefined\n\n    const transition = this.#syncState.handleMessageBatch({\n      hasMessages: true,\n      hasUpToDateMessage,\n      isSse: isSseMessage,\n      upToDateOffset,\n      now: Date.now(),\n      currentCursor: this.#syncState.liveCacheBuster,\n    })\n    this.#syncState = transition.state\n\n    if (hasUpToDateMessage) {\n      if (transition.suppressBatch) {\n        return\n      }\n\n      if (this.#currentFetchUrl) {\n        const shapeKey = canonicalShapeKey(this.#currentFetchUrl)\n        upToDateTracker.recordUpToDate(\n          shapeKey,\n          this.#syncState.liveCacheBuster\n        )\n        this.#expiredShapeRecoveryKey = null\n      }\n    }\n\n    // Filter messages using snapshot tracker\n    const messagesToProcess = batch.filter((message) => {\n      if (isChangeMessage(message)) {\n        return !this.#snapshotTracker.shouldRejectMessage(message)\n      }\n      return true // Always process control messages\n    })\n\n    await this.#publish(messagesToProcess)\n  }\n\n  /**\n   * Fetches the shape from the server using either long polling or SSE.\n   * Upon receiving a successful response, the #onInitialResponse method is called.\n   * Afterwards, the #onMessages method is called for all the incoming updates.\n   * @param opts - The options for the request.\n   * @returns A promise that resolves when the request is complete (i.e. the long poll receives a response or the SSE connection is closed).\n   */\n  async #fetchShape(opts: {\n    fetchUrl: URL\n    requestAbortController: AbortController\n    headers: Record<string, string>\n    resumingFromPause?: boolean\n  }): Promise<void> {\n    // Store current fetch URL for shape key computation\n    this.#currentFetchUrl = opts.fetchUrl\n\n    // Check if we should enter replay mode (replaying cached responses)\n    // This happens when we're starting fresh (offset=-1 or before first up-to-date)\n    // and there's a recent up-to-date in localStorage (< 60s)\n    if (!this.#syncState.isUpToDate && this.#syncState.canEnterReplayMode()) {\n      const shapeKey = canonicalShapeKey(opts.fetchUrl)\n      const lastSeenCursor = upToDateTracker.shouldEnterReplayMode(shapeKey)\n      if (lastSeenCursor) {\n        // Enter replay mode and store the last seen cursor\n        this.#syncState = this.#syncState.enterReplayMode(lastSeenCursor)\n      }\n    }\n\n    const useSse = this.options.liveSse ?? this.options.experimentalLiveSse\n    if (\n      this.#syncState.shouldUseSse({\n        liveSseEnabled: !!useSse,\n        isRefreshing: this.#isRefreshing,\n        resumingFromPause: !!opts.resumingFromPause,\n      })\n    ) {\n      opts.fetchUrl.searchParams.set(EXPERIMENTAL_LIVE_SSE_QUERY_PARAM, `true`)\n      opts.fetchUrl.searchParams.set(LIVE_SSE_QUERY_PARAM, `true`)\n      return this.#requestShapeSSE(opts)\n    }\n\n    return this.#requestShapeLongPoll(opts)\n  }\n\n  async #requestShapeLongPoll(opts: {\n    fetchUrl: URL\n    requestAbortController: AbortController\n    headers: Record<string, string>\n  }): Promise<void> {\n    const { fetchUrl, requestAbortController, headers } = opts\n    const response = await this.#fetchClient(fetchUrl.toString(), {\n      signal: requestAbortController.signal,\n      headers,\n    })\n\n    this.#connected = true\n    const shouldProcessBody = await this.#onInitialResponse(response)\n    if (!shouldProcessBody) return\n\n    const schema = this.#syncState.schema! // we know that it is not undefined because it is set by `this.#onInitialResponse`\n    const res = await response.text()\n    const messages = res || `[]`\n    const batch = this.#messageParser.parse<Array<Message<T>>>(messages, schema)\n\n    if (!Array.isArray(batch)) {\n      const preview = bigintSafeStringify(batch)?.slice(0, 200)\n      throw new FetchError(\n        response.status,\n        `Received non-array response body from shape endpoint. ` +\n          `This may indicate a proxy or CDN is returning an unexpected response. ` +\n          `Expected a JSON array, got ${typeof batch}: ${preview}`,\n        undefined,\n        Object.fromEntries(response.headers.entries()),\n        fetchUrl.toString()\n      )\n    }\n\n    await this.#onMessages(batch)\n  }\n\n  async #requestShapeSSE(opts: {\n    fetchUrl: URL\n    requestAbortController: AbortController\n    headers: Record<string, string>\n  }): Promise<void> {\n    const { fetchUrl, requestAbortController, headers } = opts\n    const fetch = this.#sseFetchClient\n\n    // Track when the SSE connection starts\n    this.#lastSseConnectionStartTime = Date.now()\n\n    // Add Accept header for SSE requests\n    const sseHeaders = {\n      ...headers,\n      Accept: `text/event-stream`,\n    }\n\n    let ignoredStaleResponse = false\n    try {\n      let buffer: Array<Message<T>> = []\n      await fetchEventSource(fetchUrl.toString(), {\n        headers: sseHeaders,\n        fetch,\n        onopen: async (response: Response) => {\n          this.#connected = true\n          const shouldProcessBody = await this.#onInitialResponse(response)\n          if (!shouldProcessBody) {\n            ignoredStaleResponse = true\n            throw new Error(`stale response ignored`)\n          }\n        },\n        onmessage: (event: EventSourceMessage) => {\n          if (event.data) {\n            // event.data is a single JSON object\n            const schema = this.#syncState.schema! // we know that it is not undefined because it is set in onopen when we call this.#onInitialResponse\n            const message = this.#messageParser.parse<Message<T>>(\n              event.data,\n              schema\n            )\n            buffer.push(message)\n\n            if (isUpToDateMessage(message)) {\n              // Flush the buffer on up-to-date message.\n              // Ensures that we only process complete batches of operations.\n              this.#onMessages(buffer, true)\n              buffer = []\n            }\n          }\n        },\n        onerror: (error: Error) => {\n          // rethrow to close the SSE connection\n          throw error\n        },\n        signal: requestAbortController.signal,\n      })\n    } catch (error) {\n      if (ignoredStaleResponse) {\n        // Stale response was ignored in onopen — let the fetch loop retry\n        return\n      }\n      if (requestAbortController.signal.aborted) {\n        // An abort during SSE stream parsing produces a raw AbortError\n        // instead of going through createFetchWithBackoff -- wrap it so\n        // #start handles it correctly.\n        throw new FetchBackoffAbortError()\n      }\n      // Re-throw known Electric errors so the caller can handle them\n      // (e.g., 409 shape rotation, stale cache retry, missing headers).\n      // Other errors (body parsing, SSE protocol failures, null body)\n      // are SSE connection failures handled by the fallback mechanism\n      // in the finally block below.\n      if (\n        error instanceof FetchError ||\n        error instanceof StaleCacheError ||\n        error instanceof MissingHeadersError\n      ) {\n        throw error\n      }\n    } finally {\n      // Check if the SSE connection closed too quickly\n      // This can happen when responses are cached or when the proxy/server\n      // is misconfigured for SSE and closes the connection immediately\n      const connectionDuration = Date.now() - this.#lastSseConnectionStartTime!\n      const wasAborted = requestAbortController.signal.aborted\n\n      const transition = this.#syncState.handleSseConnectionClosed({\n        connectionDuration,\n        wasAborted,\n        minConnectionDuration: this.#minSseConnectionDuration,\n        maxShortConnections: this.#maxShortSseConnections,\n      })\n      this.#syncState = transition.state\n\n      if (transition.fellBackToLongPolling) {\n        console.warn(\n          `[Electric] SSE connections are closing immediately (possibly due to proxy buffering or misconfiguration). ` +\n            `Falling back to long polling. ` +\n            `Your proxy must support streaming SSE responses (not buffer the complete response). ` +\n            `Configuration: Nginx add 'X-Accel-Buffering: no', Caddy add 'flush_interval -1' to reverse_proxy. ` +\n            `Note: Do NOT disable caching entirely - Electric uses cache headers to enable request collapsing for efficiency.`,\n          new Error(`stack trace`)\n        )\n      } else if (transition.wasShortConnection) {\n        // Exponential backoff with full jitter: random(0, min(cap, base * 2^attempt))\n        const maxDelay = Math.min(\n          this.#sseBackoffMaxDelay,\n          this.#sseBackoffBaseDelay *\n            Math.pow(2, this.#syncState.consecutiveShortSseConnections)\n        )\n        const delayMs = Math.floor(Math.random() * maxDelay)\n        await new Promise((resolve) => setTimeout(resolve, delayMs))\n      }\n    }\n  }\n\n  subscribe(\n    callback: (messages: Message<T>[]) => MaybePromise<void>,\n    onError: (error: Error) => void = () => {}\n  ) {\n    const subscriptionId = {}\n\n    this.#subscribers.set(subscriptionId, [callback, onError])\n    if (!this.#started) this.#start()\n\n    return () => {\n      this.#subscribers.delete(subscriptionId)\n    }\n  }\n\n  unsubscribeAll(): void {\n    this.#subscribers.clear()\n    this.#unsubscribeFromVisibilityChanges?.()\n    this.#unsubscribeFromWakeDetection?.()\n  }\n\n  /** Unix time at which we last synced. Undefined until first successful up-to-date. */\n  lastSyncedAt(): number | undefined {\n    return this.#syncState.lastSyncedAt\n  }\n\n  /** Time elapsed since last sync (in ms). Infinity if we did not yet sync. */\n  lastSynced(): number {\n    if (this.#syncState.lastSyncedAt === undefined) return Infinity\n    return Date.now() - this.#syncState.lastSyncedAt\n  }\n\n  /** Indicates if we are connected to the Electric sync service. */\n  isConnected(): boolean {\n    return this.#connected\n  }\n\n  /** True during initial fetch. False afterwards.  */\n  isLoading(): boolean {\n    return !this.#syncState.isUpToDate\n  }\n\n  hasStarted(): boolean {\n    return this.#started\n  }\n\n  isPaused(): boolean {\n    return this.#pauseLock.isPaused\n  }\n\n  /** Await the next tick of the request loop */\n  async #nextTick() {\n    if (this.#pauseLock.isPaused) {\n      throw new Error(\n        `Cannot wait for next tick while PauseLock is held — this would deadlock because the request loop is paused`\n      )\n    }\n    if (this.#tickPromise) {\n      return this.#tickPromise\n    }\n    this.#tickPromise = new Promise((resolve, reject) => {\n      this.#tickPromiseResolver = resolve\n      this.#tickPromiseRejecter = reject\n    })\n    this.#tickPromise.finally(() => {\n      this.#tickPromise = undefined\n      this.#tickPromiseResolver = undefined\n      this.#tickPromiseRejecter = undefined\n    })\n    return this.#tickPromise\n  }\n\n  /**\n   * Refreshes the shape stream.\n   * This preemptively aborts any ongoing long poll and reconnects without\n   * long polling, ensuring that the stream receives an up to date message with the\n   * latest LSN from Postgres at that point in time.\n   */\n  async forceDisconnectAndRefresh(): Promise<void> {\n    this.#refreshCount++\n    try {\n      if (\n        this.#syncState.isUpToDate &&\n        !this.#requestAbortController?.signal.aborted\n      ) {\n        // If we are \"up to date\", any current request will be a \"live\" request\n        // and needs to be aborted\n        this.#requestAbortController?.abort(FORCE_DISCONNECT_AND_REFRESH)\n      }\n      await this.#nextTick()\n    } finally {\n      this.#refreshCount--\n    }\n  }\n\n  async #publish(messages: Message<T>[]): Promise<void[]> {\n    // We process messages asynchronously\n    // but SSE's `onmessage` handler is synchronous.\n    // We use a promise chain to ensure that the handlers\n    // execute sequentially in the order the messages were received.\n    this.#messageChain = this.#messageChain.then(() =>\n      Promise.all(\n        Array.from(this.#subscribers.values()).map(async ([callback, __]) => {\n          try {\n            await callback(messages)\n          } catch (err) {\n            queueMicrotask(() => {\n              throw err\n            })\n          }\n        })\n      )\n    )\n\n    return this.#messageChain\n  }\n\n  #sendErrorToSubscribers(error: Error) {\n    this.#subscribers.forEach(([_, errorFn]) => {\n      errorFn?.(error)\n    })\n  }\n\n  #hasBrowserVisibilityAPI(): boolean {\n    return (\n      typeof document === `object` &&\n      typeof document.hidden === `boolean` &&\n      typeof document.addEventListener === `function`\n    )\n  }\n\n  #subscribeToVisibilityChanges() {\n    if (this.#hasBrowserVisibilityAPI()) {\n      const visibilityHandler = () => {\n        if (document.hidden) {\n          this.#pauseLock.acquire(`visibility`)\n        } else {\n          this.#pauseLock.release(`visibility`)\n        }\n      }\n\n      document.addEventListener(`visibilitychange`, visibilityHandler)\n\n      // Store cleanup function to remove the event listener\n      this.#unsubscribeFromVisibilityChanges = () => {\n        document.removeEventListener(`visibilitychange`, visibilityHandler)\n        this.#unsubscribeFromVisibilityChanges = undefined\n      }\n    }\n  }\n\n  /**\n   * Detects system wake from sleep using timer gap detection.\n   * When the system sleeps, setInterval timers are paused. On wake,\n   * the elapsed wall-clock time since the last tick will be much larger\n   * than the interval period, indicating the system was asleep.\n   *\n   * Only active in non-browser environments (Bun, Node.js) where\n   * `document.visibilitychange` is not available. In browsers,\n   * `#subscribeToVisibilityChanges` handles this instead. Without wake\n   * detection, in-flight HTTP requests (long-poll or SSE) may hang until\n   * the OS TCP timeout.\n   */\n  #subscribeToWakeDetection() {\n    if (this.#hasBrowserVisibilityAPI()) return\n    if (this.#unsubscribeFromWakeDetection) return\n\n    const INTERVAL_MS = 2_000\n    const WAKE_THRESHOLD_MS = 4_000\n\n    let lastTickTime = Date.now()\n\n    const timer = setInterval(() => {\n      const now = Date.now()\n      const elapsed = now - lastTickTime\n      lastTickTime = now\n\n      if (elapsed > INTERVAL_MS + WAKE_THRESHOLD_MS) {\n        if (!this.#pauseLock.isPaused && this.#requestAbortController) {\n          this.#refreshCount++\n          this.#requestAbortController.abort(SYSTEM_WAKE)\n          // Wake handler is synchronous (setInterval callback) so we can't\n          // use try/finally + await like forceDisconnectAndRefresh. Instead,\n          // decrement via queueMicrotask — safe because the abort triggers\n          // #requestShape to re-run, which reads #isRefreshing synchronously\n          // before the microtask fires.\n          queueMicrotask(() => {\n            this.#refreshCount--\n          })\n        }\n      }\n    }, INTERVAL_MS)\n\n    // Ensure the timer doesn't prevent the process from exiting\n    if (typeof timer === `object` && `unref` in timer) {\n      timer.unref()\n    }\n\n    this.#unsubscribeFromWakeDetection = () => {\n      clearInterval(timer)\n      this.#unsubscribeFromWakeDetection = undefined\n    }\n  }\n\n  /**\n   * Resets the state of the stream, optionally with a provided\n   * shape handle\n   */\n  #reset(handle?: string) {\n    this.#syncState = this.#syncState.markMustRefetch(handle)\n    this.#connected = false\n    // releaseAllMatching intentionally doesn't fire onReleased — every caller\n    // (#requestShape's 409 handler, #checkFastLoop, and stale-retry\n    // self-healing in #onInitialResponse) runs inside the active stream loop,\n    // so the stream is already active and doesn't need a resume signal.\n    this.#pauseLock.releaseAllMatching(`snapshot`)\n  }\n\n  /**\n   * Request a snapshot for subset of data and inject it into the subscribed data stream.\n   *\n   * Only available when mode is `changes_only`.\n   * Returns the insertion point & the data, but more importantly injects the data\n   * into the subscribed data stream. Returned value is unlikely to be useful for the caller,\n   * unless the caller has complicated additional logic.\n   *\n   * Data will be injected in a way that's also tracking further incoming changes, and it'll\n   * skip the ones that are already in the snapshot.\n   *\n   * @param opts - The options for the snapshot request.\n   * @returns The metadata and the data for the snapshot.\n   */\n  async requestSnapshot(opts: SubsetParams): Promise<{\n    metadata: SnapshotMetadata\n    data: Array<ChangeMessage<T>>\n  }> {\n    if (this.#mode === `full`) {\n      throw new Error(\n        `Snapshot requests are not supported in ${this.#mode} mode, as the consumer is guaranteed to observe all data`\n      )\n    }\n    // Start the stream if not started — fire-and-forget like subscribe() does.\n    // We must NOT await #start() because it runs the full request loop. The\n    // PauseLock acquire below will abort the in-flight request, and the\n    // re-check guard in #requestShape handles the race.\n    if (!this.#started) {\n      this.#start().catch(() => {})\n    }\n\n    const snapshotReason = `snapshot-${++this.#snapshotCounter}`\n\n    this.#pauseLock.acquire(snapshotReason)\n\n    // Warn if the snapshot holds the pause lock for too long — this likely\n    // indicates a hung fetch or leaked lock. Visibility pauses are\n    // intentionally long-lived so the warning lives here, not in PauseLock.\n    const snapshotWarnTimer = setTimeout(() => {\n      console.warn(\n        `[Electric] Snapshot \"${snapshotReason}\" has held the pause lock for 30s — ` +\n          `possible hung request or leaked lock. ` +\n          `Current holders: ${[...new Set([snapshotReason])].join(`, `)}`,\n        new Error(`stack trace`)\n      )\n    }, 30_000)\n\n    try {\n      const { metadata, data, responseOffset, responseHandle } =\n        await this.fetchSnapshot(opts)\n\n      const dataWithEndBoundary = (data as Array<Message<T>>).concat([\n        { headers: { control: `snapshot-end`, ...metadata } },\n        { headers: { control: `subset-end`, ...opts } },\n      ])\n\n      this.#snapshotTracker.addSnapshot(\n        metadata,\n        new Set(data.map((message) => message.key))\n      )\n      this.#onMessages(dataWithEndBoundary, false)\n\n      // On cold start the stream's offset is still at \"now\". Advance it\n      // to the snapshot's position so no updates are missed in between.\n      if (responseOffset !== null || responseHandle !== null) {\n        const transition = this.#syncState.handleResponseMetadata({\n          status: 200,\n          responseHandle,\n          responseOffset,\n          responseCursor: null,\n          expiredHandle: null,\n          now: Date.now(),\n          maxStaleCacheRetries: this.#maxStaleCacheRetries,\n          createCacheBuster,\n        })\n        if (transition.action === `accepted`) {\n          this.#syncState = transition.state\n        } else {\n          console.warn(\n            `[Electric] Snapshot response metadata was not accepted ` +\n              `by state \"${this.#syncState.kind}\" (action: ${transition.action}). ` +\n              `Stream offset was not advanced from snapshot.`,\n            new Error(`stack trace`)\n          )\n        }\n      }\n\n      return {\n        metadata,\n        data,\n      }\n    } finally {\n      clearTimeout(snapshotWarnTimer)\n      this.#pauseLock.release(snapshotReason)\n    }\n  }\n\n  /**\n   * Fetch a snapshot for subset of data.\n   * Returns the metadata and the data, but does not inject it into the subscribed data stream.\n   *\n   * By default, uses GET to send subset parameters as query parameters. This may hit URL length\n   * limits (HTTP 414) with large WHERE clauses or many parameters. Set `method: 'POST'` or use\n   * `subsetMethod: 'POST'` on the stream to send parameters in the request body instead.\n   *\n   * @param opts - The options for the snapshot request.\n   * @returns The metadata, data, and the response's offset/handle for state advancement.\n   */\n  async fetchSnapshot(opts: SubsetParams): Promise<{\n    metadata: SnapshotMetadata\n    data: Array<ChangeMessage<T>>\n    responseOffset: Offset | null\n    responseHandle: string | null\n  }> {\n    return this.#fetchSnapshotWithRetry(opts, 0)\n  }\n\n  async #fetchSnapshotWithRetry(\n    opts: SubsetParams,\n    retryCount: number,\n    cacheBuster?: string\n  ): Promise<{\n    metadata: SnapshotMetadata\n    data: Array<ChangeMessage<T>>\n    responseOffset: Offset | null\n    responseHandle: string | null\n  }> {\n    const method = opts.method ?? this.options.subsetMethod ?? `GET`\n    const usePost = method === `POST`\n\n    let fetchUrl: URL\n    let fetchOptions: RequestInit\n\n    if (usePost) {\n      const result = await this.#constructUrl(this.options.url, true)\n      fetchUrl = result.fetchUrl\n      fetchOptions = {\n        method: `POST`,\n        headers: {\n          ...result.requestHeaders,\n          'Content-Type': `application/json`,\n        },\n        body: bigintSafeStringify(this.#buildSubsetBody(opts)),\n      }\n    } else {\n      const result = await this.#constructUrl(this.options.url, true, opts)\n      fetchUrl = result.fetchUrl\n      fetchOptions = { headers: result.requestHeaders }\n    }\n\n    // Apply cache buster from same-handle 409 retry\n    if (cacheBuster) {\n      fetchUrl.searchParams.set(CACHE_BUSTER_QUERY_PARAM, cacheBuster)\n      fetchUrl.searchParams.sort()\n    }\n\n    // Capture handle before fetch to avoid race conditions if it changes during the request\n    const usedHandle = this.#syncState.handle\n\n    let response: Response\n    try {\n      response = await this.#fetchClient(fetchUrl.toString(), fetchOptions)\n    } catch (e) {\n      // Handle 409 \"must-refetch\" - shape handle changed/expired.\n      // The fetch wrapper throws FetchError for non-OK responses, so we catch here.\n      // Unlike #requestShape, we don't call #reset() here as that would\n      // clear the pause lock and break requestSnapshot's pause/resume logic.\n      if (e instanceof FetchError && e.status === 409) {\n        const nextRetryCount = retryCount + 1\n        if (nextRetryCount > this.#maxSnapshotRetries) {\n          throw new FetchError(\n            502,\n            undefined,\n            undefined,\n            {},\n            fetchUrl.toString(),\n            `Snapshot request stuck in 409 retry loop after ${this.#maxSnapshotRetries} attempts. ` +\n              `This indicates a proxy/CDN misconfiguration. ` +\n              `For more information visit the troubleshooting guide: ${TROUBLESHOOTING_URL}`\n          )\n        }\n\n        if (usedHandle) {\n          const shapeKey = canonicalShapeKey(fetchUrl)\n          expiredShapesCache.markExpired(shapeKey, usedHandle)\n        }\n\n        // For snapshot 409s, only update the handle — don't reset offset/schema/etc.\n        // The main stream is paused and should not be disturbed.\n        const nextHandle = e.headers[SHAPE_HANDLE_HEADER]\n        if (nextHandle) {\n          this.#syncState = this.#syncState.withHandle(nextHandle)\n        } else {\n          console.warn(\n            `[Electric] Received 409 response without a shape handle header. ` +\n              `This likely indicates a proxy or CDN stripping required headers.`,\n            new Error(`stack trace`)\n          )\n        }\n        const nextCacheBuster = createCacheBuster()\n\n        return this.#fetchSnapshotWithRetry(\n          opts,\n          nextRetryCount,\n          nextCacheBuster\n        )\n      }\n      throw e\n    }\n\n    // Handle non-OK responses from custom fetch clients that bypass the wrapper chain\n    if (!response.ok) {\n      throw await FetchError.fromResponse(response, fetchUrl.toString())\n    }\n\n    const schema: Schema =\n      this.#syncState.schema ??\n      getSchemaFromHeaders(response.headers, {\n        required: true,\n        url: fetchUrl.toString(),\n      })\n\n    const { metadata, data: rawData } = await response.json()\n    const data = this.#messageParser.parseSnapshotData<ChangeMessage<T>>(\n      rawData,\n      schema\n    )\n\n    const responseOffset =\n      (response.headers.get(CHUNK_LAST_OFFSET_HEADER) as Offset) || null\n    const responseHandle = response.headers.get(SHAPE_HANDLE_HEADER)\n\n    return { metadata, data, responseOffset, responseHandle }\n  }\n\n  #buildSubsetBody(opts: SubsetParams): Record<string, unknown> {\n    const body: Record<string, unknown> = {}\n\n    if (opts.whereExpr) {\n      body.where = compileExpression(\n        opts.whereExpr,\n        this.options.columnMapper?.encode\n      )\n      body.where_expr = opts.whereExpr\n    } else if (opts.where && typeof opts.where === `string`) {\n      body.where = encodeWhereClause(\n        opts.where,\n        this.options.columnMapper?.encode\n      )\n    }\n\n    if (opts.params) {\n      body.params = opts.params\n    }\n\n    if (opts.limit !== undefined) {\n      body.limit = opts.limit\n    }\n\n    if (opts.offset !== undefined) {\n      body.offset = opts.offset\n    }\n\n    if (opts.orderByExpr) {\n      body.order_by = compileOrderBy(\n        opts.orderByExpr,\n        this.options.columnMapper?.encode\n      )\n      body.order_by_expr = opts.orderByExpr\n    } else if (opts.orderBy && typeof opts.orderBy === `string`) {\n      body.order_by = encodeWhereClause(\n        opts.orderBy,\n        this.options.columnMapper?.encode\n      )\n    }\n\n    return body\n  }\n}\n\n/**\n * Extracts the schema from response headers.\n * @param headers - The response headers\n * @param options - Options for schema extraction\n * @param options.required - If true, throws MissingHeadersError when header is missing. Defaults to false.\n * @param options.url - The URL to include in the error message if required is true\n * @returns The parsed schema, or an empty object if not required and header is missing\n * @throws {MissingHeadersError} if required is true and the header is missing\n */\nfunction getSchemaFromHeaders(\n  headers: Headers,\n  options?: { required?: boolean; url?: string }\n): Schema {\n  const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER)\n  if (!schemaHeader) {\n    if (options?.required && options?.url) {\n      throw new MissingHeadersError(options.url, [SHAPE_SCHEMA_HEADER])\n    }\n    return {}\n  }\n  return JSON.parse(schemaHeader)\n}\n\n/**\n * Validates that no reserved parameter names are used in the provided params object\n * @throws {ReservedParamError} if any reserved parameter names are found\n */\nfunction validateParams(params: Record<string, unknown> | undefined): void {\n  if (!params) return\n\n  const reservedParams = Object.keys(params).filter((key) =>\n    RESERVED_PARAMS.has(key as ReservedParamKeys)\n  )\n  if (reservedParams.length > 0) {\n    throw new ReservedParamError(reservedParams)\n  }\n}\n\nfunction validateOptions<T>(options: Partial<ShapeStreamOptions<T>>): void {\n  if (!options.url) {\n    throw new MissingShapeUrlError()\n  }\n  if (options.signal && !(options.signal instanceof AbortSignal)) {\n    throw new InvalidSignalError()\n  }\n\n  if (\n    options.offset !== undefined &&\n    options.offset !== `-1` &&\n    options.offset !== `now` &&\n    !options.handle\n  ) {\n    throw new MissingShapeHandleError()\n  }\n\n  validateParams(options.params)\n\n  return\n}\n\n// `unknown` being in the value is a bit of defensive programming if user doesn't use TS\nfunction setQueryParam(\n  url: URL,\n  key: string,\n  value: Record<string, string> | string | unknown\n): void {\n  if (value === undefined || value == null) {\n    return\n  } else if (typeof value === `string`) {\n    url.searchParams.set(key, value)\n  } else if (typeof value === `object`) {\n    for (const [k, v] of Object.entries(value)) {\n      url.searchParams.set(`${key}[${k}]`, v)\n    }\n  } else {\n    url.searchParams.set(key, value.toString())\n  }\n}\n\nfunction convertWhereParamsToObj(\n  allPgParams: ExternalParamsRecord<Row>\n): ExternalParamsRecord<Row> {\n  if (Array.isArray(allPgParams.params)) {\n    return {\n      ...allPgParams,\n      params: Object.fromEntries(allPgParams.params.map((v, i) => [i + 1, v])),\n    }\n  }\n  return allPgParams\n}\n","import { Message, Offset, Row } from './types'\nimport {\n  isChangeMessage,\n  isControlMessage,\n  canonicalBigintSafeStringify,\n} from './helpers'\nimport { FetchError } from './error'\nimport { LogMode, ShapeStreamInterface } from './client'\n\nexport type ShapeData<T extends Row<unknown> = Row> = Map<string, T>\nexport type ShapeChangedCallback<T extends Row<unknown> = Row> = (data: {\n  value: ShapeData<T>\n  rows: T[]\n}) => void\n\ntype ShapeStatus = `syncing` | `up-to-date`\n\n/**\n * Shape subscriber notification semantics (see SPEC.md → \"Shape\n * notification semantics\" for the canonical contract):\n *\n * - **N1 (no notify while syncing).** Data messages that arrive while\n *   `status === 'syncing'` apply to `#data` but do NOT call `#notify`.\n *   A notification fires only when the shape transitions from\n *   `syncing` to `up-to-date` via an up-to-date control message. This\n *   guarantees that subscribers observing `rows` always see a\n *   consistent snapshot of the shape AND can read a defined\n *   `stream.lastSyncedAt()`.\n *\n * - **N2 (notify on change while up-to-date).** Once\n *   `status === 'up-to-date'`, any data message (insert/update/delete,\n *   subject to `mode === 'changes_only'` filtering) triggers a\n *   notification; the status then transitions back to `syncing` until\n *   the next up-to-date message.\n *\n * A `must-refetch` control message clears `#data` and transitions the\n * status back to `syncing`, which re-engages N1 — subscribers will\n * receive the post-rotation state on the next `up-to-date` without\n * ever observing an intermediate empty-rows notification.\n */\n\n/**\n * A Shape is an object that subscribes to a shape log,\n * keeps a materialised shape `.rows` in memory and\n * notifies subscribers when the value has changed.\n *\n * It can be used without a framework and as a primitive\n * to simplify developing framework hooks.\n *\n * @constructor\n * @param {ShapeStream<T extends Row>} - the underlying shape stream\n * @example\n * ```\n * const shapeStream = new ShapeStream<{ foo: number }>({\n *   url: `http://localhost:3000/v1/shape`,\n *   params: {\n *     table: `foo`\n *   }\n * })\n * const shape = new Shape(shapeStream)\n * ```\n *\n * `rows` returns a promise that resolves the Shape data once the Shape has been\n * fully loaded (and when resuming from being offline):\n *\n *     const rows = await shape.rows\n *\n * `currentRows` returns the current data synchronously:\n *\n *     const rows = shape.currentRows\n *\n *  Subscribe to updates. Called whenever the shape updates in Postgres.\n *\n *     shape.subscribe(({ rows }) => {\n *       console.log(rows)\n *     })\n */\nexport class Shape<T extends Row<unknown> = Row> {\n  readonly stream: ShapeStreamInterface<T>\n\n  readonly #data: ShapeData<T> = new Map()\n  readonly #subscribers = new Map<object, ShapeChangedCallback<T>>()\n  readonly #insertedKeys = new Set<string>()\n  readonly #requestedSubSnapshots = new Set<string>()\n  #reexecuteSnapshotsPending = false\n  #status: ShapeStatus = `syncing`\n  #error: FetchError | false = false\n\n  constructor(stream: ShapeStreamInterface<T>) {\n    this.stream = stream\n    this.stream.subscribe(\n      this.#process.bind(this),\n      this.#handleError.bind(this)\n    )\n  }\n\n  get isUpToDate(): boolean {\n    return this.#status === `up-to-date`\n  }\n\n  get lastOffset(): Offset {\n    return this.stream.lastOffset\n  }\n\n  get handle(): string | undefined {\n    return this.stream.shapeHandle\n  }\n\n  get rows(): Promise<T[]> {\n    return this.value.then((v) => Array.from(v.values()))\n  }\n\n  get currentRows(): T[] {\n    return Array.from(this.currentValue.values())\n  }\n\n  get value(): Promise<ShapeData<T>> {\n    return new Promise((resolve, reject) => {\n      if (this.stream.isUpToDate) {\n        resolve(this.currentValue)\n      } else {\n        const unsubscribe = this.subscribe(({ value }) => {\n          unsubscribe()\n          if (this.#error) reject(this.#error)\n          resolve(value)\n        })\n      }\n    })\n  }\n\n  get currentValue() {\n    return this.#data\n  }\n\n  get error() {\n    return this.#error\n  }\n\n  /** Unix time at which we last synced. Undefined when `isLoading` is true. */\n  lastSyncedAt(): number | undefined {\n    return this.stream.lastSyncedAt()\n  }\n\n  /** Time elapsed since last sync (in ms). Infinity if we did not yet sync. */\n  lastSynced() {\n    return this.stream.lastSynced()\n  }\n\n  /** True during initial fetch. False afterwise.  */\n  isLoading() {\n    return this.stream.isLoading()\n  }\n\n  /** Indicates if we are connected to the Electric sync service. */\n  isConnected(): boolean {\n    return this.stream.isConnected()\n  }\n\n  /** Current log mode of the underlying stream */\n  get mode(): LogMode {\n    return this.stream.mode\n  }\n\n  /**\n   * Request a snapshot for subset of data. Only available when mode is changes_only.\n   * Returns void; data will be emitted via the stream and processed by this Shape.\n   */\n  async requestSnapshot(\n    params: Parameters<ShapeStreamInterface<T>[`requestSnapshot`]>[0]\n  ): Promise<void> {\n    // Track this snapshot request for future re-execution on shape rotation.\n    // Use canonical stringify so permutation-equivalent params dedup.\n    const key = canonicalBigintSafeStringify(params)\n    this.#requestedSubSnapshots.add(key)\n    // Ensure the stream is up-to-date so schema is available for parsing\n    await this.#awaitUpToDate()\n    await this.stream.requestSnapshot(params)\n  }\n\n  subscribe(callback: ShapeChangedCallback<T>): () => void {\n    const subscriptionId = {}\n\n    this.#subscribers.set(subscriptionId, callback)\n\n    return () => {\n      this.#subscribers.delete(subscriptionId)\n    }\n  }\n\n  unsubscribeAll(): void {\n    this.#subscribers.clear()\n  }\n\n  get numSubscribers() {\n    return this.#subscribers.size\n  }\n\n  #process(messages: Message<T>[]): void {\n    let shouldNotify = false\n\n    messages.forEach((message) => {\n      if (isChangeMessage(message)) {\n        const wasUpToDate = this.#status === `up-to-date`\n        this.#updateShapeStatus(`syncing`)\n        if (this.mode === `full`) {\n          switch (message.headers.operation) {\n            case `insert`:\n              this.#data.set(message.key, message.value)\n              if (wasUpToDate) shouldNotify = true\n              break\n            case `update`:\n              this.#data.set(message.key, {\n                ...this.#data.get(message.key)!,\n                ...message.value,\n              })\n              if (wasUpToDate) shouldNotify = true\n              break\n            case `delete`:\n              this.#data.delete(message.key)\n              if (wasUpToDate) shouldNotify = true\n              break\n          }\n        } else {\n          // changes_only: only apply updates/deletes for keys for which we observed an insert\n          switch (message.headers.operation) {\n            case `insert`:\n              this.#insertedKeys.add(message.key)\n              this.#data.set(message.key, message.value)\n              if (wasUpToDate) shouldNotify = true\n              break\n            case `update`:\n              if (this.#insertedKeys.has(message.key)) {\n                this.#data.set(message.key, {\n                  ...this.#data.get(message.key)!,\n                  ...message.value,\n                })\n                if (wasUpToDate) shouldNotify = true\n              }\n              break\n            case `delete`:\n              if (this.#insertedKeys.has(message.key)) {\n                this.#data.delete(message.key)\n                this.#insertedKeys.delete(message.key)\n                if (wasUpToDate) shouldNotify = true\n              }\n              break\n          }\n        }\n      }\n\n      if (isControlMessage(message)) {\n        switch (message.headers.control) {\n          case `up-to-date`:\n            if (this.#updateShapeStatus(`up-to-date`)) shouldNotify = true\n            if (this.#reexecuteSnapshotsPending) {\n              this.#reexecuteSnapshotsPending = false\n              void this.#reexecuteSnapshots()\n            }\n            break\n          case `must-refetch`:\n            this.#data.clear()\n            this.#insertedKeys.clear()\n            this.#error = false\n            this.#updateShapeStatus(`syncing`)\n            this.#reexecuteSnapshotsPending = true\n            break\n        }\n      }\n    })\n\n    if (shouldNotify) this.#notify()\n  }\n\n  async #reexecuteSnapshots(): Promise<void> {\n    try {\n      await this.#awaitUpToDate()\n    } catch (e) {\n      this.#surfaceReexecuteError(e)\n      return\n    }\n\n    const results = await Promise.all(\n      Array.from(this.#requestedSubSnapshots).map(async (jsonParams) => {\n        try {\n          const snapshot = JSON.parse(jsonParams)\n          await this.stream.requestSnapshot(snapshot)\n          return undefined\n        } catch (e) {\n          return e\n        }\n      })\n    )\n\n    const firstError = results.find((e) => e !== undefined)\n    if (firstError !== undefined) this.#surfaceReexecuteError(firstError)\n  }\n\n  #surfaceReexecuteError(e: unknown): void {\n    if (e instanceof FetchError) {\n      this.#error = e\n    } else if (e instanceof Error) {\n      this.#error = new FetchError(0, e.message, undefined, {}, ``, e.message)\n    } else {\n      this.#error = new FetchError(0, String(e), undefined, {}, ``, String(e))\n    }\n    this.#notify()\n  }\n\n  async #awaitUpToDate(): Promise<void> {\n    if (this.#error) throw this.#error\n    if (this.stream.isUpToDate) return\n    if (this.stream.error) throw this.stream.error as Error\n    await new Promise<void>((resolve, reject) => {\n      let settled = false\n      let interval: ReturnType<typeof setInterval>\n      let unsub: (() => void) | undefined\n      const done = (action: () => void) => {\n        if (settled) return\n        settled = true\n        clearInterval(interval)\n        unsub?.()\n        action()\n      }\n      const check = () => {\n        if (this.stream.isUpToDate) return done(resolve)\n        const streamError = this.stream.error as Error | undefined\n        if (streamError) return done(() => reject(streamError))\n        if (this.#error) {\n          const err = this.#error\n          return done(() => reject(err))\n        }\n      }\n      interval = setInterval(check, 10)\n      unsub = this.stream.subscribe(\n        () => check(),\n        (err) => done(() => reject(err))\n      )\n      check()\n    })\n  }\n\n  #updateShapeStatus(status: ShapeStatus): boolean {\n    const stateChanged = this.#status !== status\n    this.#status = status\n    return stateChanged && status === `up-to-date`\n  }\n\n  #handleError(e: Error): void {\n    if (e instanceof FetchError) {\n      this.#error = e\n      this.#notify()\n    }\n  }\n\n  #notify(): void {\n    this.#subscribers.forEach((callback) => {\n      callback({ value: this.currentValue, rows: this.currentRows })\n    })\n  }\n}\n"],"mappings":"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","names":["FetchError","_FetchError","status","text","json","headers","url","message","response","contentType","FetchBackoffAbortError","MissingShapeUrlError","InvalidSignalError","MissingShapeHandleError","ReservedParamError","reservedParams","ParserNullValueError","columnName","MissingHeadersError","url","missingHeaders","msg","h","StaleCacheError","message","parseNumber","value","parseBool","parseBigInt","parseJson","identityParser","v","defaultParser","pgArrayParser","parser","i","char","str","quoted","last","p","extractValue","x","start","end","val","loop","xs","MessageParser","transformer","__spreadValues","messages","schema","key","message","msg","row","_b","columnInfo","_a","typ","dimensions","additionalInfo","__objRest","typeParser","makeNullableParser","_","columnName","isNullable","ParserNullValueError","quoteIdentifier","identifier","snakeToCamel","str","_a","_b","_c","_d","leadingUnderscores","withoutLeading","trailingUnderscores","camelCased","match","letter","camelToSnake","createColumnMapper","mapping","reverseMapping","dbName","appName","dbColumnName","appColumnName","encodeWhereClause","whereClause","encode","sqlKeywords","quotedRanges","pos","ch","start","quoteChar","isInQuotedString","range","identifierPattern","_p1","offset","snakeCamelMapper","schema","dbColumn","isChangeMessage","message","isControlMessage","isUpToDateMessage","getOffset","lsn","bigintReplacer","_key","value","bigintSafeStringify","canonicalBigintSafeStringify","canonicalize","sorted","k","isVisibleInSnapshot","txid","snapshot","xid","xmin","xmax","xip","LIVE_CACHE_BUSTER_HEADER","SHAPE_HANDLE_HEADER","CHUNK_LAST_OFFSET_HEADER","SHAPE_SCHEMA_HEADER","CHUNK_UP_TO_DATE_HEADER","COLUMNS_QUERY_PARAM","LIVE_CACHE_BUSTER_QUERY_PARAM","EXPIRED_HANDLE_QUERY_PARAM","SHAPE_HANDLE_QUERY_PARAM","LIVE_QUERY_PARAM","OFFSET_QUERY_PARAM","TABLE_QUERY_PARAM","WHERE_QUERY_PARAM","REPLICA_PARAM","WHERE_PARAMS_PARAM","EXPERIMENTAL_LIVE_SSE_QUERY_PARAM","LIVE_SSE_QUERY_PARAM","FORCE_DISCONNECT_AND_REFRESH","PAUSE_STREAM","SYSTEM_WAKE","LOG_MODE_QUERY_PARAM","SUBSET_PARAM_WHERE","SUBSET_PARAM_LIMIT","SUBSET_PARAM_OFFSET","SUBSET_PARAM_ORDER_BY","SUBSET_PARAM_WHERE_PARAMS","SUBSET_PARAM_WHERE_EXPR","SUBSET_PARAM_ORDER_BY_EXPR","CACHE_BUSTER_QUERY_PARAM","ELECTRIC_PROTOCOL_QUERY_PARAMS","HTTP_RETRY_STATUS_CODES","BackoffDefaults","parseRetryAfterHeader","retryAfter","retryAfterSec","retryDate","deltaMs","createFetchWithBackoff","fetchClient","backoffOptions","initialDelay","maxDelay","multiplier","debug","onFailedAttempt","maxRetries","args","_a","url","options","delay","attempt","result","FetchError","e","FetchBackoffAbortError","serverMinimumMs","jitter","clientBackoffMs","waitMs","source","resolve","NO_BODY_STATUS_CODES","createFetchWithConsumedMessages","_b","res","text","err","ChunkPrefetchDefaults","createFetchWithChunkBuffer","prefetchOptions","maxChunksToPrefetch","prefetchQueue","getRequestMethod","prefetchedRequest","response","nextUrl","getNextChunkUrl","PrefetchQueue","requiredElectricResponseHeaders","CHUNK_LAST_OFFSET_HEADER","SHAPE_HANDLE_HEADER","requiredLiveResponseHeaders","LIVE_CACHE_BUSTER_HEADER","requiredNonLiveResponseHeaders","SHAPE_SCHEMA_HEADER","createFetchWithResponseHeadersCheck","headers","missingHeaders","addMissingHeaders","requiredHeaders","h","urlString","SUBSET_PARAM_WHERE","SUBSET_PARAM_WHERE_PARAMS","SUBSET_PARAM_LIMIT","SUBSET_PARAM_OFFSET","SUBSET_PARAM_ORDER_BY","p","LIVE_QUERY_PARAM","MissingHeadersError","_fetchClient","_maxPrefetchedRequests","_prefetchQueue","_queueHeadUrl","_queueTailUrl","_PrefetchQueue_instances","prefetch_fn","__privateAdd","__privateSet","__privateGet","__privateMethod","_","aborter","entry","request","signal","cleanup","chainAborter","__spreadProps","__spreadValues","shapeHandle","lastOffset","isUpToDate","CHUNK_UP_TO_DATE_HEADER","expiredHandle","EXPIRED_HANDLE_QUERY_PARAM","SHAPE_HANDLE_QUERY_PARAM","OFFSET_QUERY_PARAM","sourceSignal","noop","abortParent","input","init","compileExpression","expr","columnMapper","mappedColumn","quoteIdentifier","compileFunction","_exhaustive","args","arg","a","compileOrderBy","clauses","clause","sql","getBytes","stream","onChunk","reader","result","getLines","onLine","buffer","position","fieldLength","discardTrailingNewline","arr","concat","bufLength","lineStart","lineEnd","getMessages","onId","onRetry","onMessage","message","newMessage","decoder","line","field","valueOffset","value","retry","a","b","res","EventStreamContentType","DefaultRetryInterval","LastEventId","fetchEventSource","input","_a","inputSignal","inputHeaders","inputOnOpen","onmessage","onclose","onerror","openWhenHidden","inputFetch","rest","__rest","resolve","reject","headers","curRequestController","onVisibilityChange","create","retryInterval","retryTimer","dispose","fetch","onopen","defaultOnOpen","sig","response","getLines","getMessages","id","retry","err","interval","innerErr","ExpiredShapesCache","shapeUrl","entry","handle","keys","oldest","min","k","e","stored","expiredShapesCache","UpToDateTracker","shapeKey","cursor","keys","oldest","min","k","now","timeSinceLastWrite","delay","entry","modified","key","e","stored","upToDateTracker","_SnapshotTracker_instances","detachFromReverseIndexes_fn","addToSet_fn","removeFromSet_fn","SnapshotTracker","__privateAdd","metadata","keys","__privateMethod","xmax","databaseLsn","snapshotMark","message","txids","xid","snapshots","snapshot","x","isVisibleInSnapshot","newDatabaseLsn","dbLsn","existing","map","key","value","set","ShapeStreamState","_cursor","_opts","_input","_url","_context","PausedState","error","ErrorState","handle","InitialState","_shared","ActiveState","shared","__privateAdd","__privateSet","__privateGet","url","OFFSET_QUERY_PARAM","SHAPE_HANDLE_QUERY_PARAM","input","_a","_b","_c","responseHandle","offset","liveCacheBuster","schema","lastSyncedAt","expiredHandle","retryCount","StaleRetryState","__spreadProps","__spreadValues","LiveState","FetchingState","staleResult","SyncingState","cursor","ReplayingState","_InitialState","_SyncingState","_staleCacheBuster","_staleCacheRetryCount","_StaleRetryState","fields","staleCacheBuster","staleCacheRetryCount","__objRest","context","CACHE_BUSTER_QUERY_PARAM","_consecutiveShortSseConnections","_sseFallbackToLongPolling","_LiveState","sseState","LIVE_CACHE_BUSTER_QUERY_PARAM","LIVE_QUERY_PARAM","opts","nextConsecutiveShort","nextFallback","fellBackToLongPolling","wasShortConnection","_replayCursor","_ReplayingState","replayCursor","suppressBatch","_PausedState","previousState","transition","_exhaustive","_ErrorState","createInitialState","_holders","_onAcquired","_onReleased","PauseLock","callbacks","__privateAdd","__privateSet","reason","__privateGet","wasUnlocked","prefix","RESERVED_PARAMS","LIVE_CACHE_BUSTER_QUERY_PARAM","SHAPE_HANDLE_QUERY_PARAM","LIVE_QUERY_PARAM","OFFSET_QUERY_PARAM","CACHE_BUSTER_QUERY_PARAM","TROUBLESHOOTING_URL","createCacheBuster","resolveValue","value","toInternalParams","params","entries","resolvedEntries","key","resolvedValue","_","resolveHeaders","headers","canonicalShapeKey","url","cleanUrl","ELECTRIC_PROTOCOL_QUERY_PARAMS","_error","_fetchClient","_sseFetchClient","_messageParser","_subscribers","_started","_syncState","_connected","_mode","_onError","_requestAbortController","_refreshCount","_snapshotCounter","_ShapeStream_instances","isRefreshing_get","_tickPromise","_tickPromiseResolver","_tickPromiseRejecter","_messageChain","_snapshotTracker","_pauseLock","_currentFetchUrl","_lastSseConnectionStartTime","_minSseConnectionDuration","_maxShortSseConnections","_sseBackoffBaseDelay","_sseBackoffMaxDelay","_unsubscribeFromVisibilityChanges","_unsubscribeFromWakeDetection","_maxStaleCacheRetries","_recentRequestEntries","_fastLoopWindowMs","_fastLoopThreshold","_fastLoopBackoffBaseMs","_fastLoopBackoffMaxMs","_fastLoopConsecutiveCount","_fastLoopMaxCount","_pendingRequestShapeCacheBuster","_maxSnapshotRetries","_expiredShapeRecoveryKey","_pendingSelfHealCheck","_consecutiveErrorRetries","_maxConsecutiveErrorRetries","start_fn","teardown_fn","requestShape_fn","checkFastLoop_fn","constructUrl_fn","createAbortListener_fn","onInitialResponse_fn","onMessages_fn","fetchShape_fn","requestShapeLongPoll_fn","requestShapeSSE_fn","nextTick_fn","publish_fn","sendErrorToSubscribers_fn","hasBrowserVisibilityAPI_fn","subscribeToVisibilityChanges_fn","subscribeToWakeDetection_fn","reset_fn","fetchSnapshotWithRetry_fn","buildSubsetBody_fn","ShapeStream","options","__privateAdd","SnapshotTracker","_a","_b","_c","_d","__spreadValues","validateOptions","__privateSet","createInitialState","PauseLock","__privateGet","PAUSE_STREAM","__privateMethod","transformer","applyColumnMapper","row","result","dbKey","appKey","MessageParser","baseFetchClient","args","backOffOpts","__spreadProps","BackoffDefaults","fetchWithBackoffClient","createFetchWithBackoff","createFetchWithResponseHeadersCheck","createFetchWithChunkBuffer","createFetchWithConsumedMessages","callback","onError","subscriptionId","__privateWrapper","FORCE_DISCONNECT_AND_REFRESH","opts","snapshotReason","snapshotWarnTimer","metadata","data","responseOffset","responseHandle","dataWithEndBoundary","message","transition","err","retryOpts","isRetryable","MissingHeadersError","ErrorState","requestShapeCacheBuster","activeCacheBuster","resumingFromPause","PausedState","signal","fetchUrl","requestHeaders","abortListener","requestAbortController","e","abortReason","isRestartAbort","SYSTEM_WAKE","FetchError","FetchBackoffAbortError","StaleCacheError","shapeKey","expiredShapesCache","newShapeHandle","SHAPE_HANDLE_HEADER","nextRequestShapeCacheBuster","now","currentOffset","upToDateTracker","maxDelay","delayMs","resolve","subsetParams","_e","_f","convertWhereParamsToObj","validateParams","setQueryParam","TABLE_QUERY_PARAM","encodedWhere","encodeWhereClause","WHERE_QUERY_PARAM","originalColumns","encodedColumns","serializedColumns","quoteIdentifier","COLUMNS_QUERY_PARAM","REPLICA_PARAM","WHERE_PARAMS_PARAM","customParams","compiledWhere","compileExpression","SUBSET_PARAM_WHERE","SUBSET_PARAM_WHERE_EXPR","SUBSET_PARAM_WHERE_PARAMS","bigintSafeStringify","SUBSET_PARAM_LIMIT","SUBSET_PARAM_OFFSET","compiledOrderBy","compileOrderBy","SUBSET_PARAM_ORDER_BY","SUBSET_PARAM_ORDER_BY_EXPR","encodedOrderBy","LOG_MODE_QUERY_PARAM","expiredHandle","EXPIRED_HANDLE_QUERY_PARAM","response","status","shapeHandle","healedKey","staleHandle","CHUNK_LAST_OFFSET_HEADER","LIVE_CACHE_BUSTER_HEADER","getSchemaFromHeaders","batch","isSseMessage","lastMessage","hasUpToDateMessage","isUpToDateMessage","upToDateOffset","getOffset","messagesToProcess","isChangeMessage","lastSeenCursor","useSse","EXPERIMENTAL_LIVE_SSE_QUERY_PARAM","LIVE_SSE_QUERY_PARAM","schema","messages","preview","fetch","sseHeaders","ignoredStaleResponse","buffer","fetchEventSource","event","error","connectionDuration","wasAborted","reject","__","errorFn","visibilityHandler","INTERVAL_MS","WAKE_THRESHOLD_MS","lastTickTime","timer","elapsed","handle","retryCount","cacheBuster","usePost","fetchOptions","usedHandle","nextRetryCount","nextHandle","nextCacheBuster","rawData","body","schemaHeader","SHAPE_SCHEMA_HEADER","reservedParams","ReservedParamError","MissingShapeUrlError","InvalidSignalError","MissingShapeHandleError","k","v","allPgParams","i","_data","_subscribers","_insertedKeys","_requestedSubSnapshots","_reexecuteSnapshotsPending","_status","_error","_Shape_instances","process_fn","reexecuteSnapshots_fn","surfaceReexecuteError_fn","awaitUpToDate_fn","updateShapeStatus_fn","handleError_fn","notify_fn","Shape","stream","__privateAdd","__privateMethod","__privateGet","v","resolve","reject","unsubscribe","value","params","key","canonicalBigintSafeStringify","callback","subscriptionId","messages","shouldNotify","message","isChangeMessage","wasUpToDate","__spreadValues","isControlMessage","__privateSet","e","firstError","jsonParams","snapshot","FetchError","settled","interval","unsub","done","action","check","streamError","err","status","stateChanged"]}