@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 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.89.23",
3
+ "version": "3.91.1",
4
4
  "license": "GPL-3.0",
5
- "main": "./lib/index.js",
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
- "flow": "flow; test $? -eq 0 -o $? -eq 2",
13
- "test": "rollup --config rollup_test.config.js && node 'build/test/Suite.js'"
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
- "lib/*",
18
+ "dist/*",
18
19
  "README.md",
19
20
  "LICENSE.txt"
20
21
  ],
21
22
  "devDependencies": {
22
- "@babel/core": "7.14.6",
23
- "@babel/plugin-proposal-class-properties": "7.14.5",
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
- }
@@ -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
- }
@@ -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
- }