@autofleet/rabbit 3.2.19-beta.5 → 3.2.19-beta.7

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/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import { EventEmitter } from 'events';
3
3
  import { AmqpConnectionManager, ChannelWrapper, CreateChannelOpts } from 'amqp-connection-manager';
4
4
  import { ConfirmChannel, ConsumeMessage, Options, Replies } from 'amqplib';
5
5
  import { RedisConfig } from './lib/redis';
6
- import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary } from './lib/types';
6
+ import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, ConnectionRole } from './lib/types';
7
7
  export interface IAfRabbitMq {
8
8
  ack: any;
9
9
  nack: any;
@@ -37,10 +37,12 @@ type newChannelOpts = {
37
37
  name?: string;
38
38
  onClose?: null | ((args: any | null) => void);
39
39
  options?: CreateChannelOpts | undefined;
40
+ connectionRole: ConnectionRole;
40
41
  };
41
42
  type assertChannelOpts = {
42
43
  channelName?: string;
43
44
  force?: boolean;
45
+ connectionRole: ConnectionRole;
44
46
  };
45
47
  declare class RabbitMq implements IAfRabbitMq {
46
48
  static parseMsg(msg: any): any;
@@ -60,9 +62,11 @@ declare class RabbitMq implements IAfRabbitMq {
60
62
  channel: ChannelWrapper | null;
61
63
  publishChannelSetupPromise: Promise<ChannelWrapper> | null;
62
64
  blockReconnect: boolean | null | undefined;
63
- connection: AmqpConnectionManager | null | undefined;
65
+ consumeConnection: AmqpConnectionManager | null | undefined;
66
+ publishConnection: AmqpConnectionManager | null | undefined;
64
67
  em: EventEmitter;
65
- creatingConnection: boolean;
68
+ creatingConsumeConnection: boolean;
69
+ creatingPublishConnection: boolean;
66
70
  exchanges: ExchangesCache;
67
71
  queues: QueuesCache;
68
72
  queueSetupPromises: QueueSetupPromisesDictionary;
@@ -76,15 +80,19 @@ declare class RabbitMq implements IAfRabbitMq {
76
80
  private shouldConsumeMessageByTimestamp;
77
81
  ack: (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp?: boolean, releaseLock?: null) => (userMsg: ConsumeMessage) => Promise<any>;
78
82
  nack: (channel: ConfirmChannel, queue: string, options: any, deadQueueOptions: Options.AssertQueue, msg: ConsumeMessageOrNull, releaseLock: any) => (userMsg: ConsumeMessageOrNull, { skipRetry, }?: NackOptions) => Promise<any>;
79
- getConnection(): Promise<AmqpConnectionManager>;
80
- getNewChannel({ name, onClose, options }?: newChannelOpts): Promise<ChannelWrapper>;
81
- assertChannel({ force }?: assertChannelOpts): Promise<ChannelWrapper>;
83
+ geConnectionByRole: (connectionRole: ConnectionRole) => {
84
+ connectionByRole: AmqpConnectionManager | null | undefined;
85
+ creatingConnectionByRole: boolean;
86
+ };
87
+ getConnection(connectionRole: ConnectionRole): Promise<AmqpConnectionManager | null | undefined>;
88
+ getNewChannel({ name, onClose, options, connectionRole, }?: newChannelOpts): Promise<ChannelWrapper>;
89
+ assertChannel({ force, connectionRole }?: assertChannelOpts): Promise<ChannelWrapper>;
82
90
  assertExchange(exchangeName: string, options?: any): Promise<any>;
83
- getQueueLength(queue: string): Promise<Replies.AssertQueue>;
91
+ getQueueLength(queue: string, connectionRole?: ConnectionRole): Promise<Replies.AssertQueue>;
84
92
  private deleteQueue;
85
- bindQueue(queue: string, exchange: string): Promise<void>;
86
- setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
87
- assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any>;
93
+ bindQueue(queue: string, exchange: string, connectionRole: ConnectionRole): Promise<void>;
94
+ setupQueue(queueName: string, connectionRole: ConnectionRole, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
95
+ assertQueue(queueName: string, connectionRole: ConnectionRole, options?: Options.AssertQueue): Promise<any>;
88
96
  private saveConsumer;
89
97
  consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
90
98
  private lockRedisIfNeeded;
@@ -93,7 +101,7 @@ declare class RabbitMq implements IAfRabbitMq {
93
101
  consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
94
102
  publish(exchange: string, content: any, customHeaders?: any): Promise<boolean>;
95
103
  sendToQueue(queue: string, content: any, options?: any, customHeaders?: any): Promise<boolean | undefined>;
96
- isConnected(): Promise<boolean>;
104
+ isConnected(connectionRole: ConnectionRole): Promise<boolean>;
97
105
  gracefulShutdown(signal: string): Promise<void>;
98
106
  }
99
107
  export default RabbitMq;
package/dist/index.js CHANGED
@@ -115,11 +115,16 @@ class RabbitMq {
115
115
  });
116
116
  }
117
117
  };
118
+ this.geConnectionByRole = (connectionRole) => (connectionRole === types_1.ConnectionRole.Consumer
119
+ ? { connectionByRole: this.consumeConnection, creatingConnectionByRole: this.creatingConsumeConnection }
120
+ : { connectionByRole: this.publishConnection, creatingConnectionByRole: this.creatingPublishConnection });
118
121
  this.em = new events_1.EventEmitter();
119
122
  this.channel = null;
120
123
  this.publishChannelSetupPromise = null;
121
- this.connection = null;
122
- this.creatingConnection = false;
124
+ this.consumeConnection = null;
125
+ this.publishConnection = null;
126
+ this.creatingPublishConnection = false;
127
+ this.creatingConsumeConnection = false;
123
128
  this.exchanges = {};
124
129
  this.queues = {};
125
130
  this.queueSetupPromises = {};
@@ -140,30 +145,31 @@ class RabbitMq {
140
145
  });
141
146
  }
142
147
  }
143
- async getConnection() {
148
+ async getConnection(connectionRole) {
144
149
  return new Promise(async (resolve, reject) => {
150
+ let { connectionByRole, creatingConnectionByRole } = this.geConnectionByRole(connectionRole);
145
151
  if (this.blockReconnect) {
146
152
  debug('rabbit: block reconnect');
147
153
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
148
154
  // @ts-ignore
149
155
  return resolve();
150
156
  }
151
- if (this.connection !== null) {
152
- if (this.options?.disableReconnect || this.connection?.isConnected()) {
157
+ if (connectionByRole !== null) {
158
+ if (this.options?.disableReconnect || connectionByRole?.isConnected()) {
153
159
  debug('rabbit: connection - is connected');
154
160
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
155
161
  // @ts-ignore
156
- return resolve(this.connection);
162
+ return resolve(connectionByRole);
157
163
  }
158
164
  debug('rabbit: connection - reconnecting');
159
165
  }
160
- if (this.creatingConnection) {
166
+ if (creatingConnectionByRole) {
161
167
  debug('rabbit: creating connection emi');
162
168
  this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
163
169
  this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
164
170
  return;
165
171
  }
166
- this.creatingConnection = true;
172
+ creatingConnectionByRole = true;
167
173
  let isResolved = false;
168
174
  // It is import to use it as a function and not as a variable
169
175
  // because of k8s changes the env variables
@@ -176,11 +182,15 @@ class RabbitMq {
176
182
  return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
177
183
  };
178
184
  const defaultUrls = findServers();
179
- const connection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
185
+ const newConnection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
180
186
  findServers,
181
187
  });
182
- this.connection = connection;
183
- this.connection.on('error', (err) => {
188
+ if (!newConnection) {
189
+ logger_1.default.error('rabbit: couldnt create a connection');
190
+ return resolve(connectionByRole);
191
+ }
192
+ connectionByRole = newConnection;
193
+ connectionByRole.on('error', (err) => {
184
194
  logger_1.default.error('rabbit: connection error', { err });
185
195
  if (!isResolved) {
186
196
  isResolved = true;
@@ -188,7 +198,7 @@ class RabbitMq {
188
198
  this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
189
199
  }
190
200
  });
191
- this.connection.on('connectFailed', (err) => {
201
+ connectionByRole.on('connectFailed', (err) => {
192
202
  this.consumersTags = [];
193
203
  logger_1.default.error('rabbit: connection connectFailed', { err });
194
204
  if (!isResolved) {
@@ -197,7 +207,7 @@ class RabbitMq {
197
207
  this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
198
208
  }
199
209
  });
200
- this.connection.on('disconnect', ({ err }) => {
210
+ connectionByRole.on('disconnect', ({ err }) => {
201
211
  this.consumersTags = [];
202
212
  debug('rabbit: connection closed');
203
213
  if (this.options?.disableReconnect) {
@@ -208,24 +218,27 @@ class RabbitMq {
208
218
  logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
209
219
  }
210
220
  });
211
- this.connection.once('connect', async () => {
221
+ connectionByRole.once('connect', async () => {
212
222
  debug('rabbit: connection established');
213
- this.creatingConnection = false;
214
- this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
223
+ creatingConnectionByRole = false;
224
+ this.em.emit(consts_1.CONNECTION_CREATED_CONST, connectionByRole);
215
225
  isResolved = true;
216
- resolve(connection);
226
+ resolve(connectionByRole);
217
227
  });
218
228
  });
219
229
  }
220
- async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {} } = {}) {
230
+ async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connectionRole, } = { connectionRole: types_1.ConnectionRole.Consumer }) {
221
231
  let connection;
222
232
  try {
223
- connection = await this.getConnection();
233
+ connection = await this.getConnection(connectionRole);
224
234
  }
225
235
  catch (e) {
226
236
  logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
227
237
  throw e;
228
238
  }
239
+ if (!connection) {
240
+ throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
241
+ }
229
242
  const channel = connection.createChannel({ ...options });
230
243
  (0, events_1.once)(channel, 'close').then((args) => {
231
244
  logger_1.default.error(`rabbit: channel ${name} closed`);
@@ -241,14 +254,14 @@ class RabbitMq {
241
254
  throw err;
242
255
  }
243
256
  }
244
- async assertChannel({ force = false } = {}) {
257
+ async assertChannel({ force = false, connectionRole } = { connectionRole: types_1.ConnectionRole.Consumer }) {
245
258
  if (!this.publishChannelSetupPromise) {
246
259
  this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
247
260
  if (this.channel && !force) {
248
261
  return resolve(this.channel);
249
262
  }
250
263
  try {
251
- const channel = await this.getNewChannel({});
264
+ const channel = await this.getNewChannel({ connectionRole });
252
265
  channel.on('error', (err) => {
253
266
  logger_1.default.error('rabbit: channel error', { err });
254
267
  });
@@ -263,7 +276,7 @@ class RabbitMq {
263
276
  return this.publishChannelSetupPromise;
264
277
  }
265
278
  async assertExchange(exchangeName, options) {
266
- const channel = await this.assertChannel();
279
+ const channel = await this.assertChannel({ connectionRole: options.connectionRole });
267
280
  if (this.exchanges[exchangeName]) {
268
281
  return this.exchanges[exchangeName];
269
282
  }
@@ -271,32 +284,33 @@ class RabbitMq {
271
284
  this.exchanges[exchangeName] = exchange;
272
285
  return exchange;
273
286
  }
274
- async getQueueLength(queue) {
287
+ async getQueueLength(queue, connectionRole = types_1.ConnectionRole.Consumer) {
275
288
  RabbitMq.validateName('queue', queue);
289
+ const { connectionByRole } = this.geConnectionByRole(connectionRole);
276
290
  const { channel } = this;
277
291
  if (!channel) {
278
292
  throw new Error('channel is not defined');
279
293
  }
280
- debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
294
+ debug('rabbit: getting queue length', { queue, connected: connectionByRole?.isConnected() });
281
295
  return channel?.checkQueue(queue);
282
296
  }
283
- async deleteQueue(queue) {
297
+ async deleteQueue(queue, connectionRole) {
284
298
  RabbitMq.validateName('queue', queue);
285
- const channel = await this.assertChannel();
299
+ const channel = await this.assertChannel({ connectionRole });
286
300
  logger_1.default.info('rabbit: deleting queue', { queue });
287
301
  const deleteQueueRes = await channel.deleteQueue(queue);
288
302
  debug('queue deleted', deleteQueueRes);
289
303
  return deleteQueueRes;
290
304
  }
291
- async bindQueue(queue, exchange) {
292
- const channel = await this.assertChannel();
305
+ async bindQueue(queue, exchange, connectionRole) {
306
+ const channel = await this.assertChannel({ connectionRole });
293
307
  await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
294
308
  return channel.bindQueue(queue, exchange, '');
295
309
  }
296
- async setupQueue(queueName, options) {
310
+ async setupQueue(queueName, connectionRole, options) {
297
311
  let queue;
298
312
  try {
299
- const channel = await this.assertChannel();
313
+ const channel = await this.assertChannel({ connectionRole });
300
314
  debug('assertQueue->channel.addSetup', { queueName });
301
315
  await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
302
316
  debug('assertQueue->channel.assertQueue', { queueName });
@@ -306,8 +320,8 @@ class RabbitMq {
306
320
  logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
307
321
  if (!this.options?.dontRetryAssert) {
308
322
  debug('retrying assertQueue', { queueName });
309
- const channel = await this.assertChannel({ force: true });
310
- await this.deleteQueue(queueName);
323
+ const channel = await this.assertChannel({ force: true, connectionRole });
324
+ await this.deleteQueue(queueName, connectionRole);
311
325
  debug('retrying assertQueue->channel.addSetup', { queueName });
312
326
  await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
313
327
  debug('retrying assertQueue->channel.assertQueue', { queueName });
@@ -320,7 +334,7 @@ class RabbitMq {
320
334
  this.queues[queueName] = queueName;
321
335
  return queue;
322
336
  }
323
- async assertQueue(queueName, options) {
337
+ async assertQueue(queueName, connectionRole, options) {
324
338
  RabbitMq.validateName('queue', queueName);
325
339
  if (this.queues[queueName]) {
326
340
  delete this.queueSetupPromises[queueName];
@@ -329,7 +343,7 @@ class RabbitMq {
329
343
  if (this.queueSetupPromises[queueName]) {
330
344
  return this.queueSetupPromises[queueName];
331
345
  }
332
- this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
346
+ this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionRole, options);
333
347
  return this.queueSetupPromises[queueName];
334
348
  }
335
349
  saveConsumer(queue, callback, options) {
@@ -372,7 +386,7 @@ class RabbitMq {
372
386
  }
373
387
  logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
374
388
  }
375
- const channel = await this.getNewChannel({});
389
+ const channel = await this.getNewChannel({ connectionRole: types_1.ConnectionRole.Consumer });
376
390
  return channel.addSetup(async (confirmChannel) => {
377
391
  await confirmChannel.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
378
392
  await confirmChannel.prefetch(limit, true);
@@ -452,7 +466,7 @@ class RabbitMq {
452
466
  RabbitMq.validateName('queue', queue);
453
467
  const { limit, deadMessageTtl } = optionsWithDefaults;
454
468
  await this.saveConsumer(queue, callback, options);
455
- const channel = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
469
+ const channel = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}`, connectionRole: types_1.ConnectionRole.Consumer });
456
470
  return channel.addSetup(async (c) => {
457
471
  const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
458
472
  await c.assertQueue(queue);
@@ -467,14 +481,14 @@ class RabbitMq {
467
481
  async publish(exchange, content, customHeaders) {
468
482
  return (0, utils_1.wrapSetImmediate)(async () => {
469
483
  RabbitMq.validateName('exchange', exchange);
470
- const channel = await this.assertChannel();
471
- await this.assertExchange(exchange);
484
+ const channel = await this.assertChannel({ connectionRole: types_1.ConnectionRole.Publisher });
485
+ await this.assertExchange(exchange, { connectionRole: types_1.ConnectionRole.Publisher });
472
486
  await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
473
487
  });
474
488
  }
475
489
  async sendToQueue(queue, content, options, customHeaders) {
476
490
  try {
477
- await this.assertChannel();
491
+ await this.assertChannel({ connectionRole: types_1.ConnectionRole.Publisher });
478
492
  }
479
493
  catch (e) {
480
494
  logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
@@ -482,7 +496,7 @@ class RabbitMq {
482
496
  }
483
497
  try {
484
498
  RabbitMq.validateName('queue', queue);
485
- await this.assertQueue(queue, options);
499
+ await this.assertQueue(queue, types_1.ConnectionRole.Publisher, options);
486
500
  }
487
501
  catch (e) {
488
502
  logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
@@ -494,13 +508,17 @@ class RabbitMq {
494
508
  return res;
495
509
  }
496
510
  catch (e) {
497
- const isConnected = await this.isConnected();
511
+ const isConnected = await this.isConnected(types_1.ConnectionRole.Publisher);
498
512
  logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
499
513
  throw e;
500
514
  }
501
515
  }
502
- async isConnected() {
503
- const connection = await this.getConnection();
516
+ async isConnected(connectionRole) {
517
+ const connection = await this.getConnection(connectionRole);
518
+ if (!connection) {
519
+ logger_1.default.error('rabbit: isConnected - false');
520
+ return false;
521
+ }
504
522
  const isConnected = connection.isConnected();
505
523
  if (!isConnected) {
506
524
  logger_1.default.error('rabbit: isConnected - false');
@@ -38,3 +38,7 @@ export type AfConsumer = {
38
38
  options: ConsumeOptions | undefined;
39
39
  };
40
40
  export declare const CONSUMER_DEFAULT_OPTIONS: Options.Consume;
41
+ export declare enum ConnectionRole {
42
+ Consumer = "consumer",
43
+ Publisher = "publisher"
44
+ }
package/dist/lib/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CONSUMER_DEFAULT_OPTIONS = void 0;
3
+ exports.ConnectionRole = exports.CONSUMER_DEFAULT_OPTIONS = void 0;
4
4
  const HA_PROMOTE_ON_FAILURE = 'ha-promote-on-failure';
5
5
  const HA_PROMOTE_ON_SHUTDOWN = 'ha-promote-on-shutdown';
6
6
  exports.CONSUMER_DEFAULT_OPTIONS = {
@@ -9,3 +9,8 @@ exports.CONSUMER_DEFAULT_OPTIONS = {
9
9
  [HA_PROMOTE_ON_SHUTDOWN]: 'always',
10
10
  },
11
11
  };
12
+ var ConnectionRole;
13
+ (function (ConnectionRole) {
14
+ ConnectionRole["Consumer"] = "consumer";
15
+ ConnectionRole["Publisher"] = "publisher";
16
+ })(ConnectionRole = exports.ConnectionRole || (exports.ConnectionRole = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/rabbit",
3
- "version": "3.2.19-beta.5",
3
+ "version": "3.2.19-beta.7",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
package/src/index.ts CHANGED
@@ -36,13 +36,13 @@ import {
36
36
  QueuesCache,
37
37
  RedisLockType,
38
38
  ExchangesCache, CONSUMER_DEFAULT_OPTIONS, QueueSetupPromisesDictionary,
39
+ ConnectionRole,
39
40
  } from './lib/types';
40
41
 
41
42
  // const debug = nodeDebug('af-rabbitmq')
42
43
  const debug = logger.debug.bind(logger);
43
44
 
44
45
  const PUBLISH_TIMEOUT = 1000 * 10;
45
-
46
46
  export interface IAfRabbitMq {
47
47
  ack: any;
48
48
  nack: any;
@@ -82,11 +82,13 @@ type newChannelOpts = {
82
82
  name?: string;
83
83
  onClose?: null | ((args: any | null) => void);
84
84
  options?: CreateChannelOpts | undefined;
85
+ connectionRole: ConnectionRole,
85
86
  };
86
87
 
87
88
  type assertChannelOpts = {
88
89
  channelName?: string;
89
90
  force?: boolean;
91
+ connectionRole: ConnectionRole;
90
92
  }
91
93
 
92
94
  type AfConsumer = {
@@ -145,11 +147,15 @@ class RabbitMq implements IAfRabbitMq {
145
147
 
146
148
  blockReconnect: boolean | null | undefined
147
149
 
148
- connection: AmqpConnectionManager | null | undefined
150
+ consumeConnection: AmqpConnectionManager | null | undefined;
151
+
152
+ publishConnection: AmqpConnectionManager | null | undefined;
149
153
 
150
154
  em: EventEmitter;
151
155
 
152
- creatingConnection: boolean;
156
+ creatingConsumeConnection: boolean;
157
+
158
+ creatingPublishConnection: boolean;
153
159
 
154
160
  exchanges: ExchangesCache;
155
161
 
@@ -172,8 +178,10 @@ class RabbitMq implements IAfRabbitMq {
172
178
  this.em = new EventEmitter();
173
179
  this.channel = null;
174
180
  this.publishChannelSetupPromise = null;
175
- this.connection = null;
176
- this.creatingConnection = false;
181
+ this.consumeConnection = null;
182
+ this.publishConnection = null;
183
+ this.creatingPublishConnection = false;
184
+ this.creatingConsumeConnection = false;
177
185
  this.exchanges = {};
178
186
  this.queues = {};
179
187
  this.queueSetupPromises = {};
@@ -270,30 +278,35 @@ class RabbitMq implements IAfRabbitMq {
270
278
  }
271
279
  }
272
280
 
273
- async getConnection() {
274
- return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
281
+ geConnectionByRole = (connectionRole: ConnectionRole) => (connectionRole === ConnectionRole.Consumer
282
+ ? { connectionByRole: this.consumeConnection, creatingConnectionByRole: this.creatingConsumeConnection }
283
+ : { connectionByRole: this.publishConnection, creatingConnectionByRole: this.creatingPublishConnection });
284
+
285
+ async getConnection(connectionRole: ConnectionRole) {
286
+ return new Promise<AmqpConnectionManager | undefined | null>(async (resolve, reject) => {
287
+ let { connectionByRole, creatingConnectionByRole } = this.geConnectionByRole(connectionRole);
275
288
  if (this.blockReconnect) {
276
289
  debug('rabbit: block reconnect');
277
290
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
278
291
  // @ts-ignore
279
292
  return resolve();
280
293
  }
281
- if (this.connection !== null) {
282
- if (this.options?.disableReconnect || this.connection?.isConnected()) {
294
+ if (connectionByRole !== null) {
295
+ if (this.options?.disableReconnect || connectionByRole?.isConnected()) {
283
296
  debug('rabbit: connection - is connected');
284
297
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
285
298
  // @ts-ignore
286
- return resolve(this.connection);
299
+ return resolve(connectionByRole);
287
300
  }
288
301
  debug('rabbit: connection - reconnecting');
289
302
  }
290
- if (this.creatingConnection) {
303
+ if (creatingConnectionByRole) {
291
304
  debug('rabbit: creating connection emi');
292
305
  this.em.once(CONNECTION_CREATED_CONST, resolve);
293
306
  this.em.once(CONNECTION_FAILED_CONST, reject);
294
307
  return;
295
308
  }
296
- this.creatingConnection = true;
309
+ creatingConnectionByRole = true;
297
310
  let isResolved = false;
298
311
 
299
312
  // It is import to use it as a function and not as a variable
@@ -310,12 +323,18 @@ class RabbitMq implements IAfRabbitMq {
310
323
  };
311
324
 
312
325
  const defaultUrls = findServers();
313
- const connection: AmqpConnectionManager = await connect(defaultUrls, {
326
+ const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
314
327
  findServers,
315
328
  });
316
329
 
317
- this.connection = connection;
318
- this.connection.on('error', (err) => {
330
+ if (!newConnection) {
331
+ logger.error('rabbit: couldnt create a connection');
332
+ return resolve(connectionByRole);
333
+ }
334
+
335
+ connectionByRole = newConnection;
336
+
337
+ connectionByRole.on('error', (err) => {
319
338
  logger.error('rabbit: connection error', { err });
320
339
  if (!isResolved) {
321
340
  isResolved = true;
@@ -324,7 +343,7 @@ class RabbitMq implements IAfRabbitMq {
324
343
  }
325
344
  });
326
345
 
327
- this.connection.on('connectFailed', (err) => {
346
+ connectionByRole.on('connectFailed', (err) => {
328
347
  this.consumersTags = [];
329
348
  logger.error('rabbit: connection connectFailed', { err });
330
349
  if (!isResolved) {
@@ -334,7 +353,7 @@ class RabbitMq implements IAfRabbitMq {
334
353
  }
335
354
  });
336
355
 
337
- this.connection.on('disconnect', ({ err }) => {
356
+ connectionByRole.on('disconnect', ({ err }) => {
338
357
  this.consumersTags = [];
339
358
  debug('rabbit: connection closed');
340
359
  if (this.options?.disableReconnect) {
@@ -344,24 +363,29 @@ class RabbitMq implements IAfRabbitMq {
344
363
  logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
345
364
  }
346
365
  });
347
- this.connection.once('connect', async () => {
366
+ connectionByRole.once('connect', async () => {
348
367
  debug('rabbit: connection established');
349
- this.creatingConnection = false;
350
- this.em.emit(CONNECTION_CREATED_CONST, connection);
368
+ creatingConnectionByRole = false;
369
+ this.em.emit(CONNECTION_CREATED_CONST, connectionByRole);
351
370
  isResolved = true;
352
- resolve(connection);
371
+ resolve(connectionByRole);
353
372
  });
354
373
  });
355
374
  }
356
375
 
357
- async getNewChannel({ name = rand().toString(), onClose = null, options = {} }: newChannelOpts = {}) {
358
- let connection!: AmqpConnectionManager;
376
+ async getNewChannel({
377
+ name = rand().toString(), onClose = null, options = {}, connectionRole,
378
+ }: newChannelOpts = { connectionRole: ConnectionRole.Consumer }): Promise<ChannelWrapper> {
379
+ let connection!: AmqpConnectionManager | undefined | null;
359
380
  try {
360
- connection = await this.getConnection();
381
+ connection = await this.getConnection(connectionRole);
361
382
  } catch (e) {
362
383
  logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
363
384
  throw e;
364
385
  }
386
+ if (!connection) {
387
+ throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
388
+ }
365
389
  const channel = connection.createChannel({ ...options });
366
390
  once(channel, 'close').then((args) => {
367
391
  logger.error(`rabbit: channel ${name} closed`);
@@ -377,7 +401,7 @@ class RabbitMq implements IAfRabbitMq {
377
401
  }
378
402
  }
379
403
 
380
- async assertChannel({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
404
+ async assertChannel({ force = false, connectionRole }: assertChannelOpts = { connectionRole: ConnectionRole.Consumer }): Promise<ChannelWrapper> {
381
405
  if (!this.publishChannelSetupPromise) {
382
406
  this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
383
407
  if (this.channel && !force) {
@@ -385,7 +409,7 @@ class RabbitMq implements IAfRabbitMq {
385
409
  }
386
410
 
387
411
  try {
388
- const channel = await this.getNewChannel({});
412
+ const channel = await this.getNewChannel({ connectionRole });
389
413
  channel.on('error', (err) => {
390
414
  logger.error('rabbit: channel error', { err });
391
415
  });
@@ -400,7 +424,7 @@ class RabbitMq implements IAfRabbitMq {
400
424
  }
401
425
 
402
426
  async assertExchange(exchangeName: string, options?: any) {
403
- const channel: ChannelWrapper = await this.assertChannel();
427
+ const channel: ChannelWrapper = await this.assertChannel({ connectionRole: options.connectionRole });
404
428
  if (this.exchanges[exchangeName]) {
405
429
  return this.exchanges[exchangeName];
406
430
  }
@@ -409,35 +433,36 @@ class RabbitMq implements IAfRabbitMq {
409
433
  return exchange;
410
434
  }
411
435
 
412
- async getQueueLength(queue: string) {
436
+ async getQueueLength(queue: string, connectionRole: ConnectionRole = ConnectionRole.Consumer): Promise<Replies.AssertQueue> {
413
437
  RabbitMq.validateName('queue', queue);
438
+ const { connectionByRole } = this.geConnectionByRole(connectionRole);
414
439
  const { channel } = this;
415
440
  if (!channel) {
416
441
  throw new Error('channel is not defined');
417
442
  }
418
- debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
443
+ debug('rabbit: getting queue length', { queue, connected: connectionByRole?.isConnected() });
419
444
  return channel?.checkQueue(queue);
420
445
  }
421
446
 
422
- private async deleteQueue(queue: string) {
447
+ private async deleteQueue(queue: string, connectionRole: ConnectionRole) {
423
448
  RabbitMq.validateName('queue', queue);
424
- const channel: ChannelWrapper = await this.assertChannel();
449
+ const channel: ChannelWrapper = await this.assertChannel({ connectionRole });
425
450
  logger.info('rabbit: deleting queue', { queue });
426
451
  const deleteQueueRes = await channel.deleteQueue(queue);
427
452
  debug('queue deleted', deleteQueueRes);
428
453
  return deleteQueueRes;
429
454
  }
430
455
 
431
- async bindQueue(queue: string, exchange: string) {
432
- const channel: ChannelWrapper = await this.assertChannel();
456
+ async bindQueue(queue: string, exchange: string, connectionRole: ConnectionRole) {
457
+ const channel: ChannelWrapper = await this.assertChannel({ connectionRole });
433
458
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
434
459
  return channel.bindQueue(queue, exchange, '');
435
460
  }
436
461
 
437
- async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
462
+ async setupQueue(queueName: string, connectionRole: ConnectionRole, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
438
463
  let queue: Replies.AssertQueue;
439
464
  try {
440
- const channel: ChannelWrapper = await this.assertChannel();
465
+ const channel: ChannelWrapper = await this.assertChannel({ connectionRole });
441
466
  debug('assertQueue->channel.addSetup', { queueName });
442
467
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, options));
443
468
  debug('assertQueue->channel.assertQueue', { queueName });
@@ -446,8 +471,8 @@ class RabbitMq implements IAfRabbitMq {
446
471
  logger.error('rabbit: assertQueue error', { queueName, options, error: e });
447
472
  if (!this.options?.dontRetryAssert) {
448
473
  debug('retrying assertQueue', { queueName });
449
- const channel = await this.assertChannel({ force: true });
450
- await this.deleteQueue(queueName);
474
+ const channel = await this.assertChannel({ force: true, connectionRole });
475
+ await this.deleteQueue(queueName, connectionRole);
451
476
 
452
477
  debug('retrying assertQueue->channel.addSetup', { queueName });
453
478
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, options));
@@ -462,7 +487,7 @@ class RabbitMq implements IAfRabbitMq {
462
487
  return queue;
463
488
  }
464
489
 
465
- async assertQueue(queueName: string, options?: Options.AssertQueue) {
490
+ async assertQueue(queueName: string, connectionRole: ConnectionRole, options?: Options.AssertQueue) {
466
491
  RabbitMq.validateName('queue', queueName);
467
492
  if (this.queues[queueName]) {
468
493
  delete this.queueSetupPromises[queueName];
@@ -473,7 +498,7 @@ class RabbitMq implements IAfRabbitMq {
473
498
  return this.queueSetupPromises[queueName];
474
499
  }
475
500
 
476
- this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
501
+ this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionRole, options);
477
502
  return this.queueSetupPromises[queueName];
478
503
  }
479
504
 
@@ -527,7 +552,7 @@ class RabbitMq implements IAfRabbitMq {
527
552
  }
528
553
  logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
529
554
  }
530
- const channel = await this.getNewChannel({});
555
+ const channel = await this.getNewChannel({ connectionRole: ConnectionRole.Consumer });
531
556
  return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
532
557
  await confirmChannel.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
533
558
  await confirmChannel.prefetch(limit, true);
@@ -619,7 +644,7 @@ class RabbitMq implements IAfRabbitMq {
619
644
  RabbitMq.validateName('queue', queue);
620
645
  const { limit, deadMessageTtl } = optionsWithDefaults;
621
646
  await this.saveConsumer(queue, callback, options);
622
- const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
647
+ const channel: ChannelWrapper = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}`, connectionRole: ConnectionRole.Consumer });
623
648
 
624
649
  return channel.addSetup(async (c: ConfirmChannel) => {
625
650
  const assertExchange = await assertExchangeFanout(c, exchange);
@@ -640,8 +665,8 @@ class RabbitMq implements IAfRabbitMq {
640
665
  async publish(exchange: string, content: any, customHeaders?: any) : Promise<boolean> {
641
666
  return wrapSetImmediate(async () => {
642
667
  RabbitMq.validateName('exchange', exchange);
643
- const channel: ChannelWrapper = await this.assertChannel();
644
- await this.assertExchange(exchange);
668
+ const channel: ChannelWrapper = await this.assertChannel({ connectionRole: ConnectionRole.Publisher });
669
+ await this.assertExchange(exchange, { connectionRole: ConnectionRole.Publisher });
645
670
  await channel.publish(exchange, '',
646
671
  Buffer.from(JSON.stringify(content)),
647
672
  RabbitMq.getPublishOptions(customHeaders));
@@ -655,7 +680,7 @@ class RabbitMq implements IAfRabbitMq {
655
680
  customHeaders?: any,
656
681
  ): Promise<boolean | undefined> {
657
682
  try {
658
- await this.assertChannel();
683
+ await this.assertChannel({ connectionRole: ConnectionRole.Publisher });
659
684
  } catch (e) {
660
685
  logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
661
686
  throw e;
@@ -663,7 +688,7 @@ class RabbitMq implements IAfRabbitMq {
663
688
 
664
689
  try {
665
690
  RabbitMq.validateName('queue', queue);
666
- await this.assertQueue(queue, options);
691
+ await this.assertQueue(queue, ConnectionRole.Publisher, options);
667
692
  } catch (e) {
668
693
  logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
669
694
  throw e;
@@ -676,14 +701,18 @@ class RabbitMq implements IAfRabbitMq {
676
701
  debug(`rabbit: sending to queue ${queue}`, { res });
677
702
  return res;
678
703
  } catch (e) {
679
- const isConnected = await this.isConnected();
704
+ const isConnected = await this.isConnected(ConnectionRole.Publisher);
680
705
  logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
681
706
  throw e;
682
707
  }
683
708
  }
684
709
 
685
- async isConnected() : Promise<boolean> {
686
- const connection = await this.getConnection();
710
+ async isConnected(connectionRole: ConnectionRole): Promise<boolean> {
711
+ const connection = await this.getConnection(connectionRole);
712
+ if (!connection) {
713
+ logger.error('rabbit: isConnected - false');
714
+ return false;
715
+ }
687
716
  const isConnected = connection.isConnected();
688
717
  if (!isConnected) {
689
718
  logger.error('rabbit: isConnected - false');
package/src/lib/types.ts CHANGED
@@ -55,3 +55,8 @@ export const CONSUMER_DEFAULT_OPTIONS: Options.Consume = {
55
55
  [HA_PROMOTE_ON_SHUTDOWN]: 'always',
56
56
  },
57
57
  };
58
+
59
+ export enum ConnectionRole {
60
+ Consumer = 'consumer',
61
+ Publisher = 'publisher',
62
+ }
@@ -1,7 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <coverage generated="1675584950929" clover="3.2.0">
3
- <project timestamp="1675584950929" name="All files">
4
- <metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0" elements="0" coveredelements="0" complexity="0" loc="0" ncloc="0" packages="0" files="0" classes="0">
5
- </metrics>
6
- </project>
7
- </coverage>
@@ -1 +0,0 @@
1
- {}
@@ -1,212 +0,0 @@
1
- body, html {
2
- margin:0; padding: 0;
3
- height: 100%;
4
- }
5
- body {
6
- font-family: Helvetica Neue, Helvetica, Arial;
7
- font-size: 14px;
8
- color:#333;
9
- }
10
- .small { font-size: 12px; }
11
- *, *:after, *:before {
12
- -webkit-box-sizing:border-box;
13
- -moz-box-sizing:border-box;
14
- box-sizing:border-box;
15
- }
16
- h1 { font-size: 20px; margin: 0;}
17
- h2 { font-size: 14px; }
18
- pre {
19
- font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
20
- margin: 0;
21
- padding: 0;
22
- -moz-tab-size: 2;
23
- -o-tab-size: 2;
24
- tab-size: 2;
25
- }
26
- a { color:#0074D9; text-decoration:none; }
27
- a:hover { text-decoration:underline; }
28
- .strong { font-weight: bold; }
29
- .space-top1 { padding: 10px 0 0 0; }
30
- .pad2y { padding: 20px 0; }
31
- .pad1y { padding: 10px 0; }
32
- .pad2x { padding: 0 20px; }
33
- .pad2 { padding: 20px; }
34
- .pad1 { padding: 10px; }
35
- .space-left2 { padding-left:55px; }
36
- .space-right2 { padding-right:20px; }
37
- .center { text-align:center; }
38
- .clearfix { display:block; }
39
- .clearfix:after {
40
- content:'';
41
- display:block;
42
- height:0;
43
- clear:both;
44
- visibility:hidden;
45
- }
46
- .fl { float: left; }
47
- @media only screen and (max-width:640px) {
48
- .col3 { width:100%; max-width:100%; }
49
- .hide-mobile { display:none!important; }
50
- }
51
-
52
- .quiet {
53
- color: #7f7f7f;
54
- color: rgba(0,0,0,0.5);
55
- }
56
- .quiet a { opacity: 0.7; }
57
-
58
- .fraction {
59
- font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
60
- font-size: 10px;
61
- color: #555;
62
- background: #E8E8E8;
63
- padding: 4px 5px;
64
- border-radius: 3px;
65
- vertical-align: middle;
66
- }
67
-
68
- div.path a:link, div.path a:visited { color: #333; }
69
- table.coverage {
70
- border-collapse: collapse;
71
- margin: 10px 0 0 0;
72
- padding: 0;
73
- }
74
-
75
- table.coverage td {
76
- margin: 0;
77
- padding: 0;
78
- vertical-align: top;
79
- }
80
- table.coverage td.line-count {
81
- text-align: right;
82
- padding: 0 5px 0 20px;
83
- }
84
- table.coverage td.line-coverage {
85
- text-align: right;
86
- padding-right: 10px;
87
- min-width:20px;
88
- }
89
-
90
- table.coverage td span.cline-any {
91
- display: inline-block;
92
- padding: 0 5px;
93
- width: 100%;
94
- }
95
- .missing-if-branch {
96
- display: inline-block;
97
- margin-right: 5px;
98
- border-radius: 3px;
99
- position: relative;
100
- padding: 0 4px;
101
- background: #333;
102
- color: yellow;
103
- }
104
-
105
- .skip-if-branch {
106
- display: none;
107
- margin-right: 10px;
108
- position: relative;
109
- padding: 0 4px;
110
- background: #ccc;
111
- color: white;
112
- }
113
- .missing-if-branch .typ, .skip-if-branch .typ {
114
- color: inherit !important;
115
- }
116
- .coverage-summary {
117
- border-collapse: collapse;
118
- width: 100%;
119
- }
120
- .coverage-summary tr { border-bottom: 1px solid #bbb; }
121
- .keyline-all { border: 1px solid #ddd; }
122
- .coverage-summary td, .coverage-summary th { padding: 10px; }
123
- .coverage-summary tbody { border: 1px solid #bbb; }
124
- .coverage-summary td { border-right: 1px solid #bbb; }
125
- .coverage-summary td:last-child { border-right: none; }
126
- .coverage-summary th {
127
- text-align: left;
128
- font-weight: normal;
129
- white-space: nowrap;
130
- }
131
- .coverage-summary th.file { border-right: none !important; }
132
- .coverage-summary th.pct { }
133
- .coverage-summary th.pic,
134
- .coverage-summary th.abs,
135
- .coverage-summary td.pct,
136
- .coverage-summary td.abs { text-align: right; }
137
- .coverage-summary td.file { white-space: nowrap; }
138
- .coverage-summary td.pic { min-width: 120px !important; }
139
- .coverage-summary tfoot td { }
140
-
141
- .coverage-summary .sorter {
142
- height: 10px;
143
- width: 7px;
144
- display: inline-block;
145
- margin-left: 0.5em;
146
- background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
147
- }
148
- .coverage-summary .sorted .sorter {
149
- background-position: 0 -20px;
150
- }
151
- .coverage-summary .sorted-desc .sorter {
152
- background-position: 0 -10px;
153
- }
154
- .status-line { height: 10px; }
155
- /* dark red */
156
- .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
157
- .low .chart { border:1px solid #C21F39 }
158
- /* medium red */
159
- .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
160
- /* light red */
161
- .low, .cline-no { background:#FCE1E5 }
162
- /* light green */
163
- .high, .cline-yes { background:rgb(230,245,208) }
164
- /* medium green */
165
- .cstat-yes { background:rgb(161,215,106) }
166
- /* dark green */
167
- .status-line.high, .high .cover-fill { background:rgb(77,146,33) }
168
- .high .chart { border:1px solid rgb(77,146,33) }
169
-
170
-
171
- .medium .chart { border:1px solid #666; }
172
- .medium .cover-fill { background: #666; }
173
-
174
- .cbranch-no { background: yellow !important; color: #111; }
175
-
176
- .cstat-skip { background: #ddd; color: #111; }
177
- .fstat-skip { background: #ddd; color: #111 !important; }
178
- .cbranch-skip { background: #ddd !important; color: #111; }
179
-
180
- span.cline-neutral { background: #eaeaea; }
181
- .medium { background: #eaeaea; }
182
-
183
- .cover-fill, .cover-empty {
184
- display:inline-block;
185
- height: 12px;
186
- }
187
- .chart {
188
- line-height: 0;
189
- }
190
- .cover-empty {
191
- background: white;
192
- }
193
- .cover-full {
194
- border-right: none !important;
195
- }
196
- pre.prettyprint {
197
- border: none !important;
198
- padding: 0 !important;
199
- margin: 0 !important;
200
- }
201
- .com { color: #999 !important; }
202
- .ignore-none { color: #999; font-weight: normal; }
203
-
204
- .wrapper {
205
- min-height: 100%;
206
- height: auto !important;
207
- height: 100%;
208
- margin: 0 auto -48px;
209
- }
210
- .footer, .push {
211
- height: 48px;
212
- }
@@ -1,60 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <title>Code coverage report for All files</title>
5
- <meta charset="utf-8" />
6
- <link rel="stylesheet" href="prettify.css" />
7
- <link rel="stylesheet" href="base.css" />
8
- <meta name="viewport" content="width=device-width, initial-scale=1">
9
- <style type='text/css'>
10
- .coverage-summary .sorter {
11
- background-image: url(sort-arrow-sprite.png);
12
- }
13
- </style>
14
- </head>
15
- <body>
16
- <div class='wrapper'>
17
- <div class='pad1'>
18
- <h1>
19
- All files
20
- </h1>
21
- <div class='clearfix'>
22
- </div>
23
- </div>
24
- <div class='status-line medium'></div>
25
- <div class="pad1">
26
- <table class="coverage-summary">
27
- <thead>
28
- <tr>
29
- <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
30
- <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
31
- <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
32
- <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
33
- <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
34
- <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
35
- <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
36
- <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
37
- <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
38
- <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
39
- </tr>
40
- </thead>
41
- <tbody></tbody>
42
- </table>
43
- </div><div class='push'></div><!-- for sticky footer -->
44
- </div><!-- /wrapper -->
45
- <div class='footer quiet pad2 space-top1 center small'>
46
- Code coverage
47
- generated by <a href="https://istanbul.js.org/" target="_blank">istanbul</a> at Sun Feb 05 2023 10:15:50 GMT+0200 (Israel Standard Time)
48
- </div>
49
- </div>
50
- <script src="prettify.js"></script>
51
- <script>
52
- window.onload = function () {
53
- if (typeof prettyPrint === 'function') {
54
- prettyPrint();
55
- }
56
- };
57
- </script>
58
- <script src="sorter.js"></script>
59
- </body>
60
- </html>
@@ -1 +0,0 @@
1
- .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
@@ -1 +0,0 @@
1
- window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V<U;++V){var ae=Z[V];if(ae.ignoreCase){ac=true}else{if(/[a-z]/i.test(ae.source.replace(/\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi,""))){S=true;ac=false;break}}}var Y={b:8,t:9,n:10,v:11,f:12,r:13};function ab(ah){var ag=ah.charCodeAt(0);if(ag!==92){return ag}var af=ah.charAt(1);ag=Y[af];if(ag){return ag}else{if("0"<=af&&af<="7"){return parseInt(ah.substring(1),8)}else{if(af==="u"||af==="x"){return parseInt(ah.substring(2),16)}else{return ah.charCodeAt(1)}}}}function T(af){if(af<32){return(af<16?"\\x0":"\\x")+af.toString(16)}var ag=String.fromCharCode(af);if(ag==="\\"||ag==="-"||ag==="["||ag==="]"){ag="\\"+ag}return ag}function X(am){var aq=am.substring(1,am.length-1).match(new RegExp("\\\\u[0-9A-Fa-f]{4}|\\\\x[0-9A-Fa-f]{2}|\\\\[0-3][0-7]{0,2}|\\\\[0-7]{1,2}|\\\\[\\s\\S]|-|[^-\\\\]","g"));var ak=[];var af=[];var ao=aq[0]==="^";for(var ar=ao?1:0,aj=aq.length;ar<aj;++ar){var ah=aq[ar];if(/\\[bdsw]/i.test(ah)){ak.push(ah)}else{var ag=ab(ah);var al;if(ar+2<aj&&"-"===aq[ar+1]){al=ab(aq[ar+2]);ar+=2}else{al=ag}af.push([ag,al]);if(!(al<65||ag>122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;ar<af.length;++ar){var at=af[ar];if(at[0]<=ap[1]+1){ap[1]=Math.max(ap[1],at[1])}else{ai.push(ap=at)}}var an=["["];if(ao){an.push("^")}an.push.apply(an,ak);for(var ar=0;ar<ai.length;++ar){var at=ai[ar];an.push(T(at[0]));if(at[1]>at[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak<ah;++ak){var ag=aj[ak];if(ag==="("){++am}else{if("\\"===ag.charAt(0)){var af=+ag.substring(1);if(af&&af<=am){an[af]=-1}}}}for(var ak=1;ak<an.length;++ak){if(-1===an[ak]){an[ak]=++ad}}for(var ak=0,am=0;ak<ah;++ak){var ag=aj[ak];if(ag==="("){++am;if(an[am]===undefined){aj[ak]="(?:"}}else{if("\\"===ag.charAt(0)){var af=+ag.substring(1);if(af&&af<=am){aj[ak]="\\"+an[am]}}}}for(var ak=0,am=0;ak<ah;++ak){if("^"===aj[ak]&&"^"!==aj[ak+1]){aj[ak]=""}}if(al.ignoreCase&&S){for(var ak=0;ak<ah;++ak){var ag=aj[ak];var ai=ag.charAt(0);if(ag.length>=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V<U;++V){var ae=Z[V];if(ae.global||ae.multiline){throw new Error(""+ae)}aa.push("(?:"+W(ae)+")")}return new RegExp(aa.join("|"),ac?"gi":"g")}function a(V){var U=/(?:^|\s)nocode(?:\s|$)/;var X=[];var T=0;var Z=[];var W=0;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=document.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Y=S&&"pre"===S.substring(0,3);function aa(ab){switch(ab.nodeType){case 1:if(U.test(ab.className)){return}for(var ae=ab.firstChild;ae;ae=ae.nextSibling){aa(ae)}var ad=ab.nodeName;if("BR"===ad||"LI"===ad){X[W]="\n";Z[W<<1]=T++;Z[(W++<<1)|1]=ab}break;case 3:case 4:var ac=ab.nodeValue;if(ac.length){if(!Y){ac=ac.replace(/[ \t\r\n]+/g," ")}else{ac=ac.replace(/\r\n?/g,"\n")}X[W]=ac;Z[W<<1]=T;T+=ac.length;Z[(W++<<1)|1]=ab}break}}aa(V);return{sourceCode:X.join("").replace(/\n$/,""),spans:Z}}function B(S,U,W,T){if(!U){return}var V={sourceCode:U,basePos:S};W(V);T.push.apply(T,V.decorations)}var v=/\S/;function o(S){var V=undefined;for(var U=S.firstChild;U;U=U.nextSibling){var T=U.nodeType;V=(T===1)?(V?S:U):(T===3)?(v.test(U.nodeValue)?S:V):V}return V===S?undefined:V}function g(U,T){var S={};var V;(function(){var ad=U.concat(T);var ah=[];var ag={};for(var ab=0,Z=ad.length;ab<Z;++ab){var Y=ad[ab];var ac=Y[3];if(ac){for(var ae=ac.length;--ae>=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae<aq;++ae){var ag=an[ae];var ap=aj[ag];var ai=void 0;var am;if(typeof ap==="string"){am=false}else{var aa=S[ag.charAt(0)];if(aa){ai=ag.match(aa[1]);ap=aa[0]}else{for(var ao=0;ao<X;++ao){aa=T[ao];ai=ag.match(aa[1]);if(ai){ap=aa[0];break}}if(!ai){ap=F}}am=ap.length>=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y<W.length;++Y){ae(W[Y])}if(ag===(ag|0)){W[0].setAttribute("value",ag)}var aa=ac.createElement("OL");aa.className="linenums";var X=Math.max(0,((ag-1))|0)||0;for(var Y=0,T=W.length;Y<T;++Y){af=W[Y];af.className="L"+((Y+X)%10);if(!af.firstChild){af.appendChild(ac.createTextNode("\xA0"))}aa.appendChild(af)}V.appendChild(aa)}function D(ac){var aj=/\bMSIE\b/.test(navigator.userAgent);var am=/\n/g;var al=ac.sourceCode;var an=al.length;var V=0;var aa=ac.spans;var T=aa.length;var ah=0;var X=ac.decorations;var Y=X.length;var Z=0;X[Y]=an;var ar,aq;for(aq=ar=0;aq<Y;){if(X[aq]!==X[aq+2]){X[ar++]=X[aq++];X[ar++]=X[aq++]}else{aq+=2}}Y=ar;for(aq=ar=0;aq<Y;){var at=X[aq];var ab=X[aq+1];var W=aq+2;while(W+2<=Y&&X[W+1]===ab){W+=2}X[ar++]=at;X[ar++]=ab;aq=W}Y=X.length=ar;var ae=null;while(ah<T){var af=aa[ah];var S=aa[ah+2]||an;var ag=X[Z];var ap=X[Z+2]||an;var W=Math.min(S,ap);var ak=aa[ah+1];var U;if(ak.nodeType!==1&&(U=al.substring(V,W))){if(aj){U=U.replace(am,"\r")}ak.nodeValue=U;var ai=ak.ownerDocument;var ao=ai.createElement("SPAN");ao.className=X[Z+1];var ad=ak.parentNode;ad.replaceChild(ao,ak);ao.appendChild(ak);if(V<S){aa[ah+1]=ak=ai.createTextNode(al.substring(W,S));ad.insertBefore(ak,ao.nextSibling)}}V=W;if(V>=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*</.test(S)?"default-markup":"default-code"}return t[T]}c(K,["default-code"]);c(g([],[[F,/^[^<?]+/],[E,/^<!\w[^>]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa<ac.length;++aa){for(var Z=0,V=ac[aa].length;Z<V;++Z){T.push(ac[aa][Z])}}ac=null;var W=Date;if(!W.now){W={now:function(){return +(new Date)}}}var X=0;var S;var ab=/\blang(?:uage)?-([\w.]+)(?!\S)/;var ae=/\bprettyprint\b/;function U(){var ag=(window.PR_SHOULD_USE_CONTINUATION?W.now()+250:Infinity);for(;X<T.length&&W.now()<ag;X++){var aj=T[X];var ai=aj.className;if(ai.indexOf("prettyprint")>=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X<T.length){setTimeout(U,250)}else{if(ad){ad()}}}U()}window.prettyPrintOne=y;window.prettyPrint=b;window.PR={createSimpleLexer:g,registerLangHandler:c,sourceDecorator:i,PR_ATTRIB_NAME:P,PR_ATTRIB_VALUE:n,PR_COMMENT:j,PR_DECLARATION:E,PR_KEYWORD:z,PR_LITERAL:G,PR_NOCODE:N,PR_PLAIN:F,PR_PUNCTUATION:L,PR_SOURCE:J,PR_STRING:C,PR_TAG:m,PR_TYPE:O}})();PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_DECLARATION,/^<!\w[^>]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^<script\b[^>]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:<!--|-->)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]);
@@ -1,158 +0,0 @@
1
- var addSorting = (function () {
2
- "use strict";
3
- var cols,
4
- currentSort = {
5
- index: 0,
6
- desc: false
7
- };
8
-
9
- // returns the summary table element
10
- function getTable() { return document.querySelector('.coverage-summary'); }
11
- // returns the thead element of the summary table
12
- function getTableHeader() { return getTable().querySelector('thead tr'); }
13
- // returns the tbody element of the summary table
14
- function getTableBody() { return getTable().querySelector('tbody'); }
15
- // returns the th element for nth column
16
- function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; }
17
-
18
- // loads all columns
19
- function loadColumns() {
20
- var colNodes = getTableHeader().querySelectorAll('th'),
21
- colNode,
22
- cols = [],
23
- col,
24
- i;
25
-
26
- for (i = 0; i < colNodes.length; i += 1) {
27
- colNode = colNodes[i];
28
- col = {
29
- key: colNode.getAttribute('data-col'),
30
- sortable: !colNode.getAttribute('data-nosort'),
31
- type: colNode.getAttribute('data-type') || 'string'
32
- };
33
- cols.push(col);
34
- if (col.sortable) {
35
- col.defaultDescSort = col.type === 'number';
36
- colNode.innerHTML = colNode.innerHTML + '<span class="sorter"></span>';
37
- }
38
- }
39
- return cols;
40
- }
41
- // attaches a data attribute to every tr element with an object
42
- // of data values keyed by column name
43
- function loadRowData(tableRow) {
44
- var tableCols = tableRow.querySelectorAll('td'),
45
- colNode,
46
- col,
47
- data = {},
48
- i,
49
- val;
50
- for (i = 0; i < tableCols.length; i += 1) {
51
- colNode = tableCols[i];
52
- col = cols[i];
53
- val = colNode.getAttribute('data-value');
54
- if (col.type === 'number') {
55
- val = Number(val);
56
- }
57
- data[col.key] = val;
58
- }
59
- return data;
60
- }
61
- // loads all row data
62
- function loadData() {
63
- var rows = getTableBody().querySelectorAll('tr'),
64
- i;
65
-
66
- for (i = 0; i < rows.length; i += 1) {
67
- rows[i].data = loadRowData(rows[i]);
68
- }
69
- }
70
- // sorts the table using the data for the ith column
71
- function sortByIndex(index, desc) {
72
- var key = cols[index].key,
73
- sorter = function (a, b) {
74
- a = a.data[key];
75
- b = b.data[key];
76
- return a < b ? -1 : a > b ? 1 : 0;
77
- },
78
- finalSorter = sorter,
79
- tableBody = document.querySelector('.coverage-summary tbody'),
80
- rowNodes = tableBody.querySelectorAll('tr'),
81
- rows = [],
82
- i;
83
-
84
- if (desc) {
85
- finalSorter = function (a, b) {
86
- return -1 * sorter(a, b);
87
- };
88
- }
89
-
90
- for (i = 0; i < rowNodes.length; i += 1) {
91
- rows.push(rowNodes[i]);
92
- tableBody.removeChild(rowNodes[i]);
93
- }
94
-
95
- rows.sort(finalSorter);
96
-
97
- for (i = 0; i < rows.length; i += 1) {
98
- tableBody.appendChild(rows[i]);
99
- }
100
- }
101
- // removes sort indicators for current column being sorted
102
- function removeSortIndicators() {
103
- var col = getNthColumn(currentSort.index),
104
- cls = col.className;
105
-
106
- cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
107
- col.className = cls;
108
- }
109
- // adds sort indicators for current column being sorted
110
- function addSortIndicators() {
111
- getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted';
112
- }
113
- // adds event listeners for all sorter widgets
114
- function enableUI() {
115
- var i,
116
- el,
117
- ithSorter = function ithSorter(i) {
118
- var col = cols[i];
119
-
120
- return function () {
121
- var desc = col.defaultDescSort;
122
-
123
- if (currentSort.index === i) {
124
- desc = !currentSort.desc;
125
- }
126
- sortByIndex(i, desc);
127
- removeSortIndicators();
128
- currentSort.index = i;
129
- currentSort.desc = desc;
130
- addSortIndicators();
131
- };
132
- };
133
- for (i =0 ; i < cols.length; i += 1) {
134
- if (cols[i].sortable) {
135
- // add the click event handler on the th so users
136
- // dont have to click on those tiny arrows
137
- el = getNthColumn(i).querySelector('.sorter').parentElement;
138
- if (el.addEventListener) {
139
- el.addEventListener('click', ithSorter(i));
140
- } else {
141
- el.attachEvent('onclick', ithSorter(i));
142
- }
143
- }
144
- }
145
- }
146
- // adds sorting functionality to the UI
147
- return function () {
148
- if (!getTable()) {
149
- return;
150
- }
151
- cols = loadColumns();
152
- loadData(cols);
153
- addSortIndicators();
154
- enableUI();
155
- };
156
- })();
157
-
158
- window.addEventListener('load', addSorting);
File without changes