@bitblit/ratchet-common 6.0.146-alpha → 6.0.148-alpha
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/package.json +2 -1
- package/src/2d/line-2d.ts +6 -0
- package/src/2d/matrix-factory.ts +94 -0
- package/src/2d/plane-2d-type.ts +6 -0
- package/src/2d/plane-2d.ts +7 -0
- package/src/2d/point-2d.ts +4 -0
- package/src/2d/poly-line-2d.ts +5 -0
- package/src/2d/ratchet-2d.spec.ts +205 -0
- package/src/2d/ratchet-2d.ts +350 -0
- package/src/2d/transformation-matrix.ts +19 -0
- package/src/build/build-information.ts +8 -0
- package/src/build/ratchet-common-info.ts +19 -0
- package/src/histogram/histogram-entry.ts +4 -0
- package/src/histogram/histogram.spec.ts +25 -0
- package/src/histogram/histogram.ts +61 -0
- package/src/jwt/common-jwt-token.ts +17 -0
- package/src/jwt/expired-jwt-handling.ts +5 -0
- package/src/jwt/jwt-decode-only-ratchet.ts +26 -0
- package/src/jwt/jwt-payload-expiration-ratchet.ts +45 -0
- package/src/jwt/jwt-token-base.ts +14 -0
- package/src/lang/array-ratchet.spec.ts +79 -0
- package/src/lang/array-ratchet.ts +141 -0
- package/src/lang/base64-ratchet.spec.ts +48 -0
- package/src/lang/base64-ratchet.ts +247 -0
- package/src/lang/boolean-ratchet.spec.ts +95 -0
- package/src/lang/boolean-ratchet.ts +52 -0
- package/src/lang/composite-last-success-provider.spec.ts +31 -0
- package/src/lang/composite-last-success-provider.ts +30 -0
- package/src/lang/currency-ratchet.ts +29 -0
- package/src/lang/date-ratchet.spec.ts +27 -0
- package/src/lang/date-ratchet.ts +42 -0
- package/src/lang/duration-ratchet.spec.ts +47 -0
- package/src/lang/duration-ratchet.ts +77 -0
- package/src/lang/enum-ratchet.spec.ts +45 -0
- package/src/lang/enum-ratchet.ts +41 -0
- package/src/lang/error-handling-approach.ts +6 -0
- package/src/lang/error-ratchet.spec.ts +25 -0
- package/src/lang/error-ratchet.ts +70 -0
- package/src/lang/esm-ratchet.ts +81 -0
- package/src/lang/expiring-object.spec.ts +56 -0
- package/src/lang/expiring-object.ts +84 -0
- package/src/lang/geolocation-ratchet.spec.ts +177 -0
- package/src/lang/geolocation-ratchet.ts +341 -0
- package/src/lang/global-ratchet.spec.ts +17 -0
- package/src/lang/global-ratchet.ts +105 -0
- package/src/lang/key-value.ts +8 -0
- package/src/lang/last-success-provider.ts +4 -0
- package/src/lang/map-ratchet.spec.ts +113 -0
- package/src/lang/map-ratchet.ts +220 -0
- package/src/lang/no.spec.ts +9 -0
- package/src/lang/no.ts +7 -0
- package/src/lang/number-ratchet.spec.ts +154 -0
- package/src/lang/number-ratchet.ts +253 -0
- package/src/lang/parsed-url.ts +10 -0
- package/src/lang/promise-ratchet.spec.ts +104 -0
- package/src/lang/promise-ratchet.ts +196 -0
- package/src/lang/range.ts +4 -0
- package/src/lang/require-ratchet.spec.ts +85 -0
- package/src/lang/require-ratchet.ts +68 -0
- package/src/lang/simple-arg-ratchet.spec.ts +13 -0
- package/src/lang/simple-arg-ratchet.ts +47 -0
- package/src/lang/simple-encryption-ratchet.ts +88 -0
- package/src/lang/sort-ratchet.spec.ts +58 -0
- package/src/lang/sort-ratchet.ts +50 -0
- package/src/lang/stop-watch.spec.ts +53 -0
- package/src/lang/stop-watch.ts +202 -0
- package/src/lang/string-ratchet.spec.ts +226 -0
- package/src/lang/string-ratchet.ts +676 -0
- package/src/lang/time-zone-ratchet.spec.ts +51 -0
- package/src/lang/time-zone-ratchet.ts +148 -0
- package/src/lang/timeout-token.spec.ts +12 -0
- package/src/lang/timeout-token.ts +21 -0
- package/src/lang/uint-8-array-ratchet.spec.ts +22 -0
- package/src/lang/uint-8-array-ratchet.ts +48 -0
- package/src/lang/web-stream-ratchet.spec.ts +12 -0
- package/src/lang/web-stream-ratchet.ts +96 -0
- package/src/logger/classic-single-line-log-message-formatter.ts +19 -0
- package/src/logger/log-message-builder.ts +60 -0
- package/src/logger/log-message-format-type.ts +11 -0
- package/src/logger/log-message-formatter.ts +6 -0
- package/src/logger/log-message-processor.ts +6 -0
- package/src/logger/log-message.ts +9 -0
- package/src/logger/log-snapshot.ts +6 -0
- package/src/logger/logger-instance.ts +269 -0
- package/src/logger/logger-level-name.ts +11 -0
- package/src/logger/logger-meta.ts +7 -0
- package/src/logger/logger-options.ts +14 -0
- package/src/logger/logger-output-function.ts +10 -0
- package/src/logger/logger-ring-buffer.ts +89 -0
- package/src/logger/logger-util.spec.ts +11 -0
- package/src/logger/logger-util.ts +68 -0
- package/src/logger/logger.spec.ts +177 -0
- package/src/logger/logger.ts +213 -0
- package/src/logger/none-log-message-formatter.ts +10 -0
- package/src/logger/single-line-no-level-log-message-formatter.ts +18 -0
- package/src/logger/structured-json-log-message-formatter.ts +25 -0
- package/src/mail/archive-email-result.ts +8 -0
- package/src/mail/email-attachment.ts +23 -0
- package/src/mail/mail-sending-provider.ts +21 -0
- package/src/mail/mailer-config.ts +30 -0
- package/src/mail/mailer-like.ts +38 -0
- package/src/mail/mailer-util.ts +65 -0
- package/src/mail/mailer.spec.ts +120 -0
- package/src/mail/mailer.ts +214 -0
- package/src/mail/ready-to-send-email.ts +67 -0
- package/src/mail/resolved-ready-to-send-email.ts +17 -0
- package/src/mail/send-email-result.ts +16 -0
- package/src/mail/test-mail-sending-provider.ts +35 -0
- package/src/network/browser-local-ip-provider.spec.ts +23 -0
- package/src/network/browser-local-ip-provider.ts +26 -0
- package/src/network/fixed-local-ip-provider.ts +9 -0
- package/src/network/local-ip-provider.ts +4 -0
- package/src/network/network-ratchet.spec.ts +17 -0
- package/src/network/network-ratchet.ts +209 -0
- package/src/network/remote-file-tracker/backup-result.ts +6 -0
- package/src/network/remote-file-tracker/file-transfer-result-type.ts +5 -0
- package/src/network/remote-file-tracker/file-transfer-result.ts +9 -0
- package/src/network/remote-file-tracker/remote-file-tracker-options.ts +6 -0
- package/src/network/remote-file-tracker/remote-file-tracker-push-options.ts +4 -0
- package/src/network/remote-file-tracker/remote-file-tracker.ts +117 -0
- package/src/network/remote-file-tracker/remote-file-tracking-provider.ts +19 -0
- package/src/network/remote-file-tracker/remote-status-data-and-content.ts +6 -0
- package/src/network/remote-file-tracker/remote-status-data.ts +7 -0
- package/src/network/restful-api-http-error.spec.ts +13 -0
- package/src/network/restful-api-http-error.ts +173 -0
- package/src/template/ratchet-template-renderer.ts +8 -0
- package/src/third-party/google/google-recaptcha-ratchet.spec.ts +27 -0
- package/src/third-party/google/google-recaptcha-ratchet.ts +36 -0
- package/src/third-party/twilio/twilio-ratchet.ts +92 -0
- package/src/third-party/twilio/twilio-verify-ratchet.ts +83 -0
- package/src/transform/built-in-transforms.ts +214 -0
- package/src/transform/transform-ratchet.spec.ts +134 -0
- package/src/transform/transform-ratchet.ts +88 -0
- package/src/transform/transform-rule.ts +7 -0
- package/src/tx/transaction-configuration.ts +8 -0
- package/src/tx/transaction-final-state.ts +7 -0
- package/src/tx/transaction-ratchet.spec.ts +150 -0
- package/src/tx/transaction-ratchet.ts +98 -0
- package/src/tx/transaction-result.ts +10 -0
- package/src/tx/transaction-step.ts +5 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { TimeZoneRatchet } from './time-zone-ratchet.js';
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
|
|
4
|
+
const tz: TimeZoneRatchet = TimeZoneRatchet.PACIFIC;
|
|
5
|
+
|
|
6
|
+
describe('#currentHour', function () {
|
|
7
|
+
test('should return 0- 23', function () {
|
|
8
|
+
const result: number = tz.currentHour();
|
|
9
|
+
expect(result).toBeGreaterThanOrEqual(0);
|
|
10
|
+
expect(result).toBeLessThanOrEqual(23);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe('#startOfTodayEpochSeconds', function () {
|
|
15
|
+
test('should never be more than now or more than 86400 seconds in the past', function () {
|
|
16
|
+
const now: number = tz.nowEpochSeconds(); // Math.floor(new Date().getTime() / 1000);
|
|
17
|
+
const start: number = tz.startOfTodayEpochSeconds();
|
|
18
|
+
const result: number = now - start;
|
|
19
|
+
expect(result).toBeGreaterThanOrEqual(0);
|
|
20
|
+
expect(result).toBeLessThanOrEqual(86400);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('#dailySlotCount', function () {
|
|
25
|
+
test('should return 86400', function () {
|
|
26
|
+
const result: number = tz.dailySlotCount(1000);
|
|
27
|
+
expect(result).toEqual(86400);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('#currentTimeSlotIdx', function () {
|
|
32
|
+
// CAW 2020-11-01 : This actually fails on the day of spring forward/fall back because of the extra hour!
|
|
33
|
+
test.skip('should return same as current hour', function () {
|
|
34
|
+
const curHour: number = tz.currentHour();
|
|
35
|
+
const hourSlot: number = 1000 * 60 * 60;
|
|
36
|
+
const curSlot: number = tz.currentTimeSlotIdx(hourSlot);
|
|
37
|
+
|
|
38
|
+
expect(curSlot).toEqual(curHour);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('#matchingTimeSlotIdx', function () {
|
|
43
|
+
// CAW 2020-11-01 : This actually fails on the day of spring forward/fall back because of the extra hour!
|
|
44
|
+
test.skip('should return same as current hour', function () {
|
|
45
|
+
const curHour: number = tz.currentHour();
|
|
46
|
+
const hourSlot: number = 1000 * 60 * 60;
|
|
47
|
+
const matchSlot: number = tz.matchingTimeSlotIdx(new Date().getTime(), hourSlot);
|
|
48
|
+
|
|
49
|
+
expect(matchSlot).toEqual(curHour);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { DateTime } from 'luxon';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Functions for working with dates specifically a given time zone
|
|
5
|
+
TODO: Refactor the 'today' slots to work in terms of the 'matching' slot endpoints
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export class TimeZoneRatchet {
|
|
9
|
+
public static PACIFIC = new TimeZoneRatchet('America/Los_Angeles');
|
|
10
|
+
|
|
11
|
+
constructor(private timezoneIanaName: string) {
|
|
12
|
+
if (!timezoneIanaName || timezoneIanaName.trim().length === 0) {
|
|
13
|
+
throw 'Timezone cannot be null or empty';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public get ianaName(): string {
|
|
18
|
+
return this.timezoneIanaName;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Returns 0-23
|
|
22
|
+
public currentHour(): number {
|
|
23
|
+
const rval = DateTime.local().setZone(this.timezoneIanaName).hour;
|
|
24
|
+
return rval;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private toEpochSeconds(dt: DateTime) {
|
|
28
|
+
return Math.round(dt.toMillis() / 1000);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public nowEpochSeconds(): number {
|
|
32
|
+
return Math.floor(DateTime.local().setZone(this.timezoneIanaName).toSeconds());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Returns midnight in the current timezone in epoch seconds
|
|
36
|
+
public startOfTodayEpochSeconds(): number {
|
|
37
|
+
const startOfToday = this.toEpochSeconds(
|
|
38
|
+
DateTime.local().setZone(this.timezoneIanaName).set({ hour: 0, minute: 0, second: 0, millisecond: 0 }),
|
|
39
|
+
);
|
|
40
|
+
return startOfToday;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Returns midnight of the passed timestamp in the current timezone in epoch seconds
|
|
44
|
+
public startOfMatchingDayEpochSeconds(inputTS: number): number {
|
|
45
|
+
const startOfToday = this.toEpochSeconds(DateTime.fromMillis(inputTS).set({ hour: 0, minute: 0, second: 0, millisecond: 0 }));
|
|
46
|
+
return startOfToday;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Returns midnight of the passed timestamp in the current timezone in epoch ms
|
|
50
|
+
public startOfMatchingDayEpochMS(inputTS: number): number {
|
|
51
|
+
return this.startOfMatchingDayEpochSeconds(inputTS) * 1000;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Returns the start of the current hour in epoch seconds
|
|
55
|
+
public startOfCurrentHourEpochSeconds(): number {
|
|
56
|
+
const rval = this.toEpochSeconds(DateTime.local().setZone(this.timezoneIanaName).set({ minute: 0, second: 0, millisecond: 0 }));
|
|
57
|
+
return rval;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Returns the start of the current minute in epoch seconds
|
|
61
|
+
public startOfCurrentMinuteEpochSeconds(): number {
|
|
62
|
+
const rval = this.toEpochSeconds(DateTime.local().setZone(this.timezoneIanaName).set({ second: 0, millisecond: 0 }));
|
|
63
|
+
return rval;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Returns the start of the current second in epoch seconds
|
|
67
|
+
public startOfCurrentSecondEpochSeconds(): number {
|
|
68
|
+
const rval = this.toEpochSeconds(DateTime.local().setZone(this.timezoneIanaName).set({ millisecond: 0 }));
|
|
69
|
+
return rval;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Returns midnight in the current timezone in epoch ms
|
|
73
|
+
public startOfTodayEpochMS(): number {
|
|
74
|
+
const startOfToday = DateTime.local().setZone(this.timezoneIanaName).set({ hour: 0, minute: 0, second: 0, millisecond: 0 }).toMillis();
|
|
75
|
+
return startOfToday;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Returns the number of slots in a day (simple math)
|
|
80
|
+
* @param {number} slotWidthMs
|
|
81
|
+
* @returns {number} containing the number of slots in a day (last one may be partial)
|
|
82
|
+
*/
|
|
83
|
+
public dailySlotCount(slotWidthMs: number): number {
|
|
84
|
+
return Math.ceil(86400000 / slotWidthMs);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Imagine a day cut into N 'slots', each slotWidthMS wide - then there are
|
|
89
|
+
* 86,400,000 / slotWidthMS slots available, indexed from 0 on up. This function
|
|
90
|
+
* returns that index
|
|
91
|
+
* @param {number} slotWidthMs
|
|
92
|
+
* @returns {number} containing the current index
|
|
93
|
+
*/
|
|
94
|
+
public currentTimeSlotIdx(slotWidthMs: number): number {
|
|
95
|
+
if (slotWidthMs < 1) {
|
|
96
|
+
throw new Error('Cannot process with slot less than one ms wide');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const startOfToday = this.startOfTodayEpochMS();
|
|
100
|
+
const now = new Date().getTime();
|
|
101
|
+
const delta = now - startOfToday;
|
|
102
|
+
const idx = Math.floor(delta / slotWidthMs);
|
|
103
|
+
return idx;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Imagine a day cut into N 'slots', each slotWidthMS wide - then there are
|
|
108
|
+
* 86,400,000 / slotWidthMS slots available, indexed from 0 on up. This function
|
|
109
|
+
* returns that index
|
|
110
|
+
* @param timestamp Number of the epochMS to check
|
|
111
|
+
* @param slotWidthMs Number of the width of each slot in MS
|
|
112
|
+
*/
|
|
113
|
+
public matchingTimeSlotIdx(timestamp: number, slotWidthMs: number): number {
|
|
114
|
+
if (slotWidthMs < 1) {
|
|
115
|
+
throw new Error('Cannot process with slot less than one ms wide');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const startOfDay = this.startOfMatchingDayEpochMS(timestamp);
|
|
119
|
+
const delta = timestamp - startOfDay;
|
|
120
|
+
|
|
121
|
+
const idx = Math.floor(delta / slotWidthMs);
|
|
122
|
+
return idx;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Given the definition of slotWidth and currentTimeSlotIdx from above, return the ms that is the
|
|
127
|
+
* start of the slot that "now" falls within.
|
|
128
|
+
* @param {number} slotWidthMs
|
|
129
|
+
* @returns {number}
|
|
130
|
+
*/
|
|
131
|
+
public startOfCurrentSlotEpochMS(slotWidthMs: number): number {
|
|
132
|
+
const startOfToday = this.startOfTodayEpochMS();
|
|
133
|
+
const currentIdx = this.currentTimeSlotIdx(slotWidthMs);
|
|
134
|
+
return startOfToday + currentIdx * slotWidthMs;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Given the definition of slotWidth and currentTimeSlotIdx from above, return the ms that is the
|
|
139
|
+
* start of the slot that the passed timestamp falls within.
|
|
140
|
+
* @param {number} slotWidthMs
|
|
141
|
+
* @returns {number}
|
|
142
|
+
*/
|
|
143
|
+
public startOfMatchingSlotEpochMS(timestamp: number, slotWidthMs: number): number {
|
|
144
|
+
const startOfDay = this.startOfMatchingDayEpochMS(timestamp);
|
|
145
|
+
const currentIdx = this.matchingTimeSlotIdx(timestamp, slotWidthMs);
|
|
146
|
+
return startOfDay + currentIdx * slotWidthMs;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TimeoutToken } from './timeout-token.js';
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
|
|
4
|
+
describe('#timeoutToken', function () {
|
|
5
|
+
test('should return the correct value for isTimeoutToken', function () {
|
|
6
|
+
const tt: TimeoutToken = new TimeoutToken('title', 20000);
|
|
7
|
+
const ntt: any = { a: 'b' };
|
|
8
|
+
|
|
9
|
+
expect(TimeoutToken.isTimeoutToken(tt)).toBeTruthy();
|
|
10
|
+
expect(TimeoutToken.isTimeoutToken(ntt)).toBeFalsy();
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Logger } from '../logger/logger.js';
|
|
2
|
+
import { LoggerLevelName } from '../logger/logger-level-name.js';
|
|
3
|
+
|
|
4
|
+
export class TimeoutToken {
|
|
5
|
+
private __timeoutTokenFlagField = true;
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
private title: string,
|
|
9
|
+
private timeoutMS: number,
|
|
10
|
+
) {
|
|
11
|
+
Object.setPrototypeOf(this, TimeoutToken.prototype);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public writeToLog(logLevel: LoggerLevelName = LoggerLevelName.warn): void {
|
|
15
|
+
Logger.logByLevel(logLevel, 'Timed out after %d ms waiting for results of %s', this.timeoutMS, this.title);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public static isTimeoutToken(value: any): boolean {
|
|
19
|
+
return !!value && !!value['__timeoutTokenFlagField'];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Uint8ArrayRatchet } from './uint-8-array-ratchet.js';
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
|
|
4
|
+
describe('#Uint8Array', function () {
|
|
5
|
+
test('should combine uint8 arrays', function () {
|
|
6
|
+
const arrayOne: Uint8Array = new Uint8Array([2, 4, 8]);
|
|
7
|
+
const arrayTwo: Uint8Array = new Uint8Array([16, 32, 64]);
|
|
8
|
+
const result: Uint8Array = Uint8ArrayRatchet.mergeArrays([arrayOne, arrayTwo]);
|
|
9
|
+
expect(result).toBeTruthy();
|
|
10
|
+
expect(result.length).toEqual(6);
|
|
11
|
+
expect(result[0]).toEqual(2);
|
|
12
|
+
expect(result[5]).toEqual(64);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('should compare arrays', function () {
|
|
16
|
+
const arrayOne: Uint8Array = new Uint8Array([2, 4, 8]);
|
|
17
|
+
const arrayOneX: Uint8Array = new Uint8Array([2, 4, 8]);
|
|
18
|
+
const arrayTwo: Uint8Array = new Uint8Array([16, 32, 64]);
|
|
19
|
+
expect(Uint8ArrayRatchet.deepEqual(arrayOne, arrayTwo)).toBeFalsy();
|
|
20
|
+
expect(Uint8ArrayRatchet.deepEqual(arrayOne, arrayOneX)).toBeTruthy();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Uint8Array is the common denominator between node and web apis
|
|
2
|
+
// https://medium.com/@naveenkumarasinghe/javascript-lost-in-binaries-buffer-blob-uint8array-arraybuffer-ed8d2b4de44a
|
|
3
|
+
export class Uint8ArrayRatchet {
|
|
4
|
+
// Prevent instantiation
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
6
|
+
private constructor() {}
|
|
7
|
+
|
|
8
|
+
// Taken from https://stackoverflow.com/questions/49129643/how-do-i-merge-an-array-of-uint8arrays
|
|
9
|
+
public static mergeArrays(myArrays: Uint8Array[]): Uint8Array {
|
|
10
|
+
let rval: Uint8Array = null;
|
|
11
|
+
if (myArrays?.length) {
|
|
12
|
+
// Get the total length of all arrays.
|
|
13
|
+
let length = 0;
|
|
14
|
+
myArrays.forEach((item) => {
|
|
15
|
+
length += item.length;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Create a new array with total length and merge all source arrays.
|
|
19
|
+
rval = new Uint8Array(length);
|
|
20
|
+
let offset = 0;
|
|
21
|
+
myArrays.forEach((item) => {
|
|
22
|
+
rval.set(item, offset);
|
|
23
|
+
offset += item.length;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return rval;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public static deepEqual(arr1: Uint8Array, arr2: Uint8Array): boolean {
|
|
30
|
+
let rval = false;
|
|
31
|
+
if (arr1 && arr2 && arr1.length === arr2.length) {
|
|
32
|
+
let mismatch = false;
|
|
33
|
+
for (let i = 0; i < arr1.length && !mismatch; i++) {
|
|
34
|
+
mismatch = arr1[i] !== arr2[i];
|
|
35
|
+
}
|
|
36
|
+
rval = !mismatch;
|
|
37
|
+
}
|
|
38
|
+
return rval;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Taken from https://stackoverflow.com/a/22114687
|
|
42
|
+
public static deepCopy(src: Uint8Array): Uint8Array {
|
|
43
|
+
const tmp: ArrayBuffer = new ArrayBuffer(src.byteLength);
|
|
44
|
+
const dst: Uint8Array = new Uint8Array(tmp);
|
|
45
|
+
dst.set(new Uint8Array(src));
|
|
46
|
+
return dst;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { WebStreamRatchet } from './web-stream-ratchet.js';
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
|
|
4
|
+
describe('#WebStreamRatchet', function () {
|
|
5
|
+
test('should roundtrip from string to stream and back', async () => {
|
|
6
|
+
const input = 'test';
|
|
7
|
+
const r: ReadableStream = WebStreamRatchet.stringToWebReadableStream('test');
|
|
8
|
+
const out: string = await WebStreamRatchet.webReadableStreamToString(r);
|
|
9
|
+
|
|
10
|
+
expect(input).toEqual(out);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ErrorRatchet } from './error-ratchet.js';
|
|
2
|
+
import { StringRatchet } from './string-ratchet.js';
|
|
3
|
+
import { Uint8ArrayRatchet } from './uint-8-array-ratchet.js';
|
|
4
|
+
import { Logger } from '../logger/logger.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This class is specifically for dealing with web streams, NOT
|
|
8
|
+
* node streams (ie, ReadableStream and WriteableStream, NOT
|
|
9
|
+
* Readable and Writeable, and CERTAINLY not the FS based streams)
|
|
10
|
+
*/
|
|
11
|
+
export class WebStreamRatchet {
|
|
12
|
+
// Prevent instantiation
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
14
|
+
private constructor() {}
|
|
15
|
+
|
|
16
|
+
public static async webReadableStreamToUint8Array(stream: ReadableStream): Promise<Uint8Array> {
|
|
17
|
+
const out: Uint8Array[] = [];
|
|
18
|
+
const writer: WritableStream = new WritableStream(
|
|
19
|
+
{
|
|
20
|
+
async write(chunk, _controller): Promise<any> {
|
|
21
|
+
if (typeof chunk === 'string') {
|
|
22
|
+
out.push(StringRatchet.stringToUint8Array(chunk));
|
|
23
|
+
} else {
|
|
24
|
+
out.push(chunk);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return null;
|
|
28
|
+
},
|
|
29
|
+
abort(reason) {
|
|
30
|
+
ErrorRatchet.throwFormattedErr('StringWebWritableStream failure : %s', reason);
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
highWaterMark: 3,
|
|
35
|
+
size: () => 1,
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
try {
|
|
39
|
+
Logger.debug('Starting pipe');
|
|
40
|
+
await stream.pipeTo(writer, { preventAbort: true, preventCancel: true, preventClose: true });
|
|
41
|
+
Logger.debug('Completed pipe');
|
|
42
|
+
return Uint8ArrayRatchet.mergeArrays(out);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
Logger.error('Caught error while trying to convert to array : %s', err, err);
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
public static async webReadableStreamToString(stream: ReadableStream): Promise<string> {
|
|
49
|
+
const buf: Uint8Array = await WebStreamRatchet.webReadableStreamToUint8Array(stream);
|
|
50
|
+
return new TextDecoder().decode(buf);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public static stringToWebReadableStream(input: string): ReadableStream {
|
|
54
|
+
//if (typeof ReadableStream !== 'undefined') {
|
|
55
|
+
// ErrorRatchet.throwFormattedErr('ReadableStream not supported on this platform');
|
|
56
|
+
// }
|
|
57
|
+
|
|
58
|
+
const rval: ReadableStream = new ReadableStream<string>(
|
|
59
|
+
{
|
|
60
|
+
start(controller) {
|
|
61
|
+
if (input) {
|
|
62
|
+
controller.enqueue(input); // May as well write it all
|
|
63
|
+
}
|
|
64
|
+
controller.close();
|
|
65
|
+
return null;
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
highWaterMark: input ? input.length : null,
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
return rval;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public static uint8ArrayToWebReadableStream(input: Uint8Array): ReadableStream {
|
|
76
|
+
//if (typeof ReadableStream !== 'undefined') {
|
|
77
|
+
// ErrorRatchet.throwFormattedErr('ReadableStream not supported on this platform');
|
|
78
|
+
// }
|
|
79
|
+
|
|
80
|
+
const rval: ReadableStream = new ReadableStream<Uint8Array>(
|
|
81
|
+
{
|
|
82
|
+
start(controller) {
|
|
83
|
+
if (input) {
|
|
84
|
+
controller.enqueue(input); // May as well write it all
|
|
85
|
+
}
|
|
86
|
+
controller.close();
|
|
87
|
+
return null;
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
highWaterMark: input ? input.length : null,
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
return rval;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LogMessage } from './log-message.js';
|
|
2
|
+
import { LogMessageFormatter } from './log-message-formatter.js';
|
|
3
|
+
import { LoggerMeta } from './logger-meta.js';
|
|
4
|
+
import { StringRatchet } from '../lang/string-ratchet.js';
|
|
5
|
+
|
|
6
|
+
export class ClassicSingleLineLogMessageFormatter implements LogMessageFormatter {
|
|
7
|
+
public formatMessage(msg: LogMessage, meta: LoggerMeta): string {
|
|
8
|
+
let tmp: string = null;
|
|
9
|
+
|
|
10
|
+
if (msg) {
|
|
11
|
+
tmp = '';
|
|
12
|
+
tmp += meta?.options?.trace ? meta.options.trace + ' ' : '';
|
|
13
|
+
|
|
14
|
+
tmp += '[' + msg.lvl + '] ';
|
|
15
|
+
tmp += StringRatchet.format(msg?.messageSource ?? '', ...(msg.subsVars || []));
|
|
16
|
+
}
|
|
17
|
+
return tmp;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { LoggerLevelName } from './logger-level-name.js';
|
|
2
|
+
import { LogMessage } from './log-message.js';
|
|
3
|
+
|
|
4
|
+
export class LogMessageBuilder {
|
|
5
|
+
private wrapped: LogMessage;
|
|
6
|
+
|
|
7
|
+
constructor(lvl: LoggerLevelName, messageSource?: string) {
|
|
8
|
+
if (!lvl) {
|
|
9
|
+
throw Error('Cannot set level to null/undefined');
|
|
10
|
+
}
|
|
11
|
+
this.wrapped = { lvl: lvl, timestamp: Date.now(), messageSource: messageSource };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public clone(): LogMessageBuilder {
|
|
15
|
+
const rval: LogMessageBuilder = new LogMessageBuilder(this.wrapped.lvl, this.wrapped.messageSource);
|
|
16
|
+
rval.wrapped = Object.assign({}, this.wrapped);
|
|
17
|
+
return rval;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public level(lvl: LoggerLevelName): LogMessageBuilder {
|
|
21
|
+
if (!lvl) {
|
|
22
|
+
throw Error('Cannot set level to null/undefined');
|
|
23
|
+
}
|
|
24
|
+
this.wrapped.lvl = lvl;
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public timestamp(val: number): LogMessageBuilder {
|
|
29
|
+
if (!val) {
|
|
30
|
+
throw Error('Cannot set timestamp to null/undefined');
|
|
31
|
+
}
|
|
32
|
+
this.wrapped.timestamp = val;
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public messageSource(val: string): LogMessageBuilder {
|
|
37
|
+
this.wrapped.messageSource = val;
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public subVars(val: any[]): LogMessageBuilder {
|
|
42
|
+
this.wrapped.subsVars = val || [];
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public params(val: Record<string, string | number | boolean>): LogMessageBuilder {
|
|
47
|
+
this.wrapped.params = val || {};
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public p(name: string, value: string | number | boolean): LogMessageBuilder {
|
|
52
|
+
this.wrapped.params = this.wrapped.params || {};
|
|
53
|
+
this.wrapped.params[name] = value;
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public toMessage(): LogMessage {
|
|
58
|
+
return Object.assign({}, this.wrapped);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// NOTE: This is a psuedo-enum to fix some issues with Typescript enums. See: https://exploringjs.com/tackling-ts/ch_enum-alternatives.html for details
|
|
2
|
+
|
|
3
|
+
export const LogMessageFormatType = {
|
|
4
|
+
ClassicSingleLine: 'ClassicSingleLine',
|
|
5
|
+
SingleLineNoLevel: 'SingleLineNoLevel',
|
|
6
|
+
StructuredJson: 'StructuredJson',
|
|
7
|
+
|
|
8
|
+
None: 'None',
|
|
9
|
+
} as const;
|
|
10
|
+
|
|
11
|
+
export type LogMessageFormatType = (typeof LogMessageFormatType)[keyof typeof LogMessageFormatType];
|