@electric-sql/client 1.0.11 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -118,6 +118,52 @@ type Parser<Extensions = never> = {
118
118
  };
119
119
  type TransformFunction<Extensions = never> = (message: Row<Extensions>) => Row<Extensions>;
120
120
 
121
+ /**
122
+ * Type guard for checking {@link Message} is {@link ChangeMessage}.
123
+ *
124
+ * See [TS docs](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)
125
+ * for information on how to use type guards.
126
+ *
127
+ * @param message - the message to check
128
+ * @returns true if the message is a {@link ChangeMessage}
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * if (isChangeMessage(message)) {
133
+ * const msgChng: ChangeMessage = message // Ok
134
+ * const msgCtrl: ControlMessage = message // Err, type mismatch
135
+ * }
136
+ * ```
137
+ */
138
+ declare function isChangeMessage<T extends Row<unknown> = Row>(message: Message<T>): message is ChangeMessage<T>;
139
+ /**
140
+ * Type guard for checking {@link Message} is {@link ControlMessage}.
141
+ *
142
+ * See [TS docs](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)
143
+ * for information on how to use type guards.
144
+ *
145
+ * @param message - the message to check
146
+ * @returns true if the message is a {@link ControlMessage}
147
+ *
148
+ * * @example
149
+ * ```ts
150
+ * if (isControlMessage(message)) {
151
+ * const msgChng: ChangeMessage = message // Err, type mismatch
152
+ * const msgCtrl: ControlMessage = message // Ok
153
+ * }
154
+ * ```
155
+ */
156
+ declare function isControlMessage<T extends Row<unknown> = Row>(message: Message<T>): message is ControlMessage;
157
+ /**
158
+ * Checks if a transaction is visible in a snapshot.
159
+ *
160
+ * @param txid - the transaction id to check
161
+ * @param snapshot - the information about the snapshot
162
+ * @returns true if the transaction is visible in the snapshot
163
+ */
164
+ declare function isVisibleInSnapshot(txid: number | bigint | `${bigint}`, snapshot: PostgresSnapshot | NormalizedPgSnapshot): boolean;
165
+ type ShardSubdomainOption = `always` | `localhost` | `never` | boolean;
166
+
121
167
  declare class FetchError extends Error {
122
168
  url: string;
123
169
  status: number;
@@ -284,7 +330,43 @@ interface ShapeStreamOptions<T = never> {
284
330
  /**
285
331
  * Initial data loading mode
286
332
  */
287
- mode?: LogMode;
333
+ log?: LogMode;
334
+ /**
335
+ * Enable subdomain sharding to bypass browser HTTP/1.1 connection limits.
336
+ * This is useful in local development and is enabled by default for localhost URLs.
337
+ *
338
+ * See https://electric-sql.com/docs/guides/troubleshooting#slow-shapes-mdash-why-are-my-shapes-slow-in-the-browser-in-local-development
339
+ *
340
+ * When sharded, each shape stream gets a unique subdomain (e.g., `a7f2c.localhost`),
341
+ * which bypasses the browser HTTP/1.1 connection limits. This avoids the need to serve
342
+ * the development server over HTTP/2 (and thus HTTPS) in development.
343
+ *
344
+ * Options:
345
+ * - `'localhost'` - Automatically shard `localhost` and `*.localhost` URLs (the default)
346
+ * - `'always'` - Shard URLs regardless of the hostname
347
+ * - `'never'` - Disable sharding
348
+ * - `true` - Alias for `'always'`
349
+ * - `false` - Alias for `'never'`
350
+ *
351
+ * @default 'localhost'
352
+ *
353
+ * @example
354
+ * { url: 'http://localhost:3000/v1/shape', shardSubdomain: 'localhost' }
355
+ * // → http://a1c2f.localhost:3000/v1/shape
356
+ *
357
+ * @example
358
+ * { url: 'https://api.example.com', shardSubdomain: 'localhost' }
359
+ * // → https://api.example.com
360
+ *
361
+ * @example
362
+ * { url: 'https://localhost:3000', shardSubdomain: 'never' }
363
+ * // → https://localhost:3000
364
+ *
365
+ * @example
366
+ * { url: 'https://api.example.com', shardSubdomain: 'always' }
367
+ * // → https://b2d3g.api.example.com
368
+ */
369
+ shardSubdomain?: ShardSubdomainOption;
288
370
  signal?: AbortSignal;
289
371
  fetchClient?: typeof fetch;
290
372
  backoffOptions?: BackoffOptions;
@@ -490,49 +572,4 @@ declare class Shape<T extends Row<unknown> = Row> {
490
572
  get numSubscribers(): number;
491
573
  }
492
574
 
493
- /**
494
- * Type guard for checking {@link Message} is {@link ChangeMessage}.
495
- *
496
- * See [TS docs](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)
497
- * for information on how to use type guards.
498
- *
499
- * @param message - the message to check
500
- * @returns true if the message is a {@link ChangeMessage}
501
- *
502
- * @example
503
- * ```ts
504
- * if (isChangeMessage(message)) {
505
- * const msgChng: ChangeMessage = message // Ok
506
- * const msgCtrl: ControlMessage = message // Err, type mismatch
507
- * }
508
- * ```
509
- */
510
- declare function isChangeMessage<T extends Row<unknown> = Row>(message: Message<T>): message is ChangeMessage<T>;
511
- /**
512
- * Type guard for checking {@link Message} is {@link ControlMessage}.
513
- *
514
- * See [TS docs](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)
515
- * for information on how to use type guards.
516
- *
517
- * @param message - the message to check
518
- * @returns true if the message is a {@link ControlMessage}
519
- *
520
- * * @example
521
- * ```ts
522
- * if (isControlMessage(message)) {
523
- * const msgChng: ChangeMessage = message // Err, type mismatch
524
- * const msgCtrl: ControlMessage = message // Ok
525
- * }
526
- * ```
527
- */
528
- declare function isControlMessage<T extends Row<unknown> = Row>(message: Message<T>): message is ControlMessage;
529
- /**
530
- * Checks if a transaction is visible in a snapshot.
531
- *
532
- * @param txid - the transaction id to check
533
- * @param snapshot - the information about the snapshot
534
- * @returns true if the transaction is visible in the snapshot
535
- */
536
- declare function isVisibleInSnapshot(txid: number | bigint | `${bigint}`, snapshot: PostgresSnapshot | NormalizedPgSnapshot): boolean;
537
-
538
- export { BackoffDefaults, type BackoffOptions, type BitColumn, type BpcharColumn, type ChangeMessage, type ColumnInfo, type CommonColumnProps, type ControlMessage, ELECTRIC_PROTOCOL_QUERY_PARAMS, type ExternalHeadersRecord, type ExternalParamsRecord, FetchError, type GetExtensions, type IntervalColumn, type IntervalColumnWithPrecision, type LogMode, type MaybePromise, type Message, type NormalizedPgSnapshot, type NumericColumn, type Offset, type Operation, type PostgresParams, type PostgresSnapshot, type RegularColumn, type Row, type Schema, Shape, type ShapeChangedCallback, type ShapeData, ShapeStream, type ShapeStreamInterface, type ShapeStreamOptions, type SnapshotMetadata, type SubsetParams, type TimeColumn, type TypedMessages, type Value, type VarcharColumn, isChangeMessage, isControlMessage, isVisibleInSnapshot, resolveValue };
575
+ export { BackoffDefaults, type BackoffOptions, type BitColumn, type BpcharColumn, type ChangeMessage, type ColumnInfo, type CommonColumnProps, type ControlMessage, ELECTRIC_PROTOCOL_QUERY_PARAMS, type ExternalHeadersRecord, type ExternalParamsRecord, FetchError, type GetExtensions, type IntervalColumn, type IntervalColumnWithPrecision, type LogMode, type MaybePromise, type Message, type NormalizedPgSnapshot, type NumericColumn, type Offset, type Operation, type PostgresParams, type PostgresSnapshot, type RegularColumn, type Row, type Schema, Shape, type ShapeChangedCallback, type ShapeData, ShapeStream, type ShapeStreamInterface, type ShapeStreamOptions, type ShardSubdomainOption, type SnapshotMetadata, type SubsetParams, type TimeColumn, type TypedMessages, type Value, type VarcharColumn, isChangeMessage, isControlMessage, isVisibleInSnapshot, resolveValue };
@@ -271,6 +271,26 @@ function isVisibleInSnapshot(txid, snapshot) {
271
271
  const xip = snapshot.xip_list.map(BigInt);
272
272
  return xid < xmin || xid < xmax && !xip.includes(xid);
273
273
  }
274
+ function generateShardId() {
275
+ return Math.floor(Math.random() * 1048575).toString(16).padStart(5, `0`);
276
+ }
277
+ function isLocalhostUrl(url) {
278
+ const hostname = url.hostname.toLowerCase();
279
+ return hostname === `localhost` || hostname.endsWith(`.localhost`);
280
+ }
281
+ function applySubdomainSharding(originalUrl, option = `localhost`) {
282
+ if (option === `never` || option === false) {
283
+ return originalUrl;
284
+ }
285
+ const url = new URL(originalUrl);
286
+ const shouldShard = option === `always` || option === true || option === `localhost` && isLocalhostUrl(url);
287
+ if (!shouldShard) {
288
+ return originalUrl;
289
+ }
290
+ const shardId = generateShardId();
291
+ url.hostname = `${shardId}.${url.hostname}`;
292
+ return url.toString();
293
+ }
274
294
 
275
295
  // src/constants.ts
276
296
  var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
@@ -749,6 +769,10 @@ var ShapeStream = class {
749
769
  var _a, _b, _c, _d;
750
770
  this.options = __spreadValues({ subscribe: true }, options);
751
771
  validateOptions(this.options);
772
+ this.options.url = applySubdomainSharding(
773
+ this.options.url,
774
+ this.options.shardSubdomain
775
+ );
752
776
  __privateSet(this, _lastOffset, (_a = this.options.offset) != null ? _a : `-1`);
753
777
  __privateSet(this, _liveCacheBuster, ``);
754
778
  __privateSet(this, _shapeHandle, this.options.handle);
@@ -757,7 +781,7 @@ var ShapeStream = class {
757
781
  options.transformer
758
782
  ));
759
783
  __privateSet(this, _onError, this.options.onError);
760
- __privateSet(this, _mode, (_b = this.options.mode) != null ? _b : `full`);
784
+ __privateSet(this, _mode, (_b = this.options.log) != null ? _b : `full`);
761
785
  const baseFetchClient = (_c = options.fetchClient) != null ? _c : (...args) => fetch(...args);
762
786
  const backOffOpts = __spreadProps(__spreadValues({}, (_d = options.backoffOptions) != null ? _d : BackoffDefaults), {
763
787
  onFailedAttempt: () => {