@nanolink/mirrors 1.0.5 → 1.0.6

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.
@@ -0,0 +1,593 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __spreadValues = (a, b) => {
12
+ for (var prop in b || (b = {}))
13
+ if (__hasOwnProp.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ if (__getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(b)) {
17
+ if (__propIsEnum.call(b, prop))
18
+ __defNormalProp(a, prop, b[prop]);
19
+ }
20
+ return a;
21
+ };
22
+ var __export = (target, all) => {
23
+ for (var name in all)
24
+ __defProp(target, name, { get: all[name], enumerable: true });
25
+ };
26
+ var __copyProps = (to, from, except, desc) => {
27
+ if (from && typeof from === "object" || typeof from === "function") {
28
+ for (let key of __getOwnPropNames(from))
29
+ if (!__hasOwnProp.call(to, key) && key !== except)
30
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
31
+ }
32
+ return to;
33
+ };
34
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
35
+ // If the importer is in node compatibility mode or this is not an ESM
36
+ // file that has been converted to a CommonJS file using a Babel-
37
+ // compatible transform (i.e. "__esModule" has not been set), then set
38
+ // "default" to the CommonJS "module.exports" for node compatibility.
39
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
40
+ mod
41
+ ));
42
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
43
+
44
+ // src/index.ts
45
+ var index_exports = {};
46
+ __export(index_exports, {
47
+ Connection: () => Connection,
48
+ DelegatingObject: () => DelegatingObject,
49
+ MirrorSync: () => MirrorSync,
50
+ SubscriptionClient: () => SubscriptionClient_default
51
+ });
52
+ module.exports = __toCommonJS(index_exports);
53
+
54
+ // src/DelegateMap.ts
55
+ var DelegatingMap = class {
56
+ constructor(resolver) {
57
+ this.resolver = resolver;
58
+ }
59
+ t() {
60
+ return this.resolver();
61
+ }
62
+ get size() {
63
+ return this.t().size;
64
+ }
65
+ get(key) {
66
+ return this.t().get(key);
67
+ }
68
+ has(key) {
69
+ return this.t().has(key);
70
+ }
71
+ entries() {
72
+ return this.t().entries();
73
+ }
74
+ keys() {
75
+ return this.t().keys();
76
+ }
77
+ values() {
78
+ return this.t().values();
79
+ }
80
+ forEach(callbackfn, thisArg) {
81
+ this.t().forEach(((v, k) => callbackfn(v, k, this)), thisArg);
82
+ }
83
+ [Symbol.iterator]() {
84
+ return this.t()[Symbol.iterator]();
85
+ }
86
+ };
87
+ var DelegatingObject = class {
88
+ constructor(source, mapFn) {
89
+ this.source = source;
90
+ this.mapFn = mapFn;
91
+ const mapper = (v, k) => this.mapFn ? this.mapFn(v, k) : v;
92
+ const handler = {
93
+ get: (_target, prop, receiver) => {
94
+ if (prop === Symbol.toStringTag) return "Object";
95
+ if (typeof prop !== "string") return Reflect.get(Object.prototype, prop, receiver);
96
+ const key = prop;
97
+ const v = this.source.get(key);
98
+ return v === void 0 ? void 0 : mapper(v, key);
99
+ },
100
+ set: () => false,
101
+ // immutable
102
+ has: (_target, prop) => typeof prop === "string" && this.source.has(prop),
103
+ deleteProperty: () => false,
104
+ // immutable
105
+ ownKeys: () => Array.from(this.source.keys()),
106
+ getOwnPropertyDescriptor: (_target, prop) => {
107
+ if (typeof prop === "string" && this.source.has(prop)) {
108
+ const key = prop;
109
+ const v = this.source.get(key);
110
+ return { configurable: true, enumerable: true, value: mapper(v, key) };
111
+ }
112
+ return void 0;
113
+ },
114
+ defineProperty: () => false
115
+ // immutable
116
+ };
117
+ this.proxy = new Proxy(/* @__PURE__ */ Object.create(null), handler);
118
+ }
119
+ asObject() {
120
+ return this.proxy;
121
+ }
122
+ };
123
+
124
+ // src/MirrorSync.ts
125
+ var import_eventemitter3 = __toESM(require("eventemitter3"));
126
+ var MirrorSync = class extends import_eventemitter3.default {
127
+ constructor(client, options) {
128
+ super();
129
+ this.mirror = /* @__PURE__ */ new Map();
130
+ this.buffer = null;
131
+ // used during full/resubscription phase
132
+ this.currentVersion = -1;
133
+ // numeric cursor (-1 => full sync needed)
134
+ this.currentOpVersion = null;
135
+ // op cursor (null => full sync needed)
136
+ this.unsub = null;
137
+ this.fullSyncPhase = false;
138
+ this.dmap = new DelegatingMap(() => this.mirror);
139
+ this.client = client;
140
+ this.options = options;
141
+ this.log = options.log;
142
+ this.attachClientEvents();
143
+ this.resetLoaded();
144
+ }
145
+ get Storage() {
146
+ return this.mirror;
147
+ }
148
+ l(msg, meta) {
149
+ if (this.log) this.log(msg, meta);
150
+ }
151
+ attachClientEvents() {
152
+ this.onReconnected = () => {
153
+ this.emit("resubscribe", { fromVersion: this.currentVersion });
154
+ this.resubscribe();
155
+ };
156
+ this.onConnected = () => {
157
+ if ((this.currentVersion === -1 || this.currentOpVersion === null) && !this.unsub) {
158
+ this.l("initial connected subscribe", { version: this.currentVersion, opVersion: this.currentOpVersion });
159
+ this.subscribe(this.currentVersion, this.currentOpVersion);
160
+ }
161
+ };
162
+ this.client.on("reconnected", this.onReconnected);
163
+ this.client.on("connected", this.onConnected);
164
+ }
165
+ detachClientEvents() {
166
+ if (this.onReconnected) {
167
+ this.client.off("reconnected", this.onReconnected);
168
+ this.onReconnected = void 0;
169
+ }
170
+ if (this.onConnected) {
171
+ this.client.off("connected", this.onConnected);
172
+ this.onConnected = void 0;
173
+ }
174
+ }
175
+ needsFullSync() {
176
+ return this.currentVersion === -1 || this.currentOpVersion === null;
177
+ }
178
+ targetMap() {
179
+ return this.fullSyncPhase && this.buffer ? this.buffer : this.mirror;
180
+ }
181
+ advanceNumeric(v) {
182
+ if (typeof v === "number" && v > this.currentVersion) this.currentVersion = v;
183
+ }
184
+ advanceOp(v) {
185
+ if (v && (!this.currentOpVersion || v > this.currentOpVersion)) this.currentOpVersion = v;
186
+ }
187
+ subscribe(version, opVersion) {
188
+ if (!this.client) throw new Error("SubscriptionClient missing");
189
+ if (this.unsub) {
190
+ this.unsub();
191
+ this.unsub = null;
192
+ }
193
+ const variables = __spreadValues({ version, opVersion }, this.options.baseVariables || {});
194
+ this.l("subscribe", { version, opVersion });
195
+ this.unsub = this.client.subscribe(
196
+ { query: this.options.subscription, variables },
197
+ {
198
+ next: (envelope) => {
199
+ if (envelope.errors) {
200
+ this.l("graphql errors", envelope.errors);
201
+ this.emit("error", envelope.errors);
202
+ }
203
+ const payload = envelope.data;
204
+ if (payload) this.handlePayload(payload);
205
+ },
206
+ error: (err) => this.l("subscription error", err),
207
+ complete: () => this.l("subscription complete")
208
+ }
209
+ );
210
+ }
211
+ resubscribe() {
212
+ this.subscribe(this.currentVersion, this.currentOpVersion);
213
+ }
214
+ start() {
215
+ if (!this.client.isConnected()) {
216
+ this.l("start deferred (not connected)");
217
+ return;
218
+ }
219
+ this.subscribe(this.currentVersion, this.currentOpVersion);
220
+ }
221
+ load() {
222
+ if (!this.unsub) this.start();
223
+ return this.loaded;
224
+ }
225
+ resetLoaded() {
226
+ this.loaded = new Promise((resolve) => {
227
+ this.resolveLoaded = resolve;
228
+ });
229
+ }
230
+ handlePayload(raw) {
231
+ if (!raw) return;
232
+ const firstKey = Object.keys(raw)[0];
233
+ if (!firstKey) return;
234
+ const msg = raw[firstKey];
235
+ if (!msg) return;
236
+ const { type } = msg;
237
+ switch (type) {
238
+ case "START" /* START */: {
239
+ const isFull = this.needsFullSync();
240
+ this.fullSyncPhase = isFull;
241
+ this.buffer = isFull ? /* @__PURE__ */ new Map() : null;
242
+ const payload = { full: isFull };
243
+ this.emit("start", payload);
244
+ break;
245
+ }
246
+ case "UPDATED" /* UPDATED */:
247
+ this.applyUpdated(msg.data, this.fullSyncPhase);
248
+ break;
249
+ case "DELETED" /* DELETED */:
250
+ this.applyDeleted(msg.deleteId, msg.deleteVersion, msg.deleteOpVersion);
251
+ break;
252
+ case "DONE" /* DONE */: {
253
+ const wasFull = this.fullSyncPhase;
254
+ if (wasFull && this.buffer) {
255
+ this.mirror = this.buffer;
256
+ this.buffer = null;
257
+ }
258
+ this.fullSyncPhase = false;
259
+ const d = { full: wasFull };
260
+ this.emit("done", d);
261
+ if (this.resolveLoaded) {
262
+ const r = this.resolveLoaded;
263
+ this.resolveLoaded = void 0;
264
+ r(this.dmap);
265
+ }
266
+ break;
267
+ }
268
+ case "VERSION_ERROR" /* VERSION_ERROR */:
269
+ const ve = { currentVersion: this.currentVersion };
270
+ this.emit("versionError", ve);
271
+ this.currentVersion = -1;
272
+ this.currentOpVersion = null;
273
+ this.buffer = null;
274
+ this.resetLoaded();
275
+ this.subscribe(this.currentVersion, this.currentOpVersion);
276
+ break;
277
+ }
278
+ }
279
+ normalizeItems(data) {
280
+ if (!data) return [];
281
+ return Array.isArray(data) ? data : [data];
282
+ }
283
+ applyUpdated(data, isFullPhase) {
284
+ for (const item of this.normalizeItems(data)) {
285
+ if (!item || typeof item.id !== "string") continue;
286
+ const hasNumeric = typeof item.version === "number";
287
+ const hasOp = typeof item.opVersion === "string";
288
+ if (!hasNumeric && !hasOp) continue;
289
+ const existing = this.targetMap().get(item.id);
290
+ const newerNumeric = hasNumeric && (!(existing == null ? void 0 : existing.version) || item.version >= existing.version);
291
+ const newerOp = hasOp && (!(existing == null ? void 0 : existing.opVersion) || item.opVersion >= existing.opVersion);
292
+ if (!existing || newerNumeric || newerOp) {
293
+ this.targetMap().set(item.id, item);
294
+ this.advanceNumeric(item.version);
295
+ this.advanceOp(item.opVersion);
296
+ this.emit("updated", item, isFullPhase);
297
+ }
298
+ }
299
+ }
300
+ applyDeleted(id, deleteVersion, deleteOpVersion) {
301
+ var _a;
302
+ if (!id) return;
303
+ const numericStale = !this.fullSyncPhase && typeof deleteVersion === "number" && this.currentVersion >= 0 && deleteVersion < this.currentVersion;
304
+ const opStale = !this.fullSyncPhase && deleteOpVersion && this.currentOpVersion && deleteOpVersion < this.currentOpVersion;
305
+ if (numericStale && opStale) {
306
+ (_a = this.l) == null ? void 0 : _a.call(this, "skip stale delete", { id, deleteVersion, deleteOpVersion });
307
+ return;
308
+ }
309
+ this.targetMap().delete(id);
310
+ this.advanceNumeric(deleteVersion);
311
+ this.advanceOp(deleteOpVersion);
312
+ const del = { id, version: deleteVersion };
313
+ if (deleteOpVersion) del.opVersion = deleteOpVersion;
314
+ this.emit("deleted", del);
315
+ }
316
+ get(id) {
317
+ return this.mirror.get(id);
318
+ }
319
+ values() {
320
+ return Array.from(this.mirror.values());
321
+ }
322
+ stats() {
323
+ return { version: this.currentVersion, opVersion: this.currentOpVersion, size: this.mirror.size, fullSyncInProgress: this.fullSyncPhase };
324
+ }
325
+ stop() {
326
+ if (this.unsub) {
327
+ this.unsub();
328
+ this.unsub = null;
329
+ }
330
+ this.detachClientEvents();
331
+ this.currentVersion = -1;
332
+ this.currentOpVersion = null;
333
+ this.buffer = null;
334
+ if (this.resolveLoaded) {
335
+ const r = this.resolveLoaded;
336
+ this.resolveLoaded = void 0;
337
+ r(this.dmap);
338
+ }
339
+ this.resetLoaded();
340
+ }
341
+ };
342
+
343
+ // src/SubscriptionClient.ts
344
+ var import_graphql_ws = require("graphql-ws");
345
+ var import_eventemitter32 = __toESM(require("eventemitter3"));
346
+ var SubscriptionClient = class extends import_eventemitter32.default {
347
+ constructor(opts) {
348
+ var _a, _b, _c, _d, _e;
349
+ super();
350
+ this.client = null;
351
+ this.connectAttempt = 0;
352
+ this.closedExplicitly = false;
353
+ this.reconnecting = false;
354
+ this.hasEverConnected = false;
355
+ this.options = {
356
+ url: opts.url.replace(/^http/, "ws").replace(/^https/, "wss") + "/ws",
357
+ connectionParams: opts.connectionParams,
358
+ maxReconnectAttempts: (_a = opts.maxReconnectAttempts) != null ? _a : Infinity,
359
+ retryDelayMs: (_b = opts.retryDelayMs) != null ? _b : 500,
360
+ maxRetryDelayMs: (_c = opts.maxRetryDelayMs) != null ? _c : 1e4,
361
+ operationTimeoutMs: (_d = opts.operationTimeoutMs) != null ? _d : 0,
362
+ webSocketImpl: (_e = opts.webSocketImpl) != null ? _e : void 0,
363
+ log: opts.log
364
+ };
365
+ }
366
+ resolveWebSocketImpl() {
367
+ if (this.options.webSocketImpl) return this.options.webSocketImpl;
368
+ const proxyEnv = process.env.ALL_PROXY || process.env.HTTPS_PROXY || process.env.HTTP_PROXY || process.env.GLOBAL_AGENT_HTTP_PROXY;
369
+ if (proxyEnv) {
370
+ try {
371
+ return require("ws");
372
+ } catch (e) {
373
+ this.log("Proxy detected but failed to load ws package", e);
374
+ }
375
+ }
376
+ const g = globalThis;
377
+ if (typeof g.WebSocket === "function") return g.WebSocket;
378
+ try {
379
+ return require("ws");
380
+ } catch (e) {
381
+ throw new Error("No WebSocket implementation found. Provide webSocketImpl in SubscriptionClientOptions.");
382
+ }
383
+ }
384
+ /** Returns true if an underlying websocket client exists (connected or in-flight). */
385
+ isConnected() {
386
+ return !!this.client;
387
+ }
388
+ log(msg, meta) {
389
+ if (this.options.log) this.options.log(msg, meta);
390
+ }
391
+ createClientIfNeeded(isReconnect) {
392
+ if (this.client) return;
393
+ const { url, connectionParams } = this.options;
394
+ const webSocketImpl = this.resolveWebSocketImpl();
395
+ this.client = (0, import_graphql_ws.createClient)({
396
+ url,
397
+ connectionParams,
398
+ webSocketImpl,
399
+ lazy: false,
400
+ keepAlive: 12e3,
401
+ retryAttempts: 0,
402
+ shouldRetry: () => false,
403
+ on: {
404
+ connected: () => {
405
+ const wasReconnect = isReconnect && this.connectAttempt > 0;
406
+ this.connectAttempt = 0;
407
+ if (wasReconnect && this.hasEverConnected) {
408
+ this.emit("reconnected");
409
+ this.log("reconnected");
410
+ } else {
411
+ this.emit("connected");
412
+ this.log("connected");
413
+ }
414
+ this.hasEverConnected = true;
415
+ },
416
+ error: (err) => {
417
+ this.emit("error", err);
418
+ this.log("socket error", err);
419
+ },
420
+ closed: (ev) => {
421
+ var _a, _b, _c, _d;
422
+ const e = ev;
423
+ this.log("closed", e);
424
+ this.emit("disconnected", { code: e.code, reason: (_c = (_b = (_a = e.reason) == null ? void 0 : _a.toString) == null ? void 0 : _b.call(_a)) != null ? _c : "", wasClean: (_d = e.wasClean) != null ? _d : false });
425
+ this.client = null;
426
+ if (!this.closedExplicitly) this.scheduleReconnect();
427
+ }
428
+ }
429
+ });
430
+ }
431
+ // assureConnected removed per revert request
432
+ /** Establish the WebSocket connection if not already created. Idempotent. */
433
+ connect() {
434
+ if (this.client) return;
435
+ this.emit("connecting");
436
+ this.log("connecting");
437
+ this.createClientIfNeeded(false);
438
+ }
439
+ scheduleReconnect() {
440
+ if (this.reconnecting) return;
441
+ if (this.connectAttempt >= this.options.maxReconnectAttempts) {
442
+ this.log("max reconnect attempts reached");
443
+ return;
444
+ }
445
+ this.reconnecting = true;
446
+ const delay = Math.min(
447
+ this.options.retryDelayMs * Math.pow(2, this.connectAttempt),
448
+ this.options.maxRetryDelayMs
449
+ );
450
+ this.emit("retry", { attempt: this.connectAttempt });
451
+ this.log("reconnect scheduled", { attempt: this.connectAttempt, delay });
452
+ setTimeout(() => {
453
+ this.reconnecting = false;
454
+ this.connectAttempt++;
455
+ this.emit("connecting");
456
+ this.log("connecting");
457
+ this.createClientIfNeeded(true);
458
+ }, delay);
459
+ }
460
+ subscribe(options, observer) {
461
+ var _a;
462
+ if (!this.client) throw new Error("Client not connected - call connect() first");
463
+ const payload = {
464
+ query: options.query,
465
+ variables: options.variables,
466
+ operationName: options.operationName,
467
+ extensions: options.extensions
468
+ };
469
+ let finished = false;
470
+ const timeoutMs = (_a = options.timeoutMs) != null ? _a : this.options.operationTimeoutMs;
471
+ let timeout;
472
+ if (timeoutMs > 0) {
473
+ timeout = setTimeout(() => {
474
+ var _a2;
475
+ if (finished) return;
476
+ finished = true;
477
+ (_a2 = observer.error) == null ? void 0 : _a2.call(observer, new Error("Operation timed out"));
478
+ dispose();
479
+ }, timeoutMs);
480
+ }
481
+ const dispose = this.client.subscribe(payload, {
482
+ next: (data) => {
483
+ var _a2;
484
+ return (_a2 = observer.next) == null ? void 0 : _a2.call(observer, data);
485
+ },
486
+ error: (err) => {
487
+ var _a2;
488
+ if (finished) return;
489
+ finished = true;
490
+ if (timeout) clearTimeout(timeout);
491
+ (_a2 = observer.error) == null ? void 0 : _a2.call(observer, err);
492
+ },
493
+ complete: () => {
494
+ var _a2;
495
+ if (finished) return;
496
+ finished = true;
497
+ if (timeout) clearTimeout(timeout);
498
+ (_a2 = observer.complete) == null ? void 0 : _a2.call(observer);
499
+ }
500
+ });
501
+ return () => {
502
+ if (finished) return;
503
+ finished = true;
504
+ if (timeout) clearTimeout(timeout);
505
+ dispose();
506
+ };
507
+ }
508
+ /** Async iterator variant (convenience wrapper around underlying client.iterate). */
509
+ iterate(options) {
510
+ if (!this.client) throw new Error("Client not connected - call connect() first");
511
+ const payload = {
512
+ query: options.query,
513
+ variables: options.variables,
514
+ operationName: options.operationName,
515
+ extensions: options.extensions
516
+ };
517
+ return this.client.iterate(payload);
518
+ }
519
+ async close(code = 1e3, reason = "Normal Closure") {
520
+ this.closedExplicitly = true;
521
+ this.log("closing client");
522
+ const c = this.client;
523
+ if (c) {
524
+ try {
525
+ c.dispose();
526
+ } catch (e) {
527
+ }
528
+ }
529
+ this.client = null;
530
+ this.removeAllListeners();
531
+ }
532
+ };
533
+ var SubscriptionClient_default = SubscriptionClient;
534
+
535
+ // src/index.ts
536
+ var Connection = class extends SubscriptionClient_default {
537
+ constructor(url, connectionParams) {
538
+ super({
539
+ url,
540
+ connectionParams
541
+ });
542
+ this.mirrorMap = /* @__PURE__ */ new Map();
543
+ this.mirrors = new DelegatingObject(new DelegatingMap(() => this.mirrorMap), (v) => v.sync.Storage);
544
+ }
545
+ get Mirrors() {
546
+ return this.mirrors.asObject();
547
+ }
548
+ getMirror(name, subscription, baseVariables) {
549
+ let entry = this.mirrorMap.get(name);
550
+ if (!entry) {
551
+ const sync = new MirrorSync(this, { subscription, baseVariables });
552
+ const forward = (event, payload) => {
553
+ this.emit(`mirror:${event}`, __spreadValues({ mirrorName: name }, payload));
554
+ };
555
+ const handlers = [
556
+ ["start", (p) => forward("start", p)],
557
+ ["updated", (item, fullPhase) => forward("updated", { item, fullPhase })],
558
+ ["deleted", (p) => forward("deleted", p)],
559
+ ["done", (p) => forward("done", p)],
560
+ ["versionError", (p) => forward("versionError", p)],
561
+ ["resubscribe", (p) => forward("resubscribe", p)],
562
+ ["error", (p) => forward("error", p)]
563
+ ];
564
+ handlers.forEach(([e, h]) => sync.on(e, h));
565
+ const detach = () => handlers.forEach(([e, h]) => sync.off(e, h));
566
+ this.mirrorMap.set(name, { sync, detach });
567
+ entry = { sync, detach };
568
+ }
569
+ return entry.sync.load();
570
+ }
571
+ removeMirror(name) {
572
+ const entry = this.mirrorMap.get(name);
573
+ if (!entry) return;
574
+ entry.detach();
575
+ entry.sync.stop();
576
+ this.mirrorMap.delete(name);
577
+ this.emit("mirror:removed", { mirrorName: name });
578
+ }
579
+ clearMirrors() {
580
+ for (const name of Array.from(this.mirrorMap.keys())) {
581
+ this.removeMirror(name);
582
+ }
583
+ this.emit("mirror:cleared");
584
+ }
585
+ };
586
+ // Annotate the CommonJS export names for ESM import in node:
587
+ 0 && (module.exports = {
588
+ Connection,
589
+ DelegatingObject,
590
+ MirrorSync,
591
+ SubscriptionClient
592
+ });
593
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/DelegateMap.ts","../src/MirrorSync.ts","../src/SubscriptionClient.ts"],"sourcesContent":["import { MirrorSync } from \"./MirrorSync\";\r\nimport { ReadonlyMapView, DelegatingObject, DelegatingMap } from \"./DelegateMap\";\r\nimport SubscriptionClient from \"./SubscriptionClient\";\r\n\r\nexport { SubscriptionClient, MirrorSync, DelegatingObject };\r\nexport type { ReadonlyMapView };\r\n\r\nexport class Connection extends SubscriptionClient {\r\n private mirrorMap: Map<string, { sync: MirrorSync; detach: () => void }> = new Map();\r\n private mirrors = new DelegatingObject<string, any>(new DelegatingMap(() => this.mirrorMap), (v) => v.sync.Storage);\r\n get Mirrors(): any { return this.mirrors.asObject(); }\r\n\r\n constructor(url: string, connectionParams: () => Promise<Record<string, unknown> | Record<string, unknown>>) {\r\n super({\r\n url: url,\r\n connectionParams: connectionParams\r\n });\r\n }\r\n\r\n getMirror(name: string, subscription: string, baseVariables: Record<string, unknown>): Promise<ReadonlyMapView<string, any>> {\r\n let entry = this.mirrorMap.get(name);\r\n if (!entry) {\r\n const sync = new MirrorSync(this, { subscription, baseVariables });\r\n const forward = (event: string, payload: any) => {\r\n this.emit(`mirror:${event}`, { mirrorName: name, ...payload });\r\n };\r\n const handlers: Array<[string, (...args: any[]) => void]> = [\r\n ['start', (p) => forward('start', p)],\r\n ['updated', (item, fullPhase) => forward('updated', { item, fullPhase })],\r\n ['deleted', (p) => forward('deleted', p)],\r\n ['done', (p) => forward('done', p)],\r\n ['versionError', (p) => forward('versionError', p)],\r\n ['resubscribe', (p) => forward('resubscribe', p)],\r\n ['error', (p) => forward('error', p)],\r\n ];\r\n handlers.forEach(([e, h]) => sync.on(e as any, h));\r\n const detach = () => handlers.forEach(([e, h]) => sync.off(e as any, h));\r\n this.mirrorMap.set(name, { sync, detach });\r\n entry = { sync, detach };\r\n }\r\n return entry.sync.load();\r\n }\r\n\r\n removeMirror(name: string) {\r\n const entry = this.mirrorMap.get(name);\r\n if (!entry) return;\r\n entry.detach();\r\n entry.sync.stop();\r\n this.mirrorMap.delete(name);\r\n this.emit('mirror:removed', { mirrorName: name });\r\n }\r\n\r\n clearMirrors() {\r\n for (const name of Array.from(this.mirrorMap.keys())) {\r\n this.removeMirror(name);\r\n }\r\n this.emit('mirror:cleared');\r\n }\r\n}\r\n","// Restored specialized read-only view interface (runtime enforced via absence of mutators)\r\nexport interface ReadonlyMapView<K, V> extends Iterable<[K, V]> {\r\n readonly size: number;\r\n get(key: K): V | undefined;\r\n has(key: K): boolean;\r\n entries(): IterableIterator<[K, V]>;\r\n keys(): IterableIterator<K>;\r\n values(): IterableIterator<V>;\r\n forEach(callbackfn: (value: V, key: K, map: ReadonlyMapView<K, V>) => void, thisArg?: any): void;\r\n [Symbol.iterator](): IterableIterator<[K, V]>;\r\n}\r\n\r\nexport class DelegatingMap<K, V> implements ReadonlyMapView<K, V> {\r\n constructor(private readonly resolver: () => Map<K, V>) { }\r\n private t(): Map<K, V> { return this.resolver(); }\r\n get size(): number { return this.t().size; }\r\n get(key: K): V | undefined { return this.t().get(key); }\r\n has(key: K): boolean { return this.t().has(key); }\r\n entries(): IterableIterator<[K, V]> { return this.t().entries(); }\r\n keys(): IterableIterator<K> { return this.t().keys(); }\r\n values(): IterableIterator<V> { return this.t().values(); }\r\n forEach(callbackfn: (value: V, key: K, map: ReadonlyMapView<K, V>) => void, thisArg?: any): void {\r\n this.t().forEach(((v: V, k: K) => callbackfn(v, k, this)) as any, thisArg);\r\n }\r\n [Symbol.iterator](): IterableIterator<[K, V]> { return this.t()[Symbol.iterator](); }\r\n}\r\n\r\n/**\r\n * Presents a ReadonlyMapView<K,V> as an immutable plain object via Proxy.\r\n * Optionally maps values through a mapper function on access.\r\n */\r\nexport class DelegatingObject<K extends string, V, R = V> {\r\n private readonly proxy: Record<K, R>;\r\n constructor(private readonly source: ReadonlyMapView<K, V>, private readonly mapFn?: (value: V, key: K) => R) {\r\n const mapper = (v: V, k: K): R => (this.mapFn ? this.mapFn(v, k) : (v as unknown as R));\r\n const handler: ProxyHandler<any> = {\r\n get: (_target, prop: PropertyKey, receiver) => {\r\n if (prop === Symbol.toStringTag) return 'Object';\r\n if (typeof prop !== 'string') return Reflect.get(Object.prototype, prop, receiver);\r\n const key = prop as K;\r\n const v = this.source.get(key);\r\n return v === undefined ? undefined : mapper(v, key);\r\n },\r\n set: () => false, // immutable\r\n has: (_target, prop: PropertyKey) => typeof prop === 'string' && this.source.has(prop as K),\r\n deleteProperty: () => false, // immutable\r\n ownKeys: () => Array.from(this.source.keys() as unknown as IterableIterator<string>),\r\n getOwnPropertyDescriptor: (_target, prop: PropertyKey) => {\r\n if (typeof prop === 'string' && this.source.has(prop as K)) {\r\n const key = prop as K;\r\n const v = this.source.get(key)!;\r\n return { configurable: true, enumerable: true, value: mapper(v, key) };\r\n }\r\n return undefined;\r\n },\r\n defineProperty: () => false, // immutable\r\n };\r\n this.proxy = new Proxy(Object.create(null), handler);\r\n }\r\n asObject(): Record<K, R> { return this.proxy; }\r\n}\r\n","import { DelegatingMap, ReadonlyMapView } from './DelegateMap';\r\nimport { SubscriptionClient } from './SubscriptionClient';\r\nimport EventEmitter from 'eventemitter3';\r\n\r\n// SyncType enum as described\r\nexport enum SyncType {\r\n DELETED = 'DELETED',\r\n UPDATED = 'UPDATED',\r\n START = 'START',\r\n DONE = 'DONE',\r\n VERSION_ERROR = 'VERSION_ERROR',\r\n}\r\n\r\n// Shape of the subscription payload (generic)\r\nexport interface CacheSyncResult {\r\n type: SyncType;\r\n total?: number | null;\r\n deleteId?: string; // ObjectId as string\r\n data?: any;\r\n deleteVersion?: number;\r\n // Optional operation-based versioning (string) for hybrid mode\r\n deleteOpVersion?: string;\r\n opVersion?: string; // May appear on items inside data\r\n}\r\n\r\nexport interface MirrorSyncOptions {\r\n /** Subscription document (query string) */\r\n subscription: string;\r\n /** Optional logger */\r\n /** Custom variables base (besides version) */\r\n baseVariables?: Record<string, unknown>;\r\n}\r\n\r\nexport interface MirrorStats {\r\n version: number; // numeric version cursor\r\n opVersion?: string | null; // operation (ObjectId) cursor\r\n size: number;\r\n fullSyncInProgress: boolean;\r\n totalHint?: number | null;\r\n}\r\n\r\nexport interface MirrorSyncEvents {\r\n start: (info: { full: boolean }) => void;\r\n updated: (item: MirrorItem, isFullPhase: boolean) => void;\r\n deleted: (info: { id: string; version: number | undefined }) => void;\r\n done: (info: { full: boolean }) => void;\r\n versionError: (info: { currentVersion: number }) => void;\r\n resubscribe: (info: { fromVersion: number }) => void;\r\n error?: (errors: any) => void;\r\n}\r\n\r\n\r\nexport interface MirrorItem { id: string; version?: number; opVersion?: string;[k: string]: any }\r\n\r\n/**\r\n * MirrorSync maintains an in-memory mirror of server state based on CacheSyncResult messages.\r\n * Generic TBase is the item shape (must include id & version).\r\n */\r\nexport class MirrorSync extends EventEmitter {\r\n private client: SubscriptionClient;\r\n private options: MirrorSyncOptions;\r\n private mirror = new Map<string, MirrorItem>();\r\n private buffer: Map<string, MirrorItem> | null = null; // used during full/resubscription phase\r\n private currentVersion = -1; // numeric cursor (-1 => full sync needed)\r\n private currentOpVersion: string | null = null; // op cursor (null => full sync needed)\r\n private unsub: (() => void) | null = null;\r\n private fullSyncPhase = false; // only true during START..DONE when initiating from version -1\r\n private log?: (msg: string, meta?: any) => void;\r\n private dmap = new DelegatingMap<string, any>(() => this.mirror);\r\n /** Deferred promise resolved on first DONE after a (re)start full sync cycle. Reset on constructor, start(), and VERSION_ERROR re-init. */\r\n public loaded!: Promise<ReadonlyMapView<string, any>>; // set via resetLoaded()\r\n private resolveLoaded?: (ref: ReadonlyMapView<string, any>) => void;\r\n // Stored client event handlers to allow proper detachment\r\n private onReconnected?: () => void;\r\n private onConnected?: () => void;\r\n\r\n get Storage(): ReadonlyMapView<string, any> { return this.mirror; }\r\n\r\n constructor(client: SubscriptionClient, options: MirrorSyncOptions & { log?: (msg: string, meta?: any) => void }) {\r\n super();\r\n this.client = client;\r\n this.options = options;\r\n this.log = options.log;\r\n this.attachClientEvents();\r\n this.resetLoaded();\r\n }\r\n\r\n private l(msg: string, meta?: any) { if (this.log) this.log(msg, meta); }\r\n\r\n private attachClientEvents() {\r\n this.onReconnected = () => {\r\n this.emit('resubscribe', { fromVersion: this.currentVersion });\r\n this.resubscribe();\r\n };\r\n this.onConnected = () => {\r\n if ((this.currentVersion === -1 || this.currentOpVersion === null) && !this.unsub) {\r\n this.l('initial connected subscribe', { version: this.currentVersion, opVersion: this.currentOpVersion });\r\n this.subscribe(this.currentVersion, this.currentOpVersion);\r\n }\r\n };\r\n this.client.on('reconnected', this.onReconnected);\r\n this.client.on('connected', this.onConnected);\r\n }\r\n\r\n private detachClientEvents() {\r\n if (this.onReconnected) {\r\n this.client.off('reconnected', this.onReconnected);\r\n this.onReconnected = undefined;\r\n }\r\n if (this.onConnected) {\r\n this.client.off('connected', this.onConnected);\r\n this.onConnected = undefined;\r\n }\r\n }\r\n\r\n private needsFullSync(): boolean { return this.currentVersion === -1 || this.currentOpVersion === null; }\r\n private targetMap(): Map<string, MirrorItem> { return (this.fullSyncPhase && this.buffer) ? this.buffer! : this.mirror; }\r\n private advanceNumeric(v?: number) { if (typeof v === 'number' && v > this.currentVersion) this.currentVersion = v; }\r\n private advanceOp(v?: string) { if (v && (!this.currentOpVersion || v > this.currentOpVersion)) this.currentOpVersion = v; }\r\n\r\n private subscribe(version: number, opVersion: string | null) {\r\n if (!this.client) throw new Error('SubscriptionClient missing');\r\n if (this.unsub) { this.unsub(); this.unsub = null; }\r\n const variables = { version, opVersion, ...(this.options.baseVariables || {}) };\r\n this.l('subscribe', { version, opVersion });\r\n this.unsub = this.client.subscribe<{ [k: string]: CacheSyncResult }>(\r\n { query: this.options.subscription, variables },\r\n {\r\n next: (envelope: { data?: { [k: string]: CacheSyncResult }; errors?: any }) => {\r\n if (envelope.errors) {\r\n this.l('graphql errors', envelope.errors);\r\n this.emit('error', envelope.errors);\r\n }\r\n const payload = envelope.data as { [k: string]: CacheSyncResult } | undefined;\r\n if (payload) this.handlePayload(payload);\r\n },\r\n error: (err: unknown) => this.l('subscription error', err),\r\n complete: () => this.l('subscription complete'),\r\n }\r\n );\r\n }\r\n\r\n private resubscribe() {\r\n // Resubscribe starting from current cursors\r\n this.subscribe(this.currentVersion, this.currentOpVersion);\r\n }\r\n\r\n private start() {\r\n if (!this.client.isConnected()) { this.l('start deferred (not connected)'); return; }\r\n this.subscribe(this.currentVersion, this.currentOpVersion);\r\n }\r\n\r\n load(): Promise<ReadonlyMapView<string, any>> {\r\n if (!this.unsub) this.start();\r\n return this.loaded;\r\n }\r\n\r\n private resetLoaded() {\r\n this.loaded = new Promise<ReadonlyMapView<string, any>>(resolve => {\r\n this.resolveLoaded = resolve;\r\n });\r\n }\r\n\r\n private handlePayload(raw: { [k: string]: CacheSyncResult } | undefined) {\r\n if (!raw) return;\r\n // Always pick the first field in the payload object regardless of configured rootField\r\n const firstKey = Object.keys(raw)[0];\r\n if (!firstKey) return;\r\n const msg = raw[firstKey];\r\n if (!msg) return;\r\n const { type } = msg;\r\n switch (type) {\r\n case SyncType.START: {\r\n const isFull = this.needsFullSync();\r\n this.fullSyncPhase = isFull;\r\n // Only allocate a new buffer for a true full sync; for delta/resubscribe we append directly\r\n this.buffer = isFull ? new Map() : null;\r\n const payload = { full: isFull };\r\n this.emit('start', payload);\r\n break;\r\n }\r\n case SyncType.UPDATED:\r\n this.applyUpdated(msg.data, this.fullSyncPhase);\r\n break;\r\n case SyncType.DELETED:\r\n this.applyDeleted(msg.deleteId, msg.deleteVersion, msg.deleteOpVersion);\r\n break;\r\n case SyncType.DONE: {\r\n const wasFull = this.fullSyncPhase;\r\n if (wasFull && this.buffer) {\r\n this.mirror = this.buffer;\r\n this.buffer = null;\r\n }\r\n this.fullSyncPhase = false;\r\n const d = { full: wasFull };\r\n this.emit('done', d);\r\n // Resolve deferred if pending\r\n if (this.resolveLoaded) {\r\n const r = this.resolveLoaded; this.resolveLoaded = undefined; r(this.dmap);\r\n }\r\n break;\r\n }\r\n case SyncType.VERSION_ERROR:\r\n const ve = { currentVersion: this.currentVersion };\r\n this.emit('versionError', ve);\r\n // restart as full sync\r\n this.currentVersion = -1;\r\n this.currentOpVersion = null;\r\n this.buffer = null;\r\n this.resetLoaded();\r\n this.subscribe(this.currentVersion, this.currentOpVersion);\r\n break;\r\n }\r\n }\r\n\r\n private normalizeItems(data: any): MirrorItem[] {\r\n if (!data) return [];\r\n return Array.isArray(data) ? data : [data];\r\n }\r\n\r\n private applyUpdated(data: any, isFullPhase: boolean) {\r\n for (const item of this.normalizeItems(data)) {\r\n if (!item || typeof item.id !== 'string') continue;\r\n const hasNumeric = typeof item.version === 'number';\r\n const hasOp = typeof item.opVersion === 'string';\r\n if (!hasNumeric && !hasOp) continue;\r\n const existing = this.targetMap().get(item.id);\r\n const newerNumeric = hasNumeric && (!existing?.version || item.version! >= existing.version);\r\n const newerOp = hasOp && (!existing?.opVersion || item.opVersion! >= existing.opVersion);\r\n if (!existing || newerNumeric || newerOp) {\r\n this.targetMap().set(item.id, item);\r\n this.advanceNumeric(item.version);\r\n this.advanceOp(item.opVersion);\r\n this.emit('updated', item, isFullPhase);\r\n }\r\n }\r\n }\r\n private applyDeleted(id?: string, deleteVersion?: number, deleteOpVersion?: string) {\r\n if (!id) return;\r\n const numericStale = !this.fullSyncPhase && typeof deleteVersion === 'number' && this.currentVersion >= 0 && deleteVersion < this.currentVersion;\r\n const opStale = !this.fullSyncPhase && deleteOpVersion && this.currentOpVersion && deleteOpVersion < this.currentOpVersion;\r\n if (numericStale && opStale) {\r\n this.l?.('skip stale delete', { id, deleteVersion, deleteOpVersion });\r\n return;\r\n }\r\n this.targetMap().delete(id);\r\n this.advanceNumeric(deleteVersion);\r\n this.advanceOp(deleteOpVersion);\r\n const del: any = { id, version: deleteVersion };\r\n if (deleteOpVersion) del.opVersion = deleteOpVersion;\r\n this.emit('deleted', del);\r\n }\r\n\r\n get(id: string): any | undefined { return this.mirror.get(id); }\r\n values(): any[] { return Array.from(this.mirror.values()); }\r\n stats(): MirrorStats { return { version: this.currentVersion, opVersion: this.currentOpVersion, size: this.mirror.size, fullSyncInProgress: this.fullSyncPhase }; }\r\n\r\n stop() {\r\n if (this.unsub) { this.unsub(); this.unsub = null; }\r\n this.detachClientEvents();\r\n this.currentVersion = -1;\r\n this.currentOpVersion = null;\r\n this.buffer = null;\r\n if (this.resolveLoaded) {\r\n const r = this.resolveLoaded; this.resolveLoaded = undefined; r(this.dmap);\r\n }\r\n this.resetLoaded();\r\n }\r\n}\r\n","import { createClient, Client, ClientOptions, SubscribePayload } from 'graphql-ws';\r\n// Removed direct 'ws' import for browser compatibility; will resolve at runtime if needed.\r\nimport EventEmitter from 'eventemitter3';\r\n\r\n/** Events emitted by SubscriptionClient\r\n * connecting - before opening a socket (initial or reconnect attempt)\r\n * connected - first successful connection\r\n * reconnected - a subsequent successful connection after a disconnect\r\n * disconnected - socket closed ( { code, reason, wasClean } )\r\n * error - protocol or network error (Error)\r\n * retry - about to attempt reconnect ( { attempt } )\r\n */\r\nexport interface SubscriptionClientEvents {\r\n connecting: () => void;\r\n connected: () => void;\r\n reconnected: () => void;\r\n disconnected: (info: { code: number; reason: string; wasClean: boolean }) => void;\r\n error: (err: unknown) => void;\r\n retry: (info: { attempt: number }) => void;\r\n}\r\n\r\nexport interface SubscriptionClientOptions {\r\n url: string;\r\n /** optional async or sync connection params */\r\n connectionParams?: ClientOptions['connectionParams'];\r\n /** number of reconnect attempts, default Infinity */\r\n maxReconnectAttempts?: number;\r\n /** initial delay ms before first retry (exponential backoff) */\r\n retryDelayMs?: number;\r\n /** max backoff cap */\r\n maxRetryDelayMs?: number;\r\n /** idle timeout for operations (optional) */\r\n operationTimeoutMs?: number;\r\n /** optional custom WebSocket implementation */\r\n webSocketImpl?: any; // generic to avoid pulling node 'ws' types into browser bundle\r\n /** optional logger */\r\n log?: (msg: string, meta?: any) => void;\r\n}\r\n\r\nexport interface SubscribeOptions<TData = any, TVars = Record<string, unknown>> {\r\n query: string;\r\n variables?: TVars;\r\n /** optional per-subscription timeout override */\r\n timeoutMs?: number;\r\n operationName?: string;\r\n extensions?: Record<string, any>;\r\n}\r\n\r\nexport interface Observer<T> {\r\n next?: (data: T) => void;\r\n error?: (err: unknown) => void;\r\n complete?: () => void;\r\n}\r\n\r\nexport class SubscriptionClient extends EventEmitter {\r\n private options: Required<Omit<SubscriptionClientOptions, 'connectionParams' | 'log' | 'webSocketImpl'>> & Pick<SubscriptionClientOptions, 'connectionParams' | 'log' | 'webSocketImpl'>;\r\n private client: Client | null = null;\r\n private connectAttempt = 0;\r\n private closedExplicitly = false;\r\n private reconnecting = false;\r\n private hasEverConnected = false;\r\n\r\n constructor(opts: SubscriptionClientOptions) {\r\n super();\r\n this.options = {\r\n url: opts.url.replace(/^http/, \"ws\").replace(/^https/, \"wss\") + '/ws',\r\n connectionParams: opts.connectionParams,\r\n maxReconnectAttempts: opts.maxReconnectAttempts ?? Infinity,\r\n retryDelayMs: opts.retryDelayMs ?? 500,\r\n maxRetryDelayMs: opts.maxRetryDelayMs ?? 10_000,\r\n operationTimeoutMs: opts.operationTimeoutMs ?? 0,\r\n webSocketImpl: opts.webSocketImpl ?? undefined,\r\n log: opts.log,\r\n } as any;\r\n }\r\n\r\n private resolveWebSocketImpl(): any {\r\n // Proxy-aware priority:\r\n // 1. Explicit user-supplied impl\r\n // 2. If proxy env detected -> prefer 'ws' (so caller can attach proxy agent externally if needed)\r\n // 3. Global WebSocket (browser / Node >= 18 native) if no proxy forcing 'ws'\r\n // 4. Fallback to require('ws')\r\n if (this.options.webSocketImpl) return this.options.webSocketImpl;\r\n const proxyEnv = process.env.ALL_PROXY || process.env.HTTPS_PROXY || process.env.HTTP_PROXY || process.env.GLOBAL_AGENT_HTTP_PROXY;\r\n if (proxyEnv) {\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-var-requires\r\n return require('ws');\r\n } catch (e) {\r\n this.log('Proxy detected but failed to load ws package', e);\r\n }\r\n }\r\n const g: any = (globalThis as any);\r\n if (typeof g.WebSocket === 'function') return g.WebSocket;\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-var-requires\r\n return require('ws');\r\n } catch {\r\n throw new Error('No WebSocket implementation found. Provide webSocketImpl in SubscriptionClientOptions.');\r\n }\r\n }\r\n\r\n /** Returns true if an underlying websocket client exists (connected or in-flight). */\r\n isConnected(): boolean {\r\n return !!this.client; // createClientIfNeeded only after socket open attempt\r\n }\r\n\r\n private log(msg: string, meta?: any) {\r\n if (this.options.log) this.options.log(msg, meta);\r\n }\r\n\r\n private createClientIfNeeded(isReconnect: boolean) {\r\n if (this.client) return; // already have client\r\n const { url, connectionParams } = this.options;\r\n const webSocketImpl = this.resolveWebSocketImpl();\r\n this.client = createClient({\r\n url,\r\n connectionParams,\r\n webSocketImpl: webSocketImpl as any,\r\n lazy: false,\r\n keepAlive: 12000,\r\n retryAttempts: 0,\r\n shouldRetry: () => false,\r\n on: {\r\n connected: () => {\r\n const wasReconnect = isReconnect && this.connectAttempt > 0;\r\n this.connectAttempt = 0;\r\n if (wasReconnect && this.hasEverConnected) {\r\n this.emit('reconnected');\r\n this.log('reconnected');\r\n } else {\r\n this.emit('connected');\r\n this.log('connected');\r\n }\r\n this.hasEverConnected = true;\r\n },\r\n error: (err) => {\r\n this.emit('error', err);\r\n this.log('socket error', err);\r\n },\r\n closed: (ev) => {\r\n const e = ev as CloseEvent | { code: number; reason: string; wasClean: boolean };\r\n this.log('closed', e);\r\n this.emit('disconnected', { code: (e as any).code, reason: (e as any).reason?.toString?.() ?? '', wasClean: (e as any).wasClean ?? false });\r\n this.client = null; // allow recreate\r\n if (!this.closedExplicitly) this.scheduleReconnect();\r\n },\r\n },\r\n });\r\n }\r\n\r\n // assureConnected removed per revert request\r\n\r\n /** Establish the WebSocket connection if not already created. Idempotent. */\r\n connect(): void {\r\n if (this.client) return; // already created\r\n this.emit('connecting');\r\n this.log('connecting');\r\n this.createClientIfNeeded(false);\r\n }\r\n\r\n private scheduleReconnect() {\r\n if (this.reconnecting) return;\r\n if (this.connectAttempt >= this.options.maxReconnectAttempts) {\r\n this.log('max reconnect attempts reached');\r\n return;\r\n }\r\n this.reconnecting = true;\r\n const delay = Math.min(\r\n this.options.retryDelayMs * Math.pow(2, this.connectAttempt),\r\n this.options.maxRetryDelayMs,\r\n );\r\n this.emit('retry', { attempt: this.connectAttempt });\r\n this.log('reconnect scheduled', { attempt: this.connectAttempt, delay });\r\n setTimeout(() => {\r\n this.reconnecting = false;\r\n this.connectAttempt++;\r\n this.emit('connecting');\r\n this.log('connecting');\r\n this.createClientIfNeeded(true);\r\n }, delay);\r\n }\r\n\r\n subscribe<TData = any, TVars extends Record<string, unknown> = Record<string, unknown>>(options: SubscribeOptions<TData, TVars>, observer: Observer<{ data?: TData; errors?: any }>): () => void {\r\n if (!this.client) throw new Error('Client not connected - call connect() first');\r\n const payload: SubscribePayload = {\r\n query: options.query,\r\n variables: options.variables as any,\r\n operationName: options.operationName,\r\n extensions: options.extensions,\r\n };\r\n let finished = false;\r\n const timeoutMs = options.timeoutMs ?? this.options.operationTimeoutMs;\r\n let timeout: NodeJS.Timeout | undefined;\r\n if (timeoutMs > 0) {\r\n timeout = setTimeout(() => {\r\n if (finished) return;\r\n finished = true;\r\n observer.error?.(new Error('Operation timed out'));\r\n dispose();\r\n }, timeoutMs);\r\n }\r\n const dispose = this.client.subscribe(payload, {\r\n next: (data) => observer.next?.(data as any),\r\n error: (err) => {\r\n if (finished) return;\r\n finished = true;\r\n if (timeout) clearTimeout(timeout);\r\n observer.error?.(err);\r\n },\r\n complete: () => {\r\n if (finished) return;\r\n finished = true;\r\n if (timeout) clearTimeout(timeout);\r\n observer.complete?.();\r\n },\r\n });\r\n return () => {\r\n if (finished) return;\r\n finished = true;\r\n if (timeout) clearTimeout(timeout);\r\n dispose();\r\n };\r\n }\r\n\r\n /** Async iterator variant (convenience wrapper around underlying client.iterate). */\r\n iterate<TData = any, TVars extends Record<string, unknown> = Record<string, unknown>>(options: SubscribeOptions<TData, TVars>): AsyncIterable<{ data?: TData; errors?: any }> {\r\n if (!this.client) throw new Error('Client not connected - call connect() first');\r\n const payload: SubscribePayload = {\r\n query: options.query,\r\n variables: options.variables as any,\r\n operationName: options.operationName,\r\n extensions: options.extensions,\r\n };\r\n // graphql-ws client.iterate returns AsyncIterableExecutionResult\r\n // We forward it directly (user can for-await over it).\r\n return (this.client as any).iterate(payload);\r\n }\r\n\r\n async close(code = 1000, reason = 'Normal Closure') {\r\n this.closedExplicitly = true;\r\n this.log('closing client');\r\n const c = this.client as any;\r\n if (c) { try { c.dispose(); } catch { } }\r\n this.client = null;\r\n this.removeAllListeners();\r\n }\r\n}\r\n\r\nexport default SubscriptionClient;\r\n"],"mappings":"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","names":["EventEmitter","import_eventemitter3","EventEmitter","_a"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanolink/mirrors",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "GraphQL subscription client + mirror synchronization utilities.",
5
5
  "license": "MIT",
6
6
  "author": "Nanolink ApS, Mogens Bak Nielsen",
@@ -10,16 +10,19 @@
10
10
  "scripts": {
11
11
  "clean": "rimraf dist",
12
12
  "build": "npm run clean && tsc -p .",
13
+ "build:compat": "tsup src/index.ts --format cjs --target es2017 --out-dir dist-compat --sourcemap --no-splitting --no-dts --no-minify --no-clean --no-shims --external ws",
14
+ "build:all": "npm run build && npm run build:compat",
13
15
  "dev": "tsx watch src/index.ts",
14
16
  "start": "node dist/index.js",
15
17
  "typecheck": "tsc -p . --noEmit",
16
- "prepublishOnly": "npm run build",
18
+ "prepublishOnly": "npm run build:all",
17
19
  "publish:dry": "npm publish --dry-run",
18
- "release": "npm run build && npm publish --access public",
20
+ "release": "npm run build:all && npm publish --access public",
19
21
  "test": "echo \"No tests\""
20
22
  },
21
23
  "devDependencies": {
22
24
  "@types/node": "^24.4.0",
25
+ "tsup": "^8.1.0",
23
26
  "tsx": "^4.20.5",
24
27
  "typescript": "^5.9.2",
25
28
  "rimraf": "^5.0.5"
@@ -34,9 +37,14 @@
34
37
  },
35
38
  "files": [
36
39
  "dist",
40
+ "dist-compat",
37
41
  "README.md",
38
42
  "LICENSE"
39
43
  ],
44
+ "browser": {
45
+ "./dist/index.js": "./dist-compat/index.js",
46
+ "ws": false
47
+ },
40
48
  "exports": {
41
49
  ".": {
42
50
  "require": "./dist/index.js",