hcloud-sdk 22.44.0 → 22.45.0

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,6 @@
1
- ## [22.44.0](https://github.com/moovit-sp-gmbh/hcloud-sdk-js/compare/v22.43.0...v22.44.0) (2026-10-02)
1
+ ## [22.45.0](https://github.com/moovit-sp-gmbh/hcloud-sdk-js/compare/v22.44.0...v22.45.0) (2026-10-02)
2
2
 
3
3
  ### Features
4
4
 
5
- * add SSO provider retrieval for domains ([e1cc605](https://github.com/moovit-sp-gmbh/hcloud-sdk-js/commit/e1cc605f865f7ce53898ca5eea915c5b8a9b70cd))
5
+ * add HCloud.clone() to adjust a shared instance for a single call ([c9c49d5](https://github.com/moovit-sp-gmbh/hcloud-sdk-js/commit/c9c49d5ef68f777a747942c15f4039feec27ed26))
6
+ * back off NATS reconnects on authorization errors and allow aborting them ([1680eb7](https://github.com/moovit-sp-gmbh/hcloud-sdk-js/commit/1680eb756f569cf7dc5ce040e46ee44240c308a1))
package/lib/Hcloud.d.ts CHANGED
@@ -46,6 +46,14 @@ export declare class HCloud {
46
46
  private refreshToken;
47
47
  private refreshPromise;
48
48
  constructor(options: Options);
49
+ /**
50
+ * Returns an independent copy of this instance that shares its NATS connection.
51
+ * Options (e.g. server) and default request headers (e.g. auth token, correlationID) are copied, so calling
52
+ * setServer, setAuthToken, setCorrelationId, ... on the copy never affects this instance and vice versa.
53
+ * Use it whenever a long-lived, shared instance has to be adjusted for a single call.
54
+ * A refresh token or a custom axios instance set via setAxios is not carried over.
55
+ */
56
+ clone(): HCloud;
49
57
  /**
50
58
  * Sets the server url that the SDK shall do a request to.
51
59
  */
package/lib/Hcloud.js CHANGED
@@ -164,6 +164,19 @@ class HCloud {
164
164
  return Promise.reject(error);
165
165
  }));
166
166
  }
167
+ /**
168
+ * Returns an independent copy of this instance that shares its NATS connection.
169
+ * Options (e.g. server) and default request headers (e.g. auth token, correlationID) are copied, so calling
170
+ * setServer, setAuthToken, setCorrelationId, ... on the copy never affects this instance and vice versa.
171
+ * Use it whenever a long-lived, shared instance has to be adjusted for a single call.
172
+ * A refresh token or a custom axios instance set via setAxios is not carried over.
173
+ */
174
+ clone() {
175
+ const copy = new HCloud(Object.assign({}, this.options));
176
+ copy.axios.defaults.headers.common = Object.assign({}, this.axios.defaults.headers.common);
177
+ copy._Nats = this.Nats;
178
+ return copy;
179
+ }
167
180
  /**
168
181
  * Sets the server url that the SDK shall do a request to.
169
182
  */
@@ -1,6 +1,15 @@
1
1
  import { Msg, NatsConnection, PublishOptions, RequestOptions, Subscription, SubscriptionOptions } from "nats";
2
2
  import Base from "../../Base";
3
3
  import { NatsCallback, NatsMessageType, NatsObject, NatsObjectType, RawMsg } from "../../interfaces/nats";
4
+ /**
5
+ * Called on every consecutive 'Authorization Violation' the NATS server answers a (re)connect with.
6
+ * Return false to close the connection and stop reconnecting (e.g. once the token turned out to be invalid),
7
+ * true to keep reconnecting with an increasing delay.
8
+ *
9
+ * Note: the server reports a failing or unreachable auth service with the same error, so only give up once
10
+ * the credentials are known to be invalid, e.g. because an HTTP request with the same token is rejected with 401.
11
+ */
12
+ export type NatsAuthErrorHandler = (consecutiveAuthErrors: number) => boolean | Promise<boolean>;
4
13
  /**
5
14
  * Connect and authenticate using email and jwt
6
15
  *
@@ -16,6 +25,7 @@ type ConnectParamsJwt = {
16
25
  servers?: string[];
17
26
  debug?: boolean;
18
27
  forceWebsocket?: boolean;
28
+ onAuthError?: NatsAuthErrorHandler;
19
29
  };
20
30
  /**
21
31
  * Connect and authenticate using username and password
@@ -33,12 +43,23 @@ type ConnectParamsPassword = {
33
43
  servers?: string[];
34
44
  debug?: boolean;
35
45
  forceWebsocket?: boolean;
46
+ onAuthError?: NatsAuthErrorHandler;
36
47
  };
48
+ /**
49
+ * Delay before the next reconnect attempt: the usual 2s, doubled for every consecutive authorization error
50
+ * (4s, 8s, ... up to 5min), so clients with rejected credentials don't hammer the server (and its auth callout).
51
+ */
52
+ export declare function reconnectDelay(consecutiveAuthErrors: number): number;
37
53
  declare class Nats extends Base {
38
54
  private natsConnection;
39
55
  private subMap;
40
56
  private connection;
41
57
  connect(params: ConnectParamsJwt | ConnectParamsPassword): Promise<NatsConnection>;
58
+ /**
59
+ * Counts consecutive authorization errors of a connection (reset by a successful reconnect) and closes it
60
+ * once onAuthError asks to stop.
61
+ */
62
+ private watchAuthErrors;
42
63
  getConnection: () => NatsConnection | undefined;
43
64
  sub(subject: string, callback: NatsCallback, options?: SubscriptionOptions): Promise<Subscription>;
44
65
  unsub(subject: string | Subscription): void;
@@ -8,14 +8,33 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
12
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
13
+ var m = o[Symbol.asyncIterator], i;
14
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
15
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
16
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
17
+ };
11
18
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
19
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
20
  };
14
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.reconnectDelay = reconnectDelay;
15
23
  const nats_1 = require("nats");
16
24
  const nats_ws_1 = require("nats.ws");
17
25
  const Base_1 = __importDefault(require("../../Base"));
18
26
  const isBrowser = typeof window !== "undefined";
27
+ const RECONNECT_TIME_WAIT_MS = 2 * 1000;
28
+ const RECONNECT_JITTER_MS = 1000;
29
+ const AUTH_ERROR_RECONNECT_MAX_WAIT_MS = 5 * 60 * 1000;
30
+ /**
31
+ * Delay before the next reconnect attempt: the usual 2s, doubled for every consecutive authorization error
32
+ * (4s, 8s, ... up to 5min), so clients with rejected credentials don't hammer the server (and its auth callout).
33
+ */
34
+ function reconnectDelay(consecutiveAuthErrors) {
35
+ const wait = Math.min(RECONNECT_TIME_WAIT_MS * Math.pow(2, consecutiveAuthErrors), AUTH_ERROR_RECONNECT_MAX_WAIT_MS);
36
+ return wait + Math.floor(Math.random() * RECONNECT_JITTER_MS);
37
+ }
19
38
  class Nats extends Base_1.default {
20
39
  constructor() {
21
40
  super(...arguments);
@@ -47,16 +66,21 @@ class Nats extends Base_1.default {
47
66
  params.servers = ["hcloud-nats-0.hcloud-nats:4222", "hcloud-nats-1.hcloud-nats:4222", "hcloud-nats-2.hcloud-nats:4222"];
48
67
  }
49
68
  }
69
+ // Authorization errors are not fatal on their own (the server also reports a failing auth service that way),
70
+ // so keep reconnecting, but back off and let onAuthError decide when to give up.
71
+ const authErrors = { consecutive: 0 };
50
72
  this.natsConnection = yield this.connection({
51
73
  name: params.name,
52
74
  debug: params.debug,
53
75
  maxReconnectAttempts: -1,
54
76
  ignoreAuthErrorAbort: true,
77
+ reconnectDelayHandler: () => reconnectDelay(authErrors.consecutive),
55
78
  servers: params.servers,
56
79
  pingInterval: 55 * 1000, //ping every 55 seconds
57
80
  user: params.email !== undefined ? params.email : params.username,
58
81
  pass: params.jwt !== undefined ? params.jwt : params.password,
59
82
  });
83
+ void this.watchAuthErrors(this.natsConnection, authErrors, params.onAuthError);
60
84
  for (const entry of previousSubs) {
61
85
  yield this.sub(entry.subject, (err, data, msg) => {
62
86
  var _a, _b;
@@ -70,6 +94,47 @@ class Nats extends Base_1.default {
70
94
  return this.natsConnection;
71
95
  });
72
96
  }
97
+ /**
98
+ * Counts consecutive authorization errors of a connection (reset by a successful reconnect) and closes it
99
+ * once onAuthError asks to stop.
100
+ */
101
+ watchAuthErrors(connection, authErrors, onAuthError) {
102
+ return __awaiter(this, void 0, void 0, function* () {
103
+ var _a, e_1, _b, _c;
104
+ var _d, _e, _f;
105
+ try {
106
+ try {
107
+ for (var _g = true, _h = __asyncValues(connection.status()), _j; _j = yield _h.next(), _a = _j.done, !_a; _g = true) {
108
+ _c = _j.value;
109
+ _g = false;
110
+ const status = _c;
111
+ if (status.type === nats_1.Events.Reconnect) {
112
+ authErrors.consecutive = 0;
113
+ }
114
+ else if (status.type === nats_1.Events.Error && status.data === nats_1.ErrorCode.AuthorizationViolation) {
115
+ authErrors.consecutive++;
116
+ (_d = this.logger) === null || _d === void 0 ? void 0 : _d.warn(`NATS authorization failed (${authErrors.consecutive} in a row), retrying in about ${reconnectDelay(authErrors.consecutive) / 1000}s`);
117
+ if (onAuthError && !(yield onAuthError(authErrors.consecutive))) {
118
+ (_e = this.logger) === null || _e === void 0 ? void 0 : _e.error("NATS authorization failed permanently, closing the connection");
119
+ yield connection.close();
120
+ return;
121
+ }
122
+ }
123
+ }
124
+ }
125
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
126
+ finally {
127
+ try {
128
+ if (!_g && !_a && (_b = _h.return)) yield _b.call(_h);
129
+ }
130
+ finally { if (e_1) throw e_1.error; }
131
+ }
132
+ }
133
+ catch (err) {
134
+ (_f = this.logger) === null || _f === void 0 ? void 0 : _f.error(`Unable to watch NATS connection status: ${String(err)}`);
135
+ }
136
+ });
137
+ }
73
138
  sub(subject, callback, options) {
74
139
  return __awaiter(this, void 0, void 0, function* () {
75
140
  const delay = 500;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hcloud-sdk",
3
- "version": "22.44.0",
3
+ "version": "22.45.0",
4
4
  "description": "hcloud platform sdk",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",