@tutao/tutanota-utils 3.89.23 → 3.91.1
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/README.md +1 -6
- package/package.json +8 -16
- package/lib/ArrayUtils.js +0 -444
- package/lib/AsyncResult.js +0 -37
- package/lib/CollectionUtils.js +0 -10
- package/lib/DateUtils.js +0 -134
- package/lib/Encoding.js +0 -329
- package/lib/LazyLoaded.js +0 -87
- package/lib/MapUtils.js +0 -42
- package/lib/MathUtils.js +0 -12
- package/lib/PromiseMap.js +0 -121
- package/lib/PromiseUtils.js +0 -144
- package/lib/SortedArray.js +0 -66
- package/lib/StringUtils.js +0 -126
- package/lib/TypeRef.js +0 -25
- package/lib/Utils.js +0 -416
- package/lib/index.js +0 -207
package/README.md
CHANGED
|
@@ -3,9 +3,4 @@
|
|
|
3
3
|
This is a collection of common utils we use across multiple projects/modules internally. As creating this module really
|
|
4
4
|
is just an intermediate step towards re-organising some of the dependency structure of our software, it is most likely
|
|
5
5
|
going to change a lot in the future and might even disappear altogether. For these reasons **we strongly discourage
|
|
6
|
-
anyone outside of our organisation to directly depend on this module**.
|
|
7
|
-
|
|
8
|
-
For now we only provide this as a flow-typed source module and not compiled for ES6 or CJS. The reason is that our
|
|
9
|
-
current tooling (flow, rollup, webstorm) will not allow us to use the compiled version directly without putting a lot of
|
|
10
|
-
effort into restructuring the way we build tutanota and/or changing some of our tools or losing some convenient IDE
|
|
11
|
-
features such as usage search.
|
|
6
|
+
anyone outside of our organisation to directly depend on this module**.
|
package/package.json
CHANGED
|
@@ -1,35 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tutao/tutanota-utils",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.91.1",
|
|
4
4
|
"license": "GPL-3.0",
|
|
5
|
-
"main": "./
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "https://github.com/tutao/tutanota.git",
|
|
9
10
|
"directory": "packages/tutanota-utils"
|
|
10
11
|
},
|
|
11
12
|
"scripts": {
|
|
12
|
-
"
|
|
13
|
-
"
|
|
13
|
+
"test": "rm -r build; tsc --project test/tsconfig.json && cd build/test && node 'Suite.js'",
|
|
14
|
+
"build": "rm -r dist; tsc"
|
|
14
15
|
},
|
|
15
16
|
"type": "module",
|
|
16
17
|
"files": [
|
|
17
|
-
"
|
|
18
|
+
"dist/*",
|
|
18
19
|
"README.md",
|
|
19
20
|
"LICENSE.txt"
|
|
20
21
|
],
|
|
21
22
|
"devDependencies": {
|
|
22
|
-
"@
|
|
23
|
-
"
|
|
24
|
-
"@babel/plugin-transform-flow-strip-types": "7.4.4",
|
|
25
|
-
"@babel/plugin-proposal-nullish-coalescing-operator": "7.14.5",
|
|
26
|
-
"@babel/plugin-proposal-optional-chaining": "7.14.5",
|
|
27
|
-
"@rollup/plugin-commonjs": "21.0.1",
|
|
28
|
-
"@rollup/plugin-babel": "5.2.2",
|
|
29
|
-
"@rollup/plugin-node-resolve": "11.0.1",
|
|
30
|
-
"flow-bin": "0.152.0",
|
|
31
|
-
"rollup": "2.55.1",
|
|
32
|
-
"@tutao/tutanota-test-utils": "3.89.23",
|
|
23
|
+
"@tutao/tutanota-test-utils": "3.91.1",
|
|
24
|
+
"typescript": "4.5.4",
|
|
33
25
|
"ospec": "https://github.com/tutao/ospec.git#0472107629ede33be4c4d19e89f237a6d7b0cb11"
|
|
34
26
|
}
|
|
35
27
|
}
|
package/lib/ArrayUtils.js
DELETED
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
//@flow
|
|
2
|
-
import {downcast, identity, neverNull} from "./Utils"
|
|
3
|
-
import {getFromMap} from "./MapUtils"
|
|
4
|
-
|
|
5
|
-
export function concat(...arrays: Uint8Array[]): Uint8Array {
|
|
6
|
-
let length = arrays.reduce((previous, current) => previous + current.length, 0)
|
|
7
|
-
let result = new Uint8Array(length)
|
|
8
|
-
let index = 0
|
|
9
|
-
arrays.forEach(array => {
|
|
10
|
-
result.set(array, index)
|
|
11
|
-
index += array.length
|
|
12
|
-
})
|
|
13
|
-
return result
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function numberRange(min: number, max: number): Array<number> {
|
|
17
|
-
return [...Array(max + 1).keys()].slice(min)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Compares two arrays for equality based on ===.
|
|
22
|
-
* @param {Array} a1 The first array.
|
|
23
|
-
* @param {Array} a2 The second array.
|
|
24
|
-
* @return {boolean} True if the arrays are equal, false otherwise.
|
|
25
|
-
*
|
|
26
|
-
* It is valid to compare Uint8Array to Array<T>, don't restrict it to be one type
|
|
27
|
-
*/
|
|
28
|
-
export function arrayEquals<T, A: Uint8Array | Array<T>>(a1: A, a2: A): boolean {
|
|
29
|
-
if (a1 === a2) {
|
|
30
|
-
return true
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (a1.length === a2.length) {
|
|
34
|
-
for (let i = 0; i < a1.length; i++) {
|
|
35
|
-
if (a1[i] !== a2[i]) {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Compares two arrays for equality based on a predicate
|
|
46
|
-
* @param a1
|
|
47
|
-
* @param a2
|
|
48
|
-
* @param predicate
|
|
49
|
-
* @returns {boolean}
|
|
50
|
-
*/
|
|
51
|
-
export function arrayEqualsWithPredicate<T>(a1: $ReadOnlyArray<T>, a2: $ReadOnlyArray<T>, predicate: (T, T) => boolean): boolean {
|
|
52
|
-
if (a1.length === a2.length) {
|
|
53
|
-
for (let i = 0; i < a1.length; i++) {
|
|
54
|
-
if (!predicate(a1[i], a2[i])) {
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function arrayHash(array: Uint8Array): number {
|
|
64
|
-
let hash = 0;
|
|
65
|
-
hash |= 0;
|
|
66
|
-
for (let i = 0; i < array.length; i++) {
|
|
67
|
-
hash = ((hash << 5) - hash) + array[i];
|
|
68
|
-
hash |= 0; // Convert to 32bit integer
|
|
69
|
-
}
|
|
70
|
-
return hash;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Remove the element from theArray if it is contained in the array.
|
|
75
|
-
* @param theArray The array to remove the element from.
|
|
76
|
-
* @param elementToRemove The element to remove from the array.
|
|
77
|
-
* @return True if the element was removed, false otherwise.
|
|
78
|
-
*/
|
|
79
|
-
export function remove<T>(theArray: Array<T>, elementToRemove: T): boolean {
|
|
80
|
-
let i = theArray.indexOf(elementToRemove)
|
|
81
|
-
if (i !== -1) {
|
|
82
|
-
theArray.splice(i, 1)
|
|
83
|
-
return true;
|
|
84
|
-
} else {
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Find all items in an array that pass the given predicate
|
|
91
|
-
*/
|
|
92
|
-
export function findAll<T>(theArray: Array<T>, finder: (T) => boolean): Array<T> {
|
|
93
|
-
const found = []
|
|
94
|
-
for (let element of theArray) {
|
|
95
|
-
if (finder(element)) {
|
|
96
|
-
found.push(element)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return found
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* @param theArray
|
|
104
|
-
* @param finder
|
|
105
|
-
* @return {boolean} if the element was found
|
|
106
|
-
*/
|
|
107
|
-
export function findAndRemove<T>(theArray: Array<T>, finder: (T) => boolean): boolean {
|
|
108
|
-
const index = theArray.findIndex(finder)
|
|
109
|
-
if (index !== -1) {
|
|
110
|
-
theArray.splice(index, 1)
|
|
111
|
-
return true
|
|
112
|
-
} else {
|
|
113
|
-
return false
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function findAllAndRemove<T>(theArray: Array<T>, finder: (T) => boolean, startIndex: number = 0): boolean {
|
|
118
|
-
var removedElement = false
|
|
119
|
-
for (let i = theArray.length - 1; i >= startIndex; i--) {
|
|
120
|
-
if (finder(theArray[i])) {
|
|
121
|
-
theArray.splice(i, 1)
|
|
122
|
-
removedElement = true
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return removedElement
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function replace(theArray: Array<any>, oldElement: any, newElement: any): boolean {
|
|
129
|
-
let i = theArray.indexOf(oldElement)
|
|
130
|
-
if (i !== -1) {
|
|
131
|
-
theArray.splice(i, 1, newElement)
|
|
132
|
-
return true;
|
|
133
|
-
} else {
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Same as filterMap in some languages. Apply mapper and then only include non-nullable items.
|
|
140
|
-
*/
|
|
141
|
-
export function mapAndFilterNull<T, R>(array: $ReadOnlyArray<T>, mapper: (T) => ?R): Array<R> {
|
|
142
|
-
const resultList = []
|
|
143
|
-
for (const item of array) {
|
|
144
|
-
const resultItem = mapper(item)
|
|
145
|
-
if (resultItem != null) {
|
|
146
|
-
resultList.push(resultItem)
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return resultList
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export function filterNull<T>(array: $ReadOnlyArray<?T>): Array<T> {
|
|
153
|
-
return downcast(array.filter(item => item != null))
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Provides the last element of the given array.
|
|
158
|
-
* @param theArray The array.
|
|
159
|
-
* @return The last element of the array.
|
|
160
|
-
*/
|
|
161
|
-
export function last<T>(theArray: $ReadOnlyArray<T>): ?T {
|
|
162
|
-
return theArray[theArray.length - 1];
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export function isEmpty<T>(array: $ReadOnlyArray<T>): boolean {
|
|
166
|
-
return array.length === 0
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export function lastThrow<T>(array: $ReadOnlyArray<T>): T {
|
|
170
|
-
if (isEmpty(array)) {
|
|
171
|
-
throw new RangeError("Array is empty")
|
|
172
|
-
}
|
|
173
|
-
return neverNull(last(array))
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export function firstThrow<T>(array: $ReadOnlyArray<T>): T {
|
|
177
|
-
if (isEmpty(array)) {
|
|
178
|
-
throw new RangeError("Array is empty")
|
|
179
|
-
}
|
|
180
|
-
return array[0]
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export function first<T>(array: $ReadOnlyArray<T>): T | null {
|
|
184
|
-
return array[0] || null
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export function findLast<T>(array: Array<T>, predicate: (T) => boolean): ?T {
|
|
188
|
-
const index = findLastIndex(array, predicate)
|
|
189
|
-
if (index !== -1) {
|
|
190
|
-
return array[index]
|
|
191
|
-
}
|
|
192
|
-
return null
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export function findLastIndex<T>(array: Array<T>, predicate: (T) => boolean): number {
|
|
196
|
-
for (let i = array.length - 1; i >= 0; i--) {
|
|
197
|
-
if (predicate(array[i])) {
|
|
198
|
-
return i
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return -1
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
export function contains(theArray: Array<any>, elementToCheck: any): boolean {
|
|
205
|
-
return theArray.indexOf(elementToCheck) !== -1
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
export function addAll(array: Array<any>, elements: Array<any>) {
|
|
209
|
-
array.push(...elements)
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export function removeAll(array: Array<any>, elements: Array<any>) {
|
|
213
|
-
elements.forEach(element => {
|
|
214
|
-
remove(array, element)
|
|
215
|
-
})
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Group an array based on the given discriminator, but each group will have only unique items
|
|
220
|
-
*/
|
|
221
|
-
export function groupByAndMapUniquely<T, R, E>(iterable: Iterable<T>, discriminator: T => R, mapper: T => E): Map<R, Set<E>> {
|
|
222
|
-
const map = new Map()
|
|
223
|
-
for (let el of iterable) {
|
|
224
|
-
const key = discriminator(el)
|
|
225
|
-
getFromMap(map, key, () => new Set()).add(mapper(el))
|
|
226
|
-
}
|
|
227
|
-
return map
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* convert an Array of T's into a Map of Arrays of E's by
|
|
232
|
-
* * grouping them based on a discriminator
|
|
233
|
-
* * mapping them from T to E
|
|
234
|
-
* @param iterable the array to split into groups
|
|
235
|
-
* @param discriminator a function that produces the keys to group the elements by
|
|
236
|
-
* @param mapper a function that maps the array elements before they get added to the group
|
|
237
|
-
* @returns {Map<R, Array<E>>}
|
|
238
|
-
*/
|
|
239
|
-
export function groupByAndMap<T, R, E>(iterable: Iterable<T>, discriminator: (T) => R, mapper: (T) => E): Map<R, Array<E>> {
|
|
240
|
-
const map = new Map()
|
|
241
|
-
for (let el of iterable) {
|
|
242
|
-
const key = discriminator(el)
|
|
243
|
-
getFromMap(map, key, () => []).push(mapper(el))
|
|
244
|
-
}
|
|
245
|
-
return map
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Group array elements based on keys produced by a discriminator
|
|
250
|
-
* @param iterable the array to split into groups
|
|
251
|
-
* @param discriminator a function that produces the keys to group the elements by
|
|
252
|
-
* @returns {NodeJS.Global.Map<R, Array<T>>}
|
|
253
|
-
*/
|
|
254
|
-
export function groupBy<T, R>(iterable: Iterable<T>, discriminator: (T) => R): Map<R, Array<T>> {
|
|
255
|
-
return groupByAndMap(iterable, discriminator, identity)
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* split an array into chunks of a given size.
|
|
260
|
-
* the last chunk will be smaller if there are less than chunkSize elements left.
|
|
261
|
-
* @param chunkSize
|
|
262
|
-
* @param array
|
|
263
|
-
* @returns {Array<Array<T>>}
|
|
264
|
-
*/
|
|
265
|
-
export function splitInChunks<T>(chunkSize: number, array: Array<T>): Array<Array<T>> {
|
|
266
|
-
if (chunkSize < 1) {
|
|
267
|
-
return []
|
|
268
|
-
}
|
|
269
|
-
let chunkNum = 0
|
|
270
|
-
const chunks = []
|
|
271
|
-
let end
|
|
272
|
-
do {
|
|
273
|
-
let start = chunkNum * chunkSize
|
|
274
|
-
end = start + chunkSize
|
|
275
|
-
chunks[chunkNum] = array.slice(start, end)
|
|
276
|
-
chunkNum++
|
|
277
|
-
} while (end < array.length)
|
|
278
|
-
return chunks
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
export function flat<T>(arrays: $ReadOnlyArray<$ReadOnlyArray<T>>): Array<T> {
|
|
282
|
-
return arrays.reduce((acc, val) => {
|
|
283
|
-
acc.push(...val)
|
|
284
|
-
return acc
|
|
285
|
-
}, [])
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Maps an array into a nested array and then flattens it
|
|
290
|
-
* @param array
|
|
291
|
-
* @param mapper
|
|
292
|
-
* @returns {T|*[]}
|
|
293
|
-
*/
|
|
294
|
-
export function flatMap<T, U>(array: $ReadOnlyArray<T>, mapper: T => Array<U>): Array<U> {
|
|
295
|
-
return array.reduce((acc, val) => acc.concat(mapper(val)), [])
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Inserts element into the sorted array. Will find <b>the last</b> matching position.
|
|
301
|
-
* Might add or replace element based on {@param replaceIf} identity check.
|
|
302
|
-
* Equality per {@param comparator} is precondition for replacement.
|
|
303
|
-
* @param element to place
|
|
304
|
-
* @param array where element should be placed
|
|
305
|
-
* @param comparator for sorting
|
|
306
|
-
* @param replaceIf identity comparison for replacement
|
|
307
|
-
*/
|
|
308
|
-
export function insertIntoSortedArray<T>(element: T, array: Array<T>,
|
|
309
|
-
comparator: (left: T, right: T) => number,
|
|
310
|
-
replaceIf: (newElement: T, existing: T) => boolean = () => false) {
|
|
311
|
-
let i = 0
|
|
312
|
-
while (i < array.length) {
|
|
313
|
-
const compareResult = comparator(array[i], element)
|
|
314
|
-
// We need to check for replacement for each element that is equal or we might miss it
|
|
315
|
-
if (compareResult === 0 && replaceIf(element, array[i])) {
|
|
316
|
-
array.splice(i, 1, element)
|
|
317
|
-
return
|
|
318
|
-
} else if (compareResult <= 0) {
|
|
319
|
-
// We continue searching until the last suitable position
|
|
320
|
-
i++
|
|
321
|
-
} else {
|
|
322
|
-
break
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// This also handles empty array
|
|
327
|
-
array.splice(i, 0, element)
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
export function zip<A, B>(arr1: Array<A>, arr2: Array<B>): Array<[A, B]> {
|
|
331
|
-
const zipped = []
|
|
332
|
-
for (let i = 0; i < Math.min(arr1.length, arr2.length); i++) {
|
|
333
|
-
zipped.push([arr1[i], arr2[i]])
|
|
334
|
-
}
|
|
335
|
-
return zipped
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
export function deduplicate<T>(arr: Array<T>, comp: (T, T) => boolean = (a, b) => a === b): Array<T> {
|
|
339
|
-
const deduplicated = []
|
|
340
|
-
arr.forEach(a => {
|
|
341
|
-
const isDuplicate = deduplicated.some(b => comp(a, b))
|
|
342
|
-
if (!isDuplicate) {
|
|
343
|
-
deduplicated.push(a)
|
|
344
|
-
}
|
|
345
|
-
})
|
|
346
|
-
return deduplicated
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* http://jsfiddle.net/aryzhov/pkfst550/
|
|
352
|
-
* Binary search in JavaScript.
|
|
353
|
-
* Returns the index of of the element in a sorted array or (-n-1) where n is the insertion point for the new element.
|
|
354
|
-
* Parameters:
|
|
355
|
-
* ar - A sorted array
|
|
356
|
-
* el - An element to search for
|
|
357
|
-
* compare_fn - A comparator function. The function takes two arguments: (a, b) and returns:
|
|
358
|
-
* a negative number if a is less than b;
|
|
359
|
-
* 0 if a is equal to b;
|
|
360
|
-
* a positive number of a is greater than b.
|
|
361
|
-
* The array may contain duplicate elements. If there are more than one equal elements in the array,
|
|
362
|
-
* the returned value can be the index of any one of the equal elements.
|
|
363
|
-
*/
|
|
364
|
-
export function binarySearch<T>(ar: Array<T>, el: T, compare_fn: (T, T) => number): number {
|
|
365
|
-
var m = 0;
|
|
366
|
-
var n = ar.length - 1;
|
|
367
|
-
while (m <= n) {
|
|
368
|
-
var k = (n + m) >> 1;
|
|
369
|
-
var cmp = compare_fn(el, ar[k]);
|
|
370
|
-
if (cmp > 0) {
|
|
371
|
-
m = k + 1;
|
|
372
|
-
} else if (cmp < 0) {
|
|
373
|
-
n = k - 1;
|
|
374
|
-
} else {
|
|
375
|
-
return k;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
return -m - 1;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
export function lastIndex<T>(array: $ReadOnlyArray<T>): number {
|
|
382
|
-
if (array.length === 0) {
|
|
383
|
-
return 0
|
|
384
|
-
} else {
|
|
385
|
-
return array.length - 1
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* All of the elements in all of the arguments combined, and deduplicated
|
|
391
|
-
*/
|
|
392
|
-
export function union<T>(...iterables: Array<Iterable<T>>): Set<T> {
|
|
393
|
-
return new Set(...iterables.map(iterable => Array.from(iterable)))
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* return a new array containing every item from array1 that isn't in array2
|
|
398
|
-
* @param array1
|
|
399
|
-
* @param array2
|
|
400
|
-
* @param compare: compare items in the array for equality
|
|
401
|
-
* @returns {Array<$NonMaybeType<T>>|Array<T>}
|
|
402
|
-
*/
|
|
403
|
-
export function difference<T>(array1: $ReadOnlyArray<T>, array2: $ReadOnlyArray<T>, compare: (T, T) => boolean): Array<T> {
|
|
404
|
-
return array1.filter(element1 => !array2.some(element2 => compare(element1, element2)))
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* Returns a set with elements that are *not* in both sets.
|
|
409
|
-
*
|
|
410
|
-
* {a, b, c} △ {b, c, d} == {a, d}
|
|
411
|
-
*/
|
|
412
|
-
export function symmetricDifference<T>(set1: $ReadOnlySet<T>, set2: $ReadOnlySet<T>): Set<T> {
|
|
413
|
-
const diff = new Set()
|
|
414
|
-
for (const el of set1) {
|
|
415
|
-
if (!set2.has(el)) {
|
|
416
|
-
diff.add(el)
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
for (const el of set2) {
|
|
420
|
-
if (!set1.has(el)) {
|
|
421
|
-
diff.add(el)
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
return diff
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* Splits an array into two based on a predicate, where elements that match the predicate go into the left side
|
|
429
|
-
* @param array
|
|
430
|
-
* @param predicate
|
|
431
|
-
*/
|
|
432
|
-
export function partition<T>(array: Array<T>, predicate: T => boolean): [Array<T>, Array<T>] {
|
|
433
|
-
const left = []
|
|
434
|
-
const right = []
|
|
435
|
-
for (let item of array) {
|
|
436
|
-
if (predicate(item)) {
|
|
437
|
-
left.push(item)
|
|
438
|
-
} else {
|
|
439
|
-
right.push(item)
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
return [left, right]
|
|
444
|
-
}
|
package/lib/AsyncResult.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
|
-
type StatePending<T> = {status: "pending", promise: Promise<T>}
|
|
4
|
-
type StateComplete<T> = {status: "complete", result: T}
|
|
5
|
-
type StateFailure = {status: "failure", error: any}
|
|
6
|
-
type AsyncResultState<T> = StatePending<T> | StateComplete<T> | StateFailure
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Represents a resource that is either not ready, ready, or error
|
|
10
|
-
* Sort of fills a similar role to LazyLoaded, usage is more verbose but also more typesafe. maybe this should be reconciled.
|
|
11
|
-
*/
|
|
12
|
-
export class AsyncResult<T> {
|
|
13
|
-
_state: AsyncResultState<T>
|
|
14
|
-
|
|
15
|
-
constructor(promise: Promise<T>) {
|
|
16
|
-
this._state = pending(promise)
|
|
17
|
-
promise
|
|
18
|
-
.then(result => this._state = complete(result))
|
|
19
|
-
.catch(error => this._state = failure(error))
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
state(): $ReadOnly<AsyncResultState<T>> {
|
|
23
|
-
return this._state
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function pending<T>(promise: Promise<T>): StatePending<T> {
|
|
28
|
-
return {status: "pending", promise}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function complete<T>(result: T): StateComplete<T> {
|
|
32
|
-
return {status: "complete", result}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function failure(error: any): StateFailure {
|
|
36
|
-
return {status: "failure", error}
|
|
37
|
-
}
|
package/lib/CollectionUtils.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Everything that is in both array1 and array2
|
|
6
|
-
* This is a naive implementation, don't use it on large inputs
|
|
7
|
-
*/
|
|
8
|
-
export function intersection<T>(set1: Set<T>, set2: Set<T>): Set<T> {
|
|
9
|
-
return new Set(Array.from(set1).filter(item => set2.has(item)))
|
|
10
|
-
}
|
package/lib/DateUtils.js
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file DateUtils which do not use Luxon. Used in worker as well as in client parts.
|
|
5
|
-
* As functions here do not use Luxon it cannot be used for calculating things in different time zones, they
|
|
6
|
-
* are dependent on the system time zone.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export const DAY_IN_MILLIS = 1000 * 60 * 60 * 24
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Provides a date representing the beginning of the next day of the given date in local time.
|
|
13
|
-
*/
|
|
14
|
-
export function getStartOfNextDay(date: Date): Date {
|
|
15
|
-
let d = new Date(date.getTime())
|
|
16
|
-
d.setDate(date.getDate() + 1);
|
|
17
|
-
d.setHours(0, 0, 0, 0) // sets the beginning of the day in local time
|
|
18
|
-
return d
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Provides a date representing the end of the given date in local time.
|
|
23
|
-
*/
|
|
24
|
-
export function getEndOfDay(date: Date): Date {
|
|
25
|
-
let d = new Date(date.getTime())
|
|
26
|
-
d.setHours(23, 59, 59, 999)
|
|
27
|
-
return d
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Provides a date representing the beginning of the given date in local time.
|
|
32
|
-
*/
|
|
33
|
-
export function getStartOfDay(date: Date): Date {
|
|
34
|
-
return getHourOfDay(date, 0)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Provides a date representing the day of the given date at the given hour in local time.
|
|
39
|
-
*/
|
|
40
|
-
export function getHourOfDay(date: Date, hour: number): Date {
|
|
41
|
-
let d = new Date(date.getTime())
|
|
42
|
-
d.setHours(hour, 0, 0, 0)
|
|
43
|
-
return d
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function isStartOfDay(date: Date): boolean {
|
|
47
|
-
return date.getHours() === 0 && date.getMinutes() === 0
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Returns true if the given date is today in local time.
|
|
52
|
-
*/
|
|
53
|
-
export function isToday(date: Date): boolean {
|
|
54
|
-
return new Date().toDateString() === date.toDateString()
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Returns true if the given dates represent the same day (time of day is ignored).
|
|
59
|
-
*/
|
|
60
|
-
export function isSameDay(date1: Date, date2: Date): boolean {
|
|
61
|
-
return date1.toDateString() === date2.toDateString()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Creates new date in with {@param days} added to it as if the days are just fixed
|
|
66
|
-
* periods of time and are not subject to daylight saving.
|
|
67
|
-
*/
|
|
68
|
-
export function getDayShifted(date: Date, days: number): Date {
|
|
69
|
-
return new Date(date.getTime() + days * DAY_IN_MILLIS)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Increment the date in place and return it
|
|
74
|
-
*/
|
|
75
|
-
export function incrementDate(date: Date, byValue: number): Date {
|
|
76
|
-
date.setDate(date.getDate() + byValue)
|
|
77
|
-
return date
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function incrementMonth(d: Date, byValue: number): Date {
|
|
81
|
-
const date = new Date(d)
|
|
82
|
-
date.setMonth(date.getMonth() + byValue)
|
|
83
|
-
return date
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function isSameDayOfDate(date1: ?Date, date2: ?Date): boolean {
|
|
87
|
-
return !date1 && !date2
|
|
88
|
-
|| date1 != null && date2 != null
|
|
89
|
-
&& date1.getFullYear() === date2.getFullYear()
|
|
90
|
-
&& date1.getMonth() === date2.getMonth()
|
|
91
|
-
&& date1.getDate() === date2.getDate()
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Formats as yyyy-mm-dd
|
|
97
|
-
*/
|
|
98
|
-
export function formatSortableDate(date: Date): string {
|
|
99
|
-
const month = ("0" + (date.getMonth() + 1)).slice(-2)
|
|
100
|
-
const day = ("0" + date.getDate()).slice(-2)
|
|
101
|
-
return `${date.getFullYear()}-${month}-${day}`
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Formats as yyyy-mm-dd-<hh>h-<mm>m-<ss>
|
|
106
|
-
*/
|
|
107
|
-
export function formatSortableDateTime(date: Date): string {
|
|
108
|
-
const hours = ("0" + date.getHours()).slice(-2)
|
|
109
|
-
const minutes = ("0" + date.getMinutes()).slice(-2)
|
|
110
|
-
const seconds = ("0" + date.getSeconds()).slice(-2)
|
|
111
|
-
return `${formatSortableDate(date)}-${hours}h${minutes}m${seconds}s`
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @returns {string} sortableDateTime of the current time
|
|
116
|
-
*/
|
|
117
|
-
export function sortableTimestamp(): string {
|
|
118
|
-
return formatSortableDateTime(new Date())
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export function isValidDate(date: Date): boolean {
|
|
122
|
-
return !isNaN(date.getTime())
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* not interested in any fancy calendar edge cases, only use this where approximation is ok
|
|
127
|
-
*/
|
|
128
|
-
export function millisToDays(millis: number): number {
|
|
129
|
-
return millis / DAY_IN_MILLIS
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function daysToMillis(days: number): number {
|
|
133
|
-
return days * DAY_IN_MILLIS
|
|
134
|
-
}
|