@flowcore/data-pump 0.21.3 → 0.21.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.21.4](https://github.com/flowcore-io/data-pump/compare/v0.21.3...v0.21.4) (2026-06-09)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **notifier:** arm timeout/abort before connect() to prevent hung-reconnect wedge ([#78](https://github.com/flowcore-io/data-pump/issues/78)) ([0fe4bbb](https://github.com/flowcore-io/data-pump/commit/0fe4bbbbda38ff58804403765832f41d2950a6df))
9
+
3
10
  ## [0.21.3](https://github.com/flowcore-io/data-pump/compare/v0.21.2...v0.21.3) (2026-06-02)
4
11
 
5
12
 
@@ -1 +1 @@
1
- {"version":3,"file":"notifier.d.ts","sourceRoot":"","sources":["../../src/data-pump/notifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA0B,MAAM,+CAA+C,CAAA;AAG1G,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAKlG,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,0BAA0B,CAAA;IACtC,IAAI,EAAE,oBAAoB,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,qBAAa,gBAAgB;IASf,OAAO,CAAC,QAAQ,CAAC,OAAO;IARpC,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,WAAW,CAAC,CAAuB;IAC3C,OAAO,CAAC,OAAO,CAAC,CAA4B;IAC5C,OAAO,CAAC,kBAAkB,CAAC,CAAoB;IAC/C,OAAO,CAAC,aAAa,CAAC,CAAY;IAElC,OAAO,CAAC,KAAK,CAAC,CAAK;gBAEU,OAAO,EAAE,uBAAuB;IAetD,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW;YASlB,UAAU;YAUV,QAAQ;IAoCtB,OAAO,CAAC,gBAAgB;YAMV,aAAa;IA2C3B,OAAO,CAAC,aAAa;CAiBtB;AAqBD;;;;;GAKG;AAEH,KAAK,yBAAyB,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,kBAAkB,CAAA;AACvE,eAAO,MAAM,UAAU,EAAE;IAAE,wBAAwB,EAAE,yBAAyB,CAAA;CAG7E,CAAA"}
1
+ {"version":3,"file":"notifier.d.ts","sourceRoot":"","sources":["../../src/data-pump/notifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA0B,MAAM,+CAA+C,CAAA;AAG1G,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAKlG,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,0BAA0B,CAAA;IACtC,IAAI,EAAE,oBAAoB,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,qBAAa,gBAAgB;IASf,OAAO,CAAC,QAAQ,CAAC,OAAO;IARpC,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,WAAW,CAAC,CAAuB;IAC3C,OAAO,CAAC,OAAO,CAAC,CAA4B;IAC5C,OAAO,CAAC,kBAAkB,CAAC,CAAoB;IAC/C,OAAO,CAAC,aAAa,CAAC,CAAY;IAElC,OAAO,CAAC,KAAK,CAAC,CAAK;gBAEU,OAAO,EAAE,uBAAuB;IAetD,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW;YASlB,UAAU;YAUV,QAAQ;IAoCtB,OAAO,CAAC,gBAAgB;YAMV,aAAa;IAyD3B,OAAO,CAAC,aAAa;CAiBtB;AAqBD;;;;;GAKG;AAEH,KAAK,yBAAyB,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,kBAAkB,CAAA;AACvE,eAAO,MAAM,UAAU,EAAE;IAAE,wBAAwB,EAAE,yBAAyB,CAAA;CAG7E,CAAA"}
@@ -124,6 +124,15 @@ export class FlowcoreNotifier {
124
124
  this.eventResolver?.();
125
125
  },
126
126
  });
127
+ // Arm the timeout + abort listener BEFORE connect(). A reconnect whose
128
+ // connect() never resolves (half-open / black-holed socket) must not wedge
129
+ // the pump: the timer then resolves the wait at timeoutMs and the fetch loop
130
+ // re-polls (catching up any backlog) and attempts a fresh connect next cycle.
131
+ // The moment a connect() succeeds, that cycle waits on real WS events again —
132
+ // there is no sticky poll-mode state, so recovery to push mode is automatic.
133
+ clearTimeout(this.timer);
134
+ this.timer = setTimeout(() => this.eventResolver?.(), this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
135
+ signal?.addEventListener("abort", () => this.eventResolver?.());
127
136
  this.notificationClient = _internals.createNotificationClient(this.subject, this.webSocketAuth(), {
128
137
  tenant: this.dataSource.tenant,
129
138
  dataCore: this.dataSource.dataCore,
@@ -132,14 +141,18 @@ export class FlowcoreNotifier {
132
141
  logger: createNotificationClientLogger(this.options.logger ?? noOpLogger),
133
142
  reconnectInterval: 1000,
134
143
  });
144
+ const connectPromise = this.notificationClient.connect();
135
145
  try {
136
- await this.notificationClient.connect();
137
- clearTimeout(this.timer);
138
- this.timer = setTimeout(() => this.eventResolver?.(), this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
139
- signal?.addEventListener("abort", () => this.eventResolver?.());
146
+ // Race connect() against the resolution promise so a hung connect can't
147
+ // block. A connect REJECTION still surfaces (preserving the main-loop
148
+ // self-heal that restarts on a thrown error).
149
+ await Promise.race([connectPromise, promise]);
140
150
  await promise;
141
151
  }
142
152
  finally {
153
+ // Swallow a late connect() rejection that may arrive after we already
154
+ // moved on via the timeout, so it never surfaces as an unhandledRejection.
155
+ connectPromise.catch(() => { });
143
156
  clearTimeout(this.timer);
144
157
  this.eventResolver = undefined;
145
158
  this.notificationClient.disconnect();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowcore/data-pump",
3
- "version": "0.21.3",
3
+ "version": "0.21.4",
4
4
  "description": "Flowcore Data Pump",
5
5
  "homepage": "https://github.com/flowcore-io/flowcore-data-pump#readme",
6
6
  "repository": {
@@ -1 +1 @@
1
- {"version":3,"file":"notifier.d.ts","sourceRoot":"","sources":["../../src/data-pump/notifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA0B,MAAM,+CAA+C,CAAA;AAG1G,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAKlG,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,0BAA0B,CAAA;IACtC,IAAI,EAAE,oBAAoB,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,qBAAa,gBAAgB;IASf,OAAO,CAAC,QAAQ,CAAC,OAAO;IARpC,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,WAAW,CAAC,CAAuB;IAC3C,OAAO,CAAC,OAAO,CAAC,CAA4B;IAC5C,OAAO,CAAC,kBAAkB,CAAC,CAAoB;IAC/C,OAAO,CAAC,aAAa,CAAC,CAAY;IAElC,OAAO,CAAC,KAAK,CAAC,CAAK;gBAEU,OAAO,EAAE,uBAAuB;IAetD,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW;YASlB,UAAU;YAUV,QAAQ;IAoCtB,OAAO,CAAC,gBAAgB;YAMV,aAAa;IA2C3B,OAAO,CAAC,aAAa;CAiBtB;AAqBD;;;;;GAKG;AAEH,KAAK,yBAAyB,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,kBAAkB,CAAA;AACvE,eAAO,MAAM,UAAU,EAAE;IAAE,wBAAwB,EAAE,yBAAyB,CAAA;CAG7E,CAAA"}
1
+ {"version":3,"file":"notifier.d.ts","sourceRoot":"","sources":["../../src/data-pump/notifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA0B,MAAM,+CAA+C,CAAA;AAG1G,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAKlG,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,0BAA0B,CAAA;IACtC,IAAI,EAAE,oBAAoB,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,cAAc,CAAA;IACvB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,qBAAa,gBAAgB;IASf,OAAO,CAAC,QAAQ,CAAC,OAAO;IARpC,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,WAAW,CAAC,CAAuB;IAC3C,OAAO,CAAC,OAAO,CAAC,CAA4B;IAC5C,OAAO,CAAC,kBAAkB,CAAC,CAAoB;IAC/C,OAAO,CAAC,aAAa,CAAC,CAAY;IAElC,OAAO,CAAC,KAAK,CAAC,CAAK;gBAEU,OAAO,EAAE,uBAAuB;IAetD,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW;YASlB,UAAU;YAUV,QAAQ;IAoCtB,OAAO,CAAC,gBAAgB;YAMV,aAAa;IAyD3B,OAAO,CAAC,aAAa;CAiBtB;AAqBD;;;;;GAKG;AAEH,KAAK,yBAAyB,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,kBAAkB,CAAA;AACvE,eAAO,MAAM,UAAU,EAAE;IAAE,wBAAwB,EAAE,yBAAyB,CAAA;CAG7E,CAAA"}
@@ -127,6 +127,15 @@ class FlowcoreNotifier {
127
127
  this.eventResolver?.();
128
128
  },
129
129
  });
130
+ // Arm the timeout + abort listener BEFORE connect(). A reconnect whose
131
+ // connect() never resolves (half-open / black-holed socket) must not wedge
132
+ // the pump: the timer then resolves the wait at timeoutMs and the fetch loop
133
+ // re-polls (catching up any backlog) and attempts a fresh connect next cycle.
134
+ // The moment a connect() succeeds, that cycle waits on real WS events again —
135
+ // there is no sticky poll-mode state, so recovery to push mode is automatic.
136
+ clearTimeout(this.timer);
137
+ this.timer = setTimeout(() => this.eventResolver?.(), this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
138
+ signal?.addEventListener("abort", () => this.eventResolver?.());
130
139
  this.notificationClient = exports._internals.createNotificationClient(this.subject, this.webSocketAuth(), {
131
140
  tenant: this.dataSource.tenant,
132
141
  dataCore: this.dataSource.dataCore,
@@ -135,14 +144,18 @@ class FlowcoreNotifier {
135
144
  logger: createNotificationClientLogger(this.options.logger ?? no_op_logger_js_1.noOpLogger),
136
145
  reconnectInterval: 1000,
137
146
  });
147
+ const connectPromise = this.notificationClient.connect();
138
148
  try {
139
- await this.notificationClient.connect();
140
- clearTimeout(this.timer);
141
- this.timer = setTimeout(() => this.eventResolver?.(), this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
142
- signal?.addEventListener("abort", () => this.eventResolver?.());
149
+ // Race connect() against the resolution promise so a hung connect can't
150
+ // block. A connect REJECTION still surfaces (preserving the main-loop
151
+ // self-heal that restarts on a thrown error).
152
+ await Promise.race([connectPromise, promise]);
143
153
  await promise;
144
154
  }
145
155
  finally {
156
+ // Swallow a late connect() rejection that may arrive after we already
157
+ // moved on via the timeout, so it never surfaces as an unhandledRejection.
158
+ connectPromise.catch(() => { });
146
159
  clearTimeout(this.timer);
147
160
  this.eventResolver = undefined;
148
161
  this.notificationClient.disconnect();