@ragestudio/scylla-odm 0.22.2 → 0.22.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.
Files changed (153) hide show
  1. package/batch/index.d.ts +3 -3
  2. package/batch/index.d.ts.map +1 -1
  3. package/client.d.ts +6 -5
  4. package/client.d.ts.map +1 -1
  5. package/client.js +7 -7
  6. package/client.js.map +1 -1
  7. package/cql_gen/create_table.d.ts +1 -1
  8. package/cql_gen/create_table.d.ts.map +1 -1
  9. package/document/index.d.ts +3 -3
  10. package/document/index.d.ts.map +1 -1
  11. package/driver/LICENSE.txt +177 -0
  12. package/driver/NOTICE.txt +67 -0
  13. package/driver/auth/index.d.ts +37 -0
  14. package/driver/auth/index.js +37 -0
  15. package/driver/auth/no-auth-provider.js +73 -0
  16. package/driver/auth/plain-text-auth-provider.js +81 -0
  17. package/driver/auth/provider.js +77 -0
  18. package/driver/client-options.js +442 -0
  19. package/driver/client.js +1267 -0
  20. package/driver/concurrent/index.d.ts +49 -0
  21. package/driver/concurrent/index.js +366 -0
  22. package/driver/connection.js +1034 -0
  23. package/driver/control-connection.js +1282 -0
  24. package/driver/encoder.js +2316 -0
  25. package/driver/errors.js +223 -0
  26. package/driver/execution-options.js +612 -0
  27. package/driver/execution-profile.js +274 -0
  28. package/driver/host-connection-pool.js +587 -0
  29. package/driver/host.js +699 -0
  30. package/driver/index.d.ts +387 -0
  31. package/driver/index.js +81 -0
  32. package/driver/mapping/cache.js +214 -0
  33. package/driver/mapping/doc-info-adapter.js +171 -0
  34. package/driver/mapping/index.d.ts +219 -0
  35. package/driver/mapping/index.js +57 -0
  36. package/driver/mapping/mapper.js +225 -0
  37. package/driver/mapping/mapping-handler.js +641 -0
  38. package/driver/mapping/model-batch-item.js +215 -0
  39. package/driver/mapping/model-batch-mapper.js +141 -0
  40. package/driver/mapping/model-mapper.js +315 -0
  41. package/driver/mapping/model-mapping-info.js +225 -0
  42. package/driver/mapping/object-selector.js +417 -0
  43. package/driver/mapping/q.js +156 -0
  44. package/driver/mapping/query-generator.js +556 -0
  45. package/driver/mapping/result-mapper.js +123 -0
  46. package/driver/mapping/result.js +139 -0
  47. package/driver/mapping/table-mappings.js +133 -0
  48. package/driver/mapping/tree.js +160 -0
  49. package/driver/metadata/aggregate.js +79 -0
  50. package/driver/metadata/client-state.js +119 -0
  51. package/driver/metadata/data-collection.js +182 -0
  52. package/driver/metadata/event-debouncer.js +174 -0
  53. package/driver/metadata/index.d.ts +276 -0
  54. package/driver/metadata/index.js +1156 -0
  55. package/driver/metadata/materialized-view.js +49 -0
  56. package/driver/metadata/schema-function.js +98 -0
  57. package/driver/metadata/schema-index.js +166 -0
  58. package/driver/metadata/schema-parser.js +1399 -0
  59. package/driver/metadata/table-metadata.js +77 -0
  60. package/driver/operation-state.js +206 -0
  61. package/driver/policies/address-resolution.js +145 -0
  62. package/driver/policies/index.d.ts +241 -0
  63. package/driver/policies/index.js +110 -0
  64. package/driver/policies/load-balancing.js +970 -0
  65. package/driver/policies/reconnection.js +166 -0
  66. package/driver/policies/retry.js +326 -0
  67. package/driver/policies/speculative-execution.js +150 -0
  68. package/driver/policies/timestamp-generation.js +176 -0
  69. package/driver/prepare-handler.js +347 -0
  70. package/driver/promise-utils.js +191 -0
  71. package/driver/readers.js +624 -0
  72. package/driver/request-execution.js +644 -0
  73. package/driver/request-handler.js +332 -0
  74. package/driver/requests.js +618 -0
  75. package/driver/stream-id-stack.js +209 -0
  76. package/driver/streams.js +745 -0
  77. package/driver/token.js +325 -0
  78. package/driver/tokenizer.js +631 -0
  79. package/driver/types/big-decimal.js +282 -0
  80. package/driver/types/duration.js +576 -0
  81. package/driver/types/index.d.ts +486 -0
  82. package/driver/types/index.js +733 -0
  83. package/driver/types/inet-address.js +262 -0
  84. package/driver/types/integer.js +818 -0
  85. package/driver/types/local-date.js +280 -0
  86. package/driver/types/local-time.js +299 -0
  87. package/driver/types/mutable-long.js +385 -0
  88. package/driver/types/protocol-version.js +391 -0
  89. package/driver/types/result-set.js +287 -0
  90. package/driver/types/result-stream.js +164 -0
  91. package/driver/types/row.js +85 -0
  92. package/driver/types/time-uuid.js +414 -0
  93. package/driver/types/tuple.js +103 -0
  94. package/driver/types/uuid.js +160 -0
  95. package/driver/types/vector.js +130 -0
  96. package/driver/types/version-number.js +153 -0
  97. package/driver/utils.js +1485 -0
  98. package/driver/writers.js +350 -0
  99. package/global.d.ts +1 -1
  100. package/global.d.ts.map +1 -1
  101. package/index.d.ts +6 -6
  102. package/index.d.ts.map +1 -1
  103. package/index.js +6 -6
  104. package/index.js.map +1 -1
  105. package/migrate/index.d.ts +1 -1
  106. package/migrate/index.d.ts.map +1 -1
  107. package/migrate/index.js +1 -1
  108. package/migrate/index.js.map +1 -1
  109. package/model/index.d.ts +6 -6
  110. package/model/index.d.ts.map +1 -1
  111. package/model/index.js +10 -10
  112. package/model/index.js.map +1 -1
  113. package/operations/countAll.d.ts +1 -1
  114. package/operations/countAll.d.ts.map +1 -1
  115. package/operations/delete.d.ts +3 -4
  116. package/operations/delete.d.ts.map +1 -1
  117. package/operations/delete.js +1 -1
  118. package/operations/delete.js.map +1 -1
  119. package/operations/find.d.ts +2 -2
  120. package/operations/find.d.ts.map +1 -1
  121. package/operations/find.js +1 -1
  122. package/operations/find.js.map +1 -1
  123. package/operations/findOne.d.ts +2 -2
  124. package/operations/findOne.d.ts.map +1 -1
  125. package/operations/findOne.js +1 -1
  126. package/operations/findOne.js.map +1 -1
  127. package/operations/insert.d.ts +3 -3
  128. package/operations/insert.d.ts.map +1 -1
  129. package/operations/insert.js +2 -2
  130. package/operations/insert.js.map +1 -1
  131. package/operations/sync.d.ts +1 -1
  132. package/operations/sync.d.ts.map +1 -1
  133. package/operations/sync.js +1 -1
  134. package/operations/sync.js.map +1 -1
  135. package/operations/tableExists.d.ts +1 -1
  136. package/operations/tableExists.d.ts.map +1 -1
  137. package/operations/update.d.ts +3 -3
  138. package/operations/update.d.ts.map +1 -1
  139. package/operations/update.js +2 -2
  140. package/operations/update.js.map +1 -1
  141. package/package.json +4 -12
  142. package/schema/index.d.ts +1 -1
  143. package/schema/index.d.ts.map +1 -1
  144. package/types.d.ts +4 -4
  145. package/types.d.ts.map +1 -1
  146. package/utils/queryParser.d.ts +1 -1
  147. package/utils/queryParser.d.ts.map +1 -1
  148. package/utils/queryParser.js +1 -1
  149. package/utils/queryParser.js.map +1 -1
  150. package/utils/typeChecker.d.ts +1 -1
  151. package/utils/typeChecker.d.ts.map +1 -1
  152. package/utils/typeChecker.js +1 -1
  153. package/utils/typeChecker.js.map +1 -1
package/driver/host.js ADDED
@@ -0,0 +1,699 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ import events from "events"
20
+
21
+ import utils from "./utils.js"
22
+ import types from "./types/index.js"
23
+ import HostConnectionPool from "./host-connection-pool.js"
24
+ import PrepareHandler from "./prepare-handler.js"
25
+ import promiseUtils from "./promise-utils.js"
26
+
27
+ const healthResponseCountInterval = 200
28
+
29
+ /**
30
+ * Represents a Cassandra node.
31
+ * @extends EventEmitter
32
+ */
33
+ class Host extends events.EventEmitter {
34
+ /**
35
+ * Creates a new Host instance.
36
+ */
37
+ constructor(address, protocolVersion, options, metadata) {
38
+ super()
39
+ /**
40
+ * Gets ip address and port number of the node separated by `:`.
41
+ * @type {String}
42
+ */
43
+ this.address = address
44
+ this.setDownAt = 0
45
+ this.log = utils.log
46
+
47
+ /**
48
+ * Gets the timestamp of the moment when the Host was marked as UP.
49
+ * @type {Number|null}
50
+ * @ignore
51
+ * @internal
52
+ */
53
+ this.isUpSince = null
54
+ Object.defineProperty(this, "options", {
55
+ value: options,
56
+ enumerable: false,
57
+ writable: false,
58
+ })
59
+
60
+ /**
61
+ * The host pool.
62
+ * @internal
63
+ * @ignore
64
+ * @type {HostConnectionPool}
65
+ */
66
+ Object.defineProperty(this, "pool", {
67
+ value: new HostConnectionPool(this, protocolVersion),
68
+ enumerable: false,
69
+ })
70
+
71
+ this.pool.on("open", (err) =>
72
+ promiseUtils.toBackground(this._onNewConnectionOpen(err)),
73
+ )
74
+ this.pool.on("remove", () => this._checkPoolState())
75
+
76
+ /**
77
+ * Gets string containing the Cassandra version.
78
+ * @type {String}
79
+ */
80
+ this.cassandraVersion = null
81
+
82
+ /**
83
+ * Gets data center name of the node.
84
+ * @type {String}
85
+ */
86
+ this.datacenter = null
87
+
88
+ /**
89
+ * Gets rack name of the node.
90
+ * @type {String}
91
+ */
92
+ this.rack = null
93
+
94
+ /**
95
+ * Gets the tokens assigned to the node.
96
+ * @type {Array}
97
+ */
98
+ this.tokens = null
99
+
100
+ /**
101
+ * Gets the id of the host.
102
+ * <p>This identifier is used by the server for internal communication / gossip.</p>
103
+ * @type {Uuid}
104
+ */
105
+ this.hostId = null
106
+
107
+ /**
108
+ * Gets string containing the DSE version or null if not set.
109
+ * @type {String}
110
+ */
111
+ this.dseVersion = null
112
+
113
+ /**
114
+ * Gets the DSE Workloads the host is running.
115
+ * <p>
116
+ * This is based on the "workload" or "workloads" columns in {@code system.local} and {@code system.peers}.
117
+ * <p/>
118
+ * <p>
119
+ * Workload labels may vary depending on the DSE version in use;e.g. DSE 5.1 may report two distinct workloads:
120
+ * <code>Search</code> and <code>Analytics</code>, while DSE 5.0 would report a single
121
+ * <code>SearchAnalytics</code> workload instead. The driver simply returns the workload labels as reported by
122
+ * DSE, without any form of pre-processing.
123
+ * <p/>
124
+ * <p>When the information is unavailable, this property returns an empty array.</p>
125
+ * @type {Array<string>}
126
+ */
127
+ this.workloads = utils.emptyArray
128
+
129
+ // the distance as last set using the load balancing policy
130
+ this._distance = types.distance.ignored
131
+ this._healthResponseCounter = 0
132
+
133
+ // Make some of the private instance variables not enumerable to prevent from showing when inspecting
134
+ Object.defineProperty(this, "_metadata", {
135
+ value: metadata,
136
+ enumerable: false,
137
+ })
138
+ Object.defineProperty(this, "_healthResponseCountTimer", {
139
+ value: null,
140
+ enumerable: false,
141
+ writable: true,
142
+ })
143
+
144
+ this.reconnectionSchedule =
145
+ this.options.policies.reconnection.newSchedule()
146
+ this.reconnectionDelay = 0
147
+ }
148
+
149
+ /**
150
+ * Marks this host as not available for query coordination, when the host was previously marked as UP, otherwise its
151
+ * a no-op.
152
+ * @internal
153
+ * @ignore
154
+ */
155
+ setDown() {
156
+ // Multiple events signaling that a host is failing could cause multiple calls to this method
157
+ if (this.setDownAt !== 0) {
158
+ // the host is already marked as Down
159
+ return
160
+ }
161
+ if (this.pool.isClosing()) {
162
+ // the pool is being closed/shutdown, don't mind
163
+ return
164
+ }
165
+ this.setDownAt = Date.now()
166
+ if (this.pool.coreConnectionsLength > 0) {
167
+ // According to the distance, there should be connections open to it => issue a warning
168
+ this.log(
169
+ "warning",
170
+ `Host ${this.address} considered as DOWN. Reconnection delay ${this.reconnectionDelay}ms.`,
171
+ )
172
+ } else {
173
+ this.log("info", `Host ${this.address} considered as DOWN.`)
174
+ }
175
+ this.emit("down")
176
+ this._checkPoolState()
177
+ }
178
+
179
+ /**
180
+ * Marks this host as available for querying.
181
+ * @param {Boolean} [clearReconnection]
182
+ * @internal
183
+ * @ignore
184
+ */
185
+ setUp(clearReconnection) {
186
+ if (!this.setDownAt) {
187
+ //The host is already marked as UP
188
+ return
189
+ }
190
+ this.log("info", `Setting host ${this.address} as UP`)
191
+ this.setDownAt = 0
192
+ this.isUpSince = Date.now()
193
+ //if it was unhealthy and now it is not, lets reset the reconnection schedule.
194
+ this.reconnectionSchedule =
195
+ this.options.policies.reconnection.newSchedule()
196
+ if (clearReconnection) {
197
+ this.pool.clearNewConnectionAttempt()
198
+ }
199
+ this.emit("up")
200
+ }
201
+
202
+ /**
203
+ * Resets the reconnectionSchedule and tries to issue a reconnection immediately.
204
+ * @internal
205
+ * @ignore
206
+ */
207
+ checkIsUp() {
208
+ if (this.isUp()) {
209
+ return
210
+ }
211
+ this.reconnectionSchedule =
212
+ this.options.policies.reconnection.newSchedule()
213
+ this.reconnectionDelay = 0
214
+ this.pool.attemptNewConnectionImmediate()
215
+ }
216
+
217
+ /**
218
+ * @param {Boolean} [waitForPending] When true, it waits for in-flight operations to be finish before closing the
219
+ * connections.
220
+ * @returns {Promise<void>}
221
+ * @internal
222
+ * @ignore
223
+ */
224
+ shutdown(waitForPending) {
225
+ if (this._healthResponseCountTimer) {
226
+ clearInterval(this._healthResponseCountTimer)
227
+ }
228
+ if (waitForPending) {
229
+ this.pool.drainAndShutdown()
230
+ // Gracefully draining and shutting down the pool is being done in the background
231
+ return Promise.resolve()
232
+ }
233
+ return this.pool.shutdown()
234
+ }
235
+
236
+ /**
237
+ * Determines if the node is UP now (seen as UP by the driver).
238
+ * @returns {boolean}
239
+ */
240
+ isUp() {
241
+ return !this.setDownAt
242
+ }
243
+
244
+ /**
245
+ * Determines if the host can be considered as UP.
246
+ * Deprecated: Use {@link Host#isUp()} instead.
247
+ * @returns {boolean}
248
+ */
249
+ canBeConsideredAsUp() {
250
+ const self = this
251
+ function hasTimePassed() {
252
+ return (
253
+ new Date().getTime() - self.setDownAt >= self.reconnectionDelay
254
+ )
255
+ }
256
+ return !this.setDownAt || hasTimePassed()
257
+ }
258
+
259
+ /**
260
+ * Sets the distance of the host relative to the client using the load balancing policy.
261
+ * @param {Number} distance
262
+ * @internal
263
+ * @ignore
264
+ */
265
+ setDistance(distance) {
266
+ const previousDistance = this._distance
267
+ this._distance = distance || types.distance.local
268
+ if (this.options.pooling.coreConnectionsPerHost) {
269
+ this.pool.coreConnectionsLength =
270
+ this.options.pooling.coreConnectionsPerHost[this._distance] || 0
271
+ } else {
272
+ this.pool.coreConnectionsLength = 1
273
+ }
274
+ if (this._distance === previousDistance) {
275
+ return this._distance
276
+ }
277
+ if (this._healthResponseCountTimer) {
278
+ clearInterval(this._healthResponseCountTimer)
279
+ }
280
+ if (this._distance === types.distance.ignored) {
281
+ // this host was local/remote and now must be ignored
282
+ this.emit("ignore")
283
+ this.pool.drainAndShutdown()
284
+ } else {
285
+ if (!this.isUp()) {
286
+ this.checkIsUp()
287
+ }
288
+ // Reset the health check timer
289
+ this._healthResponseCountTimer = setInterval(() => {
290
+ this._healthResponseCounter =
291
+ this.pool.getAndResetResponseCounter()
292
+ }, healthResponseCountInterval)
293
+ }
294
+ return this._distance
295
+ }
296
+
297
+ /**
298
+ * Changes the protocol version of a given host
299
+ * @param {Number} value
300
+ * @internal
301
+ * @ignore
302
+ */
303
+ setProtocolVersion(value) {
304
+ this.pool.protocolVersion = value
305
+ }
306
+
307
+ /**
308
+ * Gets the least busy connection from the pool.
309
+ * @param {Connection} [previousConnection] When provided, the pool should attempt to obtain a different connection.
310
+ * @returns {Connection!}
311
+ * @throws {Error}
312
+ * @throws {BusyConnectionError}
313
+ * @internal
314
+ * @ignore
315
+ */
316
+ borrowConnection(previousConnection) {
317
+ return this.pool.borrowConnection(previousConnection)
318
+ }
319
+
320
+ /**
321
+ * Creates all the connection in the pool.
322
+ * @param {string} keyspace
323
+ * @internal
324
+ * @ignore
325
+ */
326
+ warmupPool(keyspace) {
327
+ return this.pool.warmup(keyspace)
328
+ }
329
+
330
+ /**
331
+ * Starts creating the pool in the background.
332
+ * @internal
333
+ * @ignore
334
+ */
335
+ initializePool() {
336
+ this.pool.increaseSize()
337
+ }
338
+ /**
339
+ * Gets any connection that is already opened or null if not found.
340
+ * @returns {Connection}
341
+ * @internal
342
+ * @ignore
343
+ */
344
+ getActiveConnection() {
345
+ if (!this.isUp() || !this.pool.connections.length) {
346
+ return null
347
+ }
348
+ return this.pool.connections[0]
349
+ }
350
+
351
+ /**
352
+ * Internal method to get the amount of responses dequeued in the last interval (between 200ms and 400ms) on all
353
+ * connections to the host.
354
+ * @returns {Number}
355
+ * @internal
356
+ * @ignore
357
+ */
358
+ getResponseCount() {
359
+ // Last interval plus the current count
360
+ return this._healthResponseCounter + this.pool.responseCounter
361
+ }
362
+
363
+ /**
364
+ * Checks the health of a connection in the pool
365
+ * @param {Connection} connection
366
+ * @internal
367
+ * @ignore
368
+ */
369
+ checkHealth(connection) {
370
+ if (
371
+ connection.timedOutOperations <=
372
+ this.options.socketOptions.defunctReadTimeoutThreshold
373
+ ) {
374
+ return
375
+ }
376
+ this.removeFromPool(connection)
377
+ }
378
+
379
+ /**
380
+ * @param {Connection} connection
381
+ * @internal
382
+ * @ignore
383
+ */
384
+ removeFromPool(connection) {
385
+ this.pool.remove(connection)
386
+ this._checkPoolState()
387
+ }
388
+
389
+ /**
390
+ * Internal method that gets the amount of in-flight requests on all connections to the host.
391
+ * @internal
392
+ * @ignore
393
+ */
394
+ getInFlight() {
395
+ return this.pool.getInFlight()
396
+ }
397
+
398
+ /**
399
+ * Validates that the internal state of the connection pool.
400
+ * If the pool size is smaller than expected, schedule a new connection attempt.
401
+ * If the amount of connections is 0 for not ignored hosts, the host must be down.
402
+ * @private
403
+ */
404
+ _checkPoolState() {
405
+ if (this.pool.isClosing()) {
406
+ return
407
+ }
408
+ if (this.pool.connections.length < this.pool.coreConnectionsLength) {
409
+ // the pool needs to grow / reconnect
410
+ if (!this.pool.hasScheduledNewConnection()) {
411
+ this.reconnectionDelay = this.reconnectionSchedule.next().value
412
+ this.pool.scheduleNewConnectionAttempt(this.reconnectionDelay)
413
+ }
414
+ }
415
+ const shouldHaveConnections =
416
+ this._distance !== types.distance.ignored &&
417
+ this.pool.coreConnectionsLength > 0
418
+ if (shouldHaveConnections && this.pool.connections.length === 0) {
419
+ // Mark as DOWN, if its UP
420
+ this.setDown()
421
+ }
422
+ }
423
+
424
+ /**
425
+ * Executed after an scheduled new connection attempt finished
426
+ * @private
427
+ */
428
+ async _onNewConnectionOpen(err) {
429
+ if (err) {
430
+ this._checkPoolState()
431
+ return
432
+ }
433
+ if (!this.isUp() && this.options.rePrepareOnUp) {
434
+ this.log(
435
+ "info",
436
+ `Re-preparing all queries on host ${this.address} before setting it as UP`,
437
+ )
438
+ const allPrepared = this._metadata.getAllPrepared()
439
+ try {
440
+ await PrepareHandler.prepareAllQueries(this, allPrepared)
441
+ } catch (err) {
442
+ this.log(
443
+ "warning",
444
+ `Failed re-preparing on host ${this.address}: ${err}`,
445
+ err,
446
+ )
447
+ }
448
+ }
449
+ this.setUp()
450
+ this.pool.increaseSize()
451
+ }
452
+
453
+ /**
454
+ * Returns an array containing the Cassandra Version as an Array of Numbers having the major version in the first
455
+ * position.
456
+ * @returns {Array.<Number>}
457
+ */
458
+ getCassandraVersion() {
459
+ if (!this.cassandraVersion) {
460
+ return utils.emptyArray
461
+ }
462
+ return this.cassandraVersion
463
+ .split("-")[0]
464
+ .split(".")
465
+ .map((x) => parseInt(x, 10))
466
+ }
467
+
468
+ /**
469
+ * Gets the DSE version of the host as an Array, containing the major version in the first position.
470
+ * In case the cluster is not a DSE cluster, it returns an empty Array.
471
+ * @returns {Array}
472
+ */
473
+ getDseVersion() {
474
+ if (!this.dseVersion) {
475
+ return utils.emptyArray
476
+ }
477
+ return this.dseVersion
478
+ .split("-")[0]
479
+ .split(".")
480
+ .map((x) => parseInt(x, 10))
481
+ }
482
+ }
483
+
484
+ /**
485
+ * Represents an associative-array of {@link Host hosts} that can be iterated.
486
+ * It creates an internal copy when adding or removing, making it safe to iterate using the values()
487
+ * method within async operations.
488
+ * @extends events.EventEmitter
489
+ * @constructor
490
+ */
491
+ class HostMap extends events.EventEmitter {
492
+ constructor() {
493
+ super()
494
+
495
+ this._items = new Map()
496
+ this._values = null
497
+
498
+ Object.defineProperty(this, "length", {
499
+ get: () => this.values().length,
500
+ enumerable: true,
501
+ })
502
+
503
+ /**
504
+ * Emitted when a host is added to the map
505
+ * @event HostMap#add
506
+ */
507
+ /**
508
+ * Emitted when a host is removed from the map
509
+ * @event HostMap#remove
510
+ */
511
+ }
512
+
513
+ /**
514
+ * Executes a provided function once per map element.
515
+ * @param callback
516
+ */
517
+ forEach(callback) {
518
+ const items = this._items
519
+ for (const [key, value] of items) {
520
+ callback(value, key)
521
+ }
522
+ }
523
+
524
+ /**
525
+ * Gets a {@link Host host} by key or undefined if not found.
526
+ * @param {String} key
527
+ * @returns {Host}
528
+ */
529
+ get(key) {
530
+ return this._items.get(key)
531
+ }
532
+
533
+ /**
534
+ * Returns an array of host addresses.
535
+ * @returns {Array.<String>}
536
+ */
537
+ keys() {
538
+ return Array.from(this._items.keys())
539
+ }
540
+
541
+ /**
542
+ * Removes an item from the map.
543
+ * @param {String} key The key of the host
544
+ * @fires HostMap#remove
545
+ */
546
+ remove(key) {
547
+ const value = this._items.get(key)
548
+ if (value === undefined) {
549
+ return
550
+ }
551
+
552
+ // Clear cache
553
+ this._values = null
554
+
555
+ // Copy the values
556
+ const copy = new Map(this._items)
557
+ copy.delete(key)
558
+
559
+ this._items = copy
560
+ this.emit("remove", value)
561
+ }
562
+
563
+ /**
564
+ * Removes multiple hosts from the map.
565
+ * @param {Array.<String>} keys
566
+ * @fires HostMap#remove
567
+ */
568
+ removeMultiple(keys) {
569
+ // Clear value cache
570
+ this._values = null
571
+
572
+ // Copy the values
573
+ const copy = new Map(this._items)
574
+ const removedHosts = []
575
+
576
+ for (const key of keys) {
577
+ const h = copy.get(key)
578
+
579
+ if (!h) {
580
+ continue
581
+ }
582
+
583
+ removedHosts.push(h)
584
+ copy.delete(key)
585
+ }
586
+
587
+ this._items = copy
588
+ removedHosts.forEach((h) => this.emit("remove", h))
589
+ }
590
+
591
+ /**
592
+ * Adds a new item to the map.
593
+ * @param {String} key The key of the host
594
+ * @param {Host} value The host to be added
595
+ * @fires HostMap#remove
596
+ * @fires HostMap#add
597
+ */
598
+ set(key, value) {
599
+ // Clear values cache
600
+ this._values = null
601
+
602
+ const originalValue = this._items.get(key)
603
+ if (originalValue) {
604
+ //The internal structure does not change
605
+ this._items.set(key, value)
606
+ //emit a remove followed by a add
607
+ this.emit("remove", originalValue)
608
+ this.emit("add", value)
609
+ return
610
+ }
611
+
612
+ // Copy the values
613
+ const copy = new Map(this._items)
614
+ copy.set(key, value)
615
+ this._items = copy
616
+ this.emit("add", value)
617
+ return value
618
+ }
619
+
620
+ /**
621
+ * Returns a shallow copy of a portion of the items into a new array object.
622
+ * Backward-compatibility.
623
+ * @param {Number} [begin]
624
+ * @param {Number} [end]
625
+ * @returns {Array}
626
+ * @ignore
627
+ */
628
+ slice(begin, end) {
629
+ if (!begin && !end) {
630
+ // Avoid making a copy of the copy
631
+ return this.values()
632
+ }
633
+
634
+ return this.values().slice(begin || 0, end)
635
+ }
636
+
637
+ /**
638
+ * Deprecated: Use set() instead.
639
+ * @ignore
640
+ * @deprecated
641
+ */
642
+ push(k, v) {
643
+ this.set(k, v)
644
+ }
645
+
646
+ /**
647
+ * Returns a shallow copy of the values of the map.
648
+ * @returns {Array.<Host>}
649
+ */
650
+ values() {
651
+ if (!this._values) {
652
+ // Cache the values
653
+ this._values = Object.freeze(Array.from(this._items.values()))
654
+ }
655
+
656
+ return this._values
657
+ }
658
+
659
+ /**
660
+ * Removes all items from the map.
661
+ * @returns {Array.<Host>} The previous items
662
+ */
663
+ clear() {
664
+ const previousItems = this.values()
665
+
666
+ // Clear cache
667
+ this._values = null
668
+
669
+ // Clear items
670
+ this._items = new Map()
671
+
672
+ // Emit events
673
+ previousItems.forEach((h) => this.emit("remove", h))
674
+
675
+ return previousItems
676
+ }
677
+
678
+ inspect() {
679
+ return this._items
680
+ }
681
+
682
+ toJSON() {
683
+ // Node 10 and below don't support Object.fromEntries()
684
+ if (Object.fromEntries) {
685
+ return Object.fromEntries(this._items)
686
+ }
687
+
688
+ const obj = {}
689
+ for (const [key, value] of this._items) {
690
+ obj[key] = value
691
+ }
692
+
693
+ return obj
694
+ }
695
+ }
696
+
697
+ export { Host, HostMap }
698
+
699
+ export default { Host, HostMap }