ts-patch-mongoose 2.9.6 → 3.0.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/README.md +42 -27
- package/biome.json +1 -1
- package/dist/index.cjs +273 -53
- package/dist/index.d.cts +41 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +41 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +273 -53
- package/package.json +12 -18
- package/src/helpers.ts +118 -9
- package/src/hooks/delete-hooks.ts +1 -4
- package/src/hooks/update-hooks.ts +6 -15
- package/src/index.ts +4 -32
- package/src/ms.ts +66 -0
- package/src/omit-deep.ts +95 -0
- package/src/patch.ts +19 -21
- package/src/version.ts +5 -4
- package/tests/em.test.ts +2 -0
- package/tests/helpers.test.ts +229 -2
- package/tests/ms.test.ts +113 -0
- package/tests/omit-deep.test.ts +220 -0
- package/tests/plugin-all-features.test.ts +741 -0
- package/tests/plugin-complex-data.test.ts +1332 -0
- package/tsconfig.json +2 -3
- package/src/modules/omit-deep.d.ts +0 -3
package/tests/ms.test.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
})
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { omitDeep } from '../src/omit-deep'
|
|
4
|
+
|
|
5
|
+
describe('omitDeep', () => {
|
|
6
|
+
it('should return empty object for undefined', () => {
|
|
7
|
+
expect(omitDeep(undefined, 'a')).toEqual({})
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('should return primitives as-is', () => {
|
|
11
|
+
expect(omitDeep(42 as unknown, 'a')).toBe(42)
|
|
12
|
+
expect(omitDeep('hello' as unknown, 'a')).toBe('hello')
|
|
13
|
+
expect(omitDeep(null as unknown, 'a')).toBe(null)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('should omit top-level keys', () => {
|
|
17
|
+
const obj = { a: 1, b: 2, c: 3 }
|
|
18
|
+
expect(omitDeep(obj, ['b'])).toEqual({ a: 1, c: 3 })
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('should omit with a single string key', () => {
|
|
22
|
+
const obj = { a: 1, b: 2 }
|
|
23
|
+
expect(omitDeep(obj, 'b')).toEqual({ a: 1 })
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('should omit nested keys recursively', () => {
|
|
27
|
+
const obj = { a: 1, nested: { a: 2, b: 3 } }
|
|
28
|
+
expect(omitDeep(obj, ['a'])).toEqual({ nested: { b: 3 } })
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should handle arrays of objects', () => {
|
|
32
|
+
const arr = [
|
|
33
|
+
{ a: 1, b: 2 },
|
|
34
|
+
{ a: 3, b: 4 },
|
|
35
|
+
]
|
|
36
|
+
expect(omitDeep(arr, ['a'])).toEqual([{ b: 2 }, { b: 4 }])
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should handle deeply nested structures', () => {
|
|
40
|
+
const obj = { level1: { level2: { level3: { secret: 'hidden', keep: 'visible' } } } }
|
|
41
|
+
expect(omitDeep(obj, ['secret'])).toEqual({ level1: { level2: { level3: { keep: 'visible' } } } })
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('should support dot-notation paths', () => {
|
|
45
|
+
const obj = { address: { street: '123 Main', city: 'Springfield' }, name: 'John' }
|
|
46
|
+
expect(omitDeep(obj, ['address.street'])).toEqual({ address: { city: 'Springfield' }, name: 'John' })
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should handle dot-notation with nested arrays', () => {
|
|
50
|
+
const obj = { config: { items: [{ secret: 1 }] }, meta: { secret: 2 } }
|
|
51
|
+
expect(omitDeep(obj, ['secret'])).toEqual({ config: { items: [{}] }, meta: {} })
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should block __proto__ in dot-notation paths', () => {
|
|
55
|
+
const obj = { a: { b: 1 } }
|
|
56
|
+
omitDeep(obj, ['__proto__.polluted'])
|
|
57
|
+
expect(({} as Record<string, unknown>).polluted).toBeUndefined()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('should block constructor in dot-notation paths', () => {
|
|
61
|
+
const obj = { a: 1 }
|
|
62
|
+
omitDeep(obj, ['constructor.something'])
|
|
63
|
+
expect(obj).toEqual({ a: 1 })
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should block prototype in dot-notation paths', () => {
|
|
67
|
+
const obj = { a: 1 }
|
|
68
|
+
omitDeep(obj, ['prototype.something'])
|
|
69
|
+
expect(obj).toEqual({ a: 1 })
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should return value if keys is not an array or string', () => {
|
|
73
|
+
const obj = { a: 1 }
|
|
74
|
+
expect(omitDeep(obj, 123 as unknown as string[])).toEqual({ a: 1 })
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should handle non-plain objects', () => {
|
|
78
|
+
const date = new Date()
|
|
79
|
+
expect(omitDeep(date as unknown, 'a')).toBe(date)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('should handle empty objects', () => {
|
|
83
|
+
expect(omitDeep({}, ['a'])).toEqual({})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('should handle empty arrays', () => {
|
|
87
|
+
expect(omitDeep([], ['a'])).toEqual([])
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('should handle dot-notation where intermediate path is missing', () => {
|
|
91
|
+
const obj = { a: 1 }
|
|
92
|
+
expect(omitDeep(obj, ['x.y.z'])).toEqual({ a: 1 })
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('should handle dot-notation where intermediate is not an object', () => {
|
|
96
|
+
const obj = { a: 'string' }
|
|
97
|
+
expect(omitDeep(obj, ['a.b'])).toEqual({ a: 'string' })
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should handle objects with null prototype', () => {
|
|
101
|
+
const obj = Object.create(null) as Record<string, unknown>
|
|
102
|
+
obj.a = 1
|
|
103
|
+
obj.b = 2
|
|
104
|
+
expect(omitDeep(obj, ['b'])).toEqual({ a: 1 })
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('should handle multiple keys at once', () => {
|
|
108
|
+
const obj = { a: 1, b: 2, c: 3, d: 4 }
|
|
109
|
+
expect(omitDeep(obj, ['a', 'c'])).toEqual({ b: 2, d: 4 })
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('should not omit from dot-notation when value at path has no value', () => {
|
|
113
|
+
const obj = { a: { b: '' } }
|
|
114
|
+
expect(omitDeep(obj, ['a.b'])).toEqual({ a: { b: '' } })
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('should omit dot-notation with nested value present', () => {
|
|
118
|
+
const obj = { a: { b: { c: 1 } } }
|
|
119
|
+
expect(omitDeep(obj, ['a.b.c'])).toEqual({ a: { b: {} } })
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('should handle object where nested values are all empty', () => {
|
|
123
|
+
const obj = { a: { b: {}, c: '' } }
|
|
124
|
+
expect(omitDeep(obj, ['x'])).toEqual({ a: { b: {}, c: '' } })
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('should handle object with RegExp values', () => {
|
|
128
|
+
const obj = { pattern: /test/i, name: 'hello' }
|
|
129
|
+
expect(omitDeep(obj, ['name'])).toEqual({ pattern: /test/i })
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('should handle object with Error values', () => {
|
|
133
|
+
const err = new Error('test error')
|
|
134
|
+
const obj = { error: err, name: 'hello' }
|
|
135
|
+
const result = omitDeep(obj, ['name'])
|
|
136
|
+
expect(result).toEqual({ error: err })
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('should handle object with Map values', () => {
|
|
140
|
+
const map = new Map([['key', 'value']])
|
|
141
|
+
const obj = { data: map, name: 'hello' } as Record<string, unknown>
|
|
142
|
+
const result = omitDeep(obj, ['name'])
|
|
143
|
+
expect(result.data).toBe(map)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('should handle object with Set values', () => {
|
|
147
|
+
const set = new Set([1, 2, 3])
|
|
148
|
+
const obj = { data: set, name: 'hello' } as Record<string, unknown>
|
|
149
|
+
const result = omitDeep(obj, ['name'])
|
|
150
|
+
expect(result.data).toBe(set)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('should handle object with boolean and number values at dot path', () => {
|
|
154
|
+
const obj = { config: { enabled: true, count: 0 } }
|
|
155
|
+
expect(omitDeep(obj, ['config.enabled'])).toEqual({ config: { count: 0 } })
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('should handle object with function values at dot path', () => {
|
|
159
|
+
const fn = () => 42
|
|
160
|
+
const obj = { handler: fn, name: 'test' } as Record<string, unknown>
|
|
161
|
+
expect(omitDeep(obj, ['name'])).toEqual({ handler: fn })
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('should handle object with array values at dot path', () => {
|
|
165
|
+
const obj = { tags: ['a', 'b'], name: 'test' }
|
|
166
|
+
expect(omitDeep(obj, ['tags'])).toEqual({ name: 'test' })
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should unset non-empty RegExp at dot path', () => {
|
|
170
|
+
const obj = { config: { pattern: /test/i } }
|
|
171
|
+
expect(omitDeep(obj, ['config.pattern'])).toEqual({ config: {} })
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('should unset Error with message at dot path', () => {
|
|
175
|
+
const obj = { config: { err: new Error('oops') } }
|
|
176
|
+
expect(omitDeep(obj, ['config.err'])).toEqual({ config: {} })
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('should unset non-empty Map at dot path', () => {
|
|
180
|
+
const obj = { config: { data: new Map([['a', 1]]) } } as Record<string, unknown>
|
|
181
|
+
expect(omitDeep(obj, ['config.data'])).toEqual({ config: {} })
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('should not unset empty Map at dot path (hasValue is false)', () => {
|
|
185
|
+
const obj = { config: { data: new Map() } } as Record<string, unknown>
|
|
186
|
+
const result = omitDeep(obj, ['config.data'])
|
|
187
|
+
expect(result.config).toHaveProperty('data')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('should unset non-empty Set at dot path', () => {
|
|
191
|
+
const obj = { config: { data: new Set([1]) } } as Record<string, unknown>
|
|
192
|
+
expect(omitDeep(obj, ['config.data'])).toEqual({ config: {} })
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
it('should not unset empty Set at dot path (hasValue is false)', () => {
|
|
196
|
+
const obj = { config: { data: new Set() } } as Record<string, unknown>
|
|
197
|
+
const result = omitDeep(obj, ['config.data'])
|
|
198
|
+
expect(result.config).toHaveProperty('data')
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it('should handle null value at dot-notation intermediate', () => {
|
|
202
|
+
const obj = { a: null } as Record<string, unknown>
|
|
203
|
+
expect(omitDeep(obj, ['a.b'])).toEqual({ a: null })
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('should handle mixed arrays with primitives and objects', () => {
|
|
207
|
+
const arr = [1, 'two', { a: 1, b: 2 }, null]
|
|
208
|
+
expect(omitDeep(arr, ['a'])).toEqual([1, 'two', { b: 2 }, null])
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('should unset non-empty array at dot path', () => {
|
|
212
|
+
const obj = { config: { items: [1, 2] } }
|
|
213
|
+
expect(omitDeep(obj, ['config.items'])).toEqual({ config: {} })
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('should not unset empty array at dot path (hasValue is false)', () => {
|
|
217
|
+
const obj = { config: { items: [] } }
|
|
218
|
+
expect(omitDeep(obj, ['config.items'])).toEqual({ config: { items: [] } })
|
|
219
|
+
})
|
|
220
|
+
})
|