whatap 1.0.4 → 1.0.5-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/core/agent.js CHANGED
@@ -29,7 +29,7 @@ var Interceptor = require('./interceptor').Interceptor,
29
29
  IORedisObserver = require('../observers/ioredis-observer').IORedisObserver,
30
30
  MssqlObserver = require('../observers/mssql-observer').MssqlObserver,
31
31
  PgSqlObserver = require('../observers/pgsql-observer').PgSqlObserver,
32
- // GRpcObserver = require('../observers/grpc-observer').GRpcObserver,
32
+ GRpcObserver = require('../observers/grpc-observer').GRpcObserver,
33
33
  ApolloObserver = require('../observers/apollo-server-observer').ApolloServerObserver,
34
34
  PrismaObserver = require('../observers/prisma-observer').PrismaObserver,
35
35
  OracleObserver = require('../observers/oracle-observer').OracleObserver,
@@ -1105,7 +1105,7 @@ NodeAgent.prototype.loadObserves = function() {
1105
1105
  observes.push(IORedisObserver);
1106
1106
  observes.push(MssqlObserver);
1107
1107
  observes.push(PgSqlObserver);
1108
- // observes.push(GRpcObserver);
1108
+ observes.push(GRpcObserver);
1109
1109
  observes.push(ApolloObserver);
1110
1110
  observes.push(PrismaObserver);
1111
1111
  observes.push(OracleObserver);
@@ -4,10 +4,7 @@
4
4
  * can be found in the LICENSE file.
5
5
  */
6
6
 
7
- var TraceContextManager = require('../trace/trace-context-manager'),
8
- secuMaster = require('../net/security-master'),
9
- conf = require('../conf/configure'),
10
- DateUtil = require('./../util/dateutil'),
7
+ var conf = require('../conf/configure'),
11
8
  Logger = require('../logger'),
12
9
  GCAction = require('../system/gc-action');
13
10
 
@@ -6,15 +6,18 @@
6
6
 
7
7
  var conf = require('../../conf/configure'),
8
8
  TraceContextManager = require('../../trace/trace-context-manager'),
9
- KeyGen = require('../../util/keygen'),
10
- HashUtil = require('../../util/hashutil'),
11
- Logger = require('../../logger'),
12
- Long = require('long');
9
+ Logger = require('../../logger');
13
10
 
14
11
  function ActiveTransaction() {}
15
12
  ActiveTransaction.prototype.getActiveStats = function () {
16
13
  var stats = new Array(5).fill(0);
17
14
  var en = TraceContextManager.getContextEnumeration();
15
+
16
+ if(conf.getProperty('debug_activestats_size_enabled', false)){
17
+ Logger.print("WHATAP-DEBUG-ACTIVESTATS-1", "Size: " + TraceContextManager.size(), false);
18
+ Logger.print("WHATAP-DEBUG-ACTIVESTATS-2", "Pid: " + process.pid, false);
19
+ }
20
+
18
21
  try {
19
22
  while (en.hasMoreElements()) {
20
23
  var ctx = en.nextElement();
@@ -0,0 +1,335 @@
1
+ /**
2
+ * Copyright 2016 the WHATAP project authors. All rights reserved.
3
+ * Use of this source code is governed by a license that
4
+ * can be found in the LICENSE file.
5
+ */
6
+
7
+ var TraceContextManager = require('../trace/trace-context-manager'),
8
+ conf = require('../conf/configure'),
9
+ Logger = require('../logger');
10
+ const HashUtil = require("../util/hashutil");
11
+ const shimmer = require('../core/shimmer');
12
+ const AsyncSender = require('../udp/async_sender');
13
+ const PacketTypeEnum = require('../udp/packet_type_enum');
14
+ const os = require('os');
15
+
16
+ var grpc_profile_enabled = conf.getProperty('grpc_profile_enabled', true);
17
+ var grpc_profile_stream_client_enabled = conf.getProperty('grpc_profile_stream_client_enabled', true);
18
+ var grpc_profile_stream_server_enabled = conf.getProperty('grpc_profile_stream_server_enabled', true);
19
+ var grpc_profile_ignore_method = conf.getProperty('grpc_profile_ignore_method', '');
20
+ var ignore_method_set = null;
21
+ conf.on('grpc_profile_enabled', function(newProperty) {
22
+ grpc_profile_enabled = newProperty;
23
+ });
24
+ conf.on('grpc_profile_stream_client_enabled', function(newProperty) {
25
+ grpc_profile_stream_client_enabled = newProperty;
26
+ });
27
+ conf.on('grpc_profile_stream_server_enabled', function(newProperty) {
28
+ grpc_profile_stream_server_enabled = newProperty;
29
+ });
30
+ conf.on('grpc_profile_ignore_method', function(newProperty) {
31
+ grpc_profile_ignore_method = newProperty;
32
+ });
33
+
34
+ var GRpcObserver = function(agent) {
35
+ this.agent = agent;
36
+ this.packages = ['@grpc/grpc-js'];
37
+ };
38
+
39
+ const types = {
40
+ unary: 'unary',
41
+ client_stream: 'clientStream',
42
+ server_stream: 'serverStream',
43
+ bidi: 'bidi'
44
+ };
45
+
46
+ GRpcObserver.prototype.inject = function(mod, moduleName) {
47
+ if (mod.__whatap_observe__) {
48
+ return;
49
+ }
50
+ mod.__whatap_observe__ = true;
51
+ Logger.initPrint("GRpcObserver");
52
+
53
+ if (grpc_profile_enabled) {
54
+ shimmer.wrap(mod.Server.prototype, 'register', wrapRegister);
55
+ shimmer.wrap(mod.Client.prototype, 'register', wrapRegister);
56
+ }
57
+ };
58
+
59
+ function checkIgnoreMethod(ignore_method, method_name){
60
+ try{
61
+ if (ignore_method) {
62
+ ignore_method_set = new Set(ignore_method.split(','));
63
+ } else {
64
+ ignore_method_set = null;
65
+ }
66
+ if (ignore_method_set && ignore_method_set.has(method_name)) {
67
+ return true;
68
+ }
69
+ }catch (e) {
70
+ Logger.printError('WHATAP-263', 'gRPC checkIgnoreMethod error: ' + e, false);
71
+ }
72
+ return false;
73
+ }
74
+
75
+ function wrapHandler(handler, methodName, type) {
76
+ return function(call, callback) {
77
+ var method_name = methodName.includes('/') ? methodName.substring(methodName.lastIndexOf('/')+1, methodName.length) : methodName;
78
+
79
+ if (!grpc_profile_enabled || checkIgnoreMethod(conf.getProperty('grpc_profile_ignore_method', ''), method_name)) {
80
+ return handler.call(this, call, callback);
81
+ }
82
+
83
+ TraceContextManager._asyncLocalStorage.run(initCtx(call, methodName), () => {
84
+ var ctx = TraceContextManager._asyncLocalStorage.getStore();
85
+ if (!ctx) {
86
+ return handler.call(this, call, callback);
87
+ }
88
+
89
+ try {
90
+ // TX_START: 트랜잭션 시작
91
+ let startDatas = [
92
+ os.hostname(),
93
+ methodName,
94
+ ctx.remoteIp || '0.0.0.0',
95
+ '', // userAgent
96
+ '', // referer
97
+ String(ctx.userid || 0),
98
+ 'false', // isStaticContents
99
+ method_name
100
+ ];
101
+ AsyncSender.send_packet(PacketTypeEnum.TX_START, ctx, startDatas);
102
+
103
+ // TX_MSG: Type 정보
104
+ let typeDatas = ['Type', 'Type', type];
105
+ AsyncSender.send_packet(PacketTypeEnum.TX_MSG, ctx, typeDatas);
106
+
107
+ // TX_MSG: Method 정보
108
+ let methodDatas = ['Method', 'Method', method_name];
109
+ AsyncSender.send_packet(PacketTypeEnum.TX_MSG, ctx, methodDatas);
110
+
111
+ // TX_MSG: Parameter 정보
112
+ if (call.request && Object.keys(call.request).length > 0) {
113
+ let paramDatas = ['Parameter', 'Parameter', JSON.stringify(call.request)];
114
+ AsyncSender.send_packet(PacketTypeEnum.TX_MSG, ctx, paramDatas);
115
+ }
116
+
117
+ function wrappedCallback(err, response, ctx) {
118
+ if (err) {
119
+ // TX_ERROR: 에러 발생
120
+ let errors = [
121
+ err.name || err.constructor?.name || 'GrpcError',
122
+ err.message || 'Unknown error'
123
+ ];
124
+ AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
125
+ ctx.status = 500;
126
+ } else {
127
+ ctx.status = 200;
128
+ }
129
+
130
+ endTransaction(ctx);
131
+
132
+ if (typeof callback === 'function') {
133
+ return callback(err, response);
134
+ }
135
+ }
136
+
137
+ return handler.call(this, call, (err, response) => wrappedCallback(err, response, ctx));
138
+ } catch (err) {
139
+ Logger.printError('WHATAP-261', 'gRPC wrapHandler error: ' + err, false);
140
+
141
+ // TX_ERROR: 예외 발생
142
+ let errors = [
143
+ err.name || err.constructor?.name || 'GrpcError',
144
+ err.message || 'Unknown error'
145
+ ];
146
+ AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
147
+
148
+ endTransaction(ctx);
149
+
150
+ return handler.call(this, call, callback);
151
+ }
152
+ });
153
+ };
154
+ }
155
+
156
+ function wrapStreamHandler(handler, methodName, type) {
157
+ return function(call, callback) {
158
+ var method_name = methodName.includes('/') ? methodName.substring(methodName.lastIndexOf('/')+1, methodName.length) : methodName;
159
+
160
+ if (!grpc_profile_enabled || checkIgnoreMethod(conf.getProperty('grpc_profile_ignore_method', ''), method_name)) {
161
+ return handler.call(this, call, callback);
162
+ }
163
+
164
+ switch (type) {
165
+ case 'serverStream':
166
+ if (!grpc_profile_stream_server_enabled) {
167
+ return handler.call(this, call, callback);
168
+ }
169
+ break;
170
+ case 'clientStream':
171
+ if (!grpc_profile_stream_client_enabled) {
172
+ return handler.call(this, call, callback);
173
+ }
174
+ break;
175
+ case 'bidi':
176
+ if (!grpc_profile_stream_server_enabled || !grpc_profile_stream_client_enabled) {
177
+ return handler.call(this, call, callback);
178
+ }
179
+ break;
180
+ }
181
+
182
+ TraceContextManager._asyncLocalStorage.run(initCtx(call, methodName), () => {
183
+ var ctx = TraceContextManager._asyncLocalStorage.getStore();
184
+ if (!ctx) {
185
+ return handler.call(this, call, callback);
186
+ }
187
+
188
+ try {
189
+ // TX_START: 트랜잭션 시작
190
+ let startDatas = [
191
+ os.hostname(),
192
+ methodName,
193
+ ctx.remoteIp || '0.0.0.0',
194
+ '', // userAgent
195
+ '', // referer
196
+ String(ctx.userid || 0),
197
+ 'false', // isStaticContents
198
+ method_name
199
+ ];
200
+ AsyncSender.send_packet(PacketTypeEnum.TX_START, ctx, startDatas);
201
+
202
+ // TX_MSG: Type 정보
203
+ let typeDatas = ['Type', 'Type', type];
204
+ AsyncSender.send_packet(PacketTypeEnum.TX_MSG, ctx, typeDatas);
205
+
206
+ // TX_MSG: Method 정보
207
+ let methodDatas = ['Method', 'Method', method_name];
208
+ AsyncSender.send_packet(PacketTypeEnum.TX_MSG, ctx, methodDatas);
209
+
210
+ ctx.status = 200;
211
+
212
+ call.on('end', () => {
213
+ endTransaction(ctx);
214
+ });
215
+
216
+ call.once('cancelled', () => {
217
+ // TX_MSG: Cancelled 정보
218
+ let cancelDatas = ['Cancelled', 'Cancelled', 'Request cancelled'];
219
+ AsyncSender.send_packet(PacketTypeEnum.TX_MSG, ctx, cancelDatas);
220
+ ctx.status = 499; // Client Closed Request
221
+ endTransaction(ctx);
222
+ });
223
+
224
+ call.on('error', (err) => {
225
+ // TX_ERROR: 에러 발생
226
+ let errors = [
227
+ err.name || err.constructor?.name || 'GrpcError',
228
+ err.message || 'Unknown error'
229
+ ];
230
+ AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
231
+ ctx.status = 500;
232
+ endTransaction(ctx);
233
+ });
234
+
235
+ return handler.call(this, call);
236
+ } catch (e) {
237
+ Logger.printError('WHATAP-262', 'gRPC wrapStreamHandler error: ' + e, false);
238
+
239
+ // TX_ERROR: 예외 발생
240
+ let errors = [
241
+ e.name || e.constructor?.name || 'GrpcError',
242
+ e.message || 'Unknown error'
243
+ ];
244
+ AsyncSender.send_packet(PacketTypeEnum.TX_ERROR, ctx, errors);
245
+
246
+ endTransaction(ctx);
247
+
248
+ return handler.call(this, call);
249
+ }
250
+ });
251
+ };
252
+ }
253
+
254
+ function wrapRegister(register) {
255
+ return function(name, handler, serialize, deserialize, type) {
256
+ if (typeof handler === 'function') {
257
+ switch (type) {
258
+ case types.client_stream:
259
+ // Client Streaming은 callback이 필요하므로 wrapHandler 사용
260
+ handler = wrapHandler(handler, name, 'clientStream');
261
+ break;
262
+ case types.server_stream:
263
+ handler = wrapStreamHandler(handler, name, 'serverStream');
264
+ break;
265
+ case types.bidi:
266
+ handler = wrapStreamHandler(handler, name, 'bidi');
267
+ break;
268
+ case types.unary:
269
+ default:
270
+ handler = wrapHandler(handler, name, 'unary');
271
+ }
272
+ }
273
+ return register.call(this, name, handler, serialize, deserialize, type);
274
+ };
275
+ }
276
+
277
+ function endTransaction(ctx) {
278
+ if (!ctx || TraceContextManager.isExist(ctx.id) === false) {
279
+ return;
280
+ }
281
+
282
+ try {
283
+ ctx.elapsed = Date.now() - ctx.start_time;
284
+
285
+ // TX_END: 트랜잭션 종료
286
+ let endDatas = [
287
+ os.hostname(),
288
+ ctx.service_name,
289
+ ctx.mtid || 0,
290
+ ctx.mdepth || 0,
291
+ ctx.mcaller_txid || 0,
292
+ ctx.mcaller_pcode || 0,
293
+ ctx.mcaller_spec || '',
294
+ String(ctx.mcaller_url_hash || 0),
295
+ ctx.status || 0
296
+ ];
297
+ AsyncSender.send_packet(PacketTypeEnum.TX_END, ctx, endDatas);
298
+
299
+ TraceContextManager.end(ctx.id);
300
+ } catch (e) {
301
+ Logger.printError('WHATAP-265', 'gRPC end transaction error: ' + e, false);
302
+ TraceContextManager.end(ctx.id);
303
+ ctx = null;
304
+ }
305
+ }
306
+
307
+ function initCtx(call, methodName) {
308
+ if (!grpc_profile_enabled) {
309
+ return null;
310
+ }
311
+
312
+ const ctx = TraceContextManager.start();
313
+ if (!ctx) {
314
+ return null;
315
+ }
316
+
317
+ // gRPC 메타데이터에서 정보 추출
318
+ try {
319
+ if (call.metadata) {
320
+ // Remote IP 추출 시도
321
+ const metadata = call.metadata.getMap();
322
+ ctx.remoteIp = metadata['x-forwarded-for'] || metadata['x-real-ip'] || '0.0.0.0';
323
+ }
324
+ } catch (e) {
325
+ Logger.printError('WHATAP-264', 'gRPC metadata parsing error: ' + e, false);
326
+ }
327
+
328
+ ctx.service_name = methodName;
329
+ ctx.service_hash = HashUtil.hashFromString(methodName);
330
+ ctx.start_time = Date.now();
331
+
332
+ return ctx;
333
+ }
334
+
335
+ exports.GRpcObserver = GRpcObserver;
@@ -145,6 +145,31 @@ HttpObserver.prototype.__createTransactionObserver = function (callback, isHttps
145
145
  self.__endTransaction(null, ctx, req, res);
146
146
  });
147
147
 
148
+ // 브라우저가 닫히면 진행 중인 외부 HTTP 요청도 정리하고 TX_HTTPC, TX_END 전송
149
+ res.on('close', function () {
150
+ if (ctx == null) {
151
+ return;
152
+ }
153
+
154
+ // 진행 중인 모든 외부 HTTP 요청에 대해 TX_HTTPC 전송
155
+ // if (ctx.activeHttpcList && ctx.activeHttpcList.length > 0) {
156
+ // ctx.activeHttpcList.forEach(function(httpcInfo) {
157
+ // try {
158
+ // let urls = `${httpcInfo.host}:${httpcInfo.port}${httpcInfo.url}`;
159
+ // let httpcDatas = [urls, httpcInfo.mcallee];
160
+ // ctx.elapsed = Date.now() - httpcInfo.start_time;
161
+ // TraceHttpc.isSlowHttpc(ctx);
162
+ // AsyncSender.send_packet(PacketTypeEnum.TX_HTTPC, ctx, httpcDatas);
163
+ // } catch (e) {
164
+ // Logger.printError('WHATAP-243', 'Failed to send TX_HTTPC on close', e, false);
165
+ // }
166
+ // });
167
+ // ctx.activeHttpcList = [];
168
+ // }
169
+
170
+ self.__endTransaction(null, ctx, req, res);
171
+ });
172
+
148
173
  try {
149
174
  AsyncSender.send_packet(PacketTypeEnum.TX_START, ctx, datas)
150
175
  return callback.apply(this, arguments);
@@ -181,6 +206,9 @@ function initCtx(req, res) {
181
206
  return null;
182
207
  }
183
208
 
209
+ // 진행 중인 외부 HTTP 요청 정보를 저장 (여러 개 동시 추적)
210
+ ctx.activeHttpcList = [];
211
+
184
212
  // RemoteIP
185
213
  var remote_addr;
186
214
  try {
@@ -495,10 +523,20 @@ HttpObserver.prototype.inject = function (mod, moduleName) {
495
523
  ctx.httpc_host = options.host || options.hostname || '';
496
524
  ctx.httpc_port = options.port || -1;
497
525
  ctx.active_httpc_hash = true;
498
-
526
+
499
527
  if (ctx.httpc_port < 0) {
500
528
  ctx.httpc_port = 80
501
529
  }
530
+
531
+ // Send TX_HTTPC at start if enabled (for lost connection cases)
532
+ if (conf.getProperty('profile_httpc_start_step_enabled', false)) {
533
+ ctx.active_httpc_hash = false;
534
+ let urls = `${ctx.httpc_host}:${ctx.httpc_port}${ctx.httpc_url}`;
535
+ let httpcDatas = [urls, ctx.mcallee];
536
+ ctx.elapsed = 0;
537
+ TraceHttpc.isSlowHttpc(ctx);
538
+ AsyncSender.send_packet(PacketTypeEnum.TX_HTTPC, ctx, httpcDatas);
539
+ }
502
540
  } catch (e) {
503
541
  Logger.printError('WHATAP-240', 'Http Setup Error', e, false);
504
542
  return original.apply(this, arguments);
@@ -507,6 +545,30 @@ HttpObserver.prototype.inject = function (mod, moduleName) {
507
545
  return original.apply(this, arguments);
508
546
  }
509
547
 
548
+ // 진행 중인 요청 정보 저장 (여러 개 추적)
549
+ const httpcInfo = {
550
+ host: ctx.httpc_host,
551
+ port: ctx.httpc_port,
552
+ url: ctx.httpc_url,
553
+ mcallee: ctx.mcallee,
554
+ start_time: ctx.start_time
555
+ };
556
+
557
+ // 배열에 추가
558
+ if (ctx.activeHttpcList) {
559
+ ctx.activeHttpcList.push(httpcInfo);
560
+ }
561
+
562
+ // 배열에서 제거하는 헬퍼 함수
563
+ const removeHttpcInfo = function() {
564
+ if (ctx.activeHttpcList) {
565
+ const index = ctx.activeHttpcList.indexOf(httpcInfo);
566
+ if (index > -1) {
567
+ ctx.activeHttpcList.splice(index, 1);
568
+ }
569
+ }
570
+ };
571
+
510
572
  var wrappedCallback;
511
573
  if (typeof callback === 'function') {
512
574
  wrappedCallback = function (response) {
@@ -515,7 +577,10 @@ HttpObserver.prototype.inject = function (mod, moduleName) {
515
577
  }
516
578
 
517
579
  response.on('end', function () {
518
- // HTTP 클라이언트 에러 처리 - Python 스타일로 단순화
580
+ // 배열에서 제거
581
+ removeHttpcInfo();
582
+
583
+ // HTTP 클라이언트 에러 처리
519
584
  if (response.statusCode >= 400 && transaction_status_error_enable) {
520
585
  let error = {
521
586
  class: response.statusMessage,
@@ -538,6 +603,9 @@ HttpObserver.prototype.inject = function (mod, moduleName) {
538
603
  return;
539
604
  }
540
605
 
606
+ // 배열에서 제거
607
+ removeHttpcInfo();
608
+
541
609
  const isIgnoreHttpc = shouldIgnoreError(err.code, ctx.httpc_url, httpc_status_ignore, httpc_status_ignore_set);
542
610
 
543
611
  const networkErrorToStatusCode = {
@@ -574,7 +642,6 @@ HttpObserver.prototype.inject = function (mod, moduleName) {
574
642
  bodyData = arguments[0].toString('utf8');
575
643
  }
576
644
  if (bodyData) {
577
- console.log(`body Data: ${bodyData}`)
578
645
  let paramDatas = ['HTTP-PARAMETERS', options.method, bodyData];
579
646
  AsyncSender.send_packet(PacketTypeEnum.TX_SECURE_MSG, ctx, paramDatas);
580
647
  }
@@ -591,12 +658,16 @@ HttpObserver.prototype.inject = function (mod, moduleName) {
591
658
  }
592
659
  });
593
660
 
594
- // HTTP 클라이언트 종료 처리 - 단순화
595
661
  function endHttpc(ctx) {
596
662
  if (ctx == null) {
597
663
  return;
598
664
  }
599
665
 
666
+ // If profile_httpc_start_step_enabled is true, TX_HTTPC was already sent at start
667
+ if (conf.getProperty('profile_httpc_start_step_enabled', false)) {
668
+ return;
669
+ }
670
+
600
671
  ctx.active_httpc_hash = false;
601
672
  let urls = `${ctx.httpc_host}:${ctx.httpc_port}${ctx.httpc_url}`;
602
673
  let httpcDatas = [urls, ctx.mcallee];
@@ -509,7 +509,7 @@ SocketIOObserver.prototype.__endTransaction = function(error, ctx) {
509
509
  AsyncSender.send_packet(PacketTypeEnum.TX_END, ctx, datas);
510
510
 
511
511
  TraceContextManager.end(ctx.id);
512
-
512
+ ctx = null;
513
513
  } catch (e) {
514
514
  Logger.printError('WHATAP-231', 'Socket.io end transaction error', e, false);
515
515
  TraceContextManager.end(ctx.id);
@@ -378,7 +378,7 @@ WebsocketObserver.prototype.__endTransaction = function(error, ctx) {
378
378
  ctx.elapsed = Date.now() - ctx.start_time;
379
379
  AsyncSender.send_packet(PacketTypeEnum.TX_END, ctx, datas);
380
380
  TraceContextManager.end(ctx.id);
381
-
381
+ ctx = null;
382
382
  } catch (e) {
383
383
  Logger.printError('WHATAP-222', 'WebSocket end transaction error', e, false);
384
384
  TraceContextManager.end(ctx.id);
@@ -5,7 +5,6 @@
5
5
  */
6
6
 
7
7
  var IntKeyMap = require('../util/intkey-map'),
8
- HashUtil = require('../util/hashutil'),
9
8
  TraceContext = require('./trace-context'),
10
9
  Logger = require('../logger');
11
10
  var Long = require('long');
@@ -19,7 +18,7 @@ function TraceContextManager() {
19
18
  this.nextId = 1;
20
19
  this.currentId = null;
21
20
  this._asyncLocalStorage = new AsyncLocalStorage();
22
- this.entry = new IntKeyMap(1021,1).setMax(5000);
21
+ this.entry = new IntKeyMap(1021,1).setMax(10000);
23
22
  this.node = null;
24
23
  this.clock_seq = null;
25
24
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "whatap",
3
3
  "homepage": "http://www.whatap.io",
4
- "version": "1.0.4",
5
- "releaseDate": "20251016",
4
+ "version": "1.0.5-canary.0",
5
+ "releaseDate": "20251029",
6
6
  "description": "Monitoring and Profiling Service",
7
7
  "main": "index.js",
8
8
  "scripts": {},