@electric-sql/client
Version:
Postgres everywhere - your data, in sync, wherever you need it.
1 lines • 267 kB
Source Map (JSON)
{"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/runtime-visibility.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 SNAPSHOT_HEADER = `electric-snapshot`\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 LIVE_REQUEST_TIMEOUT = `live-request-timeout`\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 SNAPSHOT_HEADER,\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\nasync function abortableSleep(\n waitMs: number,\n signal?: AbortSignal\n): Promise<void> {\n if (waitMs <= 0) return\n if (signal?.aborted) throw new FetchBackoffAbortError()\n\n await new Promise<void>((resolve, reject) => {\n let settled = false\n const done = (err?: Error) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n signal?.removeEventListener(`abort`, onAbort)\n err ? reject(err) : resolve()\n }\n const onAbort = () => done(new FetchBackoffAbortError())\n const timer = setTimeout(() => done(), waitMs)\n signal?.addEventListener(`abort`, onAbort, { once: true })\n if (signal?.aborted) onAbort()\n })\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, but let stream-level aborts\n // interrupt the backoff immediately (e.g. system wake reconnects).\n await abortableSleep(waitMs, options?.signal ?? undefined)\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\nexport async function consumeResponseBody(\n res: Response,\n url: string,\n signal?: AbortSignal\n): Promise<Response> {\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 (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,\n err instanceof Error\n ? err.message\n : typeof err === `string`\n ? err\n : `failed to read body`\n )\n }\n}\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 return consumeResponseBody(\n res,\n url.toString(),\n args[1]?.signal ?? undefined\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 const isSnapshot = res.headers.get(SNAPSHOT_HEADER) === `true`\n\n // only prefetch if shape handle and offset for next chunk are available, and\n // response is not already up-to-date\n // snapshot (subset) responses are one-shot and must never be prefetched:\n // there is no \"next chunk\" URL for them, only a repeat of the same request\n if (!shapeHandle || !lastOffset || isUpToDate || isSnapshot) 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 * Resolves a 32-bit xid against an epoch-aware xid8.\n *\n * This signed modulo-2^32 calculation requires the reference and xid to be\n * within 2^31 transactions. ShapeStream enforces that lifetime bound by\n * retiring snapshots as global_last_seen_lsn passes their database_lsn.\n *\n * Mirrors `Electric.Postgres.Xid`\n * (`packages/sync-service/lib/electric/postgres/xid.ex`).\n */\n #toXid8(xid: number, referenceXid8: bigint): bigint {\n return referenceXid8 + BigInt.asIntN(32, BigInt(xid) - referenceXid8)\n }\n\n /**\n * Resolves each contributing xid into the epoch nearest `referenceXid8` and\n * returns the latest. Snapshot callers use `xmax` as the reference.\n */\n #resolveLatestXid8(xids: number[], referenceXid8: bigint): bigint {\n return xids.reduce((latest, xid) => {\n const xid8 = this.#toXid8(xid, referenceXid8)\n return xid8 > latest ? xid8 : latest\n }, BigInt(-1))\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 for (const [xmax, snapshots] of this.xmaxSnapshots.entries()) {\n const xid8 = this.#resolveLatestXid8(txids, xmax)\n if (xid8 >= xmax) {\n for (const snapshot of snapshots) {\n this.removeSnapshot(snapshot)\n }\n }\n }\n\n return [...this.activeSnapshots.values()].some((snapshot) => {\n if (!snapshot.keys.has(message.key)) return false\n const xid8 = this.#resolveLatestXid8(txids, snapshot.xmax)\n return isVisibleInSnapshot(xid8, snapshot)\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 suppressUpToDate: 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, suppressUpToDate: 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, suppressUpToDate: 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 suppressUpToDate: 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 suppressUpToDate: 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 the replayed up-to-date notification when the cursor has not moved since\n // the previous session (non-SSE only).\n const suppressUpToDate =\n !input.isSse && this.#replayCursor === input.currentCursor\n return {\n state: new LiveState(shared),\n suppressUpToDate,\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 type { RuntimeVisibilityAdapter } from './client'\n\ntype RuntimeVisibilityAdapterFactory = () =>\n | RuntimeVisibilityAdapter\n | undefined\n\nlet defaultRuntimeVisibilityAdapterFactory:\n | RuntimeVisibilityAdapterFactory\n | undefined\n\nexport function setDefaultRuntimeVisibilityAdapterFactory(\n factory: RuntimeVisibilityAdapterFactory | undefined\n): void {\n defaultRuntimeVisibilityAdapterFactory = factory\n}\n\nexport function getDefaultRuntimeVisibilityAdapterFactory():\n | RuntimeVisibilityAdapterFactory\n | undefined {\n return defaultRuntimeVisibilityAdapterFactory\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 InvalidShapeOptionsError,\n ReservedParamError,\n MissingHeadersError,\n StaleCacheError,\n} from './error'\nimport {\n BackoffDefaults,\n BackoffOptions,\n consumeResponseBody,\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 LIVE_REQUEST_TIMEOUT,\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'\nimport { getDefaultRuntimeVisibilityAdapterFactory } from './runtime-visibility'\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 type RuntimeVisibilityState = `visible` | `hidden`\n\nexport type RuntimeVisibilityAdapter = {\n getCurrentState?: () => RuntimeVisibilityState | undefined\n subscribe: (callback: (state: RuntimeVisibilityState) => void) => () => void\n}\n\ntype ReactNativeAppStateStatus = `active` | `background` | `inactive` | null\n\ntype ReactNativeAppStateLike = {\n currentState: ReactNativeAppStateStatus\n addEventListener: (\n type: `change`,\n listener: (state: ReactNativeAppStateStatus) => void\n ) => { remove: () => void }\n}\n\nconst reactNativeAppStateToVisibility = (\n state: ReactNativeAppStateStatus\n): RuntimeVisibilityState | undefined => {\n if (state === null) return undefined\n return state === `active` ? `visible` : `hidden`\n}\n\nexport function createReactNativeRuntimeVisibilityAdapter(\n AppState: ReactNativeAppStateLike\n): RuntimeVisibilityAdapter {\n return {\n getCurrentState: () =>\n reactNativeAppStateToVisibility(AppState.currentState),\n subscribe: (callback) => {\n const subscription = AppState.addEventListener(`change`, (state) => {\n const visibilityState = reactNativeAppStateToVisibility(state)\n if (visibilityState) callback(visibilityState)\n })\n return () => subscription.remove()\n },\n }\n}\n\nfunction getDefaultRuntimeVisibilityAdapter():\n | RuntimeVisibilityAdapter\n | undefined {\n return getDefaultRuntimeVisibilityAdapterFactory()?.()\n}\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\n /**\n * Runtime lifecycle adapter for environments without `document.visibilitychange`,\n * such as React Native. Hidden state pauses the stream and aborts in-flight\n * requests; visible state resumes with a non-live catch-up request.\n */\n runtimeVisibility?: RuntimeVisibilityAdapter\n backoffOptions?: BackoffOptions\n\n /**\n * Maximum time in milliseconds to wait for a live long-poll request or\n * refresh catch-up request before aborting it and reconnecting. This guards\n * against runtimes (notably some React Native fetch implementations) where an\n * in-flight fetch can hang indefinitely across app lifecycle or network\n * transitions.\n *\n * The default (45s) is intentionally longer than Electric's server-side\n * long-poll timeout (20s), so firing the watchdog indicates a request that\n * failed to settle rather than a normal long-poll waiting for data.\n *\n * Must be a positive finite number. Set to `false` to disable the watchdog.\n */\n liveRequestTimeoutMs?: number | false\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 * Retries requested by `onError` use exponential backoff with full jitter,\n * using the stream's `backoffOptions` timings, and are still bounded by a\n * consecutive retry guard to prevent permanent retry loops.\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 /**\n * Warn in browser console when using HTTP URLs (default: true).\n * HTTP limits browsers to 6 concurrent connections which can cause\n * slow shapes with multiple subscriptions.\n */\n warnOnHttp?: boolean\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 #restartAbortControllers = new WeakSet<AbortController>()\n #refreshCount = 0\n #refreshCatchUpWatchdogActive = false\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 // Tracks when subscriber callbacks are actively being delivered from\n // #messageChain. requestSnapshot can inject a nested batch from inside a\n // subscriber; in that reentrant case #publish uses this as an intentional\n // escape hatch to deliver the nested snapshot batch immediately rather than\n // queueing it behind the subscriber that is awaiting it.\n #isPublishing = false\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 #liveRequestTimeoutMs: number | false\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 #onErrorBackoff: {\n initialDelay: number\n maxDelay: number\n multiplier: number\n }\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 // Default exceeds Electric's 20s server long-poll timeout, so this only\n // fires when the runtime request appears wedged rather than normally held.\n this.#liveRequestTimeoutMs = this.options.liveRequestTimeoutMs ?? 45_000\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 this.#onErrorBackoff = {\n initialDelay: backOffOpts.initialDelay,\n maxDelay: backOffOpts.maxDelay,\n multiplier: backOffOpts.multiplier,\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 await this.#backoffOnErrorRetry(this.#consecutiveErrorRetries)\n if (this.options.signal?.aborted) {\n this.#teardown()\n return\n }\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 #backoffOnErrorRetry(retryAttempt: number): Promise<void> {\n // Non-429 4xx errors intentionally bypass fetch-layer backoff so\n // `onError` can repair auth/params; network failures that exhaust fetch\n // backoff also reach this path. If the handler asks to retry we still\n // need pacing to avoid tight retry loops on persistent failures.\n // Uses the same full-jitter exponential backoff strategy as the fetch layer.\n const { initialDelay, maxDelay, multiplier } = this.#onErrorBackoff\n const cappedDelay = Math.min(\n maxDelay,\n initialDelay * Math.pow(multiplier, retryAttempt - 1) // 1-indexed: first retry uses multiplier^0\n )\n const delayMs = Math.floor(Math.random() * cappedDelay)\n\n const signal = this.options.signal\n if (delayMs <= 0 || signal?.aborted) return\n\n await new Promise<void>((resolve) => {\n let settled = false\n const done = () => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n signal?.removeEventListener(`abort`, done)\n resolve()\n }\n const timer = setTimeout(done, delayMs)\n signal?.addEventListener(`abort`, done, { once: true })\n if (signal?.aborted) done()\n })\n }\n\n async #requestShape(requestShapeCacheBuster?: string): Promise<void> {\n if (this.options.signal?.aborted) {\n this.#teardown()\n return\n }\n\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 isMarkedRestartAbort = this.#restartAbortControllers.delete(\n requestAbortController\n )\n const isRestartAbort =\n requestAbortController.signal.aborted &&\n (isMarkedRestartAbort ||\n abortReason === FORCE_DISCONNECT_AND_REFRESH ||\n abortReason === SYSTEM_WAKE ||\n abortReason === LIVE_REQUEST_TIMEOUT)\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 )\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(\n batch: Array<Message<T>>,\n isSseMessage = false,\n opts: { allowReentrantPublishBypass?: boolean } = {}\n ) {\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 this.#refreshCatchUpWatchdogActive = false\n if (!transition.suppressUpToDate && 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 const changeLsn = message.headers.lsn\n if (typeof changeLsn === `string` && changeLsn) {\n // A quiet SSE stream may deliver its first later change before the\n // next up-to-date boundary. Retire snapshots against that change's\n // WAL position before resolving its wrapped transaction ID.\n this.#snapshotTracker.lastSeenUpdate(BigInt(changeLsn))\n }\n return !this.#snapshotTracker.shouldRejectMessage(message)\n }\n\n if (isUpToDateMessage(message)) {\n const lastSeenLsn = message.headers.global_last_seen_lsn\n if (typeof lastSeenLsn === `string` && lastSeenLsn) {\n // Process this in message order: changes before the up-to-date\n // boundary still need snapshot deduplication, while later changes do\n // not once the database has passed the snapshot's LSN.\n this.#snapshotTracker.lastSeenUpdate(BigInt(lastSeenLsn))\n }\n\n // A replayed up-to-date for the previous session's cursor still\n // carries real WAL progress, which is applied above; only its\n // delivery to subscribers is skipped.\n if (transition.suppressUpToDate) return false\n }\n\n return true // Always process control messages\n })\n\n // A replay batch that carried nothing but the suppressed up-to-date has\n // nothing to deliver. Skip the callback rather than publishing an empty\n // batch: subscribers are promised one or more messages per notification,\n // and the pre-suppression code never notified for a suppressed replay.\n // Batches emptied by the snapshot tracker alone are still published as\n // before.\n if (messagesToProcess.length === 0 && transition.suppressUpToDate) return\n\n await this.#publish(messagesToProcess, {\n allowReentrantBypass: opts.allowReentrantPublishBypass,\n })\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 #withRequestTimeout<T>(\n promise: Promise<T>,\n requestAbortController: AbortController,\n fetchUrl: URL\n ): Promise<T> {\n const timeoutMs = this.#liveRequestTimeoutMs\n const isLiveRequest = fetchUrl.searchParams.get(LIVE_QUERY_PARAM) === `true`\n const isRefreshCatchUpRequest =\n this.#isRefreshing || this.#refreshCatchUpWatchdogActive\n\n if (timeoutMs === false || (!isLiveRequest && !isRefreshCatchUpRequest)) {\n return promise\n }\n\n let timeout: ReturnType<typeof setTimeout> | undefined\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeout = setTimeout(() => {\n if (!requestAbortController.signal.aborted) {\n this.#restartAbortControllers.add(requestAbortController)\n requestAbortController.abort(LIVE_REQUEST_TIMEOUT)\n }\n reject(new FetchBackoffAbortError())\n }, timeoutMs)\n })\n\n try {\n return await Promise.race([promise, timeoutPromise])\n } finally {\n if (timeout) clearTimeout(timeout)\n }\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 fetchUrlString = fetchUrl.toString()\n const rawResponse = await this.#withRequestTimeout(\n this.#sseFetchClient(fetchUrlString, {\n signal: requestAbortController.signal,\n headers,\n }),\n requestAbortController,\n fetchUrl\n )\n const response = await consumeResponseBody(\n rawResponse,\n fetchUrlString,\n requestAbortController.signal\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) {\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 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\n .finally(() => {\n this.#tickPromise = undefined\n this.#tickPromiseResolver = undefined\n this.#tickPromiseRejecter = undefined\n })\n .catch(() => {\n // The original tick promise is returned to callers; this chained promise\n // is only for cleanup, so consume rejections to avoid unhandled errors.\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 this.#refreshCatchUpWatchdogActive = true\n try {\n const requestAbortController = this.#requestAbortController\n if (\n this.#syncState.isUpToDate &&\n requestAbortController &&\n !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. Track restart intent ourselves instead of\n // relying only on AbortSignal.reason, which is missing in some React\n // Native runtimes.\n this.#restartAbortControllers.add(requestAbortController)\n requestAbortController.abort(FORCE_DISCONNECT_AND_REFRESH)\n }\n await this.#nextTick()\n } finally {\n this.#refreshCount--\n }\n }\n\n async #publish(\n messages: Message<T>[],\n opts: { allowReentrantBypass?: boolean } = {}\n ): Promise<void[]> {\n const deliver = () =>\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 // We process messages asynchronously but SSE's `onmessage` handler is\n // synchronous. Use a promise chain to ensure handlers execute sequentially\n // in the order messages were received. Only requestSnapshot's injected\n // snapshot batch is allowed to bypass the queue reentrantly; ordinary\n // stream batches (including SSE batches) must remain serialized.\n if (this.#isPublishing && opts.allowReentrantBypass) {\n return deliver()\n }\n\n this.#messageChain = this.#messageChain.then(async () => {\n this.#isPublishing = true\n try {\n return await deliver()\n } finally {\n this.#isPublishing = false\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 #setVisibilityPaused(isHidden: boolean) {\n if (isHidden) {\n this.#pauseLock.acquire(`visibility`)\n } else if (this.#pauseLock.isHeldBy(`visibility`)) {\n this.#pauseLock.release(`visibility`)\n }\n }\n\n #subscribeToVisibilityChanges() {\n const runtimeVisibility =\n this.options.runtimeVisibility ?? getDefaultRuntimeVisibilityAdapter()\n\n if (runtimeVisibility) {\n this.#setVisibilityPaused(\n runtimeVisibility.getCurrentState?.() === `hidden`\n )\n\n const unsubscribe = runtimeVisibility.subscribe((state) => {\n this.#setVisibilityPaused(state === `hidden`)\n })\n\n this.#unsubscribeFromVisibilityChanges = () => {\n unsubscribe()\n this.#unsubscribeFromVisibilityChanges = undefined\n }\n return\n }\n\n if (this.#hasBrowserVisibilityAPI()) {\n const visibilityHandler = () => {\n this.#setVisibilityPaused(document.hidden)\n }\n\n visibilityHandler()\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 #forceDisconnectAndRefreshFromWake() {\n const requestAbortController = this.#requestAbortController\n if (\n this.#pauseLock.isPaused ||\n !requestAbortController ||\n requestAbortController.signal.aborted ||\n this.options.signal?.aborted\n ) {\n return\n }\n\n this.#refreshCount++\n this.#refreshCatchUpWatchdogActive = true\n // Track restart intent ourselves instead of relying only on\n // AbortSignal.reason, which is missing in some React Native runtimes.\n this.#restartAbortControllers.add(requestAbortController)\n requestAbortController.abort(SYSTEM_WAKE)\n\n this.#nextTick()\n .catch(() => {\n // Teardown or user abort can reject the tick promise. Wake recovery runs\n // from a timer callback, so avoid surfacing that as an unhandled rejection.\n })\n .finally(() => {\n this.#refreshCount--\n })\n }\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 this.#forceDisconnectAndRefreshFromWake()\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 await this.#onMessages(dataWithEndBoundary, false, {\n allowReentrantPublishBypass: true,\n })\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 )\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\n// Module-level flag to ensure HTTP warning is only shown once per process/module\nlet didWarnOnHttp = false\n\n/**\n * Safely gets NODE_ENV without throwing ReferenceError in browser builds\n * where `process` may not be defined.\n */\nfunction getNodeEnvSafely(): string | undefined {\n return typeof process !== `undefined` ? process.env?.NODE_ENV : undefined\n}\n\n/**\n * Attempts to resolve a URL string, optionally with a base URL.\n * Returns undefined if the URL cannot be parsed.\n */\nfunction resolveUrlMaybe(url: string, base?: string): URL | undefined {\n try {\n return new URL(url, base)\n } catch {\n return undefined\n }\n}\n\n/**\n * Checks if we're in a browser environment (has window object).\n */\nfunction isBrowserEnvironment(): boolean {\n return typeof window !== `undefined`\n}\n\n/**\n * Gets the current page URL in browser environments.\n * Returns undefined in non-browser environments.\n */\nfunction getWindowLocationHref(): string | undefined {\n if (isBrowserEnvironment() && typeof window.location !== `undefined`) {\n return window.location.href\n }\n return undefined\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.liveRequestTimeoutMs !== undefined &&\n options.liveRequestTimeoutMs !== false &&\n (!Number.isFinite(options.liveRequestTimeoutMs) ||\n options.liveRequestTimeoutMs <= 0)\n ) {\n throw new InvalidShapeOptionsError(\n `Invalid shape options: liveRequestTimeoutMs must be a positive finite number or false`\n )\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 // Warn about HTTP URLs in browser environments (unless disabled or in tests)\n // HTTP forces HTTP/1.1 which typically limits browsers to ~6 concurrent connections per origin\n const nodeEnv = getNodeEnvSafely()\n const warnOnHttp = options.warnOnHttp ?? nodeEnv !== `test`\n\n if (warnOnHttp && !didWarnOnHttp && isBrowserEnvironment()) {\n if (typeof console !== `undefined`) {\n // Try to resolve the URL, using window.location.href as base for relative URLs\n const baseUrl = getWindowLocationHref()\n const resolvedUrl = resolveUrlMaybe(options.url, baseUrl)\n\n // Check if the resolved URL (or the page itself for relative URLs) uses HTTP\n const isHttp = resolvedUrl?.protocol === `http:`\n\n if (isHttp) {\n didWarnOnHttp = true\n console.warn(\n `[Electric] Using HTTP (not HTTPS) typically limits browsers to ~6 concurrent connections per origin under HTTP/1.1. ` +\n `This can cause slow shapes and app freezes with multiple shapes. ` +\n `Use HTTPS for HTTP/2 support. See: https://electric-sql.com/r/electric-http2`\n )\n }\n }\n }\n\n return\n}\n\n/**\n * Resets the HTTP warning flag. Only for testing purposes.\n * @internal\n */\nexport function _resetHttpWarningForTesting(): void {\n didWarnOnHttp = false\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":["value","pos","fetch","_a","_fetchClient","_a","_b","fetch","_subscribers","_error"]}