ts-cache-mongoose 2.0.0 → 2.2.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 +68 -16
- package/dist/index.cjs +154 -132
- package/dist/index.d.cts +52 -10
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +52 -10
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +154 -132
- package/dist/nest/index.cjs +4 -1
- package/dist/nest/index.mjs +4 -1
- package/package.json +22 -24
- package/biome.json +0 -47
- package/src/cache/Cache.ts +0 -72
- package/src/cache/engine/MemoryCacheEngine.ts +0 -41
- package/src/cache/engine/RedisCacheEngine.ts +0 -52
- package/src/extend/aggregate.ts +0 -52
- package/src/extend/query.ts +0 -82
- package/src/index.ts +0 -68
- package/src/key.ts +0 -10
- package/src/ms.ts +0 -55
- package/src/nest/cache.module.ts +0 -79
- package/src/nest/cache.service.ts +0 -37
- package/src/nest/index.ts +0 -4
- package/src/nest/interfaces.ts +0 -17
- package/src/sort-keys.ts +0 -38
- package/src/types.ts +0 -20
- package/src/version.ts +0 -18
- package/tests/cache-debug.test.ts +0 -73
- package/tests/cache-memory.test.ts +0 -217
- package/tests/cache-options.test.ts +0 -83
- package/tests/cache-redis.test.ts +0 -521
- package/tests/key.test.ts +0 -103
- package/tests/models/Story.ts +0 -29
- package/tests/models/User.ts +0 -39
- package/tests/mongo/.gitignore +0 -3
- package/tests/mongo/server.ts +0 -29
- package/tests/ms.test.ts +0 -93
- package/tests/nest.test.ts +0 -158
- package/tests/sort-keys.test.ts +0 -80
- package/tsconfig.json +0 -34
- package/vite.config.mts +0 -23
package/tests/mongo/server.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
import { MongoMemoryServer } from 'mongodb-memory-server'
|
|
3
|
-
import mongoose from 'mongoose'
|
|
4
|
-
|
|
5
|
-
export 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
|
-
}
|
package/tests/ms.test.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { ms } from '../src/ms'
|
|
4
|
-
|
|
5
|
-
describe('ms', () => {
|
|
6
|
-
it('should parse seconds', () => {
|
|
7
|
-
expect(ms('1s')).toBe(1000)
|
|
8
|
-
expect(ms('5 seconds')).toBe(5000)
|
|
9
|
-
expect(ms('30 sec')).toBe(30000)
|
|
10
|
-
expect(ms('1 second')).toBe(1000)
|
|
11
|
-
expect(ms('2 secs')).toBe(2000)
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it('should parse minutes', () => {
|
|
15
|
-
expect(ms('1m')).toBe(60000)
|
|
16
|
-
expect(ms('5 minutes')).toBe(300000)
|
|
17
|
-
expect(ms('1 minute')).toBe(60000)
|
|
18
|
-
expect(ms('2 min')).toBe(120000)
|
|
19
|
-
expect(ms('3 mins')).toBe(180000)
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
it('should parse hours', () => {
|
|
23
|
-
expect(ms('1h')).toBe(3600000)
|
|
24
|
-
expect(ms('2 hours')).toBe(7200000)
|
|
25
|
-
expect(ms('1 hour')).toBe(3600000)
|
|
26
|
-
expect(ms('3 hr')).toBe(10800000)
|
|
27
|
-
expect(ms('4 hrs')).toBe(14400000)
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('should parse days', () => {
|
|
31
|
-
expect(ms('1d')).toBe(86400000)
|
|
32
|
-
expect(ms('2 days')).toBe(172800000)
|
|
33
|
-
expect(ms('1 day')).toBe(86400000)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('should parse weeks', () => {
|
|
37
|
-
expect(ms('1w')).toBe(604800000)
|
|
38
|
-
expect(ms('2 weeks')).toBe(1209600000)
|
|
39
|
-
expect(ms('1 week')).toBe(604800000)
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('should parse years', () => {
|
|
43
|
-
expect(ms('1y')).toBe(31557600000)
|
|
44
|
-
expect(ms('1 year')).toBe(31557600000)
|
|
45
|
-
expect(ms('2 yrs')).toBe(63115200000)
|
|
46
|
-
expect(ms('1 yr')).toBe(31557600000)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('should parse milliseconds', () => {
|
|
50
|
-
expect(ms('100ms')).toBe(100)
|
|
51
|
-
expect(ms('500 milliseconds')).toBe(500)
|
|
52
|
-
expect(ms('1 millisecond')).toBe(1)
|
|
53
|
-
expect(ms('200 msec')).toBe(200)
|
|
54
|
-
expect(ms('300 msecs')).toBe(300)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it('should parse decimal values', () => {
|
|
58
|
-
expect(ms('1.5h')).toBe(5400000)
|
|
59
|
-
expect(ms('0.5d')).toBe(43200000)
|
|
60
|
-
expect(ms('.5s')).toBe(500)
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('should parse negative values', () => {
|
|
64
|
-
expect(ms('-1s')).toBe(-1000)
|
|
65
|
-
expect(ms('-3m')).toBe(-180000)
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it('should default to milliseconds without unit', () => {
|
|
69
|
-
expect(ms('100')).toBe(100)
|
|
70
|
-
expect(ms('0')).toBe(0)
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
it('should be case insensitive', () => {
|
|
74
|
-
expect(ms('1S')).toBe(1000)
|
|
75
|
-
expect(ms('1M')).toBe(60000)
|
|
76
|
-
expect(ms('1H')).toBe(3600000)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('should return 0 for invalid strings', () => {
|
|
80
|
-
expect(ms('invalid')).toBe(0)
|
|
81
|
-
expect(ms('')).toBe(0)
|
|
82
|
-
expect(ms('abc123')).toBe(0)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('should return 0 for strings longer than 100 characters', () => {
|
|
86
|
-
expect(ms('a'.repeat(101))).toBe(0)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('should handle whitespace between number and unit', () => {
|
|
90
|
-
expect(ms('1 s')).toBe(1000)
|
|
91
|
-
expect(ms('5 minutes')).toBe(300000)
|
|
92
|
-
})
|
|
93
|
-
})
|
package/tests/nest.test.ts
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { CacheModule } from '../src/nest/cache.module'
|
|
4
|
-
import { CACHE_OPTIONS, CacheService } from '../src/nest/cache.service'
|
|
5
|
-
|
|
6
|
-
vi.mock('@nestjs/common', () => {
|
|
7
|
-
const LoggerMock = class {
|
|
8
|
-
log = vi.fn()
|
|
9
|
-
}
|
|
10
|
-
return {
|
|
11
|
-
Module: () => () => {},
|
|
12
|
-
Logger: LoggerMock,
|
|
13
|
-
}
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
vi.mock('../src/index', () => ({
|
|
17
|
-
default: {
|
|
18
|
-
init: vi.fn().mockReturnValue({
|
|
19
|
-
clear: vi.fn(),
|
|
20
|
-
close: vi.fn(),
|
|
21
|
-
}),
|
|
22
|
-
},
|
|
23
|
-
}))
|
|
24
|
-
|
|
25
|
-
const defaultOptions = { engine: 'memory' as const, defaultTTL: '60 seconds' }
|
|
26
|
-
|
|
27
|
-
describe('CacheModule', () => {
|
|
28
|
-
describe('forRoot', () => {
|
|
29
|
-
it('should return a dynamic module with providers', () => {
|
|
30
|
-
const result = CacheModule.forRoot(defaultOptions)
|
|
31
|
-
expect(result.module).toBe(CacheModule)
|
|
32
|
-
expect(result.providers).toBeDefined()
|
|
33
|
-
expect(result.exports).toContain(CacheService)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('should set global to false by default', () => {
|
|
37
|
-
const result = CacheModule.forRoot(defaultOptions)
|
|
38
|
-
expect(result.global).toBe(false)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('should set global when isGlobal is true', () => {
|
|
42
|
-
const result = CacheModule.forRoot({ ...defaultOptions, isGlobal: true })
|
|
43
|
-
expect(result.global).toBe(true)
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it('should provide CACHE_OPTIONS with useValue', () => {
|
|
47
|
-
const result = CacheModule.forRoot(defaultOptions)
|
|
48
|
-
const optionsProvider = (result.providers as { provide: symbol; useValue: unknown }[]).find((p) => p.provide === CACHE_OPTIONS)
|
|
49
|
-
expect(optionsProvider).toBeDefined()
|
|
50
|
-
expect(optionsProvider?.useValue).toEqual(defaultOptions)
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
describe('forRootAsync', () => {
|
|
55
|
-
it('should return a dynamic module with useFactory', () => {
|
|
56
|
-
const result = CacheModule.forRootAsync({
|
|
57
|
-
useFactory: () => defaultOptions,
|
|
58
|
-
})
|
|
59
|
-
expect(result.module).toBe(CacheModule)
|
|
60
|
-
expect(result.providers).toBeDefined()
|
|
61
|
-
expect(result.exports).toContain(CacheService)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('should support useClass', () => {
|
|
65
|
-
class TestFactory {
|
|
66
|
-
createCacheOptions() {
|
|
67
|
-
return defaultOptions
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
const result = CacheModule.forRootAsync({ useClass: TestFactory })
|
|
71
|
-
expect(result.providers).toBeDefined()
|
|
72
|
-
expect(result.providers?.length).toBeGreaterThan(1)
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('should support useExisting', () => {
|
|
76
|
-
class TestFactory {
|
|
77
|
-
createCacheOptions() {
|
|
78
|
-
return defaultOptions
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
const result = CacheModule.forRootAsync({ useExisting: TestFactory })
|
|
82
|
-
expect(result.providers).toBeDefined()
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('should return empty providers when no factory method', () => {
|
|
86
|
-
const result = CacheModule.forRootAsync({})
|
|
87
|
-
const providers = result.providers as unknown[]
|
|
88
|
-
const serviceProvider = providers.find((p) => typeof p === 'object' && p !== null && 'provide' in p && (p as { provide: unknown }).provide === CacheService)
|
|
89
|
-
expect(serviceProvider).toBeDefined()
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('should pass imports through', () => {
|
|
93
|
-
const result = CacheModule.forRootAsync({
|
|
94
|
-
imports: [],
|
|
95
|
-
useFactory: () => defaultOptions,
|
|
96
|
-
})
|
|
97
|
-
expect(result.imports).toEqual([])
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('should set global when isGlobal is true', () => {
|
|
101
|
-
const result = CacheModule.forRootAsync({
|
|
102
|
-
isGlobal: true,
|
|
103
|
-
useFactory: () => defaultOptions,
|
|
104
|
-
})
|
|
105
|
-
expect(result.global).toBe(true)
|
|
106
|
-
})
|
|
107
|
-
})
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
describe('CacheService', () => {
|
|
111
|
-
it('should store options', () => {
|
|
112
|
-
const service = new CacheService(defaultOptions)
|
|
113
|
-
expect(service).toBeDefined()
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('should initialize cache on bootstrap', async () => {
|
|
117
|
-
const service = new CacheService(defaultOptions)
|
|
118
|
-
await service.onApplicationBootstrap()
|
|
119
|
-
expect(service.instance).toBeDefined()
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it('should close cache on shutdown', async () => {
|
|
123
|
-
const service = new CacheService(defaultOptions)
|
|
124
|
-
await service.onApplicationBootstrap()
|
|
125
|
-
const closeSpy = vi.spyOn(service.instance, 'close')
|
|
126
|
-
await service.onApplicationShutdown()
|
|
127
|
-
expect(closeSpy).toHaveBeenCalled()
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('should not throw on shutdown when not initialized', async () => {
|
|
131
|
-
const service = new CacheService(defaultOptions)
|
|
132
|
-
await expect(service.onApplicationShutdown()).resolves.not.toThrow()
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('should clear cache with custom key', async () => {
|
|
136
|
-
const service = new CacheService(defaultOptions)
|
|
137
|
-
await service.onApplicationBootstrap()
|
|
138
|
-
const clearSpy = vi.spyOn(service.instance, 'clear')
|
|
139
|
-
await service.clear('test-key')
|
|
140
|
-
expect(clearSpy).toHaveBeenCalledWith('test-key')
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it('should clear all cache without key', async () => {
|
|
144
|
-
const service = new CacheService(defaultOptions)
|
|
145
|
-
await service.onApplicationBootstrap()
|
|
146
|
-
const clearSpy = vi.spyOn(service.instance, 'clear')
|
|
147
|
-
await service.clear()
|
|
148
|
-
expect(clearSpy).toHaveBeenCalledWith(undefined)
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
it('should expose instance getter', async () => {
|
|
152
|
-
const service = new CacheService(defaultOptions)
|
|
153
|
-
await service.onApplicationBootstrap()
|
|
154
|
-
expect(service.instance).toBeDefined()
|
|
155
|
-
expect(service.instance.clear).toBeDefined()
|
|
156
|
-
expect(service.instance.close).toBeDefined()
|
|
157
|
-
})
|
|
158
|
-
})
|
package/tests/sort-keys.test.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { sortKeys } from '../src/sort-keys'
|
|
4
|
-
|
|
5
|
-
describe('sortKeys', () => {
|
|
6
|
-
it('should sort object keys alphabetically', () => {
|
|
7
|
-
expect(sortKeys({ c: 1, a: 2, b: 3 })).toEqual({ a: 2, b: 3, c: 1 })
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
it('should sort nested objects deeply', () => {
|
|
11
|
-
expect(sortKeys({ b: { d: 1, c: 2 }, a: 1 })).toEqual({ a: 1, b: { c: 2, d: 1 } })
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it('should sort arrays of objects', () => {
|
|
15
|
-
const input = [
|
|
16
|
-
{ b: 1, a: 2 },
|
|
17
|
-
{ d: 3, c: 4 },
|
|
18
|
-
]
|
|
19
|
-
const expected = [
|
|
20
|
-
{ a: 2, b: 1 },
|
|
21
|
-
{ c: 4, d: 3 },
|
|
22
|
-
]
|
|
23
|
-
expect(sortKeys(input)).toEqual(expected)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('should handle nested arrays', () => {
|
|
27
|
-
const input = { items: [{ z: 1, a: 2 }] }
|
|
28
|
-
expect(sortKeys(input)).toEqual({ items: [{ a: 2, z: 1 }] })
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('should handle nested arrays of arrays', () => {
|
|
32
|
-
const input = { data: [[{ b: 1, a: 2 }]] }
|
|
33
|
-
expect(sortKeys(input)).toEqual({ data: [[{ a: 2, b: 1 }]] })
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('should preserve non-object values in arrays', () => {
|
|
37
|
-
const input = { items: [1, 'string', true, null] }
|
|
38
|
-
expect(sortKeys(input)).toEqual({ items: [1, 'string', true, null] })
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('should handle empty objects', () => {
|
|
42
|
-
expect(sortKeys({})).toEqual({})
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('should handle empty arrays', () => {
|
|
46
|
-
expect(sortKeys([])).toEqual([])
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('should handle deeply nested structures', () => {
|
|
50
|
-
const input = { c: { b: { a: { z: 1, y: 2 } } } }
|
|
51
|
-
expect(sortKeys(input)).toEqual({ c: { b: { a: { y: 2, z: 1 } } } })
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('should not modify non-plain objects', () => {
|
|
55
|
-
const date = new Date()
|
|
56
|
-
const input = { b: date, a: 1 }
|
|
57
|
-
const result = sortKeys(input) as Record<string, unknown>
|
|
58
|
-
expect(result.a).toBe(1)
|
|
59
|
-
expect(result.b).toBe(date)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('should handle circular references without infinite loop', () => {
|
|
63
|
-
const obj: Record<string, unknown> = { a: 1 }
|
|
64
|
-
obj.self = obj
|
|
65
|
-
const result = sortKeys(obj)
|
|
66
|
-
expect(result).toBeDefined()
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('should produce deterministic output regardless of input order', () => {
|
|
70
|
-
const a = sortKeys({ z: 1, a: 2, m: 3 })
|
|
71
|
-
const b = sortKeys({ a: 2, m: 3, z: 1 })
|
|
72
|
-
expect(JSON.stringify(a)).toBe(JSON.stringify(b))
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('should handle top-level array input', () => {
|
|
76
|
-
const input = [{ b: 1, a: 2 }] as Record<string, unknown>[]
|
|
77
|
-
const result = sortKeys(input)
|
|
78
|
-
expect(result).toEqual([{ a: 2, b: 1 }])
|
|
79
|
-
})
|
|
80
|
-
})
|
package/tsconfig.json
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"include": ["src"],
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"target": "ES2021",
|
|
5
|
-
"lib": ["ES2021"],
|
|
6
|
-
"types": ["node"],
|
|
7
|
-
"module": "Preserve",
|
|
8
|
-
"moduleResolution": "bundler",
|
|
9
|
-
"outDir": "dist",
|
|
10
|
-
"strict": true,
|
|
11
|
-
"allowSyntheticDefaultImports": true,
|
|
12
|
-
"allowUnreachableCode": false,
|
|
13
|
-
"allowUnusedLabels": false,
|
|
14
|
-
"checkJs": true,
|
|
15
|
-
"declaration": true,
|
|
16
|
-
"declarationMap": true,
|
|
17
|
-
"esModuleInterop": true,
|
|
18
|
-
"experimentalDecorators": true,
|
|
19
|
-
"exactOptionalPropertyTypes": true,
|
|
20
|
-
"forceConsistentCasingInFileNames": true,
|
|
21
|
-
"importHelpers": true,
|
|
22
|
-
"isolatedModules": true,
|
|
23
|
-
"noEmitOnError": true,
|
|
24
|
-
"noFallthroughCasesInSwitch": true,
|
|
25
|
-
"noImplicitOverride": true,
|
|
26
|
-
"noImplicitReturns": true,
|
|
27
|
-
"noUncheckedIndexedAccess": true,
|
|
28
|
-
"noUnusedLocals": true,
|
|
29
|
-
"noUnusedParameters": true,
|
|
30
|
-
"removeComments": true,
|
|
31
|
-
"skipLibCheck": true,
|
|
32
|
-
"sourceMap": true
|
|
33
|
-
}
|
|
34
|
-
}
|
package/vite.config.mts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vitest/config'
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
test: {
|
|
5
|
-
include: ['tests/**/*.test.ts'],
|
|
6
|
-
name: 'node',
|
|
7
|
-
environment: 'node',
|
|
8
|
-
coverage: {
|
|
9
|
-
provider: 'v8',
|
|
10
|
-
reporter: ['text', 'json', 'html', 'lcov'],
|
|
11
|
-
include: ['src/**/*.ts'],
|
|
12
|
-
exclude: [
|
|
13
|
-
'node_modules/**',
|
|
14
|
-
'dist/**',
|
|
15
|
-
'coverage/**',
|
|
16
|
-
'**/*.d.ts',
|
|
17
|
-
'**/*.config.*',
|
|
18
|
-
'**/tests/**',
|
|
19
|
-
'examples/**',
|
|
20
|
-
],
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
})
|