├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── custom.md
│ └── feature_request.md
├── dependabot.yml
└── workflows
│ ├── changelog.yaml
│ └── pr-check.yaml
├── .gitignore
├── .vscode
├── extensions.json
└── settings.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── biome.json
├── package-lock.json
├── package.json
├── src
├── cache
│ ├── Cache.ts
│ └── engine
│ │ ├── MemoryCacheEngine.ts
│ │ └── RedisCacheEngine.ts
├── extend
│ ├── aggregate.ts
│ └── query.ts
├── index.ts
├── key.ts
├── types.ts
└── version.ts
├── tests
├── cache-debug.test.ts
├── cache-memory.test.ts
├── cache-options.test.ts
├── cache-redis.test.ts
├── key.test.ts
├── models
│ ├── Story.ts
│ └── User.ts
└── mongo
│ ├── .gitignore
│ └── server.ts
├── tsconfig.json
└── vite.config.mts
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 |
16 | 1. Go to '...'
17 | 2. Click on '....'
18 | 3. Scroll down to '....'
19 | 4. See error
20 |
21 | **Expected behavior**
22 | A clear and concise description of what you expected to happen.
23 |
24 | **Screenshots**
25 | If applicable, add screenshots to help explain your problem.
26 |
27 | **Desktop (please complete the following information):**
28 |
29 | - OS: [e.g. iOS]
30 | - Browser [e.g. chrome, safari]
31 | - Version [e.g. 22]
32 |
33 | **Smartphone (please complete the following information):**
34 |
35 | - Device: [e.g. iPhone6]
36 | - OS: [e.g. iOS8.1]
37 | - Browser [e.g. stock browser, safari]
38 | - Version [e.g. 22]
39 |
40 | **Additional context**
41 | Add any other context about the problem here.
42 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Custom issue template
3 | about: Describe this issue template's purpose here.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "npm" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 | day: "monday"
13 | ignore:
14 | - dependency-name: "sort-keys"
15 | commit-message:
16 | prefix: "Update dependencies"
17 | pull-request-branch-name:
18 | separator: "-"
19 | groups:
20 | all-dependencies:
21 | patterns:
22 | - "*"
23 |
--------------------------------------------------------------------------------
/.github/workflows/changelog.yaml:
--------------------------------------------------------------------------------
1 | name: Changelog
2 |
3 | on:
4 | workflow_dispatch:
5 | # Trigger analysis when pushing in master or pull requests,
6 | # and when creating a pull request.
7 | release:
8 | types:
9 | - published
10 |
11 | jobs:
12 | changelog:
13 | name: Update changelog
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v4
18 | with:
19 | ref: main
20 | - name: Changelog
21 | uses: rhysd/changelog-from-release/action@v3
22 | with:
23 | file: CHANGELOG.md
24 | github_token: ${{ secrets.GITHUB_TOKEN }}
25 |
--------------------------------------------------------------------------------
/.github/workflows/pr-check.yaml:
--------------------------------------------------------------------------------
1 | name: PR check
2 |
3 | on:
4 | workflow_dispatch:
5 | # Trigger analysis when pushing in master or pull requests,
6 | # and when creating a pull request.
7 | push:
8 | branches:
9 | - master
10 | - feature*
11 |
12 | pull_request:
13 | types: [opened, synchronize, reopened]
14 |
15 | jobs:
16 | tests:
17 | name: Coverage
18 | runs-on: ubuntu-latest
19 |
20 | # Service containers to run with `container-job`
21 | services:
22 | # Label used to access the service container
23 | redis:
24 | # Docker Hub image
25 | image: redis
26 | # Set health checks to wait until redis has started
27 | options: >-
28 | --health-cmd "redis-cli ping"
29 | --health-interval 10s
30 | --health-timeout 5s
31 | --health-retries 5
32 | ports:
33 | # Maps port 6379 on service container to the host
34 | - 6379:6379
35 |
36 | strategy:
37 | matrix:
38 | node-version: [18.x, 20.x]
39 | mongoose-version:
40 | [
41 | [mongoose@6.12.8, bson@^4.7.2],
42 | [mongoose@7.6.11, bson@^5.5.0],
43 | [mongoose@latest, bson@^6.10.1],
44 | ]
45 | steps:
46 | - name: Checkout
47 | uses: actions/checkout@v4
48 | with:
49 | # Disabling shallow clone is recommended for improving relevancy of reporting
50 | fetch-depth: 0
51 |
52 | - name: Use Node.js ${{ matrix.node-version }}
53 | uses: actions/setup-node@v4
54 | with:
55 | node-version: ${{ matrix.node-version }}
56 | cache: npm
57 |
58 | - name: Installing dependencies
59 | run: npm ci
60 |
61 | - name: Installing ${{ matrix.mongoose-version[0] }} {{ matrix.mongoose-version[1] }}
62 | run: npm i ${{ matrix.mongoose-version[0] }} ${{ matrix.mongoose-version[1] }}
63 |
64 | - name: Coverage
65 | run: npm run test
66 | env:
67 | # The hostname used to communicate with the Redis service container
68 | REDIS_HOST: localhost
69 | # The default Redis port
70 | REDIS_PORT: 6379
71 |
72 | sonar:
73 | name: Biome, Coverage & Sonar
74 | runs-on: ubuntu-latest
75 |
76 | # Service containers to run with `container-job`
77 | services:
78 | # Label used to access the service container
79 | redis:
80 | # Docker Hub image
81 | image: redis
82 | # Set health checks to wait until redis has started
83 | options: >-
84 | --health-cmd "redis-cli ping"
85 | --health-interval 10s
86 | --health-timeout 5s
87 | --health-retries 5
88 | ports:
89 | # Maps port 6379 on service container to the host
90 | - 6379:6379
91 |
92 | strategy:
93 | matrix:
94 | node-version: [22.x]
95 | steps:
96 | - name: Checkout
97 | uses: actions/checkout@v4
98 | with:
99 | # Disabling shallow clone is recommended for improving relevancy of reporting
100 | fetch-depth: 0
101 |
102 | - name: Use Node.js ${{ matrix.node-version }}
103 | uses: actions/setup-node@v4
104 | with:
105 | node-version: ${{ matrix.node-version }}
106 | cache: npm
107 |
108 | - name: Installing dependencies
109 | run: npm ci
110 |
111 | - name: Biome
112 | run: npm run biome
113 |
114 | - name: Coverage
115 | run: npm run test
116 | env:
117 | # The hostname used to communicate with the Redis service container
118 | REDIS_HOST: localhost
119 | # The default Redis port
120 | REDIS_PORT: 6379
121 |
122 | - name: Scan
123 | if: env.SONAR_TOKEN
124 | uses: SonarSource/sonarqube-scan-action@master
125 | env:
126 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
127 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
128 | with:
129 | args: >
130 | -Dsonar.organization=ilovepixelart
131 | -Dsonar.projectName=ts-cache-mongoose
132 | -Dsonar.projectKey=ilovepixelart_ts-cache-mongoose
133 | -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info
134 | -Dsonar.sources=src
135 | -Dsonar.tests=tests
136 | -Dsonar.test.exclusions=tests/**
137 | -Dsonar.coverage.exclusions=tests/**
138 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # System files
2 | .DS_Store
3 | Thumbs.db
4 |
5 | # Dependency directory
6 | node_modules
7 | dist
8 | coverage
9 |
10 | # npm debug logs
11 | npm-debug.log
12 |
13 | # Webstorm
14 | .idea
15 | .env
16 |
17 | # In-memory mongo config
18 | /globalConfig.json
19 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "streetsidesoftware.code-spell-checker",
4 | "mikestead.dotenv",
5 | "biomejs.biome",
6 | "eamodio.gitlens",
7 | "vitest.explorer",
8 | "DavidAnson.vscode-markdownlint"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "explorer.fileNesting.enabled": true,
3 | "explorer.fileNesting.patterns": {
4 | "tsconfig.json": "tsconfig.*.json, env.d.ts",
5 | "package.json": "package-lock.json, .gitignore",
6 | "README.md": "CHANGELOG.md, CODE_OF_CONDUCT.md, CONTRIBUTING.md"
7 | },
8 | "editor.tabSize": 2,
9 | "editor.insertSpaces": true,
10 | "editor.defaultFormatter": "biomejs.biome",
11 | "editor.codeActionsOnSave": {
12 | "quickfix.biome": "explicit",
13 | "source.organizeImports.biome": "explicit"
14 | },
15 | "cSpell.words": [
16 | "autosync",
17 | "biomejs",
18 | "bson",
19 | "dbaeumer",
20 | "Dsonar",
21 | "EJSON",
22 | "ilovepixelart",
23 | "jsonpatch",
24 | "lcov",
25 | "linebreak",
26 | "nargs",
27 | "nodenext",
28 | "nosql",
29 | "npmignore",
30 | "parens",
31 | "pkgroll",
32 | "rhysd",
33 | "setex",
34 | "singleline",
35 | "sonarcloud",
36 | "sonarjs",
37 | "sonarqube",
38 | "sonarsource",
39 | "virtuals",
40 | "Webstorm"
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | # [v1.7.3](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.7.3) - 2025-04-05
3 |
4 | - Merge pull request [#287](https://github.com/ilovepixelart/ts-cache-mongoose/issues/287) from ilovepixelart/feature/dep 23469b7
5 | - Dep dbe8573
6 | - Merge pull request [#286](https://github.com/ilovepixelart/ts-cache-mongoose/issues/286) from ilovepixelart/feature/dep 85c0988
7 | - Dep 029495e
8 | - Merge pull request [#285](https://github.com/ilovepixelart/ts-cache-mongoose/issues/285) from ilovepixelart/dependabot-npm_and_yarn-all-dependencies-90d3097017 d6714ad
9 | - Update dependencies: Bump the all-dependencies group with 4 updates a0025dc
10 | - Merge pull request [#284](https://github.com/ilovepixelart/ts-cache-mongoose/issues/284) from ilovepixelart/dependabot-npm_and_yarn-all-dependencies-91aaeed3ea d009999
11 | - Update dependencies: Bump the all-dependencies group with 5 updates 4efc2c3
12 | - Merge pull request [#283](https://github.com/ilovepixelart/ts-cache-mongoose/issues/283) from ilovepixelart/dependabot-npm_and_yarn-all-dependencies-50bf62f782 5faf95d
13 | - Update dependencies: Bump the all-dependencies group with 5 updates 874d0cb
14 | - Update changelog for v1.7.2 d36dabf
15 |
16 | ---
17 |
18 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.7.2...v1.7.3
19 |
20 | [Changes][v1.7.3]
21 |
22 |
23 |
24 | # [v1.7.2](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.7.2) - 2025-03-02
25 |
26 | - Type check de1b39a
27 | - Lock 2b15683
28 | - Dep 7d60d5a
29 | - Doc 184f1f8
30 | - Merge pull request [#282](https://github.com/ilovepixelart/ts-cache-mongoose/issues/282) from ilovepixelart/feature/esm 76ad448
31 | - ESM 939621a
32 | - Merge pull request [#281](https://github.com/ilovepixelart/ts-cache-mongoose/issues/281) from ilovepixelart/feature/dep d505bb6
33 | - Dep aab190e
34 | - Override c268240
35 | - Merge pull request [#278](https://github.com/ilovepixelart/ts-cache-mongoose/issues/278) from ilovepixelart/feature/pkgroll 472772d
36 | - Merge remote-tracking branch &[#39](https://github.com/ilovepixelart/ts-cache-mongoose/issues/39);origin/main&[#39](https://github.com/ilovepixelart/ts-cache-mongoose/issues/39); into feature/pkgroll 988d322
37 | - Merge pull request [#279](https://github.com/ilovepixelart/ts-cache-mongoose/issues/279) from ilovepixelart/dependabot-npm_and_yarn-all-dependencies-b484da3d92 d698f0b
38 | - Update dependencies: Bump the all-dependencies group with 5 updates 6f8eec0
39 | - Dep 36c82ec
40 | - Clean lock bb52056
41 | - Import 5ee7e30
42 | - Switch to pkgroll 49ebd60
43 | - Merge pull request [#277](https://github.com/ilovepixelart/ts-cache-mongoose/issues/277) from ilovepixelart/dependabot-npm_and_yarn-all-dependencies-840c07ccc3 c4b9c70
44 | - Update dependencies: Bump the all-dependencies group with 2 updates ea6ac5c
45 | - Merge pull request [#276](https://github.com/ilovepixelart/ts-cache-mongoose/issues/276) from ilovepixelart/dependabot-npm_and_yarn-all-dependencies-e2d4cef7ba a68ca84
46 | - Update dependencies: Bump the all-dependencies group with 6 updates 1f8d281
47 | - Merge pull request [#275](https://github.com/ilovepixelart/ts-cache-mongoose/issues/275) from ilovepixelart/dependabot-npm_and_yarn-all-dependencies-8fd2c74fee 90d1369
48 | - Update dependencies: Bump the all-dependencies group with 4 updates 3073331
49 | - Merge pull request [#274](https://github.com/ilovepixelart/ts-cache-mongoose/issues/274) from ilovepixelart/dependabot-npm_and_yarn-all-dependencies-10fca7b204 b8bc4d7
50 | - Update dependencies: Bump the all-dependencies group with 3 updates 3cee387
51 | - Update changelog for v1.7.1 c23e87d
52 |
53 | ---
54 |
55 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.7.1...v1.7.2
56 |
57 | [Changes][v1.7.2]
58 |
59 |
60 |
61 | # [v1.7.1](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.7.1) - 2025-01-19
62 |
63 | - Biome afbb41d
64 | - Remove unused bffab0d
65 | - Remove unused 16fbb85
66 | - Update changelog for v1.7.0 6b47229
67 |
68 | ---
69 |
70 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.7.0...v1.7.1
71 |
72 | [Changes][v1.7.1]
73 |
74 |
75 |
76 | # [v1.7.0](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.7.0) - 2025-01-19
77 |
78 | - Merge pull request [#273](https://github.com/ilovepixelart/ts-cache-mongoose/issues/273) from ilovepixelart/feature/move-all-types-to-one-file c169849
79 | - Sort imports dc45b11
80 | - Refactor import, export, test models 84508bf
81 | - Convert cache options to type 1a90719
82 | - Move all types to one file and renamed: - type IData -> CacheData - type ICacheOptions -> CacheOptions - type ICacheEngine -> CacheEngine - class CacheEngine -> Cache 8851b9a
83 | - Update changelog for v1.6.8 8d56584
84 |
85 | ---
86 |
87 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.8...v1.7.0
88 |
89 | [Changes][v1.7.0]
90 |
91 |
92 |
93 | # [v1.6.8](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.6.8) - 2025-01-19
94 |
95 | - Merge pull request [#272](https://github.com/ilovepixelart/ts-cache-mongoose/issues/272) from ilovepixelart/feature/better-types-for-ttl c61d9e1
96 | - Better types for TTL 85454ca
97 | - Merge pull request [#271](https://github.com/ilovepixelart/ts-cache-mongoose/issues/271) from ilovepixelart/dependabot-npm_and_yarn-all-dependencies-0bc38f1f4e cdb5fee
98 | - Update dependencies: Bump the all-dependencies group with 5 updates 5f78453
99 | - Dependabot a135c4e
100 | - Update settings f1e2765
101 | - Merge pull request [#270](https://github.com/ilovepixelart/ts-cache-mongoose/issues/270) from ilovepixelart/feature/update-action-and-dep 6f3f7d3
102 | - Action and Dep 3e003dc
103 | - Merge pull request [#267](https://github.com/ilovepixelart/ts-cache-mongoose/issues/267) from ilovepixelart/dependabot/npm_and_yarn/mongoose-8.9.3 bbbac03
104 | - Bump mongoose from 8.9.2 to 8.9.3 55bd4fa
105 | - Merge pull request [#268](https://github.com/ilovepixelart/ts-cache-mongoose/issues/268) from ilovepixelart/dependabot/npm_and_yarn/types/node-22.10.5 823c518
106 | - Merge pull request [#269](https://github.com/ilovepixelart/ts-cache-mongoose/issues/269) from ilovepixelart/dependabot/npm_and_yarn/mongodb-memory-server-10.1.3 14d27bc
107 | - Bump mongodb-memory-server from 10.1.2 to 10.1.3 0071236
108 | - Bump @types/node from 22.10.2 to 22.10.5 411c9ae
109 | - Merge pull request [#265](https://github.com/ilovepixelart/ts-cache-mongoose/issues/265) from ilovepixelart/dependabot/npm_and_yarn/ioredis-5.4.2 7facf90
110 | - Merge pull request [#266](https://github.com/ilovepixelart/ts-cache-mongoose/issues/266) from ilovepixelart/dependabot/npm_and_yarn/mongoose-8.9.2 9ee8e8d
111 | - Bump mongoose from 8.9.0 to 8.9.2 2db95e8
112 | - Bump ioredis from 5.4.1 to 5.4.2 85bec09
113 | - Merge pull request [#263](https://github.com/ilovepixelart/ts-cache-mongoose/issues/263) from ilovepixelart/dependabot/npm_and_yarn/types/node-22.10.2 562a9fe
114 | - Merge pull request [#264](https://github.com/ilovepixelart/ts-cache-mongoose/issues/264) from ilovepixelart/dependabot/npm_and_yarn/mongoose-8.9.0 5e99298
115 | - Bump mongoose from 8.8.4 to 8.9.0 817c968
116 | - Bump @types/node from 22.10.1 to 22.10.2 cd0b7f1
117 | - Merge pull request [#262](https://github.com/ilovepixelart/ts-cache-mongoose/issues/262) from ilovepixelart/dependabot/npm_and_yarn/bson-6.10.1 0fd4eac
118 | - Merge pull request [#261](https://github.com/ilovepixelart/ts-cache-mongoose/issues/261) from ilovepixelart/dependabot/npm_and_yarn/mongoose-8.8.4 8b30004
119 | - Bump bson from 6.10.0 to 6.10.1 dd15d78
120 | - Bump mongoose from 8.8.3 to 8.8.4 12a09cf
121 | - Update changelog for v1.6.7 3c2df91
122 |
123 | ---
124 |
125 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.7...v1.6.8
126 |
127 | [Changes][v1.6.8]
128 |
129 |
130 |
131 | # [v1.6.7](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.6.7) - 2024-12-03
132 |
133 | - Merge pull request [#260](https://github.com/ilovepixelart/ts-cache-mongoose/issues/260) from ilovepixelart/feature/dep ff760c0
134 | - Dep 93cda9f
135 | - Merge pull request [#259](https://github.com/ilovepixelart/ts-cache-mongoose/issues/259) from ilovepixelart/feature/dep f5a8ba2
136 | - Dep 62d36fd
137 | - Merge pull request [#256](https://github.com/ilovepixelart/ts-cache-mongoose/issues/256) from ilovepixelart/dependabot/npm_and_yarn/types/node-22.9.3 84e0e12
138 | - Merge pull request [#257](https://github.com/ilovepixelart/ts-cache-mongoose/issues/257) from ilovepixelart/dependabot/npm_and_yarn/bson-6.10.0 f3fe5e1
139 | - Merge pull request [#258](https://github.com/ilovepixelart/ts-cache-mongoose/issues/258) from ilovepixelart/dependabot/npm_and_yarn/typescript-5.7.2 fcb68dd
140 | - Bump typescript from 5.6.3 to 5.7.2 8042d70
141 | - Bump bson from 6.9.0 to 6.10.0 8194b74
142 | - Bump @types/node from 22.9.1 to 22.9.3 fd90046
143 | - File 2b810ac
144 | - Merge pull request [#255](https://github.com/ilovepixelart/ts-cache-mongoose/issues/255) from ilovepixelart/snyk-upgrade-0409fd4b367c28a88d5514ee927f3f98 f78af6b
145 | - fix: upgrade bson from 6.8.0 to 6.9.0 7e98dba
146 | - Merge pull request [#254](https://github.com/ilovepixelart/ts-cache-mongoose/issues/254) from ilovepixelart/feature/dep 9d3a6a4
147 | - Dep & Coverage 1fd9ab2
148 | - Merge pull request [#253](https://github.com/ilovepixelart/ts-cache-mongoose/issues/253) from ilovepixelart/feature/vitest f726974
149 | - Test 319b7c5
150 | - Merge pull request [#252](https://github.com/ilovepixelart/ts-cache-mongoose/issues/252) from ilovepixelart/feature/vitest 02d856c
151 | - npm 6c73895
152 | - Biome 0bf9013
153 | - Server create, destroy 7cfc5a0
154 | - Biome 989871b
155 | - Unify, fs fallback 44e650e
156 | - Types 7142164
157 | - Types 6576451
158 | - Biome cfb97ab
159 | - Refactor b27064e
160 | - Refactor fc1f2a6
161 | - toBe db01da3
162 | - Biome fix b5b88f4
163 | - Vitest ac72099
164 | - Doc 958d33a
165 | - Doc 7bd8e6d
166 | - Merge pull request [#251](https://github.com/ilovepixelart/ts-cache-mongoose/issues/251) from ilovepixelart/dependabot/npm_and_yarn/mongoose-8.8.1 c1f4e07
167 | - Merge pull request [#250](https://github.com/ilovepixelart/ts-cache-mongoose/issues/250) from ilovepixelart/dependabot/npm_and_yarn/types/node-22.9.0 d8d481a
168 | - Bump mongoose from 8.8.0 to 8.8.1 3095ccd
169 | - Bump @types/node from 22.8.7 to 22.9.0 6a7f71f
170 | - Merge pull request [#248](https://github.com/ilovepixelart/ts-cache-mongoose/issues/248) from ilovepixelart/dependabot/npm_and_yarn/types/node-22.8.7 4ef2107
171 | - Merge pull request [#249](https://github.com/ilovepixelart/ts-cache-mongoose/issues/249) from ilovepixelart/dependabot/npm_and_yarn/mongoose-8.8.0 6dd5b2f
172 | - Bump mongoose from 8.7.3 to 8.8.0 1c67e93
173 | - Bump @types/node from 22.8.1 to 22.8.7 7624065
174 | - Update changelog for v1.6.6 2382ddd
175 |
176 | ---
177 |
178 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.6...v1.6.7
179 |
180 | [Changes][v1.6.7]
181 |
182 |
183 |
184 | # [v1.6.6](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.6.6) - 2024-10-26
185 |
186 | - Merge pull request [#246](https://github.com/ilovepixelart/ts-cache-mongoose/issues/246) from ilovepixelart/feature/dep 37a0af2
187 | - Dep d8c0a00
188 | - Merge pull request [#243](https://github.com/ilovepixelart/ts-cache-mongoose/issues/243) from ilovepixelart/dependabot/npm_and_yarn/biomejs/biome-1.9.4 8ef3716
189 | - Merge pull request [#244](https://github.com/ilovepixelart/ts-cache-mongoose/issues/244) from ilovepixelart/dependabot/npm_and_yarn/types/node-22.7.7 89805b9
190 | - Bump @types/node from 22.7.5 to 22.7.7 dd5d7bf
191 | - Bump @biomejs/biome from 1.9.3 to 1.9.4 e19eedb
192 | - Merge pull request [#242](https://github.com/ilovepixelart/ts-cache-mongoose/issues/242) from ilovepixelart/dependabot/npm_and_yarn/mongoose-8.7.2 e264412
193 | - Bump mongoose from 8.7.1 to 8.7.2 c9e36ed
194 | - Merge pull request [#239](https://github.com/ilovepixelart/ts-cache-mongoose/issues/239) from ilovepixelart/dependabot/npm_and_yarn/mongoose-8.7.1 3e93949
195 | - Merge pull request [#241](https://github.com/ilovepixelart/ts-cache-mongoose/issues/241) from ilovepixelart/dependabot/npm_and_yarn/types/node-22.7.5 00729a3
196 | - Merge pull request [#240](https://github.com/ilovepixelart/ts-cache-mongoose/issues/240) from ilovepixelart/dependabot/npm_and_yarn/typescript-5.6.3 9dd411e
197 | - Bump @types/node from 22.7.4 to 22.7.5 1cf6f88
198 | - Bump typescript from 5.6.2 to 5.6.3 693b60b
199 | - Bump mongoose from 8.7.0 to 8.7.1 b31da83
200 | - Merge pull request [#238](https://github.com/ilovepixelart/ts-cache-mongoose/issues/238) from ilovepixelart/feature/switch-to-biome 8deb6a1
201 | - Command f761f98
202 | - Simplify biome config to required rules ecf033b
203 | - Merge pull request [#237](https://github.com/ilovepixelart/ts-cache-mongoose/issues/237) from ilovepixelart/feature/switch-to-biome 24f7b4f
204 | - Switch to biome d1bf328
205 | - Cleanup 66d2087
206 | - Test for debug logs in cache 4a98e25
207 | - Update changelog for v1.6.5 f74152c
208 |
209 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.5...v1.6.6
210 |
211 | [Changes][v1.6.6]
212 |
213 |
214 |
215 | # [v1.6.5](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.6.5) - 2024-09-21
216 |
217 | - Merge pull request [#231](https://github.com/ilovepixelart/ts-cache-mongoose/issues/231) from ilovepixelart/feature/dep 1886957
218 | - Dep a1937c6
219 | - Update changelog for v1.6.4 0bc67f7
220 |
221 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.4...v1.6.5
222 |
223 | [Changes][v1.6.5]
224 |
225 |
226 |
227 | # [v1.6.4](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.6.4) - 2024-08-01
228 |
229 | - Merge pull request [#224](https://github.com/ilovepixelart/ts-cache-mongoose/issues/224) from ilovepixelart/feature/dep dc8b560
230 | - Dep 900009d
231 | - Update changelog for v1.6.3 f3cc884
232 |
233 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.3...v1.6.4
234 |
235 | [Changes][v1.6.4]
236 |
237 |
238 |
239 | # [v1.6.3](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.6.3) - 2024-07-06
240 |
241 | - Merge pull request [#214](https://github.com/ilovepixelart/ts-cache-mongoose/issues/214) from ilovepixelart/feature/dep d34c8fc
242 | - Dep ed45048
243 | - Doc 42649db
244 | - Update changelog for v1.6.2 a25949e
245 |
246 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.2...v1.6.3
247 |
248 | [Changes][v1.6.3]
249 |
250 |
251 |
252 | # [v1.6.2](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.6.2) - 2024-06-15
253 |
254 | - Merge pull request [#208](https://github.com/ilovepixelart/ts-cache-mongoose/issues/208) from ilovepixelart/feature/dep 81bbd44
255 | - Doc 6fad2db
256 | - Dep 75a5b37
257 | - Merge pull request [#207](https://github.com/ilovepixelart/ts-cache-mongoose/issues/207) from ilovepixelart/feature/upgrade-actions 65e97a7
258 | - Upgrade actions 49c732b
259 | - Merge pull request [#206](https://github.com/ilovepixelart/ts-cache-mongoose/issues/206) from ilovepixelart/dependabot/npm_and_yarn/typescript-eslint/parser-7.11.0 8781ae1
260 | - Bump @typescript-eslint/parser from 7.9.0 to 7.11.0 a299c97
261 | - Merge pull request [#202](https://github.com/ilovepixelart/ts-cache-mongoose/issues/202) from ilovepixelart/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-7.11.0 24183e9
262 | - Merge pull request [#205](https://github.com/ilovepixelart/ts-cache-mongoose/issues/205) from ilovepixelart/dependabot/npm_and_yarn/mongoose-8.4.1 dad5623
263 | - Merge pull request [#204](https://github.com/ilovepixelart/ts-cache-mongoose/issues/204) from ilovepixelart/dependabot/npm_and_yarn/types/node-20.13.0 56dce3d
264 | - Bump mongoose from 8.4.0 to 8.4.1 ed0fc49
265 | - Bump @types/node from 20.12.7 to 20.13.0 3614288
266 | - Bump @typescript-eslint/eslint-plugin from 7.9.0 to 7.11.0 23417ad
267 | - Update changelog for v1.6.1 66bec2f
268 |
269 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.1...v1.6.2
270 |
271 | [Changes][v1.6.2]
272 |
273 |
274 |
275 | # [v1.6.1](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.6.1) - 2024-05-18
276 |
277 | - Merge pull request [#201](https://github.com/ilovepixelart/ts-cache-mongoose/issues/201) from ilovepixelart/feature/dep 708483f
278 | - Dep 331728b
279 | - Update changelog for v1.6.0 fd97423
280 |
281 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.0...v1.6.1
282 |
283 | [Changes][v1.6.1]
284 |
285 |
286 |
287 | # [v1.6.0](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.6.0) - 2024-05-10
288 |
289 | - Merge pull request [#200](https://github.com/ilovepixelart/ts-cache-mongoose/issues/200) from ilovepixelart/feature/test de2f1d1
290 | - Test for populated 766d766
291 | - Merge pull request [#199](https://github.com/ilovepixelart/ts-cache-mongoose/issues/199) from ilovepixelart/feature/tests 9a28de6
292 | - More tests 81dfdd8
293 | - Merge pull request [#198](https://github.com/ilovepixelart/ts-cache-mongoose/issues/198) from ilovepixelart/feature/serializer 12ea7f8
294 | - Import order 42dd7a4
295 | - Fallback to db in case serialization fails 5e95320
296 | - Node 22 1f9798c
297 | - Dep aba8c01
298 | - Types 26e8a4b
299 | - Dep 72e2ae5
300 | - Latest major versions d171775
301 | - Try parallel again 239419d
302 | - Dep 62691c0
303 | - Sequential + bson versions 4041200
304 | - Pipeline ffc9994
305 | - Check for all versions a9486cb
306 | - Dep 5564204
307 | - Dep c43e7e2
308 | - Lint 2ff3b04
309 | - Corner case for mongoose 6 a3341e4
310 | - Dep 5215a13
311 | - Dep d794831
312 | - Serializer b146f59
313 | - Merge pull request [#193](https://github.com/ilovepixelart/ts-cache-mongoose/issues/193) from ilovepixelart/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-7.8.0 2c29a03
314 | - Bump @typescript-eslint/eslint-plugin from 7.7.0 to 7.8.0 c369e18
315 | - Merge pull request [#194](https://github.com/ilovepixelart/ts-cache-mongoose/issues/194) from ilovepixelart/dependabot/npm_and_yarn/typescript-eslint/parser-7.8.0 dbc3837
316 | - Merge pull request [#195](https://github.com/ilovepixelart/ts-cache-mongoose/issues/195) from ilovepixelart/dependabot/npm_and_yarn/eslint-plugin-jest-28.3.0 1e342fd
317 | - Merge pull request [#196](https://github.com/ilovepixelart/ts-cache-mongoose/issues/196) from ilovepixelart/dependabot/npm_and_yarn/swc/helpers-0.5.11 aefc0d3
318 | - Merge pull request [#197](https://github.com/ilovepixelart/ts-cache-mongoose/issues/197) from ilovepixelart/dependabot/npm_and_yarn/types/node-20.12.7 934d890
319 | - Bump @types/node from 20.12.2 to 20.12.7 25a8db9
320 | - Bump @swc/helpers from 0.5.10 to 0.5.11 e62b766
321 | - Bump eslint-plugin-jest from 28.2.0 to 28.3.0 2299342
322 | - Bump @typescript-eslint/parser from 7.7.0 to 7.8.0 7723e23
323 | - Merge pull request [#192](https://github.com/ilovepixelart/ts-cache-mongoose/issues/192) from ilovepixelart/feature/dep 13eed72
324 | - Dep 44a924e
325 | - Update changelog for v1.5.0 aa9177b
326 |
327 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.5.0...v1.6.0
328 |
329 | [Changes][v1.6.0]
330 |
331 |
332 |
333 | # [v1.5.0](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.5.0) - 2024-04-01
334 |
335 | - Eslint 6f4aea2
336 | - Merge pull request [#183](https://github.com/ilovepixelart/ts-cache-mongoose/issues/183) from PfisterFactor/main e1da1f1
337 | - fix: lint again 214fb35
338 | - Merge pull request [#189](https://github.com/ilovepixelart/ts-cache-mongoose/issues/189) from ilovepixelart/dependabot/npm_and_yarn/eslint-plugin-sonarjs-0.25.0 7a9fa42
339 | - Bump eslint-plugin-sonarjs from 0.24.0 to 0.25.0 a454088
340 | - Merge pull request [#186](https://github.com/ilovepixelart/ts-cache-mongoose/issues/186) from ilovepixelart/dependabot/npm_and_yarn/mongoose-8.2.4 919f735
341 | - Merge pull request [#187](https://github.com/ilovepixelart/ts-cache-mongoose/issues/187) from ilovepixelart/dependabot/npm_and_yarn/swc/cli-0.3.12 6da6a7d
342 | - Merge pull request [#188](https://github.com/ilovepixelart/ts-cache-mongoose/issues/188) from ilovepixelart/dependabot/npm_and_yarn/swc/helpers-0.5.8 481b6d4
343 | - Merge pull request [#190](https://github.com/ilovepixelart/ts-cache-mongoose/issues/190) from ilovepixelart/dependabot/npm_and_yarn/types/node-20.12.2 f84e1b6
344 | - Bump @types/node from 20.11.24 to 20.12.2 7792d7f
345 | - Bump @swc/helpers from 0.5.7 to 0.5.8 14d4306
346 | - Bump @swc/cli from 0.3.10 to 0.3.12 fadec48
347 | - Bump mongoose from 8.2.3 to 8.2.4 d2c9f49
348 | - fix: linting 414b4a5
349 | - Merge pull request [#185](https://github.com/ilovepixelart/ts-cache-mongoose/issues/185) from ilovepixelart/feature/dep 1a1a1f3
350 | - Dep 9282287
351 | - Merge pull request [#184](https://github.com/ilovepixelart/ts-cache-mongoose/issues/184) from ilovepixelart/snyk-upgrade-0cc3c149099921abc857d6f2207ebc4d ec6382e
352 | - fix: upgrade mongoose from 8.2.0 to 8.2.1 3ed60f8
353 | - fix: boolean check 49cf22e
354 | - feat: add debug logging 59bd5df
355 | - Update changelog for v1.4.7 2ebcef7
356 |
357 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.7...v1.5.0
358 |
359 | [Changes][v1.5.0]
360 |
361 |
362 |
363 | # [v1.4.7](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.4.7) - 2024-03-14
364 |
365 | - Merge pull request [#181](https://github.com/ilovepixelart/ts-cache-mongoose/issues/181) from ilovepixelart/feature/dep 9b6afae
366 | - tsconfig 196a371
367 | - Dep edfe4e3
368 | - Merge pull request [#180](https://github.com/ilovepixelart/ts-cache-mongoose/issues/180) from ilovepixelart/feature/lint 531edee
369 | - Lint eb152d4
370 | - Merge pull request [#179](https://github.com/ilovepixelart/ts-cache-mongoose/issues/179) from ilovepixelart/dependabot/npm_and_yarn/swc/cli-0.3.10 4ad38ae
371 | - Merge pull request [#177](https://github.com/ilovepixelart/ts-cache-mongoose/issues/177) from ilovepixelart/dependabot/npm_and_yarn/typescript-eslint/parser-7.1.0 6cde986
372 | - Bump @typescript-eslint/parser from 7.0.2 to 7.1.0 0eb587c
373 | - Merge pull request [#176](https://github.com/ilovepixelart/ts-cache-mongoose/issues/176) from ilovepixelart/dependabot/npm_and_yarn/types/node-20.11.24 639fba0
374 | - Merge pull request [#178](https://github.com/ilovepixelart/ts-cache-mongoose/issues/178) from ilovepixelart/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-7.1.0 090792f
375 | - Bump @swc/cli from 0.3.9 to 0.3.10 4ff420b
376 | - Bump @typescript-eslint/eslint-plugin from 7.0.2 to 7.1.0 dec1024
377 | - Bump @types/node from 20.11.20 to 20.11.24 80a8ad8
378 | - Merge pull request [#173](https://github.com/ilovepixelart/ts-cache-mongoose/issues/173) from ekamahuja/private-ecma-# e846475
379 | - Merge remote-tracking branch &[#39](https://github.com/ilovepixelart/ts-cache-mongoose/issues/39);upstream/main&[#39](https://github.com/ilovepixelart/ts-cache-mongoose/issues/39); into private-ecma-# bc82eb9
380 | - Skip sonar for forked PRs 9d7989f
381 | - Merge pull request [#175](https://github.com/ilovepixelart/ts-cache-mongoose/issues/175) from ekamahuja/installation-bun-readme 18ba561
382 | - Added &[#39](https://github.com/ilovepixelart/ts-cache-mongoose/issues/39);bun&[#39](https://github.com/ilovepixelart/ts-cache-mongoose/issues/39); commands to installation instructions in README.md 1331853
383 | - Refactor: Replace private keyword with ECMAScript private fields e26d905
384 | - Update changelog for v1.4.6 a9f621c
385 |
386 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.6...v1.4.7
387 |
388 | [Changes][v1.4.7]
389 |
390 |
391 |
392 | # [v1.4.6](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.4.6) - 2024-02-24
393 |
394 | - Lock a389447
395 | - Merge pull request [#169](https://github.com/ilovepixelart/ts-cache-mongoose/issues/169) from ilovepixelart/dependabot/npm_and_yarn/mongoose-8.2.0 752171b
396 | - Merge pull request [#170](https://github.com/ilovepixelart/ts-cache-mongoose/issues/170) from ilovepixelart/dependabot/npm_and_yarn/types/node-20.11.20 a1d3f34
397 | - Merge pull request [#171](https://github.com/ilovepixelart/ts-cache-mongoose/issues/171) from ilovepixelart/dependabot/npm_and_yarn/eslint-8.57.0 4a101a8
398 | - Bump eslint from 8.56.0 to 8.57.0 ae10a2c
399 | - Bump @types/node from 20.8.10 to 20.11.20 d63732c
400 | - Bump mongoose from 8.0.0 to 8.2.0 b82efd0
401 | - Dependabot ignore 0181902
402 | - Merge pull request [#168](https://github.com/ilovepixelart/ts-cache-mongoose/issues/168) from ilovepixelart/feature/dep a3d1be1
403 | - Merge pull request [#167](https://github.com/ilovepixelart/ts-cache-mongoose/issues/167) from ekamahuja/memor-engine-map 8f50596
404 | - Remove eslint delete method memory engine comment cb72814
405 | - Enhancement: Upgrade cache to use Map for improved performance e3df1e9
406 | - Dep da387c0
407 | - Update changelog for v1.4.5 538865a
408 | - Merge pull request [#163](https://github.com/ilovepixelart/ts-cache-mongoose/issues/163) from ilovepixelart/feature/dep 9f37105
409 | - Dep 3916a8f
410 | - Merge pull request [#155](https://github.com/ilovepixelart/ts-cache-mongoose/issues/155) from ilovepixelart/feature/dep 13d99f3
411 | - Dep 03656ba
412 | - Update changelog for "v1.4.5" 13a3e8e
413 |
414 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.5...v1.4.6
415 |
416 | [Changes][v1.4.6]
417 |
418 |
419 |
420 | # [v1.4.5](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.4.5) - 2024-01-20
421 |
422 | - Merge pull request [#154](https://github.com/ilovepixelart/ts-cache-mongoose/issues/154) from ilovepixelart/feature/dep 450ff32
423 | - Dep e49aa31
424 | - Update changelog for "v1.4.4" da6b190
425 |
426 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.4...v1.4.5
427 |
428 | [Changes][v1.4.5]
429 |
430 |
431 |
432 | # [v1.4.4](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.4.4) - 2024-01-09
433 |
434 | - Merge pull request [#152](https://github.com/ilovepixelart/ts-cache-mongoose/issues/152) from ilovepixelart/feature/esm-bash-and-dep 5ea3ecb
435 | - Audit e98d515
436 | - ESM bash and Dep d6851db
437 | - Update changelog for "v1.4.3" ca0e4d2
438 |
439 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.3...v1.4.4
440 |
441 | [Changes][v1.4.4]
442 |
443 |
444 |
445 | # [v1.4.3](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.4.3) - 2023-12-06
446 |
447 | - Doc ae2fee9
448 | - Merge pull request [#143](https://github.com/ilovepixelart/ts-cache-mongoose/issues/143) from ilovepixelart/feature/esm-support 9cdaa4a
449 | - Doc ee606bb
450 | - ESM support 1f2c00f
451 | - Settings 70858d6
452 | - System files ignore 41bcd1c
453 | - Delimiter style for interfaces 68003aa
454 | - Update changelog for "v1.4.2" 06cacc7
455 |
456 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.2...v1.4.3
457 |
458 | [Changes][v1.4.3]
459 |
460 |
461 |
462 | # [v1.4.2](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.4.2) - 2023-11-28
463 |
464 | - Merge pull request [#135](https://github.com/ilovepixelart/ts-cache-mongoose/issues/135) from ilovepixelart/feature/dep 9823556
465 | - Dep 73c0524
466 | - Update changelog for "v1.4.1" 67a8c78
467 |
468 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.1...v1.4.2
469 |
470 | [Changes][v1.4.2]
471 |
472 |
473 |
474 | # [v1.4.1](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.4.1) - 2023-11-15
475 |
476 | - Merge pull request [#134](https://github.com/ilovepixelart/ts-cache-mongoose/issues/134) from ilovepixelart/feature/custom-key-for-aggregate bd4cd95
477 | - Enable custom cache key for aggregate ff4151c
478 | - Doc b6300b6
479 | - Doc dad993d
480 | - Update changelog for "v1.4.0" 8fe292c
481 |
482 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.0...v1.4.1
483 |
484 | [Changes][v1.4.1]
485 |
486 |
487 |
488 | # [v1.4.0](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.4.0) - 2023-11-12
489 |
490 | - Merge pull request [#131](https://github.com/ilovepixelart/ts-cache-mongoose/issues/131) from ilovepixelart/feature/tsconfig-eslint df15116
491 | - Dep 9a8e813
492 | - Engines 66b73a8
493 | - Tsconfig strictest rules 64923f0
494 | - Update changelog for "v1.3.0" a215539
495 |
496 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.3.0...v1.4.0
497 |
498 | [Changes][v1.4.0]
499 |
500 |
501 |
502 | # [v1.3.0](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.3.0) - 2023-11-06
503 |
504 | - Merge pull request [#130](https://github.com/ilovepixelart/ts-cache-mongoose/issues/130) from ilovepixelart/feature/mongoose-8 682d868
505 | - Latest 7a5bcd8
506 | - Doc dd80f0b
507 | - Mongoose 8 07eb6e0
508 | - Merge pull request [#129](https://github.com/ilovepixelart/ts-cache-mongoose/issues/129) from ilovepixelart/feature/dep 5ffeae7
509 | - Fixed versions 04b67c6
510 | - Dep b030219
511 | - Merge pull request [#123](https://github.com/ilovepixelart/ts-cache-mongoose/issues/123) from ilovepixelart/dependabot/npm_and_yarn/babel/traverse-7.23.2 5201ef3
512 | - Bump @babel/traverse from 7.22.17 to 7.23.2 8361552
513 | - Update changelog for "v1.2.1" fd583ee
514 |
515 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.2.1...v1.3.0
516 |
517 | [Changes][v1.3.0]
518 |
519 |
520 |
521 | # [v1.2.1](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.2.1) - 2023-10-16
522 |
523 | - Merge pull request [#122](https://github.com/ilovepixelart/ts-cache-mongoose/issues/122) from ilovepixelart/feature/dep 43c9973
524 | - Dep fc808db
525 | - Merge pull request [#121](https://github.com/ilovepixelart/ts-cache-mongoose/issues/121) from ilovepixelart/feature/object-id-detection 4e6c111
526 | - Certain strings get erroneously deserialised to ObjectID b045283
527 | - Update changelog for "v1.2.0" b65a2fa
528 |
529 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.2.0...v1.2.1
530 |
531 | [Changes][v1.2.1]
532 |
533 |
534 |
535 | # [v1.2.0](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.2.0) - 2023-10-02
536 |
537 | - Merge pull request [#118](https://github.com/ilovepixelart/ts-cache-mongoose/issues/118) from ilovepixelart/feature/dep 4541bc3
538 | - Dep e805e09
539 | - Merge pull request [#110](https://github.com/ilovepixelart/ts-cache-mongoose/issues/110) from ilovepixelart/feature/dep b132dcb
540 | - Monthly 992e7b6
541 | - Dep 7b134aa
542 | - Update changelog for "v1.1.2" 9290d14
543 |
544 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.1.2...v1.2.0
545 |
546 | [Changes][v1.2.0]
547 |
548 |
549 |
550 | # [v1.1.2](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.1.2) - 2023-09-01
551 |
552 | - Merge pull request [#102](https://github.com/ilovepixelart/ts-cache-mongoose/issues/102) from ilovepixelart/feature/dep 916df05
553 | - NodeNext 66dbe4d
554 | - Dep df4e305
555 | - Merge pull request [#94](https://github.com/ilovepixelart/ts-cache-mongoose/issues/94) from pilot22/main 6a9671f
556 | - Update README.md 440d924
557 | - Merge pull request [#93](https://github.com/ilovepixelart/ts-cache-mongoose/issues/93) from ilovepixelart/feature/dep e17fafb
558 | - Dep 1c7a4b2
559 | - Merge pull request [#91](https://github.com/ilovepixelart/ts-cache-mongoose/issues/91) from ilovepixelart/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-6.4.0 2156674
560 | - Merge pull request [#88](https://github.com/ilovepixelart/ts-cache-mongoose/issues/88) from ilovepixelart/dependabot/npm_and_yarn/swc/core-1.3.78 85a356f
561 | - Bump @typescript-eslint/eslint-plugin from 6.3.0 to 6.4.0 e0511e1
562 | - Bump @swc/core from 1.3.76 to 1.3.78 46c70ab
563 | - Update changelog for "v1.1.1" 4cc4ddf
564 |
565 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.1.1...v1.1.2
566 |
567 | [Changes][v1.1.2]
568 |
569 |
570 |
571 | # [v1.1.1](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.1.1) - 2023-08-14
572 |
573 | - Merge pull request [#85](https://github.com/ilovepixelart/ts-cache-mongoose/issues/85) from ilovepixelart/feature/dep 59009f3
574 | - Dep - Update dependencies 8999ac0
575 | - Update changelog for "v1.1.0" 6fa4781
576 |
577 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.1.0...v1.1.1
578 |
579 | [Changes][v1.1.1]
580 |
581 |
582 |
583 | # [v1.1.0](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.1.0) - 2023-08-05
584 |
585 | - Merge pull request [#79](https://github.com/ilovepixelart/ts-cache-mongoose/issues/79) from ilovepixelart/feature/dep 189e3f4
586 | - Dep + new version of swc - Remove env use target in .swcrc 47d040e
587 | - Merge pull request [#76](https://github.com/ilovepixelart/ts-cache-mongoose/issues/76) from ilovepixelart/dependabot/npm_and_yarn/eslint-8.46.0 d6d11d1
588 | - Merge pull request [#77](https://github.com/ilovepixelart/ts-cache-mongoose/issues/77) from ilovepixelart/dependabot/npm_and_yarn/jest-29.6.2 45f271e
589 | - Bump jest from 29.6.1 to 29.6.2 ba66284
590 | - Bump eslint from 8.45.0 to 8.46.0 ae317b3
591 | - Update changelog for "v1.0.9" 73394a4
592 |
593 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.9...v1.1.0
594 |
595 | [Changes][v1.1.0]
596 |
597 |
598 |
599 | # [v1.0.9](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.0.9) - 2023-07-24
600 |
601 | - Merge pull request [#72](https://github.com/ilovepixelart/ts-cache-mongoose/issues/72) from ilovepixelart/feature/dep c39c013
602 | - Dep 4350ab8
603 | - Merge pull request [#69](https://github.com/ilovepixelart/ts-cache-mongoose/issues/69) from ilovepixelart/feature/lint de1f96d
604 | - Eslint strict 67dda77
605 | - Update changelog for "v1.0.8" 56b6c8a
606 |
607 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.8...v1.0.9
608 |
609 | [Changes][v1.0.9]
610 |
611 |
612 |
613 | # [v1.0.8](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.0.8) - 2023-07-20
614 |
615 | - Merge pull request [#68](https://github.com/ilovepixelart/ts-cache-mongoose/issues/68) from ilovepixelart/feature/dep f4fec16
616 | - Dep a852dcf
617 | - Merge pull request [#67](https://github.com/ilovepixelart/ts-cache-mongoose/issues/67) from ilovepixelart/feature/custom-key-empty-string 27204ec
618 | - Check != null 9fd1486
619 | - Type of string 5a4fb3e
620 | - Test description d790d68
621 | - Custom key empty string 79880ed
622 | - Update changelog for "v1.0.7" 8d4ce3e
623 |
624 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.7...v1.0.8
625 |
626 | [Changes][v1.0.8]
627 |
628 |
629 |
630 | # [v1.0.7](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.0.7) - 2023-07-16
631 |
632 | - Merge pull request [#63](https://github.com/ilovepixelart/ts-cache-mongoose/issues/63) from ilovepixelart/feature/dep daf6776
633 | - Ts node aa08a17
634 | - Dep 5fb1efb
635 | - Merge pull request [#58](https://github.com/ilovepixelart/ts-cache-mongoose/issues/58) from ilovepixelart/snyk-upgrade-8a29cddcb70f40bb2243efa11c83881a b784814
636 | - fix: upgrade mongoose from 6.11.1 to 6.11.2 fe5468a
637 | - Update changelog for "v1.0.6" 1c92b67
638 |
639 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.6...v1.0.7
640 |
641 | [Changes][v1.0.7]
642 |
643 |
644 |
645 | # [v1.0.6](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.0.6) - 2023-07-04
646 |
647 | - Merge pull request [#57](https://github.com/ilovepixelart/ts-cache-mongoose/issues/57) from ilovepixelart/feature/dep 06e9cb2
648 | - Dep 3d3876f
649 | - Merge pull request [#55](https://github.com/ilovepixelart/ts-cache-mongoose/issues/55) from ilovepixelart/dependabot/npm_and_yarn/eslint-8.44.0 edb1de4
650 | - Bump eslint from 8.43.0 to 8.44.0 183a8bc
651 | - Merge pull request [#52](https://github.com/ilovepixelart/ts-cache-mongoose/issues/52) from ilovepixelart/dependabot/npm_and_yarn/types/node-20.3.3 a885149
652 | - Merge pull request [#54](https://github.com/ilovepixelart/ts-cache-mongoose/issues/54) from ilovepixelart/dependabot/npm_and_yarn/eslint-plugin-n-16.0.1 0df2358
653 | - Merge pull request [#56](https://github.com/ilovepixelart/ts-cache-mongoose/issues/56) from ilovepixelart/dependabot/npm_and_yarn/swc/core-1.3.67 062be47
654 | - Bump @swc/core from 1.3.66 to 1.3.67 6a01da3
655 | - Bump eslint-plugin-n from 16.0.0 to 16.0.1 51d0445
656 | - Bump @types/node from 20.3.1 to 20.3.3 d48f584
657 | - Merge pull request [#51](https://github.com/ilovepixelart/ts-cache-mongoose/issues/51) from ilovepixelart/snyk-upgrade-28ddc4ad3cc15b5ccaf3efbf4c8efe08 12f1c00
658 | - fix: upgrade mongoose from 6.10.5 to 6.11.1 e94b5cc
659 | - Merge pull request [#50](https://github.com/ilovepixelart/ts-cache-mongoose/issues/50) from ilovepixelart/dependabot/npm_and_yarn/fast-xml-parser-and-aws-sdk/credential-providers-4.2.5 f94d51d
660 | - Merge pull request [#46](https://github.com/ilovepixelart/ts-cache-mongoose/issues/46) from ilovepixelart/dependabot/npm_and_yarn/swc/core-1.3.66 d25909e
661 | - Merge pull request [#44](https://github.com/ilovepixelart/ts-cache-mongoose/issues/44) from ilovepixelart/dependabot/npm_and_yarn/eslint-8.43.0 730dc1b
662 | - Merge pull request [#49](https://github.com/ilovepixelart/ts-cache-mongoose/issues/49) from ilovepixelart/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-5.60.1 9918498
663 | - Bump fast-xml-parser and @aws-sdk/credential-providers 565a529
664 | - Bump @typescript-eslint/eslint-plugin from 5.59.11 to 5.60.1 f360866
665 | - Bump eslint from 8.42.0 to 8.43.0 e83cedb
666 | - Merge pull request [#48](https://github.com/ilovepixelart/ts-cache-mongoose/issues/48) from ilovepixelart/dependabot/npm_and_yarn/eslint-plugin-jest-27.2.2 4a11f94
667 | - Bump eslint-plugin-jest from 27.2.1 to 27.2.2 53e086d
668 | - Bump @swc/core from 1.3.62 to 1.3.66 dedaba1
669 | - Merge pull request [#41](https://github.com/ilovepixelart/ts-cache-mongoose/issues/41) from ilovepixelart/dependabot/npm_and_yarn/typescript-eslint/parser-5.59.11 1530df4
670 | - Merge pull request [#40](https://github.com/ilovepixelart/ts-cache-mongoose/issues/40) from ilovepixelart/dependabot/npm_and_yarn/types/node-20.3.1 0314799
671 | - Bump @typescript-eslint/parser from 5.59.9 to 5.59.11 a7aa637
672 | - Bump @types/node from 20.2.5 to 20.3.1 f70db9f
673 | - Merge pull request [#43](https://github.com/ilovepixelart/ts-cache-mongoose/issues/43) from ilovepixelart/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-5.59.11 dcab8c5
674 | - Bump @typescript-eslint/eslint-plugin from 5.59.9 to 5.59.11 e3b41f6
675 | - Update changelog for "v1.0.5" 427b286
676 |
677 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.5...v1.0.6
678 |
679 | [Changes][v1.0.6]
680 |
681 |
682 |
683 | # [v1.0.5](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.0.5) - 2023-06-09
684 |
685 | - Merge pull request [#36](https://github.com/ilovepixelart/ts-cache-mongoose/issues/36) from ilovepixelart/feature/dep 723a84b
686 | - Sonar cloud latest version, settings for vscode fdffdf1
687 | - Dep 4d68f53
688 | - Merge pull request [#35](https://github.com/ilovepixelart/ts-cache-mongoose/issues/35) from ilovepixelart/feature/dep 5cc9e34
689 | - Dep eca2a0a
690 | - Merge pull request [#34](https://github.com/ilovepixelart/ts-cache-mongoose/issues/34) from ilovepixelart/dependabot/npm_and_yarn/typescript-5.1.3 1947bb2
691 | - Bump typescript from 5.0.4 to 5.1.3 2a337d6
692 | - Merge pull request [#33](https://github.com/ilovepixelart/ts-cache-mongoose/issues/33) from ilovepixelart/dependabot/npm_and_yarn/swc/core-1.3.62 990a7a5
693 | - Merge pull request [#31](https://github.com/ilovepixelart/ts-cache-mongoose/issues/31) from ilovepixelart/dependabot/npm_and_yarn/types/jest-29.5.2 25ced59
694 | - Merge pull request [#30](https://github.com/ilovepixelart/ts-cache-mongoose/issues/30) from ilovepixelart/dependabot/npm_and_yarn/eslint-8.42.0 f7c9eda
695 | - Bump @swc/core from 1.3.61 to 1.3.62 be64cb2
696 | - Bump @types/jest from 29.5.1 to 29.5.2 5e4001a
697 | - Bump eslint from 8.41.0 to 8.42.0 4900965
698 | - Merge pull request [#29](https://github.com/ilovepixelart/ts-cache-mongoose/issues/29) from ilovepixelart/feature/dep 124df0e
699 | - Dep 9b1b0b9
700 | - Merge pull request [#24](https://github.com/ilovepixelart/ts-cache-mongoose/issues/24) from ilovepixelart/dependabot/npm_and_yarn/types/node-20.2.5 bb9388c
701 | - Merge pull request [#23](https://github.com/ilovepixelart/ts-cache-mongoose/issues/23) from ilovepixelart/dependabot/npm_and_yarn/swc/core-1.3.60 008f11c
702 | - Merge pull request [#22](https://github.com/ilovepixelart/ts-cache-mongoose/issues/22) from ilovepixelart/dependabot/npm_and_yarn/typescript-eslint/parser-5.59.7 6d86a7d
703 | - Bump @types/node from 20.2.3 to 20.2.5 000a98b
704 | - Bump @swc/core from 1.3.59 to 1.3.60 f3d3509
705 | - Bump @typescript-eslint/parser from 5.59.6 to 5.59.7 f62a530
706 | - Merge pull request [#21](https://github.com/ilovepixelart/ts-cache-mongoose/issues/21) from ilovepixelart/dependabot/npm_and_yarn/eslint-8.41.0 bdffee1
707 | - Merge pull request [#20](https://github.com/ilovepixelart/ts-cache-mongoose/issues/20) from ilovepixelart/dependabot/npm_and_yarn/swc/core-1.3.59 02e51e1
708 | - Merge pull request [#18](https://github.com/ilovepixelart/ts-cache-mongoose/issues/18) from ilovepixelart/dependabot/npm_and_yarn/types/node-20.2.3 b3da66e
709 | - Bump eslint from 8.40.0 to 8.41.0 01e08b5
710 | - Bump @swc/core from 1.3.58 to 1.3.59 0bc14d7
711 | - Bump @types/node from 20.1.7 to 20.2.3 dacefb5
712 | - Update changelog for "v1.0.4" 9fadca3
713 |
714 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.4...v1.0.5
715 |
716 | [Changes][v1.0.5]
717 |
718 |
719 |
720 | # [v1.0.4](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.0.4) - 2023-05-17
721 |
722 | - Merge pull request [#17](https://github.com/ilovepixelart/ts-cache-mongoose/issues/17) from ilovepixelart/feature/dep c13843f
723 | - Dep 370436d
724 | - Merge pull request [#15](https://github.com/ilovepixelart/ts-cache-mongoose/issues/15) from ilovepixelart/dependabot/npm_and_yarn/swc/core-1.3.58 c405297
725 | - Merge pull request [#16](https://github.com/ilovepixelart/ts-cache-mongoose/issues/16) from ilovepixelart/dependabot/npm_and_yarn/types/node-20.1.4 a56a146
726 | - Bump @types/node from 20.1.3 to 20.1.4 051256c
727 | - Bump @swc/core from 1.3.57 to 1.3.58 96ba39c
728 | - Merge pull request [#13](https://github.com/ilovepixelart/ts-cache-mongoose/issues/13) from ilovepixelart/feature/dep 97f21a6
729 | - Dep 186fb1b
730 | - Merge pull request [#12](https://github.com/ilovepixelart/ts-cache-mongoose/issues/12) from ilovepixelart/dependabot/npm_and_yarn/types/node-20.1.0 e819e8f
731 | - Bump @types/node from 18.16.3 to 20.1.0 ce3c4d9
732 | - Merge pull request [#11](https://github.com/ilovepixelart/ts-cache-mongoose/issues/11) from ilovepixelart/feature/dep 24363c5
733 | - Dep eb57f13
734 | - Update changelog for "v1.0.3" 343521a
735 |
736 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.3...v1.0.4
737 |
738 | [Changes][v1.0.4]
739 |
740 |
741 |
742 | # [v1.0.3](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.0.3) - 2023-05-03
743 |
744 | ```
745 | - Type for simplicity
746 | - Lean with hydration of dates and ids
747 | - Distinct hydration of dates and ids
748 | - Apply args for callbacks
749 | - Some tests to cover above
750 | ```
751 |
752 | - Doc 6b5d1c7
753 | - Merge pull request [#10](https://github.com/ilovepixelart/ts-cache-mongoose/issues/10) from ilovepixelart/feature/apply 6184e24
754 | - Lint 17d03e2
755 | - Fixes - Type for simplicity - Lean with hydration of dates and ids - Distinct hydration of dates and ids - Apply args for callbacks - Some tests to cover above c363e2b
756 | - Update changelog for "v1.0.2" e04dc70
757 |
758 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.2...v1.0.3
759 |
760 | [Changes][v1.0.3]
761 |
762 |
763 |
764 | # [v1.0.2](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.0.2) - 2023-05-02
765 |
766 | - Release 36e67ed
767 | - Merge pull request [#9](https://github.com/ilovepixelart/ts-cache-mongoose/issues/9) from ilovepixelart/feature/test 4b850da
768 | - Fix rehydration scenario for lean objects 25e2caf
769 | - Test 820d958
770 | - Merge pull request [#8](https://github.com/ilovepixelart/ts-cache-mongoose/issues/8) from ilovepixelart/feature/dep b0fcbef
771 | - Dep 66e8265
772 | - Doc 49a26ad
773 | - Update README.md 61c4494
774 | - Update changelog for "v1.0.1" cc5c31e
775 |
776 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.1...v1.0.2
777 |
778 | [Changes][v1.0.2]
779 |
780 |
781 |
782 | # [v1.0.1](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.0.1) - 2023-04-30
783 |
784 | - Dep e67c1e3
785 | - Dep cc8ebd2
786 | - Merge pull request [#5](https://github.com/ilovepixelart/ts-cache-mongoose/issues/5) from ilovepixelart/feature/remove-dependency 39cba89
787 | - Dep 9a3e0bb
788 | - Remove dependency 811df42
789 | - Cache npm 0298794
790 | - Key e9b19a5
791 | - Update changelog for "v1.0.0" 777e8c1
792 |
793 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.0...v1.0.1
794 |
795 | [Changes][v1.0.1]
796 |
797 |
798 |
799 | # [v1.0.0](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v1.0.0) - 2023-04-29
800 |
801 | - Doc dcaa81f
802 | - Cleanup bd95d5e
803 | - Description 2f18d42
804 | - Description 4115926
805 | - Doc 684cbbd
806 | - Merge pull request [#4](https://github.com/ilovepixelart/ts-cache-mongoose/issues/4) from ilovepixelart/feature/test 33a5d0c
807 | - Test 962f8b1
808 | - Doc 0b7286a
809 | - Merge pull request [#3](https://github.com/ilovepixelart/ts-cache-mongoose/issues/3) from ilovepixelart/feature/dep 165321e
810 | - Dep bbd94bf
811 | - Update changelog for "v0.0.6" c184433
812 |
813 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v0.0.6...v1.0.0
814 |
815 | [Changes][v1.0.0]
816 |
817 |
818 |
819 | # [v0.0.6](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v0.0.6) - 2023-04-28
820 |
821 | - Key b27f663
822 | - Hydration cases aec2d0b
823 | - Update changelog for "v0.0.5" 92756a9
824 |
825 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v0.0.5...v0.0.6
826 |
827 | [Changes][v0.0.6]
828 |
829 |
830 |
831 | # [v0.0.5](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v0.0.5) - 2023-04-27
832 |
833 | - Doc 11f0282
834 | - Doc 6dc537d
835 |
836 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v0.0.4...v0.0.5
837 |
838 | [Changes][v0.0.5]
839 |
840 |
841 |
842 | # [v0.0.4](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v0.0.4) - 2023-04-27
843 |
844 | - Merge pull request [#2](https://github.com/ilovepixelart/ts-cache-mongoose/issues/2) from ilovepixelart/feature/aggregate 40ed36b
845 | - Test 5af6586
846 | - Refactor hashing bc291de
847 | - Cleanup 6f48f36
848 | - Test 2df4b5e
849 | - Aggregate 7d87eb7
850 |
851 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v0.0.3...v0.0.4
852 |
853 | [Changes][v0.0.4]
854 |
855 |
856 |
857 | # [v0.0.3](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v0.0.3) - 2023-04-27
858 |
859 | - Doc 796d8ae
860 |
861 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v0.0.2...v0.0.3
862 |
863 | [Changes][v0.0.3]
864 |
865 |
866 |
867 | # [v0.0.2](https://github.com/ilovepixelart/ts-cache-mongoose/releases/tag/v0.0.2) - 2023-04-27
868 |
869 | - Doc 3a1a96e
870 | - Features 546f96d
871 | - Merge pull request [#1](https://github.com/ilovepixelart/ts-cache-mongoose/issues/1) from ilovepixelart/feature/cache-engine c1e2adc
872 | - Redis localhost 73e000f
873 | - Redis pipeline 6897e9d
874 | - Redis 001b158
875 | - More tests 90f01f4
876 | - Redis test 39fb0f5
877 | - Cache bug fix ttl, more tests 8761c96
878 | - Doc c745179
879 | - Cache engine - Close underlying connection - Regis cache options support - Delete old in memory cache - Test for redis be18959
880 | - Name 3c9b736
881 | - Dep dfca630
882 | - Dep 63ee84f
883 |
884 | https://github.com/ilovepixelart/ts-cache-mongoose/compare/v0.0.1...v0.0.2
885 |
886 | [Changes][v0.0.2]
887 |
888 |
889 | [v1.7.3]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.7.2...v1.7.3
890 | [v1.7.2]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.7.1...v1.7.2
891 | [v1.7.1]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.7.0...v1.7.1
892 | [v1.7.0]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.8...v1.7.0
893 | [v1.6.8]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.7...v1.6.8
894 | [v1.6.7]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.6...v1.6.7
895 | [v1.6.6]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.5...v1.6.6
896 | [v1.6.5]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.4...v1.6.5
897 | [v1.6.4]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.3...v1.6.4
898 | [v1.6.3]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.2...v1.6.3
899 | [v1.6.2]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.1...v1.6.2
900 | [v1.6.1]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.6.0...v1.6.1
901 | [v1.6.0]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.5.0...v1.6.0
902 | [v1.5.0]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.7...v1.5.0
903 | [v1.4.7]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.6...v1.4.7
904 | [v1.4.6]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.5...v1.4.6
905 | [v1.4.5]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.4...v1.4.5
906 | [v1.4.4]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.3...v1.4.4
907 | [v1.4.3]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.2...v1.4.3
908 | [v1.4.2]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.1...v1.4.2
909 | [v1.4.1]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.4.0...v1.4.1
910 | [v1.4.0]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.3.0...v1.4.0
911 | [v1.3.0]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.2.1...v1.3.0
912 | [v1.2.1]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.2.0...v1.2.1
913 | [v1.2.0]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.1.2...v1.2.0
914 | [v1.1.2]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.1.1...v1.1.2
915 | [v1.1.1]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.1.0...v1.1.1
916 | [v1.1.0]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.9...v1.1.0
917 | [v1.0.9]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.8...v1.0.9
918 | [v1.0.8]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.7...v1.0.8
919 | [v1.0.7]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.6...v1.0.7
920 | [v1.0.6]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.5...v1.0.6
921 | [v1.0.5]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.4...v1.0.5
922 | [v1.0.4]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.3...v1.0.4
923 | [v1.0.3]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.2...v1.0.3
924 | [v1.0.2]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.1...v1.0.2
925 | [v1.0.1]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v1.0.0...v1.0.1
926 | [v1.0.0]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v0.0.6...v1.0.0
927 | [v0.0.6]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v0.0.5...v0.0.6
928 | [v0.0.5]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v0.0.4...v0.0.5
929 | [v0.0.4]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v0.0.3...v0.0.4
930 | [v0.0.3]: https://github.com/ilovepixelart/ts-cache-mongoose/compare/v0.0.2...v0.0.3
931 | [v0.0.2]: https://github.com/ilovepixelart/ts-cache-mongoose/tree/v0.0.2
932 |
933 |
934 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
4 |
5 | ## Our Standards
6 |
7 | Examples of behavior that contributes to creating a positive environment include:
8 |
9 | * Using welcoming and inclusive language
10 | * Being respectful of differing viewpoints and experiences
11 | * Gracefully accepting constructive criticism
12 | * Focusing on what is best for the community
13 | * Showing empathy towards other community members
14 |
15 | Examples of unacceptable behavior by participants include:
16 |
17 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
18 | * Trolling, insulting/derogatory comments, and personal or political attacks
19 | * Public or private harassment
20 | * Publishing others’ private information, such as a physical or electronic address, without explicit permission
21 | * Other conduct which could reasonably be considered inappropriate in a professional setting
22 |
23 | ## Our Responsibilities
24 |
25 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
26 |
27 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
28 |
29 | ## Scope
30 |
31 | This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
32 |
33 | ## Enforcement
34 |
35 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership.
36 |
37 | ## Attribution
38 |
39 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at
40 |
41 | For answers to common questions about this code of conduct, see
42 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute
2 |
3 | 1. Start an issue. We will discuss the best approach
4 | 2. Make a pull request. I'll review it and comment until we are both confident about it
5 | 3. I'll merge your PR and bump the version of the package
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2025 ilovepixelart
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ts-cache-mongoose
2 |
3 | Cache query and aggregate in mongoose using in-memory or redis
4 |
5 | [](https://www.npmjs.com/package/ts-cache-mongoose)
6 | [](https://www.npmjs.com/package/ts-cache-mongoose)
7 | [](https://github.com/ilovepixelart/ts-cache-mongoose/blob/main/LICENSE)
8 | \
9 | [](https://sonarcloud.io/summary/new_code?id=ilovepixelart_ts-cache-mongoose)
10 | [](https://sonarcloud.io/summary/new_code?id=ilovepixelart_ts-cache-mongoose)
11 | \
12 | [](https://sonarcloud.io/summary/new_code?id=ilovepixelart_ts-cache-mongoose)
13 | [](https://sonarcloud.io/summary/new_code?id=ilovepixelart_ts-cache-mongoose)
14 | [](https://sonarcloud.io/summary/new_code?id=ilovepixelart_ts-cache-mongoose)
15 |
16 | ## Motivation
17 |
18 | ts-cache-mongoose is a plugin for mongoose
19 | \
20 | Caching queries is a good way to improve performance of your application
21 |
22 | ## Supports and tested with
23 |
24 | ```json
25 | {
26 | "node": "18.x || 20.x || 22.x",
27 | "mongoose": ">=6.6.x || 7.x || 8.x",
28 | }
29 | ```
30 |
31 | ## Features
32 |
33 | - In-memory caching
34 | - Redis caching
35 | - Cache expiration
36 | - Cache invalidation
37 | - Cache key generation
38 | - Cache key prefix
39 | - Query caching
40 | - Aggregate caching
41 | - Supports ESM and CommonJS
42 |
43 | ## Installation
44 |
45 | - Locally inside your project
46 |
47 | ```bash
48 | npm install ts-cache-mongoose
49 | pnpm add ts-cache-mongoose
50 | yarn add ts-cache-mongoose
51 | bun add ts-cache-mongoose
52 | ```
53 |
54 | - This plugin requires mongoose `>=6.6.x || 7.x || 8.x` to be installed as a peer dependency
55 |
56 | ```bash
57 | # For latest mongoose 6
58 | npm install mongoose@6
59 | pnpm add mongoose@6
60 | yarn add mongoose@6
61 | bun add mongoose@6
62 | # For latest mongoose 7
63 | npm install mongoose@7
64 | pnpm add mongoose@7
65 | yarn add mongoose@7
66 | bun add mongoose@7
67 | # For latest mongoose 8
68 | npm install mongoose@8
69 | pnpm add mongoose@8
70 | yarn add mongoose@8
71 | bun add mongoose@8
72 | ```
73 |
74 | ## Example
75 |
76 | ```typescript
77 | // On your application startup
78 | import mongoose from 'mongoose'
79 | import cache from 'ts-cache-mongoose'
80 |
81 | // In-memory example
82 | const instance = cache.init(mongoose, {
83 | defaultTTL: '60 seconds',
84 | engine: 'memory',
85 | debug: true, // Debug mode enables hit/miss logs in console, not for production use
86 | })
87 |
88 | // OR
89 |
90 | // Redis example
91 | const instance = cache.init(mongoose, {
92 | defaultTTL: '60 seconds',
93 | engine: 'redis',
94 | engineOptions: {
95 | host: 'localhost',
96 | port: 6379,
97 | },
98 | debug: true, // Debug mode enables hit/miss logs in console, not for production use
99 | })
100 |
101 | // Connect to your database
102 | mongoose.connect('mongodb://localhost:27017/my-database')
103 |
104 | // Somewhere in your code
105 | const users = await User.find({ role: 'user' }).cache('10 seconds').exec()
106 | // Cache hit
107 | const users = await User.find({ role: 'user' }).cache('10 seconds').exec()
108 |
109 | const book = await Book.findById(id).cache('1 hour').exec()
110 | const bookCount = await Book.countDocuments().cache('1 minute').exec()
111 | const authors = await Book.distinct('author').cache('30 seconds').exec()
112 |
113 | const books = await Book.aggregate([
114 | {
115 | $match: {
116 | genre: 'fantasy',
117 | },
118 | },
119 | {
120 | $group: {
121 | _id: '$author',
122 | count: { $sum: 1 },
123 | },
124 | },
125 | {
126 | $project: {
127 | _id: 0,
128 | author: '$_id',
129 | count: 1,
130 | },
131 | }
132 | ]).cache('1 minute').exec()
133 |
134 | // Cache invalidation
135 |
136 | // To clear all cache, don't use in production unless you know what you are doing
137 | await instance.clear()
138 |
139 | // Instead use custom cache key
140 | const user = await User.findById('61bb4d6a1786e5123d7f4cf1').cache('1 minute', 'some-custom-key').exec()
141 | await instance.clear('some-custom-key')
142 | ```
143 |
144 | ## Check my other projects
145 |
146 | - [ts-migrate-mongoose](https://github.com/ilovepixelart/ts-migrate-mongoose) - Migration framework for mongoose
147 | - [ts-patch-mongoose](https://github.com/ilovepixelart/ts-patch-mongoose) - Patch history & events plugin for mongoose
148 |
--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false },
4 | "files": {
5 | "ignoreUnknown": false,
6 | "ignore": ["node_modules", "dist", "coverage"]
7 | },
8 | "formatter": { "enabled": true, "indentStyle": "space", "indentWidth": 2 },
9 | "organizeImports": { "enabled": true },
10 | "linter": {
11 | "enabled": true,
12 | "rules": {
13 | "recommended": true
14 | }
15 | },
16 | "javascript": {
17 | "formatter": {
18 | "trailingCommas": "all",
19 | "quoteStyle": "single",
20 | "semicolons": "asNeeded",
21 | "lineWidth": 320
22 | },
23 | "globals": ["Atomics", "SharedArrayBuffer"]
24 | },
25 | "json": {
26 | "formatter": {
27 | "trailingCommas": "none"
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ts-cache-mongoose",
3 | "version": "1.7.3",
4 | "description": "Cache plugin for mongoose Queries and Aggregate (in-memory, redis)",
5 | "author": "ilovepixelart",
6 | "license": "MIT",
7 | "repository": {
8 | "type": "git",
9 | "url": "git://github.com/ilovepixelart/ts-cache-mongoose.git"
10 | },
11 | "bugs": {
12 | "url": "https://github.com/ilovepixelart/ts-cache-mongoose/issues"
13 | },
14 | "homepage": "https://github.com/ilovepixelart/ts-cache-mongoose#readme",
15 | "directories": {
16 | "examples": "examples"
17 | },
18 | "keywords": [
19 | "backend",
20 | "mongo",
21 | "mongodb",
22 | "mongoose",
23 | "plugin",
24 | "schema",
25 | "db",
26 | "nosql",
27 | "ts",
28 | "typescript",
29 | "cache",
30 | "redis",
31 | "store",
32 | "memory",
33 | "ttl",
34 | "query",
35 | "aggregate"
36 | ],
37 | "engines": {
38 | "node": ">=16"
39 | },
40 | "files": [
41 | "dist",
42 | "src",
43 | "tests",
44 | "tsconfig.json",
45 | "vite.config.mts",
46 | "biome.json"
47 | ],
48 | "type": "module",
49 | "exports": {
50 | "require": {
51 | "types": "./dist/index.d.cts",
52 | "default": "./dist/index.cjs"
53 | },
54 | "import": {
55 | "types": "./dist/index.d.mts",
56 | "default": "./dist/index.mjs"
57 | }
58 | },
59 | "main": "./dist/index.cjs",
60 | "module": "./dist/index.mjs",
61 | "types": "./dist/index.d.cts",
62 | "scripts": {
63 | "prepare": "simple-git-hooks",
64 | "biome": "npx @biomejs/biome check",
65 | "biome:fix": "npx @biomejs/biome check --write .",
66 | "test": "vitest run --coverage",
67 | "test:open": "vitest run --coverage && open-cli coverage/lcov-report/index.html",
68 | "clean": "rm -rf ./dist",
69 | "type:check": "tsc --noEmit",
70 | "build": "pkgroll --clean-dist",
71 | "release": "npm install && npm run biome && npm run type:check && npm run build && np"
72 | },
73 | "dependencies": {
74 | "@types/ms": "2.1.0",
75 | "@types/semver": "7.7.0",
76 | "ioredis": "5.6.1",
77 | "ms": "2.1.3",
78 | "semver": "7.7.2",
79 | "sort-keys": "4.2.0"
80 | },
81 | "devDependencies": {
82 | "@biomejs/biome": "1.9.4",
83 | "@types/node": "22.15.30",
84 | "@vitest/coverage-v8": "3.2.2",
85 | "bson": "^6.10.3",
86 | "mongodb-memory-server": "10.1.4",
87 | "mongoose": "8.15.1",
88 | "open-cli": "8.0.0",
89 | "pkgroll": "2.12.2",
90 | "simple-git-hooks": "2.13.0",
91 | "typescript": "5.8.3",
92 | "vitest": "3.2.2"
93 | },
94 | "peerDependencies": {
95 | "bson": ">=4.7.2 < 7",
96 | "mongoose": ">=6.6.0 < 9"
97 | },
98 | "simple-git-hooks": {
99 | "pre-commit": "npm run type:check",
100 | "pre-push": "npm run biome:fix"
101 | },
102 | "overrides": {
103 | "esbuild": "0.25.0"
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/cache/Cache.ts:
--------------------------------------------------------------------------------
1 | import ms from 'ms'
2 |
3 | import { MemoryCacheEngine } from './engine/MemoryCacheEngine'
4 | import { RedisCacheEngine } from './engine/RedisCacheEngine'
5 |
6 | import type { CacheData, CacheEngine, CacheOptions, CacheTTL } from '../types'
7 |
8 | export class Cache {
9 | readonly #engine!: CacheEngine
10 | readonly #defaultTTL: number
11 | readonly #debug: boolean
12 | readonly #engines = ['memory', 'redis'] as const
13 |
14 | constructor(cacheOptions: CacheOptions) {
15 | if (!this.#engines.includes(cacheOptions.engine)) {
16 | throw new Error(`Invalid engine name: ${cacheOptions.engine}`)
17 | }
18 |
19 | if (cacheOptions.engine === 'redis' && !cacheOptions.engineOptions) {
20 | throw new Error(`Engine options are required for ${cacheOptions.engine} engine`)
21 | }
22 |
23 | if (!cacheOptions.defaultTTL) {
24 | cacheOptions.defaultTTL = '1 minute'
25 | }
26 |
27 | this.#defaultTTL = typeof cacheOptions.defaultTTL === 'string' ? ms(cacheOptions.defaultTTL) : cacheOptions.defaultTTL
28 |
29 | if (cacheOptions.engine === 'redis' && cacheOptions.engineOptions) {
30 | this.#engine = new RedisCacheEngine(cacheOptions.engineOptions)
31 | }
32 |
33 | if (cacheOptions.engine === 'memory') {
34 | this.#engine = new MemoryCacheEngine()
35 | }
36 |
37 | this.#debug = cacheOptions.debug === true
38 | }
39 |
40 | async get(key: string): Promise {
41 | const cacheEntry = await this.#engine.get(key)
42 | if (this.#debug) {
43 | const cacheHit = cacheEntry != null ? 'HIT' : 'MISS'
44 | console.log(`[ts-cache-mongoose] GET '${key}' - ${cacheHit}`)
45 | }
46 | return cacheEntry
47 | }
48 |
49 | async set(key: string, value: CacheData, ttl: CacheTTL | null): Promise {
50 | const givenTTL = typeof ttl === 'string' ? ms(ttl) : ttl
51 | const actualTTL = givenTTL ?? this.#defaultTTL
52 | await this.#engine.set(key, value, actualTTL)
53 | if (this.#debug) {
54 | console.log(`[ts-cache-mongoose] SET '${key}' - ttl: ${actualTTL.toFixed(0)} ms`)
55 | }
56 | }
57 |
58 | async del(key: string): Promise {
59 | await this.#engine.del(key)
60 | if (this.#debug) {
61 | console.log(`[ts-cache-mongoose] DEL '${key}'`)
62 | }
63 | }
64 |
65 | async clear(): Promise {
66 | await this.#engine.clear()
67 | if (this.#debug) {
68 | console.log('[ts-cache-mongoose] CLEAR')
69 | }
70 | }
71 |
72 | async close(): Promise {
73 | return this.#engine.close()
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/cache/engine/MemoryCacheEngine.ts:
--------------------------------------------------------------------------------
1 | import ms from 'ms'
2 |
3 | import type { CacheData, CacheEngine, CacheTTL } from '../../types'
4 |
5 | export class MemoryCacheEngine implements CacheEngine {
6 | readonly #cache: Map
7 |
8 | constructor() {
9 | this.#cache = new Map()
10 | }
11 |
12 | get(key: string): CacheData {
13 | const item = this.#cache.get(key)
14 | if (!item || item.expiresAt < Date.now()) {
15 | this.del(key)
16 | return undefined
17 | }
18 | return item.value
19 | }
20 |
21 | set(key: string, value: CacheData, ttl?: CacheTTL): void {
22 | const givenTTL = typeof ttl === 'string' ? ms(ttl) : ttl
23 | const actualTTL = givenTTL ?? Number.POSITIVE_INFINITY
24 | this.#cache.set(key, {
25 | value,
26 | expiresAt: Date.now() + actualTTL,
27 | })
28 | }
29 |
30 | del(key: string): void {
31 | this.#cache.delete(key)
32 | }
33 |
34 | clear(): void {
35 | this.#cache.clear()
36 | }
37 |
38 | close(): void {
39 | // do nothing
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/cache/engine/RedisCacheEngine.ts:
--------------------------------------------------------------------------------
1 | import { EJSON } from 'bson'
2 | import IORedis from 'ioredis'
3 | import ms from 'ms'
4 |
5 | import { convertToObject } from '../../version'
6 |
7 | import type { Redis, RedisOptions } from 'ioredis'
8 | import type { CacheData, CacheEngine, CacheTTL } from '../../types'
9 |
10 | export class RedisCacheEngine implements CacheEngine {
11 | readonly #client: Redis
12 |
13 | constructor(options: RedisOptions) {
14 | if (!options.keyPrefix) {
15 | options.keyPrefix = 'cache-mongoose:'
16 | }
17 | this.#client = new IORedis(options)
18 | }
19 |
20 | async get(key: string): Promise {
21 | try {
22 | const value = await this.#client.get(key)
23 | if (value === null) {
24 | return undefined
25 | }
26 | return EJSON.parse(value) as CacheData
27 | } catch (err) {
28 | console.error(err)
29 | return undefined
30 | }
31 | }
32 |
33 | async set(key: string, value: CacheData, ttl?: CacheTTL): Promise {
34 | try {
35 | const givenTTL = typeof ttl === 'string' ? ms(ttl) : ttl
36 | const actualTTL = givenTTL ?? Number.POSITIVE_INFINITY
37 | const serializedValue = EJSON.stringify(convertToObject(value))
38 | await this.#client.setex(key, Math.ceil(actualTTL / 1000), serializedValue)
39 | } catch (err) {
40 | console.error(err)
41 | }
42 | }
43 |
44 | async del(key: string): Promise {
45 | await this.#client.del(key)
46 | }
47 |
48 | async clear(): Promise {
49 | await this.#client.flushdb()
50 | }
51 |
52 | async close(): Promise {
53 | await this.#client.quit()
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/extend/aggregate.ts:
--------------------------------------------------------------------------------
1 | import { getKey } from '../key'
2 |
3 | import type { Mongoose } from 'mongoose'
4 | import type { Cache } from '../cache/Cache'
5 | import type { CacheTTL } from '../types'
6 |
7 | export function extendAggregate(mongoose: Mongoose, cache: Cache): void {
8 | const mongooseExec = mongoose.Aggregate.prototype.exec
9 |
10 | mongoose.Aggregate.prototype.getCacheKey = function (): string {
11 | if (this._key != null) return this._key
12 |
13 | return getKey({
14 | pipeline: this.pipeline(),
15 | })
16 | }
17 |
18 | mongoose.Aggregate.prototype.getCacheTTL = function (): CacheTTL | null {
19 | return this._ttl
20 | }
21 |
22 | mongoose.Aggregate.prototype.cache = function (ttl?: CacheTTL, customKey?: string) {
23 | this._ttl = ttl ?? null
24 | this._key = customKey ?? null
25 | return this
26 | }
27 |
28 | mongoose.Aggregate.prototype.exec = async function (...args: []) {
29 | if (!Object.prototype.hasOwnProperty.call(this, '_ttl')) {
30 | return mongooseExec.apply(this, args)
31 | }
32 |
33 | const key = this.getCacheKey()
34 | const ttl = this.getCacheTTL()
35 |
36 | const resultCache = await cache.get(key).catch((err: unknown) => {
37 | console.error(err)
38 | })
39 |
40 | if (resultCache) {
41 | return resultCache
42 | }
43 |
44 | const result = (await mongooseExec.call(this)) as Record[] | Record
45 | await cache.set(key, result, ttl).catch((err: unknown) => {
46 | console.error(err)
47 | })
48 |
49 | return result
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/extend/query.ts:
--------------------------------------------------------------------------------
1 | import { getKey } from '../key'
2 |
3 | import type { Mongoose } from 'mongoose'
4 | import type { Cache } from '../cache/Cache'
5 | import type { CacheTTL } from '../types'
6 |
7 | export function extendQuery(mongoose: Mongoose, cache: Cache): void {
8 | const mongooseExec = mongoose.Query.prototype.exec
9 |
10 | mongoose.Query.prototype.getCacheKey = function (): string {
11 | if (this._key != null) return this._key
12 |
13 | const filter = this.getFilter()
14 | const update = this.getUpdate()
15 | const options = this.getOptions()
16 | const mongooseOptions = this.mongooseOptions()
17 |
18 | return getKey({
19 | model: this.model.modelName,
20 | op: this.op,
21 | filter,
22 | update,
23 | options,
24 | mongooseOptions,
25 | _path: this._path,
26 | _fields: this._fields,
27 | _distinct: this._distinct,
28 | _conditions: this._conditions,
29 | })
30 | }
31 |
32 | mongoose.Query.prototype.getCacheTTL = function (): CacheTTL | null {
33 | return this._ttl
34 | }
35 |
36 | mongoose.Query.prototype.cache = function (ttl?: CacheTTL, customKey?: string) {
37 | this._ttl = ttl ?? null
38 | this._key = customKey ?? null
39 | return this
40 | }
41 |
42 | mongoose.Query.prototype.exec = async function (...args: []) {
43 | if (!Object.prototype.hasOwnProperty.call(this, '_ttl')) {
44 | return mongooseExec.apply(this, args)
45 | }
46 |
47 | const key = this.getCacheKey()
48 | const ttl = this.getCacheTTL()
49 | const mongooseOptions = this.mongooseOptions()
50 |
51 | const isCount = this.op?.includes('count') ?? false
52 | const isDistinct = this.op === 'distinct'
53 | const model = this.model.modelName
54 |
55 | const resultCache = await cache.get(key).catch((err: unknown) => {
56 | console.error(err)
57 | })
58 |
59 | if (resultCache) {
60 | if (isCount || isDistinct || mongooseOptions.lean) {
61 | return resultCache
62 | }
63 |
64 | const modelConstructor = mongoose.model(model)
65 |
66 | if (Array.isArray(resultCache)) {
67 | return resultCache.map((item) => {
68 | return modelConstructor.hydrate(item)
69 | })
70 | }
71 | return modelConstructor.hydrate(resultCache)
72 | }
73 |
74 | const result = (await mongooseExec.call(this)) as Record[] | Record
75 | await cache.set(key, result, ttl).catch((err: unknown) => {
76 | console.error(err)
77 | })
78 |
79 | return result
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Cache } from './cache/Cache'
2 | import { extendAggregate } from './extend/aggregate'
3 | import { extendQuery } from './extend/query'
4 |
5 | import type { Mongoose } from 'mongoose'
6 | import type { CacheOptions, CacheTTL } from './types'
7 |
8 | export * from './types'
9 |
10 | declare module 'mongoose' {
11 | interface Query {
12 | cache: (this: Query, ttl?: CacheTTL, customKey?: string) => this
13 | _key: string | null
14 | getCacheKey: (this: Query) => string
15 | _ttl: CacheTTL | null
16 | getCacheTTL: (this: Query) => CacheTTL | null
17 | op?: string
18 | _path?: unknown
19 | _fields?: unknown
20 | _distinct?: unknown
21 | _conditions?: unknown
22 | }
23 |
24 | interface Aggregate {
25 | cache: (this: Aggregate, ttl?: CacheTTL, customKey?: string) => this
26 | _key: string | null
27 | getCacheKey: (this: Aggregate) => string
28 | _ttl: CacheTTL | null
29 | getCacheTTL: (this: Aggregate) => CacheTTL | null
30 | }
31 | }
32 |
33 | class CacheMongoose {
34 | static #instance: CacheMongoose | undefined
35 | private cache!: Cache
36 |
37 | private constructor() {
38 | // Private constructor to prevent external instantiation
39 | }
40 |
41 | public static init(mongoose: Mongoose, cacheOptions: CacheOptions): CacheMongoose {
42 | if (!CacheMongoose.#instance) {
43 | CacheMongoose.#instance = new CacheMongoose()
44 | CacheMongoose.#instance.cache = new Cache(cacheOptions)
45 |
46 | const cache = CacheMongoose.#instance.cache
47 |
48 | extendQuery(mongoose, cache)
49 | extendAggregate(mongoose, cache)
50 | }
51 |
52 | return CacheMongoose.#instance
53 | }
54 |
55 | public async clear(customKey?: string): Promise {
56 | if (customKey != null) {
57 | await this.cache.del(customKey)
58 | } else {
59 | await this.cache.clear()
60 | }
61 | }
62 |
63 | public async close(): Promise {
64 | await this.cache.close()
65 | }
66 | }
67 |
68 | export default CacheMongoose
69 |
--------------------------------------------------------------------------------
/src/key.ts:
--------------------------------------------------------------------------------
1 | import { createHash } from 'node:crypto'
2 | import sortKeys from 'sort-keys'
3 |
4 | export function getKey(data: Record[] | Record): string {
5 | const sortedObj = sortKeys(data, { deep: true })
6 | const sortedStr = JSON.stringify(sortedObj, (_, val: unknown) => {
7 | return val instanceof RegExp ? String(val) : val
8 | })
9 | return createHash('sha1').update(sortedStr).digest('hex')
10 | }
11 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { RedisOptions } from 'ioredis'
2 | import type { StringValue } from 'ms'
3 |
4 | export type CacheTTL = number | StringValue
5 |
6 | export type CacheData = Record | Record[] | unknown[] | number | undefined
7 |
8 | export type CacheOptions = {
9 | engine: 'memory' | 'redis'
10 | engineOptions?: RedisOptions
11 | defaultTTL?: CacheTTL
12 | debug?: boolean
13 | }
14 |
15 | export interface CacheEngine {
16 | get: (key: string) => Promise | CacheData
17 | set: (key: string, value: CacheData, ttl?: CacheTTL) => Promise | void
18 | del: (key: string) => Promise | void
19 | clear: () => Promise | void
20 | close: () => Promise | void
21 | }
22 |
--------------------------------------------------------------------------------
/src/version.ts:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 | import { satisfies } from 'semver'
3 |
4 | import type { CacheData } from './types'
5 |
6 | export const isMongooseLessThan7 = satisfies(mongoose.version, '<7')
7 |
8 | export const convertToObject = (value: (T & { toObject?: () => CacheData }) | undefined): CacheData => {
9 | if (isMongooseLessThan7) {
10 | if (value != null && typeof value === 'object' && !Array.isArray(value) && value.toObject) {
11 | return value.toObject()
12 | }
13 | if (Array.isArray(value)) {
14 | return value.map((doc) => convertToObject(doc))
15 | }
16 | }
17 |
18 | return value
19 | }
20 |
--------------------------------------------------------------------------------
/tests/cache-debug.test.ts:
--------------------------------------------------------------------------------
1 | import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
2 |
3 | import mongoose from 'mongoose'
4 | import CacheMongoose from '../src/index'
5 | import { UserModel } from './models/User'
6 | import { server } from './mongo/server'
7 |
8 | describe('cache-debug', async () => {
9 | const instance = server('cache-debug')
10 | let cache: CacheMongoose
11 |
12 | beforeAll(async () => {
13 | cache = CacheMongoose.init(mongoose, {
14 | engine: 'memory',
15 | debug: true,
16 | })
17 |
18 | await instance.create()
19 | })
20 |
21 | afterAll(async () => {
22 | await cache.clear()
23 | await cache.close()
24 | await instance.destroy()
25 | })
26 |
27 | beforeEach(async () => {
28 | vi.spyOn(global.console, 'log')
29 | await mongoose.connection.collection('users').deleteMany({})
30 | })
31 |
32 | afterEach(async () => {
33 | vi.restoreAllMocks()
34 | })
35 |
36 | describe('debug scenarios', () => {
37 | it('should create a use and and query it two time first is cache miss second is hit, also clear by key and global', async () => {
38 | const user = await UserModel.create({
39 | name: 'John Doe',
40 | role: 'admin',
41 | })
42 |
43 | const key = 'key'
44 | const ttl = '1 second'
45 | const cacheMissRegExp = /\[ts-cache-mongoose\] GET '.*?' - MISS/
46 | const cacheHitRegExp = /\[ts-cache-mongoose\] GET '.*?' - HIT/
47 | const cacheSetRegExp = /\[ts-cache-mongoose\] SET '.*?' - ttl: \d+ ms/
48 | const cacheClearRegExp = /\[ts-cache-mongoose\] CLEAR/
49 | const cacheDelRegExp = /\[ts-cache-mongoose\] DEL '.*?'/
50 |
51 | const userCacheMiss = await UserModel.findById(user._id).cache(ttl, key).exec()
52 | expect(console.log).toHaveBeenCalledWith(expect.stringMatching(cacheMissRegExp))
53 | expect(userCacheMiss).not.toBeNull()
54 | expect(userCacheMiss?._id.toString()).toBe(user._id.toString())
55 | expect(userCacheMiss?.name).toEqual(user.name)
56 | expect(userCacheMiss?.role).toEqual(user.role)
57 |
58 | const userCacheHit = await UserModel.findById(user._id).cache(ttl, key).exec()
59 | expect(console.log).toHaveBeenCalledWith(expect.stringMatching(cacheSetRegExp))
60 | expect(console.log).toHaveBeenCalledWith(expect.stringMatching(cacheHitRegExp))
61 | expect(userCacheHit).not.toBeNull()
62 | expect(userCacheHit?._id.toString()).toBe(user._id.toString())
63 | expect(userCacheHit?.name).toEqual(user.name)
64 | expect(userCacheHit?.role).toEqual(user.role)
65 |
66 | await cache.clear(key)
67 | expect(console.log).toHaveBeenCalledWith(expect.stringMatching(cacheDelRegExp))
68 |
69 | await cache.clear()
70 | expect(console.log).toHaveBeenCalledWith(expect.stringMatching(cacheClearRegExp))
71 | })
72 | })
73 | })
74 |
--------------------------------------------------------------------------------
/tests/cache-memory.test.ts:
--------------------------------------------------------------------------------
1 | import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest'
2 |
3 | import mongoose from 'mongoose'
4 | import CacheMongoose from '../src/index'
5 | import { UserModel } from './models/User'
6 | import { server } from './mongo/server'
7 |
8 | describe('cache-memory', async () => {
9 | const instance = server('cache-memory')
10 | let cache: CacheMongoose
11 |
12 | beforeAll(async () => {
13 | cache = CacheMongoose.init(mongoose, {
14 | engine: 'memory',
15 | })
16 |
17 | await instance.create()
18 | })
19 |
20 | afterAll(async () => {
21 | await cache.clear()
22 | await cache.close()
23 | await instance.destroy()
24 | })
25 |
26 | beforeEach(async () => {
27 | await mongoose.connection.collection('users').deleteMany({})
28 | })
29 |
30 | describe('memory scenarios', () => {
31 | it('should use memory cache', async () => {
32 | const user = await UserModel.create({
33 | name: 'John Doe',
34 | role: 'admin',
35 | })
36 |
37 | const user1 = await UserModel.findById(user._id).cache().exec()
38 | await UserModel.findOneAndUpdate({ _id: user._id }, { name: 'John Doe 2' }).exec()
39 | const user2 = await UserModel.findById(user._id).cache().exec()
40 |
41 | expect(user1).not.toBeNull()
42 | expect(user2).not.toBeNull()
43 | expect(user1?._id.toString()).toBe(user2?._id.toString())
44 | expect(user1?.name).toEqual(user2?.name)
45 | })
46 |
47 | it('should not use cache', async () => {
48 | const user = await UserModel.create({
49 | name: 'John Doe',
50 | role: 'admin',
51 | })
52 |
53 | const cache1 = await UserModel.findById(user._id).cache().exec()
54 | await UserModel.findByIdAndUpdate(user._id, { name: 'John Doe 2' }).exec()
55 | await cache.clear()
56 | const cache2 = await UserModel.findById(user._id).cache().exec()
57 |
58 | expect(cache1).not.toBeNull()
59 | expect(cache2).not.toBeNull()
60 | expect(cache1?._id.toString()).toBe(cache2?._id.toString())
61 | expect(cache1?.name).not.toEqual(cache2?.name)
62 | })
63 |
64 | it('should use memory cache with custom key', async () => {
65 | const user = await UserModel.create({
66 | name: 'John Doe',
67 | role: 'admin',
68 | })
69 |
70 | const cache1 = await UserModel.findById(user._id).cache('1 minute', 'test-custom-key').exec()
71 | await UserModel.findOneAndUpdate({ _id: user._id }, { name: 'John Doe 2' }).exec()
72 | const cache2 = await UserModel.findById(user._id).cache('1 minute', 'test-custom-key').exec()
73 |
74 | expect(cache1).not.toBeNull()
75 | expect(cache2).not.toBeNull()
76 | expect(cache1?._id.toString()).toBe(cache2?._id.toString())
77 | expect(cache1?.name).toEqual(cache2?.name)
78 | })
79 |
80 | it('should use memory cache and clear custom key', async () => {
81 | const user = await UserModel.create({
82 | name: 'John Doe',
83 | role: 'admin',
84 | })
85 |
86 | const cache1 = await UserModel.findById(user._id).cache('1 minute', 'test-custom-key-second').exec()
87 | await UserModel.updateOne({ _id: user._id }, { name: 'John Doe 2' }).exec()
88 | await cache.clear('test-custom-key-second')
89 | const cache2 = await UserModel.findById(user._id).cache('1 minute', 'test-custom-key-second').exec()
90 |
91 | expect(cache1).not.toBeNull()
92 | expect(cache2).not.toBeNull()
93 | expect(cache1?._id.toString()).toBe(cache2?._id.toString())
94 | expect(cache1?.name).not.toEqual(cache2?.name)
95 | })
96 |
97 | it('should use memory cache and custom key with an empty string', async () => {
98 | const user = await UserModel.create({
99 | name: 'John Doe',
100 | role: 'admin',
101 | })
102 |
103 | const cache1 = await UserModel.findById(user._id).cache('1 minute', '').exec()
104 | await UserModel.updateOne({ _id: user._id }, { name: 'John Doe 2' }).exec()
105 | const cache2 = await UserModel.findById(user._id).cache('1 minute', '').exec()
106 |
107 | expect(cache1).not.toBeNull()
108 | expect(cache2).not.toBeNull()
109 | expect(cache1?._id.toString()).toBe(cache2?._id.toString())
110 | expect(cache1?.name).toEqual(cache2?.name)
111 |
112 | await cache.clear('')
113 | const cache3 = await UserModel.findById(user._id).cache('1 minute', '').exec()
114 | expect(cache3).not.toBeNull()
115 | expect(cache2?._id.toString()).toBe(cache3?._id.toString())
116 | expect(cache2?.name).not.toEqual(cache3?.name)
117 | })
118 |
119 | it('should use memory cache and aggregate', async () => {
120 | await UserModel.create([
121 | { name: 'John', role: 'admin' },
122 | { name: 'Bob', role: 'admin' },
123 | { name: 'Alice', role: 'user' },
124 | ])
125 |
126 | const cache1 = await UserModel.aggregate([{ $match: { role: 'admin' } }, { $group: { _id: '$role', count: { $sum: 1 } } }])
127 | .cache()
128 | .exec()
129 |
130 | await UserModel.create({ name: 'Mark', role: 'admin' })
131 |
132 | const cache2 = await UserModel.aggregate([{ $match: { role: 'admin' } }, { $group: { _id: '$role', count: { $sum: 1 } } }])
133 | .cache()
134 | .exec()
135 |
136 | expect(cache1).not.toBeNull()
137 | expect(cache2).not.toBeNull()
138 | expect(cache1?.[0].count).toEqual(cache2?.[0].count)
139 | })
140 | })
141 |
142 | describe('memory scenarios with ttl', () => {
143 | const users = [
144 | { name: 'John', age: 30, role: 'admin' },
145 | { name: 'Alice', age: 25, role: 'user' },
146 | { name: 'Bob', age: 35, role: 'user' },
147 | ]
148 |
149 | beforeEach(async () => {
150 | // Delete all users before each test
151 | await UserModel.deleteMany().exec()
152 |
153 | // Create new users
154 | await UserModel.create(users)
155 | })
156 |
157 | it('findById', async () => {
158 | const john = await UserModel.create({ name: 'John', age: 30, role: 'admin' })
159 | const user = await UserModel.findById(john._id).cache('1 minute').exec()
160 | const cachedUser = await UserModel.findById(john._id).cache('1 minute').exec()
161 |
162 | expect(user?._id.toString()).toBe(cachedUser?._id.toString())
163 | expect(user?.name).toEqual(cachedUser?.name)
164 | expect(user?.createdAt).toEqual(cachedUser?.createdAt)
165 | expect(user?.updatedAt).toEqual(cachedUser?.updatedAt)
166 | })
167 |
168 | it('findOne', async () => {
169 | const user = await UserModel.findOne({ name: 'John', age: 30, role: 'admin' }).cache('1 minute').exec()
170 | await UserModel.create({ name: 'Steve', age: 30, role: 'admin' })
171 | const cachedUser = await UserModel.findOne({
172 | name: 'John',
173 | age: 30,
174 | role: 'admin',
175 | })
176 | .cache('1 minute')
177 | .exec()
178 |
179 | expect(user?._id.toString()).toBe(cachedUser?._id.toString())
180 | expect(user?.name).toEqual(cachedUser?.name)
181 | expect(user?.createdAt).toEqual(cachedUser?.createdAt)
182 | expect(user?.updatedAt).toEqual(cachedUser?.updatedAt)
183 | })
184 |
185 | it('find', async () => {
186 | const users = await UserModel.find({ age: { $gte: 30 } })
187 | .cache('1 minute')
188 | .exec()
189 | await UserModel.create({ name: 'Steve', age: 30, role: 'admin' })
190 | const cachedUsers = await UserModel.find({ age: { $gte: 30 } })
191 | .cache('1 minute')
192 | .exec()
193 |
194 | expect(users).toHaveLength(cachedUsers.length)
195 | })
196 |
197 | it('count', async () => {
198 | const count = await UserModel.countDocuments({ age: { $gte: 30 } })
199 | .cache('1 minute')
200 | .exec()
201 | await UserModel.create({ name: 'Steve', age: 30, role: 'admin' })
202 | const cachedCount = await UserModel.countDocuments({ age: { $gte: 30 } })
203 | .cache('1 minute')
204 | .exec()
205 |
206 | expect(count).toEqual(cachedCount)
207 | })
208 |
209 | it('distinct', async () => {
210 | const emails = await UserModel.distinct('name').cache('1 minute').exec()
211 | await UserModel.create({ name: 'Steve', age: 30, role: 'admin' })
212 | const cachedEmails = await UserModel.distinct('name').cache('1 minute').exec()
213 |
214 | expect(emails).toEqual(cachedEmails)
215 | })
216 | })
217 | })
218 |
--------------------------------------------------------------------------------
/tests/cache-options.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it, vi } from 'vitest'
2 | import { Cache } from '../src/cache/Cache'
3 |
4 | import type { CacheOptions } from '../src/types'
5 |
6 | describe('Cache class tests', () => {
7 | it('should create a new instance of Cache', () => {
8 | const cacheOptions: CacheOptions = {
9 | engine: 'memory',
10 | }
11 |
12 | const cache = new Cache(cacheOptions)
13 | expect(cache).toBeInstanceOf(Cache)
14 | expect(cache).toHaveProperty('get')
15 | expect(cache).toHaveProperty('set')
16 | expect(cache).toHaveProperty('del')
17 | expect(cache).toHaveProperty('clear')
18 | expect(cache).toHaveProperty('close')
19 | })
20 |
21 | it('should throw an error if the cache engine is not supported', () => {
22 | const cacheOptions: CacheOptions = {
23 | // @ts-expect-error Testing invalid engine name
24 | engine: 'not-supported',
25 | }
26 |
27 | expect(() => new Cache(cacheOptions)).toThrow(`Invalid engine name: ${cacheOptions.engine}`)
28 | })
29 |
30 | it('should throw an error if the cache engine is redis and no engine options are provided', () => {
31 | const cacheOptions: CacheOptions = {
32 | engine: 'redis',
33 | }
34 |
35 | expect(() => new Cache(cacheOptions)).toThrow(`Engine options are required for ${cacheOptions.engine} engine`)
36 | })
37 |
38 | it('should create a new instance of Cache with redis engine', async () => {
39 | const cacheOptions: CacheOptions = {
40 | engine: 'redis',
41 | engineOptions: {
42 | host: 'localhost',
43 | port: 6379,
44 | },
45 | defaultTTL: '10 minutes',
46 | }
47 |
48 | const cache = new Cache(cacheOptions)
49 | expect(cache).toBeInstanceOf(Cache)
50 | expect(cache).toHaveProperty('get')
51 | expect(cache).toHaveProperty('set')
52 | expect(cache).toHaveProperty('del')
53 | expect(cache).toHaveProperty('clear')
54 | expect(cache).toHaveProperty('close')
55 |
56 | await cache.set('bob', { test: 'bob' }, null)
57 | await cache.set('john', { test: 'john' }, '1 minute')
58 |
59 | const value = await cache.get('bob')
60 | expect(value).toEqual({ test: 'bob' })
61 |
62 | await cache.del('bob')
63 | const clearBob = await cache.get('bob')
64 | expect(clearBob).toBeUndefined()
65 |
66 | const john = await cache.get('john')
67 | expect(john).toEqual({ test: 'john' })
68 |
69 | await cache.clear()
70 | const clearJohn = await cache.get('john')
71 | expect(clearJohn).toBeUndefined()
72 |
73 | const mockSet = vi.fn()
74 | cache.set = mockSet
75 |
76 | await cache.set('bob', { test: 'bob' }, null)
77 | expect(mockSet).toHaveBeenCalled()
78 | expect(mockSet).toHaveBeenCalledWith('bob', { test: 'bob' }, null)
79 |
80 | await cache.close()
81 | })
82 | })
83 |
--------------------------------------------------------------------------------
/tests/cache-redis.test.ts:
--------------------------------------------------------------------------------
1 | import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest'
2 |
3 | import mongoose from 'mongoose'
4 | import plugin from '../src/index'
5 | import { server } from './mongo/server'
6 |
7 | import { ObjectId } from 'bson'
8 | import { StoryModel } from './models/Story'
9 | import { UserModel } from './models/User'
10 |
11 | import type CacheMongoose from '../src/index'
12 |
13 | describe('cache-redis', async () => {
14 | const instance = server('cache-redis')
15 | let cache: CacheMongoose
16 |
17 | beforeAll(async () => {
18 | cache = plugin.init(mongoose, {
19 | engine: 'redis',
20 | engineOptions: {
21 | host: 'localhost',
22 | port: 6379,
23 | },
24 | defaultTTL: '10 seconds',
25 | })
26 |
27 | await instance.create()
28 | })
29 |
30 | afterAll(async () => {
31 | await cache.clear()
32 | await cache.close()
33 | await instance.destroy()
34 | })
35 |
36 | beforeEach(async () => {
37 | await mongoose.connection.collection('users').deleteMany({})
38 | await mongoose.connection.collection('stories').deleteMany({})
39 | })
40 |
41 | it('should use cache', async () => {
42 | const user = await UserModel.create({
43 | name: 'John Doe',
44 | role: 'admin',
45 | })
46 |
47 | const user1 = await UserModel.findById(user._id).cache().exec()
48 | await UserModel.findOneAndUpdate({ _id: user._id }, { name: 'John Doe 2' }).exec()
49 | const user2 = await UserModel.findById(user._id).cache().exec()
50 |
51 | expect(user1).not.toBeNull()
52 | expect(user2).not.toBeNull()
53 | expect(user1?._id.toString()).toBe(user2?._id.toString())
54 | expect(user1?.name).toEqual(user2?.name)
55 | })
56 |
57 | it('should clear cache', async () => {
58 | const user = await UserModel.create({
59 | name: 'John Doe',
60 | role: 'admin',
61 | })
62 |
63 | const cache1 = await UserModel.findById(user._id).cache().exec()
64 | await UserModel.findByIdAndUpdate(user._id, { name: 'Steve' }).exec()
65 | await cache.clear()
66 | const cache2 = await UserModel.findById(user._id).cache().exec()
67 |
68 | expect(cache1).not.toBeNull()
69 | expect(cache2).not.toBeNull()
70 | expect(cache1?._id.toString()).toBe(cache2?._id.toString())
71 | expect(cache1?.name).not.toEqual(cache2?.name)
72 | })
73 |
74 | it('should use cache with custom-key-1', async () => {
75 | const user = await UserModel.create({
76 | name: 'John Doe',
77 | role: 'admin',
78 | })
79 |
80 | const cache1 = await UserModel.findById(user._id).cache('30 seconds', 'custom-key-1').exec()
81 | await UserModel.findOneAndUpdate({ _id: user._id }, { name: 'John Doe 2' }).exec()
82 | const cache2 = await UserModel.findById(user._id).cache('30 seconds', 'custom-key-1').exec()
83 |
84 | expect(cache1).not.toBeNull()
85 | expect(cache2).not.toBeNull()
86 | expect(cache1?._id.toString()).toBe(cache2?._id.toString())
87 | expect(cache1?.name).toEqual(cache2?.name)
88 | })
89 |
90 | it('should clear custom-key-2', async () => {
91 | const user = await UserModel.create({
92 | name: 'John Doe',
93 | role: 'admin',
94 | })
95 |
96 | const cache1 = await UserModel.findById(user._id).cache('30 seconds', 'custom-key-2').exec()
97 | await UserModel.updateOne({ _id: user._id }, { name: 'Steve' }).exec()
98 | await cache.clear('custom-key-2')
99 | const cache2 = await UserModel.findById(user._id).cache('30 seconds', 'custom-key-2').exec()
100 |
101 | expect(cache1).not.toBeNull()
102 | expect(cache2).not.toBeNull()
103 | expect(cache1?._id.toString()).toBe(cache2?._id.toString())
104 | expect(cache1?.name).not.toEqual(cache2?.name)
105 | })
106 |
107 | it('should use cache without cache options', async () => {
108 | expect(cache).toBeDefined()
109 |
110 | const user = await UserModel.create({
111 | name: 'John Doe',
112 | role: 'admin',
113 | })
114 |
115 | const cache1 = await UserModel.findById(user._id).cache().exec()
116 | await UserModel.updateOne({ _id: user._id }, { name: 'John Doe 2' }).exec()
117 | const cache2 = await UserModel.findById(user._id).cache().exec()
118 |
119 | expect(cache1).not.toBeNull()
120 | expect(cache2).not.toBeNull()
121 | expect(cache1?._id.toString()).toBe(cache2?._id.toString())
122 | expect(cache1?.name).toEqual(cache2?.name)
123 |
124 | await UserModel.create([
125 | {
126 | name: 'Alice',
127 | role: 'admin',
128 | },
129 | {
130 | name: 'Bob',
131 | role: 'admin',
132 | },
133 | ])
134 |
135 | const cache3 = await UserModel.find({ role: 'admin' }).cache().exec()
136 | await UserModel.updateMany({ role: 'admin' }, { name: 'Steve' }).exec()
137 | const cache4 = await UserModel.find({ role: 'admin' }).cache().exec()
138 |
139 | expect(cache3).not.toBeNull()
140 | expect(cache4).not.toBeNull()
141 | expect(cache3?.length).toEqual(cache4?.length)
142 | expect(cache3?.[0].name).toEqual(cache4?.[0].name)
143 | })
144 |
145 | it('should use cache on aggregate', async () => {
146 | await UserModel.create([
147 | { name: 'John', role: 'admin' },
148 | { name: 'Bob', role: 'admin' },
149 | { name: 'Alice', role: 'user' },
150 | ])
151 |
152 | const cache1 = await UserModel.aggregate([{ $match: { role: 'admin' } }, { $group: { _id: '$role', count: { $sum: 1 } } }])
153 | .cache('30 seconds')
154 | .exec()
155 |
156 | await UserModel.create({ name: 'Mark', role: 'admin' })
157 |
158 | const cache2 = await UserModel.aggregate([{ $match: { role: 'admin' } }, { $group: { _id: '$role', count: { $sum: 1 } } }])
159 | .cache('30 seconds')
160 | .exec()
161 |
162 | expect(cache1).not.toBeNull()
163 | expect(cache2).not.toBeNull()
164 | expect(cache1?.[0].count).toEqual(cache2?.[0].count)
165 | })
166 |
167 | it('should use cache on aggregate with custom-key', async () => {
168 | await UserModel.create([
169 | { name: 'John', role: 'admin' },
170 | { name: 'Bob', role: 'admin' },
171 | { name: 'Alice', role: 'user' },
172 | ])
173 |
174 | const cache1 = await UserModel.aggregate([{ $match: { role: 'admin' } }, { $group: { _id: '$role', count: { $sum: 1 } } }])
175 | .cache('30 seconds', 'aggregate-custom-key')
176 | .exec()
177 |
178 | await UserModel.create({ name: 'Mark', role: 'admin' })
179 |
180 | const cache2 = await UserModel.aggregate([{ $match: { role: 'admin' } }, { $group: { _id: '$role', count: { $sum: 1 } } }])
181 | .cache('30 seconds', 'aggregate-custom-key')
182 | .exec()
183 |
184 | // Don't use cache key
185 | const cache3 = await UserModel.aggregate([{ $match: { role: 'admin' } }, { $group: { _id: '$role', count: { $sum: 1 } } }]).exec()
186 |
187 | expect(cache1).not.toBeNull()
188 | expect(cache2).not.toBeNull()
189 | expect(cache3).not.toBeNull()
190 | expect(cache1?.[0].count).toEqual(cache2?.[0].count)
191 | expect(cache1?.[0].count).not.toEqual(cache3?.[0].count)
192 | })
193 |
194 | it('should test lean cache', async () => {
195 | await UserModel.create([
196 | { name: 'John', role: 'admin' },
197 | { name: 'Bob', role: 'admin' },
198 | { name: 'Alice', role: 'user' },
199 | ])
200 |
201 | const cache1 = await UserModel.find({ role: 'admin' }).lean().cache('30 seconds').exec()
202 | await UserModel.create({ name: 'Mark', role: 'admin' })
203 | const cache2 = await UserModel.find({ role: 'admin' }).lean().cache('30 seconds').exec()
204 |
205 | expect(cache1).not.toBeNull()
206 | expect(cache2).not.toBeNull()
207 | expect(cache1?.length).toEqual(cache2?.length)
208 | })
209 |
210 | it('should cache with regex query', async () => {
211 | await UserModel.create([
212 | { name: 'John', role: 'admin' },
213 | { name: 'Alice', role: 'user' },
214 | { name: 'Andy', role: 'user' },
215 | ])
216 |
217 | const cache1 = await UserModel.find({ name: /^J/ }).cache('30 seconds').exec()
218 | await UserModel.create({ name: 'Jenifer', role: 'admin' })
219 | const cache2 = await UserModel.find({ name: /^J/ }).cache('30 seconds').exec()
220 |
221 | expect(cache1).not.toBeNull()
222 | expect(cache2).not.toBeNull()
223 | expect(cache1?.length).toEqual(cache2?.length)
224 |
225 | const cache3 = await UserModel.find({ name: /^A/ }).cache('30 seconds').exec()
226 | await UserModel.create({ name: 'Alex', role: 'admin' })
227 | const cache4 = await UserModel.find({ name: /^A/ }).cache('30 seconds').exec()
228 |
229 | expect(cache3).not.toBeNull()
230 | expect(cache4).not.toBeNull()
231 | expect(cache3?.length).toEqual(cache4?.length)
232 | })
233 |
234 | it('should findOne', async () => {
235 | await UserModel.create([
236 | { name: 'C', role: 'admin' },
237 | { name: 'V', role: 'user' },
238 | { name: 'G', role: 'user' },
239 | ])
240 |
241 | const miss = await UserModel.findOne({ name: 'G' }).lean().cache('30 seconds').exec()
242 | expect(miss).not.toBeNull()
243 |
244 | expect(typeof miss?._id).toBe('object')
245 | expect(miss?._id instanceof mongoose.Types.ObjectId).toBeTruthy()
246 |
247 | expect(miss).toHaveProperty('name', 'G')
248 |
249 | const hit = await UserModel.findOne({ name: 'G' }).lean().cache('30 seconds').exec()
250 | expect(hit).not.toBeNull()
251 |
252 | expect(typeof hit?._id).toBe('object')
253 | expect(hit?._id instanceof ObjectId).toBeTruthy()
254 |
255 | expect(hit).toHaveProperty('name', 'G')
256 |
257 | expect(miss?._id.toString()).toBe(hit?._id.toString())
258 | expect(miss?.name).toEqual(hit?.name)
259 | expect(miss?.role).toEqual(hit?.role)
260 | expect(miss?.createdAt).toEqual(hit?.createdAt)
261 | expect(miss?.updatedAt).toEqual(hit?.updatedAt)
262 | })
263 |
264 | it('should distinct("_id") and distinct("role") and distinct("createdAt")', async () => {
265 | await UserModel.create({ name: 'i', role: 'admin' })
266 | await UserModel.create({ name: 'p', role: 'user' })
267 | await UserModel.create({ name: 'm', role: 'user' })
268 |
269 | const miss = await UserModel.distinct('_id').cache('30 seconds').exec()
270 | expect(miss).not.toBeNull()
271 | expect(miss?.length).toBe(3)
272 |
273 | expect(typeof miss?.[0]).toBe('object')
274 | expect(miss?.[0] instanceof mongoose.Types.ObjectId).toBeTruthy()
275 |
276 | const hit = await UserModel.distinct('_id').cache('30 seconds').exec()
277 | expect(hit).not.toBeNull()
278 | expect(hit?.length).toBe(3)
279 |
280 | expect(typeof hit?.[0]).toBe('object')
281 | expect(hit?.[0] instanceof ObjectId).toBeTruthy()
282 |
283 | const cache4 = await UserModel.distinct('role').cache('30 seconds').exec()
284 | expect(cache4).not.toBeNull()
285 | expect(cache4?.length).toBe(2)
286 | expect(cache4).toEqual(['admin', 'user'])
287 |
288 | const cache5 = await UserModel.distinct('role').cache('30 seconds').exec()
289 | expect(cache5).not.toBeNull()
290 | expect(cache5?.length).toBe(2)
291 | expect(cache5).toEqual(['admin', 'user'])
292 |
293 | const cache6 = await UserModel.distinct('name').cache('30 seconds').exec()
294 | expect(cache6).not.toBeNull()
295 | expect(cache6?.length).toBe(3)
296 |
297 | const cache7 = await UserModel.distinct('name').cache('30 seconds').exec()
298 |
299 | expect(miss.map((id) => id.toString())).toEqual(hit.map((id) => id.toString()))
300 | expect(cache4).toEqual(cache5)
301 | expect(cache6).toEqual(cache7)
302 | })
303 |
304 | it('should test exceptions', async () => {
305 | const user = await UserModel.create({ name: 'i', role: 'admin' })
306 | const story1 = await StoryModel.create({ title: '1', userId: user._id })
307 | const story2 = await StoryModel.create({ title: '2', userId: user._id })
308 |
309 | const miss = await UserModel.findOne({ name: 'i' }).populate({ path: 'stories' }).lean().cache('30 seconds').exec()
310 | const hit = await UserModel.findOne({ name: 'i' }).populate({ path: 'stories' }).lean().cache('30 seconds').exec()
311 |
312 | expect(miss).not.toBeNull()
313 |
314 | expect(typeof miss?._id).toBe('object')
315 | expect(miss?._id instanceof mongoose.Types.ObjectId).toBeTruthy()
316 |
317 | expect(miss?.name).toBe('i')
318 | expect(miss?.stories).not.toBeNull()
319 | expect(miss?.stories?.length).toBe(2)
320 |
321 | expect(miss?.stories?.[0]._id.toString()).toBe(story1._id.toString())
322 |
323 | expect(typeof miss?.stories?.[0]._id).toBe('object')
324 | expect(miss?.stories?.[0]._id instanceof mongoose.Types.ObjectId).toBeTruthy()
325 |
326 | expect(typeof miss?.stories?.[0].createdAt).toBe('object')
327 | expect(miss?.stories?.[0].createdAt instanceof Date).toBeTruthy()
328 |
329 | expect(miss?.stories?.[1]._id.toString()).toBe(story2._id.toString())
330 |
331 | expect(typeof miss?.stories?.[1]._id).toBe('object')
332 | expect(miss?.stories?.[1]._id instanceof mongoose.Types.ObjectId).toBeTruthy()
333 |
334 | expect(typeof miss?.stories?.[1].createdAt).toBe('object')
335 | expect(miss?.stories?.[1].createdAt instanceof Date).toBeTruthy()
336 |
337 | expect(hit).not.toBeNull()
338 |
339 | expect(typeof hit?._id).toBe('object')
340 | expect(hit?._id instanceof ObjectId).toBeTruthy()
341 |
342 | expect(hit?.name).toBe('i')
343 | expect(hit?.stories).not.toBeNull()
344 | expect(hit?.stories?.length).toBe(2)
345 |
346 | expect(hit?.stories?.[0]._id.toString()).toBe(story1._id.toString())
347 |
348 | expect(typeof hit?.stories?.[0]._id).toBe('object')
349 | expect(hit?.stories?.[0]._id instanceof ObjectId).toBeTruthy()
350 |
351 | expect(hit?.stories?.[0].createdAt instanceof Date).toBeTruthy()
352 | expect(typeof hit?.stories?.[0].createdAt).toBe('object')
353 |
354 | expect(hit?.stories?.[1]._id.toString()).toBe(story2._id.toString())
355 |
356 | expect(typeof hit?.stories?.[1]._id).toBe('object')
357 | expect(hit?.stories?.[1]._id instanceof ObjectId).toBeTruthy()
358 |
359 | expect(hit?.stories?.[1].createdAt instanceof Date).toBeTruthy()
360 | expect(typeof hit?.stories?.[1].createdAt).toBe('object')
361 |
362 | expect(miss?._id.toString()).toBe(hit?._id.toString())
363 | expect(miss?.name).toBe(hit?.name)
364 | expect(miss?.role).toBe(hit?.role)
365 | expect(miss?.createdAt?.toString()).toBe(hit?.createdAt?.toString())
366 | expect(miss?.updatedAt?.toString()).toBe(hit?.updatedAt?.toString())
367 |
368 | expect(miss?.stories?.[0]._id.toString()).toBe(hit?.stories?.[0]._id.toString())
369 | expect(miss?.stories?.[0].title).toBe(hit?.stories?.[0].title)
370 | expect(miss?.stories?.[0].userId.toString()).toBe(hit?.stories?.[0].userId.toString())
371 | expect(miss?.stories?.[0].createdAt?.toString()).toBe(hit?.stories?.[0].createdAt?.toString())
372 | expect(miss?.stories?.[0].updatedAt?.toString()).toBe(hit?.stories?.[0].updatedAt?.toString())
373 |
374 | expect(miss?.stories?.[1]._id.toString()).toBe(hit?.stories?.[1]._id.toString())
375 | expect(miss?.stories?.[1].title).toBe(hit?.stories?.[1].title)
376 | expect(miss?.stories?.[1].userId.toString()).toBe(hit?.stories?.[1].userId.toString())
377 | expect(miss?.stories?.[1].createdAt?.toString()).toBe(hit?.stories?.[1].createdAt?.toString())
378 | expect(miss?.stories?.[1].updatedAt?.toString()).toBe(hit?.stories?.[1].updatedAt?.toString())
379 | })
380 |
381 | it('should not misclassify certain fields as objectIds', async () => {
382 | // ObjectId.isValid will return true for multiple scenarios.
383 | // A string being a potentially valid objectId should not be the
384 | // determining factor on wether or not deserialize it as objectId.
385 | const user = await UserModel.create({
386 | name: '12CharString',
387 | role: '660ef695677786928202dc1f',
388 | })
389 | const pureLean = await UserModel.findOne({ _id: user._id }).lean()
390 |
391 | const miss = await UserModel.findOne({ _id: user._id }).lean().cache('30 seconds')
392 | const hit = await UserModel.findOne({ _id: user._id }).lean().cache('30 seconds')
393 |
394 | expect(pureLean).not.toBeNull()
395 | expect(typeof pureLean?._id).toBe('object')
396 | expect(typeof pureLean?.createdAt).toBe('object')
397 |
398 | expect(miss).not.toBeNull()
399 | expect(typeof miss?._id).toBe('object')
400 | expect(typeof miss?.createdAt).toBe('object')
401 |
402 | expect(hit).not.toBeNull()
403 | expect(typeof hit?._id).toBe('object')
404 | expect(typeof hit?.createdAt).toBe('object')
405 |
406 | expect(miss?._id.toString()).toBe(hit?._id.toString())
407 | expect(miss?.role).toEqual(hit?.role)
408 | expect(miss?.createdAt).toEqual(hit?.createdAt)
409 |
410 | const distinctMiss = await UserModel.distinct('_id').cache('30 seconds').lean().exec()
411 | expect(distinctMiss).not.toBeNull()
412 | expect(distinctMiss?.length).toBe(1)
413 | expect(distinctMiss).toEqual([pureLean?._id])
414 |
415 | const distinctHit = await UserModel.distinct('_id').cache('30 seconds').lean().exec()
416 | expect(distinctHit).not.toBeNull()
417 | expect(distinctHit?.length).toBe(1)
418 | expect(distinctHit.map((id) => id.toString())).toEqual([pureLean?._id.toString()])
419 |
420 | const distinctCreatedAtMiss = await UserModel.distinct('createdAt').cache('30 seconds').lean().exec()
421 | expect(distinctCreatedAtMiss).not.toBeNull()
422 | expect(distinctCreatedAtMiss?.length).toBe(1)
423 | expect(typeof distinctCreatedAtMiss?.[0]).toBe('object')
424 | expect(distinctCreatedAtMiss?.[0] instanceof Date).toBeTruthy()
425 | expect(distinctCreatedAtMiss).toEqual([pureLean?.createdAt])
426 |
427 | const distinctCreatedAtHit = await UserModel.distinct('createdAt').cache('30 seconds').lean().exec()
428 | expect(distinctCreatedAtHit).not.toBeNull()
429 | expect(distinctCreatedAtHit?.length).toBe(1)
430 | expect(typeof distinctCreatedAtMiss?.[0]).toBe('object')
431 | expect(distinctCreatedAtMiss?.[0] instanceof Date).toBeTruthy()
432 | expect(distinctCreatedAtHit).toEqual([pureLean?.createdAt])
433 |
434 | expect(miss?._id.toString()).toBe(hit?._id.toString())
435 | expect(miss?.name).toBe(hit?.name)
436 | expect(miss?.role).toBe(hit?.role)
437 | expect(miss?.createdAt?.toString()).toBe(hit?.createdAt?.toString())
438 | expect(miss?.updatedAt?.toString()).toBe(hit?.updatedAt?.toString())
439 | })
440 |
441 | it('should hydrate populated objects from cache', async () => {
442 | const user = await UserModel.create({ name: 'Alex', role: 'user' })
443 | const story1 = await StoryModel.create({ title: 'Ticket 1', userId: user._id })
444 | const story2 = await StoryModel.create({ title: 'Ticket 2', userId: user._id })
445 |
446 | const populatedOriginal = await UserModel.findOne({ name: 'Alex' }).populate('stories').lean().cache('1 minute').exec()
447 |
448 | expect(populatedOriginal).not.toBeNull()
449 |
450 | expect(typeof populatedOriginal?._id).toBe('object')
451 | expect(populatedOriginal?._id instanceof mongoose.Types.ObjectId).toBeTruthy()
452 |
453 | expect(populatedOriginal?.name).toBe('Alex')
454 | expect(populatedOriginal?.stories).not.toBeNull()
455 | expect(populatedOriginal?.stories?.length).toBe(2)
456 |
457 | expect(populatedOriginal?.stories?.[0]._id.toString()).toBe(story1._id.toString())
458 |
459 | expect(typeof populatedOriginal?.stories?.[0]._id).toBe('object')
460 | expect(populatedOriginal?.stories?.[0]._id instanceof mongoose.Types.ObjectId).toBeTruthy()
461 |
462 | expect(typeof populatedOriginal?.stories?.[0].createdAt).toBe('object')
463 | expect(populatedOriginal?.stories?.[0].createdAt instanceof Date).toBeTruthy()
464 |
465 | expect(populatedOriginal?.stories?.[1]._id.toString()).toBe(story2._id.toString())
466 |
467 | expect(typeof populatedOriginal?.stories?.[1]._id).toBe('object')
468 | expect(populatedOriginal?.stories?.[1]._id instanceof mongoose.Types.ObjectId).toBeTruthy()
469 |
470 | expect(typeof populatedOriginal?.stories?.[1].createdAt).toBe('object')
471 | expect(populatedOriginal?.stories?.[1].createdAt instanceof Date).toBeTruthy()
472 |
473 | // Deleting user and stories, to ensure that the cache is used
474 | await UserModel.deleteOne({ _id: user._id }).exec()
475 | await StoryModel.deleteMany({ userId: user._id }).exec()
476 |
477 | const populatedCache = await UserModel.findOne({ name: 'Alex' }).populate('stories').lean().cache('1 minute').exec()
478 |
479 | expect(populatedCache).not.toBeNull()
480 |
481 | expect(typeof populatedCache?._id).toBe('object')
482 | expect(populatedCache?._id instanceof ObjectId).toBeTruthy()
483 |
484 | expect(populatedCache?.name).toBe('Alex')
485 | expect(populatedCache?.stories).not.toBeNull()
486 | expect(populatedCache?.stories?.length).toBe(2)
487 |
488 | expect(populatedCache?.stories?.[0]._id.toString()).toBe(story1._id.toString())
489 |
490 | expect(typeof populatedCache?.stories?.[0]._id).toBe('object')
491 | expect(populatedCache?.stories?.[0]._id instanceof ObjectId).toBeTruthy()
492 |
493 | expect(typeof populatedCache?.stories?.[0].createdAt).toBe('object')
494 | expect(populatedCache?.stories?.[0].createdAt instanceof Date).toBeTruthy()
495 |
496 | expect(populatedCache?.stories?.[1]._id.toString()).toBe(story2._id.toString())
497 |
498 | expect(typeof populatedCache?.stories?.[1]._id).toBe('object')
499 | expect(populatedCache?.stories?.[1]._id instanceof ObjectId).toBeTruthy()
500 |
501 | expect(typeof populatedCache?.stories?.[1].createdAt).toBe('object')
502 | expect(populatedCache?.stories?.[1].createdAt instanceof Date).toBeTruthy()
503 |
504 | expect(populatedOriginal?._id.toString()).toBe(populatedCache?._id.toString())
505 | expect(populatedOriginal?.name).toBe(populatedCache?.name)
506 | expect(populatedOriginal?.role).toBe(populatedCache?.role)
507 | expect(populatedOriginal?.createdAt?.toString()).toBe(populatedCache?.createdAt?.toString())
508 | expect(populatedOriginal?.updatedAt?.toString()).toBe(populatedCache?.updatedAt?.toString())
509 |
510 | expect(populatedOriginal?.stories?.[0]._id.toString()).toBe(populatedCache?.stories?.[0]._id.toString())
511 | expect(populatedOriginal?.stories?.[0].title).toBe(populatedCache?.stories?.[0].title)
512 | expect(populatedOriginal?.stories?.[0].userId.toString()).toBe(populatedCache?.stories?.[0].userId.toString())
513 | expect(populatedOriginal?.stories?.[0].createdAt?.toString()).toBe(populatedCache?.stories?.[0].createdAt?.toString())
514 | expect(populatedOriginal?.stories?.[0].updatedAt?.toString()).toBe(populatedCache?.stories?.[0].updatedAt?.toString())
515 |
516 | expect(populatedOriginal?.stories?.[1]._id.toString()).toBe(populatedCache?.stories?.[1]._id.toString())
517 | expect(populatedOriginal?.stories?.[1].title).toBe(populatedCache?.stories?.[1].title)
518 | expect(populatedOriginal?.stories?.[1].userId.toString()).toBe(populatedCache?.stories?.[1].userId.toString())
519 | expect(populatedOriginal?.stories?.[1].createdAt?.toString()).toBe(populatedCache?.stories?.[1].createdAt?.toString())
520 | expect(populatedOriginal?.stories?.[1].updatedAt?.toString()).toBe(populatedCache?.stories?.[1].updatedAt?.toString())
521 | })
522 | })
523 |
--------------------------------------------------------------------------------
/tests/key.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from 'vitest'
2 |
3 | import { Types } from 'mongoose'
4 | import { getKey } from '../src/key'
5 |
6 | const { ObjectId } = Types
7 |
8 | describe('generateHash()', () => {
9 | const data1 = {
10 | foo: 42,
11 | bar: {
12 | baz: [3, 2, 1],
13 | qux: 'hello',
14 | _id: new ObjectId('5f9b3b3b3b3b3b3b3b3b3b3b'),
15 | wow: {
16 | word: 'world',
17 | hey: {
18 | waldo: true,
19 | fred: null,
20 | missing: undefined,
21 | },
22 | },
23 | },
24 | }
25 |
26 | const data2 = {
27 | foo: 42,
28 | bar: {
29 | _id: new ObjectId('5f9b3b3b3b3b3b3b3b3b3b3b'),
30 | baz: [3, 2, 1],
31 | qux: 'hello',
32 | wow: {
33 | word: 'world',
34 | hey: {
35 | waldo: true,
36 | fred: null,
37 | missing: undefined,
38 | },
39 | },
40 | },
41 | }
42 |
43 | const data3 = {
44 | _id: new ObjectId('5f9b3b3b3b3b3b3b3b3b3b3b'),
45 | bar: {
46 | qux: 'hello',
47 | baz: [3, 2, 1],
48 | wow: {
49 | hey: {
50 | fred: null,
51 | waldo: true,
52 | missing: undefined,
53 | },
54 | word: 'world',
55 | },
56 | },
57 | foo: 42,
58 | }
59 |
60 | const data4 = {
61 | _id: new ObjectId('1f9b3b3b3b3b3b3b3b3b3b3b'),
62 | bar: {
63 | qux: 'hello',
64 | baz: [3, 2, 1],
65 | wow: {
66 | hey: {
67 | fred: null,
68 | waldo: true,
69 | missing: undefined,
70 | },
71 | word: 'world',
72 | },
73 | },
74 | foo: 42,
75 | }
76 |
77 | it('should generate hash keys for objects with different key orders', () => {
78 | const hash1 = getKey(data1)
79 | const hash2 = getKey(data2)
80 | const hash3 = getKey(data3)
81 | const hash4 = getKey(data4)
82 |
83 | expect(hash1).toEqual(hash2)
84 | expect(hash1).not.toEqual(hash3)
85 | expect(hash3).not.toEqual(hash4)
86 | })
87 |
88 | it('should test dates', async () => {
89 | const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
90 | const date = new Date()
91 | const hash1 = getKey({
92 | name: 'John Doe',
93 | date: { $lte: date },
94 | })
95 | await wait(50)
96 | const date2 = new Date()
97 | const hash2 = getKey({
98 | name: 'John Doe',
99 | date: { $lte: date2 },
100 | })
101 | expect(hash1).not.toEqual(hash2)
102 | })
103 | })
104 |
--------------------------------------------------------------------------------
/tests/models/Story.ts:
--------------------------------------------------------------------------------
1 | import { Schema, model, models } from 'mongoose'
2 |
3 | import type { Model, Types } from 'mongoose'
4 |
5 | export interface Story {
6 | _id: Types.ObjectId
7 | userId: Types.ObjectId
8 | title: string
9 | createdAt: Date
10 | updatedAt: Date
11 | }
12 |
13 | export const StorySchema = new Schema(
14 | {
15 | userId: {
16 | type: Schema.Types.ObjectId,
17 | ref: 'User',
18 | required: true,
19 | index: true,
20 | },
21 | title: {
22 | type: String,
23 | required: true,
24 | },
25 | },
26 | { timestamps: true },
27 | )
28 |
29 | export const StoryModel = (models.Story as Model | undefined) ?? model('Story', StorySchema)
30 |
--------------------------------------------------------------------------------
/tests/models/User.ts:
--------------------------------------------------------------------------------
1 | import { Schema, model, models } from 'mongoose'
2 |
3 | import type { HydratedDocument, Model, Types } from 'mongoose'
4 | import type { Story } from './Story'
5 |
6 | export interface User {
7 | _id: Types.ObjectId
8 | name: string
9 | role: string
10 | age?: number
11 | createdAt?: Date
12 | updatedAt?: Date
13 | stories?: HydratedDocument[]
14 | }
15 |
16 | export const UserSchema = new Schema(
17 | {
18 | name: {
19 | type: String,
20 | required: true,
21 | },
22 | role: {
23 | type: String,
24 | required: true,
25 | },
26 | age: {
27 | type: Number,
28 | },
29 | },
30 | { timestamps: true },
31 | )
32 |
33 | UserSchema.virtual('stories', {
34 | ref: 'Story',
35 | localField: '_id',
36 | foreignField: 'userId',
37 | })
38 |
39 | export const UserModel = (models.User as Model | undefined) ?? model('User', UserSchema)
40 |
--------------------------------------------------------------------------------
/tests/mongo/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 | !server.ts
--------------------------------------------------------------------------------
/tests/mongo/server.ts:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs'
2 | import mongoose from 'mongoose'
3 |
4 | import { MongoMemoryServer } from 'mongodb-memory-server'
5 |
6 | export const server = (dbName: string) => {
7 | let mongo: MongoMemoryServer
8 | const dbPath = `./tests/mongo/${dbName}`
9 |
10 | const create = async () => {
11 | fs.mkdirSync(dbPath, { recursive: true })
12 | mongo = await MongoMemoryServer.create({
13 | instance: {
14 | dbName,
15 | dbPath,
16 | },
17 | })
18 |
19 | const uri = mongo.getUri()
20 | await mongoose.connect(uri)
21 | }
22 |
23 | const destroy = async () => {
24 | await mongoose.connection.dropDatabase()
25 | await mongoose.connection.close()
26 | await mongo.stop({ doCleanup: true, force: true })
27 | }
28 |
29 | return { create, destroy }
30 | }
31 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
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 | "exactOptionalPropertyTypes": true,
19 | "forceConsistentCasingInFileNames": true,
20 | "importHelpers": true,
21 | "isolatedModules": true,
22 | "noEmitOnError": true,
23 | "noFallthroughCasesInSwitch": true,
24 | "noImplicitOverride": true,
25 | "noImplicitReturns": true,
26 | "noUncheckedIndexedAccess": true,
27 | "noUnusedLocals": true,
28 | "noUnusedParameters": true,
29 | "removeComments": true,
30 | "skipLibCheck": true,
31 | "sourceMap": true
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/vite.config.mts:
--------------------------------------------------------------------------------
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 | reporter: ['lcov'],
10 | include: ['src/**/*.ts'],
11 | },
12 | },
13 | })
14 |
--------------------------------------------------------------------------------