@naturalcycles/js-lib 14.97.0 → 14.98.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/dist/datetime/dateInterval.d.ts +8 -7
- package/dist/datetime/dateInterval.js +18 -15
- package/dist/datetime/localDate.d.ts +4 -9
- package/dist/datetime/localDate.js +58 -56
- package/dist/datetime/localTime.d.ts +4 -2
- package/dist/datetime/localTime.js +33 -10
- package/dist/datetime/timeInterval.d.ts +38 -0
- package/dist/datetime/timeInterval.js +91 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/json-schema/jsonSchemaBuilder.d.ts +2 -2
- package/dist/math/math.util.d.ts +4 -0
- package/dist/math/math.util.js +8 -1
- package/dist/math/sma.d.ts +4 -0
- package/dist/math/sma.js +4 -0
- package/dist/types.d.ts +27 -27
- package/dist-esm/datetime/dateInterval.js +18 -15
- package/dist-esm/datetime/localDate.js +58 -56
- package/dist-esm/datetime/localTime.js +33 -10
- package/dist-esm/datetime/timeInterval.js +87 -0
- package/dist-esm/index.js +1 -0
- package/dist-esm/math/math.util.js +6 -0
- package/dist-esm/math/sma.js +4 -0
- package/package.json +1 -1
- package/src/datetime/dateInterval.ts +22 -18
- package/src/datetime/localDate.ts +66 -85
- package/src/datetime/localTime.ts +37 -11
- package/src/datetime/timeInterval.ts +104 -0
- package/src/index.ts +4 -0
- package/src/json-schema/jsonSchemaBuilder.ts +6 -2
- package/src/math/math.util.ts +7 -0
- package/src/math/sma.ts +4 -0
- package/src/types.ts +39 -32
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { _assert } from '../error/assert'
|
|
2
|
-
import {
|
|
3
|
-
import { END, IsoDateString, UnixTimestampNumber } from '../types'
|
|
2
|
+
import { IsoDateString, UnixTimestampNumber } from '../types'
|
|
4
3
|
import { LocalTime } from './localTime'
|
|
5
4
|
|
|
6
5
|
export type LocalDateUnit = 'year' | 'month' | 'day'
|
|
7
6
|
export type Inclusiveness = '()' | '[]' | '[)' | '(]'
|
|
8
7
|
|
|
9
|
-
const
|
|
8
|
+
const MDAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
9
|
+
const DATE_REGEX = /^(\d\d\d\d)-(\d\d)-(\d\d)$/
|
|
10
10
|
|
|
11
|
-
export type LocalDateConfig = LocalDate |
|
|
11
|
+
export type LocalDateConfig = LocalDate | IsoDateString
|
|
12
12
|
|
|
13
13
|
/* eslint-disable no-dupe-class-members */
|
|
14
14
|
|
|
@@ -61,8 +61,13 @@ export class LocalDate {
|
|
|
61
61
|
if (!d) return null
|
|
62
62
|
if (d instanceof LocalDate) return d
|
|
63
63
|
|
|
64
|
-
//
|
|
65
|
-
const
|
|
64
|
+
// const [year, month, day] = d.slice(0, 10).split('-').map(Number)
|
|
65
|
+
const matches = DATE_REGEX.exec(d.slice(0, 10))
|
|
66
|
+
if (!matches) return null
|
|
67
|
+
|
|
68
|
+
const year = Number(matches[1])
|
|
69
|
+
const month = Number(matches[2])
|
|
70
|
+
const day = Number(matches[3])
|
|
66
71
|
|
|
67
72
|
if (
|
|
68
73
|
!year ||
|
|
@@ -79,6 +84,11 @@ export class LocalDate {
|
|
|
79
84
|
return new LocalDate(year, month, day)
|
|
80
85
|
}
|
|
81
86
|
|
|
87
|
+
// Can use just .toString()
|
|
88
|
+
// static parseToString(d: LocalDateConfig): IsoDateString {
|
|
89
|
+
// return typeof d === 'string' ? d : d.toString()
|
|
90
|
+
// }
|
|
91
|
+
|
|
82
92
|
static isValid(iso: string | undefined | null): boolean {
|
|
83
93
|
return this.parseOrNull(iso) !== null
|
|
84
94
|
}
|
|
@@ -117,64 +127,30 @@ export class LocalDate {
|
|
|
117
127
|
}
|
|
118
128
|
|
|
119
129
|
static range(
|
|
120
|
-
|
|
121
|
-
|
|
130
|
+
min: LocalDateConfig,
|
|
131
|
+
max: LocalDateConfig,
|
|
132
|
+
incl: Inclusiveness = '[)',
|
|
122
133
|
step = 1,
|
|
123
134
|
stepUnit: LocalDateUnit = 'day',
|
|
124
135
|
): LocalDate[] {
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
const max = LocalDate.of(
|
|
128
|
-
|
|
129
|
-
do {
|
|
130
|
-
days.push(current)
|
|
131
|
-
current = current.add(step, stepUnit)
|
|
132
|
-
} while (current.isBefore(max))
|
|
133
|
-
|
|
134
|
-
return days
|
|
135
|
-
}
|
|
136
|
+
const dates: LocalDate[] = []
|
|
137
|
+
const $min = LocalDate.of(min)
|
|
138
|
+
const $max = LocalDate.of(max).startOf(stepUnit)
|
|
136
139
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const min = LocalDate.of(minIncl).startOf(stepUnit)
|
|
144
|
-
const max = LocalDate.of(maxExcl).startOf(stepUnit)
|
|
145
|
-
return Sequence.create(min, d => {
|
|
146
|
-
const next = d.add(step, stepUnit)
|
|
147
|
-
return next.isAfter(max) ? END : next
|
|
148
|
-
})
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
static rangeString(
|
|
152
|
-
minIncl: LocalDateConfig,
|
|
153
|
-
maxExcl: LocalDateConfig,
|
|
154
|
-
step = 1,
|
|
155
|
-
stepUnit: LocalDateUnit = 'day',
|
|
156
|
-
): IsoDateString[] {
|
|
157
|
-
return LocalDate.range(minIncl, maxExcl, step, stepUnit).map(ld => ld.toString())
|
|
158
|
-
}
|
|
140
|
+
let current = $min.startOf(stepUnit)
|
|
141
|
+
if (current.isAfter($min, incl[0] === '[')) {
|
|
142
|
+
// ok
|
|
143
|
+
} else {
|
|
144
|
+
current.add(1, stepUnit, true)
|
|
145
|
+
}
|
|
159
146
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
): LocalDate[] {
|
|
166
|
-
return LocalDate.range(minIncl, LocalDate.of(maxIncl).add(1, stepUnit), step, stepUnit)
|
|
167
|
-
}
|
|
147
|
+
const incl2 = incl[1] === ']'
|
|
148
|
+
while (current.isBefore($max, incl2)) {
|
|
149
|
+
dates.push(current)
|
|
150
|
+
current = current.add(step, stepUnit)
|
|
151
|
+
}
|
|
168
152
|
|
|
169
|
-
|
|
170
|
-
minIncl: LocalDateConfig,
|
|
171
|
-
maxIncl: LocalDateConfig,
|
|
172
|
-
step = 1,
|
|
173
|
-
stepUnit: LocalDateUnit = 'day',
|
|
174
|
-
): IsoDateString[] {
|
|
175
|
-
return LocalDate.range(minIncl, LocalDate.of(maxIncl).add(1, stepUnit), step, stepUnit).map(
|
|
176
|
-
ld => ld.toString(),
|
|
177
|
-
)
|
|
153
|
+
return dates
|
|
178
154
|
}
|
|
179
155
|
|
|
180
156
|
get(unit: LocalDateUnit): number {
|
|
@@ -216,16 +192,18 @@ export class LocalDate {
|
|
|
216
192
|
return this.$day === d.$day && this.$month === d.$month && this.$year === d.$year
|
|
217
193
|
}
|
|
218
194
|
|
|
219
|
-
isBefore(d: LocalDateConfig): boolean {
|
|
220
|
-
|
|
195
|
+
isBefore(d: LocalDateConfig, inclusive = false): boolean {
|
|
196
|
+
const r = this.cmp(d)
|
|
197
|
+
return r === -1 || (r === 0 && inclusive)
|
|
221
198
|
}
|
|
222
199
|
|
|
223
200
|
isSameOrBefore(d: LocalDateConfig): boolean {
|
|
224
201
|
return this.cmp(d) <= 0
|
|
225
202
|
}
|
|
226
203
|
|
|
227
|
-
isAfter(d: LocalDateConfig): boolean {
|
|
228
|
-
|
|
204
|
+
isAfter(d: LocalDateConfig, inclusive = false): boolean {
|
|
205
|
+
const r = this.cmp(d)
|
|
206
|
+
return r === 1 || (r === 0 && inclusive)
|
|
229
207
|
}
|
|
230
208
|
|
|
231
209
|
isSameOrAfter(d: LocalDateConfig): boolean {
|
|
@@ -317,26 +295,31 @@ export class LocalDate {
|
|
|
317
295
|
}
|
|
318
296
|
|
|
319
297
|
// check day overflow
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
298
|
+
if (unit === 'day') {
|
|
299
|
+
if ($day < 1) {
|
|
300
|
+
while ($day < 1) {
|
|
301
|
+
$month -= 1
|
|
302
|
+
if ($month < 1) {
|
|
303
|
+
$year -= 1
|
|
304
|
+
$month += 12
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
$day += LocalDate.getMonthLength($year, $month)
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
let monLen = LocalDate.getMonthLength($year, $month)
|
|
311
|
+
|
|
312
|
+
while ($day > monLen) {
|
|
313
|
+
$day -= monLen
|
|
314
|
+
$month += 1
|
|
315
|
+
if ($month > 12) {
|
|
316
|
+
$year += 1
|
|
317
|
+
$month -= 12
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
monLen = LocalDate.getMonthLength($year, $month)
|
|
321
|
+
}
|
|
337
322
|
}
|
|
338
|
-
|
|
339
|
-
monLen = LocalDate.getMonthLength($year, $month)
|
|
340
323
|
}
|
|
341
324
|
|
|
342
325
|
// check month overflow
|
|
@@ -388,13 +371,11 @@ export class LocalDate {
|
|
|
388
371
|
|
|
389
372
|
static getMonthLength(year: number, month: number): number {
|
|
390
373
|
if (month === 2) return this.isLeapYear(year) ? 29 : 28
|
|
391
|
-
return
|
|
374
|
+
return MDAYS[month]!
|
|
392
375
|
}
|
|
393
376
|
|
|
394
377
|
static isLeapYear(year: number): boolean {
|
|
395
|
-
|
|
396
|
-
if (year % 100 !== 0) return true
|
|
397
|
-
return year % 400 === 0
|
|
378
|
+
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)
|
|
398
379
|
}
|
|
399
380
|
|
|
400
381
|
clone(): LocalDate {
|
|
@@ -62,7 +62,7 @@ export class LocalTime {
|
|
|
62
62
|
} else if (typeof d === 'number') {
|
|
63
63
|
date = new Date(d * 1000)
|
|
64
64
|
} else {
|
|
65
|
-
date = new Date(d)
|
|
65
|
+
date = new Date(d.slice(0, 19))
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// validation
|
|
@@ -78,6 +78,32 @@ export class LocalTime {
|
|
|
78
78
|
return new LocalTime(date, false)
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
static parseToDate(d: LocalTimeConfig): Date {
|
|
82
|
+
if (d instanceof LocalTime) return d.$date
|
|
83
|
+
if (d instanceof Date) return d
|
|
84
|
+
|
|
85
|
+
const date = typeof d === 'number' ? new Date(d * 1000) : new Date(d)
|
|
86
|
+
|
|
87
|
+
if (isNaN(date.getDate())) {
|
|
88
|
+
throw new TypeError(`Cannot parse "${d}" to Date`)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return date
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static parseToUnixTimestamp(d: LocalTimeConfig): UnixTimestampNumber {
|
|
95
|
+
if (typeof d === 'number') return d
|
|
96
|
+
if (d instanceof LocalTime) return d.unix()
|
|
97
|
+
|
|
98
|
+
const date = d instanceof Date ? d : new Date(d)
|
|
99
|
+
|
|
100
|
+
if (isNaN(date.getDate())) {
|
|
101
|
+
throw new TypeError(`Cannot parse "${d}" to UnixTimestamp`)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return date.valueOf() / 1000
|
|
105
|
+
}
|
|
106
|
+
|
|
81
107
|
static isValid(d: LocalTimeConfig | undefined | null): boolean {
|
|
82
108
|
return this.parseOrNull(d) !== null
|
|
83
109
|
}
|
|
@@ -205,7 +231,7 @@ export class LocalTime {
|
|
|
205
231
|
}
|
|
206
232
|
|
|
207
233
|
diff(other: LocalTimeConfig, unit: LocalTimeUnit): number {
|
|
208
|
-
const date2 = LocalTime.
|
|
234
|
+
const date2 = LocalTime.parseToDate(other)
|
|
209
235
|
|
|
210
236
|
if (unit === 'year') {
|
|
211
237
|
return this.$date.getFullYear() - date2.getFullYear()
|
|
@@ -295,16 +321,18 @@ export class LocalTime {
|
|
|
295
321
|
return this.cmp(d) === 0
|
|
296
322
|
}
|
|
297
323
|
|
|
298
|
-
isBefore(d: LocalTimeConfig): boolean {
|
|
299
|
-
|
|
324
|
+
isBefore(d: LocalTimeConfig, inclusive = false): boolean {
|
|
325
|
+
const r = this.cmp(d)
|
|
326
|
+
return r === -1 || (r === 0 && inclusive)
|
|
300
327
|
}
|
|
301
328
|
|
|
302
329
|
isSameOrBefore(d: LocalTimeConfig): boolean {
|
|
303
330
|
return this.cmp(d) <= 0
|
|
304
331
|
}
|
|
305
332
|
|
|
306
|
-
isAfter(d: LocalTimeConfig): boolean {
|
|
307
|
-
|
|
333
|
+
isAfter(d: LocalTimeConfig, inclusive = false): boolean {
|
|
334
|
+
const r = this.cmp(d)
|
|
335
|
+
return r === 1 || (r === 0 && inclusive)
|
|
308
336
|
}
|
|
309
337
|
|
|
310
338
|
isSameOrAfter(d: LocalTimeConfig): boolean {
|
|
@@ -326,7 +354,7 @@ export class LocalTime {
|
|
|
326
354
|
*/
|
|
327
355
|
cmp(d: LocalTimeConfig): -1 | 0 | 1 {
|
|
328
356
|
const t1 = this.$date.valueOf()
|
|
329
|
-
const t2 = LocalTime.
|
|
357
|
+
const t2 = LocalTime.parseToDate(d).valueOf()
|
|
330
358
|
if (t1 === t2) return 0
|
|
331
359
|
return t1 < t2 ? -1 : 1
|
|
332
360
|
}
|
|
@@ -355,8 +383,8 @@ export class LocalTime {
|
|
|
355
383
|
}
|
|
356
384
|
}
|
|
357
385
|
|
|
358
|
-
fromNow(now: LocalTimeConfig =
|
|
359
|
-
const msDiff = LocalTime.
|
|
386
|
+
fromNow(now: LocalTimeConfig = new Date()): string {
|
|
387
|
+
const msDiff = LocalTime.parseToDate(now).valueOf() - this.$date.valueOf()
|
|
360
388
|
|
|
361
389
|
if (msDiff === 0) return 'now'
|
|
362
390
|
|
|
@@ -499,5 +527,3 @@ export class LocalTime {
|
|
|
499
527
|
export function localTime(d?: LocalTimeConfig): LocalTime {
|
|
500
528
|
return d ? LocalTime.of(d) : LocalTime.now()
|
|
501
529
|
}
|
|
502
|
-
|
|
503
|
-
// todo: range
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { UnixTimestampNumber } from '../types'
|
|
2
|
+
import { Inclusiveness } from './localDate'
|
|
3
|
+
import { LocalTime, LocalTimeConfig } from './localTime'
|
|
4
|
+
|
|
5
|
+
export type TimeIntervalConfig = TimeInterval | TimeIntervalString
|
|
6
|
+
export type TimeIntervalString = string
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Class that supports an "interval of time" between 2 timestamps - start and end.
|
|
10
|
+
* Example: `1649267185/1649267187`.
|
|
11
|
+
*
|
|
12
|
+
* @experimental
|
|
13
|
+
*/
|
|
14
|
+
export class TimeInterval {
|
|
15
|
+
private constructor(private $start: UnixTimestampNumber, private $end: UnixTimestampNumber) {}
|
|
16
|
+
|
|
17
|
+
static of(start: LocalTimeConfig, end: LocalTimeConfig): TimeInterval {
|
|
18
|
+
return new TimeInterval(
|
|
19
|
+
LocalTime.parseToUnixTimestamp(start),
|
|
20
|
+
LocalTime.parseToUnixTimestamp(end),
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get start(): UnixTimestampNumber {
|
|
25
|
+
return this.$start
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get end(): UnixTimestampNumber {
|
|
29
|
+
return this.$end
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get startTime(): LocalTime {
|
|
33
|
+
return LocalTime.of(this.$start)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get endTime(): LocalTime {
|
|
37
|
+
return LocalTime.of(this.$end)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Parses string like `1649267185/1649267187` into a TimeInterval.
|
|
42
|
+
*/
|
|
43
|
+
static parse(d: TimeIntervalConfig): TimeInterval {
|
|
44
|
+
if (d instanceof TimeInterval) return d
|
|
45
|
+
|
|
46
|
+
const [start, end] = d.split('/').map(Number)
|
|
47
|
+
|
|
48
|
+
if (!end || !start) {
|
|
49
|
+
throw new Error(`Cannot parse "${d}" into TimeInterval`)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return new TimeInterval(start, end)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
isSame(d: TimeIntervalConfig): boolean {
|
|
56
|
+
return this.cmp(d) === 0
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
isBefore(d: TimeIntervalConfig, inclusive = false): boolean {
|
|
60
|
+
const r = this.cmp(d)
|
|
61
|
+
return r === -1 || (r === 0 && inclusive)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
isSameOrBefore(d: TimeIntervalConfig): boolean {
|
|
65
|
+
return this.cmp(d) <= 0
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
isAfter(d: TimeIntervalConfig, inclusive = false): boolean {
|
|
69
|
+
const r = this.cmp(d)
|
|
70
|
+
return r === 1 || (r === 0 && inclusive)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
isSameOrAfter(d: TimeIntervalConfig): boolean {
|
|
74
|
+
return this.cmp(d) >= 0
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
includes(d: LocalTimeConfig, incl: Inclusiveness = '[)'): boolean {
|
|
78
|
+
d = LocalTime.parseToUnixTimestamp(d)
|
|
79
|
+
if (d < this.$start || (d === this.$start && incl[0] === '(')) return false
|
|
80
|
+
if (d > this.$end || (d === this.$end && incl[1] === ')')) return false
|
|
81
|
+
return true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* TimeIntervals compare by start date.
|
|
86
|
+
* If it's the same - then by end date.
|
|
87
|
+
*/
|
|
88
|
+
cmp(d: TimeIntervalConfig): -1 | 0 | 1 {
|
|
89
|
+
d = TimeInterval.parse(d)
|
|
90
|
+
if (this.$start > d.$start) return 1
|
|
91
|
+
if (this.$start < d.$start) return -1
|
|
92
|
+
if (this.$end > d.$end) return 1
|
|
93
|
+
if (this.$end < d.$end) return -1
|
|
94
|
+
return 0
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
toString(): TimeIntervalString {
|
|
98
|
+
return [this.$start, this.$end].join('/')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
toJSON(): TimeIntervalString {
|
|
102
|
+
return this.toString()
|
|
103
|
+
}
|
|
104
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -160,13 +160,17 @@ export * from './string/leven'
|
|
|
160
160
|
export * from './datetime/localDate'
|
|
161
161
|
export * from './datetime/localTime'
|
|
162
162
|
export * from './datetime/dateInterval'
|
|
163
|
+
export * from './datetime/timeInterval'
|
|
163
164
|
import { LocalDateConfig, LocalDateUnit, Inclusiveness } from './datetime/localDate'
|
|
164
165
|
import { LocalTimeConfig, LocalTimeUnit, LocalTimeComponents } from './datetime/localTime'
|
|
165
166
|
import { DateIntervalConfig, DateIntervalString } from './datetime/dateInterval'
|
|
167
|
+
import { TimeIntervalConfig, TimeIntervalString } from './datetime/timeInterval'
|
|
166
168
|
|
|
167
169
|
export type {
|
|
168
170
|
DateIntervalConfig,
|
|
169
171
|
DateIntervalString,
|
|
172
|
+
TimeIntervalConfig,
|
|
173
|
+
TimeIntervalString,
|
|
170
174
|
LocalDateConfig,
|
|
171
175
|
LocalDateUnit,
|
|
172
176
|
Inclusiveness,
|
|
@@ -365,7 +365,9 @@ export class JsonSchemaObjectBuilder<T extends AnyObject> extends JsonSchemaAnyB
|
|
|
365
365
|
return this
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
baseDBEntity<ID
|
|
368
|
+
baseDBEntity<ID extends string | number = string>(
|
|
369
|
+
idType = 'string',
|
|
370
|
+
): JsonSchemaObjectBuilder<T & BaseDBEntity<ID>> {
|
|
369
371
|
Object.assign(this.schema.properties, {
|
|
370
372
|
id: { type: idType },
|
|
371
373
|
created: { type: 'number', format: 'unixTimestamp' },
|
|
@@ -375,7 +377,9 @@ export class JsonSchemaObjectBuilder<T extends AnyObject> extends JsonSchemaAnyB
|
|
|
375
377
|
return this
|
|
376
378
|
}
|
|
377
379
|
|
|
378
|
-
savedDBEntity<ID
|
|
380
|
+
savedDBEntity<ID extends string | number = string>(
|
|
381
|
+
idType = 'string',
|
|
382
|
+
): JsonSchemaObjectBuilder<T & SavedDBEntity<ID>> {
|
|
379
383
|
return this.baseDBEntity(idType).addRequired(['id', 'created', 'updated']) as any
|
|
380
384
|
}
|
|
381
385
|
|
package/src/math/math.util.ts
CHANGED
|
@@ -12,6 +12,13 @@ export function _average(values: number[]): number {
|
|
|
12
12
|
return values.reduce((a, b) => a + b) / values.length
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Same as _average, but safely returns null if input array is empty or nullish.
|
|
17
|
+
*/
|
|
18
|
+
export function _averageOrNull(values: number[] | undefined | null): number | null {
|
|
19
|
+
return values?.length ? values.reduce((a, b) => a + b) / values.length : null
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
/**
|
|
16
23
|
* valuesArray and weightsArray length is expected to be the same.
|
|
17
24
|
*/
|
package/src/math/sma.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { Merge, Promisable } from './typeFest'
|
|
|
6
6
|
* Alternative: Record<string, T | undefined>
|
|
7
7
|
*/
|
|
8
8
|
export interface StringMap<T = string> {
|
|
9
|
-
[k: string]: T | undefined
|
|
9
|
+
[k: string | number]: T | undefined
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -29,15 +29,50 @@ export interface CreatedUpdated {
|
|
|
29
29
|
updated: number
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export interface CreatedUpdatedId<ID = string
|
|
32
|
+
export interface CreatedUpdatedId<ID extends string | number = string | number>
|
|
33
|
+
extends CreatedUpdated {
|
|
33
34
|
id: ID
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
export interface ObjectWithId<ID = string> {
|
|
37
|
+
export interface ObjectWithId<ID extends string | number = string | number> {
|
|
37
38
|
id: ID
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
export interface AnyObjectWithId<ID = string
|
|
41
|
+
export interface AnyObjectWithId<ID extends string | number = string | number>
|
|
42
|
+
extends AnyObject,
|
|
43
|
+
ObjectWithId<ID> {}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Base interface for any Entity that was saved to DB.
|
|
47
|
+
*/
|
|
48
|
+
export interface SavedDBEntity<ID extends string | number = string> {
|
|
49
|
+
id: ID
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* unixTimestamp of when the entity was first created (in the DB).
|
|
53
|
+
*/
|
|
54
|
+
created: UnixTimestampNumber
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* unixTimestamp of when the entity was last updated (in the DB).
|
|
58
|
+
*/
|
|
59
|
+
updated: UnixTimestampNumber
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Base interface for any Entity that can be saved to DB.
|
|
64
|
+
* This interface fits when entity was NOT YET saved to DB,
|
|
65
|
+
* hence `id`, `created` and `updated` fields CAN BE undefined (yet).
|
|
66
|
+
* When it's known to be saved - `SavedDBEntity` interface can be used instead.
|
|
67
|
+
*/
|
|
68
|
+
export type BaseDBEntity<ID extends string | number = string> = Partial<SavedDBEntity<ID>>
|
|
69
|
+
|
|
70
|
+
export type Saved<T extends Partial<ObjectWithId>> = Merge<
|
|
71
|
+
T,
|
|
72
|
+
SavedDBEntity<Exclude<T['id'], undefined>>
|
|
73
|
+
>
|
|
74
|
+
|
|
75
|
+
export type Unsaved<T extends ObjectWithId> = Merge<T, BaseDBEntity<T['id']>>
|
|
41
76
|
|
|
42
77
|
/**
|
|
43
78
|
* Convenience type shorthand.
|
|
@@ -181,34 +216,6 @@ export type UnixTimestamp = number
|
|
|
181
216
|
*/
|
|
182
217
|
export type Integer = number
|
|
183
218
|
|
|
184
|
-
/**
|
|
185
|
-
* Base interface for any Entity that was saved to DB.
|
|
186
|
-
*/
|
|
187
|
-
export interface SavedDBEntity<ID = string> {
|
|
188
|
-
id: ID
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* unixTimestamp of when the entity was first created (in the DB).
|
|
192
|
-
*/
|
|
193
|
-
created: UnixTimestampNumber
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* unixTimestamp of when the entity was last updated (in the DB).
|
|
197
|
-
*/
|
|
198
|
-
updated: UnixTimestampNumber
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Base interface for any Entity that can be saved to DB.
|
|
203
|
-
* This interface fits when entity was NOT YET saved to DB,
|
|
204
|
-
* hence `id`, `created` and `updated` fields CAN BE undefined (yet).
|
|
205
|
-
* When it's known to be saved - `SavedDBEntity` interface can be used instead.
|
|
206
|
-
*/
|
|
207
|
-
export type BaseDBEntity<ID = string> = Partial<SavedDBEntity<ID>>
|
|
208
|
-
|
|
209
|
-
export type Saved<E, ID = string> = Merge<E, SavedDBEntity<ID>>
|
|
210
|
-
export type Unsaved<E, ID = string> = Merge<E, BaseDBEntity<ID>>
|
|
211
|
-
|
|
212
219
|
/**
|
|
213
220
|
* Named type for JSON.parse / JSON.stringify second argument
|
|
214
221
|
*/
|