@naturalcycles/js-lib 14.86.0 → 14.89.0
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/localDate.d.ts +75 -0
- package/dist/datetime/localDate.js +290 -0
- package/dist/datetime/localTime.d.ts +78 -0
- package/dist/datetime/localTime.js +304 -0
- package/dist/decorators/createPromiseDecorator.d.ts +2 -2
- package/dist/decorators/createPromiseDecorator.js +6 -12
- package/dist/index.d.ts +6 -1
- package/dist/index.js +47 -44
- package/dist/string/leven.d.ts +6 -0
- package/dist/string/leven.js +75 -0
- package/dist-esm/datetime/localDate.js +285 -0
- package/dist-esm/datetime/localTime.js +299 -0
- package/dist-esm/decorators/createPromiseDecorator.js +8 -15
- package/dist-esm/index.js +3 -0
- package/dist-esm/string/leven.js +71 -0
- package/dist-esm/vendor/is.js +4 -4
- package/package.json +2 -1
- package/src/__exclude/lazyLocalDate.ts +73 -0
- package/src/datetime/localDate.ts +355 -0
- package/src/datetime/localTime.ts +362 -0
- package/src/decorators/createPromiseDecorator.ts +64 -70
- package/src/index.ts +10 -0
- package/src/string/leven.ts +86 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { _assert } from '../error/assert'
|
|
2
|
+
import { IsoDateTime, UnixTimestamp } from '../types'
|
|
3
|
+
|
|
4
|
+
export type LocalTimeUnit = 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second'
|
|
5
|
+
|
|
6
|
+
export type LocalTimeConfig = LocalTime | Date | IsoDateTime | UnixTimestamp
|
|
7
|
+
|
|
8
|
+
export interface LocalTimeComponents {
|
|
9
|
+
year: number
|
|
10
|
+
month: number
|
|
11
|
+
day: number
|
|
12
|
+
hour: number
|
|
13
|
+
minute: number
|
|
14
|
+
second: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* eslint-disable no-dupe-class-members */
|
|
18
|
+
|
|
19
|
+
// Design choices:
|
|
20
|
+
// No milliseconds
|
|
21
|
+
// No timezone support, ISO8601 is parsed as LocalDateTime, discarding Timezone information
|
|
22
|
+
// Formats as unix timestamp, ISO8601 or "pretty string"
|
|
23
|
+
// toString and .toJSON formats as unix timestamp
|
|
24
|
+
// No "unixMillis", just pure unixtimestamp
|
|
25
|
+
// .valueOf returns unix timestamp (no millis)
|
|
26
|
+
// Prevents dayjs(undefined) being dayjs.now()
|
|
27
|
+
// Validates on parse, throws if invalid. Doesn't allow invalid objects
|
|
28
|
+
/**
|
|
29
|
+
* @experimental
|
|
30
|
+
*/
|
|
31
|
+
export class LocalTime {
|
|
32
|
+
private constructor(private $date: Date) {}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parses input String into LocalDate.
|
|
36
|
+
* Input can already be a LocalDate - it is returned as-is in that case.
|
|
37
|
+
*/
|
|
38
|
+
static of(d: LocalTimeConfig): LocalTime {
|
|
39
|
+
if (d instanceof LocalTime) return d
|
|
40
|
+
if (d instanceof Date) return new LocalTime(d)
|
|
41
|
+
|
|
42
|
+
if (typeof d === 'number') {
|
|
43
|
+
// unix timestamp
|
|
44
|
+
return new LocalTime(new Date(d * 1000))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const date = new Date(d)
|
|
48
|
+
|
|
49
|
+
// validation
|
|
50
|
+
if (isNaN(date.getDate())) {
|
|
51
|
+
throw new TypeError(`Cannot parse "${d}" into LocalTime`)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return new LocalTime(date)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static unix(ts: UnixTimestamp): LocalTime {
|
|
58
|
+
return new LocalTime(new Date(ts * 1000))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static now(): LocalTime {
|
|
62
|
+
return this.of(new Date())
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static fromComponents(
|
|
66
|
+
c: { year: number; month: number } & Partial<LocalTimeComponents>,
|
|
67
|
+
): LocalTime {
|
|
68
|
+
return new LocalTime(new Date(c.year, c.month - 1, c.day, c.hour, c.minute, c.second))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get(unit: LocalTimeUnit): number {
|
|
72
|
+
if (unit === 'year') {
|
|
73
|
+
return this.$date.getFullYear()
|
|
74
|
+
}
|
|
75
|
+
if (unit === 'month') {
|
|
76
|
+
return this.$date.getMonth() + 1
|
|
77
|
+
}
|
|
78
|
+
if (unit === 'day') {
|
|
79
|
+
return this.$date.getDate()
|
|
80
|
+
}
|
|
81
|
+
if (unit === 'hour') {
|
|
82
|
+
return this.$date.getHours()
|
|
83
|
+
}
|
|
84
|
+
if (unit === 'minute') {
|
|
85
|
+
return this.$date.getMinutes()
|
|
86
|
+
}
|
|
87
|
+
// second
|
|
88
|
+
return this.$date.getSeconds()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
set(unit: LocalTimeUnit, v: number, mutate = false): LocalTime {
|
|
92
|
+
const t = mutate ? this : this.clone()
|
|
93
|
+
|
|
94
|
+
if (unit === 'year') {
|
|
95
|
+
t.$date.setFullYear(v)
|
|
96
|
+
} else if (unit === 'month') {
|
|
97
|
+
t.$date.setMonth(v - 1)
|
|
98
|
+
} else if (unit === 'day') {
|
|
99
|
+
t.$date.setDate(v)
|
|
100
|
+
} else if (unit === 'hour') {
|
|
101
|
+
t.$date.setHours(v)
|
|
102
|
+
} else if (unit === 'minute') {
|
|
103
|
+
t.$date.setMinutes(v)
|
|
104
|
+
} else if (unit === 'second') {
|
|
105
|
+
t.$date.setSeconds(v)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return t
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
year(): number
|
|
112
|
+
year(v: number): LocalTime
|
|
113
|
+
year(v?: number): number | LocalTime {
|
|
114
|
+
return v === undefined ? this.$date.getFullYear() : this.set('year', v)
|
|
115
|
+
}
|
|
116
|
+
month(): number
|
|
117
|
+
month(v: number): LocalTime
|
|
118
|
+
month(v?: number): number | LocalTime {
|
|
119
|
+
return v === undefined ? this.$date.getMonth() + 1 : this.set('month', v)
|
|
120
|
+
}
|
|
121
|
+
date(): number
|
|
122
|
+
date(v: number): LocalTime
|
|
123
|
+
date(v?: number): number | LocalTime {
|
|
124
|
+
return v === undefined ? this.$date.getDate() : this.set('day', v)
|
|
125
|
+
}
|
|
126
|
+
hour(): number
|
|
127
|
+
hour(v: number): LocalTime
|
|
128
|
+
hour(v?: number): number | LocalTime {
|
|
129
|
+
return v === undefined ? this.$date.getHours() : this.set('hour', v)
|
|
130
|
+
}
|
|
131
|
+
minute(): number
|
|
132
|
+
minute(v: number): LocalTime
|
|
133
|
+
minute(v?: number): number | LocalTime {
|
|
134
|
+
return v === undefined ? this.$date.getMinutes() : this.set('minute', v)
|
|
135
|
+
}
|
|
136
|
+
second(): number
|
|
137
|
+
second(v: number): LocalTime
|
|
138
|
+
second(v?: number): number | LocalTime {
|
|
139
|
+
return v === undefined ? this.$date.getSeconds() : this.set('second', v)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
setComponents(c: Partial<LocalTimeComponents>, mutate = false): LocalTime {
|
|
143
|
+
const d = mutate ? this.$date : new Date(this.$date)
|
|
144
|
+
|
|
145
|
+
if (c.year) {
|
|
146
|
+
d.setFullYear(c.year)
|
|
147
|
+
}
|
|
148
|
+
if (c.month) {
|
|
149
|
+
d.setMonth(c.month - 1)
|
|
150
|
+
}
|
|
151
|
+
if (c.day) {
|
|
152
|
+
d.setDate(c.day)
|
|
153
|
+
}
|
|
154
|
+
if (c.hour !== undefined) {
|
|
155
|
+
d.setHours(c.hour)
|
|
156
|
+
}
|
|
157
|
+
if (c.minute !== undefined) {
|
|
158
|
+
d.setMinutes(c.minute)
|
|
159
|
+
}
|
|
160
|
+
if (c.second !== undefined) {
|
|
161
|
+
d.setSeconds(c.second)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return mutate ? this : new LocalTime(d)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
add(num: number, unit: LocalTimeUnit, mutate = false): LocalTime {
|
|
168
|
+
return this.set(unit, this.get(unit) + num, mutate)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
subtract(num: number, unit: LocalTimeUnit, mutate = false): LocalTime {
|
|
172
|
+
return this.add(-num, unit, mutate)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
absDiff(other: LocalTimeConfig, unit: LocalTimeUnit): number {
|
|
176
|
+
return Math.abs(this.diff(other, unit))
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
diff(other: LocalTimeConfig, unit: LocalTimeUnit): number {
|
|
180
|
+
const date2 = LocalTime.of(other).$date
|
|
181
|
+
|
|
182
|
+
if (unit === 'year') {
|
|
183
|
+
return this.$date.getFullYear() - date2.getFullYear()
|
|
184
|
+
}
|
|
185
|
+
if (unit === 'month') {
|
|
186
|
+
return (
|
|
187
|
+
(this.$date.getFullYear() - date2.getFullYear()) * 12 +
|
|
188
|
+
this.$date.getMonth() -
|
|
189
|
+
date2.getMonth()
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000
|
|
194
|
+
let r
|
|
195
|
+
|
|
196
|
+
if (unit === 'day') {
|
|
197
|
+
r = secDiff / (24 * 60 * 60)
|
|
198
|
+
} else if (unit === 'hour') {
|
|
199
|
+
r = secDiff / (60 * 60)
|
|
200
|
+
} else if (unit === 'minute') {
|
|
201
|
+
r = secDiff / 60
|
|
202
|
+
} else {
|
|
203
|
+
// unit === 'second'
|
|
204
|
+
r = secDiff
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
r = r < 0 ? -Math.floor(-r) : Math.floor(r)
|
|
208
|
+
if (Object.is(r, -0)) return 0
|
|
209
|
+
return r
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
startOf(unit: LocalTimeUnit, mutate = false): LocalTime {
|
|
213
|
+
if (unit === 'second') return this
|
|
214
|
+
|
|
215
|
+
if (mutate) {
|
|
216
|
+
const d = this.$date
|
|
217
|
+
d.setSeconds(0)
|
|
218
|
+
if (unit === 'minute') return this
|
|
219
|
+
d.setMinutes(0)
|
|
220
|
+
if (unit === 'hour') return this
|
|
221
|
+
d.setHours(0)
|
|
222
|
+
if (unit === 'day') return this
|
|
223
|
+
d.setDate(0)
|
|
224
|
+
if (unit === 'month') return this
|
|
225
|
+
d.setMonth(0)
|
|
226
|
+
return this
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const c = this.components()
|
|
230
|
+
|
|
231
|
+
c.second = 0
|
|
232
|
+
if (unit === 'year') {
|
|
233
|
+
c.month = c.day = 1
|
|
234
|
+
c.hour = c.minute = 0
|
|
235
|
+
} else if (unit === 'month') {
|
|
236
|
+
c.day = 1
|
|
237
|
+
c.hour = c.minute = 0
|
|
238
|
+
} else if (unit === 'day') {
|
|
239
|
+
c.hour = c.minute = 0
|
|
240
|
+
} else if (unit === 'hour') {
|
|
241
|
+
c.minute = 0
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return LocalTime.fromComponents(c)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
static sort(items: LocalTime[], mutate = false, descending = false): LocalTime[] {
|
|
248
|
+
const mod = descending ? -1 : 1
|
|
249
|
+
return (mutate ? items : [...items]).sort((a, b) => {
|
|
250
|
+
const v1 = a.$date.valueOf()
|
|
251
|
+
const v2 = b.$date.valueOf()
|
|
252
|
+
if (v1 === v2) return 0
|
|
253
|
+
return (v1 < v2 ? -1 : 1) * mod
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
static earliestOrUndefined(items: LocalTime[]): LocalTime | undefined {
|
|
258
|
+
return items.length ? LocalTime.earliest(items) : undefined
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
static earliest(items: LocalTime[]): LocalTime {
|
|
262
|
+
_assert(items.length, 'LocalTime.earliest called on empty array')
|
|
263
|
+
|
|
264
|
+
return items.reduce((min, item) => (min.isSameOrBefore(item) ? min : item))
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
static latestOrUndefined(items: LocalTime[]): LocalTime | undefined {
|
|
268
|
+
return items.length ? LocalTime.latest(items) : undefined
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
static latest(items: LocalTime[]): LocalTime {
|
|
272
|
+
_assert(items.length, 'LocalTime.latest called on empty array')
|
|
273
|
+
|
|
274
|
+
return items.reduce((max, item) => (max.isSameOrAfter(item) ? max : item))
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
isSame(d: LocalTimeConfig): boolean {
|
|
278
|
+
return this.cmp(d) === 0
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
isBefore(d: LocalTimeConfig): boolean {
|
|
282
|
+
return this.cmp(d) === -1
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
isSameOrBefore(d: LocalTimeConfig): boolean {
|
|
286
|
+
return this.cmp(d) <= 0
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
isAfter(d: LocalTimeConfig): boolean {
|
|
290
|
+
return this.cmp(d) === 1
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
isSameOrAfter(d: LocalTimeConfig): boolean {
|
|
294
|
+
return this.cmp(d) >= 0
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Returns 1 if this > d
|
|
299
|
+
* returns 0 if they are equal
|
|
300
|
+
* returns -1 if this < d
|
|
301
|
+
*/
|
|
302
|
+
cmp(d: LocalTimeConfig): -1 | 0 | 1 {
|
|
303
|
+
const t1 = this.$date.valueOf()
|
|
304
|
+
const t2 = LocalTime.of(d).$date.valueOf()
|
|
305
|
+
if (t1 === t2) return 0
|
|
306
|
+
return t1 < t2 ? -1 : 1
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// todo: endOf
|
|
310
|
+
|
|
311
|
+
components(): LocalTimeComponents {
|
|
312
|
+
return {
|
|
313
|
+
year: this.$date.getFullYear(),
|
|
314
|
+
month: this.$date.getMonth() + 1,
|
|
315
|
+
day: this.$date.getDate(),
|
|
316
|
+
hour: this.$date.getHours(),
|
|
317
|
+
minute: this.$date.getMinutes(),
|
|
318
|
+
second: this.$date.getSeconds(),
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
getDate(): Date {
|
|
323
|
+
return this.$date
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
clone(): LocalTime {
|
|
327
|
+
return new LocalTime(new Date(this.$date))
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
unix(): UnixTimestamp {
|
|
331
|
+
return Math.floor(this.$date.valueOf() / 1000)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
valueOf(): UnixTimestamp {
|
|
335
|
+
return Math.floor(this.$date.valueOf() / 1000)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
toISO8601(): IsoDateTime {
|
|
339
|
+
return this.$date.toISOString().slice(0, 19)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
toPretty(): IsoDateTime {
|
|
343
|
+
return this.$date.toISOString().slice(0, 19).split('T').join(' ')
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
toString(): string {
|
|
347
|
+
return String(this.unix())
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
toJSON(): UnixTimestamp {
|
|
351
|
+
return this.unix()
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Shortcut wrapper around `LocalDate.parse` / `LocalDate.today`
|
|
357
|
+
*/
|
|
358
|
+
export function localTime(d?: LocalTimeConfig): LocalTime {
|
|
359
|
+
return d ? LocalTime.of(d) : LocalTime.now()
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// todo: range
|
|
@@ -5,9 +5,9 @@ export interface PromiseDecoratorCfg<RES = any, PARAMS = any> {
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Called BEFORE the original function.
|
|
8
|
-
*
|
|
8
|
+
* If Promise is returned - it will be awaited.
|
|
9
9
|
*/
|
|
10
|
-
beforeFn?: (r: PromiseDecoratorResp<PARAMS>) => void
|
|
10
|
+
beforeFn?: (r: PromiseDecoratorResp<PARAMS>) => void | Promise<void>
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Called just AFTER the original function.
|
|
@@ -73,77 +73,71 @@ export function _createPromiseDecorator<RES = any, PARAMS = any>(
|
|
|
73
73
|
// console.log(`@${cfg.decoratorName} called inside function`)
|
|
74
74
|
const started = Date.now()
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
decoratorName,
|
|
88
|
-
started,
|
|
89
|
-
})
|
|
90
|
-
}
|
|
76
|
+
try {
|
|
77
|
+
// Before function
|
|
78
|
+
// console.log(`@${cfg.decoratorName} Before`)
|
|
79
|
+
if (cfg.beforeFn) {
|
|
80
|
+
await cfg.beforeFn({
|
|
81
|
+
decoratorParams,
|
|
82
|
+
args,
|
|
83
|
+
key,
|
|
84
|
+
target,
|
|
85
|
+
decoratorName,
|
|
86
|
+
started,
|
|
91
87
|
})
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
cfg.finallyFn?.(resp)
|
|
113
|
-
|
|
114
|
-
return res
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Original function
|
|
91
|
+
let res = await originalMethod.apply(this, args)
|
|
92
|
+
|
|
93
|
+
// console.log(`${cfg.decoratorName} After`)
|
|
94
|
+
const resp: PromiseDecoratorResp<PARAMS> = {
|
|
95
|
+
decoratorParams,
|
|
96
|
+
args,
|
|
97
|
+
key,
|
|
98
|
+
target,
|
|
99
|
+
decoratorName,
|
|
100
|
+
started,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (cfg.thenFn) {
|
|
104
|
+
res = cfg.thenFn({
|
|
105
|
+
...resp,
|
|
106
|
+
res,
|
|
115
107
|
})
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (!handled) {
|
|
141
|
-
throw err // rethrow
|
|
142
|
-
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
cfg.finallyFn?.(resp)
|
|
111
|
+
|
|
112
|
+
return res
|
|
113
|
+
} catch (err) {
|
|
114
|
+
console.error(`@${decoratorName} ${methodSignature} catch:`, err)
|
|
115
|
+
|
|
116
|
+
const resp: PromiseDecoratorResp<PARAMS> = {
|
|
117
|
+
decoratorParams,
|
|
118
|
+
args,
|
|
119
|
+
key,
|
|
120
|
+
target,
|
|
121
|
+
decoratorName,
|
|
122
|
+
started,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let handled = false
|
|
126
|
+
|
|
127
|
+
if (cfg.catchFn) {
|
|
128
|
+
cfg.catchFn({
|
|
129
|
+
...resp,
|
|
130
|
+
err,
|
|
143
131
|
})
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
132
|
+
handled = true
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
cfg.finallyFn?.(resp)
|
|
136
|
+
|
|
137
|
+
if (!handled) {
|
|
138
|
+
throw err // rethrow
|
|
139
|
+
}
|
|
140
|
+
}
|
|
147
141
|
}
|
|
148
142
|
|
|
149
143
|
return pd
|
package/src/index.ts
CHANGED
|
@@ -155,8 +155,18 @@ export * from './string/safeJsonStringify'
|
|
|
155
155
|
import { PQueue, PQueueCfg } from './promise/pQueue'
|
|
156
156
|
export * from './seq/seq'
|
|
157
157
|
export * from './math/stack.util'
|
|
158
|
+
export * from './string/leven'
|
|
159
|
+
export * from './datetime/localDate'
|
|
160
|
+
export * from './datetime/localTime'
|
|
161
|
+
import { LocalDateConfig, LocalDateUnit } from './datetime/localDate'
|
|
162
|
+
import { LocalTimeConfig, LocalTimeUnit, LocalTimeComponents } from './datetime/localTime'
|
|
158
163
|
|
|
159
164
|
export type {
|
|
165
|
+
LocalDateConfig,
|
|
166
|
+
LocalDateUnit,
|
|
167
|
+
LocalTimeConfig,
|
|
168
|
+
LocalTimeUnit,
|
|
169
|
+
LocalTimeComponents,
|
|
160
170
|
AbortableMapper,
|
|
161
171
|
AbortablePredicate,
|
|
162
172
|
AbortableAsyncPredicate,
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const array: number[] = []
|
|
2
|
+
const characterCodeCache: number[] = []
|
|
3
|
+
|
|
4
|
+
/* eslint-disable unicorn/prefer-code-point, no-bitwise */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Modified version of: https://github.com/sindresorhus/leven/
|
|
8
|
+
*
|
|
9
|
+
* Returns a Levenshtein distance between first and second word.
|
|
10
|
+
*/
|
|
11
|
+
export function _leven(first: string, second: string): number {
|
|
12
|
+
if (first === second) {
|
|
13
|
+
return 0
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const swap = first
|
|
17
|
+
|
|
18
|
+
// Swapping the strings if `a` is longer than `b` so we know which one is the
|
|
19
|
+
// shortest & which one is the longest
|
|
20
|
+
if (first.length > second.length) {
|
|
21
|
+
first = second
|
|
22
|
+
second = swap
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let firstLength = first.length
|
|
26
|
+
let secondLength = second.length
|
|
27
|
+
|
|
28
|
+
// Performing suffix trimming:
|
|
29
|
+
// We can linearly drop suffix common to both strings since they
|
|
30
|
+
// don't increase distance at all
|
|
31
|
+
// Note: `~-` is the bitwise way to perform a `- 1` operation
|
|
32
|
+
while (firstLength > 0 && first.charCodeAt(~-firstLength) === second.charCodeAt(~-secondLength)) {
|
|
33
|
+
firstLength--
|
|
34
|
+
secondLength--
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Performing prefix trimming
|
|
38
|
+
// We can linearly drop prefix common to both strings since they
|
|
39
|
+
// don't increase distance at all
|
|
40
|
+
let start = 0
|
|
41
|
+
|
|
42
|
+
while (start < firstLength && first.charCodeAt(start) === second.charCodeAt(start)) {
|
|
43
|
+
start++
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
firstLength -= start
|
|
47
|
+
secondLength -= start
|
|
48
|
+
|
|
49
|
+
if (firstLength === 0) {
|
|
50
|
+
return secondLength
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let bCharacterCode
|
|
54
|
+
let result: number
|
|
55
|
+
let temporary: number
|
|
56
|
+
let temporary2: number
|
|
57
|
+
let index = 0
|
|
58
|
+
let index2 = 0
|
|
59
|
+
|
|
60
|
+
while (index < firstLength) {
|
|
61
|
+
characterCodeCache[index] = first.charCodeAt(start + index)
|
|
62
|
+
array[index] = ++index
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
while (index2 < secondLength) {
|
|
66
|
+
bCharacterCode = second.charCodeAt(start + index2)
|
|
67
|
+
temporary = index2++
|
|
68
|
+
result = index2
|
|
69
|
+
|
|
70
|
+
for (index = 0; index < firstLength; index++) {
|
|
71
|
+
temporary2 = bCharacterCode === characterCodeCache[index] ? temporary : temporary + 1
|
|
72
|
+
temporary = array[index]!
|
|
73
|
+
// eslint-disable-next-line no-multi-assign
|
|
74
|
+
result = array[index] =
|
|
75
|
+
temporary > result
|
|
76
|
+
? temporary2 > result
|
|
77
|
+
? result + 1
|
|
78
|
+
: temporary2
|
|
79
|
+
: temporary2 > temporary
|
|
80
|
+
? temporary + 1
|
|
81
|
+
: temporary2
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return result!
|
|
86
|
+
}
|