@electric-sql/experimental 0.1.2-beta.1 → 0.1.2-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.
@@ -1,8 +1,30 @@
1
1
  "use strict";
2
2
  var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
+ var __getProtoOf = Object.getPrototypeOf;
5
9
  var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
11
+ var __reflectGet = Reflect.get;
12
+ var __typeError = (msg) => {
13
+ throw TypeError(msg);
14
+ };
15
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
16
+ var __spreadValues = (a, b) => {
17
+ for (var prop in b || (b = {}))
18
+ if (__hasOwnProp.call(b, prop))
19
+ __defNormalProp(a, prop, b[prop]);
20
+ if (__getOwnPropSymbols)
21
+ for (var prop of __getOwnPropSymbols(b)) {
22
+ if (__propIsEnum.call(b, prop))
23
+ __defNormalProp(a, prop, b[prop]);
24
+ }
25
+ return a;
26
+ };
27
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
6
28
  var __export = (target, all) => {
7
29
  for (var name in all)
8
30
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -16,10 +38,38 @@ var __copyProps = (to, from, except, desc) => {
16
38
  return to;
17
39
  };
18
40
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
41
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
42
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
43
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
44
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
45
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
46
+ var __superGet = (cls, obj, key) => __reflectGet(__getProtoOf(cls), key, obj);
47
+ var __async = (__this, __arguments, generator) => {
48
+ return new Promise((resolve, reject) => {
49
+ var fulfilled = (value) => {
50
+ try {
51
+ step(generator.next(value));
52
+ } catch (e) {
53
+ reject(e);
54
+ }
55
+ };
56
+ var rejected = (value) => {
57
+ try {
58
+ step(generator.throw(value));
59
+ } catch (e) {
60
+ reject(e);
61
+ }
62
+ };
63
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
64
+ step((generator = generator.apply(__this, __arguments)).next());
65
+ });
66
+ };
19
67
 
20
68
  // src/index.ts
21
69
  var src_exports = {};
22
70
  __export(src_exports, {
71
+ MultiShapeStream: () => MultiShapeStream,
72
+ TransactionalMultiShapeStream: () => TransactionalMultiShapeStream,
23
73
  matchBy: () => matchBy,
24
74
  matchStream: () => matchStream
25
75
  });
@@ -57,8 +107,268 @@ function matchStream(stream, operations, matchFn, timeout = 6e4) {
57
107
  function matchBy(column, value) {
58
108
  return (message) => message.value[column] === value;
59
109
  }
110
+
111
+ // src/multi-shape-stream.ts
112
+ var import_client2 = require("@electric-sql/client");
113
+ var _shapes, _started, _checkForUpdatesTimeout, _lastDataLsns, _lastUpToDateLsns, _subscribers, _MultiShapeStream_instances, start_fn, scheduleCheckForUpdates_fn, checkForUpdates_fn, onError_fn, shapeEntries_fn;
114
+ var MultiShapeStream = class {
115
+ constructor(options) {
116
+ __privateAdd(this, _MultiShapeStream_instances);
117
+ __privateAdd(this, _shapes);
118
+ __privateAdd(this, _started, false);
119
+ __privateAdd(this, _checkForUpdatesTimeout);
120
+ // We keep track of the last lsn of data and up-to-date messages for each shape
121
+ // so that we can skip checkForUpdates if the lsn of the up-to-date message is
122
+ // greater than the last lsn of data.
123
+ __privateAdd(this, _lastDataLsns);
124
+ __privateAdd(this, _lastUpToDateLsns);
125
+ __privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
126
+ const {
127
+ start = true,
128
+ // By default we start the multi-shape stream
129
+ checkForUpdatesAfterMs = 100,
130
+ // Force a check for updates after 100ms
131
+ shapes
132
+ } = options;
133
+ this.checkForUpdatesAfterMs = checkForUpdatesAfterMs;
134
+ __privateSet(this, _shapes, Object.fromEntries(
135
+ Object.entries(shapes).map(([key, shape]) => [
136
+ key,
137
+ shape instanceof import_client2.ShapeStream ? shape : new import_client2.ShapeStream(__spreadProps(__spreadValues({}, shape), {
138
+ start: false
139
+ }))
140
+ ])
141
+ ));
142
+ __privateSet(this, _lastDataLsns, Object.fromEntries(
143
+ Object.entries(shapes).map(([key]) => [key, -Infinity])
144
+ ));
145
+ __privateSet(this, _lastUpToDateLsns, Object.fromEntries(
146
+ Object.entries(shapes).map(([key]) => [key, -Infinity])
147
+ ));
148
+ if (start) __privateMethod(this, _MultiShapeStream_instances, start_fn).call(this);
149
+ }
150
+ _publish(messages) {
151
+ return __async(this, null, function* () {
152
+ yield Promise.all(
153
+ Array.from(__privateGet(this, _subscribers).values()).map((_0) => __async(this, [_0], function* ([callback, __]) {
154
+ try {
155
+ yield callback(messages);
156
+ } catch (err) {
157
+ queueMicrotask(() => {
158
+ throw err;
159
+ });
160
+ }
161
+ }))
162
+ );
163
+ });
164
+ }
165
+ /**
166
+ * The ShapeStreams that are being subscribed to.
167
+ */
168
+ get shapes() {
169
+ return __privateGet(this, _shapes);
170
+ }
171
+ subscribe(callback, onError) {
172
+ const subscriptionId = Math.random();
173
+ __privateGet(this, _subscribers).set(subscriptionId, [callback, onError]);
174
+ if (!__privateGet(this, _started)) __privateMethod(this, _MultiShapeStream_instances, start_fn).call(this);
175
+ return () => {
176
+ __privateGet(this, _subscribers).delete(subscriptionId);
177
+ };
178
+ }
179
+ unsubscribeAll() {
180
+ __privateGet(this, _subscribers).clear();
181
+ }
182
+ /** Unix time at which we last synced. Undefined when `isLoading` is true. */
183
+ lastSyncedAt() {
184
+ return Math.min(
185
+ ...__privateMethod(this, _MultiShapeStream_instances, shapeEntries_fn).call(this).map(
186
+ ([_, shape]) => {
187
+ var _a;
188
+ return (_a = shape.lastSyncedAt()) != null ? _a : Infinity;
189
+ }
190
+ )
191
+ );
192
+ }
193
+ /** Time elapsed since last sync (in ms). Infinity if we did not yet sync. */
194
+ lastSynced() {
195
+ const lastSyncedAt = this.lastSyncedAt();
196
+ if (lastSyncedAt === void 0) return Infinity;
197
+ return Date.now() - lastSyncedAt;
198
+ }
199
+ /** Indicates if we are connected to the Electric sync service. */
200
+ isConnected() {
201
+ return __privateMethod(this, _MultiShapeStream_instances, shapeEntries_fn).call(this).every(([_, shape]) => shape.isConnected());
202
+ }
203
+ /** True during initial fetch. False afterwise. */
204
+ isLoading() {
205
+ return __privateMethod(this, _MultiShapeStream_instances, shapeEntries_fn).call(this).some(([_, shape]) => shape.isLoading());
206
+ }
207
+ get isUpToDate() {
208
+ return __privateMethod(this, _MultiShapeStream_instances, shapeEntries_fn).call(this).every(([_, shape]) => shape.isUpToDate);
209
+ }
210
+ };
211
+ _shapes = new WeakMap();
212
+ _started = new WeakMap();
213
+ _checkForUpdatesTimeout = new WeakMap();
214
+ _lastDataLsns = new WeakMap();
215
+ _lastUpToDateLsns = new WeakMap();
216
+ _subscribers = new WeakMap();
217
+ _MultiShapeStream_instances = new WeakSet();
218
+ start_fn = function() {
219
+ if (__privateGet(this, _started)) throw new Error(`Cannot start multi-shape stream twice`);
220
+ for (const [key, shape] of __privateMethod(this, _MultiShapeStream_instances, shapeEntries_fn).call(this)) {
221
+ if (shape.hasStarted()) {
222
+ throw new Error(`Shape ${key} already started`);
223
+ }
224
+ shape.subscribe(
225
+ (messages) => __async(this, null, function* () {
226
+ const upToDateLsns = messages.filter(import_client2.isControlMessage).map(({ headers }) => {
227
+ var _a;
228
+ return (_a = headers.global_last_seen_lsn) != null ? _a : 0;
229
+ });
230
+ if (upToDateLsns.length > 0) {
231
+ const maxUpToDateLsn = Math.max(...upToDateLsns);
232
+ const lastMaxUpToDateLsn = __privateGet(this, _lastUpToDateLsns)[key];
233
+ if (maxUpToDateLsn > lastMaxUpToDateLsn) {
234
+ __privateGet(this, _lastUpToDateLsns)[key] = maxUpToDateLsn;
235
+ }
236
+ }
237
+ const dataLsns = messages.filter(import_client2.isChangeMessage).map(({ headers }) => {
238
+ var _a;
239
+ return (_a = headers.lsn) != null ? _a : 0;
240
+ });
241
+ if (dataLsns.length > 0) {
242
+ const maxDataLsn = Math.max(...dataLsns);
243
+ const lastMaxDataLsn = __privateGet(this, _lastDataLsns)[key];
244
+ if (maxDataLsn > lastMaxDataLsn) {
245
+ __privateGet(this, _lastDataLsns)[key] = maxDataLsn;
246
+ }
247
+ __privateMethod(this, _MultiShapeStream_instances, scheduleCheckForUpdates_fn).call(this);
248
+ }
249
+ const multiShapeMessages = messages.map(
250
+ (message) => __spreadProps(__spreadValues({}, message), {
251
+ shape: key
252
+ })
253
+ );
254
+ yield this._publish(multiShapeMessages);
255
+ }),
256
+ (error) => __privateMethod(this, _MultiShapeStream_instances, onError_fn).call(this, error)
257
+ );
258
+ }
259
+ __privateSet(this, _started, true);
260
+ };
261
+ scheduleCheckForUpdates_fn = function() {
262
+ var _a;
263
+ (_a = __privateGet(this, _checkForUpdatesTimeout)) != null ? _a : __privateSet(this, _checkForUpdatesTimeout, setTimeout(() => {
264
+ __privateMethod(this, _MultiShapeStream_instances, checkForUpdates_fn).call(this);
265
+ __privateSet(this, _checkForUpdatesTimeout, void 0);
266
+ }, this.checkForUpdatesAfterMs));
267
+ };
268
+ checkForUpdates_fn = function() {
269
+ return __async(this, null, function* () {
270
+ const maxDataLsn = Math.max(...Object.values(__privateGet(this, _lastDataLsns)));
271
+ const refreshPromises = __privateMethod(this, _MultiShapeStream_instances, shapeEntries_fn).call(this).filter(([key]) => {
272
+ const lastUpToDateLsn = __privateGet(this, _lastUpToDateLsns)[key];
273
+ return lastUpToDateLsn < maxDataLsn;
274
+ }).map(([_, shape]) => {
275
+ return shape.forceDisconnectAndRefresh();
276
+ });
277
+ yield Promise.all(refreshPromises);
278
+ });
279
+ };
280
+ onError_fn = function(error) {
281
+ __privateGet(this, _subscribers).forEach(([_, errorFn]) => {
282
+ errorFn == null ? void 0 : errorFn(error);
283
+ });
284
+ };
285
+ /**
286
+ * Returns an array of the shape entries.
287
+ * Ensures that the shape entries are typed, as `Object.entries`
288
+ * will not type the entries correctly.
289
+ */
290
+ shapeEntries_fn = function() {
291
+ return Object.entries(__privateGet(this, _shapes));
292
+ };
293
+ var _changeMessages, _completeLsns, _TransactionalMultiShapeStream_instances, getLowestCompleteLsn_fn, accumulate_fn;
294
+ var _TransactionalMultiShapeStream = class _TransactionalMultiShapeStream extends MultiShapeStream {
295
+ constructor(options) {
296
+ super(options);
297
+ __privateAdd(this, _TransactionalMultiShapeStream_instances);
298
+ __privateAdd(this, _changeMessages, /* @__PURE__ */ new Map());
299
+ __privateAdd(this, _completeLsns);
300
+ __privateSet(this, _completeLsns, Object.fromEntries(
301
+ Object.entries(options.shapes).map(([key]) => [key, -Infinity])
302
+ ));
303
+ }
304
+ _publish(messages) {
305
+ return __async(this, null, function* () {
306
+ __privateMethod(this, _TransactionalMultiShapeStream_instances, accumulate_fn).call(this, messages);
307
+ const lowestCompleteLsn = __privateMethod(this, _TransactionalMultiShapeStream_instances, getLowestCompleteLsn_fn).call(this);
308
+ const lsnsToPublish = [...__privateGet(this, _changeMessages).keys()].filter(
309
+ (lsn) => lsn <= lowestCompleteLsn
310
+ );
311
+ const messagesToPublish = lsnsToPublish.sort((a, b) => a - b).map(
312
+ (lsn) => {
313
+ var _a;
314
+ return (_a = __privateGet(this, _changeMessages).get(lsn)) == null ? void 0 : _a.sort((a, b) => {
315
+ const { headers: aHeaders } = a;
316
+ const { headers: bHeaders } = b;
317
+ if (typeof aHeaders.op_position !== `number` || typeof bHeaders.op_position !== `number`) {
318
+ return 0;
319
+ }
320
+ return aHeaders.op_position - bHeaders.op_position;
321
+ });
322
+ }
323
+ ).filter((messages2) => messages2 !== void 0).flat();
324
+ lsnsToPublish.forEach((lsn) => {
325
+ __privateGet(this, _changeMessages).delete(lsn);
326
+ });
327
+ if (messagesToPublish.length > 0) {
328
+ yield __superGet(_TransactionalMultiShapeStream.prototype, this, "_publish").call(this, messagesToPublish);
329
+ }
330
+ });
331
+ }
332
+ };
333
+ _changeMessages = new WeakMap();
334
+ _completeLsns = new WeakMap();
335
+ _TransactionalMultiShapeStream_instances = new WeakSet();
336
+ getLowestCompleteLsn_fn = function() {
337
+ return Math.min(...Object.values(__privateGet(this, _completeLsns)));
338
+ };
339
+ accumulate_fn = function(messages) {
340
+ const isUpToDate = this.isUpToDate;
341
+ messages.forEach((message) => {
342
+ var _a;
343
+ const { shape, headers } = message;
344
+ if ((0, import_client2.isChangeMessage)(message)) {
345
+ const lsn = typeof headers.lsn === `number` ? headers.lsn : 0;
346
+ if (!__privateGet(this, _changeMessages).has(lsn)) {
347
+ __privateGet(this, _changeMessages).set(lsn, []);
348
+ }
349
+ (_a = __privateGet(this, _changeMessages).get(lsn)) == null ? void 0 : _a.push(message);
350
+ if (isUpToDate && // All shapes must be up to date
351
+ typeof headers.last === `boolean` && headers.last === true) {
352
+ __privateGet(this, _completeLsns)[shape] = Math.max(__privateGet(this, _completeLsns)[shape], lsn);
353
+ }
354
+ } else if ((0, import_client2.isControlMessage)(message)) {
355
+ if (headers.control === `up-to-date`) {
356
+ if (typeof headers.global_last_seen_lsn !== `number`) {
357
+ throw new Error(`global_last_seen_lsn is not a number`);
358
+ }
359
+ __privateGet(this, _completeLsns)[shape] = Math.max(
360
+ __privateGet(this, _completeLsns)[shape],
361
+ headers.global_last_seen_lsn
362
+ );
363
+ }
364
+ }
365
+ });
366
+ };
367
+ var TransactionalMultiShapeStream = _TransactionalMultiShapeStream;
60
368
  // Annotate the CommonJS export names for ESM import in node:
61
369
  0 && (module.exports = {
370
+ MultiShapeStream,
371
+ TransactionalMultiShapeStream,
62
372
  matchBy,
63
373
  matchStream
64
374
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts","../../src/match.ts"],"sourcesContent":["export * from './match'\n","import {\n isChangeMessage,\n type ShapeStreamInterface,\n type ChangeMessage,\n type GetExtensions,\n type Operation,\n type Row,\n type Value,\n type Message,\n} from '@electric-sql/client'\n\nexport function matchStream<T extends Row<unknown>>(\n stream: ShapeStreamInterface<T>,\n operations: Array<Operation>,\n matchFn: (message: ChangeMessage<T>) => boolean,\n timeout = 60000 // ms\n): Promise<ChangeMessage<T>> {\n return new Promise<ChangeMessage<T>>((resolve, reject) => {\n const unsubscribe: () => void = stream.subscribe(\n (messages: Array<unknown>) => {\n const message = messages\n .filter((msg): msg is ChangeMessage<T> =>\n isChangeMessage(msg as Message<Row<never>>)\n )\n .find((message) => {\n const operation: Operation = message.headers.operation\n\n return operations.includes(operation) && matchFn(message)\n })\n\n if (message) {\n return finish(message)\n }\n }\n )\n\n const timeoutId: NodeJS.Timeout = setTimeout(() => {\n const msg: string = `matchStream timed out after ${timeout}ms`\n\n console.error(msg)\n\n reject(msg)\n }, timeout)\n\n function finish(message: ChangeMessage<T>): void {\n clearTimeout(timeoutId)\n\n unsubscribe()\n\n return resolve(message)\n }\n })\n}\n\nexport function matchBy<T extends Row<unknown>>(\n column: string,\n value: Value<GetExtensions<T>>\n): (message: ChangeMessage<T>) => boolean {\n return (message: ChangeMessage<T>) => message.value[column] === value\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBASO;AAEA,SAAS,YACd,QACA,YACA,SACA,UAAU,KACiB;AAC3B,SAAO,IAAI,QAA0B,CAAC,SAAS,WAAW;AACxD,UAAM,cAA0B,OAAO;AAAA,MACrC,CAAC,aAA6B;AAC5B,cAAM,UAAU,SACb;AAAA,UAAO,CAAC,YACP,+BAAgB,GAA0B;AAAA,QAC5C,EACC,KAAK,CAACA,aAAY;AACjB,gBAAM,YAAuBA,SAAQ,QAAQ;AAE7C,iBAAO,WAAW,SAAS,SAAS,KAAK,QAAQA,QAAO;AAAA,QAC1D,CAAC;AAEH,YAAI,SAAS;AACX,iBAAO,OAAO,OAAO;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAEA,UAAM,YAA4B,WAAW,MAAM;AACjD,YAAM,MAAc,+BAA+B,OAAO;AAE1D,cAAQ,MAAM,GAAG;AAEjB,aAAO,GAAG;AAAA,IACZ,GAAG,OAAO;AAEV,aAAS,OAAO,SAAiC;AAC/C,mBAAa,SAAS;AAEtB,kBAAY;AAEZ,aAAO,QAAQ,OAAO;AAAA,IACxB;AAAA,EACF,CAAC;AACH;AAEO,SAAS,QACd,QACA,OACwC;AACxC,SAAO,CAAC,YAA8B,QAAQ,MAAM,MAAM,MAAM;AAClE;","names":["message"]}
1
+ {"version":3,"sources":["../../src/index.ts","../../src/match.ts","../../src/multi-shape-stream.ts"],"sourcesContent":["export * from './match'\nexport * from './multi-shape-stream'\n","import {\n isChangeMessage,\n type ShapeStreamInterface,\n type ChangeMessage,\n type GetExtensions,\n type Operation,\n type Row,\n type Value,\n type Message,\n} from '@electric-sql/client'\n\nexport function matchStream<T extends Row<unknown>>(\n stream: ShapeStreamInterface<T>,\n operations: Array<Operation>,\n matchFn: (message: ChangeMessage<T>) => boolean,\n timeout = 60000 // ms\n): Promise<ChangeMessage<T>> {\n return new Promise<ChangeMessage<T>>((resolve, reject) => {\n const unsubscribe: () => void = stream.subscribe(\n (messages: Array<unknown>) => {\n const message = messages\n .filter((msg): msg is ChangeMessage<T> =>\n isChangeMessage(msg as Message<Row<never>>)\n )\n .find((message) => {\n const operation: Operation = message.headers.operation\n\n return operations.includes(operation) && matchFn(message)\n })\n\n if (message) {\n return finish(message)\n }\n }\n )\n\n const timeoutId: NodeJS.Timeout = setTimeout(() => {\n const msg: string = `matchStream timed out after ${timeout}ms`\n\n console.error(msg)\n\n reject(msg)\n }, timeout)\n\n function finish(message: ChangeMessage<T>): void {\n clearTimeout(timeoutId)\n\n unsubscribe()\n\n return resolve(message)\n }\n })\n}\n\nexport function matchBy<T extends Row<unknown>>(\n column: string,\n value: Value<GetExtensions<T>>\n): (message: ChangeMessage<T>) => boolean {\n return (message: ChangeMessage<T>) => message.value[column] === value\n}\n","import {\n ShapeStream,\n isChangeMessage,\n isControlMessage,\n} from '@electric-sql/client'\nimport type {\n ChangeMessage,\n ControlMessage,\n FetchError,\n MaybePromise,\n Row,\n ShapeStreamOptions,\n} from '@electric-sql/client'\n\ninterface MultiShapeStreamOptions<\n TShapeRows extends {\n [K: string]: Row<unknown>\n } = {\n [K: string]: Row<unknown>\n },\n> {\n shapes: {\n [K in keyof TShapeRows]:\n | ShapeStreamOptions<TShapeRows[K]>\n | ShapeStream<TShapeRows[K]>\n }\n start?: boolean\n checkForUpdatesAfterMs?: number // milliseconds\n}\n\ninterface MultiShapeChangeMessage<\n T extends Row<unknown>,\n ShapeNames extends string,\n> extends ChangeMessage<T> {\n shape: ShapeNames\n}\n\ninterface MultiShapeControlMessage<ShapeNames extends string>\n extends ControlMessage {\n shape: ShapeNames\n}\n\ntype MultiShapeMessage<T extends Row<unknown>, ShapeNames extends string> =\n | MultiShapeChangeMessage<T, ShapeNames>\n | MultiShapeControlMessage<ShapeNames>\n\nexport type MultiShapeMessages<\n TShapeRows extends {\n [K: string]: Row<unknown>\n },\n> = {\n [K in keyof TShapeRows & string]: MultiShapeMessage<TShapeRows[K], K>\n}[keyof TShapeRows & string]\n\nexport interface MultiShapeStreamInterface<\n TShapeRows extends {\n [K: string]: Row<unknown>\n },\n> {\n shapes: { [K in keyof TShapeRows]: ShapeStream<TShapeRows[K]> }\n checkForUpdatesAfterMs?: number\n\n subscribe(\n callback: (\n messages: MultiShapeMessages<TShapeRows>[]\n ) => MaybePromise<void>,\n onError?: (error: FetchError | Error) => void\n ): () => void\n unsubscribeAll(): void\n\n lastSyncedAt(): number | undefined\n lastSynced(): number\n isConnected(): boolean\n isLoading(): boolean\n\n isUpToDate: boolean\n}\n\n/**\n * A multi-shape stream is a stream that can subscribe to multiple shapes.\n * It ensures that all shapes will receive at least an `up-to-date` message from\n * Electric within the `checkForUpdatesAfterMs` interval.\n *\n * @constructor\n * @param {MultiShapeStreamOptions} options - configure the multi-shape stream\n * @example\n * ```ts\n * const multiShapeStream = new MultiShapeStream({\n * shapes: {\n * shape1: {\n * url: 'http://localhost:3000/v1/shape1',\n * },\n * shape2: {\n * url: 'http://localhost:3000/v1/shape2',\n * },\n * },\n * })\n *\n * multiShapeStream.subscribe((msgs) => {\n * console.log(msgs)\n * })\n *\n * // or with ShapeStream instances\n * const multiShapeStream = new MultiShapeStream({\n * shapes: {\n * shape1: new ShapeStream({ url: 'http://localhost:3000/v1/shape1' }),\n * shape2: new ShapeStream({ url: 'http://localhost:3000/v1/shape2' }),\n * },\n * })\n * ```\n */\n\nexport class MultiShapeStream<\n TShapeRows extends {\n [K: string]: Row<unknown>\n },\n> implements MultiShapeStreamInterface<TShapeRows>\n{\n #shapes: { [K in keyof TShapeRows]: ShapeStream<TShapeRows[K]> }\n #started = false\n checkForUpdatesAfterMs?: number\n\n #checkForUpdatesTimeout?: ReturnType<typeof setTimeout> | undefined\n\n // We keep track of the last lsn of data and up-to-date messages for each shape\n // so that we can skip checkForUpdates if the lsn of the up-to-date message is\n // greater than the last lsn of data.\n #lastDataLsns: { [K in keyof TShapeRows]: number }\n #lastUpToDateLsns: { [K in keyof TShapeRows]: number }\n\n readonly #subscribers = new Map<\n number,\n [\n (messages: MultiShapeMessages<TShapeRows>[]) => MaybePromise<void>,\n ((error: Error) => void) | undefined,\n ]\n >()\n\n constructor(options: MultiShapeStreamOptions<TShapeRows>) {\n const {\n start = true, // By default we start the multi-shape stream\n checkForUpdatesAfterMs = 100, // Force a check for updates after 100ms\n shapes,\n } = options\n this.checkForUpdatesAfterMs = checkForUpdatesAfterMs\n this.#shapes = Object.fromEntries(\n Object.entries(shapes).map(([key, shape]) => [\n key,\n shape instanceof ShapeStream\n ? shape\n : new ShapeStream<TShapeRows[typeof key]>({\n ...shape,\n start: false,\n }),\n ])\n ) as { [K in keyof TShapeRows]: ShapeStream<TShapeRows[K]> }\n this.#lastDataLsns = Object.fromEntries(\n Object.entries(shapes).map(([key]) => [key, -Infinity])\n ) as { [K in keyof TShapeRows]: number }\n this.#lastUpToDateLsns = Object.fromEntries(\n Object.entries(shapes).map(([key]) => [key, -Infinity])\n ) as { [K in keyof TShapeRows]: number }\n if (start) this.#start()\n }\n\n #start() {\n if (this.#started) throw new Error(`Cannot start multi-shape stream twice`)\n for (const [key, shape] of this.#shapeEntries()) {\n if (shape.hasStarted()) {\n // The multi-shape stream needs to be started together as a whole, and so we\n // have to check that a shape is not already started.\n throw new Error(`Shape ${key} already started`)\n }\n shape.subscribe(\n async (messages) => {\n // Whats the max lsn of the up-to-date messages?\n const upToDateLsns = messages\n .filter(isControlMessage)\n .map(({ headers }) => (headers.global_last_seen_lsn as number) ?? 0)\n if (upToDateLsns.length > 0) {\n const maxUpToDateLsn = Math.max(...upToDateLsns)\n const lastMaxUpToDateLsn = this.#lastUpToDateLsns[key]\n if (maxUpToDateLsn > lastMaxUpToDateLsn) {\n this.#lastUpToDateLsns[key] = maxUpToDateLsn\n }\n }\n\n // Whats the max lsn of the data messages?\n const dataLsns = messages\n .filter(isChangeMessage)\n .map(({ headers }) => (headers.lsn as number) ?? 0)\n if (dataLsns.length > 0) {\n const maxDataLsn = Math.max(...dataLsns)\n const lastMaxDataLsn = this.#lastDataLsns[key]\n if (maxDataLsn > lastMaxDataLsn) {\n this.#lastDataLsns[key] = maxDataLsn\n }\n // There is new data, so we need to schedule a check for updates on\n // other shapes\n this.#scheduleCheckForUpdates()\n }\n\n // Publish the messages to the multi-shape stream subscribers\n const multiShapeMessages = messages.map(\n (message) =>\n ({\n ...message,\n shape: key,\n }) as MultiShapeMessages<TShapeRows>\n )\n await this._publish(multiShapeMessages)\n },\n (error) => this.#onError(error)\n )\n }\n this.#started = true\n }\n\n #scheduleCheckForUpdates() {\n this.#checkForUpdatesTimeout ??= setTimeout(() => {\n this.#checkForUpdates()\n this.#checkForUpdatesTimeout = undefined\n }, this.checkForUpdatesAfterMs)\n }\n\n async #checkForUpdates() {\n const maxDataLsn = Math.max(...Object.values(this.#lastDataLsns))\n const refreshPromises = this.#shapeEntries()\n .filter(([key]) => {\n // We only need to refresh shapes that have not seen an up-to-date message\n // lower than the max lsn of the data messages we have received.\n const lastUpToDateLsn = this.#lastUpToDateLsns[key]\n return lastUpToDateLsn < maxDataLsn\n })\n .map(([_, shape]) => {\n return shape.forceDisconnectAndRefresh()\n })\n await Promise.all(refreshPromises)\n }\n\n #onError(error: Error) {\n // TODO: we probably want to disconnect all shapes here on the first error\n this.#subscribers.forEach(([_, errorFn]) => {\n errorFn?.(error)\n })\n }\n\n protected async _publish(\n messages: MultiShapeMessages<TShapeRows>[]\n ): 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 /**\n * Returns an array of the shape entries.\n * Ensures that the shape entries are typed, as `Object.entries`\n * will not type the entries correctly.\n */\n #shapeEntries() {\n return Object.entries(this.#shapes) as [\n keyof TShapeRows & string,\n ShapeStream<TShapeRows[string]>,\n ][]\n }\n\n /**\n * The ShapeStreams that are being subscribed to.\n */\n get shapes() {\n return this.#shapes\n }\n\n subscribe(\n callback: (\n messages: MultiShapeMessages<TShapeRows>[]\n ) => MaybePromise<void>,\n onError?: (error: FetchError | 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 // Min of all the lastSyncedAt values\n return Math.min(\n ...this.#shapeEntries().map(\n ([_, shape]) => shape.lastSyncedAt() ?? Infinity\n )\n )\n }\n\n /** Time elapsed since last sync (in ms). Infinity if we did not yet sync. */\n lastSynced(): number {\n const lastSyncedAt = this.lastSyncedAt()\n if (lastSyncedAt === undefined) return Infinity\n return Date.now() - lastSyncedAt\n }\n\n /** Indicates if we are connected to the Electric sync service. */\n isConnected(): boolean {\n return this.#shapeEntries().every(([_, shape]) => shape.isConnected())\n }\n\n /** True during initial fetch. False afterwise. */\n isLoading(): boolean {\n return this.#shapeEntries().some(([_, shape]) => shape.isLoading())\n }\n\n get isUpToDate() {\n return this.#shapeEntries().every(([_, shape]) => shape.isUpToDate)\n }\n}\n\n/**\n * A transactional multi-shape stream is a multi-shape stream that emits the\n * messages in transactional batches, ensuring that all shapes will receive\n * at least an `up-to-date` message from Electric within the `checkForUpdatesAfterMs`\n * interval.\n * It uses the `lsn` metadata to infer transaction boundaries, and the `op_position`\n * metadata to sort the messages within a transaction.\n *\n * @constructor\n * @param {MultiShapeStreamOptions} options - configure the multi-shape stream\n * @example\n * ```ts\n * const transactionalMultiShapeStream = new TransactionalMultiShapeStream({\n * shapes: {\n * shape1: {\n * url: 'http://localhost:3000/v1/shape1',\n * },\n * shape2: {\n * url: 'http://localhost:3000/v1/shape2',\n * },\n * },\n * })\n *\n * transactionalMultiShapeStream.subscribe((msgs) => {\n * console.log(msgs)\n * })\n *\n * // or with ShapeStream instances\n * const transactionalMultiShapeStream = new TransactionalMultiShapeStream({\n * shapes: {\n * shape1: new ShapeStream({ url: 'http://localhost:3000/v1/shape1' }),\n * shape2: new ShapeStream({ url: 'http://localhost:3000/v1/shape2' }),\n * },\n * })\n * ```\n */\n\nexport class TransactionalMultiShapeStream<\n TShapeRows extends {\n [K: string]: Row<unknown>\n },\n> extends MultiShapeStream<TShapeRows> {\n #changeMessages = new Map<number, MultiShapeMessage<Row<unknown>, string>[]>()\n #completeLsns: {\n [K in keyof TShapeRows]: number\n }\n\n constructor(options: MultiShapeStreamOptions<TShapeRows>) {\n super(options)\n this.#completeLsns = Object.fromEntries(\n Object.entries(options.shapes).map(([key]) => [key, -Infinity])\n ) as { [K in keyof TShapeRows]: number }\n }\n\n #getLowestCompleteLsn() {\n return Math.min(...Object.values(this.#completeLsns))\n }\n\n protected async _publish(\n messages: MultiShapeMessages<TShapeRows>[]\n ): Promise<void> {\n this.#accumulate(messages)\n const lowestCompleteLsn = this.#getLowestCompleteLsn()\n const lsnsToPublish = [...this.#changeMessages.keys()].filter(\n (lsn) => lsn <= lowestCompleteLsn\n )\n const messagesToPublish = lsnsToPublish\n .sort((a, b) => a - b)\n .map((lsn) =>\n this.#changeMessages.get(lsn)?.sort((a, b) => {\n const { headers: aHeaders } = a\n const { headers: bHeaders } = b\n if (\n typeof aHeaders.op_position !== `number` ||\n typeof bHeaders.op_position !== `number`\n ) {\n return 0 // op_position is not present on the snapshot message\n }\n return aHeaders.op_position - bHeaders.op_position\n })\n )\n .filter((messages) => messages !== undefined)\n .flat() as MultiShapeMessages<TShapeRows>[]\n lsnsToPublish.forEach((lsn) => {\n this.#changeMessages.delete(lsn)\n })\n if (messagesToPublish.length > 0) {\n await super._publish(messagesToPublish)\n }\n }\n\n #accumulate(messages: MultiShapeMessages<TShapeRows>[]) {\n const isUpToDate = this.isUpToDate\n messages.forEach((message) => {\n const { shape, headers } = message\n if (isChangeMessage(message)) {\n // The snapshot message does not have an lsn, so we use 0\n const lsn = typeof headers.lsn === `number` ? headers.lsn : 0\n if (!this.#changeMessages.has(lsn)) {\n this.#changeMessages.set(lsn, [])\n }\n this.#changeMessages.get(lsn)?.push(message)\n if (\n isUpToDate && // All shapes must be up to date\n typeof headers.last === `boolean` &&\n headers.last === true\n ) {\n this.#completeLsns[shape] = Math.max(this.#completeLsns[shape], lsn)\n }\n } else if (isControlMessage(message)) {\n if (headers.control === `up-to-date`) {\n if (typeof headers.global_last_seen_lsn !== `number`) {\n throw new Error(`global_last_seen_lsn is not a number`)\n }\n this.#completeLsns[shape] = Math.max(\n this.#completeLsns[shape],\n headers.global_last_seen_lsn\n )\n }\n }\n })\n }\n}\n"],"mappings":"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","names":["message","import_client","messages"]}
@@ -1,6 +1,144 @@
1
- import { Row, ShapeStreamInterface, Operation, ChangeMessage, Value, GetExtensions } from '@electric-sql/client';
1
+ import { Row, ShapeStreamInterface, Operation, ChangeMessage, Value, GetExtensions, ShapeStream, MaybePromise, FetchError, ShapeStreamOptions, ControlMessage } from '@electric-sql/client';
2
2
 
3
3
  declare function matchStream<T extends Row<unknown>>(stream: ShapeStreamInterface<T>, operations: Array<Operation>, matchFn: (message: ChangeMessage<T>) => boolean, timeout?: number): Promise<ChangeMessage<T>>;
4
4
  declare function matchBy<T extends Row<unknown>>(column: string, value: Value<GetExtensions<T>>): (message: ChangeMessage<T>) => boolean;
5
5
 
6
- export { matchBy, matchStream };
6
+ interface MultiShapeStreamOptions<TShapeRows extends {
7
+ [K: string]: Row<unknown>;
8
+ } = {
9
+ [K: string]: Row<unknown>;
10
+ }> {
11
+ shapes: {
12
+ [K in keyof TShapeRows]: ShapeStreamOptions<TShapeRows[K]> | ShapeStream<TShapeRows[K]>;
13
+ };
14
+ start?: boolean;
15
+ checkForUpdatesAfterMs?: number;
16
+ }
17
+ interface MultiShapeChangeMessage<T extends Row<unknown>, ShapeNames extends string> extends ChangeMessage<T> {
18
+ shape: ShapeNames;
19
+ }
20
+ interface MultiShapeControlMessage<ShapeNames extends string> extends ControlMessage {
21
+ shape: ShapeNames;
22
+ }
23
+ type MultiShapeMessage<T extends Row<unknown>, ShapeNames extends string> = MultiShapeChangeMessage<T, ShapeNames> | MultiShapeControlMessage<ShapeNames>;
24
+ type MultiShapeMessages<TShapeRows extends {
25
+ [K: string]: Row<unknown>;
26
+ }> = {
27
+ [K in keyof TShapeRows & string]: MultiShapeMessage<TShapeRows[K], K>;
28
+ }[keyof TShapeRows & string];
29
+ interface MultiShapeStreamInterface<TShapeRows extends {
30
+ [K: string]: Row<unknown>;
31
+ }> {
32
+ shapes: {
33
+ [K in keyof TShapeRows]: ShapeStream<TShapeRows[K]>;
34
+ };
35
+ checkForUpdatesAfterMs?: number;
36
+ subscribe(callback: (messages: MultiShapeMessages<TShapeRows>[]) => MaybePromise<void>, onError?: (error: FetchError | Error) => void): () => void;
37
+ unsubscribeAll(): void;
38
+ lastSyncedAt(): number | undefined;
39
+ lastSynced(): number;
40
+ isConnected(): boolean;
41
+ isLoading(): boolean;
42
+ isUpToDate: boolean;
43
+ }
44
+ /**
45
+ * A multi-shape stream is a stream that can subscribe to multiple shapes.
46
+ * It ensures that all shapes will receive at least an `up-to-date` message from
47
+ * Electric within the `checkForUpdatesAfterMs` interval.
48
+ *
49
+ * @constructor
50
+ * @param {MultiShapeStreamOptions} options - configure the multi-shape stream
51
+ * @example
52
+ * ```ts
53
+ * const multiShapeStream = new MultiShapeStream({
54
+ * shapes: {
55
+ * shape1: {
56
+ * url: 'http://localhost:3000/v1/shape1',
57
+ * },
58
+ * shape2: {
59
+ * url: 'http://localhost:3000/v1/shape2',
60
+ * },
61
+ * },
62
+ * })
63
+ *
64
+ * multiShapeStream.subscribe((msgs) => {
65
+ * console.log(msgs)
66
+ * })
67
+ *
68
+ * // or with ShapeStream instances
69
+ * const multiShapeStream = new MultiShapeStream({
70
+ * shapes: {
71
+ * shape1: new ShapeStream({ url: 'http://localhost:3000/v1/shape1' }),
72
+ * shape2: new ShapeStream({ url: 'http://localhost:3000/v1/shape2' }),
73
+ * },
74
+ * })
75
+ * ```
76
+ */
77
+ declare class MultiShapeStream<TShapeRows extends {
78
+ [K: string]: Row<unknown>;
79
+ }> implements MultiShapeStreamInterface<TShapeRows> {
80
+ #private;
81
+ checkForUpdatesAfterMs?: number;
82
+ constructor(options: MultiShapeStreamOptions<TShapeRows>);
83
+ protected _publish(messages: MultiShapeMessages<TShapeRows>[]): Promise<void>;
84
+ /**
85
+ * The ShapeStreams that are being subscribed to.
86
+ */
87
+ get shapes(): { [K in keyof TShapeRows]: ShapeStream<TShapeRows[K]>; };
88
+ subscribe(callback: (messages: MultiShapeMessages<TShapeRows>[]) => MaybePromise<void>, onError?: (error: FetchError | Error) => void): () => void;
89
+ unsubscribeAll(): void;
90
+ /** Unix time at which we last synced. Undefined when `isLoading` is true. */
91
+ lastSyncedAt(): number | undefined;
92
+ /** Time elapsed since last sync (in ms). Infinity if we did not yet sync. */
93
+ lastSynced(): number;
94
+ /** Indicates if we are connected to the Electric sync service. */
95
+ isConnected(): boolean;
96
+ /** True during initial fetch. False afterwise. */
97
+ isLoading(): boolean;
98
+ get isUpToDate(): boolean;
99
+ }
100
+ /**
101
+ * A transactional multi-shape stream is a multi-shape stream that emits the
102
+ * messages in transactional batches, ensuring that all shapes will receive
103
+ * at least an `up-to-date` message from Electric within the `checkForUpdatesAfterMs`
104
+ * interval.
105
+ * It uses the `lsn` metadata to infer transaction boundaries, and the `op_position`
106
+ * metadata to sort the messages within a transaction.
107
+ *
108
+ * @constructor
109
+ * @param {MultiShapeStreamOptions} options - configure the multi-shape stream
110
+ * @example
111
+ * ```ts
112
+ * const transactionalMultiShapeStream = new TransactionalMultiShapeStream({
113
+ * shapes: {
114
+ * shape1: {
115
+ * url: 'http://localhost:3000/v1/shape1',
116
+ * },
117
+ * shape2: {
118
+ * url: 'http://localhost:3000/v1/shape2',
119
+ * },
120
+ * },
121
+ * })
122
+ *
123
+ * transactionalMultiShapeStream.subscribe((msgs) => {
124
+ * console.log(msgs)
125
+ * })
126
+ *
127
+ * // or with ShapeStream instances
128
+ * const transactionalMultiShapeStream = new TransactionalMultiShapeStream({
129
+ * shapes: {
130
+ * shape1: new ShapeStream({ url: 'http://localhost:3000/v1/shape1' }),
131
+ * shape2: new ShapeStream({ url: 'http://localhost:3000/v1/shape2' }),
132
+ * },
133
+ * })
134
+ * ```
135
+ */
136
+ declare class TransactionalMultiShapeStream<TShapeRows extends {
137
+ [K: string]: Row<unknown>;
138
+ }> extends MultiShapeStream<TShapeRows> {
139
+ #private;
140
+ constructor(options: MultiShapeStreamOptions<TShapeRows>);
141
+ protected _publish(messages: MultiShapeMessages<TShapeRows>[]): Promise<void>;
142
+ }
143
+
144
+ export { type MultiShapeMessages, MultiShapeStream, type MultiShapeStreamInterface, TransactionalMultiShapeStream, matchBy, matchStream };
@@ -1,2 +1,2 @@
1
- import{isChangeMessage as h}from"@electric-sql/client";function f(s,t,a,o=6e4){return new Promise((i,g)=>{let u=s.subscribe(e=>{let r=e.filter(n=>h(n)).find(n=>{let p=n.headers.operation;return t.includes(p)&&a(n)});if(r)return c(r)}),m=setTimeout(()=>{let e=`matchStream timed out after ${o}ms`;console.error(e),g(e)},o);function c(e){return clearTimeout(m),u(),i(e)}})}function M(s,t){return a=>a.value[s]===t}export{M as matchBy,f as matchStream};
1
+ var z=Object.defineProperty,Q=Object.defineProperties;var W=Object.getOwnPropertyDescriptors;var A=Object.getOwnPropertySymbols,X=Object.getPrototypeOf,Y=Object.prototype.hasOwnProperty,Z=Object.prototype.propertyIsEnumerable,ee=Reflect.get;var P=t=>{throw TypeError(t)};var D=(t,e,s)=>e in t?z(t,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[e]=s,K=(t,e)=>{for(var s in e||(e={}))Y.call(e,s)&&D(t,s,e[s]);if(A)for(var s of A(e))Z.call(e,s)&&D(t,s,e[s]);return t},E=(t,e)=>Q(t,W(e));var v=(t,e,s)=>e.has(t)||P("Cannot "+s);var n=(t,e,s)=>(v(t,e,"read from private field"),s?s.call(t):e.get(t)),m=(t,e,s)=>e.has(t)?P("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,s),d=(t,e,s,a)=>(v(t,e,"write to private field"),a?a.call(t,s):e.set(t,s),s),c=(t,e,s)=>(v(t,e,"access private method"),s);var j=(t,e,s)=>ee(X(t),s,e);var y=(t,e,s)=>new Promise((a,h)=>{var p=r=>{try{i(s.next(r))}catch(l){h(l)}},o=r=>{try{i(s.throw(r))}catch(l){h(l)}},i=r=>r.done?a(r.value):Promise.resolve(r.value).then(p,o);i((s=s.apply(t,e)).next())});import{isChangeMessage as se}from"@electric-sql/client";function ne(t,e,s,a=6e4){return new Promise((h,p)=>{let o=t.subscribe(l=>{let _=l.filter(w=>se(w)).find(w=>{let J=w.headers.operation;return e.includes(J)&&s(w)});if(_)return r(_)}),i=setTimeout(()=>{let l=`matchStream timed out after ${a}ms`;console.error(l),p(l)},a);function r(l){return clearTimeout(i),o(),h(l)}})}function oe(t,e){return s=>s.value[t]===e}import{ShapeStream as I,isChangeMessage as N,isControlMessage as q}from"@electric-sql/client";var R,k,C,b,T,g,u,U,G,H,V,M,O=class{constructor(e){m(this,u);m(this,R);m(this,k,!1);m(this,C);m(this,b);m(this,T);m(this,g,new Map);let{start:s=!0,checkForUpdatesAfterMs:a=100,shapes:h}=e;this.checkForUpdatesAfterMs=a,d(this,R,Object.fromEntries(Object.entries(h).map(([p,o])=>[p,o instanceof I?o:new I(E(K({},o),{start:!1}))]))),d(this,b,Object.fromEntries(Object.entries(h).map(([p])=>[p,-1/0]))),d(this,T,Object.fromEntries(Object.entries(h).map(([p])=>[p,-1/0]))),s&&c(this,u,U).call(this)}_publish(e){return y(this,null,function*(){yield Promise.all(Array.from(n(this,g).values()).map(h=>y(this,[h],function*([s,a]){try{yield s(e)}catch(p){queueMicrotask(()=>{throw p})}})))})}get shapes(){return n(this,R)}subscribe(e,s){let a=Math.random();return n(this,g).set(a,[e,s]),n(this,k)||c(this,u,U).call(this),()=>{n(this,g).delete(a)}}unsubscribeAll(){n(this,g).clear()}lastSyncedAt(){return Math.min(...c(this,u,M).call(this).map(([e,s])=>{var a;return(a=s.lastSyncedAt())!=null?a:1/0}))}lastSynced(){let e=this.lastSyncedAt();return e===void 0?1/0:Date.now()-e}isConnected(){return c(this,u,M).call(this).every(([e,s])=>s.isConnected())}isLoading(){return c(this,u,M).call(this).some(([e,s])=>s.isLoading())}get isUpToDate(){return c(this,u,M).call(this).every(([e,s])=>s.isUpToDate)}};R=new WeakMap,k=new WeakMap,C=new WeakMap,b=new WeakMap,T=new WeakMap,g=new WeakMap,u=new WeakSet,U=function(){if(n(this,k))throw new Error("Cannot start multi-shape stream twice");for(let[e,s]of c(this,u,M).call(this)){if(s.hasStarted())throw new Error(`Shape ${e} already started`);s.subscribe(a=>y(this,null,function*(){let h=a.filter(q).map(({headers:i})=>{var r;return(r=i.global_last_seen_lsn)!=null?r:0});if(h.length>0){let i=Math.max(...h),r=n(this,T)[e];i>r&&(n(this,T)[e]=i)}let p=a.filter(N).map(({headers:i})=>{var r;return(r=i.lsn)!=null?r:0});if(p.length>0){let i=Math.max(...p),r=n(this,b)[e];i>r&&(n(this,b)[e]=i),c(this,u,G).call(this)}let o=a.map(i=>E(K({},i),{shape:e}));yield this._publish(o)}),a=>c(this,u,V).call(this,a))}d(this,k,!0)},G=function(){var e;(e=n(this,C))!=null||d(this,C,setTimeout(()=>{c(this,u,H).call(this),d(this,C,void 0)},this.checkForUpdatesAfterMs))},H=function(){return y(this,null,function*(){let e=Math.max(...Object.values(n(this,b))),s=c(this,u,M).call(this).filter(([a])=>n(this,T)[a]<e).map(([a,h])=>h.forceDisconnectAndRefresh());yield Promise.all(s)})},V=function(e){n(this,g).forEach(([s,a])=>{a==null||a(e)})},M=function(){return Object.entries(n(this,R))};var f,S,x,$,B,L=class L extends O{constructor(s){super(s);m(this,x);m(this,f,new Map);m(this,S);d(this,S,Object.fromEntries(Object.entries(s.shapes).map(([a])=>[a,-1/0])))}_publish(s){return y(this,null,function*(){c(this,x,B).call(this,s);let a=c(this,x,$).call(this),h=[...n(this,f).keys()].filter(o=>o<=a),p=h.sort((o,i)=>o-i).map(o=>{var i;return(i=n(this,f).get(o))==null?void 0:i.sort((r,l)=>{let{headers:_}=r,{headers:w}=l;return typeof _.op_position!="number"||typeof w.op_position!="number"?0:_.op_position-w.op_position})}).filter(o=>o!==void 0).flat();h.forEach(o=>{n(this,f).delete(o)}),p.length>0&&(yield j(L.prototype,this,"_publish").call(this,p))})}};f=new WeakMap,S=new WeakMap,x=new WeakSet,$=function(){return Math.min(...Object.values(n(this,S)))},B=function(s){let a=this.isUpToDate;s.forEach(h=>{var i;let{shape:p,headers:o}=h;if(N(h)){let r=typeof o.lsn=="number"?o.lsn:0;n(this,f).has(r)||n(this,f).set(r,[]),(i=n(this,f).get(r))==null||i.push(h),a&&typeof o.last=="boolean"&&o.last===!0&&(n(this,S)[p]=Math.max(n(this,S)[p],r))}else if(q(h)&&o.control==="up-to-date"){if(typeof o.global_last_seen_lsn!="number")throw new Error("global_last_seen_lsn is not a number");n(this,S)[p]=Math.max(n(this,S)[p],o.global_last_seen_lsn)}})};var F=L;export{O as MultiShapeStream,F as TransactionalMultiShapeStream,oe as matchBy,ne as matchStream};
2
2
  //# sourceMappingURL=index.browser.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/match.ts"],"sourcesContent":["import {\n isChangeMessage,\n type ShapeStreamInterface,\n type ChangeMessage,\n type GetExtensions,\n type Operation,\n type Row,\n type Value,\n type Message,\n} from '@electric-sql/client'\n\nexport function matchStream<T extends Row<unknown>>(\n stream: ShapeStreamInterface<T>,\n operations: Array<Operation>,\n matchFn: (message: ChangeMessage<T>) => boolean,\n timeout = 60000 // ms\n): Promise<ChangeMessage<T>> {\n return new Promise<ChangeMessage<T>>((resolve, reject) => {\n const unsubscribe: () => void = stream.subscribe(\n (messages: Array<unknown>) => {\n const message = messages\n .filter((msg): msg is ChangeMessage<T> =>\n isChangeMessage(msg as Message<Row<never>>)\n )\n .find((message) => {\n const operation: Operation = message.headers.operation\n\n return operations.includes(operation) && matchFn(message)\n })\n\n if (message) {\n return finish(message)\n }\n }\n )\n\n const timeoutId: NodeJS.Timeout = setTimeout(() => {\n const msg: string = `matchStream timed out after ${timeout}ms`\n\n console.error(msg)\n\n reject(msg)\n }, timeout)\n\n function finish(message: ChangeMessage<T>): void {\n clearTimeout(timeoutId)\n\n unsubscribe()\n\n return resolve(message)\n }\n })\n}\n\nexport function matchBy<T extends Row<unknown>>(\n column: string,\n value: Value<GetExtensions<T>>\n): (message: ChangeMessage<T>) => boolean {\n return (message: ChangeMessage<T>) => message.value[column] === value\n}\n"],"mappings":"AAAA,OACE,mBAAAA,MAQK,uBAEA,SAASC,EACdC,EACAC,EACAC,EACAC,EAAU,IACiB,CAC3B,OAAO,IAAI,QAA0B,CAACC,EAASC,IAAW,CACxD,IAAMC,EAA0BN,EAAO,UACpCO,GAA6B,CAC5B,IAAMC,EAAUD,EACb,OAAQE,GACPX,EAAgBW,CAA0B,CAC5C,EACC,KAAMD,GAAY,CACjB,IAAME,EAAuBF,EAAQ,QAAQ,UAE7C,OAAOP,EAAW,SAASS,CAAS,GAAKR,EAAQM,CAAO,CAC1D,CAAC,EAEH,GAAIA,EACF,OAAOG,EAAOH,CAAO,CAEzB,CACF,EAEMI,EAA4B,WAAW,IAAM,CACjD,IAAMH,EAAc,+BAA+BN,CAAO,KAE1D,QAAQ,MAAMM,CAAG,EAEjBJ,EAAOI,CAAG,CACZ,EAAGN,CAAO,EAEV,SAASQ,EAAOH,EAAiC,CAC/C,oBAAaI,CAAS,EAEtBN,EAAY,EAELF,EAAQI,CAAO,CACxB,CACF,CAAC,CACH,CAEO,SAASK,EACdC,EACAC,EACwC,CACxC,OAAQP,GAA8BA,EAAQ,MAAMM,CAAM,IAAMC,CAClE","names":["isChangeMessage","matchStream","stream","operations","matchFn","timeout","resolve","reject","unsubscribe","messages","message","msg","operation","finish","timeoutId","matchBy","column","value"]}
1
+ {"version":3,"sources":["../src/match.ts","../src/multi-shape-stream.ts"],"sourcesContent":["import {\n isChangeMessage,\n type ShapeStreamInterface,\n type ChangeMessage,\n type GetExtensions,\n type Operation,\n type Row,\n type Value,\n type Message,\n} from '@electric-sql/client'\n\nexport function matchStream<T extends Row<unknown>>(\n stream: ShapeStreamInterface<T>,\n operations: Array<Operation>,\n matchFn: (message: ChangeMessage<T>) => boolean,\n timeout = 60000 // ms\n): Promise<ChangeMessage<T>> {\n return new Promise<ChangeMessage<T>>((resolve, reject) => {\n const unsubscribe: () => void = stream.subscribe(\n (messages: Array<unknown>) => {\n const message = messages\n .filter((msg): msg is ChangeMessage<T> =>\n isChangeMessage(msg as Message<Row<never>>)\n )\n .find((message) => {\n const operation: Operation = message.headers.operation\n\n return operations.includes(operation) && matchFn(message)\n })\n\n if (message) {\n return finish(message)\n }\n }\n )\n\n const timeoutId: NodeJS.Timeout = setTimeout(() => {\n const msg: string = `matchStream timed out after ${timeout}ms`\n\n console.error(msg)\n\n reject(msg)\n }, timeout)\n\n function finish(message: ChangeMessage<T>): void {\n clearTimeout(timeoutId)\n\n unsubscribe()\n\n return resolve(message)\n }\n })\n}\n\nexport function matchBy<T extends Row<unknown>>(\n column: string,\n value: Value<GetExtensions<T>>\n): (message: ChangeMessage<T>) => boolean {\n return (message: ChangeMessage<T>) => message.value[column] === value\n}\n","import {\n ShapeStream,\n isChangeMessage,\n isControlMessage,\n} from '@electric-sql/client'\nimport type {\n ChangeMessage,\n ControlMessage,\n FetchError,\n MaybePromise,\n Row,\n ShapeStreamOptions,\n} from '@electric-sql/client'\n\ninterface MultiShapeStreamOptions<\n TShapeRows extends {\n [K: string]: Row<unknown>\n } = {\n [K: string]: Row<unknown>\n },\n> {\n shapes: {\n [K in keyof TShapeRows]:\n | ShapeStreamOptions<TShapeRows[K]>\n | ShapeStream<TShapeRows[K]>\n }\n start?: boolean\n checkForUpdatesAfterMs?: number // milliseconds\n}\n\ninterface MultiShapeChangeMessage<\n T extends Row<unknown>,\n ShapeNames extends string,\n> extends ChangeMessage<T> {\n shape: ShapeNames\n}\n\ninterface MultiShapeControlMessage<ShapeNames extends string>\n extends ControlMessage {\n shape: ShapeNames\n}\n\ntype MultiShapeMessage<T extends Row<unknown>, ShapeNames extends string> =\n | MultiShapeChangeMessage<T, ShapeNames>\n | MultiShapeControlMessage<ShapeNames>\n\nexport type MultiShapeMessages<\n TShapeRows extends {\n [K: string]: Row<unknown>\n },\n> = {\n [K in keyof TShapeRows & string]: MultiShapeMessage<TShapeRows[K], K>\n}[keyof TShapeRows & string]\n\nexport interface MultiShapeStreamInterface<\n TShapeRows extends {\n [K: string]: Row<unknown>\n },\n> {\n shapes: { [K in keyof TShapeRows]: ShapeStream<TShapeRows[K]> }\n checkForUpdatesAfterMs?: number\n\n subscribe(\n callback: (\n messages: MultiShapeMessages<TShapeRows>[]\n ) => MaybePromise<void>,\n onError?: (error: FetchError | Error) => void\n ): () => void\n unsubscribeAll(): void\n\n lastSyncedAt(): number | undefined\n lastSynced(): number\n isConnected(): boolean\n isLoading(): boolean\n\n isUpToDate: boolean\n}\n\n/**\n * A multi-shape stream is a stream that can subscribe to multiple shapes.\n * It ensures that all shapes will receive at least an `up-to-date` message from\n * Electric within the `checkForUpdatesAfterMs` interval.\n *\n * @constructor\n * @param {MultiShapeStreamOptions} options - configure the multi-shape stream\n * @example\n * ```ts\n * const multiShapeStream = new MultiShapeStream({\n * shapes: {\n * shape1: {\n * url: 'http://localhost:3000/v1/shape1',\n * },\n * shape2: {\n * url: 'http://localhost:3000/v1/shape2',\n * },\n * },\n * })\n *\n * multiShapeStream.subscribe((msgs) => {\n * console.log(msgs)\n * })\n *\n * // or with ShapeStream instances\n * const multiShapeStream = new MultiShapeStream({\n * shapes: {\n * shape1: new ShapeStream({ url: 'http://localhost:3000/v1/shape1' }),\n * shape2: new ShapeStream({ url: 'http://localhost:3000/v1/shape2' }),\n * },\n * })\n * ```\n */\n\nexport class MultiShapeStream<\n TShapeRows extends {\n [K: string]: Row<unknown>\n },\n> implements MultiShapeStreamInterface<TShapeRows>\n{\n #shapes: { [K in keyof TShapeRows]: ShapeStream<TShapeRows[K]> }\n #started = false\n checkForUpdatesAfterMs?: number\n\n #checkForUpdatesTimeout?: ReturnType<typeof setTimeout> | undefined\n\n // We keep track of the last lsn of data and up-to-date messages for each shape\n // so that we can skip checkForUpdates if the lsn of the up-to-date message is\n // greater than the last lsn of data.\n #lastDataLsns: { [K in keyof TShapeRows]: number }\n #lastUpToDateLsns: { [K in keyof TShapeRows]: number }\n\n readonly #subscribers = new Map<\n number,\n [\n (messages: MultiShapeMessages<TShapeRows>[]) => MaybePromise<void>,\n ((error: Error) => void) | undefined,\n ]\n >()\n\n constructor(options: MultiShapeStreamOptions<TShapeRows>) {\n const {\n start = true, // By default we start the multi-shape stream\n checkForUpdatesAfterMs = 100, // Force a check for updates after 100ms\n shapes,\n } = options\n this.checkForUpdatesAfterMs = checkForUpdatesAfterMs\n this.#shapes = Object.fromEntries(\n Object.entries(shapes).map(([key, shape]) => [\n key,\n shape instanceof ShapeStream\n ? shape\n : new ShapeStream<TShapeRows[typeof key]>({\n ...shape,\n start: false,\n }),\n ])\n ) as { [K in keyof TShapeRows]: ShapeStream<TShapeRows[K]> }\n this.#lastDataLsns = Object.fromEntries(\n Object.entries(shapes).map(([key]) => [key, -Infinity])\n ) as { [K in keyof TShapeRows]: number }\n this.#lastUpToDateLsns = Object.fromEntries(\n Object.entries(shapes).map(([key]) => [key, -Infinity])\n ) as { [K in keyof TShapeRows]: number }\n if (start) this.#start()\n }\n\n #start() {\n if (this.#started) throw new Error(`Cannot start multi-shape stream twice`)\n for (const [key, shape] of this.#shapeEntries()) {\n if (shape.hasStarted()) {\n // The multi-shape stream needs to be started together as a whole, and so we\n // have to check that a shape is not already started.\n throw new Error(`Shape ${key} already started`)\n }\n shape.subscribe(\n async (messages) => {\n // Whats the max lsn of the up-to-date messages?\n const upToDateLsns = messages\n .filter(isControlMessage)\n .map(({ headers }) => (headers.global_last_seen_lsn as number) ?? 0)\n if (upToDateLsns.length > 0) {\n const maxUpToDateLsn = Math.max(...upToDateLsns)\n const lastMaxUpToDateLsn = this.#lastUpToDateLsns[key]\n if (maxUpToDateLsn > lastMaxUpToDateLsn) {\n this.#lastUpToDateLsns[key] = maxUpToDateLsn\n }\n }\n\n // Whats the max lsn of the data messages?\n const dataLsns = messages\n .filter(isChangeMessage)\n .map(({ headers }) => (headers.lsn as number) ?? 0)\n if (dataLsns.length > 0) {\n const maxDataLsn = Math.max(...dataLsns)\n const lastMaxDataLsn = this.#lastDataLsns[key]\n if (maxDataLsn > lastMaxDataLsn) {\n this.#lastDataLsns[key] = maxDataLsn\n }\n // There is new data, so we need to schedule a check for updates on\n // other shapes\n this.#scheduleCheckForUpdates()\n }\n\n // Publish the messages to the multi-shape stream subscribers\n const multiShapeMessages = messages.map(\n (message) =>\n ({\n ...message,\n shape: key,\n }) as MultiShapeMessages<TShapeRows>\n )\n await this._publish(multiShapeMessages)\n },\n (error) => this.#onError(error)\n )\n }\n this.#started = true\n }\n\n #scheduleCheckForUpdates() {\n this.#checkForUpdatesTimeout ??= setTimeout(() => {\n this.#checkForUpdates()\n this.#checkForUpdatesTimeout = undefined\n }, this.checkForUpdatesAfterMs)\n }\n\n async #checkForUpdates() {\n const maxDataLsn = Math.max(...Object.values(this.#lastDataLsns))\n const refreshPromises = this.#shapeEntries()\n .filter(([key]) => {\n // We only need to refresh shapes that have not seen an up-to-date message\n // lower than the max lsn of the data messages we have received.\n const lastUpToDateLsn = this.#lastUpToDateLsns[key]\n return lastUpToDateLsn < maxDataLsn\n })\n .map(([_, shape]) => {\n return shape.forceDisconnectAndRefresh()\n })\n await Promise.all(refreshPromises)\n }\n\n #onError(error: Error) {\n // TODO: we probably want to disconnect all shapes here on the first error\n this.#subscribers.forEach(([_, errorFn]) => {\n errorFn?.(error)\n })\n }\n\n protected async _publish(\n messages: MultiShapeMessages<TShapeRows>[]\n ): 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 /**\n * Returns an array of the shape entries.\n * Ensures that the shape entries are typed, as `Object.entries`\n * will not type the entries correctly.\n */\n #shapeEntries() {\n return Object.entries(this.#shapes) as [\n keyof TShapeRows & string,\n ShapeStream<TShapeRows[string]>,\n ][]\n }\n\n /**\n * The ShapeStreams that are being subscribed to.\n */\n get shapes() {\n return this.#shapes\n }\n\n subscribe(\n callback: (\n messages: MultiShapeMessages<TShapeRows>[]\n ) => MaybePromise<void>,\n onError?: (error: FetchError | 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 // Min of all the lastSyncedAt values\n return Math.min(\n ...this.#shapeEntries().map(\n ([_, shape]) => shape.lastSyncedAt() ?? Infinity\n )\n )\n }\n\n /** Time elapsed since last sync (in ms). Infinity if we did not yet sync. */\n lastSynced(): number {\n const lastSyncedAt = this.lastSyncedAt()\n if (lastSyncedAt === undefined) return Infinity\n return Date.now() - lastSyncedAt\n }\n\n /** Indicates if we are connected to the Electric sync service. */\n isConnected(): boolean {\n return this.#shapeEntries().every(([_, shape]) => shape.isConnected())\n }\n\n /** True during initial fetch. False afterwise. */\n isLoading(): boolean {\n return this.#shapeEntries().some(([_, shape]) => shape.isLoading())\n }\n\n get isUpToDate() {\n return this.#shapeEntries().every(([_, shape]) => shape.isUpToDate)\n }\n}\n\n/**\n * A transactional multi-shape stream is a multi-shape stream that emits the\n * messages in transactional batches, ensuring that all shapes will receive\n * at least an `up-to-date` message from Electric within the `checkForUpdatesAfterMs`\n * interval.\n * It uses the `lsn` metadata to infer transaction boundaries, and the `op_position`\n * metadata to sort the messages within a transaction.\n *\n * @constructor\n * @param {MultiShapeStreamOptions} options - configure the multi-shape stream\n * @example\n * ```ts\n * const transactionalMultiShapeStream = new TransactionalMultiShapeStream({\n * shapes: {\n * shape1: {\n * url: 'http://localhost:3000/v1/shape1',\n * },\n * shape2: {\n * url: 'http://localhost:3000/v1/shape2',\n * },\n * },\n * })\n *\n * transactionalMultiShapeStream.subscribe((msgs) => {\n * console.log(msgs)\n * })\n *\n * // or with ShapeStream instances\n * const transactionalMultiShapeStream = new TransactionalMultiShapeStream({\n * shapes: {\n * shape1: new ShapeStream({ url: 'http://localhost:3000/v1/shape1' }),\n * shape2: new ShapeStream({ url: 'http://localhost:3000/v1/shape2' }),\n * },\n * })\n * ```\n */\n\nexport class TransactionalMultiShapeStream<\n TShapeRows extends {\n [K: string]: Row<unknown>\n },\n> extends MultiShapeStream<TShapeRows> {\n #changeMessages = new Map<number, MultiShapeMessage<Row<unknown>, string>[]>()\n #completeLsns: {\n [K in keyof TShapeRows]: number\n }\n\n constructor(options: MultiShapeStreamOptions<TShapeRows>) {\n super(options)\n this.#completeLsns = Object.fromEntries(\n Object.entries(options.shapes).map(([key]) => [key, -Infinity])\n ) as { [K in keyof TShapeRows]: number }\n }\n\n #getLowestCompleteLsn() {\n return Math.min(...Object.values(this.#completeLsns))\n }\n\n protected async _publish(\n messages: MultiShapeMessages<TShapeRows>[]\n ): Promise<void> {\n this.#accumulate(messages)\n const lowestCompleteLsn = this.#getLowestCompleteLsn()\n const lsnsToPublish = [...this.#changeMessages.keys()].filter(\n (lsn) => lsn <= lowestCompleteLsn\n )\n const messagesToPublish = lsnsToPublish\n .sort((a, b) => a - b)\n .map((lsn) =>\n this.#changeMessages.get(lsn)?.sort((a, b) => {\n const { headers: aHeaders } = a\n const { headers: bHeaders } = b\n if (\n typeof aHeaders.op_position !== `number` ||\n typeof bHeaders.op_position !== `number`\n ) {\n return 0 // op_position is not present on the snapshot message\n }\n return aHeaders.op_position - bHeaders.op_position\n })\n )\n .filter((messages) => messages !== undefined)\n .flat() as MultiShapeMessages<TShapeRows>[]\n lsnsToPublish.forEach((lsn) => {\n this.#changeMessages.delete(lsn)\n })\n if (messagesToPublish.length > 0) {\n await super._publish(messagesToPublish)\n }\n }\n\n #accumulate(messages: MultiShapeMessages<TShapeRows>[]) {\n const isUpToDate = this.isUpToDate\n messages.forEach((message) => {\n const { shape, headers } = message\n if (isChangeMessage(message)) {\n // The snapshot message does not have an lsn, so we use 0\n const lsn = typeof headers.lsn === `number` ? headers.lsn : 0\n if (!this.#changeMessages.has(lsn)) {\n this.#changeMessages.set(lsn, [])\n }\n this.#changeMessages.get(lsn)?.push(message)\n if (\n isUpToDate && // All shapes must be up to date\n typeof headers.last === `boolean` &&\n headers.last === true\n ) {\n this.#completeLsns[shape] = Math.max(this.#completeLsns[shape], lsn)\n }\n } else if (isControlMessage(message)) {\n if (headers.control === `up-to-date`) {\n if (typeof headers.global_last_seen_lsn !== `number`) {\n throw new Error(`global_last_seen_lsn is not a number`)\n }\n this.#completeLsns[shape] = Math.max(\n this.#completeLsns[shape],\n headers.global_last_seen_lsn\n )\n }\n }\n })\n }\n}\n"],"mappings":"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","names":["isChangeMessage","matchStream","stream","operations","matchFn","timeout","resolve","reject","unsubscribe","messages","message","msg","operation","finish","timeoutId","matchBy","column","value","ShapeStream","isChangeMessage","isControlMessage","_shapes","_started","_checkForUpdatesTimeout","_lastDataLsns","_lastUpToDateLsns","_subscribers","_MultiShapeStream_instances","start_fn","scheduleCheckForUpdates_fn","checkForUpdates_fn","onError_fn","shapeEntries_fn","MultiShapeStream","options","__privateAdd","start","checkForUpdatesAfterMs","shapes","__privateSet","key","shape","ShapeStream","__spreadProps","__spreadValues","__privateMethod","messages","__async","__privateGet","_0","callback","__","err","onError","subscriptionId","_","_a","lastSyncedAt","upToDateLsns","isControlMessage","headers","maxUpToDateLsn","lastMaxUpToDateLsn","dataLsns","isChangeMessage","maxDataLsn","lastMaxDataLsn","multiShapeMessages","message","error","refreshPromises","errorFn","_changeMessages","_completeLsns","_TransactionalMultiShapeStream_instances","getLowestCompleteLsn_fn","accumulate_fn","_TransactionalMultiShapeStream","lowestCompleteLsn","lsnsToPublish","lsn","messagesToPublish","a","b","aHeaders","bHeaders","__superGet","isUpToDate","TransactionalMultiShapeStream"]}