ts-patch-mongoose 3.1.0 → 3.1.2
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 +31 -25
- package/dist/index.cjs +12 -9
- package/dist/index.d.cts +2 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +12 -9
- package/package.json +13 -14
- package/src/hooks/update-hooks.ts +8 -3
- package/src/index.ts +8 -5
- package/src/ms.ts +4 -3
- package/src/patch.ts +3 -3
- package/src/types.ts +2 -0
- package/biome.json +0 -47
- package/tests/constants/events.ts +0 -7
- package/tests/em.test.ts +0 -70
- package/tests/helpers.test.ts +0 -373
- package/tests/mongo/.gitignore +0 -3
- package/tests/mongo/server.ts +0 -31
- package/tests/ms.test.ts +0 -113
- package/tests/omit-deep.test.ts +0 -235
- package/tests/patch.test.ts +0 -200
- package/tests/plugin-all-features.test.ts +0 -844
- package/tests/plugin-complex-data.test.ts +0 -2647
- package/tests/plugin-event-created.test.ts +0 -371
- package/tests/plugin-event-deleted.test.ts +0 -400
- package/tests/plugin-event-updated.test.ts +0 -503
- package/tests/plugin-global.test.ts +0 -545
- package/tests/plugin-omit-all.test.ts +0 -349
- package/tests/plugin-patch-history-disabled.test.ts +0 -162
- package/tests/plugin-pre-delete.test.ts +0 -160
- package/tests/plugin-pre-save.test.ts +0 -54
- package/tests/plugin.test.ts +0 -576
- package/tests/schemas/Description.ts +0 -15
- package/tests/schemas/Product.ts +0 -38
- package/tests/schemas/User.ts +0 -22
- package/tsconfig.json +0 -32
- package/vite.config.mts +0 -24
package/tests/helpers.test.ts
DELETED
|
@@ -1,373 +0,0 @@
|
|
|
1
|
-
import type { Mock, MockInstance } from 'vitest'
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
-
|
|
4
|
-
import { cloneDeep, isEmpty, setPatchHistoryTTL } from '../src/helpers'
|
|
5
|
-
import { HistoryModel } from '../src/model'
|
|
6
|
-
import { ms } from '../src/ms'
|
|
7
|
-
|
|
8
|
-
vi.mock('../src/model', () => ({
|
|
9
|
-
HistoryModel: {
|
|
10
|
-
collection: {
|
|
11
|
-
indexes: vi.fn(),
|
|
12
|
-
dropIndex: vi.fn(),
|
|
13
|
-
createIndex: vi.fn(),
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
}))
|
|
17
|
-
|
|
18
|
-
const name = 'createdAt_1_TTL'
|
|
19
|
-
|
|
20
|
-
describe('useTTL', () => {
|
|
21
|
-
let dropIndexSpy: MockInstance
|
|
22
|
-
let createIndexSpy: MockInstance
|
|
23
|
-
const indexes = HistoryModel.collection.indexes as Mock
|
|
24
|
-
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
vi.clearAllMocks()
|
|
27
|
-
dropIndexSpy = vi.spyOn(HistoryModel.collection, 'dropIndex')
|
|
28
|
-
createIndexSpy = vi.spyOn(HistoryModel.collection, 'createIndex')
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
afterEach(() => {
|
|
32
|
-
vi.restoreAllMocks()
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('should drop the index if historyTTL is not set and index exists', async () => {
|
|
36
|
-
indexes.mockResolvedValue([{ name }])
|
|
37
|
-
|
|
38
|
-
// @ts-expect-error ttl can't be undefined in this case but we want to test it
|
|
39
|
-
await setPatchHistoryTTL(undefined)
|
|
40
|
-
expect(dropIndexSpy).toHaveBeenCalledWith(name)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('should drop the index if historyTTL is less than 1 second and index exists', async () => {
|
|
44
|
-
indexes.mockResolvedValue([{ name }])
|
|
45
|
-
|
|
46
|
-
await setPatchHistoryTTL('500ms')
|
|
47
|
-
expect(dropIndexSpy).toHaveBeenCalledWith(name)
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('should not recreate the index if it already exists with the correct TTL', async () => {
|
|
51
|
-
const ttl = '1h'
|
|
52
|
-
const expireAfterSeconds = ms(ttl) / 1000
|
|
53
|
-
|
|
54
|
-
indexes.mockResolvedValue([{ name, expireAfterSeconds }])
|
|
55
|
-
|
|
56
|
-
await setPatchHistoryTTL(ttl)
|
|
57
|
-
expect(dropIndexSpy).not.toHaveBeenCalled()
|
|
58
|
-
expect(createIndexSpy).not.toHaveBeenCalled()
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('should drop and recreate the index if TTL is different', async () => {
|
|
62
|
-
const ttlBefore = '1h'
|
|
63
|
-
const ttlAfter = '2h'
|
|
64
|
-
|
|
65
|
-
const expireAfterSecondsBefore = ms(ttlBefore) / 1000
|
|
66
|
-
const expireAfterSecondsAfter = ms(ttlAfter) / 1000
|
|
67
|
-
|
|
68
|
-
indexes.mockResolvedValue([{ name, expireAfterSeconds: expireAfterSecondsBefore }])
|
|
69
|
-
|
|
70
|
-
await setPatchHistoryTTL(ttlAfter)
|
|
71
|
-
expect(dropIndexSpy).toHaveBeenCalledWith(name)
|
|
72
|
-
expect(createIndexSpy).toHaveBeenCalledWith({ createdAt: 1 }, { expireAfterSeconds: expireAfterSecondsAfter, name })
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('should create the index if it does not exist', async () => {
|
|
76
|
-
const ttl = '1h'
|
|
77
|
-
const expireAfterSeconds = ms(ttl) / 1000
|
|
78
|
-
|
|
79
|
-
indexes.mockResolvedValue([])
|
|
80
|
-
|
|
81
|
-
await setPatchHistoryTTL(ttl)
|
|
82
|
-
expect(createIndexSpy).toHaveBeenCalledWith({ createdAt: 1 }, { expireAfterSeconds, name })
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
describe('isEmpty', () => {
|
|
87
|
-
it('should return true for null and undefined', () => {
|
|
88
|
-
expect(isEmpty(null)).toBe(true)
|
|
89
|
-
expect(isEmpty(undefined)).toBe(true)
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('should return true for empty arrays and strings', () => {
|
|
93
|
-
expect(isEmpty([])).toBe(true)
|
|
94
|
-
expect(isEmpty('')).toBe(true)
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
it('should return false for non-empty arrays and strings', () => {
|
|
98
|
-
expect(isEmpty([1, 2])).toBe(false)
|
|
99
|
-
expect(isEmpty('hello')).toBe(false)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
it('should return true for empty objects', () => {
|
|
103
|
-
expect(isEmpty({})).toBe(true)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('should return false for non-empty objects', () => {
|
|
107
|
-
expect(isEmpty({ a: 1 })).toBe(false)
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('should return true for empty Map and Set', () => {
|
|
111
|
-
expect(isEmpty(new Map())).toBe(true)
|
|
112
|
-
expect(isEmpty(new Set())).toBe(true)
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('should return false for non-empty Map and Set', () => {
|
|
116
|
-
expect(isEmpty(new Map([['a', 1]]))).toBe(false)
|
|
117
|
-
expect(isEmpty(new Set([1]))).toBe(false)
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
it('should return true for numbers, booleans, and functions (matches lodash)', () => {
|
|
121
|
-
expect(isEmpty(0)).toBe(true)
|
|
122
|
-
expect(isEmpty(1)).toBe(true)
|
|
123
|
-
expect(isEmpty(true)).toBe(true)
|
|
124
|
-
expect(isEmpty(false)).toBe(true)
|
|
125
|
-
expect(isEmpty(() => {})).toBe(true)
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
it('should return true for Date and RegExp (matches lodash)', () => {
|
|
129
|
-
expect(isEmpty(new Date())).toBe(true)
|
|
130
|
-
expect(isEmpty(/test/)).toBe(true)
|
|
131
|
-
})
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
describe('cloneDeep', () => {
|
|
135
|
-
it('should clone primitives', () => {
|
|
136
|
-
expect(cloneDeep(42)).toBe(42)
|
|
137
|
-
expect(cloneDeep('hello')).toBe('hello')
|
|
138
|
-
expect(cloneDeep(null)).toBe(null)
|
|
139
|
-
expect(cloneDeep(undefined)).toBe(undefined)
|
|
140
|
-
expect(cloneDeep(true)).toBe(true)
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it('should deep clone plain objects', () => {
|
|
144
|
-
const original = { a: 1, b: { c: 2 } }
|
|
145
|
-
const cloned = cloneDeep(original)
|
|
146
|
-
expect(cloned).toEqual(original)
|
|
147
|
-
expect(cloned).not.toBe(original)
|
|
148
|
-
expect(cloned.b).not.toBe(original.b)
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
it('should deep clone arrays', () => {
|
|
152
|
-
const original = [1, [2, 3], { a: 4 }]
|
|
153
|
-
const cloned = cloneDeep(original)
|
|
154
|
-
expect(cloned).toEqual(original)
|
|
155
|
-
expect(cloned).not.toBe(original)
|
|
156
|
-
expect(cloned[1]).not.toBe(original[1])
|
|
157
|
-
expect(cloned[2]).not.toBe(original[2])
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
it('should clone Date instances', () => {
|
|
161
|
-
const original = new Date('2026-01-01')
|
|
162
|
-
const cloned = cloneDeep(original)
|
|
163
|
-
expect(cloned).toEqual(original)
|
|
164
|
-
expect(cloned).not.toBe(original)
|
|
165
|
-
expect(cloned.getTime()).toBe(original.getTime())
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
it('should clone RegExp instances', () => {
|
|
169
|
-
const original = /test/gi
|
|
170
|
-
const cloned = cloneDeep(original)
|
|
171
|
-
expect(cloned).not.toBe(original)
|
|
172
|
-
expect(cloned.source).toBe(original.source)
|
|
173
|
-
expect(cloned.flags).toBe(original.flags)
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
it('should clone Map instances', () => {
|
|
177
|
-
const original = new Map([['a', { nested: 1 }]])
|
|
178
|
-
const cloned = cloneDeep(original)
|
|
179
|
-
expect(cloned).not.toBe(original)
|
|
180
|
-
expect(cloned.get('a')).toEqual({ nested: 1 })
|
|
181
|
-
expect(cloned.get('a')).not.toBe(original.get('a'))
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
it('should clone Set instances', () => {
|
|
185
|
-
const obj = { a: 1 }
|
|
186
|
-
const original = new Set([obj])
|
|
187
|
-
const cloned = cloneDeep(original)
|
|
188
|
-
expect(cloned).not.toBe(original)
|
|
189
|
-
expect(cloned.size).toBe(1)
|
|
190
|
-
const [clonedItem] = cloned
|
|
191
|
-
expect(clonedItem).toEqual(obj)
|
|
192
|
-
expect(clonedItem).not.toBe(obj)
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
it('should handle circular references in objects', () => {
|
|
196
|
-
const original: Record<string, unknown> = { a: 1 }
|
|
197
|
-
original.self = original
|
|
198
|
-
const cloned = cloneDeep(original)
|
|
199
|
-
expect(cloned.a).toBe(1)
|
|
200
|
-
expect(cloned.self).toBe(cloned)
|
|
201
|
-
expect(cloned).not.toBe(original)
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
it('should handle circular references in arrays', () => {
|
|
205
|
-
const original: unknown[] = [1, 2]
|
|
206
|
-
original.push(original)
|
|
207
|
-
const cloned = cloneDeep(original)
|
|
208
|
-
expect(cloned[0]).toBe(1)
|
|
209
|
-
expect(cloned[1]).toBe(2)
|
|
210
|
-
expect(cloned[2]).toBe(cloned)
|
|
211
|
-
expect(cloned).not.toBe(original)
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
it('should handle circular references in nested objects', () => {
|
|
215
|
-
const child: Record<string, unknown> = { value: 'child' }
|
|
216
|
-
const parent: Record<string, unknown> = { child }
|
|
217
|
-
child.parent = parent
|
|
218
|
-
const cloned = cloneDeep(parent)
|
|
219
|
-
expect(cloned).not.toBe(parent)
|
|
220
|
-
expect(cloned.child).not.toBe(child)
|
|
221
|
-
expect((cloned.child as Record<string, unknown>).value).toBe('child')
|
|
222
|
-
expect((cloned.child as Record<string, unknown>).parent).toBe(cloned)
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
it('should handle circular references in Maps', () => {
|
|
226
|
-
const original = new Map<string, unknown>()
|
|
227
|
-
original.set('self', original)
|
|
228
|
-
const cloned = cloneDeep(original)
|
|
229
|
-
expect(cloned).not.toBe(original)
|
|
230
|
-
expect(cloned.get('self')).toBe(cloned)
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
it('should handle circular references in Sets', () => {
|
|
234
|
-
const original = new Set<unknown>()
|
|
235
|
-
original.add(original)
|
|
236
|
-
const cloned = cloneDeep(original)
|
|
237
|
-
expect(cloned).not.toBe(original)
|
|
238
|
-
expect(cloned.has(cloned)).toBe(true)
|
|
239
|
-
expect(cloned.size).toBe(1)
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
it('should clone objects with toJSON method via JSON round-trip', () => {
|
|
243
|
-
const original = { value: 42, toJSON: () => ({ value: 42 }) }
|
|
244
|
-
const cloned = cloneDeep(original)
|
|
245
|
-
expect(cloned).toEqual({ value: 42 })
|
|
246
|
-
expect(cloned).not.toBe(original)
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
it('should clone ArrayBuffer', () => {
|
|
250
|
-
const original = new ArrayBuffer(8)
|
|
251
|
-
new Uint8Array(original).set([1, 2, 3, 4, 5, 6, 7, 8])
|
|
252
|
-
const cloned = cloneDeep(original)
|
|
253
|
-
expect(cloned).not.toBe(original)
|
|
254
|
-
expect(cloned.byteLength).toBe(8)
|
|
255
|
-
expect(new Uint8Array(cloned)).toEqual(new Uint8Array(original))
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
it('should clone DataView', () => {
|
|
259
|
-
const buffer = new ArrayBuffer(16)
|
|
260
|
-
const original = new DataView(buffer, 4, 8)
|
|
261
|
-
original.setInt32(0, 42)
|
|
262
|
-
const cloned = cloneDeep(original)
|
|
263
|
-
expect(cloned).not.toBe(original)
|
|
264
|
-
expect(cloned.buffer).not.toBe(original.buffer)
|
|
265
|
-
expect(cloned.byteOffset).toBe(4)
|
|
266
|
-
expect(cloned.byteLength).toBe(8)
|
|
267
|
-
expect(cloned.getInt32(0)).toBe(42)
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
it('should clone TypedArrays with offset and length', () => {
|
|
271
|
-
const buffer = new ArrayBuffer(16)
|
|
272
|
-
const original = new Uint8Array(buffer, 4, 8)
|
|
273
|
-
original.set([10, 20, 30, 40, 50, 60, 70, 80])
|
|
274
|
-
const cloned = cloneDeep(original)
|
|
275
|
-
expect(cloned).not.toBe(original)
|
|
276
|
-
expect(cloned.buffer).not.toBe(original.buffer)
|
|
277
|
-
expect(cloned.byteOffset).toBe(4)
|
|
278
|
-
expect(cloned.length).toBe(8)
|
|
279
|
-
expect(Array.from(cloned)).toEqual([10, 20, 30, 40, 50, 60, 70, 80])
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
it('should clone Float64Array', () => {
|
|
283
|
-
const original = new Float64Array([1.1, 2.2, 3.3])
|
|
284
|
-
const cloned = cloneDeep(original)
|
|
285
|
-
expect(cloned).not.toBe(original)
|
|
286
|
-
expect(cloned.buffer).not.toBe(original.buffer)
|
|
287
|
-
expect(Array.from(cloned)).toEqual([1.1, 2.2, 3.3])
|
|
288
|
-
})
|
|
289
|
-
|
|
290
|
-
it('should clone RegExp with lastIndex', () => {
|
|
291
|
-
const original = /foo/g
|
|
292
|
-
original.exec('foobar')
|
|
293
|
-
expect(original.lastIndex).toBe(3)
|
|
294
|
-
const cloned = cloneDeep(original)
|
|
295
|
-
expect(cloned).not.toBe(original)
|
|
296
|
-
expect(cloned.lastIndex).toBe(3)
|
|
297
|
-
expect(cloned.source).toBe('foo')
|
|
298
|
-
expect(cloned.flags).toBe('g')
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
it('should clone object without constructor', () => {
|
|
302
|
-
const original = Object.create(null) as Record<string, unknown>
|
|
303
|
-
original.a = 1
|
|
304
|
-
original.b = { c: 2 }
|
|
305
|
-
const cloned = cloneDeep(original)
|
|
306
|
-
expect(cloned).not.toBe(original)
|
|
307
|
-
expect(cloned.a).toBe(1)
|
|
308
|
-
expect(cloned.b).toEqual({ c: 2 })
|
|
309
|
-
expect(cloned.b).not.toBe(original.b)
|
|
310
|
-
})
|
|
311
|
-
|
|
312
|
-
it('should clone Error instances preserving message and stack', () => {
|
|
313
|
-
const original = new Error('test error')
|
|
314
|
-
const cloned = cloneDeep(original)
|
|
315
|
-
expect(cloned).not.toBe(original)
|
|
316
|
-
expect(cloned).toBeInstanceOf(Error)
|
|
317
|
-
expect(cloned.message).toBe('test error')
|
|
318
|
-
expect(cloned.stack).toBe(original.stack)
|
|
319
|
-
})
|
|
320
|
-
|
|
321
|
-
it('should fall through to cloneCollection for partial BSON-like objects', () => {
|
|
322
|
-
const original = { _bsontype: 'SomeType', value: 42 }
|
|
323
|
-
const cloned = cloneDeep(original)
|
|
324
|
-
expect(cloned).toEqual({ _bsontype: 'SomeType', value: 42 })
|
|
325
|
-
expect(cloned).not.toBe(original)
|
|
326
|
-
})
|
|
327
|
-
|
|
328
|
-
it('should clone BSON-like objects via toHexString', () => {
|
|
329
|
-
const hex = '507f1f77bcf86cd799439011'
|
|
330
|
-
const original = {
|
|
331
|
-
_bsontype: 'ObjectId',
|
|
332
|
-
toHexString: () => hex,
|
|
333
|
-
toJSON: () => hex,
|
|
334
|
-
}
|
|
335
|
-
Object.setPrototypeOf(original, {
|
|
336
|
-
constructor: class ObjectId {
|
|
337
|
-
hex: string
|
|
338
|
-
constructor(h: string) {
|
|
339
|
-
this.hex = h
|
|
340
|
-
}
|
|
341
|
-
toHexString() {
|
|
342
|
-
return this.hex
|
|
343
|
-
}
|
|
344
|
-
},
|
|
345
|
-
})
|
|
346
|
-
const cloned = cloneDeep(original)
|
|
347
|
-
expect(cloned).not.toBe(original)
|
|
348
|
-
expect(cloned.toHexString()).toBe(hex)
|
|
349
|
-
})
|
|
350
|
-
})
|
|
351
|
-
|
|
352
|
-
describe('setPatchHistoryTTL', () => {
|
|
353
|
-
it('should call custom onError handler on failure', async () => {
|
|
354
|
-
const indexes = HistoryModel.collection.indexes as Mock
|
|
355
|
-
indexes.mockRejectedValue(new Error('connection failed'))
|
|
356
|
-
|
|
357
|
-
const onError = vi.fn()
|
|
358
|
-
await setPatchHistoryTTL('1h', onError)
|
|
359
|
-
|
|
360
|
-
expect(onError).toHaveBeenCalledOnce()
|
|
361
|
-
expect(onError).toHaveBeenCalledWith(expect.any(Error))
|
|
362
|
-
})
|
|
363
|
-
|
|
364
|
-
it('should fall back to console.error when no onError provided', async () => {
|
|
365
|
-
const indexes = HistoryModel.collection.indexes as Mock
|
|
366
|
-
indexes.mockRejectedValue(new Error('connection failed'))
|
|
367
|
-
|
|
368
|
-
const spy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
369
|
-
await setPatchHistoryTTL('1h')
|
|
370
|
-
|
|
371
|
-
expect(spy).toHaveBeenCalledOnce()
|
|
372
|
-
})
|
|
373
|
-
})
|
package/tests/mongo/.gitignore
DELETED
package/tests/mongo/server.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
import { MongoMemoryServer } from 'mongodb-memory-server'
|
|
3
|
-
import mongoose from 'mongoose'
|
|
4
|
-
|
|
5
|
-
const server = (dbName: string) => {
|
|
6
|
-
let mongo: MongoMemoryServer
|
|
7
|
-
const dbPath = `./tests/mongo/${dbName}`
|
|
8
|
-
|
|
9
|
-
const create = async () => {
|
|
10
|
-
fs.mkdirSync(dbPath, { recursive: true })
|
|
11
|
-
mongo = await MongoMemoryServer.create({
|
|
12
|
-
instance: {
|
|
13
|
-
dbName,
|
|
14
|
-
dbPath,
|
|
15
|
-
},
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
const uri = mongo.getUri()
|
|
19
|
-
await mongoose.connect(uri)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const destroy = async () => {
|
|
23
|
-
await mongoose.connection.dropDatabase()
|
|
24
|
-
await mongoose.connection.close()
|
|
25
|
-
await mongo.stop({ doCleanup: true, force: true })
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return { create, destroy }
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export default server
|
package/tests/ms.test.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { ms, UNITS } from '../src/ms'
|
|
4
|
-
|
|
5
|
-
const { s, m, h, d, w, mo, y } = UNITS
|
|
6
|
-
|
|
7
|
-
describe('ms', () => {
|
|
8
|
-
it('should parse milliseconds', () => {
|
|
9
|
-
expect(ms('100ms')).toBe(100)
|
|
10
|
-
expect(ms('500msecs')).toBe(500)
|
|
11
|
-
expect(ms('1millisecond')).toBe(1)
|
|
12
|
-
expect(ms('5milliseconds')).toBe(5)
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('should parse seconds', () => {
|
|
16
|
-
expect(ms('1s')).toBe(s)
|
|
17
|
-
expect(ms('5sec')).toBe(5 * s)
|
|
18
|
-
expect(ms('10secs')).toBe(10 * s)
|
|
19
|
-
expect(ms('1second')).toBe(s)
|
|
20
|
-
expect(ms('2seconds')).toBe(2 * s)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('should parse minutes', () => {
|
|
24
|
-
expect(ms('1m')).toBe(m)
|
|
25
|
-
expect(ms('5min')).toBe(5 * m)
|
|
26
|
-
expect(ms('10mins')).toBe(10 * m)
|
|
27
|
-
expect(ms('1minute')).toBe(m)
|
|
28
|
-
expect(ms('2minutes')).toBe(2 * m)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('should parse hours', () => {
|
|
32
|
-
expect(ms('1h')).toBe(h)
|
|
33
|
-
expect(ms('2hr')).toBe(2 * h)
|
|
34
|
-
expect(ms('3hrs')).toBe(3 * h)
|
|
35
|
-
expect(ms('1hour')).toBe(h)
|
|
36
|
-
expect(ms('2hours')).toBe(2 * h)
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it('should parse days', () => {
|
|
40
|
-
expect(ms('1d')).toBe(d)
|
|
41
|
-
expect(ms('1day')).toBe(d)
|
|
42
|
-
expect(ms('2days')).toBe(2 * d)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('should parse weeks', () => {
|
|
46
|
-
expect(ms('1w')).toBe(w)
|
|
47
|
-
expect(ms('1week')).toBe(w)
|
|
48
|
-
expect(ms('2weeks')).toBe(2 * w)
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('should parse months', () => {
|
|
52
|
-
expect(ms('1mo')).toBe(mo)
|
|
53
|
-
expect(ms('1month')).toBe(mo)
|
|
54
|
-
expect(ms('2months')).toBe(2 * mo)
|
|
55
|
-
expect(ms('6mo')).toBe(6 * mo)
|
|
56
|
-
expect(ms('0.5mo')).toBe(0.5 * mo)
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('should parse years', () => {
|
|
60
|
-
expect(ms('1y')).toBe(y)
|
|
61
|
-
expect(ms('1yr')).toBe(y)
|
|
62
|
-
expect(ms('1year')).toBe(y)
|
|
63
|
-
expect(ms('2years')).toBe(2 * y)
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('should default to milliseconds when no unit', () => {
|
|
67
|
-
expect(ms('500')).toBe(500)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('should handle decimal values', () => {
|
|
71
|
-
expect(ms('1.5h')).toBe(1.5 * h)
|
|
72
|
-
expect(ms('0.5d')).toBe(0.5 * d)
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('should handle negative values', () => {
|
|
76
|
-
expect(ms('-1s')).toBe(-s)
|
|
77
|
-
expect(ms('-500ms')).toBe(-500)
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('should handle spaces between number and unit', () => {
|
|
81
|
-
expect(ms('1 hour')).toBe(h)
|
|
82
|
-
expect(ms('2 days')).toBe(2 * d)
|
|
83
|
-
expect(ms('500 ms')).toBe(500)
|
|
84
|
-
expect(ms('30 seconds')).toBe(30 * s)
|
|
85
|
-
expect(ms('5 min')).toBe(5 * m)
|
|
86
|
-
expect(ms('1 mo')).toBe(mo)
|
|
87
|
-
expect(ms('1 week')).toBe(w)
|
|
88
|
-
expect(ms('1 year')).toBe(y)
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it('should be case insensitive', () => {
|
|
92
|
-
// @ts-expect-error runtime check
|
|
93
|
-
expect(ms('1H')).toBe(h)
|
|
94
|
-
// @ts-expect-error runtime check
|
|
95
|
-
expect(ms('1D')).toBe(d)
|
|
96
|
-
// @ts-expect-error runtime check
|
|
97
|
-
expect(ms('1MS')).toBe(1)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('should return NaN for strings over 100 chars', () => {
|
|
101
|
-
// @ts-expect-error testing invalid input
|
|
102
|
-
expect(ms('a'.repeat(101))).toBeNaN()
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
it('should return NaN for invalid strings', () => {
|
|
106
|
-
// @ts-expect-error testing invalid input
|
|
107
|
-
expect(ms('abc')).toBeNaN()
|
|
108
|
-
// @ts-expect-error testing invalid input
|
|
109
|
-
expect(ms('')).toBeNaN()
|
|
110
|
-
// @ts-expect-error testing invalid input
|
|
111
|
-
expect(ms('hello world')).toBeNaN()
|
|
112
|
-
})
|
|
113
|
-
})
|