ts-cache-mongoose 1.7.7 → 2.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 +79 -56
- package/biome.json +1 -1
- package/dist/index.cjs +86 -5
- package/dist/index.d.cts +1 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +86 -5
- package/dist/nest/index.cjs +111 -0
- package/dist/nest/index.d.cts +40 -0
- package/dist/nest/index.d.cts.map +1 -0
- package/dist/nest/index.d.mts +40 -0
- package/dist/nest/index.d.mts.map +1 -0
- package/dist/nest/index.mjs +108 -0
- package/package.json +43 -21
- package/src/cache/Cache.ts +1 -1
- package/src/cache/engine/MemoryCacheEngine.ts +1 -1
- package/src/cache/engine/RedisCacheEngine.ts +1 -1
- package/src/key.ts +2 -2
- package/src/ms.ts +55 -0
- package/src/nest/cache.module.ts +79 -0
- package/src/nest/cache.service.ts +37 -0
- package/src/nest/index.ts +4 -0
- package/src/nest/interfaces.ts +17 -0
- package/src/sort-keys.ts +38 -0
- package/src/types.ts +1 -2
- package/src/version.ts +1 -2
- package/tests/ms.test.ts +93 -0
- package/tests/nest.test.ts +158 -0
- package/tests/sort-keys.test.ts +80 -0
- package/tsconfig.json +1 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Logger, Module } from '@nestjs/common';
|
|
2
|
+
import mongoose from 'mongoose';
|
|
3
|
+
import CacheMongoose from '../index.mjs';
|
|
4
|
+
import 'bson';
|
|
5
|
+
import 'ioredis';
|
|
6
|
+
import 'node:crypto';
|
|
7
|
+
|
|
8
|
+
const CACHE_OPTIONS = Symbol("CACHE_OPTIONS");
|
|
9
|
+
class CacheService {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.logger = new Logger(CacheService.name);
|
|
12
|
+
this.options = options;
|
|
13
|
+
}
|
|
14
|
+
get instance() {
|
|
15
|
+
return this.cacheMongoose;
|
|
16
|
+
}
|
|
17
|
+
async onApplicationBootstrap() {
|
|
18
|
+
this.cacheMongoose = CacheMongoose.init(mongoose, this.options);
|
|
19
|
+
this.logger.log(`Cache initialized with ${this.options.engine} engine`);
|
|
20
|
+
}
|
|
21
|
+
async onApplicationShutdown() {
|
|
22
|
+
if (this.cacheMongoose) {
|
|
23
|
+
await this.cacheMongoose.close();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async clear(customKey) {
|
|
27
|
+
await this.cacheMongoose.clear(customKey);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
32
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
33
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
34
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
35
|
+
if (decorator = decorators[i])
|
|
36
|
+
result = (decorator(result)) || result;
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
let CacheModule = class {
|
|
40
|
+
static forRoot(options) {
|
|
41
|
+
return {
|
|
42
|
+
module: CacheModule,
|
|
43
|
+
global: options.isGlobal ?? false,
|
|
44
|
+
providers: [
|
|
45
|
+
{ provide: CACHE_OPTIONS, useValue: options },
|
|
46
|
+
{
|
|
47
|
+
provide: CacheService,
|
|
48
|
+
useFactory: (opts) => new CacheService(opts),
|
|
49
|
+
inject: [CACHE_OPTIONS]
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
exports: [CacheService]
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
static forRootAsync(options) {
|
|
56
|
+
const asyncProviders = CacheModule.createAsyncProviders(options);
|
|
57
|
+
return {
|
|
58
|
+
module: CacheModule,
|
|
59
|
+
global: options.isGlobal ?? false,
|
|
60
|
+
imports: options.imports ?? [],
|
|
61
|
+
providers: [
|
|
62
|
+
...asyncProviders,
|
|
63
|
+
{
|
|
64
|
+
provide: CacheService,
|
|
65
|
+
useFactory: (opts) => new CacheService(opts),
|
|
66
|
+
inject: [CACHE_OPTIONS]
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
exports: [CacheService]
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
static createAsyncProviders(options) {
|
|
73
|
+
if (options.useFactory) {
|
|
74
|
+
return [
|
|
75
|
+
{
|
|
76
|
+
provide: CACHE_OPTIONS,
|
|
77
|
+
useFactory: options.useFactory,
|
|
78
|
+
inject: options.inject ?? []
|
|
79
|
+
}
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
if (options.useClass) {
|
|
83
|
+
return [
|
|
84
|
+
{ provide: options.useClass, useClass: options.useClass },
|
|
85
|
+
{
|
|
86
|
+
provide: CACHE_OPTIONS,
|
|
87
|
+
useFactory: (factory) => factory.createCacheOptions(),
|
|
88
|
+
inject: [options.useClass]
|
|
89
|
+
}
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
if (options.useExisting) {
|
|
93
|
+
return [
|
|
94
|
+
{
|
|
95
|
+
provide: CACHE_OPTIONS,
|
|
96
|
+
useFactory: (factory) => factory.createCacheOptions(),
|
|
97
|
+
inject: [options.useExisting]
|
|
98
|
+
}
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
CacheModule = __decorateClass([
|
|
105
|
+
Module({})
|
|
106
|
+
], CacheModule);
|
|
107
|
+
|
|
108
|
+
export { CACHE_OPTIONS, CacheModule, CacheService };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-cache-mongoose",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Cache plugin for mongoose Queries and Aggregate (in-memory, redis)",
|
|
5
5
|
"author": "ilovepixelart",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,13 +47,32 @@
|
|
|
47
47
|
],
|
|
48
48
|
"type": "module",
|
|
49
49
|
"exports": {
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
50
|
+
".": {
|
|
51
|
+
"require": {
|
|
52
|
+
"types": "./dist/index.d.cts",
|
|
53
|
+
"default": "./dist/index.cjs"
|
|
54
|
+
},
|
|
55
|
+
"import": {
|
|
56
|
+
"types": "./dist/index.d.mts",
|
|
57
|
+
"default": "./dist/index.mjs"
|
|
58
|
+
}
|
|
53
59
|
},
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
|
|
60
|
+
"./nest": {
|
|
61
|
+
"require": {
|
|
62
|
+
"types": "./dist/nest/index.d.cts",
|
|
63
|
+
"default": "./dist/nest/index.cjs"
|
|
64
|
+
},
|
|
65
|
+
"import": {
|
|
66
|
+
"types": "./dist/nest/index.d.mts",
|
|
67
|
+
"default": "./dist/nest/index.mjs"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"typesVersions": {
|
|
72
|
+
"*": {
|
|
73
|
+
"nest": [
|
|
74
|
+
"./dist/nest/index.d.cts"
|
|
75
|
+
]
|
|
57
76
|
}
|
|
58
77
|
},
|
|
59
78
|
"main": "./dist/index.cjs",
|
|
@@ -71,31 +90,33 @@
|
|
|
71
90
|
"release": "npm install && npm run biome && npm run type:check && npm run build && np --no-publish"
|
|
72
91
|
},
|
|
73
92
|
"dependencies": {
|
|
74
|
-
"
|
|
75
|
-
"@types/semver": "7.7.1",
|
|
76
|
-
"ioredis": "5.9.3",
|
|
77
|
-
"ms": "2.1.3",
|
|
78
|
-
"semver": "7.7.4",
|
|
79
|
-
"sort-keys": "4.2.0"
|
|
93
|
+
"ioredis": "5.10.0"
|
|
80
94
|
},
|
|
81
95
|
"devDependencies": {
|
|
82
|
-
"@biomejs/biome": "2.4.
|
|
83
|
-
"@
|
|
84
|
-
"@
|
|
96
|
+
"@biomejs/biome": "2.4.7",
|
|
97
|
+
"@nestjs/common": "11.1.16",
|
|
98
|
+
"@types/node": "25.5.0",
|
|
99
|
+
"@vitest/coverage-v8": "4.1.0",
|
|
85
100
|
"bson": "7.2.0",
|
|
86
101
|
"mongodb-memory-server": "11.0.1",
|
|
87
|
-
"mongoose": "9.
|
|
102
|
+
"mongoose": "9.3.0",
|
|
103
|
+
"np": "11.0.2",
|
|
88
104
|
"open-cli": "8.0.0",
|
|
89
|
-
"pkgroll": "2.
|
|
105
|
+
"pkgroll": "2.27.0",
|
|
90
106
|
"simple-git-hooks": "2.13.1",
|
|
91
107
|
"typescript": "5.9.3",
|
|
92
|
-
"vitest": "4.0
|
|
93
|
-
"np": "11.0.2"
|
|
108
|
+
"vitest": "4.1.0"
|
|
94
109
|
},
|
|
95
110
|
"peerDependencies": {
|
|
111
|
+
"@nestjs/common": ">=9.0.0",
|
|
96
112
|
"bson": ">=4.7.2 < 8",
|
|
97
113
|
"mongoose": ">=6.6.0 < 10"
|
|
98
114
|
},
|
|
115
|
+
"peerDependenciesMeta": {
|
|
116
|
+
"@nestjs/common": {
|
|
117
|
+
"optional": true
|
|
118
|
+
}
|
|
119
|
+
},
|
|
99
120
|
"simple-git-hooks": {
|
|
100
121
|
"pre-commit": "npm run type:check",
|
|
101
122
|
"pre-push": "npm run biome:fix"
|
|
@@ -104,6 +125,7 @@
|
|
|
104
125
|
"publish": false
|
|
105
126
|
},
|
|
106
127
|
"overrides": {
|
|
107
|
-
"
|
|
128
|
+
"tmp": "0.2.5",
|
|
129
|
+
"file-type": "21.3.2"
|
|
108
130
|
}
|
|
109
131
|
}
|
package/src/cache/Cache.ts
CHANGED
package/src/key.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto'
|
|
2
|
-
import sortKeys from 'sort-keys'
|
|
2
|
+
import { sortKeys } from './sort-keys'
|
|
3
3
|
|
|
4
4
|
export function getKey(data: Record<string, unknown>[] | Record<string, unknown>): string {
|
|
5
|
-
const sortedObj = sortKeys(data
|
|
5
|
+
const sortedObj = sortKeys(data)
|
|
6
6
|
const sortedStr = JSON.stringify(sortedObj, (_, val: unknown) => {
|
|
7
7
|
return val instanceof RegExp ? String(val) : val
|
|
8
8
|
})
|
package/src/ms.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const s = 1000
|
|
2
|
+
const m = s * 60
|
|
3
|
+
const h = m * 60
|
|
4
|
+
const d = h * 24
|
|
5
|
+
const w = d * 7
|
|
6
|
+
const y = d * 365.25
|
|
7
|
+
|
|
8
|
+
// NOSONAR — regex from ms package, intentionally covers all time unit aliases
|
|
9
|
+
const RE = /^(-?(?:\d+)?\.?\d+)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i
|
|
10
|
+
|
|
11
|
+
const UNITS: Record<string, number> = {
|
|
12
|
+
years: y,
|
|
13
|
+
year: y,
|
|
14
|
+
yrs: y,
|
|
15
|
+
yr: y,
|
|
16
|
+
y,
|
|
17
|
+
weeks: w,
|
|
18
|
+
week: w,
|
|
19
|
+
w,
|
|
20
|
+
days: d,
|
|
21
|
+
day: d,
|
|
22
|
+
d,
|
|
23
|
+
hours: h,
|
|
24
|
+
hour: h,
|
|
25
|
+
hrs: h,
|
|
26
|
+
hr: h,
|
|
27
|
+
h,
|
|
28
|
+
minutes: m,
|
|
29
|
+
minute: m,
|
|
30
|
+
mins: m,
|
|
31
|
+
min: m,
|
|
32
|
+
m,
|
|
33
|
+
seconds: s,
|
|
34
|
+
second: s,
|
|
35
|
+
secs: s,
|
|
36
|
+
sec: s,
|
|
37
|
+
s,
|
|
38
|
+
milliseconds: 1,
|
|
39
|
+
millisecond: 1,
|
|
40
|
+
msecs: 1,
|
|
41
|
+
msec: 1,
|
|
42
|
+
ms: 1,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const ms = (val: string): number => {
|
|
46
|
+
const str = String(val)
|
|
47
|
+
if (str.length > 100) return 0
|
|
48
|
+
|
|
49
|
+
const match = RE.exec(str)
|
|
50
|
+
if (!match) return 0
|
|
51
|
+
|
|
52
|
+
const n = Number.parseFloat(match[1] ?? '')
|
|
53
|
+
const type = (match[2] ?? 'ms').toLowerCase()
|
|
54
|
+
return n * (UNITS[type] ?? 0)
|
|
55
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** biome-ignore-all lint/complexity/noStaticOnlyClass: nest */
|
|
2
|
+
import { Module } from '@nestjs/common'
|
|
3
|
+
import { CACHE_OPTIONS, CacheService } from './cache.service'
|
|
4
|
+
|
|
5
|
+
import type { DynamicModule, Provider } from '@nestjs/common'
|
|
6
|
+
import type { CacheModuleAsyncOptions, CacheModuleOptions, CacheOptionsFactory } from './interfaces'
|
|
7
|
+
|
|
8
|
+
@Module({})
|
|
9
|
+
export class CacheModule {
|
|
10
|
+
static forRoot(options: CacheModuleOptions & { isGlobal?: boolean }): DynamicModule {
|
|
11
|
+
return {
|
|
12
|
+
module: CacheModule,
|
|
13
|
+
global: options.isGlobal ?? false,
|
|
14
|
+
providers: [
|
|
15
|
+
{ provide: CACHE_OPTIONS, useValue: options },
|
|
16
|
+
{
|
|
17
|
+
provide: CacheService,
|
|
18
|
+
useFactory: (opts: CacheModuleOptions) => new CacheService(opts),
|
|
19
|
+
inject: [CACHE_OPTIONS],
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
exports: [CacheService],
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static forRootAsync(options: CacheModuleAsyncOptions): DynamicModule {
|
|
27
|
+
const asyncProviders = CacheModule.createAsyncProviders(options)
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
module: CacheModule,
|
|
31
|
+
global: options.isGlobal ?? false,
|
|
32
|
+
imports: options.imports ?? [],
|
|
33
|
+
providers: [
|
|
34
|
+
...asyncProviders,
|
|
35
|
+
{
|
|
36
|
+
provide: CacheService,
|
|
37
|
+
useFactory: (opts: CacheModuleOptions) => new CacheService(opts),
|
|
38
|
+
inject: [CACHE_OPTIONS],
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
exports: [CacheService],
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private static createAsyncProviders(options: CacheModuleAsyncOptions): Provider[] {
|
|
46
|
+
if (options.useFactory) {
|
|
47
|
+
return [
|
|
48
|
+
{
|
|
49
|
+
provide: CACHE_OPTIONS,
|
|
50
|
+
useFactory: options.useFactory,
|
|
51
|
+
inject: options.inject ?? [],
|
|
52
|
+
},
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (options.useClass) {
|
|
57
|
+
return [
|
|
58
|
+
{ provide: options.useClass, useClass: options.useClass },
|
|
59
|
+
{
|
|
60
|
+
provide: CACHE_OPTIONS,
|
|
61
|
+
useFactory: (factory: CacheOptionsFactory) => factory.createCacheOptions(),
|
|
62
|
+
inject: [options.useClass],
|
|
63
|
+
},
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (options.useExisting) {
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
provide: CACHE_OPTIONS,
|
|
71
|
+
useFactory: (factory: CacheOptionsFactory) => factory.createCacheOptions(),
|
|
72
|
+
inject: [options.useExisting],
|
|
73
|
+
},
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return []
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Logger } from '@nestjs/common'
|
|
2
|
+
import mongoose from 'mongoose'
|
|
3
|
+
import CacheMongoose from '../index'
|
|
4
|
+
|
|
5
|
+
import type { OnApplicationBootstrap, OnApplicationShutdown } from '@nestjs/common'
|
|
6
|
+
import type { CacheModuleOptions } from './interfaces'
|
|
7
|
+
|
|
8
|
+
export const CACHE_OPTIONS = Symbol('CACHE_OPTIONS')
|
|
9
|
+
|
|
10
|
+
export class CacheService implements OnApplicationBootstrap, OnApplicationShutdown {
|
|
11
|
+
private readonly logger = new Logger(CacheService.name)
|
|
12
|
+
private readonly options: CacheModuleOptions
|
|
13
|
+
private cacheMongoose!: CacheMongoose
|
|
14
|
+
|
|
15
|
+
constructor(options: CacheModuleOptions) {
|
|
16
|
+
this.options = options
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get instance(): CacheMongoose {
|
|
20
|
+
return this.cacheMongoose
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async onApplicationBootstrap(): Promise<void> {
|
|
24
|
+
this.cacheMongoose = CacheMongoose.init(mongoose, this.options)
|
|
25
|
+
this.logger.log(`Cache initialized with ${this.options.engine} engine`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async onApplicationShutdown(): Promise<void> {
|
|
29
|
+
if (this.cacheMongoose) {
|
|
30
|
+
await this.cacheMongoose.close()
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async clear(customKey?: string): Promise<void> {
|
|
35
|
+
await this.cacheMongoose.clear(customKey)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { InjectionToken, ModuleMetadata, OptionalFactoryDependency, Type } from '@nestjs/common'
|
|
2
|
+
import type { CacheOptions } from '../types'
|
|
3
|
+
|
|
4
|
+
export type CacheModuleOptions = CacheOptions
|
|
5
|
+
|
|
6
|
+
export interface CacheOptionsFactory {
|
|
7
|
+
createCacheOptions(): CacheModuleOptions | Promise<CacheModuleOptions>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface CacheModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
|
|
11
|
+
isGlobal?: boolean
|
|
12
|
+
inject?: (InjectionToken | OptionalFactoryDependency)[]
|
|
13
|
+
useClass?: Type<CacheOptionsFactory>
|
|
14
|
+
useExisting?: Type<CacheOptionsFactory>
|
|
15
|
+
// biome-ignore lint/suspicious/noExplicitAny: NestJS convention for factory args
|
|
16
|
+
useFactory?: (...args: any[]) => CacheModuleOptions | Promise<CacheModuleOptions>
|
|
17
|
+
}
|
package/src/sort-keys.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const isPlainObject = (value: unknown): value is Record<string, unknown> => {
|
|
2
|
+
if (typeof value !== 'object' || value === null) return false
|
|
3
|
+
const proto = Object.getPrototypeOf(value) as unknown
|
|
4
|
+
return proto === Object.prototype || proto === null
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const sortKeys = (input: Record<string, unknown> | Record<string, unknown>[]): Record<string, unknown> | Record<string, unknown>[] => {
|
|
8
|
+
const seen = new WeakSet<object>()
|
|
9
|
+
|
|
10
|
+
const sortObject = (obj: Record<string, unknown>): Record<string, unknown> => {
|
|
11
|
+
if (seen.has(obj)) return obj
|
|
12
|
+
seen.add(obj)
|
|
13
|
+
|
|
14
|
+
const sorted: Record<string, unknown> = {}
|
|
15
|
+
for (const key of Object.keys(obj).sort((a, b) => a.localeCompare(b))) {
|
|
16
|
+
const value = obj[key]
|
|
17
|
+
if (Array.isArray(value)) {
|
|
18
|
+
sorted[key] = sortArray(value)
|
|
19
|
+
} else if (isPlainObject(value)) {
|
|
20
|
+
sorted[key] = sortObject(value)
|
|
21
|
+
} else {
|
|
22
|
+
sorted[key] = value
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return sorted
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const sortArray = (arr: unknown[]): unknown[] => {
|
|
29
|
+
return arr.map((item) => {
|
|
30
|
+
if (Array.isArray(item)) return sortArray(item)
|
|
31
|
+
if (isPlainObject(item)) return sortObject(item)
|
|
32
|
+
return item
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (Array.isArray(input)) return sortArray(input) as Record<string, unknown>[]
|
|
37
|
+
return sortObject(input)
|
|
38
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { RedisOptions } from 'ioredis'
|
|
2
|
-
import type { StringValue } from 'ms'
|
|
3
2
|
|
|
4
|
-
export type CacheTTL = number |
|
|
3
|
+
export type CacheTTL = number | string
|
|
5
4
|
|
|
6
5
|
export type CacheData = Record<string, unknown> | Record<string, unknown>[] | unknown[] | number | undefined
|
|
7
6
|
|
package/src/version.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import mongoose from 'mongoose'
|
|
2
|
-
import { satisfies } from 'semver'
|
|
3
2
|
|
|
4
3
|
import type { CacheData } from './types'
|
|
5
4
|
|
|
6
|
-
export const isMongooseLessThan7 =
|
|
5
|
+
export const isMongooseLessThan7 = Number.parseInt(mongoose.version, 10) < 7
|
|
7
6
|
|
|
8
7
|
export const convertToObject = <T>(value: (T & { toObject?: () => CacheData }) | undefined): CacheData => {
|
|
9
8
|
if (isMongooseLessThan7) {
|
package/tests/ms.test.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
})
|