@bitblit/ratchet-common 6.0.146-alpha → 6.0.147-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.
Files changed (140) hide show
  1. package/package.json +2 -1
  2. package/src/2d/line-2d.ts +6 -0
  3. package/src/2d/matrix-factory.ts +94 -0
  4. package/src/2d/plane-2d-type.ts +6 -0
  5. package/src/2d/plane-2d.ts +7 -0
  6. package/src/2d/point-2d.ts +4 -0
  7. package/src/2d/poly-line-2d.ts +5 -0
  8. package/src/2d/ratchet-2d.spec.ts +205 -0
  9. package/src/2d/ratchet-2d.ts +350 -0
  10. package/src/2d/transformation-matrix.ts +19 -0
  11. package/src/build/build-information.ts +8 -0
  12. package/src/build/ratchet-common-info.ts +19 -0
  13. package/src/histogram/histogram-entry.ts +4 -0
  14. package/src/histogram/histogram.spec.ts +25 -0
  15. package/src/histogram/histogram.ts +61 -0
  16. package/src/jwt/common-jwt-token.ts +17 -0
  17. package/src/jwt/expired-jwt-handling.ts +5 -0
  18. package/src/jwt/jwt-decode-only-ratchet.ts +26 -0
  19. package/src/jwt/jwt-payload-expiration-ratchet.ts +45 -0
  20. package/src/jwt/jwt-token-base.ts +14 -0
  21. package/src/lang/array-ratchet.spec.ts +79 -0
  22. package/src/lang/array-ratchet.ts +141 -0
  23. package/src/lang/base64-ratchet.spec.ts +48 -0
  24. package/src/lang/base64-ratchet.ts +247 -0
  25. package/src/lang/boolean-ratchet.spec.ts +95 -0
  26. package/src/lang/boolean-ratchet.ts +52 -0
  27. package/src/lang/composite-last-success-provider.spec.ts +31 -0
  28. package/src/lang/composite-last-success-provider.ts +30 -0
  29. package/src/lang/currency-ratchet.ts +29 -0
  30. package/src/lang/date-ratchet.spec.ts +27 -0
  31. package/src/lang/date-ratchet.ts +42 -0
  32. package/src/lang/duration-ratchet.spec.ts +47 -0
  33. package/src/lang/duration-ratchet.ts +77 -0
  34. package/src/lang/enum-ratchet.spec.ts +45 -0
  35. package/src/lang/enum-ratchet.ts +41 -0
  36. package/src/lang/error-handling-approach.ts +6 -0
  37. package/src/lang/error-ratchet.spec.ts +25 -0
  38. package/src/lang/error-ratchet.ts +70 -0
  39. package/src/lang/esm-ratchet.ts +81 -0
  40. package/src/lang/expiring-object.spec.ts +56 -0
  41. package/src/lang/expiring-object.ts +84 -0
  42. package/src/lang/geolocation-ratchet.spec.ts +177 -0
  43. package/src/lang/geolocation-ratchet.ts +341 -0
  44. package/src/lang/global-ratchet.spec.ts +17 -0
  45. package/src/lang/global-ratchet.ts +105 -0
  46. package/src/lang/key-value.ts +8 -0
  47. package/src/lang/last-success-provider.ts +4 -0
  48. package/src/lang/map-ratchet.spec.ts +113 -0
  49. package/src/lang/map-ratchet.ts +220 -0
  50. package/src/lang/no.spec.ts +9 -0
  51. package/src/lang/no.ts +7 -0
  52. package/src/lang/number-ratchet.spec.ts +154 -0
  53. package/src/lang/number-ratchet.ts +253 -0
  54. package/src/lang/parsed-url.ts +10 -0
  55. package/src/lang/promise-ratchet.spec.ts +104 -0
  56. package/src/lang/promise-ratchet.ts +196 -0
  57. package/src/lang/range.ts +4 -0
  58. package/src/lang/require-ratchet.spec.ts +85 -0
  59. package/src/lang/require-ratchet.ts +68 -0
  60. package/src/lang/simple-arg-ratchet.spec.ts +13 -0
  61. package/src/lang/simple-arg-ratchet.ts +47 -0
  62. package/src/lang/simple-encryption-ratchet.ts +88 -0
  63. package/src/lang/sort-ratchet.spec.ts +58 -0
  64. package/src/lang/sort-ratchet.ts +50 -0
  65. package/src/lang/stop-watch.spec.ts +53 -0
  66. package/src/lang/stop-watch.ts +202 -0
  67. package/src/lang/string-ratchet.spec.ts +226 -0
  68. package/src/lang/string-ratchet.ts +676 -0
  69. package/src/lang/time-zone-ratchet.spec.ts +51 -0
  70. package/src/lang/time-zone-ratchet.ts +148 -0
  71. package/src/lang/timeout-token.spec.ts +12 -0
  72. package/src/lang/timeout-token.ts +21 -0
  73. package/src/lang/uint-8-array-ratchet.spec.ts +22 -0
  74. package/src/lang/uint-8-array-ratchet.ts +48 -0
  75. package/src/lang/web-stream-ratchet.spec.ts +12 -0
  76. package/src/lang/web-stream-ratchet.ts +96 -0
  77. package/src/logger/classic-single-line-log-message-formatter.ts +19 -0
  78. package/src/logger/log-message-builder.ts +60 -0
  79. package/src/logger/log-message-format-type.ts +11 -0
  80. package/src/logger/log-message-formatter.ts +6 -0
  81. package/src/logger/log-message-processor.ts +6 -0
  82. package/src/logger/log-message.ts +9 -0
  83. package/src/logger/log-snapshot.ts +6 -0
  84. package/src/logger/logger-instance.ts +269 -0
  85. package/src/logger/logger-level-name.ts +11 -0
  86. package/src/logger/logger-meta.ts +7 -0
  87. package/src/logger/logger-options.ts +14 -0
  88. package/src/logger/logger-output-function.ts +10 -0
  89. package/src/logger/logger-ring-buffer.ts +89 -0
  90. package/src/logger/logger-util.spec.ts +11 -0
  91. package/src/logger/logger-util.ts +68 -0
  92. package/src/logger/logger.spec.ts +177 -0
  93. package/src/logger/logger.ts +213 -0
  94. package/src/logger/none-log-message-formatter.ts +10 -0
  95. package/src/logger/single-line-no-level-log-message-formatter.ts +18 -0
  96. package/src/logger/structured-json-log-message-formatter.ts +25 -0
  97. package/src/mail/archive-email-result.ts +8 -0
  98. package/src/mail/email-attachment.ts +23 -0
  99. package/src/mail/mail-sending-provider.ts +21 -0
  100. package/src/mail/mailer-config.ts +30 -0
  101. package/src/mail/mailer-like.ts +38 -0
  102. package/src/mail/mailer-util.ts +65 -0
  103. package/src/mail/mailer.spec.ts +120 -0
  104. package/src/mail/mailer.ts +214 -0
  105. package/src/mail/ready-to-send-email.ts +67 -0
  106. package/src/mail/resolved-ready-to-send-email.ts +17 -0
  107. package/src/mail/send-email-result.ts +16 -0
  108. package/src/mail/test-mail-sending-provider.ts +35 -0
  109. package/src/network/browser-local-ip-provider.spec.ts +23 -0
  110. package/src/network/browser-local-ip-provider.ts +26 -0
  111. package/src/network/fixed-local-ip-provider.ts +9 -0
  112. package/src/network/local-ip-provider.ts +4 -0
  113. package/src/network/network-ratchet.spec.ts +17 -0
  114. package/src/network/network-ratchet.ts +209 -0
  115. package/src/network/remote-file-tracker/backup-result.ts +6 -0
  116. package/src/network/remote-file-tracker/file-transfer-result-type.ts +5 -0
  117. package/src/network/remote-file-tracker/file-transfer-result.ts +9 -0
  118. package/src/network/remote-file-tracker/remote-file-tracker-options.ts +6 -0
  119. package/src/network/remote-file-tracker/remote-file-tracker-push-options.ts +4 -0
  120. package/src/network/remote-file-tracker/remote-file-tracker.ts +117 -0
  121. package/src/network/remote-file-tracker/remote-file-tracking-provider.ts +19 -0
  122. package/src/network/remote-file-tracker/remote-status-data-and-content.ts +6 -0
  123. package/src/network/remote-file-tracker/remote-status-data.ts +7 -0
  124. package/src/network/restful-api-http-error.spec.ts +13 -0
  125. package/src/network/restful-api-http-error.ts +173 -0
  126. package/src/template/ratchet-template-renderer.ts +8 -0
  127. package/src/third-party/google/google-recaptcha-ratchet.spec.ts +27 -0
  128. package/src/third-party/google/google-recaptcha-ratchet.ts +36 -0
  129. package/src/third-party/twilio/twilio-ratchet.ts +92 -0
  130. package/src/third-party/twilio/twilio-verify-ratchet.ts +83 -0
  131. package/src/transform/built-in-transforms.ts +214 -0
  132. package/src/transform/transform-ratchet.spec.ts +134 -0
  133. package/src/transform/transform-ratchet.ts +88 -0
  134. package/src/transform/transform-rule.ts +7 -0
  135. package/src/tx/transaction-configuration.ts +8 -0
  136. package/src/tx/transaction-final-state.ts +7 -0
  137. package/src/tx/transaction-ratchet.spec.ts +150 -0
  138. package/src/tx/transaction-ratchet.ts +98 -0
  139. package/src/tx/transaction-result.ts +10 -0
  140. package/src/tx/transaction-step.ts +5 -0
@@ -0,0 +1,341 @@
1
+ /*
2
+ Functions for working with geolocations
3
+ */
4
+
5
+ import { RequireRatchet } from './require-ratchet.js';
6
+ import { NumberRatchet } from './number-ratchet.js';
7
+ import { ErrorRatchet } from './error-ratchet.js';
8
+ import { Logger } from '../logger/logger.js';
9
+
10
+ export class GeolocationRatchet {
11
+ // Prevent instantiation
12
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
13
+ private constructor() {}
14
+
15
+ // Ripped off almost straight from https://www.geodatasource.com/developers/javascript
16
+ // All credit to those original authors
17
+ // Units one of M for miles, K for kilometers, N for nautical miles, F for feet, E for meters
18
+ public static distanceBetweenLocations(lat1: number, lon1: number, lat2: number, lon2: number, unit = 'M'): number {
19
+ const uU: string = unit ? unit.toUpperCase() : '';
20
+
21
+ if (['M', 'K', 'N', 'F', 'E'].indexOf(uU) === -1) {
22
+ throw new Error('Invalid unit');
23
+ }
24
+ if (lat1 == lat2 && lon1 == lon2) {
25
+ return 0;
26
+ } else {
27
+ const radlat1: number = (Math.PI * lat1) / 180;
28
+ const radlat2: number = (Math.PI * lat2) / 180;
29
+ const theta: number = lon1 - lon2;
30
+ const radtheta: number = (Math.PI * theta) / 180;
31
+ let dist: number = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
32
+ if (dist > 1) {
33
+ dist = 1;
34
+ }
35
+ dist = Math.acos(dist);
36
+ dist = (dist * 180) / Math.PI;
37
+ dist = dist * 60 * 1.1515;
38
+ if (uU === 'F') {
39
+ dist *= 5280;
40
+ }
41
+ if (uU === 'K') {
42
+ dist *= 1.609344;
43
+ }
44
+ if (uU === 'E') {
45
+ dist *= 1609.344;
46
+ }
47
+ if (uU === 'N') {
48
+ dist *= 0.8684;
49
+ }
50
+ return dist;
51
+ }
52
+ }
53
+
54
+ public static distanceBetweenRatchetGeoLocations(loc1: RatchetGeoLocation, loc2: RatchetGeoLocation, unit = 'M'): number {
55
+ return GeolocationRatchet.distanceBetweenLocations(loc1.lat, loc1.lng, loc2.lat, loc2.lng, unit);
56
+ }
57
+
58
+ public static degreeOfLatLngInMiles(latitudeInDecimalDegress = 0): number {
59
+ // It doesn't matter at what longitude you are. What matters is what latitude you are.
60
+ // Length of 1 degree of Longitude = cosine (latitude in decimal degrees) * length of degree (miles) at equator.
61
+ // Convert your latitude into decimal degrees ~ 37.26383
62
+ // Convert your decimal degrees into radians ~ 0.65038
63
+ // Angle in radians = Angle in degrees x PI / 180
64
+ // Take the cosine of the value in radians ~ 0.79585
65
+ // 1 degree of Longitude = ~0.79585 * 69.172 = ~ 55.051 miles
66
+ const latInRads: number = (latitudeInDecimalDegress * Math.PI) / 180;
67
+ const cosLat: number = Math.cos(latInRads);
68
+ const rval: number = NumberRatchet.safeNumber((cosLat * 69.172).toFixed(4));
69
+ return rval;
70
+ }
71
+
72
+ public static milesInDegLatLng(miles: number, latitudeInDecimalDegress = 0): number {
73
+ RequireRatchet.notNullOrUndefined(miles);
74
+ RequireRatchet.true(miles >= 0);
75
+ const degreeInMiles: number = GeolocationRatchet.degreeOfLatLngInMiles(latitudeInDecimalDegress);
76
+ return miles / degreeInMiles;
77
+ }
78
+
79
+ public static centerOfBounds(bounds: RatchetLocationBounds): RatchetGeoLocation {
80
+ RequireRatchet.notNullOrUndefined(bounds);
81
+ return {
82
+ lat: (bounds.extent.lat + bounds.origin.lat) / 2,
83
+ lng: (bounds.extent.lng + bounds.origin.lng) / 2,
84
+ };
85
+ }
86
+
87
+ private static calculateSplits(input: RatchetLocationBounds[], slices: number, field: string): SplitPoint[] {
88
+ RequireRatchet.notNullOrUndefined(input);
89
+ RequireRatchet.notNullOrUndefined(slices);
90
+ RequireRatchet.notNullOrUndefined(field);
91
+
92
+ input.sort((a, b) => a.origin[field] - b.origin[field]);
93
+ const centers: RatchetGeoLocation[] = input.map((i) => GeolocationRatchet.centerOfBounds(i));
94
+ const vals: number[] = centers.map((c) => c[field]);
95
+ const splits: SplitPoint[] = [];
96
+ for (let i = 1; i < vals.length; i++) {
97
+ const size: number = vals[i] - vals[i - 1];
98
+ if (splits.length < slices) {
99
+ splits.push({ idx: i, size: size });
100
+ splits.sort((a, b) => a.size - b.size);
101
+ } else if (size > splits[0].size) {
102
+ splits[0] = { idx: i, size: size };
103
+ splits.sort((a, b) => a.size - b.size);
104
+ } else {
105
+ Logger.silly('Skipping, size : %d, %j', size, splits);
106
+ }
107
+ }
108
+ Logger.info('Splits at : %j', splits);
109
+ splits.sort((a, b) => a.idx - b.idx);
110
+ return splits;
111
+ }
112
+
113
+ public static clusterGeoBounds(inputVal: RatchetLocationBounds[], latSlices = 2, lngSlices = 5): RatchetLocationBounds[] {
114
+ let rval: RatchetLocationBounds[] = null;
115
+ if (latSlices * lngSlices < 2) {
116
+ ErrorRatchet.throwFormattedErr('Cannot set slices to less than 2 : %d x %d', latSlices, lngSlices);
117
+ }
118
+ if (inputVal) {
119
+ rval = [];
120
+ const input: RatchetLocationBounds[] = Object.assign([], inputVal); // Don't want to modify the original
121
+ input.sort((a, b) => a.origin.lng - b.origin.lng);
122
+ const lngSplits: SplitPoint[] = GeolocationRatchet.calculateSplits(inputVal, lngSlices - 1, 'lng');
123
+ lngSplits.sort((a, b) => a.idx - b.idx);
124
+ for (let i = 0; i <= lngSplits.length; i++) {
125
+ const lngStartIdx: number = i === 0 ? 0 : lngSplits[i - 1].idx;
126
+ const lngEndIdx: number = i === lngSplits.length ? input.length : lngSplits[i].idx;
127
+ const lngBatch: RatchetLocationBounds[] = input.slice(lngStartIdx, lngEndIdx);
128
+ // Now split by lats
129
+ lngBatch.sort((a, b) => a.origin.lat - b.origin.lat);
130
+ const latSplits: SplitPoint[] = GeolocationRatchet.calculateSplits(lngBatch, latSlices - 1, 'lat');
131
+ for (let j = 0; j <= latSplits.length; j++) {
132
+ const latStartIdx: number = j == 0 ? 0 : latSplits[j - 1].idx;
133
+ const latEndIdx: number = j === latSplits.length ? lngBatch.length : latSplits[j].idx;
134
+ const latBatch: RatchetLocationBounds[] = lngBatch.slice(latStartIdx, latEndIdx);
135
+ rval.push(GeolocationRatchet.combineBounds(latBatch));
136
+ }
137
+ }
138
+ Logger.info('New bounds : %j', rval);
139
+ }
140
+
141
+ // const orig: RatchetLocationBounds = GeolocationRatchet.combineBounds(inputVal);
142
+ // const next: RatchetLocationBounds = GeolocationRatchet.combineBounds(rval);
143
+ // Logger.info('P: %j', orig);
144
+ // Logger.info('S: %j', next);
145
+
146
+ return rval;
147
+ }
148
+
149
+ // Puts a bounds into canonical form (upper left, lower right)
150
+ // If a crossover occurs (on both sides of date line) will throw an exception unless allow specified
151
+ public static canonicalizeBounds(inp: RatchetLocationBounds, allowCrossover = false): RatchetLocationBounds {
152
+ RequireRatchet.notNullOrUndefined(inp, 'RatchetLocationBounds');
153
+ const minLat: number = Math.min(inp.extent.lat, inp.origin.lat);
154
+ const maxLat: number = Math.max(inp.extent.lat, inp.origin.lat);
155
+ const minLng: number = Math.min(inp.extent.lng, inp.origin.lng);
156
+ const maxLng: number = Math.max(inp.extent.lng, inp.origin.lng);
157
+ const latXover: boolean = (minLat < 0 && maxLat > 0) || (minLat > 0 && maxLat < 0);
158
+ const lngXover: boolean = (minLat < 0 && maxLat > 0) || (minLat > 0 && maxLat < 0);
159
+ if (latXover || lngXover) {
160
+ if (allowCrossover) {
161
+ return inp;
162
+ } else {
163
+ throw new Error('Cannot canonicalize, bounds crosses over boundary');
164
+ }
165
+ }
166
+ const rval: RatchetLocationBounds = {
167
+ origin: {
168
+ lat: minLat,
169
+ lng: minLng,
170
+ },
171
+ extent: {
172
+ lat: maxLat,
173
+ lng: maxLng,
174
+ },
175
+ };
176
+ return rval;
177
+ }
178
+
179
+ public static combineBounds(inp: RatchetLocationBounds[]): RatchetLocationBounds {
180
+ let rval: RatchetLocationBounds = null;
181
+ if (inp && inp.length > 0) {
182
+ rval = {
183
+ origin: {
184
+ lat: inp.map((i) => i.origin.lat).reduce((a, i) => Math.min(a, i)),
185
+ lng: inp.map((i) => i.origin.lng).reduce((a, i) => Math.min(a, i)),
186
+ },
187
+ extent: {
188
+ lat: inp.map((i) => i.extent.lat).reduce((a, i) => Math.max(a, i)),
189
+ lng: inp.map((i) => i.extent.lng).reduce((a, i) => Math.max(a, i)),
190
+ },
191
+ };
192
+ }
193
+ return rval;
194
+ }
195
+
196
+ public static roundLocation(r: RatchetGeoLocation, roundDigits: number): RatchetGeoLocation {
197
+ return {
198
+ lat: NumberRatchet.safeNumber(r.lat.toFixed(roundDigits)),
199
+ lng: NumberRatchet.safeNumber(r.lng.toFixed(roundDigits)),
200
+ };
201
+ }
202
+
203
+ public static locationToBounds(loc: RatchetGeoLocation, radiusMiles: number): RatchetLocationBounds {
204
+ const offset: number = GeolocationRatchet.milesInDegLatLng(radiusMiles, loc.lat);
205
+ const gfb: RatchetLocationBounds = {
206
+ origin: {
207
+ lat: loc.lat - offset,
208
+ lng: loc.lng - offset,
209
+ },
210
+ extent: {
211
+ lat: loc.lat + offset,
212
+ lng: loc.lng + offset,
213
+ },
214
+ };
215
+ return gfb;
216
+ }
217
+
218
+ public static sameLocation(loc1: RatchetGeoLocation, loc2: RatchetGeoLocation): boolean {
219
+ return !!loc1 && !!loc2 && loc1.lat === loc2.lat && loc1.lng === loc2.lng;
220
+ }
221
+
222
+ public static pointInBounds(pt: RatchetGeoLocation, bound: RatchetLocationBounds): boolean {
223
+ return (
224
+ !!pt &&
225
+ !!bound &&
226
+ NumberRatchet.between(pt.lat, bound.origin.lat, bound.extent.lat) &&
227
+ NumberRatchet.between(pt.lng, bound.origin.lng, bound.extent.lng)
228
+ );
229
+ }
230
+
231
+ public static pointInAnyBound(pt: RatchetGeoLocation, inBounds: RatchetLocationBounds[], minPointsBeforeMapping = 10): boolean {
232
+ let rval = false;
233
+ if (inBounds.length > minPointsBeforeMapping) {
234
+ const mp: RatchetLocationBoundsMap = GeolocationRatchet.buildRatchetLocationBoundsMap(inBounds);
235
+ rval = GeolocationRatchet.pointInRatchetLocationBoundsMap(pt, mp);
236
+ } else {
237
+ for (let i = 0; i < inBounds.length && !rval; i++) {
238
+ rval = GeolocationRatchet.pointInBounds(pt, inBounds[i]);
239
+ }
240
+ }
241
+ return rval;
242
+ }
243
+
244
+ public static pointInRatchetLocationBoundsMap(pt: RatchetGeoLocation, mp: RatchetLocationBoundsMap): boolean {
245
+ let rval = false;
246
+ const entry: RatchetLocationBoundsMapEntry = GeolocationRatchet.findRatchetLocationBoundsMapEntry(pt, mp);
247
+ if (entry) {
248
+ const bounds: RatchetLocationBounds[] = entry.bounds;
249
+
250
+ for (let i = 0; i < bounds.length && !rval; i++) {
251
+ rval = GeolocationRatchet.pointInBounds(pt, bounds[i]);
252
+ }
253
+ }
254
+
255
+ return rval;
256
+ }
257
+
258
+ public static findRatchetLocationBoundsMapEntry(pt: RatchetGeoLocation, mp: RatchetLocationBoundsMap): RatchetLocationBoundsMapEntry {
259
+ let rval: RatchetLocationBoundsMapEntry = null;
260
+ if (pt.lat >= mp.latOffset && pt.lat <= mp.maxLat && pt.lng >= mp.lngOffset && pt.lng <= mp.maxLng) {
261
+ const ltIdx: number = Math.trunc(pt.lat) - mp.latOffset;
262
+ const lngIdx: number = Math.trunc(pt.lng) - mp.lngOffset;
263
+ rval = mp.mapping[ltIdx][lngIdx];
264
+ }
265
+
266
+ return rval;
267
+ }
268
+
269
+ public static buildRatchetLocationBoundsMap(inBounds: RatchetLocationBounds[]): RatchetLocationBoundsMap {
270
+ const minLat: number = inBounds.map((i) => i.origin.lat).reduce((a, i) => Math.min(a, i));
271
+ const minLng: number = inBounds.map((i) => i.origin.lng).reduce((a, i) => Math.min(a, i));
272
+ const maxLat: number = inBounds.map((i) => i.extent.lat).reduce((a, i) => Math.max(a, i));
273
+ const maxLng: number = inBounds.map((i) => i.extent.lng).reduce((a, i) => Math.max(a, i));
274
+
275
+ const latOffset: number = Math.trunc(minLat) - 1;
276
+ const lngOffset: number = Math.trunc(minLng) - 1;
277
+ const latEntries: number = Math.trunc(maxLat) - latOffset + 1;
278
+ const lngEntries: number = Math.trunc(maxLng) - lngOffset + 1;
279
+
280
+ const mapping: RatchetLocationBoundsMapEntry[][] = [];
281
+ for (let i = 0; i < latEntries; i++) {
282
+ const newRow: RatchetLocationBoundsMapEntry[] = [];
283
+ for (let j = 0; j < lngEntries; j++) {
284
+ newRow.push({
285
+ lat: latOffset + i,
286
+ lng: lngOffset + j,
287
+ bounds: [],
288
+ });
289
+ }
290
+ mapping.push(newRow);
291
+ }
292
+
293
+ inBounds.forEach((b) => {
294
+ for (let i = Math.trunc(b.origin.lat); i <= Math.trunc(b.extent.lat); i++) {
295
+ const latIdx: number = i - latOffset;
296
+ const row: RatchetLocationBoundsMapEntry[] = mapping[latIdx];
297
+ for (let j = Math.trunc(b.origin.lng); j <= Math.trunc(b.extent.lng); j++) {
298
+ const lngIdx: number = j - lngOffset;
299
+ row[lngIdx].bounds.push(b);
300
+ }
301
+ }
302
+ });
303
+
304
+ return {
305
+ latOffset: latOffset,
306
+ lngOffset: lngOffset,
307
+ maxLat: latOffset + latEntries,
308
+ maxLng: lngOffset + lngEntries,
309
+ mapping: mapping,
310
+ };
311
+ }
312
+ }
313
+
314
+ export interface RatchetGeoLocation {
315
+ lat: number;
316
+ lng: number;
317
+ }
318
+
319
+ export interface RatchetLocationBounds {
320
+ origin: RatchetGeoLocation;
321
+ extent: RatchetGeoLocation;
322
+ }
323
+
324
+ export interface RatchetLocationBoundsMap {
325
+ latOffset: number;
326
+ lngOffset: number;
327
+ maxLat: number;
328
+ maxLng: number;
329
+ mapping: RatchetLocationBoundsMapEntry[][];
330
+ }
331
+
332
+ export interface RatchetLocationBoundsMapEntry {
333
+ lat: number;
334
+ lng: number;
335
+ bounds: RatchetLocationBounds[];
336
+ }
337
+
338
+ interface SplitPoint {
339
+ idx: number;
340
+ size: number;
341
+ }
@@ -0,0 +1,17 @@
1
+ import { GlobalRatchet } from './global-ratchet.js';
2
+ import { StringRatchet } from './string-ratchet.js';
3
+ import { describe, expect, test } from 'vitest';
4
+
5
+ describe('#globalRatchet', function () {
6
+ test('fetch and set global variables', async () => {
7
+ const tester: string = StringRatchet.createType4Guid();
8
+ const tester2: string = StringRatchet.createType4Guid();
9
+ process.env['GLOBALTEST'] = tester;
10
+
11
+ const value: string = GlobalRatchet.fetchGlobalEnvVar('GLOBALTEST');
12
+ expect(value).toEqual(tester);
13
+ GlobalRatchet.setGlobalEnvVar('GLOBALTEST', tester2);
14
+ const value2: string = GlobalRatchet.fetchGlobalEnvVar('GLOBALTEST');
15
+ expect(value2).toEqual(tester2);
16
+ });
17
+ });
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Some helpers to make it safe to have the node-only classes inside Ratchet
3
+ *
4
+ * Since lots of other ratchets use global for safe singletons, we dont
5
+ * depend on anything in here (Especially logger)
6
+ */
7
+ import { RequireRatchet } from './require-ratchet.js';
8
+
9
+ export class GlobalRatchet {
10
+ // Prevent instantiation
11
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
12
+ private constructor() {}
13
+
14
+ public static globalExists(): boolean {
15
+ return typeof global !== 'undefined';
16
+ }
17
+
18
+ public static windowExists(): boolean {
19
+ return typeof window !== 'undefined';
20
+ }
21
+
22
+ public static processExists(): boolean {
23
+ return typeof process !== 'undefined';
24
+ }
25
+
26
+ // Fetches the global scope objects (string-> object)
27
+ public static fetchGlobalVarsRecord(returnNullOnNone?: boolean): Record<string, any> {
28
+ let rval: Record<string, any> = null;
29
+ try {
30
+ if (GlobalRatchet.globalExists()) {
31
+ rval = global;
32
+ } else if (GlobalRatchet.windowExists()) {
33
+ // In the browser
34
+ rval = window;
35
+ } else if (GlobalRatchet.processExists()) {
36
+ rval = process; // Final failover, not really a great option
37
+ }
38
+ } catch (err) {
39
+ throw new Error('Should not happen - error thrown trying to find global : '+ err);
40
+ }
41
+ if (!rval) {
42
+ if (returnNullOnNone) {
43
+ rval = null;
44
+ } else {
45
+ throw new Error('Could not find global, process, or window in scope');
46
+ }
47
+ }
48
+ return rval;
49
+ }
50
+
51
+ public static fetchGlobalVar<T>(key: string, defValue?: T): T {
52
+ const rec: Record<string, any> = GlobalRatchet.fetchGlobalVarsRecord(false);
53
+ let rval: T = rec[key];
54
+ if (!rval && defValue) {
55
+ rec[key] = defValue;
56
+ rval = defValue;
57
+ }
58
+ return rval;
59
+ }
60
+
61
+ public static setGlobalVar(key: string, val: any): void {
62
+ const rec: Record<string, any> = GlobalRatchet.fetchGlobalVarsRecord(false);
63
+ rec[key] = val;
64
+ }
65
+
66
+ // This is meant to handle faking env vars (basically string-> string) in the context of a browser, polyfills, etc
67
+ public static fetchGlobalEnvVarRecord(returnNullOnNone?: boolean): Record<string, any> {
68
+ let rval: Record<string, any> = null;
69
+ if (GlobalRatchet.processExists() && process?.env) {
70
+ // Running in node
71
+ rval = process.env;
72
+ } else if (GlobalRatchet.globalExists() && global?.process?.env) {
73
+ // Running poly-filled node
74
+ rval = global.process.env;
75
+ } else if (GlobalRatchet.globalExists()) {
76
+ rval = global;
77
+ } else if (GlobalRatchet.windowExists()) {
78
+ // In the browser
79
+ rval = window;
80
+ }
81
+ if (!rval) {
82
+ if (returnNullOnNone) {
83
+ rval = null;
84
+ } else {
85
+ throw new Error('Could not find process.env OR global OR window in scope');
86
+ }
87
+ }
88
+ return rval;
89
+ }
90
+
91
+ public static fetchGlobalEnvVar(envVar: string): string {
92
+ RequireRatchet.notNullOrUndefined(envVar, 'envVar');
93
+ const myGlobal: Record<string, any> = GlobalRatchet.fetchGlobalEnvVarRecord();
94
+ const value: any = myGlobal[envVar];
95
+ const sValue: string = value ? String(value) : value;
96
+ return sValue;
97
+ }
98
+
99
+ public static setGlobalEnvVar(envVar: string, value: string): void {
100
+ RequireRatchet.notNullOrUndefined(envVar, 'envVar');
101
+ RequireRatchet.notNullOrUndefined(value, 'value');
102
+ const myGlobal: Record<string, any> = GlobalRatchet.fetchGlobalEnvVarRecord();
103
+ myGlobal[envVar] = value;
104
+ }
105
+ }
@@ -0,0 +1,8 @@
1
+ /*
2
+ Simple strongly typed key-value
3
+ */
4
+
5
+ export class KeyValue<T> {
6
+ key: string;
7
+ value: T;
8
+ }
@@ -0,0 +1,4 @@
1
+ /** Classes implementing this interface return a timestamp of their last success * */
2
+ export interface LastSuccessProvider {
3
+ lastSuccess(): number;
4
+ }
@@ -0,0 +1,113 @@
1
+ import { KeyValue } from './key-value.js';
2
+ import { MapRatchet } from './map-ratchet.js';
3
+ import { describe, expect, test } from 'vitest';
4
+
5
+ describe('#toKeyValueList', function () {
6
+ test('should expand a maps nested keys', function () {
7
+ const test: any = { 'a.b': 'c', 'c.d.e': 2, 'a.d': ['BLAH'] };
8
+ const out: any = MapRatchet.expandNestedKeysToObjects<any>(test);
9
+
10
+ console.info(JSON.stringify(out));
11
+
12
+ expect(out).toBeTruthy();
13
+ expect(out.a).toBeTruthy();
14
+ expect(out.a.b).toBeTruthy();
15
+ expect(out.a.b).toEqual('c');
16
+ expect(out.c).toBeTruthy();
17
+ expect(out.c.d).toBeTruthy();
18
+ expect(out.c.d.e).toBeTruthy();
19
+ expect(out.c.d.e).toEqual(2);
20
+ expect(out.a.d).toBeTruthy();
21
+ expect(out.a.d.length).toEqual(1);
22
+ expect(out.a.d[0]).toEqual('BLAH');
23
+ });
24
+
25
+ test('should convert a map to a key-value list and back', function () {
26
+ const test: any = { key1: 'value1', key2: 'value2' };
27
+
28
+ const kvl: KeyValue<any>[] = MapRatchet.toKeyValueList(test);
29
+ expect(kvl.length).toEqual(2);
30
+
31
+ const back: any = MapRatchet.fromKeyValueList(kvl);
32
+
33
+ expect(back['key1']).toEqual('value1');
34
+ expect(back['key2']).toEqual('value2');
35
+ });
36
+
37
+ test('should remove null and empty strings but not 0', function () {
38
+ const test: any = {
39
+ key1: '',
40
+ key2: null,
41
+ key3: 0,
42
+ key4: 'fine',
43
+ key5: {
44
+ key6: null,
45
+ key7: 'also_fine',
46
+ },
47
+ };
48
+
49
+ const clean: any = MapRatchet.cleanup(test);
50
+
51
+ expect(Object.keys(clean).length).toEqual(3);
52
+ expect(Object.keys(clean).indexOf('key1')).toEqual(-1);
53
+ expect(Object.keys(clean).indexOf('key2')).toEqual(-1);
54
+ expect(Object.keys(clean).indexOf('key3')).toBeGreaterThan(-1);
55
+ expect(Object.keys(clean).indexOf('key4')).toBeGreaterThan(-1);
56
+ expect(Object.keys(clean).indexOf('key5')).toBeGreaterThan(-1);
57
+ expect(clean['key5']['key7']).toEqual('also_fine');
58
+ });
59
+
60
+ test('should extract values ignoring case', function () {
61
+ const test: any = {
62
+ 'this-Test': 'a',
63
+ test22: 'b',
64
+ };
65
+
66
+ expect(MapRatchet.extractValueFromMapIgnoreCase(test, 'THIS-TEST')).toEqual('a');
67
+ expect(MapRatchet.extractValueFromMapIgnoreCase(test, 'tEst22')).toEqual('b');
68
+ });
69
+
70
+ test('should group values', function () {
71
+ const test: any[] = [
72
+ {
73
+ id: 1,
74
+ type: 'a',
75
+ },
76
+ {
77
+ id: 2,
78
+ type: 'a',
79
+ },
80
+ {
81
+ id: 3,
82
+ type: 'b',
83
+ },
84
+ ];
85
+
86
+ const grouped: Map<string, any> = MapRatchet.groupByProperty<any, string>(test, 'type');
87
+
88
+ expect(grouped).toBeTruthy();
89
+ const aGroup: any[] = grouped.get('a');
90
+ expect(aGroup).toBeTruthy();
91
+ expect(aGroup.length).toEqual(2);
92
+ expect(grouped.has('b')).toBeTruthy();
93
+ expect(grouped.has('c')).toBeFalsy();
94
+ });
95
+
96
+ test('should fetch a value with a case-insensitive key', function () {
97
+ const testOb: any = {
98
+ test1: 'a',
99
+ Test2: 'b',
100
+ test3: 'c',
101
+ Test3: 'd',
102
+ };
103
+
104
+ expect(MapRatchet.caseInsensitiveAccess(null, null)).toBeNull();
105
+ expect(MapRatchet.caseInsensitiveAccess({}, null)).toBeNull();
106
+ expect(MapRatchet.caseInsensitiveAccess(null, 'a')).toBeNull();
107
+
108
+ expect(MapRatchet.caseInsensitiveAccess(testOb, 'test1')).toEqual('a');
109
+ expect(MapRatchet.caseInsensitiveAccess(testOb, 'test2')).toEqual('b');
110
+ expect(MapRatchet.caseInsensitiveAccess(testOb, 'TEST2')).toEqual('b');
111
+ expect(MapRatchet.caseInsensitiveAccess(testOb, 'test3')).toEqual('c');
112
+ });
113
+ });