@trap_stevo/filetide 0.0.66 → 0.0.68

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.
@@ -19,6 +19,10 @@ const {
19
19
  const {
20
20
  FileTransferManager
21
21
  } = require("./HUDManagers/FileTransferManager");
22
+ const {
23
+ aggregateMultiAverageChartData,
24
+ aggregateChartData
25
+ } = require("./HUDManagers/TidalyticsUtilityManager");
22
26
  const TideSentinelManager = require("./HUDManagers/TideSentinelManager");
23
27
  const HUDTargetQueue = require("./HUDManagers/HUDTargetQueueManager");
24
28
  const {
@@ -132,6 +136,899 @@ class FileMessager {
132
136
  clearAllTidalytics() {
133
137
  return tidalytics.clearAllMetrics();
134
138
  }
139
+ attachTiderPresenceVisualStream(options = {}) {
140
+ const {
141
+ visualDataConfigurations = {},
142
+ mode = "buffer",
143
+ forward
144
+ } = options;
145
+ if (!forward) {
146
+ return;
147
+ }
148
+ async function handle() {
149
+ if (mode === "signal") {
150
+ forward("rehydrate");
151
+ return;
152
+ }
153
+ const result = await this.getTiderPresenceVisualData(visualDataConfigurations);
154
+ forward(result);
155
+ }
156
+ tidalytics.events.on("metric:created:tider:offline", handle);
157
+ tidalytics.events.on("metric:created:tider:online", handle);
158
+ }
159
+ attachAlreadyOnlineVisualStream(options = {}) {
160
+ const {
161
+ visualDataConfigurations = {},
162
+ mode = "buffer",
163
+ forward
164
+ } = options;
165
+ if (!forward) {
166
+ return;
167
+ }
168
+ async function handle() {
169
+ if (mode === "signal") {
170
+ forward("rehydrate");
171
+ return;
172
+ }
173
+ const result = await this.getAlreadyOnlineVisualData(visualDataConfigurations);
174
+ forward(result);
175
+ }
176
+ tidalytics.events.on("metric:created:tider:already-online", handle);
177
+ }
178
+ attachTiderBanVisualStream(options = {}) {
179
+ const {
180
+ visualDataConfigurations = {},
181
+ mode = "buffer",
182
+ forward
183
+ } = options;
184
+ if (!forward) {
185
+ return;
186
+ }
187
+ async function handle() {
188
+ if (mode === "signal") {
189
+ forward("rehydrate");
190
+ return;
191
+ }
192
+ const result = await this.getTiderBanVisualData(visualDataConfigurations);
193
+ forward(result);
194
+ }
195
+ tidalytics.events.on("metric:created:tider:unbanned", handle);
196
+ tidalytics.events.on("metric:created:tider:banned", handle);
197
+ tidalytics.events.on("metric:created:tider:kicked", handle);
198
+ }
199
+ attachTideFileTransferVisualStream(options = {}) {
200
+ const {
201
+ visualDataConfigurations = {},
202
+ mode = "buffer",
203
+ forward
204
+ } = options;
205
+ if (!forward) {
206
+ return;
207
+ }
208
+ async function handle() {
209
+ if (mode === "signal") {
210
+ forward("rehydrate");
211
+ return;
212
+ }
213
+ const result = await this.getTideFileTransferVisualData(visualDataConfigurations);
214
+ forward(result);
215
+ }
216
+ tidalytics.events.on("metric:created:tide:completed-transfer", handle);
217
+ tidalytics.events.on("metric:created:tide:incoming-transfer", handle);
218
+ tidalytics.events.on("metric:created:tide:incoming-file", handle);
219
+ }
220
+ attachTideTransferStateVisualStream(options = {}) {
221
+ const {
222
+ visualDataConfigurations = {},
223
+ mode = "buffer",
224
+ forward
225
+ } = options;
226
+ if (!forward) {
227
+ return;
228
+ }
229
+ async function handle() {
230
+ if (mode === "signal") {
231
+ forward("rehydrate");
232
+ return;
233
+ }
234
+ const result = await this.getTideTransferStateVisualData(visualDataConfigurations);
235
+ forward(result);
236
+ }
237
+ tidalytics.events.on("metric:created:tide:transfer-state", handle);
238
+ }
239
+ attachFileListVisualStream(options = {}) {
240
+ const {
241
+ visualDataConfigurations = {},
242
+ mode = "buffer",
243
+ forward
244
+ } = options;
245
+ if (!forward) {
246
+ return;
247
+ }
248
+ async function handle() {
249
+ if (mode === "signal") {
250
+ forward("rehydrate");
251
+ return;
252
+ }
253
+ const result = await this.getFileListVisualData(visualDataConfigurations);
254
+ forward(result);
255
+ }
256
+ tidalytics.events.on("metric:created:file-list:received", handle);
257
+ tidalytics.events.on("metric:created:file-list:request", handle);
258
+ }
259
+ attachTideBarrierVisualStream(options = {}) {
260
+ const {
261
+ visualDataConfigurations = {},
262
+ mode = "buffer",
263
+ forward
264
+ } = options;
265
+ if (!forward) {
266
+ return;
267
+ }
268
+ async function handle() {
269
+ if (mode === "signal") {
270
+ forward("rehydrate");
271
+ return;
272
+ }
273
+ const result = await this.getTideBarrierVisualData(visualDataConfigurations);
274
+ forward(result);
275
+ }
276
+ tidalytics.events.on("metric:created:tide-barrier:accepted", handle);
277
+ tidalytics.events.on("metric:created:tide-barrier:denied", handle);
278
+ }
279
+ async getTiderPresenceVisualData(options = {}) {
280
+ const {
281
+ interval = "1d",
282
+ limit = Infinity,
283
+ filter = {},
284
+ mode = "all"
285
+ } = options;
286
+ const sources = ["storage", "memory"];
287
+ const [storedOnline, storedOffline] = await Promise.all([this.getTidalyticsSince(interval, sources[0], {
288
+ ...filter,
289
+ tags: {
290
+ "tider:online": "tider:online"
291
+ }
292
+ }), this.getTidalyticsSince(interval, sources[0], {
293
+ ...filter,
294
+ tags: {
295
+ "tider:offline": "tider:offline"
296
+ }
297
+ })]);
298
+ const [liveOnline] = await Promise.all([this.getTidalyticsSince(interval, sources[1], {
299
+ ...filter,
300
+ tags: {
301
+ "tider:online": "tider:online"
302
+ }
303
+ })]);
304
+ const onlineMetrics = [...storedOnline, ...liveOnline];
305
+ const offlineMetrics = storedOffline;
306
+ const sessionsByKey = {};
307
+ for (const event of onlineMetrics) {
308
+ const user = event.metadata?.user;
309
+ const connectionID = event.metadata?.connectionID;
310
+ const connectedOn = event.metadata?.connectedOn || event.timestamp;
311
+ const key = `${user}_${connectionID}`;
312
+ sessionsByKey[key] = {
313
+ sessionDuration: null,
314
+ disconnectedOn: null,
315
+ connectionID,
316
+ connectedOn,
317
+ user
318
+ };
319
+ }
320
+ for (const event of offlineMetrics) {
321
+ const user = event.metadata?.user;
322
+ const connectionID = event.metadata?.connectionID;
323
+ const disconnectedOn = event.timestamp;
324
+ const key = `${user}_${connectionID}`;
325
+ if (sessionsByKey[key]) {
326
+ sessionsByKey[key].sessionDuration = disconnectedOn - sessionsByKey[key].connectedOn;
327
+ sessionsByKey[key].disconnectedOn = disconnectedOn;
328
+ }
329
+ }
330
+ let allSessions = Object.values(sessionsByKey);
331
+ if (mode === "online") {
332
+ allSessions = allSessions.filter(session => session.disconnectedOn === null);
333
+ } else if (mode === "offline") {
334
+ allSessions = allSessions.filter(session => session.disconnectedOn !== null);
335
+ }
336
+ return limit === Infinity ? allSessions : allSessions.slice(-limit).reverse();
337
+ }
338
+ async getAlreadyOnlineVisualData(options = {}) {
339
+ const {
340
+ interval = "1d",
341
+ filter = {},
342
+ limit = Infinity,
343
+ format = "raw",
344
+ chartConfig = {}
345
+ } = options;
346
+ const fullChartConfig = {
347
+ groupBy: "metadata.user",
348
+ mode: "count",
349
+ valueKey: "value",
350
+ metadataFields: null,
351
+ previewLimit: 3,
352
+ ...chartConfig
353
+ };
354
+ const sources = ["storage", "memory"];
355
+ const [alreadyOnlineEvents, onlineJoins] = await Promise.all([this.getTidalyticsSince(interval, sources[0], {
356
+ ...filter,
357
+ tags: {
358
+ "tider:already-online": "tider:already-online"
359
+ }
360
+ }), this.getTidalyticsSince(interval, sources[0], {
361
+ ...filter,
362
+ tags: {
363
+ "tider:online": "tider:online"
364
+ }
365
+ })]);
366
+ const entries = alreadyOnlineEvents.map(e => ({
367
+ ...e,
368
+ type: "already-online"
369
+ })).sort((a, b) => b.timestamp - a.timestamp);
370
+ const joinCountsByUser = {};
371
+ for (const evt of onlineJoins) {
372
+ const user = evt.metadata?.user;
373
+ if (!user) {
374
+ continue;
375
+ }
376
+ joinCountsByUser[user] = (joinCountsByUser[user] || 0) + 1;
377
+ }
378
+ if (format === "chart") {
379
+ const result = fullChartConfig.mode === "average" ? aggregateMultiAverageChartData(entries, {
380
+ ...fullChartConfig,
381
+ groupBy: [].concat(fullChartConfig.groupBy)
382
+ }) : aggregateChartData(entries, {
383
+ ...fullChartConfig,
384
+ typeKey: "type"
385
+ });
386
+ return result.map(entry => {
387
+ const userKey = entry.label;
388
+ const totalJoins = joinCountsByUser[userKey] || 0;
389
+ return {
390
+ ...entry,
391
+ totalJoins,
392
+ collisionRate: totalJoins > 0 ? +(entry.count / totalJoins).toFixed(3) : 0
393
+ };
394
+ });
395
+ }
396
+ return limit === Infinity ? entries : entries.slice(0, limit);
397
+ }
398
+ async getTiderBanVisualData(options = {}) {
399
+ const {
400
+ interval = "1d",
401
+ filter = {},
402
+ limit = Infinity,
403
+ format = "raw",
404
+ chartConfig = {}
405
+ } = options;
406
+ const fullChartConfig = {
407
+ groupBy: "type",
408
+ mode: "count",
409
+ ...chartConfig
410
+ };
411
+ const sources = ["storage", "memory"];
412
+ const [banned, kicked, unbanned] = await Promise.all([this.getTidalyticsSince(interval, sources[0], {
413
+ ...filter,
414
+ tags: {
415
+ "tider:banned": "tider:banned"
416
+ }
417
+ }), this.getTidalyticsSince(interval, sources[0], {
418
+ ...filter,
419
+ tags: {
420
+ "tider:kicked": "tider:kicked"
421
+ }
422
+ }), this.getTidalyticsSince(interval, sources[0], {
423
+ ...filter,
424
+ tags: {
425
+ "tider:unbanned": "tider:unbanned"
426
+ }
427
+ })]);
428
+ const entries = [...banned.map(e => ({
429
+ ...e,
430
+ type: "banned"
431
+ })), ...kicked.map(e => ({
432
+ ...e,
433
+ type: "kicked"
434
+ })), ...unbanned.map(e => ({
435
+ ...e,
436
+ type: "unbanned"
437
+ }))].sort((a, b) => b.timestamp - a.timestamp);
438
+ if (format === "chart") {
439
+ return fullChartConfig.mode === "average" ? aggregateMultiAverageChartData(entries, {
440
+ ...fullChartConfig,
441
+ groupBy: [].concat(fullChartConfig.groupBy)
442
+ }) : aggregateChartData(entries, {
443
+ ...fullChartConfig,
444
+ typeKey: "type"
445
+ });
446
+ }
447
+ return limit === Infinity ? entries : entries.slice(0, limit);
448
+ }
449
+ async getTideFileTransferVisualData(options = {}) {
450
+ const {
451
+ interval = "1d",
452
+ filter = {},
453
+ limit = Infinity,
454
+ format = "raw",
455
+ chartConfig = {}
456
+ } = options;
457
+ const fullChartConfig = {
458
+ groupBy: "type",
459
+ mode: "count",
460
+ ...chartConfig
461
+ };
462
+ const sources = ["storage", "memory"];
463
+ const [incomingTransfers, incoming, completed] = await Promise.all([this.getTidalyticsSince(interval, sources[0], {
464
+ ...filter,
465
+ tags: {
466
+ "tide:incoming-transfer": "tide:incoming-transfer"
467
+ }
468
+ }), this.getTidalyticsSince(interval, sources[0], {
469
+ ...filter,
470
+ tags: {
471
+ "tide:incoming-file": "tide:incoming-file"
472
+ }
473
+ }), this.getTidalyticsSince(interval, sources[0], {
474
+ ...filter,
475
+ tags: {
476
+ "tide:completed-transfer": "tide:completed-transfer"
477
+ }
478
+ })]);
479
+ const entries = [...incomingTransfers.map(e => ({
480
+ ...e,
481
+ type: "incoming-transfer"
482
+ })), ...incoming.map(e => ({
483
+ ...e,
484
+ type: "incoming"
485
+ })), ...completed.map(e => ({
486
+ ...e,
487
+ type: "completed"
488
+ }))].sort((a, b) => b.timestamp - a.timestamp);
489
+ if (format === "chart") {
490
+ return fullChartConfig.mode === "average" ? aggregateMultiAverageChartData(entries, {
491
+ ...fullChartConfig,
492
+ groupBy: [].concat(fullChartConfig.groupBy)
493
+ }) : aggregateChartData(entries, {
494
+ ...fullChartConfig,
495
+ typeKey: "type"
496
+ });
497
+ }
498
+ return limit === Infinity ? entries : entries.slice(0, limit);
499
+ }
500
+ async getTideTransferStateVisualData(options = {}) {
501
+ const {
502
+ interval = "1d",
503
+ filter = {},
504
+ limit = Infinity,
505
+ format = "raw",
506
+ chartConfig = {}
507
+ } = options;
508
+ const fullChartConfig = {
509
+ groupBy: "metadata.transferStateID",
510
+ mode: "average",
511
+ valueKey: "metadata.speed",
512
+ metadataFields: ["senderID", "recipientID", "transferProgress", "speed", "transferETA", "fileName"],
513
+ previewLimit: 3,
514
+ ...chartConfig
515
+ };
516
+ const sources = ["storage", "memory"];
517
+ const [transferStates] = await Promise.all([this.getTidalyticsSince(interval, sources[0], {
518
+ ...filter,
519
+ tags: {
520
+ "tide:transfer-state": "tide:transfer-state"
521
+ }
522
+ })]);
523
+ const entries = transferStates.map(e => ({
524
+ ...e,
525
+ type: "transfer-state"
526
+ })).sort((a, b) => b.timestamp - a.timestamp);
527
+ if (format === "chart") {
528
+ return fullChartConfig.mode === "average" ? aggregateMultiAverageChartData(entries, {
529
+ ...fullChartConfig,
530
+ groupBy: [].concat(fullChartConfig.groupBy)
531
+ }) : aggregateChartData(entries, {
532
+ ...fullChartConfig,
533
+ typeKey: "type"
534
+ });
535
+ }
536
+ return limit === Infinity ? entries : entries.slice(0, limit);
537
+ }
538
+ async getFileListActivityVisualData(options = {}) {
539
+ const {
540
+ interval = "1d",
541
+ filter = {},
542
+ limit = Infinity,
543
+ format = "raw",
544
+ chartConfig = {}
545
+ } = options;
546
+ const fullChartConfig = {
547
+ groupBy: "type",
548
+ mode: "count",
549
+ ...chartConfig
550
+ };
551
+ const sources = ["storage", "memory"];
552
+ const [requested, received] = await Promise.all([this.getTidalyticsSince(interval, sources[0], {
553
+ ...filter,
554
+ tags: {
555
+ "file-list:request": "file-list:request"
556
+ }
557
+ }), this.getTidalyticsSince(interval, sources[0], {
558
+ ...filter,
559
+ tags: {
560
+ "file-list:received": "file-list:received"
561
+ }
562
+ })]);
563
+ const entries = [...requested.map(e => ({
564
+ ...e,
565
+ type: "requested"
566
+ })), ...received.map(e => ({
567
+ ...e,
568
+ type: "received"
569
+ }))].sort((a, b) => b.timestamp - a.timestamp);
570
+ if (format === "chart") {
571
+ return fullChartConfig.mode === "average" ? aggregateMultiAverageChartData(entries, {
572
+ ...fullChartConfig,
573
+ groupBy: [].concat(fullChartConfig.groupBy)
574
+ }) : aggregateChartData(entries, {
575
+ ...fullChartConfig,
576
+ typeKey: "type"
577
+ });
578
+ }
579
+ return limit === Infinity ? entries : entries.slice(0, limit);
580
+ }
581
+ async getTideBarrierVisualData(options = {}) {
582
+ const {
583
+ interval = "1d",
584
+ filter = {},
585
+ limit = Infinity,
586
+ format = "raw",
587
+ chartConfig = {}
588
+ } = options;
589
+ const fullChartConfig = {
590
+ groupBy: "type",
591
+ mode: "count",
592
+ ...chartConfig
593
+ };
594
+ const sources = ["storage", "memory"];
595
+ const [accepted, denied] = await Promise.all([this.getTidalyticsSince(interval, sources[0], {
596
+ ...filter,
597
+ tags: {
598
+ "tide-barrier:accepted": "tide-barrier:accepted"
599
+ }
600
+ }), this.getTidalyticsSince(interval, sources[0], {
601
+ ...filter,
602
+ tags: {
603
+ "tide-barrier:denied": "tide-barrier:denied"
604
+ }
605
+ })]);
606
+ const entries = [...accepted.map(e => ({
607
+ ...e,
608
+ type: "accepted"
609
+ })), ...denied.map(e => ({
610
+ ...e,
611
+ type: "denied"
612
+ }))].sort((a, b) => b.timestamp - a.timestamp);
613
+ if (format === "chart") {
614
+ return fullChartConfig.mode === "average" ? aggregateMultiAverageChartData(entries, {
615
+ ...fullChartConfig,
616
+ groupBy: [].concat(fullChartConfig.groupBy)
617
+ }) : aggregateChartData(entries, {
618
+ ...fullChartConfig,
619
+ typeKey: "type"
620
+ });
621
+ }
622
+ return limit === Infinity ? entries : entries.slice(0, limit);
623
+ }
624
+
625
+ // --- File Messager Tide Actions Management ---
626
+
627
+ async getTideCompletedTransferActions({
628
+ interval = "1d",
629
+ limit = 50,
630
+ source = "storage",
631
+ filter = {}
632
+ } = {}) {
633
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
634
+ ...filter,
635
+ tags: {
636
+ "tide:completed-transfer": "tide:completed-transfer"
637
+ }
638
+ });
639
+ if (!Array.isArray(rawMetrics)) {
640
+ return [];
641
+ }
642
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
643
+ return limitedMetrics.map(event => ({
644
+ tideActionID: "tide-completed-transfer",
645
+ tideActionDate: event.timestamp || Date.now(),
646
+ tideActionDetails: {
647
+ recipientID: event.metadata?.recipientID,
648
+ senderID: event.metadata?.senderID,
649
+ path: event.metadata?.path,
650
+ fileSize: event.metadata?.fileSize,
651
+ fileName: event.metadata?.fileName,
652
+ completedChunks: event.metadata?.completedChunks,
653
+ totalChunks: event.metadata?.totalChunks,
654
+ chunkSize: event.metadata?.chunkSize
655
+ }
656
+ }));
657
+ }
658
+ async getTideTransferStateActions({
659
+ interval = "1d",
660
+ limit = Infinity,
661
+ source = "storage",
662
+ filter = {}
663
+ } = {}) {
664
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
665
+ ...filter,
666
+ tags: {
667
+ "tide:transfer-state": "tide:transfer-state"
668
+ }
669
+ });
670
+ if (!Array.isArray(rawMetrics)) {
671
+ return [];
672
+ }
673
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
674
+ return limitedMetrics.map(event => ({
675
+ tideActionID: "tide-transfer-state",
676
+ tideActionDate: event.timestamp || Date.now(),
677
+ tideActionDetails: {
678
+ transferStateID: event.metadata?.transferStateID,
679
+ recipientID: event.metadata?.recipientID,
680
+ senderID: event.metadata?.senderID,
681
+ path: event.metadata?.path,
682
+ startTime: event.metadata?.startTime,
683
+ currentTransferredSize: event.metadata?.currentTransferredSize,
684
+ speed: event.metadata?.speed,
685
+ transferProgress: event.metadata?.transferProgress,
686
+ transferSize: event.metadata?.transferSize,
687
+ transferETA: event.metadata?.transferETA,
688
+ fileName: event.metadata?.fileName
689
+ }
690
+ }));
691
+ }
692
+ async getTideIncomingFileActions({
693
+ interval = "1d",
694
+ limit = 50,
695
+ source = "storage",
696
+ filter = {}
697
+ } = {}) {
698
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
699
+ ...filter,
700
+ tags: {
701
+ "tide:incoming-file": "tide:incoming-file"
702
+ }
703
+ });
704
+ if (!Array.isArray(rawMetrics)) {
705
+ return [];
706
+ }
707
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
708
+ return limitedMetrics.map(event => ({
709
+ tideActionID: "tide-incoming-file",
710
+ tideActionDate: event.timestamp || Date.now(),
711
+ tideActionDetails: {
712
+ recipientID: event.metadata?.recipientID,
713
+ senderID: event.metadata?.senderID,
714
+ path: event.metadata?.path,
715
+ fileSize: event.metadata?.fileSize,
716
+ fileName: event.metadata?.fileName,
717
+ totalChunks: event.metadata?.totalChunks,
718
+ chunkSize: event.metadata?.chunkSize
719
+ }
720
+ }));
721
+ }
722
+ async getTideIncomingTransferActions({
723
+ interval = "1d",
724
+ limit = 50,
725
+ source = "storage",
726
+ filter = {}
727
+ } = {}) {
728
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
729
+ ...filter,
730
+ tags: {
731
+ "tide:incoming-transfer": "tide:incoming-transfer"
732
+ }
733
+ });
734
+ if (!Array.isArray(rawMetrics)) {
735
+ return [];
736
+ }
737
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
738
+ return limitedMetrics.map(event => ({
739
+ tideActionID: "tide-incoming-transfer",
740
+ tideActionDate: event.timestamp || Date.now(),
741
+ tideActionDetails: {
742
+ recipientID: event.metadata?.recipientID,
743
+ senderID: event.metadata?.senderID,
744
+ fileSize: event.metadata?.fileSize,
745
+ fileName: event.metadata?.fileName,
746
+ path: event.metadata?.path,
747
+ startedOn: event.metadata?.startedOn
748
+ }
749
+ }));
750
+ }
751
+ async getTideBarrierAcceptedActions({
752
+ interval = "1d",
753
+ limit = 50,
754
+ source = "storage",
755
+ filter = {}
756
+ } = {}) {
757
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
758
+ ...filter,
759
+ tags: {
760
+ "tide-barrier:accepted": "tide-barrier:accepted"
761
+ }
762
+ });
763
+ if (!Array.isArray(rawMetrics)) {
764
+ return [];
765
+ }
766
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
767
+ return limitedMetrics.map(event => ({
768
+ tideActionID: "tide-barrier-accepted",
769
+ tideActionDate: event.timestamp || Date.now(),
770
+ tideActionDetails: {
771
+ recipientID: event.metadata?.recipientID,
772
+ transferID: event.metadata?.transferID,
773
+ senderID: event.metadata?.senderID,
774
+ tideItemCount: event.metadata?.tideItemCount,
775
+ tideItemSize: event.metadata?.tideItemSize,
776
+ tideTransferContentType: event.metadata?.tideTransferContentType,
777
+ tideTransferType: event.metadata?.tideTransferType,
778
+ tideDestinationPath: event.metadata?.tideDestinationPath,
779
+ tideOriginPath: event.metadata?.tideOriginPath
780
+ }
781
+ }));
782
+ }
783
+ async getTideBarrierDeniedActions({
784
+ interval = "1d",
785
+ limit = 50,
786
+ source = "storage",
787
+ filter = {}
788
+ } = {}) {
789
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
790
+ ...filter,
791
+ tags: {
792
+ "tide-barrier:denied": "tide-barrier:denied"
793
+ }
794
+ });
795
+ if (!Array.isArray(rawMetrics)) {
796
+ return [];
797
+ }
798
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
799
+ return limitedMetrics.map(event => ({
800
+ tideActionID: "tide-barrier-denied",
801
+ tideActionDate: event.timestamp || Date.now(),
802
+ tideActionDetails: {
803
+ recipientID: event.metadata?.recipientID,
804
+ transferID: event.metadata?.transferID,
805
+ senderID: event.metadata?.senderID,
806
+ tideItemCount: event.metadata?.tideItemCount,
807
+ tideItemSize: event.metadata?.tideItemSize,
808
+ tideTransferContentType: event.metadata?.tideTransferContentType,
809
+ tideTransferType: event.metadata?.tideTransferType,
810
+ tideDestinationPath: event.metadata?.tideDestinationPath,
811
+ tideOriginPath: event.metadata?.tideOriginPath
812
+ }
813
+ }));
814
+ }
815
+ async getFileListReceivedActions({
816
+ interval = "1d",
817
+ limit = 50,
818
+ source = "storage",
819
+ filter = {}
820
+ } = {}) {
821
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
822
+ ...filter,
823
+ tags: {
824
+ "file-list:received": "file-list:received"
825
+ }
826
+ });
827
+ if (!Array.isArray(rawMetrics)) {
828
+ return [];
829
+ }
830
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
831
+ return limitedMetrics.map(event => ({
832
+ tideActionID: "file-list-received",
833
+ tideActionDate: event.timestamp || Date.now(),
834
+ tideActionDetails: {
835
+ recipientID: event.metadata?.recipientID,
836
+ senderID: event.metadata?.senderID,
837
+ totalItems: event.metadata?.totalItems,
838
+ runningOn: event.metadata?.runningOn
839
+ }
840
+ }));
841
+ }
842
+ async getFileListRequestActions({
843
+ interval = "1d",
844
+ limit = 50,
845
+ source = "storage",
846
+ filter = {}
847
+ } = {}) {
848
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
849
+ ...filter,
850
+ tags: {
851
+ "file-list:request": "file-list:request"
852
+ }
853
+ });
854
+ if (!Array.isArray(rawMetrics)) {
855
+ return [];
856
+ }
857
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
858
+ return limitedMetrics.map(event => ({
859
+ tideActionID: "file-list-request",
860
+ tideActionDate: event.timestamp || Date.now(),
861
+ tideActionDetails: {
862
+ recipientID: event.metadata?.recipientID,
863
+ requesterID: event.metadata?.requesterID,
864
+ depth: event.metadata?.depth
865
+ }
866
+ }));
867
+ }
868
+ async getTiderBannedActions({
869
+ interval = "1d",
870
+ limit = 50,
871
+ source = "storage",
872
+ filter = {}
873
+ } = {}) {
874
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
875
+ ...filter,
876
+ tags: {
877
+ "tider:banned": "tider:banned"
878
+ }
879
+ });
880
+ if (!Array.isArray(rawMetrics)) {
881
+ return [];
882
+ }
883
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
884
+ return limitedMetrics.map(event => ({
885
+ tideActionID: "ban-client-activated",
886
+ tideActionDate: event.timestamp || Date.now(),
887
+ tideActionDetails: {
888
+ clientID: event.metadata?.clientID,
889
+ options: event.metadata?.options
890
+ }
891
+ }));
892
+ }
893
+ async getTiderKickedActions({
894
+ interval = "1d",
895
+ limit = 50,
896
+ source = "storage",
897
+ filter = {}
898
+ } = {}) {
899
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
900
+ ...filter,
901
+ tags: {
902
+ "tider:kicked": "tider:kicked"
903
+ }
904
+ });
905
+ if (!Array.isArray(rawMetrics)) {
906
+ return [];
907
+ }
908
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
909
+ return limitedMetrics.map(event => ({
910
+ tideActionID: "kick-client-activated",
911
+ tideActionDate: event.timestamp || Date.now(),
912
+ tideActionDetails: {
913
+ clientID: event.metadata?.clientID,
914
+ options: event.metadata?.options
915
+ }
916
+ }));
917
+ }
918
+ async getTiderUnbannedActions({
919
+ interval = "1d",
920
+ limit = 50,
921
+ source = "storage",
922
+ filter = {}
923
+ } = {}) {
924
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
925
+ ...filter,
926
+ tags: {
927
+ "tider:unbanned": "tider:unbanned"
928
+ }
929
+ });
930
+ if (!Array.isArray(rawMetrics)) {
931
+ return [];
932
+ }
933
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
934
+ return limitedMetrics.map(event => ({
935
+ tideActionID: "unban-client-activated",
936
+ tideActionDate: event.timestamp || Date.now(),
937
+ tideActionDetails: {
938
+ clientID: event.metadata?.clientID
939
+ }
940
+ }));
941
+ }
942
+ async getTiderAlreadyOnlineActions({
943
+ interval = "1d",
944
+ limit = 50,
945
+ source = "storage",
946
+ filter = {}
947
+ } = {}) {
948
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
949
+ ...filter,
950
+ tags: {
951
+ "tider:already-online": "tider:already-online"
952
+ }
953
+ });
954
+ if (!Array.isArray(rawMetrics)) {
955
+ return [];
956
+ }
957
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
958
+ return limitedMetrics.map(event => ({
959
+ tideActionID: "tider-already-online",
960
+ tideActionDate: event.timestamp || Date.now(),
961
+ tideActionDetails: {
962
+ currentTideChannel: event.metadata?.currentTideChannel || "file-room",
963
+ originUser: event.metadata?.originUser,
964
+ user: event.metadata?.user,
965
+ connectionID: event.metadata?.connectionID,
966
+ pClientID: event.metadata?.pClientID,
967
+ tideID: event.metadata?.tideID,
968
+ connectedOn: event.metadata?.connectedOn
969
+ }
970
+ }));
971
+ }
972
+ async getTiderOnlineActions({
973
+ interval = "1d",
974
+ limit = 50,
975
+ source = "storage",
976
+ filter = {}
977
+ } = {}) {
978
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
979
+ ...filter,
980
+ tags: {
981
+ "tider:online": "tider:online"
982
+ }
983
+ });
984
+ if (!rawMetrics || !Array.isArray(rawMetrics)) {
985
+ return [];
986
+ }
987
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
988
+ return limitedMetrics.map(event => ({
989
+ tideActionID: "tider-online",
990
+ tideActionDate: event.timestamp || Date.now(),
991
+ tideActionDetails: {
992
+ currentTideChannel: event.metadata?.currentTideChannel || "file-room",
993
+ connectedOn: event.metadata?.connectedOn,
994
+ originUser: event.metadata?.originUser,
995
+ user: event.metadata?.user,
996
+ connectionID: event.metadata?.connectionID,
997
+ pClientID: event.metadata?.pClientID,
998
+ tideID: event.metadata?.tideID
999
+ }
1000
+ }));
1001
+ }
1002
+ async getTiderOfflineActions({
1003
+ interval = "1d",
1004
+ limit = 50,
1005
+ source = "storage",
1006
+ filter = {}
1007
+ } = {}) {
1008
+ const rawMetrics = await this.getTidalyticsSince(interval, source, {
1009
+ ...filter,
1010
+ tags: {
1011
+ "tider:offline": "tider:offline"
1012
+ }
1013
+ });
1014
+ if (!rawMetrics || !Array.isArray(rawMetrics)) {
1015
+ return [];
1016
+ }
1017
+ const limitedMetrics = rawMetrics.slice(-limit).reverse();
1018
+ return limitedMetrics.map(event => ({
1019
+ tideActionID: "tider-offline",
1020
+ tideActionDate: event.timestamp || Date.now(),
1021
+ tideActionDetails: {
1022
+ currentTideChannel: event.metadata?.currentTideChannel || "file-room",
1023
+ connectedOn: event.metadata?.connectedOn,
1024
+ originUser: event.metadata?.originUser,
1025
+ user: event.metadata?.user,
1026
+ connectionID: event.metadata?.connectionID,
1027
+ pClientID: event.metadata?.pClientID,
1028
+ tideID: event.metadata?.tideID
1029
+ }
1030
+ }));
1031
+ }
135
1032
 
136
1033
  // --- File Messager Tide Sentinel Management ---
137
1034
 
@@ -1197,7 +2094,7 @@ class FileMessager {
1197
2094
  currentTideChannel: clientDetails.currentTideChannel || "file-room",
1198
2095
  originUser: clientDetails.originClientID,
1199
2096
  user: clientDetails.clientID,
1200
- connectionID: clientDetails.connectionID,
2097
+ connectionID: clientData.connectionID,
1201
2098
  pClientID: clientDetails.pClientID,
1202
2099
  tideID: clientDetails.tideID,
1203
2100
  connectedOn: clientDetails.connectedOn
@@ -1210,7 +2107,7 @@ class FileMessager {
1210
2107
  currentTideChannel: clientDetails.currentTideChannel || "file-room",
1211
2108
  originUser: clientDetails.originClientID,
1212
2109
  user: clientDetails.clientID,
1213
- connectionID: clientDetails.connectionID,
2110
+ connectionID: clientData.connectionID,
1214
2111
  pClientID: clientDetails.pClientID,
1215
2112
  tideID: clientDetails.tideID,
1216
2113
  connectedOn: clientDetails.connectedOn
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+
3
+ function getNestedData(obj, path) {
4
+ return path.split(".").reduce((acc, part) => acc?.[part], obj);
5
+ }
6
+ ;
7
+ function pickData(data, keys) {
8
+ if (!keys) {
9
+ return data;
10
+ }
11
+ return keys.reduce((acc, key) => {
12
+ if (data[key] !== undefined) {
13
+ acc[key] = data[key];
14
+ }
15
+ return acc;
16
+ }, {});
17
+ }
18
+ ;
19
+ function aggregateMultiAverageChartData(events, {
20
+ groupBy = ["user"],
21
+ valueKey = "metadata.fileSize",
22
+ timestampKey = "timestamp",
23
+ metadataFields = null,
24
+ previewLimit = 3
25
+ } = {}) {
26
+ const buckets = {};
27
+ for (const event of events) {
28
+ const labelParts = groupBy.map(path => {
29
+ if (path === "time") {
30
+ return event[timestampKey];
31
+ }
32
+ if (path === "user") {
33
+ return event.metadata?.clientID || event.metadata?.senderID || "unknown";
34
+ }
35
+ if (path === "type") {
36
+ return event.type || "unknown";
37
+ }
38
+ return getNestedData(event, path) ?? "unknown";
39
+ });
40
+ const label = labelParts.join(" | ");
41
+ const value = getNestedData(event, valueKey);
42
+ if (typeof value !== "number") {
43
+ continue;
44
+ }
45
+ if (!buckets[label]) {
46
+ buckets[label] = {
47
+ previewFileNames: [],
48
+ previewMetadata: [],
49
+ timestamps: [],
50
+ values: [],
51
+ total: 0,
52
+ count: 0
53
+ };
54
+ }
55
+ const bucket = buckets[label];
56
+ bucket.total += value;
57
+ bucket.count += 1;
58
+ bucket.values.push(value);
59
+ bucket.timestamps.push(event[timestampKey]);
60
+ const fileName = getNestedData(event, "metadata.fileName");
61
+ if (fileName && bucket.previewFileNames.length < previewLimit) {
62
+ bucket.previewFileNames.push(fileName);
63
+ }
64
+ if (bucket.previewMetadata.length < previewLimit) {
65
+ bucket.previewMetadata.push(pickData(event.metadata || {}, metadataFields));
66
+ }
67
+ }
68
+ return Object.entries(buckets).map(([label, bucket]) => ({
69
+ firstTimestamp: Math.min(...bucket.timestamps),
70
+ lastTimestamp: Math.max(...bucket.timestamps),
71
+ previewFileNames: bucket.previewFileNames,
72
+ previewMetadata: bucket.previewMetadata,
73
+ minValue: Math.min(...bucket.values),
74
+ maxValue: Math.max(...bucket.values),
75
+ average: +(total / count).toFixed(2),
76
+ total: bucket.total,
77
+ count: bucket.count,
78
+ label
79
+ }));
80
+ }
81
+ ;
82
+ function aggregateChartData(events, {
83
+ groupBy = "type",
84
+ typeKey = "type",
85
+ userKey = "metadata.clientID",
86
+ timestampKey = "timestamp",
87
+ valueKey = "value",
88
+ mode = "count",
89
+ metadataFields = null,
90
+ previewLimit = 3
91
+ } = {}) {
92
+ const buckets = {};
93
+ for (const event of events) {
94
+ let label;
95
+ if (groupBy === "time") {
96
+ label = event[timestampKey];
97
+ } else if (groupBy === "user") {
98
+ label = getNestedData(event, userKey) || "unknown";
99
+ } else {
100
+ label = event[typeKey] || "other";
101
+ }
102
+ const type = event[typeKey] || "other";
103
+ const value = mode === "sum" ? getNestedData(event, valueKey) || 0 : 1;
104
+ if (!buckets[type]) {
105
+ buckets[type] = {
106
+ previewFileNames: [],
107
+ previewMetadata: [],
108
+ timestamps: [],
109
+ values: [],
110
+ count: 0,
111
+ total: 0,
112
+ type
113
+ };
114
+ }
115
+ const bucket = buckets[type];
116
+ bucket.total += value;
117
+ bucket.count += 1;
118
+ bucket.values.push(value);
119
+ bucket.timestamps.push(event[timestampKey]);
120
+ const fileName = getNestedData(event, "metadata.fileName");
121
+ if (fileName && bucket.previewFileNames.length < previewLimit) {
122
+ bucket.previewFileNames.push(fileName);
123
+ }
124
+ if (bucket.previewMetadata.length < previewLimit) {
125
+ bucket.previewMetadata.push(pickData(event.metadata || {}, metadataFields));
126
+ }
127
+ }
128
+ return Object.entries(buckets).map(([type, bucket]) => ({
129
+ average: +(bucket.total / bucket.count).toFixed(2),
130
+ firstTimestamp: Math.min(...bucket.timestamps),
131
+ lastTimestamp: Math.max(...bucket.timestamps),
132
+ previewFileNames: bucket.previewFileNames,
133
+ previewMetadata: bucket.previewMetadata,
134
+ minValue: Math.min(...bucket.values),
135
+ maxValue: Math.max(...bucket.values),
136
+ total: bucket.total,
137
+ count: bucket.count,
138
+ label: type,
139
+ type
140
+ }));
141
+ }
142
+ module.exports = {
143
+ aggregateMultiAverageChartData,
144
+ aggregateChartData,
145
+ getNestedData
146
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trap_stevo/filetide",
3
- "version": "0.0.66",
3
+ "version": "0.0.68",
4
4
  "description": "Revolutionizing real-time file transfer with seamless, instant communication across any device. Deliver files instantly, regardless of platform, and experience unparalleled speed and control in managing transfers. Elevate your file-sharing capabilities with a tool designed for precision, efficiency, and effortless connectivity.",
5
5
  "main": "dist/cjs/FileTide.js",
6
6
  "scripts": {
@@ -36,7 +36,7 @@
36
36
  "dependencies": {
37
37
  "@trap_stevo/iotide": "^0.0.43",
38
38
  "@trap_stevo/iotide-client": "^0.0.25",
39
- "@trap_stevo/legendarybuilderpronodejs-utilities": "^1.0.43",
39
+ "@trap_stevo/legendarybuilderpronodejs-utilities": "^1.0.48",
40
40
  "@trap_stevo/metrictide": "^0.0.3",
41
41
  "@trap_stevo/timelyst": "^0.0.5",
42
42
  "chalk": "^4.1.2",