@electric-sql/client 1.0.0-beta.1 → 1.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/cjs/index.cjs +66 -28
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +28 -15
- package/dist/index.browser.mjs +3 -3
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.ts +28 -15
- package/dist/index.legacy-esm.js +56 -27
- package/dist/index.legacy-esm.js.map +1 -1
- package/dist/index.mjs +64 -27
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +115 -45
- package/src/types.ts +0 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<a href="https://github.com/electric-sql/electric/actions"><img src="https://github.com/electric-sql/electric/actions/workflows/ts_test.yml/badge.svg"></a>
|
|
19
19
|
<a href="https://github.com/electric-sql/electric/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache_2.0-green" alt="License - Apache 2.0"></a>
|
|
20
20
|
<a href="https://github.com/electric-sql/electric-n
|
|
21
|
-
ext/milestones"><img src="https://img.shields.io/badge/status-
|
|
21
|
+
ext/milestones"><img src="https://img.shields.io/badge/status-beta-orange" alt="Status - Beta"></a>
|
|
22
22
|
<a href="https://discord.electric-sql.com"><img src="https://img.shields.io/discord/933657521581858818?color=5969EA&label=discord" alt="Chat - Discord"></a>
|
|
23
23
|
<a href="https://x.com/ElectricSQL" target="_blank"><img src="https://img.shields.io/twitter/follow/ElectricSQL.svg?style=social&label=Follow @ElectricSQL"></a>
|
|
24
24
|
</p>
|
|
@@ -29,7 +29,7 @@ Real-time Postgres sync for modern apps.
|
|
|
29
29
|
|
|
30
30
|
Electric provides an [HTTP interface](https://electric-sql.com/docs/api/http) to Postgres to enable a massive number of clients to query and get real-time updates to subsets of the database, called [Shapes](https://electric-sql.com//docs/guides/shapes). In this way, Electric turns Postgres into a real-time database.
|
|
31
31
|
|
|
32
|
-
The TypeScript client helps ease reading Shapes from the HTTP API in the browser and other JavaScript environments, such as edge functions and server-side Node/Bun/Deno applications. It supports both fine-grained and coarse-grained reactivity patterns — you can subscribe to see every row that changes, or you can just subscribe to get the whole shape whenever it changes.
|
|
32
|
+
The TypeScript client helps ease reading Shapes from the HTTP API in the browser and other JavaScript environments, such as edge functions and server-side Node/Bun/Deno applications. It supports both fine-grained and coarse-grained reactivity patterns — you can subscribe to see every row that changes, or you can just subscribe to get the whole shape whenever it changes. The client also supports dynamic options through function-based params and headers, making it easy to handle auth tokens, user context, and other runtime values.
|
|
33
33
|
|
|
34
34
|
## Install
|
|
35
35
|
|
|
@@ -143,7 +143,7 @@ Runtime errors:
|
|
|
143
143
|
|
|
144
144
|
See the [typescript client docs on the website](https://electric-sql.com/docs/api/clients/typescript#error-handling) for more details on error handling.
|
|
145
145
|
|
|
146
|
-
And in general, see the [docs website](https://electric-sql.com) and [examples
|
|
146
|
+
And in general, see the [docs website](https://electric-sql.com) and [examples](https://electric-sql.com/demos) for more information.
|
|
147
147
|
|
|
148
148
|
## Develop
|
|
149
149
|
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -82,7 +82,8 @@ __export(src_exports, {
|
|
|
82
82
|
Shape: () => Shape,
|
|
83
83
|
ShapeStream: () => ShapeStream,
|
|
84
84
|
isChangeMessage: () => isChangeMessage,
|
|
85
|
-
isControlMessage: () => isControlMessage
|
|
85
|
+
isControlMessage: () => isControlMessage,
|
|
86
|
+
resolveValue: () => resolveValue
|
|
86
87
|
});
|
|
87
88
|
module.exports = __toCommonJS(src_exports);
|
|
88
89
|
|
|
@@ -505,14 +506,45 @@ var RESERVED_PARAMS = /* @__PURE__ */ new Set([
|
|
|
505
506
|
LIVE_QUERY_PARAM,
|
|
506
507
|
OFFSET_QUERY_PARAM
|
|
507
508
|
]);
|
|
509
|
+
function resolveValue(value) {
|
|
510
|
+
return __async(this, null, function* () {
|
|
511
|
+
if (typeof value === `function`) {
|
|
512
|
+
return value();
|
|
513
|
+
}
|
|
514
|
+
return value;
|
|
515
|
+
});
|
|
516
|
+
}
|
|
508
517
|
function toInternalParams(params) {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
518
|
+
return __async(this, null, function* () {
|
|
519
|
+
const entries = Object.entries(params);
|
|
520
|
+
const resolvedEntries = yield Promise.all(
|
|
521
|
+
entries.map((_0) => __async(this, [_0], function* ([key, value]) {
|
|
522
|
+
if (value === void 0) return [key, void 0];
|
|
523
|
+
const resolvedValue = yield resolveValue(value);
|
|
524
|
+
return [
|
|
525
|
+
key,
|
|
526
|
+
Array.isArray(resolvedValue) ? resolvedValue.join(`,`) : resolvedValue
|
|
527
|
+
];
|
|
528
|
+
}))
|
|
529
|
+
);
|
|
530
|
+
return Object.fromEntries(
|
|
531
|
+
resolvedEntries.filter(([_, value]) => value !== void 0)
|
|
532
|
+
);
|
|
533
|
+
});
|
|
514
534
|
}
|
|
515
|
-
|
|
535
|
+
function resolveHeaders(headers) {
|
|
536
|
+
return __async(this, null, function* () {
|
|
537
|
+
if (!headers) return {};
|
|
538
|
+
const entries = Object.entries(headers);
|
|
539
|
+
const resolvedEntries = yield Promise.all(
|
|
540
|
+
entries.map((_0) => __async(this, [_0], function* ([key, value]) {
|
|
541
|
+
return [key, yield resolveValue(value)];
|
|
542
|
+
}))
|
|
543
|
+
);
|
|
544
|
+
return Object.fromEntries(resolvedEntries);
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
var _error, _fetchClient2, _messageParser, _subscribers, _started, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _connected, _shapeHandle, _schema, _onError, _ShapeStream_instances, start_fn, publish_fn, sendErrorToSubscribers_fn, reset_fn;
|
|
516
548
|
var ShapeStream = class {
|
|
517
549
|
constructor(options) {
|
|
518
550
|
__privateAdd(this, _ShapeStream_instances);
|
|
@@ -520,6 +552,7 @@ var ShapeStream = class {
|
|
|
520
552
|
__privateAdd(this, _fetchClient2);
|
|
521
553
|
__privateAdd(this, _messageParser);
|
|
522
554
|
__privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
|
|
555
|
+
__privateAdd(this, _started, false);
|
|
523
556
|
__privateAdd(this, _lastOffset);
|
|
524
557
|
__privateAdd(this, _liveCacheBuster);
|
|
525
558
|
// Seconds since our Electric Epoch 😎
|
|
@@ -549,7 +582,6 @@ var ShapeStream = class {
|
|
|
549
582
|
__privateSet(this, _fetchClient2, createFetchWithResponseHeadersCheck(
|
|
550
583
|
createFetchWithChunkBuffer(fetchWithBackoffClient)
|
|
551
584
|
));
|
|
552
|
-
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
553
585
|
}
|
|
554
586
|
get shapeHandle() {
|
|
555
587
|
return __privateGet(this, _shapeHandle);
|
|
@@ -567,6 +599,7 @@ var ShapeStream = class {
|
|
|
567
599
|
}) {
|
|
568
600
|
const subscriptionId = Math.random();
|
|
569
601
|
__privateGet(this, _subscribers).set(subscriptionId, [callback, onError]);
|
|
602
|
+
if (!__privateGet(this, _started)) __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
570
603
|
return () => {
|
|
571
604
|
__privateGet(this, _subscribers).delete(subscriptionId);
|
|
572
605
|
};
|
|
@@ -596,6 +629,7 @@ _error = new WeakMap();
|
|
|
596
629
|
_fetchClient2 = new WeakMap();
|
|
597
630
|
_messageParser = new WeakMap();
|
|
598
631
|
_subscribers = new WeakMap();
|
|
632
|
+
_started = new WeakMap();
|
|
599
633
|
_lastOffset = new WeakMap();
|
|
600
634
|
_liveCacheBuster = new WeakMap();
|
|
601
635
|
_lastSyncedAt = new WeakMap();
|
|
@@ -608,20 +642,20 @@ _ShapeStream_instances = new WeakSet();
|
|
|
608
642
|
start_fn = function() {
|
|
609
643
|
return __async(this, null, function* () {
|
|
610
644
|
var _a, _b;
|
|
645
|
+
if (__privateGet(this, _started)) throw new Error(`Cannot start stream twice`);
|
|
646
|
+
__privateSet(this, _started, true);
|
|
611
647
|
try {
|
|
612
648
|
while (!((_a = this.options.signal) == null ? void 0 : _a.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
|
|
613
649
|
const { url, signal } = this.options;
|
|
650
|
+
const [requestHeaders, params] = yield Promise.all([
|
|
651
|
+
resolveHeaders(this.options.headers),
|
|
652
|
+
this.options.params ? toInternalParams(this.options.params) : void 0
|
|
653
|
+
]);
|
|
654
|
+
if (params) {
|
|
655
|
+
validateParams(params);
|
|
656
|
+
}
|
|
614
657
|
const fetchUrl = new URL(url);
|
|
615
|
-
if (
|
|
616
|
-
const reservedParams = Object.keys(this.options.params).filter(
|
|
617
|
-
(key) => RESERVED_PARAMS.has(key)
|
|
618
|
-
);
|
|
619
|
-
if (reservedParams.length > 0) {
|
|
620
|
-
throw new Error(
|
|
621
|
-
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
622
|
-
);
|
|
623
|
-
}
|
|
624
|
-
const params = toInternalParams(this.options.params);
|
|
658
|
+
if (params) {
|
|
625
659
|
if (params.table)
|
|
626
660
|
fetchUrl.searchParams.set(TABLE_QUERY_PARAM, params.table);
|
|
627
661
|
if (params.where)
|
|
@@ -658,7 +692,7 @@ start_fn = function() {
|
|
|
658
692
|
try {
|
|
659
693
|
response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
|
|
660
694
|
signal,
|
|
661
|
-
headers:
|
|
695
|
+
headers: requestHeaders
|
|
662
696
|
});
|
|
663
697
|
__privateSet(this, _connected, true);
|
|
664
698
|
} catch (e) {
|
|
@@ -718,6 +752,7 @@ start_fn = function() {
|
|
|
718
752
|
if (`headers` in retryOpts) {
|
|
719
753
|
this.options.headers = retryOpts.headers;
|
|
720
754
|
}
|
|
755
|
+
__privateSet(this, _started, false);
|
|
721
756
|
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
722
757
|
}
|
|
723
758
|
return;
|
|
@@ -764,6 +799,15 @@ ShapeStream.Replica = {
|
|
|
764
799
|
FULL: `full`,
|
|
765
800
|
DEFAULT: `default`
|
|
766
801
|
};
|
|
802
|
+
function validateParams(params) {
|
|
803
|
+
if (!params) return;
|
|
804
|
+
const reservedParams = Object.keys(params).filter(
|
|
805
|
+
(key) => RESERVED_PARAMS.has(key)
|
|
806
|
+
);
|
|
807
|
+
if (reservedParams.length > 0) {
|
|
808
|
+
throw new ReservedParamError(reservedParams);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
767
811
|
function validateOptions(options) {
|
|
768
812
|
if (!options.url) {
|
|
769
813
|
throw new MissingShapeUrlError();
|
|
@@ -774,14 +818,7 @@ function validateOptions(options) {
|
|
|
774
818
|
if (options.offset !== void 0 && options.offset !== `-1` && !options.handle) {
|
|
775
819
|
throw new MissingShapeHandleError();
|
|
776
820
|
}
|
|
777
|
-
|
|
778
|
-
const reservedParams = Object.keys(options.params).filter(
|
|
779
|
-
(key) => RESERVED_PARAMS.has(key)
|
|
780
|
-
);
|
|
781
|
-
if (reservedParams.length > 0) {
|
|
782
|
-
throw new ReservedParamError(reservedParams);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
821
|
+
validateParams(options.params);
|
|
785
822
|
return;
|
|
786
823
|
}
|
|
787
824
|
|
|
@@ -931,6 +968,7 @@ notify_fn = function() {
|
|
|
931
968
|
Shape,
|
|
932
969
|
ShapeStream,
|
|
933
970
|
isChangeMessage,
|
|
934
|
-
isControlMessage
|
|
971
|
+
isControlMessage,
|
|
972
|
+
resolveValue
|
|
935
973
|
});
|
|
936
974
|
//# sourceMappingURL=index.cjs.map
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts","../../src/error.ts","../../src/parser.ts","../../src/helpers.ts","../../src/constants.ts","../../src/fetch.ts","../../src/client.ts","../../src/shape.ts"],"sourcesContent":["export * from './client'\nexport * from './shape'\nexport * from './types'\nexport { isChangeMessage, isControlMessage } from './helpers'\nexport { FetchError } from './error'\nexport { type BackoffOptions, BackoffDefaults } from './fetch'\n","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 (contentType && contentType.includes(`application/json`)) {\n json = (await response.json()) as object\n } else {\n text = await response.text()\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","import { ColumnInfo, GetExtensions, Message, Row, Schema, Value } from './types'\nimport { ParserNullValueError } from './error'\n\ntype NullToken = null | `NULL`\ntype Token = Exclude<string, NullToken>\ntype NullableToken = Token | NullToken\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\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?: ParseFunction<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 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 &&\n xs.push(parser ? parser(x.slice(last, i)) : x.slice(last, i))\n last = i + 1\n break\n } else if (char === `,` && p !== `}` && p !== `\"`) {\n xs.push(parser ? parser(x.slice(last, i)) : x.slice(last, i))\n last = i + 1\n }\n p = char\n }\n last < i &&\n xs.push(parser ? parser(x.slice(last, i + 1)) : x.slice(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 constructor(parser?: Parser<GetExtensions<T>>) {\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 }\n\n parse(messages: string, schema: Schema): Message<T>[] {\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 if (key === `value` && typeof value === `object` && value !== null) {\n // Parse the row values\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 value\n }) as Message<T>[]\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 (isPgNull(value)) {\n if (!isNullable) {\n throw new ParserNullValueError(columnName ?? `unknown`)\n }\n return null\n }\n return parser(value, columnInfo)\n }\n}\n\nfunction isPgNull(value: NullableToken): value is NullToken {\n return value === null || value === `NULL`\n}\n","import { ChangeMessage, ControlMessage, Message, Row } 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 `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 !isChangeMessage(message)\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","export const LIVE_CACHE_BUSTER_HEADER = `electric-cursor`\nexport const SHAPE_HANDLE_HEADER = `electric-handle`\nexport const CHUNK_LAST_OFFSET_HEADER = `electric-offset`\nexport const SHAPE_SCHEMA_HEADER = `electric-schema`\nexport const CHUNK_UP_TO_DATE_HEADER = `electric-up-to-date`\nexport const COLUMNS_QUERY_PARAM = `columns`\nexport const LIVE_CACHE_BUSTER_QUERY_PARAM = `cursor`\nexport const 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`\n","import {\n CHUNK_LAST_OFFSET_HEADER,\n CHUNK_UP_TO_DATE_HEADER,\n LIVE_QUERY_PARAM,\n OFFSET_QUERY_PARAM,\n SHAPE_HANDLE_HEADER,\n SHAPE_HANDLE_QUERY_PARAM,\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 */\n maxDelay: number\n multiplier: number\n onFailedAttempt?: () => void\n debug?: boolean\n}\n\nexport const BackoffDefaults = {\n initialDelay: 100,\n maxDelay: 10_000,\n multiplier: 1.3,\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 } = 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 /* eslint-disable no-constant-condition -- we re-fetch the shape log\n * continuously until we get a non-ok response. For recoverable errors,\n * we retry the fetch with exponential backoff. Users can pass in an\n * AbortController to abort the fetching an any point.\n * */\n while (true) {\n /* eslint-enable no-constant-condition */\n try {\n const result = await fetchClient(...args)\n if (result.ok) return result\n else throw await FetchError.fromResponse(result, url.toString())\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 // Exponentially backoff on errors.\n // Wait for the current delay duration\n await new Promise((resolve) => setTimeout(resolve, delay))\n\n // Increase the delay for the next attempt\n delay = Math.min(delay * multiplier, maxDelay)\n\n if (debug) {\n attempt++\n console.log(`Retry attempt #${attempt} after ${delay}ms`)\n }\n }\n }\n }\n }\n}\n\ninterface ChunkPrefetchOptions {\n maxChunksToPrefetch: number\n}\n\nconst ChunkPrefetchDefaults = {\n maxChunksToPrefetch: 2,\n}\n\n/**\n * Creates a fetch client that prefetches subsequent log chunks for\n * consumption by the shape stream without waiting for the chunk bodies\n * themselves to be loaded.\n *\n * @param fetchClient the client to wrap\n * @param prefetchOptions options to configure prefetching\n * @returns wrapped client with prefetch capabilities\n */\nexport function createFetchWithChunkBuffer(\n fetchClient: typeof fetch,\n prefetchOptions: ChunkPrefetchOptions = ChunkPrefetchDefaults\n): typeof fetch {\n const { maxChunksToPrefetch } = prefetchOptions\n\n let prefetchQueue: PrefetchQueue\n\n const prefetchClient = async (...args: Parameters<typeof fetchClient>) => {\n const url = args[0].toString()\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 prefetchQueue?.abort()\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 `electric-offset`,\n `electric-handle`,\n]\n\nexport const requiredLiveResponseHeaders = [`electric-cursor`]\n\nexport const requiredNonLiveResponseHeaders = [`electric-schema`]\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 addMissingHeaders(requiredElectricResponseHeaders)\n\n const input = args[0]\n const urlString = input.toString()\n const url = new URL(urlString)\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 }\n\n consume(...args: Parameters<typeof fetch>): Promise<Response> | void {\n const url = args[0].toString()\n\n const request = this.#prefetchQueue.get(url)?.[0]\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 (!request || url !== this.#queueHeadUrl) return\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 request = this.#fetchClient(url, {\n ...(args[1] ?? {}),\n signal: chainAborter(aborter, args[1]?.signal),\n })\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 } catch (_) {\n // ignore prefetch errors\n }\n }\n}\n\n/**\n * Generate the next chunk's URL if the url and response are valid\n */\nfunction getNextChunkUrl(url: string, res: Response): string | void {\n const shapeHandle = res.headers.get(SHAPE_HANDLE_HEADER)\n const lastOffset = res.headers.get(CHUNK_LAST_OFFSET_HEADER)\n const isUpToDate = res.headers.has(CHUNK_UP_TO_DATE_HEADER)\n\n // only prefetch if shape handle and offset for next chunk are available, and\n // response is not already up-to-date\n if (!shapeHandle || !lastOffset || isUpToDate) return\n\n const nextUrl = new URL(url)\n\n // don't prefetch live requests, rushing them will only\n // potentially miss more recent data\n if (nextUrl.searchParams.has(LIVE_QUERY_PARAM)) return\n\n 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): AbortSignal {\n if (!sourceSignal) return aborter.signal\n if (sourceSignal.aborted) aborter.abort()\n else\n sourceSignal.addEventListener(`abort`, () => aborter.abort(), {\n once: true,\n })\n return aborter.signal\n}\n","import {\n Message,\n Offset,\n Schema,\n Row,\n MaybePromise,\n GetExtensions,\n} from './types'\nimport { MessageParser, Parser } from './parser'\nimport { isUpToDateMessage } from './helpers'\nimport {\n FetchError,\n FetchBackoffAbortError,\n MissingShapeUrlError,\n InvalidSignalError,\n MissingShapeHandleError,\n ReservedParamError,\n} from './error'\nimport {\n BackoffDefaults,\n BackoffOptions,\n createFetchWithBackoff,\n createFetchWithChunkBuffer,\n createFetchWithResponseHeadersCheck,\n} from './fetch'\nimport {\n CHUNK_LAST_OFFSET_HEADER,\n LIVE_CACHE_BUSTER_HEADER,\n LIVE_CACHE_BUSTER_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 TABLE_QUERY_PARAM,\n REPLICA_PARAM,\n} from './constants'\n\nconst RESERVED_PARAMS = new Set([\n LIVE_CACHE_BUSTER_QUERY_PARAM,\n SHAPE_HANDLE_QUERY_PARAM,\n LIVE_QUERY_PARAM,\n OFFSET_QUERY_PARAM,\n])\n\ntype Replica = `full` | `default`\n\n/**\n * PostgreSQL-specific shape parameters that can be provided externally\n */\ntype PostgresParams = {\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 */\n columns?: string[]\n\n /** The where clauses for the shape */\n where?: 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.\n *\n * Setting `replica` to `full` will result in higher bandwidth\n * usage and so is not generally recommended.\n */\n replica?: Replica\n}\n\ntype ReservedParamKeys =\n | typeof COLUMNS_QUERY_PARAM\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 TABLE_QUERY_PARAM\n | typeof WHERE_QUERY_PARAM\n | typeof REPLICA_PARAM\n\n/**\n * External params type - what users provide.\n * Includes documented PostgreSQL params and allows string or string[] values for any additional params.\n */\ntype ExternalParamsRecord = Partial<PostgresParams> & {\n [K in string as K extends ReservedParamKeys ? never : K]: string | 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]: string\n}\n\n/**\n * Helper function to convert external params to internal format\n */\nfunction toInternalParams(params: ExternalParamsRecord): InternalParamsRecord {\n const result: InternalParamsRecord = {}\n for (const [key, value] of Object.entries(params)) {\n result[key] = Array.isArray(value) ? value.join(`,`) : value\n }\n return result\n}\n\ntype RetryOpts = {\n params?: ExternalParamsRecord\n headers?: Record<string, string>\n}\n\ntype ShapeStreamErrorHandler = (\n error: Error\n) => void | RetryOpts | Promise<void | RetryOpts>\n\n/**\n * Options for constructing a ShapeStream.\n */\nexport interface ShapeStreamOptions<T = never> {\n /**\n * The full URL to where the Shape is served. This can either be the Electric server\n * directly or a proxy. E.g. for a local Electric instance, you might set `http://localhost:3000/v1/shape`\n */\n url: string\n\n /**\n * The \"offset\" on the shape log. This is typically not set as the ShapeStream\n * will handle this automatically. A common scenario where you might pass an offset\n * is if you're maintaining a local cache of the log. If you've gone offline\n * and are re-starting a ShapeStream to catch-up to the latest state of the Shape,\n * you'd pass in the last offset and shapeHandle you'd seen from the Electric server\n * so it knows at what point in the shape to catch you up from.\n */\n offset?: Offset\n\n /**\n * Similar to `offset`, this isn't typically used unless you're maintaining\n * a cache of the shape log.\n */\n handle?: string\n\n /**\n * HTTP headers to attach to requests made by the client.\n * Can be used for adding authentication headers.\n */\n headers?: Record<string, string>\n\n /**\n * Additional request parameters to attach to the URL.\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 signal?: AbortSignal\n fetchClient?: typeof fetch\n backoffOptions?: BackoffOptions\n parser?: Parser<T>\n\n /**\n * A function for handling shapestream errors.\n * This is optional, when it is not provided any shapestream errors will be thrown.\n * If the function returns an object containing parameters and/or headers\n * the shapestream will apply those changes and try syncing again.\n * If the function returns void the shapestream is stopped.\n */\n onError?: ShapeStreamErrorHandler\n}\n\nexport interface ShapeStreamInterface<T extends Row<unknown> = Row> {\n subscribe(\n callback: (messages: Message<T>[]) => MaybePromise<void>,\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\n isUpToDate: boolean\n lastOffset: Offset\n shapeHandle?: string\n error?: unknown\n}\n\n/**\n * Reads updates to a shape from Electric using HTTP requests and long polling. Notifies subscribers\n * 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 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 #messageParser: MessageParser<T>\n\n readonly #subscribers = new Map<\n number,\n [\n (messages: Message<T>[]) => MaybePromise<void>,\n ((error: Error) => void) | undefined,\n ]\n >()\n\n #lastOffset: Offset\n #liveCacheBuster: string // Seconds since our Electric Epoch 😎\n #lastSyncedAt?: number // unix time\n #isUpToDate: boolean = false\n #connected: boolean = false\n #shapeHandle?: string\n #schema?: Schema\n #onError?: ShapeStreamErrorHandler\n\n constructor(options: ShapeStreamOptions<GetExtensions<T>>) {\n this.options = { subscribe: true, ...options }\n validateOptions(this.options)\n this.#lastOffset = this.options.offset ?? `-1`\n this.#liveCacheBuster = ``\n this.#shapeHandle = this.options.handle\n this.#messageParser = new MessageParser<T>(options.parser)\n this.#onError = this.options.onError\n\n const baseFetchClient =\n options.fetchClient ??\n ((...args: Parameters<typeof fetch>) => fetch(...args))\n\n const fetchWithBackoffClient = createFetchWithBackoff(baseFetchClient, {\n ...(options.backoffOptions ?? BackoffDefaults),\n onFailedAttempt: () => {\n this.#connected = false\n options.backoffOptions?.onFailedAttempt?.()\n },\n })\n\n this.#fetchClient = createFetchWithResponseHeadersCheck(\n createFetchWithChunkBuffer(fetchWithBackoffClient)\n )\n\n this.#start()\n }\n\n get shapeHandle() {\n return this.#shapeHandle\n }\n\n get error() {\n return this.#error\n }\n\n get isUpToDate() {\n return this.#isUpToDate\n }\n\n get lastOffset() {\n return this.#lastOffset\n }\n\n async #start() {\n try {\n while (\n (!this.options.signal?.aborted && !this.#isUpToDate) ||\n this.options.subscribe\n ) {\n const { url, signal } = this.options\n\n const fetchUrl = new URL(url)\n\n // Add any custom parameters first\n if (this.options.params) {\n // Check for reserved parameter names\n const reservedParams = Object.keys(this.options.params).filter(\n (key) => RESERVED_PARAMS.has(key)\n )\n if (reservedParams.length > 0) {\n throw new Error(\n `Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`\n )\n }\n\n // Add PostgreSQL-specific parameters from params\n const params = toInternalParams(this.options.params)\n if (params.table)\n fetchUrl.searchParams.set(TABLE_QUERY_PARAM, params.table)\n if (params.where)\n fetchUrl.searchParams.set(WHERE_QUERY_PARAM, params.where)\n if (params.columns)\n fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, params.columns)\n if (params.replica)\n fetchUrl.searchParams.set(REPLICA_PARAM, params.replica)\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\n for (const [key, value] of Object.entries(customParams)) {\n fetchUrl.searchParams.set(key, value as string)\n }\n }\n\n // Add Electric's internal parameters\n fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, this.#lastOffset)\n\n if (this.#isUpToDate) {\n fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`)\n fetchUrl.searchParams.set(\n LIVE_CACHE_BUSTER_QUERY_PARAM,\n this.#liveCacheBuster\n )\n }\n\n if (this.#shapeHandle) {\n // This should probably be a header for better cache breaking?\n fetchUrl.searchParams.set(\n SHAPE_HANDLE_QUERY_PARAM,\n this.#shapeHandle!\n )\n }\n\n // sort query params in-place for stable URLs and improved cache hits\n fetchUrl.searchParams.sort()\n\n let response!: Response\n try {\n response = await this.#fetchClient(fetchUrl.toString(), {\n signal,\n headers: this.options.headers,\n })\n this.#connected = true\n } catch (e) {\n if (e instanceof FetchBackoffAbortError) break // interrupted\n if (!(e instanceof FetchError)) throw e // should never happen\n if (e.status == 409) {\n // Upon receiving a 409, we should start from scratch\n // with the newly provided shape handle\n const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER]\n this.#reset(newShapeHandle)\n await this.#publish(e.json as Message<T>[])\n continue\n } else if (e.status >= 400 && e.status < 500) {\n // Notify subscribers\n this.#sendErrorToSubscribers(e)\n\n // 400 errors are not actionable without additional user input,\n // so we exit the loop\n throw e\n }\n }\n\n const { headers, status } = response\n const shapeHandle = headers.get(SHAPE_HANDLE_HEADER)\n if (shapeHandle) {\n this.#shapeHandle = shapeHandle\n }\n\n const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER)\n if (lastOffset) {\n this.#lastOffset = lastOffset as Offset\n }\n\n const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER)\n if (liveCacheBuster) {\n this.#liveCacheBuster = liveCacheBuster\n }\n\n const getSchema = (): Schema => {\n const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER)\n return schemaHeader ? JSON.parse(schemaHeader) : {}\n }\n this.#schema = this.#schema ?? getSchema()\n\n const messages = status === 204 ? `[]` : await response.text()\n\n if (status === 204) {\n // There's no content so we are live and up to date\n this.#lastSyncedAt = Date.now()\n }\n\n const batch = this.#messageParser.parse(messages, this.#schema)\n\n // Update isUpToDate\n if (batch.length > 0) {\n const lastMessage = batch[batch.length - 1]\n if (isUpToDateMessage(lastMessage)) {\n this.#lastSyncedAt = Date.now()\n this.#isUpToDate = true\n }\n\n await this.#publish(batch)\n }\n }\n } catch (err) {\n this.#error = err\n if (this.#onError) {\n const retryOpts = await this.#onError(err as Error)\n if (typeof retryOpts === `object`) {\n this.#reset()\n\n if (`params` in retryOpts) {\n this.options.params = retryOpts.params\n }\n\n if (`headers` in retryOpts) {\n this.options.headers = retryOpts.headers\n }\n\n // Restart\n this.#start()\n }\n return\n }\n\n // If no handler is provided for errors just throw so the error still bubbles up.\n throw err\n } finally {\n this.#connected = false\n }\n }\n\n subscribe(\n callback: (messages: Message<T>[]) => MaybePromise<void>,\n onError: (error: Error) => void = () => {}\n ) {\n const subscriptionId = Math.random()\n\n this.#subscribers.set(subscriptionId, [callback, onError])\n\n return () => {\n this.#subscribers.delete(subscriptionId)\n }\n }\n\n unsubscribeAll(): void {\n this.#subscribers.clear()\n }\n\n /** Unix time at which we last synced. Undefined when `isLoading` is true. */\n lastSyncedAt(): number | undefined {\n return this.#lastSyncedAt\n }\n\n /** Time elapsed since last sync (in ms). Infinity if we did not yet sync. */\n lastSynced(): number {\n if (this.#lastSyncedAt === undefined) return Infinity\n return Date.now() - this.#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 afterwise. */\n isLoading(): boolean {\n return !this.#isUpToDate\n }\n\n async #publish(messages: Message<T>[]): Promise<void> {\n await Promise.all(\n Array.from(this.#subscribers.values()).map(async ([callback, __]) => {\n try {\n await callback(messages)\n } catch (err) {\n queueMicrotask(() => {\n throw err\n })\n }\n })\n )\n }\n\n #sendErrorToSubscribers(error: Error) {\n this.#subscribers.forEach(([_, errorFn]) => {\n errorFn?.(error)\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.#lastOffset = `-1`\n this.#liveCacheBuster = ``\n this.#shapeHandle = handle\n this.#isUpToDate = false\n this.#connected = false\n this.#schema = undefined\n }\n}\n\nfunction validateOptions<T>(options: Partial<ShapeStreamOptions<T>>): void {\n if (!options.url) {\n throw new MissingShapeUrlError()\n }\n if (options.signal && !(options.signal instanceof AbortSignal)) {\n throw new InvalidSignalError()\n }\n\n if (\n options.offset !== undefined &&\n options.offset !== `-1` &&\n !options.handle\n ) {\n throw new MissingShapeHandleError()\n }\n\n // Check for reserved parameter names\n if (options.params) {\n const reservedParams = Object.keys(options.params).filter((key) =>\n RESERVED_PARAMS.has(key)\n )\n if (reservedParams.length > 0) {\n throw new ReservedParamError(reservedParams)\n }\n }\n return\n}\n","import { Message, Offset, Row } from './types'\nimport { isChangeMessage, isControlMessage } from './helpers'\nimport { FetchError } from './error'\nimport { 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\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<number, ShapeChangedCallback<T>>()\n\n #hasNotifiedSubscribersUpToDate: boolean = false\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.stream.isUpToDate\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 subscribe(callback: ShapeChangedCallback<T>): () => void {\n const subscriptionId = Math.random()\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 dataMayHaveChanged = false\n let isUpToDate = false\n let newlyUpToDate = false\n\n messages.forEach((message) => {\n if (isChangeMessage(message)) {\n dataMayHaveChanged = [`insert`, `update`, `delete`].includes(\n message.headers.operation\n )\n\n switch (message.headers.operation) {\n case `insert`:\n this.#data.set(message.key, message.value)\n break\n case `update`:\n this.#data.set(message.key, {\n ...this.#data.get(message.key)!,\n ...message.value,\n })\n break\n case `delete`:\n this.#data.delete(message.key)\n break\n }\n }\n\n if (isControlMessage(message)) {\n switch (message.headers.control) {\n case `up-to-date`:\n isUpToDate = true\n if (!this.#hasNotifiedSubscribersUpToDate) {\n newlyUpToDate = true\n }\n break\n case `must-refetch`:\n this.#data.clear()\n this.#error = false\n this.#hasNotifiedSubscribersUpToDate = false\n isUpToDate = false\n newlyUpToDate = false\n break\n }\n }\n })\n\n // Always notify subscribers when the Shape first is up to date.\n // FIXME this would be cleaner with a simple state machine.\n if (newlyUpToDate || (isUpToDate && dataMayHaveChanged)) {\n this.#hasNotifiedSubscribersUpToDate = true\n this.#notify()\n }\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":["key","value","_fetchClient","_a","_b","_subscribers","_error"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/error.ts","../../src/parser.ts","../../src/helpers.ts","../../src/constants.ts","../../src/fetch.ts","../../src/client.ts","../../src/shape.ts"],"sourcesContent":["export * from './client'\nexport * from './shape'\nexport * from './types'\nexport { isChangeMessage, isControlMessage } from './helpers'\nexport { FetchError } from './error'\nexport { type BackoffOptions, BackoffDefaults } from './fetch'\n","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 (contentType && contentType.includes(`application/json`)) {\n json = (await response.json()) as object\n } else {\n text = await response.text()\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","import { ColumnInfo, GetExtensions, Message, Row, Schema, Value } from './types'\nimport { ParserNullValueError } from './error'\n\ntype NullToken = null | `NULL`\ntype Token = Exclude<string, NullToken>\ntype NullableToken = Token | NullToken\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\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?: ParseFunction<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 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 &&\n xs.push(parser ? parser(x.slice(last, i)) : x.slice(last, i))\n last = i + 1\n break\n } else if (char === `,` && p !== `}` && p !== `\"`) {\n xs.push(parser ? parser(x.slice(last, i)) : x.slice(last, i))\n last = i + 1\n }\n p = char\n }\n last < i &&\n xs.push(parser ? parser(x.slice(last, i + 1)) : x.slice(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 constructor(parser?: Parser<GetExtensions<T>>) {\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 }\n\n parse(messages: string, schema: Schema): Message<T>[] {\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 if (key === `value` && typeof value === `object` && value !== null) {\n // Parse the row values\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 value\n }) as Message<T>[]\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 (isPgNull(value)) {\n if (!isNullable) {\n throw new ParserNullValueError(columnName ?? `unknown`)\n }\n return null\n }\n return parser(value, columnInfo)\n }\n}\n\nfunction isPgNull(value: NullableToken): value is NullToken {\n return value === null || value === `NULL`\n}\n","import { ChangeMessage, ControlMessage, Message, Row } 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 `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 !isChangeMessage(message)\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","export const LIVE_CACHE_BUSTER_HEADER = `electric-cursor`\nexport const SHAPE_HANDLE_HEADER = `electric-handle`\nexport const CHUNK_LAST_OFFSET_HEADER = `electric-offset`\nexport const SHAPE_SCHEMA_HEADER = `electric-schema`\nexport const CHUNK_UP_TO_DATE_HEADER = `electric-up-to-date`\nexport const COLUMNS_QUERY_PARAM = `columns`\nexport const LIVE_CACHE_BUSTER_QUERY_PARAM = `cursor`\nexport const 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`\n","import {\n CHUNK_LAST_OFFSET_HEADER,\n CHUNK_UP_TO_DATE_HEADER,\n LIVE_QUERY_PARAM,\n OFFSET_QUERY_PARAM,\n SHAPE_HANDLE_HEADER,\n SHAPE_HANDLE_QUERY_PARAM,\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 */\n maxDelay: number\n multiplier: number\n onFailedAttempt?: () => void\n debug?: boolean\n}\n\nexport const BackoffDefaults = {\n initialDelay: 100,\n maxDelay: 10_000,\n multiplier: 1.3,\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 } = 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 /* eslint-disable no-constant-condition -- we re-fetch the shape log\n * continuously until we get a non-ok response. For recoverable errors,\n * we retry the fetch with exponential backoff. Users can pass in an\n * AbortController to abort the fetching an any point.\n * */\n while (true) {\n /* eslint-enable no-constant-condition */\n try {\n const result = await fetchClient(...args)\n if (result.ok) return result\n else throw await FetchError.fromResponse(result, url.toString())\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 // Exponentially backoff on errors.\n // Wait for the current delay duration\n await new Promise((resolve) => setTimeout(resolve, delay))\n\n // Increase the delay for the next attempt\n delay = Math.min(delay * multiplier, maxDelay)\n\n if (debug) {\n attempt++\n console.log(`Retry attempt #${attempt} after ${delay}ms`)\n }\n }\n }\n }\n }\n}\n\ninterface ChunkPrefetchOptions {\n maxChunksToPrefetch: number\n}\n\nconst ChunkPrefetchDefaults = {\n maxChunksToPrefetch: 2,\n}\n\n/**\n * Creates a fetch client that prefetches subsequent log chunks for\n * consumption by the shape stream without waiting for the chunk bodies\n * themselves to be loaded.\n *\n * @param fetchClient the client to wrap\n * @param prefetchOptions options to configure prefetching\n * @returns wrapped client with prefetch capabilities\n */\nexport function createFetchWithChunkBuffer(\n fetchClient: typeof fetch,\n prefetchOptions: ChunkPrefetchOptions = ChunkPrefetchDefaults\n): typeof fetch {\n const { maxChunksToPrefetch } = prefetchOptions\n\n let prefetchQueue: PrefetchQueue\n\n const prefetchClient = async (...args: Parameters<typeof fetchClient>) => {\n const url = args[0].toString()\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 prefetchQueue?.abort()\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 `electric-offset`,\n `electric-handle`,\n]\n\nexport const requiredLiveResponseHeaders = [`electric-cursor`]\n\nexport const requiredNonLiveResponseHeaders = [`electric-schema`]\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 addMissingHeaders(requiredElectricResponseHeaders)\n\n const input = args[0]\n const urlString = input.toString()\n const url = new URL(urlString)\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 }\n\n consume(...args: Parameters<typeof fetch>): Promise<Response> | void {\n const url = args[0].toString()\n\n const request = this.#prefetchQueue.get(url)?.[0]\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 (!request || url !== this.#queueHeadUrl) return\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 request = this.#fetchClient(url, {\n ...(args[1] ?? {}),\n signal: chainAborter(aborter, args[1]?.signal),\n })\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 } catch (_) {\n // ignore prefetch errors\n }\n }\n}\n\n/**\n * Generate the next chunk's URL if the url and response are valid\n */\nfunction getNextChunkUrl(url: string, res: Response): string | void {\n const shapeHandle = res.headers.get(SHAPE_HANDLE_HEADER)\n const lastOffset = res.headers.get(CHUNK_LAST_OFFSET_HEADER)\n const isUpToDate = res.headers.has(CHUNK_UP_TO_DATE_HEADER)\n\n // only prefetch if shape handle and offset for next chunk are available, and\n // response is not already up-to-date\n if (!shapeHandle || !lastOffset || isUpToDate) return\n\n const nextUrl = new URL(url)\n\n // don't prefetch live requests, rushing them will only\n // potentially miss more recent data\n if (nextUrl.searchParams.has(LIVE_QUERY_PARAM)) return\n\n 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): AbortSignal {\n if (!sourceSignal) return aborter.signal\n if (sourceSignal.aborted) aborter.abort()\n else\n sourceSignal.addEventListener(`abort`, () => aborter.abort(), {\n once: true,\n })\n return aborter.signal\n}\n","import {\n Message,\n Offset,\n Schema,\n Row,\n MaybePromise,\n GetExtensions,\n} from './types'\nimport { MessageParser, Parser } from './parser'\nimport { isUpToDateMessage } from './helpers'\nimport {\n FetchError,\n FetchBackoffAbortError,\n MissingShapeUrlError,\n InvalidSignalError,\n MissingShapeHandleError,\n ReservedParamError,\n} from './error'\nimport {\n BackoffDefaults,\n BackoffOptions,\n createFetchWithBackoff,\n createFetchWithChunkBuffer,\n createFetchWithResponseHeadersCheck,\n} from './fetch'\nimport {\n CHUNK_LAST_OFFSET_HEADER,\n LIVE_CACHE_BUSTER_HEADER,\n LIVE_CACHE_BUSTER_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 TABLE_QUERY_PARAM,\n REPLICA_PARAM,\n} from './constants'\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])\n\ntype Replica = `full` | `default`\n\n/**\n * PostgreSQL-specific shape parameters that can be provided externally\n */\nexport interface PostgresParams {\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 */\n columns?: string[]\n\n /** The where clauses for the shape */\n where?: 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.\n *\n * Setting `replica` to `full` will result in higher bandwidth\n * usage and so is not generally recommended.\n */\n replica?: Replica\n}\n\ntype ParamValue =\n | string\n | string[]\n | (() => string | string[] | Promise<string | string[]>)\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 = {\n [K in string as K extends ReservedParamKeys ? never : K]:\n | ParamValue\n | undefined\n} & Partial<PostgresParams>\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\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]: 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\n): Promise<InternalParamsRecord> {\n const entries = Object.entries(params)\n const resolvedEntries = await Promise.all(\n entries.map(async ([key, value]) => {\n if (value === undefined) return [key, undefined]\n const resolvedValue = await resolveValue(value)\n return [\n key,\n Array.isArray(resolvedValue) ? resolvedValue.join(`,`) : resolvedValue,\n ]\n })\n )\n\n return Object.fromEntries(\n resolvedEntries.filter(([_, value]) => value !== undefined)\n )\n}\n\n/**\n * Helper function to resolve headers\n */\nasync function resolveHeaders(\n headers?: ExternalHeadersRecord\n): Promise<Record<string, string>> {\n if (!headers) return {}\n\n const entries = Object.entries(headers)\n const resolvedEntries = await Promise.all(\n entries.map(async ([key, value]) => [key, await resolveValue(value)])\n )\n\n return Object.fromEntries(resolvedEntries)\n}\n\ntype RetryOpts = {\n params?: ExternalParamsRecord\n headers?: ExternalHeadersRecord\n}\n\ntype ShapeStreamErrorHandler = (\n error: Error\n) => void | RetryOpts | Promise<void | RetryOpts>\n\n/**\n * Options for constructing a ShapeStream.\n */\nexport interface ShapeStreamOptions<T = never> {\n /**\n * The full URL to where the Shape is served. This can either be the Electric server\n * directly or a proxy. E.g. for a local Electric instance, you might set `http://localhost:3000/v1/shape`\n */\n url: string\n\n /**\n * The \"offset\" on the shape log. This is typically not set as the ShapeStream\n * will handle this automatically. A common scenario where you might pass an offset\n * is if you're maintaining a local cache of the log. If you've gone offline\n * and are re-starting a ShapeStream to catch-up to the latest state of the Shape,\n * you'd pass in the last offset and shapeHandle you'd seen from the Electric server\n * so it knows at what point in the shape to catch you up from.\n */\n offset?: Offset\n\n /**\n * Similar to `offset`, this isn't typically used unless you're maintaining\n * a cache of the shape log.\n */\n handle?: string\n\n /**\n * HTTP headers to attach to requests made by the client.\n * Values can be strings or functions (sync or async) that return strings.\n * Function values are resolved in parallel when needed, making this useful\n * for authentication tokens or other dynamic headers.\n */\n headers?: ExternalHeadersRecord\n\n /**\n * Additional request parameters to attach to the URL.\n * Values can be strings, string arrays, or functions (sync or async) that return these types.\n * Function values are resolved in parallel when needed, making this useful\n * for user-specific parameters or dynamic filters.\n *\n * These will be merged with Electric's standard parameters.\n * Note: You cannot use Electric's reserved parameter names\n * (offset, handle, live, cursor).\n *\n * PostgreSQL-specific options like table, where, columns, and replica\n * should be specified here.\n */\n params?: ExternalParamsRecord\n\n /**\n * Automatically fetch updates to the Shape. If you just want to sync the current\n * shape and stop, pass false.\n */\n subscribe?: boolean\n\n signal?: AbortSignal\n fetchClient?: typeof fetch\n backoffOptions?: BackoffOptions\n parser?: Parser<T>\n\n /**\n * A function for handling shapestream errors.\n * This is optional, when it is not provided any shapestream errors will be thrown.\n * If the function returns an object containing parameters and/or headers\n * the shapestream will apply those changes and try syncing again.\n * If the function returns void the shapestream is stopped.\n */\n onError?: ShapeStreamErrorHandler\n}\n\nexport interface ShapeStreamInterface<T extends Row<unknown> = Row> {\n subscribe(\n callback: (messages: Message<T>[]) => MaybePromise<void>,\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\n isUpToDate: boolean\n lastOffset: Offset\n shapeHandle?: string\n error?: unknown\n}\n\n/**\n * Reads updates to a shape from Electric using HTTP requests and long polling. Notifies subscribers\n * 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 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 #messageParser: MessageParser<T>\n\n readonly #subscribers = new Map<\n number,\n [\n (messages: Message<T>[]) => MaybePromise<void>,\n ((error: Error) => void) | undefined,\n ]\n >()\n\n #started = false\n #lastOffset: Offset\n #liveCacheBuster: string // Seconds since our Electric Epoch 😎\n #lastSyncedAt?: number // unix time\n #isUpToDate: boolean = false\n #connected: boolean = false\n #shapeHandle?: string\n #schema?: Schema\n #onError?: ShapeStreamErrorHandler\n\n constructor(options: ShapeStreamOptions<GetExtensions<T>>) {\n this.options = { subscribe: true, ...options }\n validateOptions(this.options)\n this.#lastOffset = this.options.offset ?? `-1`\n this.#liveCacheBuster = ``\n this.#shapeHandle = this.options.handle\n this.#messageParser = new MessageParser<T>(options.parser)\n this.#onError = this.options.onError\n\n const baseFetchClient =\n options.fetchClient ??\n ((...args: Parameters<typeof fetch>) => fetch(...args))\n\n const fetchWithBackoffClient = createFetchWithBackoff(baseFetchClient, {\n ...(options.backoffOptions ?? BackoffDefaults),\n onFailedAttempt: () => {\n this.#connected = false\n options.backoffOptions?.onFailedAttempt?.()\n },\n })\n\n this.#fetchClient = createFetchWithResponseHeadersCheck(\n createFetchWithChunkBuffer(fetchWithBackoffClient)\n )\n }\n\n get shapeHandle() {\n return this.#shapeHandle\n }\n\n get error() {\n return this.#error\n }\n\n get isUpToDate() {\n return this.#isUpToDate\n }\n\n get lastOffset() {\n return this.#lastOffset\n }\n\n async #start() {\n if (this.#started) throw new Error(`Cannot start stream twice`)\n this.#started = true\n\n try {\n while (\n (!this.options.signal?.aborted && !this.#isUpToDate) ||\n this.options.subscribe\n ) {\n const { url, signal } = this.options\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(this.options.params)\n : undefined,\n ])\n\n // Validate params after resolution\n if (params) {\n validateParams(params)\n }\n\n const fetchUrl = new URL(url)\n\n // Add PostgreSQL-specific parameters\n if (params) {\n if (params.table)\n fetchUrl.searchParams.set(TABLE_QUERY_PARAM, params.table)\n if (params.where)\n fetchUrl.searchParams.set(WHERE_QUERY_PARAM, params.where)\n if (params.columns)\n fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, params.columns)\n if (params.replica)\n fetchUrl.searchParams.set(REPLICA_PARAM, params.replica)\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\n for (const [key, value] of Object.entries(customParams)) {\n fetchUrl.searchParams.set(key, value as string)\n }\n }\n\n // Add Electric's internal parameters\n fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, this.#lastOffset)\n\n if (this.#isUpToDate) {\n fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`)\n fetchUrl.searchParams.set(\n LIVE_CACHE_BUSTER_QUERY_PARAM,\n this.#liveCacheBuster\n )\n }\n\n if (this.#shapeHandle) {\n // This should probably be a header for better cache breaking?\n fetchUrl.searchParams.set(\n SHAPE_HANDLE_QUERY_PARAM,\n this.#shapeHandle!\n )\n }\n\n // sort query params in-place for stable URLs and improved cache hits\n fetchUrl.searchParams.sort()\n\n let response!: Response\n try {\n response = await this.#fetchClient(fetchUrl.toString(), {\n signal,\n headers: requestHeaders,\n })\n this.#connected = true\n } catch (e) {\n if (e instanceof FetchBackoffAbortError) break // interrupted\n if (!(e instanceof FetchError)) throw e // should never happen\n if (e.status == 409) {\n // Upon receiving a 409, we should start from scratch\n // with the newly provided shape handle\n const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER]\n this.#reset(newShapeHandle)\n await this.#publish(e.json as Message<T>[])\n continue\n } else if (e.status >= 400 && e.status < 500) {\n // Notify subscribers\n this.#sendErrorToSubscribers(e)\n\n // 400 errors are not actionable without additional user input,\n // so we exit the loop\n throw e\n }\n }\n\n const { headers, status } = response\n const shapeHandle = headers.get(SHAPE_HANDLE_HEADER)\n if (shapeHandle) {\n this.#shapeHandle = shapeHandle\n }\n\n const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER)\n if (lastOffset) {\n this.#lastOffset = lastOffset as Offset\n }\n\n const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER)\n if (liveCacheBuster) {\n this.#liveCacheBuster = liveCacheBuster\n }\n\n const getSchema = (): Schema => {\n const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER)\n return schemaHeader ? JSON.parse(schemaHeader) : {}\n }\n this.#schema = this.#schema ?? getSchema()\n\n const messages = status === 204 ? `[]` : await response.text()\n\n if (status === 204) {\n // There's no content so we are live and up to date\n this.#lastSyncedAt = Date.now()\n }\n\n const batch = this.#messageParser.parse(messages, this.#schema)\n\n // Update isUpToDate\n if (batch.length > 0) {\n const lastMessage = batch[batch.length - 1]\n if (isUpToDateMessage(lastMessage)) {\n this.#lastSyncedAt = Date.now()\n this.#isUpToDate = true\n }\n\n await this.#publish(batch)\n }\n }\n } catch (err) {\n this.#error = err\n if (this.#onError) {\n const retryOpts = await this.#onError(err as Error)\n if (typeof retryOpts === `object`) {\n this.#reset()\n\n if (`params` in retryOpts) {\n this.options.params = retryOpts.params\n }\n\n if (`headers` in retryOpts) {\n this.options.headers = retryOpts.headers\n }\n\n // Restart\n this.#started = false\n this.#start()\n }\n return\n }\n\n // If no handler is provided for errors just throw so the error still bubbles up.\n throw err\n } finally {\n this.#connected = false\n }\n }\n\n subscribe(\n callback: (messages: Message<T>[]) => MaybePromise<void>,\n onError: (error: Error) => void = () => {}\n ) {\n const subscriptionId = Math.random()\n\n this.#subscribers.set(subscriptionId, [callback, onError])\n if (!this.#started) this.#start()\n\n return () => {\n this.#subscribers.delete(subscriptionId)\n }\n }\n\n unsubscribeAll(): void {\n this.#subscribers.clear()\n }\n\n /** Unix time at which we last synced. Undefined when `isLoading` is true. */\n lastSyncedAt(): number | undefined {\n return this.#lastSyncedAt\n }\n\n /** Time elapsed since last sync (in ms). Infinity if we did not yet sync. */\n lastSynced(): number {\n if (this.#lastSyncedAt === undefined) return Infinity\n return Date.now() - this.#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 afterwise. */\n isLoading(): boolean {\n return !this.#isUpToDate\n }\n\n async #publish(messages: Message<T>[]): Promise<void> {\n await Promise.all(\n Array.from(this.#subscribers.values()).map(async ([callback, __]) => {\n try {\n await callback(messages)\n } catch (err) {\n queueMicrotask(() => {\n throw err\n })\n }\n })\n )\n }\n\n #sendErrorToSubscribers(error: Error) {\n this.#subscribers.forEach(([_, errorFn]) => {\n errorFn?.(error)\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.#lastOffset = `-1`\n this.#liveCacheBuster = ``\n this.#shapeHandle = handle\n this.#isUpToDate = false\n this.#connected = false\n this.#schema = undefined\n }\n}\n\n/**\n * Validates that no reserved parameter names are used in the provided params object\n * @throws {ReservedParamError} if any reserved parameter names are found\n */\nfunction validateParams(params: Record<string, unknown> | undefined): void {\n if (!params) return\n\n const reservedParams = Object.keys(params).filter((key) =>\n RESERVED_PARAMS.has(key as ReservedParamKeys)\n )\n if (reservedParams.length > 0) {\n throw new ReservedParamError(reservedParams)\n }\n}\n\nfunction validateOptions<T>(options: Partial<ShapeStreamOptions<T>>): void {\n if (!options.url) {\n throw new MissingShapeUrlError()\n }\n if (options.signal && !(options.signal instanceof AbortSignal)) {\n throw new InvalidSignalError()\n }\n\n if (\n options.offset !== undefined &&\n options.offset !== `-1` &&\n !options.handle\n ) {\n throw new MissingShapeHandleError()\n }\n\n validateParams(options.params)\n\n return\n}\n","import { Message, Offset, Row } from './types'\nimport { isChangeMessage, isControlMessage } from './helpers'\nimport { FetchError } from './error'\nimport { 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\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<number, ShapeChangedCallback<T>>()\n\n #hasNotifiedSubscribersUpToDate: boolean = false\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.stream.isUpToDate\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 subscribe(callback: ShapeChangedCallback<T>): () => void {\n const subscriptionId = Math.random()\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 dataMayHaveChanged = false\n let isUpToDate = false\n let newlyUpToDate = false\n\n messages.forEach((message) => {\n if (isChangeMessage(message)) {\n dataMayHaveChanged = [`insert`, `update`, `delete`].includes(\n message.headers.operation\n )\n\n switch (message.headers.operation) {\n case `insert`:\n this.#data.set(message.key, message.value)\n break\n case `update`:\n this.#data.set(message.key, {\n ...this.#data.get(message.key)!,\n ...message.value,\n })\n break\n case `delete`:\n this.#data.delete(message.key)\n break\n }\n }\n\n if (isControlMessage(message)) {\n switch (message.headers.control) {\n case `up-to-date`:\n isUpToDate = true\n if (!this.#hasNotifiedSubscribersUpToDate) {\n newlyUpToDate = true\n }\n break\n case `must-refetch`:\n this.#data.clear()\n this.#error = false\n this.#hasNotifiedSubscribersUpToDate = false\n isUpToDate = false\n newlyUpToDate = false\n break\n }\n }\n })\n\n // Always notify subscribers when the Shape first is up to date.\n // FIXME this would be cleaner with a simple state machine.\n if (newlyUpToDate || (isUpToDate && dataMayHaveChanged)) {\n this.#hasNotifiedSubscribersUpToDate = true\n this.#notify()\n }\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":["key","value","_fetchClient","_a","_b","_subscribers","_error"]}
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -23,7 +23,6 @@ type ChangeMessage<T extends Row<unknown> = Row> = {
|
|
|
23
23
|
headers: Header & {
|
|
24
24
|
operation: Operation;
|
|
25
25
|
};
|
|
26
|
-
offset: Offset;
|
|
27
26
|
};
|
|
28
27
|
type Message<T extends Row<unknown> = Row> = ControlMessage | ChangeMessage<T>;
|
|
29
28
|
/**
|
|
@@ -118,20 +117,16 @@ declare const BackoffDefaults: {
|
|
|
118
117
|
multiplier: number;
|
|
119
118
|
};
|
|
120
119
|
|
|
121
|
-
declare const COLUMNS_QUERY_PARAM = "columns";
|
|
122
120
|
declare const LIVE_CACHE_BUSTER_QUERY_PARAM = "cursor";
|
|
123
121
|
declare const SHAPE_HANDLE_QUERY_PARAM = "handle";
|
|
124
122
|
declare const LIVE_QUERY_PARAM = "live";
|
|
125
123
|
declare const OFFSET_QUERY_PARAM = "offset";
|
|
126
|
-
declare const TABLE_QUERY_PARAM = "table";
|
|
127
|
-
declare const WHERE_QUERY_PARAM = "where";
|
|
128
|
-
declare const REPLICA_PARAM = "replica";
|
|
129
124
|
|
|
130
125
|
type Replica = `full` | `default`;
|
|
131
126
|
/**
|
|
132
127
|
* PostgreSQL-specific shape parameters that can be provided externally
|
|
133
128
|
*/
|
|
134
|
-
|
|
129
|
+
interface PostgresParams {
|
|
135
130
|
/** The root table for the shape. Not required if you set the table in your proxy. */
|
|
136
131
|
table?: string;
|
|
137
132
|
/**
|
|
@@ -152,18 +147,30 @@ type PostgresParams = {
|
|
|
152
147
|
* usage and so is not generally recommended.
|
|
153
148
|
*/
|
|
154
149
|
replica?: Replica;
|
|
155
|
-
}
|
|
156
|
-
type
|
|
150
|
+
}
|
|
151
|
+
type ParamValue = string | string[] | (() => string | string[] | Promise<string | string[]>);
|
|
157
152
|
/**
|
|
158
153
|
* External params type - what users provide.
|
|
159
|
-
*
|
|
154
|
+
* Excludes reserved parameters to prevent dynamic variations that could cause stream shape changes.
|
|
155
|
+
*/
|
|
156
|
+
type ExternalParamsRecord = {
|
|
157
|
+
[K in string as K extends ReservedParamKeys ? never : K]: ParamValue | undefined;
|
|
158
|
+
} & Partial<PostgresParams>;
|
|
159
|
+
type ReservedParamKeys = typeof LIVE_CACHE_BUSTER_QUERY_PARAM | typeof SHAPE_HANDLE_QUERY_PARAM | typeof LIVE_QUERY_PARAM | typeof OFFSET_QUERY_PARAM;
|
|
160
|
+
/**
|
|
161
|
+
* External headers type - what users provide.
|
|
162
|
+
* Allows string or function values for any header.
|
|
160
163
|
*/
|
|
161
|
-
type
|
|
162
|
-
[
|
|
164
|
+
type ExternalHeadersRecord = {
|
|
165
|
+
[key: string]: string | (() => string | Promise<string>);
|
|
163
166
|
};
|
|
167
|
+
/**
|
|
168
|
+
* Helper function to resolve a function or value to its final value
|
|
169
|
+
*/
|
|
170
|
+
declare function resolveValue<T>(value: T | (() => T | Promise<T>)): Promise<T>;
|
|
164
171
|
type RetryOpts = {
|
|
165
172
|
params?: ExternalParamsRecord;
|
|
166
|
-
headers?:
|
|
173
|
+
headers?: ExternalHeadersRecord;
|
|
167
174
|
};
|
|
168
175
|
type ShapeStreamErrorHandler = (error: Error) => void | RetryOpts | Promise<void | RetryOpts>;
|
|
169
176
|
/**
|
|
@@ -191,11 +198,17 @@ interface ShapeStreamOptions<T = never> {
|
|
|
191
198
|
handle?: string;
|
|
192
199
|
/**
|
|
193
200
|
* HTTP headers to attach to requests made by the client.
|
|
194
|
-
*
|
|
201
|
+
* Values can be strings or functions (sync or async) that return strings.
|
|
202
|
+
* Function values are resolved in parallel when needed, making this useful
|
|
203
|
+
* for authentication tokens or other dynamic headers.
|
|
195
204
|
*/
|
|
196
|
-
headers?:
|
|
205
|
+
headers?: ExternalHeadersRecord;
|
|
197
206
|
/**
|
|
198
207
|
* Additional request parameters to attach to the URL.
|
|
208
|
+
* Values can be strings, string arrays, or functions (sync or async) that return these types.
|
|
209
|
+
* Function values are resolved in parallel when needed, making this useful
|
|
210
|
+
* for user-specific parameters or dynamic filters.
|
|
211
|
+
*
|
|
199
212
|
* These will be merged with Electric's standard parameters.
|
|
200
213
|
* Note: You cannot use Electric's reserved parameter names
|
|
201
214
|
* (offset, handle, live, cursor).
|
|
@@ -391,4 +404,4 @@ declare function isChangeMessage<T extends Row<unknown> = Row>(message: Message<
|
|
|
391
404
|
*/
|
|
392
405
|
declare function isControlMessage<T extends Row<unknown> = Row>(message: Message<T>): message is ControlMessage;
|
|
393
406
|
|
|
394
|
-
export { BackoffDefaults, type BackoffOptions, type BitColumn, type BpcharColumn, type ChangeMessage, type ColumnInfo, type CommonColumnProps, type ControlMessage, FetchError, type GetExtensions, type IntervalColumn, type IntervalColumnWithPrecision, type MaybePromise, type Message, type NumericColumn, type Offset, type Operation, type RegularColumn, type Row, type Schema, Shape, type ShapeChangedCallback, type ShapeData, ShapeStream, type ShapeStreamInterface, type ShapeStreamOptions, type TimeColumn, type TypedMessages, type Value, type VarcharColumn, isChangeMessage, isControlMessage };
|
|
407
|
+
export { BackoffDefaults, type BackoffOptions, type BitColumn, type BpcharColumn, type ChangeMessage, type ColumnInfo, type CommonColumnProps, type ControlMessage, type ExternalHeadersRecord, type ExternalParamsRecord, FetchError, type GetExtensions, type IntervalColumn, type IntervalColumnWithPrecision, type MaybePromise, type Message, type NumericColumn, type Offset, type Operation, type PostgresParams, type RegularColumn, type Row, type Schema, Shape, type ShapeChangedCallback, type ShapeData, ShapeStream, type ShapeStreamInterface, type ShapeStreamOptions, type TimeColumn, type TypedMessages, type Value, type VarcharColumn, isChangeMessage, isControlMessage, resolveValue };
|