@rx-ted/packages-honest-plugins 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +45 -0
- package/api-doc/.turbo/turbo-build.log +1 -0
- package/api-doc/.turbo/turbo-test.log +11 -0
- package/api-doc/.turbo/turbo-typecheck.log +1 -0
- package/api-doc/CHANGELOG.md +22 -0
- package/api-doc/README.md +80 -0
- package/api-doc/dist/api-doc.plugin.d.ts +53 -0
- package/api-doc/dist/api-doc.plugin.js +452 -0
- package/api-doc/dist/index.d.ts +5 -0
- package/api-doc/dist/index.js +3 -0
- package/api-doc/dist/interfaces/renderer.interface.d.ts +7 -0
- package/api-doc/dist/interfaces/renderer.interface.js +1 -0
- package/api-doc/dist/renderers/scalar.renderer.d.ts +10 -0
- package/api-doc/dist/renderers/scalar.renderer.js +76 -0
- package/api-doc/dist/renderers/swagger.renderer.d.ts +10 -0
- package/api-doc/dist/renderers/swagger.renderer.js +83 -0
- package/api-doc/node_modules/.bin/esbuild +22 -0
- package/api-doc/node_modules/.bin/jiti +22 -0
- package/api-doc/node_modules/.bin/sass +22 -0
- package/api-doc/node_modules/.bin/tsx +22 -0
- package/api-doc/node_modules/.bin/vite +22 -0
- package/api-doc/node_modules/.bin/vitest +22 -0
- package/api-doc/node_modules/.bin/yaml +22 -0
- package/api-doc/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/api-doc/package.json +48 -0
- package/api-doc/src/api-doc.plugin.ts +554 -0
- package/api-doc/src/index.ts +5 -0
- package/api-doc/src/interfaces/renderer.interface.ts +5 -0
- package/api-doc/src/plugin.test.ts +740 -0
- package/api-doc/src/renderers/scalar.renderer.ts +82 -0
- package/api-doc/src/renderers/swagger.renderer.ts +89 -0
- package/api-doc/tsconfig.json +21 -0
- package/api-doc/vitest.config.ts +13 -0
- package/cache/.turbo/turbo-build.log +1 -0
- package/cache/.turbo/turbo-test.log +16 -0
- package/cache/CHANGELOG.md +23 -0
- package/cache/README.md +79 -0
- package/cache/dist/cache-service.d.ts +8 -0
- package/cache/dist/cache-service.js +21 -0
- package/cache/dist/cacheable.d.ts +2 -0
- package/cache/dist/cacheable.js +10 -0
- package/cache/dist/cloudflare/driver.d.ts +23 -0
- package/cache/dist/cloudflare/driver.js +102 -0
- package/cache/dist/cloudflare/index.d.ts +3 -0
- package/cache/dist/cloudflare/index.js +2 -0
- package/cache/dist/cloudflare/plugin.d.ts +19 -0
- package/cache/dist/cloudflare/plugin.js +58 -0
- package/cache/dist/index.d.ts +11 -0
- package/cache/dist/index.js +14 -0
- package/cache/dist/local/driver.d.ts +2 -0
- package/cache/dist/local/driver.js +98 -0
- package/cache/dist/local/index.d.ts +2 -0
- package/cache/dist/local/index.js +2 -0
- package/cache/dist/local/plugin.d.ts +14 -0
- package/cache/dist/local/plugin.js +24 -0
- package/cache/dist/redis/driver.d.ts +10 -0
- package/cache/dist/redis/driver.js +102 -0
- package/cache/dist/redis/index.d.ts +4 -0
- package/cache/dist/redis/index.js +2 -0
- package/cache/dist/redis/plugin.d.ts +25 -0
- package/cache/dist/redis/plugin.js +61 -0
- package/cache/dist/resolve.d.ts +24 -0
- package/cache/dist/resolve.js +114 -0
- package/cache/dist/types.d.ts +18 -0
- package/cache/dist/types.js +1 -0
- package/cache/node_modules/.bin/esbuild +22 -0
- package/cache/node_modules/.bin/jiti +22 -0
- package/cache/node_modules/.bin/sass +22 -0
- package/cache/node_modules/.bin/tsx +22 -0
- package/cache/node_modules/.bin/vite +22 -0
- package/cache/node_modules/.bin/vitest +22 -0
- package/cache/node_modules/.bin/yaml +22 -0
- package/cache/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/cache/package.json +46 -0
- package/cache/src/cache-service.test.ts +16 -0
- package/cache/src/cache-service.ts +15 -0
- package/cache/src/cacheable.test.ts +78 -0
- package/cache/src/cacheable.ts +16 -0
- package/cache/src/cloudflare/driver.test.ts +188 -0
- package/cache/src/cloudflare/driver.ts +118 -0
- package/cache/src/cloudflare/index.ts +3 -0
- package/cache/src/cloudflare/plugin.ts +71 -0
- package/cache/src/index.test.ts +68 -0
- package/cache/src/index.ts +20 -0
- package/cache/src/local/driver.ts +117 -0
- package/cache/src/local/index.ts +2 -0
- package/cache/src/local/plugin.ts +34 -0
- package/cache/src/redis/driver.test.ts +71 -0
- package/cache/src/redis/driver.ts +124 -0
- package/cache/src/redis/index.ts +4 -0
- package/cache/src/redis/plugin.ts +84 -0
- package/cache/src/resolve.test.ts +257 -0
- package/cache/src/resolve.ts +140 -0
- package/cache/src/types.ts +14 -0
- package/cache/tsconfig.json +21 -0
- package/cache/vitest.config.ts +13 -0
- package/db/.turbo/turbo-build.log +1 -0
- package/db/.turbo/turbo-test.log +21 -0
- package/db/CHANGELOG.md +26 -0
- package/db/README.md +99 -0
- package/db/dist/constants.d.ts +1 -0
- package/db/dist/constants.js +1 -0
- package/db/dist/d1/index.d.ts +3 -0
- package/db/dist/d1/index.js +2 -0
- package/db/dist/d1/plugin.d.ts +18 -0
- package/db/dist/d1/plugin.js +104 -0
- package/db/dist/db-service.d.ts +7 -0
- package/db/dist/db-service.js +21 -0
- package/db/dist/index.d.ts +15 -0
- package/db/dist/index.js +20 -0
- package/db/dist/mysql/client.d.ts +36 -0
- package/db/dist/mysql/client.js +84 -0
- package/db/dist/mysql/driver.d.ts +18 -0
- package/db/dist/mysql/driver.js +107 -0
- package/db/dist/mysql/index.d.ts +11 -0
- package/db/dist/mysql/index.js +11 -0
- package/db/dist/mysql/plugin.d.ts +51 -0
- package/db/dist/mysql/plugin.js +94 -0
- package/db/dist/mysql/types.d.ts +38 -0
- package/db/dist/mysql/types.js +1 -0
- package/db/dist/postgres/index.d.ts +2 -0
- package/db/dist/postgres/index.js +3 -0
- package/db/dist/repository.d.ts +14 -0
- package/db/dist/repository.js +79 -0
- package/db/dist/resolve.d.ts +6 -0
- package/db/dist/resolve.js +81 -0
- package/db/dist/schema-builder/column-builder.d.ts +26 -0
- package/db/dist/schema-builder/column-builder.js +53 -0
- package/db/dist/schema-builder/compile-d1.d.ts +5 -0
- package/db/dist/schema-builder/compile-d1.js +67 -0
- package/db/dist/schema-builder/compile-mysql.d.ts +12 -0
- package/db/dist/schema-builder/compile-mysql.js +78 -0
- package/db/dist/schema-builder/index.d.ts +8 -0
- package/db/dist/schema-builder/index.js +22 -0
- package/db/dist/schema-builder/table-builder.d.ts +16 -0
- package/db/dist/schema-builder/table-builder.js +38 -0
- package/db/dist/schema-builder/types.d.ts +26 -0
- package/db/dist/schema-builder/types.js +1 -0
- package/db/dist/schema-builder/zod-bridge.d.ts +24 -0
- package/db/dist/schema-builder/zod-bridge.js +53 -0
- package/db/dist/sqlite/index.d.ts +4 -0
- package/db/dist/sqlite/index.js +2 -0
- package/db/dist/sqlite/plugin.d.ts +17 -0
- package/db/dist/sqlite/plugin.js +33 -0
- package/db/dist/types.d.ts +42 -0
- package/db/dist/types.js +1 -0
- package/db/node_modules/.bin/esbuild +22 -0
- package/db/node_modules/.bin/jiti +22 -0
- package/db/node_modules/.bin/sass +22 -0
- package/db/node_modules/.bin/tsx +22 -0
- package/db/node_modules/.bin/vite +22 -0
- package/db/node_modules/.bin/vitest +22 -0
- package/db/node_modules/.bin/yaml +22 -0
- package/db/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/db/package.json +59 -0
- package/db/src/constants.test.ts +8 -0
- package/db/src/constants.ts +1 -0
- package/db/src/d1/index.ts +13 -0
- package/db/src/d1/plugin.ts +135 -0
- package/db/src/db-service.ts +14 -0
- package/db/src/index.test.ts +63 -0
- package/db/src/index.ts +57 -0
- package/db/src/mysql/client.ts +112 -0
- package/db/src/mysql/driver.test.ts +190 -0
- package/db/src/mysql/driver.ts +116 -0
- package/db/src/mysql/index.ts +51 -0
- package/db/src/mysql/plugin.test.ts +228 -0
- package/db/src/mysql/plugin.ts +128 -0
- package/db/src/mysql/types.ts +32 -0
- package/db/src/postgres/index.ts +15 -0
- package/db/src/repository.test.ts +195 -0
- package/db/src/repository.ts +95 -0
- package/db/src/resolve.ts +105 -0
- package/db/src/schema-builder/column-builder.test.ts +55 -0
- package/db/src/schema-builder/column-builder.ts +87 -0
- package/db/src/schema-builder/compile-d1.test.ts +60 -0
- package/db/src/schema-builder/compile-d1.ts +70 -0
- package/db/src/schema-builder/compile-mysql.test.ts +64 -0
- package/db/src/schema-builder/compile-mysql.ts +102 -0
- package/db/src/schema-builder/index.test.ts +36 -0
- package/db/src/schema-builder/index.ts +41 -0
- package/db/src/schema-builder/table-builder.test.ts +55 -0
- package/db/src/schema-builder/table-builder.ts +50 -0
- package/db/src/schema-builder/types.test.ts +68 -0
- package/db/src/schema-builder/types.ts +30 -0
- package/db/src/schema-builder/zod-bridge.ts +89 -0
- package/db/src/sqlite/index.ts +14 -0
- package/db/src/sqlite/plugin.ts +46 -0
- package/db/src/types.ts +45 -0
- package/db/tsconfig.json +21 -0
- package/db/vitest.config.ts +13 -0
- package/mail/.turbo/turbo-build.log +1 -0
- package/mail/.turbo/turbo-test.log +12 -0
- package/mail/.turbo/turbo-typecheck.log +1 -0
- package/mail/CHANGELOG.md +25 -0
- package/mail/README.md +214 -0
- package/mail/dist/index.d.ts +4 -0
- package/mail/dist/index.js +2 -0
- package/mail/dist/plugin.d.ts +64 -0
- package/mail/dist/plugin.js +232 -0
- package/mail/dist/providers/brevo.provider.d.ts +15 -0
- package/mail/dist/providers/brevo.provider.js +75 -0
- package/mail/dist/providers/custom.provider.d.ts +9 -0
- package/mail/dist/providers/custom.provider.js +22 -0
- package/mail/dist/providers/resend.provider.d.ts +16 -0
- package/mail/dist/providers/resend.provider.js +77 -0
- package/mail/dist/providers/smtp.provider.d.ts +21 -0
- package/mail/dist/providers/smtp.provider.js +86 -0
- package/mail/dist/rate-limiter.d.ts +16 -0
- package/mail/dist/rate-limiter.js +49 -0
- package/mail/dist/types.d.ts +68 -0
- package/mail/dist/types.js +6 -0
- package/mail/node_modules/.bin/esbuild +22 -0
- package/mail/node_modules/.bin/jiti +22 -0
- package/mail/node_modules/.bin/sass +22 -0
- package/mail/node_modules/.bin/tsx +22 -0
- package/mail/node_modules/.bin/vite +22 -0
- package/mail/node_modules/.bin/vitest +22 -0
- package/mail/node_modules/.bin/yaml +22 -0
- package/mail/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/mail/package.json +52 -0
- package/mail/src/index.ts +4 -0
- package/mail/src/plugin.test.ts +219 -0
- package/mail/src/plugin.ts +313 -0
- package/mail/src/providers/brevo.provider.ts +78 -0
- package/mail/src/providers/custom.provider.ts +29 -0
- package/mail/src/providers/resend.provider.ts +74 -0
- package/mail/src/providers/smtp.provider.ts +85 -0
- package/mail/src/rate-limiter.test.ts +81 -0
- package/mail/src/rate-limiter.ts +54 -0
- package/mail/src/types.ts +81 -0
- package/mail/tsconfig.json +21 -0
- package/mail/vitest.config.ts +13 -0
- package/package.json +36 -0
- package/s3/.turbo/turbo-build.log +1 -0
- package/s3/.turbo/turbo-test.log +11 -0
- package/s3/CHANGELOG.md +20 -0
- package/s3/README.md +90 -0
- package/s3/dist/index.d.ts +6 -0
- package/s3/dist/index.js +4 -0
- package/s3/dist/s3-service.d.ts +7 -0
- package/s3/dist/s3-service.js +21 -0
- package/s3/dist/s3.driver.d.ts +6 -0
- package/s3/dist/s3.driver.js +221 -0
- package/s3/dist/s3.plugin.d.ts +24 -0
- package/s3/dist/s3.plugin.js +58 -0
- package/s3/dist/types.d.ts +30 -0
- package/s3/dist/types.js +1 -0
- package/s3/node_modules/.bin/esbuild +22 -0
- package/s3/node_modules/.bin/jiti +22 -0
- package/s3/node_modules/.bin/sass +22 -0
- package/s3/node_modules/.bin/tsx +22 -0
- package/s3/node_modules/.bin/vite +22 -0
- package/s3/node_modules/.bin/vitest +22 -0
- package/s3/node_modules/.bin/yaml +22 -0
- package/s3/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/s3/package.json +47 -0
- package/s3/src/index.test.ts +20 -0
- package/s3/src/index.ts +13 -0
- package/s3/src/s3-service.ts +14 -0
- package/s3/src/s3.driver.ts +259 -0
- package/s3/src/s3.plugin.ts +80 -0
- package/s3/src/types.ts +37 -0
- package/s3/tsconfig.json +21 -0
- package/s3/vitest.config.ts +13 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
|
3
|
+
|
|
4
|
+
case `uname` in
|
|
5
|
+
*CYGWIN*|*MINGW*|*MSYS*)
|
|
6
|
+
if command -v cygpath > /dev/null 2>&1; then
|
|
7
|
+
basedir=`cygpath -w "$basedir"`
|
|
8
|
+
fi
|
|
9
|
+
;;
|
|
10
|
+
esac
|
|
11
|
+
|
|
12
|
+
if [ -z "$NODE_PATH" ]; then
|
|
13
|
+
export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/node_modules:/Users/ben/projects/app/node_modules/.pnpm/yaml@2.9.0/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules"
|
|
14
|
+
else
|
|
15
|
+
export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/node_modules:/Users/ben/projects/app/node_modules/.pnpm/yaml@2.9.0/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
|
+
fi
|
|
17
|
+
if [ -x "$basedir/node" ]; then
|
|
18
|
+
exec "$basedir/node" "$basedir/../../../../../node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/bin.mjs" "$@"
|
|
19
|
+
else
|
|
20
|
+
exec node "$basedir/../../../../../node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/bin.mjs" "$@"
|
|
21
|
+
fi
|
|
22
|
+
# cmd-shim-target=/Users/ben/projects/app/node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/bin.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":"4.1.8","results":[[":src/cacheable.test.ts",{"duration":5.22483299999999,"failed":false}],[":src/redis/driver.test.ts",{"duration":5.951208999999977,"failed":false}],[":src/resolve.test.ts",{"duration":8.927333000000004,"failed":false}],[":src/index.test.ts",{"duration":1.8274999999999864,"failed":false}],[":src/cloudflare/driver.test.ts",{"duration":4.579999999999984,"failed":false}],[":src/cache-service.test.ts",{"duration":1.563874999999996,"failed":false}]]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rx-ted/packages-honest-plugins-cache",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Cache plugin for @rx-ted/packages-honest",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"vitest": "./src/index.ts",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"sideEffects": [
|
|
17
|
+
"./dist/index.js"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/rx-ted/honest.git"
|
|
22
|
+
},
|
|
23
|
+
"author": "rx-ted",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -p tsconfig.json",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"test:watch": "vitest",
|
|
32
|
+
"test:coverage": "vitest run --coverage"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"redis": "^4.7.1",
|
|
36
|
+
"@rx-ted/packages-core": "workspace:^"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@rx-ted/packages-honest": "workspace:^",
|
|
40
|
+
"hono": "^4.12.18"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^25.9.1",
|
|
44
|
+
"vitest": "^4.1.7"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { CacheService, CACHE_GLOBAL_KEY } from './cache-service';
|
|
3
|
+
|
|
4
|
+
describe('CacheService', () => {
|
|
5
|
+
it('exports CACHE_GLOBAL_KEY as "app:cache"', () => {
|
|
6
|
+
expect(CACHE_GLOBAL_KEY).toBe('app:cache');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('is a class', () => {
|
|
10
|
+
expect(typeof CacheService).toBe('function');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('throws when instantiated without ComponentManager registration', () => {
|
|
14
|
+
expect(() => new CacheService()).toThrow();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Service, ComponentManager } from '@rx-ted/packages-honest';
|
|
2
|
+
import type { CacheDriver } from './types';
|
|
3
|
+
|
|
4
|
+
export const CACHE_GLOBAL_KEY = 'app:cache';
|
|
5
|
+
|
|
6
|
+
@Service()
|
|
7
|
+
class CacheService {
|
|
8
|
+
constructor() {
|
|
9
|
+
return ComponentManager.getPlugin<CacheDriver>(CACHE_GLOBAL_KEY);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface CacheService extends CacheDriver {}
|
|
14
|
+
|
|
15
|
+
export { CacheService };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { cacheable } from './cacheable';
|
|
3
|
+
import type { CacheDriver } from './types';
|
|
4
|
+
|
|
5
|
+
function createMockDriver(): CacheDriver {
|
|
6
|
+
return {
|
|
7
|
+
get: vi.fn(),
|
|
8
|
+
set: vi.fn(),
|
|
9
|
+
delete: vi.fn(),
|
|
10
|
+
exists: vi.fn(),
|
|
11
|
+
mget: vi.fn(),
|
|
12
|
+
mset: vi.fn(),
|
|
13
|
+
deleteByPattern: vi.fn(),
|
|
14
|
+
incr: vi.fn(),
|
|
15
|
+
decr: vi.fn(),
|
|
16
|
+
expire: vi.fn(),
|
|
17
|
+
close: vi.fn(),
|
|
18
|
+
healthCheck: vi.fn(),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('cacheable', () => {
|
|
23
|
+
it('returns cached value when cache hits', async () => {
|
|
24
|
+
const cache = createMockDriver();
|
|
25
|
+
cache.get = vi.fn().mockResolvedValue('cached-value');
|
|
26
|
+
|
|
27
|
+
const result = await cacheable(cache, 'my-key', 60, async () => 'fresh-value');
|
|
28
|
+
|
|
29
|
+
expect(result).toBe('cached-value');
|
|
30
|
+
expect(cache.get).toHaveBeenCalledWith('my-key');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('calls fetch and caches result when cache misses', async () => {
|
|
34
|
+
const cache = createMockDriver();
|
|
35
|
+
cache.get = vi.fn().mockResolvedValue(null);
|
|
36
|
+
cache.set = vi.fn().mockResolvedValue(true);
|
|
37
|
+
|
|
38
|
+
const fetch = vi.fn().mockResolvedValue('fresh-value');
|
|
39
|
+
const result = await cacheable(cache, 'my-key', 60, fetch);
|
|
40
|
+
|
|
41
|
+
expect(result).toBe('fresh-value');
|
|
42
|
+
expect(cache.get).toHaveBeenCalledWith('my-key');
|
|
43
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
44
|
+
expect(cache.set).toHaveBeenCalledWith('my-key', 'fresh-value', 60);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('does not cache when fetch returns null', async () => {
|
|
48
|
+
const cache = createMockDriver();
|
|
49
|
+
cache.get = vi.fn().mockResolvedValue(null);
|
|
50
|
+
cache.set = vi.fn();
|
|
51
|
+
|
|
52
|
+
const result = await cacheable(cache, 'my-key', 60, async () => null);
|
|
53
|
+
|
|
54
|
+
expect(result).toBeNull();
|
|
55
|
+
expect(cache.set).not.toHaveBeenCalled();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('uses the provided ttl', async () => {
|
|
59
|
+
const cache = createMockDriver();
|
|
60
|
+
cache.get = vi.fn().mockResolvedValue(null);
|
|
61
|
+
cache.set = vi.fn().mockResolvedValue(true);
|
|
62
|
+
|
|
63
|
+
await cacheable(cache, 'key-ttl', 300, async () => ({ data: true }));
|
|
64
|
+
|
|
65
|
+
expect(cache.set).toHaveBeenCalledWith('key-ttl', { data: true }, 300);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('forwards exception from fetch', async () => {
|
|
69
|
+
const cache = createMockDriver();
|
|
70
|
+
cache.get = vi.fn().mockResolvedValue(null);
|
|
71
|
+
|
|
72
|
+
await expect(
|
|
73
|
+
cacheable(cache, 'error-key', 60, async () => {
|
|
74
|
+
throw new Error('fetch-failed');
|
|
75
|
+
}),
|
|
76
|
+
).rejects.toThrow('fetch-failed');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CacheDriver } from './types';
|
|
2
|
+
|
|
3
|
+
export async function cacheable<T>(
|
|
4
|
+
cache: CacheDriver,
|
|
5
|
+
key: string,
|
|
6
|
+
ttl: number,
|
|
7
|
+
fetch: () => Promise<T>,
|
|
8
|
+
): Promise<T> {
|
|
9
|
+
const cached = await cache.get<T>(key);
|
|
10
|
+
if (cached !== null) return cached;
|
|
11
|
+
const data = await fetch();
|
|
12
|
+
if (data !== null) {
|
|
13
|
+
await cache.set(key, data, ttl);
|
|
14
|
+
}
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { createCloudflareKvDriver } from './driver';
|
|
3
|
+
import type { CacheDriver } from '../types';
|
|
4
|
+
|
|
5
|
+
function mockKvNamespace() {
|
|
6
|
+
const store = new Map<string, string>();
|
|
7
|
+
return {
|
|
8
|
+
get: vi.fn(async (key: string, _options?: { cacheTtl?: number }) => store.get(key) ?? null),
|
|
9
|
+
put: vi.fn(async (key: string, value: string) => {
|
|
10
|
+
store.set(key, value);
|
|
11
|
+
}),
|
|
12
|
+
delete: vi.fn(async (key: string) => {
|
|
13
|
+
store.delete(key);
|
|
14
|
+
}),
|
|
15
|
+
list: vi.fn(async (opts?: { prefix?: string }) => {
|
|
16
|
+
const prefix = opts?.prefix ?? '';
|
|
17
|
+
const keys = [...store.keys()].filter((k) => k.startsWith(prefix)).map((name) => ({ name }));
|
|
18
|
+
return { keys, list_complete: true, cacheStatus: null };
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('CloudflareKvDriver', () => {
|
|
24
|
+
let driver: CacheDriver;
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
driver = createCloudflareKvDriver(mockKvNamespace());
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('get/set', () => {
|
|
31
|
+
it('returns null for missing key', async () => {
|
|
32
|
+
const result = await driver.get('missing');
|
|
33
|
+
expect(result).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('stores and retrieves a value', async () => {
|
|
37
|
+
await driver.set('foo', { hello: 'world' });
|
|
38
|
+
const result = await driver.get('foo');
|
|
39
|
+
expect(result).toEqual({ hello: 'world' });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('stores with ttl', async () => {
|
|
43
|
+
const result = await driver.set('bar', 'value', 60);
|
|
44
|
+
expect(result).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('retrieves string values', async () => {
|
|
48
|
+
await driver.set('str', 'plain-string');
|
|
49
|
+
const result = await driver.get<string>('str');
|
|
50
|
+
expect(result).toBe('plain-string');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('retrieves numeric values', async () => {
|
|
54
|
+
await driver.set('num', 42);
|
|
55
|
+
const result = await driver.get<number>('num');
|
|
56
|
+
expect(result).toBe(42);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('delete', () => {
|
|
61
|
+
it('deletes an existing key', async () => {
|
|
62
|
+
await driver.set('tmp', 'value');
|
|
63
|
+
const result = await driver.delete('tmp');
|
|
64
|
+
expect(result).toBe(true);
|
|
65
|
+
const got = await driver.get('tmp');
|
|
66
|
+
expect(got).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('returns true even when key does not exist', async () => {
|
|
70
|
+
const result = await driver.delete('nonexistent');
|
|
71
|
+
expect(result).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('exists', () => {
|
|
76
|
+
it('returns true for existing key', async () => {
|
|
77
|
+
await driver.set('present', 'yes');
|
|
78
|
+
const result = await driver.exists('present');
|
|
79
|
+
expect(result).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('returns false for missing key', async () => {
|
|
83
|
+
const result = await driver.exists('absent');
|
|
84
|
+
expect(result).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('mget', () => {
|
|
89
|
+
it('returns values for multiple keys', async () => {
|
|
90
|
+
await driver.set('a', 1);
|
|
91
|
+
await driver.set('b', 2);
|
|
92
|
+
const result = await driver.mget(['a', 'b', 'c']);
|
|
93
|
+
expect(result).toEqual([1, 2, null]);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('mset', () => {
|
|
98
|
+
it('sets multiple items', async () => {
|
|
99
|
+
const result = await driver.mset([
|
|
100
|
+
{ key: 'x', value: 10 },
|
|
101
|
+
{ key: 'y', value: 20, ttl: 60 },
|
|
102
|
+
]);
|
|
103
|
+
expect(result).toBe(true);
|
|
104
|
+
expect(await driver.get('x')).toBe(10);
|
|
105
|
+
expect(await driver.get('y')).toBe(20);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('deleteByPattern', () => {
|
|
110
|
+
it('deletes keys matching prefix pattern', async () => {
|
|
111
|
+
await driver.set('session:abc', 1);
|
|
112
|
+
await driver.set('session:def', 2);
|
|
113
|
+
await driver.set('other:xyz', 3);
|
|
114
|
+
|
|
115
|
+
const deleted = await driver.deleteByPattern('session:*');
|
|
116
|
+
expect(deleted).toBe(2);
|
|
117
|
+
expect(await driver.get('session:abc')).toBeNull();
|
|
118
|
+
expect(await driver.get('session:def')).toBeNull();
|
|
119
|
+
expect(await driver.get('other:xyz')).toBe(3);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('returns 0 when no keys match', async () => {
|
|
123
|
+
await driver.set('a', 1);
|
|
124
|
+
const deleted = await driver.deleteByPattern('z:*');
|
|
125
|
+
expect(deleted).toBe(0);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('incr/decr', () => {
|
|
130
|
+
it('incr increments from 0 for missing key', async () => {
|
|
131
|
+
const result = await driver.incr('counter');
|
|
132
|
+
expect(result).toBe(1);
|
|
133
|
+
expect(await driver.get<number>('counter')).toBe(1);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('incr increments existing value', async () => {
|
|
137
|
+
await driver.set('counter', 5);
|
|
138
|
+
const result = await driver.incr('counter');
|
|
139
|
+
expect(result).toBe(6);
|
|
140
|
+
expect(await driver.get<number>('counter')).toBe(6);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('decr decrements from 0 for missing key', async () => {
|
|
144
|
+
const result = await driver.decr('counter');
|
|
145
|
+
expect(result).toBe(-1);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('decr decrements existing value', async () => {
|
|
149
|
+
await driver.set('counter', 5);
|
|
150
|
+
const result = await driver.decr('counter');
|
|
151
|
+
expect(result).toBe(4);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('multiple incr calls accumulate', async () => {
|
|
155
|
+
await driver.incr('c');
|
|
156
|
+
await driver.incr('c');
|
|
157
|
+
const result = await driver.incr('c');
|
|
158
|
+
expect(result).toBe(3);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('expire', () => {
|
|
163
|
+
it('sets ttl on existing key', async () => {
|
|
164
|
+
await driver.set('tmp', 'value');
|
|
165
|
+
const result = await driver.expire('tmp', 60);
|
|
166
|
+
expect(result).toBe(true);
|
|
167
|
+
expect(await driver.get('tmp')).toBe('value');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('returns false for missing key', async () => {
|
|
171
|
+
const result = await driver.expire('missing', 60);
|
|
172
|
+
expect(result).toBe(false);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe('close', () => {
|
|
177
|
+
it('does not throw', async () => {
|
|
178
|
+
await expect(driver.close()).resolves.toBeUndefined();
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('healthCheck', () => {
|
|
183
|
+
it('returns true when namespace responds', async () => {
|
|
184
|
+
const result = await driver.healthCheck();
|
|
185
|
+
expect(result).toBe(true);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { CacheDriver } from '../types';
|
|
2
|
+
|
|
3
|
+
export interface KvNamespace {
|
|
4
|
+
get(key: string): Promise<string | null>;
|
|
5
|
+
put(key: string, value: string, options?: { expirationTtl?: number }): Promise<void>;
|
|
6
|
+
delete(key: string): Promise<void>;
|
|
7
|
+
list(options?: { prefix?: string; cursor?: string }): Promise<{
|
|
8
|
+
keys: { name: string }[];
|
|
9
|
+
list_complete: boolean;
|
|
10
|
+
cursor?: string;
|
|
11
|
+
cacheStatus: string | null;
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface CloudflareKvOptions {
|
|
16
|
+
namespace: KvNamespace;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class CloudflareKvDriver implements CacheDriver {
|
|
20
|
+
constructor(private namespace: KvNamespace) {}
|
|
21
|
+
|
|
22
|
+
async get<T = unknown>(key: string): Promise<T | null> {
|
|
23
|
+
if (!this.namespace) return null;
|
|
24
|
+
const value = await this.namespace.get(key);
|
|
25
|
+
if (value === null) return null;
|
|
26
|
+
return JSON.parse(value) as T;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async set(key: string, value: unknown, ttl?: number): Promise<boolean> {
|
|
30
|
+
if (!this.namespace) return false;
|
|
31
|
+
const serialized = JSON.stringify(value);
|
|
32
|
+
const options: { expirationTtl?: number } = {};
|
|
33
|
+
if (ttl) {
|
|
34
|
+
options.expirationTtl = Math.max(ttl, 60);
|
|
35
|
+
}
|
|
36
|
+
await this.namespace.put(key, serialized, options);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async delete(key: string): Promise<boolean> {
|
|
41
|
+
if (!this.namespace) return false;
|
|
42
|
+
await this.namespace.delete(key);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async exists(key: string): Promise<boolean> {
|
|
47
|
+
if (!this.namespace) return false;
|
|
48
|
+
const value = await this.namespace.get(key);
|
|
49
|
+
return value !== null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async mget<T = unknown>(keys: string[]): Promise<(T | null)[]> {
|
|
53
|
+
return Promise.all(keys.map((key) => this.get<T>(key)));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async mset(items: Array<{ key: string; value: unknown; ttl?: number }>): Promise<boolean> {
|
|
57
|
+
await Promise.all(items.map((item) => this.set(item.key, item.value, item.ttl)));
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async deleteByPattern(pattern: string): Promise<number> {
|
|
62
|
+
if (!this.namespace) return 0;
|
|
63
|
+
const prefix = pattern.replace(/\*.*$/, '');
|
|
64
|
+
let cursor: string | undefined;
|
|
65
|
+
let total = 0;
|
|
66
|
+
do {
|
|
67
|
+
const result = await this.namespace.list({ prefix, cursor });
|
|
68
|
+
if (result.keys.length > 0) {
|
|
69
|
+
await Promise.all(result.keys.map((k) => this.namespace.delete(k.name)));
|
|
70
|
+
total += result.keys.length;
|
|
71
|
+
}
|
|
72
|
+
cursor = result.list_complete ? undefined : result.cursor;
|
|
73
|
+
} while (cursor);
|
|
74
|
+
return total;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async incr(key: string): Promise<number> {
|
|
78
|
+
if (!this.namespace) return 0;
|
|
79
|
+
const raw = await this.namespace.get(key);
|
|
80
|
+
const current = raw !== null ? Number(JSON.parse(raw)) : 0;
|
|
81
|
+
const next = current + 1;
|
|
82
|
+
await this.namespace.put(key, JSON.stringify(next));
|
|
83
|
+
return next;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async decr(key: string): Promise<number> {
|
|
87
|
+
if (!this.namespace) return 0;
|
|
88
|
+
const raw = await this.namespace.get(key);
|
|
89
|
+
const current = raw !== null ? Number(JSON.parse(raw)) : 0;
|
|
90
|
+
const next = current - 1;
|
|
91
|
+
await this.namespace.put(key, JSON.stringify(next));
|
|
92
|
+
return next;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async expire(key: string, ttl: number): Promise<boolean> {
|
|
96
|
+
if (!this.namespace) return false;
|
|
97
|
+
const value = await this.namespace.get(key);
|
|
98
|
+
if (value === null) return false;
|
|
99
|
+
await this.namespace.put(key, value, { expirationTtl: ttl });
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async close(): Promise<void> {}
|
|
104
|
+
|
|
105
|
+
async healthCheck(): Promise<boolean> {
|
|
106
|
+
if (!this.namespace) return false;
|
|
107
|
+
try {
|
|
108
|
+
await this.namespace.get('__health_check__');
|
|
109
|
+
return true;
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function createCloudflareKvDriver(namespace: KvNamespace): CacheDriver {
|
|
117
|
+
return new CloudflareKvDriver(namespace);
|
|
118
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ENV_SYMBOL, type Env, type ILogger, resolveBinding } from '@rx-ted/packages-core';
|
|
2
|
+
import { ComponentManager, resolvePluginLogger } from '@rx-ted/packages-honest';
|
|
3
|
+
import type { Application, IPlugin } from '@rx-ted/packages-honest';
|
|
4
|
+
import { createCloudflareKvDriver } from './driver';
|
|
5
|
+
import type { KvNamespace } from './driver';
|
|
6
|
+
import type { CacheDriver } from '../types';
|
|
7
|
+
import { CACHE_GLOBAL_KEY } from '../cache-service';
|
|
8
|
+
|
|
9
|
+
export const CLOUDFLARE_KV_CONTEXT_KEY = 'honest:cloudflare-kv';
|
|
10
|
+
|
|
11
|
+
export interface CloudflareKvPluginOptions {
|
|
12
|
+
namespace?: KvNamespace;
|
|
13
|
+
binding?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class CloudflareKvPlugin implements IPlugin {
|
|
17
|
+
static readonly globalKey = 'cloudflare-kv';
|
|
18
|
+
readonly name = 'cloudflare-kv-plugin';
|
|
19
|
+
readonly version = '1.0.0';
|
|
20
|
+
|
|
21
|
+
logger?: ILogger;
|
|
22
|
+
private driver?: CacheDriver;
|
|
23
|
+
|
|
24
|
+
constructor(private options: CloudflareKvPluginOptions) {}
|
|
25
|
+
|
|
26
|
+
async beforeModulesRegistered(app: Application): Promise<void> {
|
|
27
|
+
this.logger ??= resolvePluginLogger(this.name);
|
|
28
|
+
this.logger.info('Cloudflare KV: connecting...');
|
|
29
|
+
|
|
30
|
+
const appEnv = ComponentManager.hasPlugin(ENV_SYMBOL)
|
|
31
|
+
? ComponentManager.getPlugin<Env>(ENV_SYMBOL)
|
|
32
|
+
: undefined;
|
|
33
|
+
const ns = this.options.namespace ?? resolveBinding(this.options.binding ?? '', appEnv);
|
|
34
|
+
|
|
35
|
+
if (ns) {
|
|
36
|
+
this.driver = createCloudflareKvDriver(ns);
|
|
37
|
+
} else if (this.options.binding) {
|
|
38
|
+
this.driver = createLazyDriver(() => resolveBinding(this.options.binding!, appEnv));
|
|
39
|
+
this.logger.info(`Cloudflare KV: binding "${this.options.binding}" deferred`);
|
|
40
|
+
} else {
|
|
41
|
+
throw new Error('Cloudflare KV: namespace or binding option required');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
app.getContext().set(CLOUDFLARE_KV_CONTEXT_KEY, this.driver);
|
|
45
|
+
ComponentManager.registerPlugin(CACHE_GLOBAL_KEY, this.driver);
|
|
46
|
+
this.logger.info('Cloudflare KV: connected');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async close(): Promise<void> {
|
|
50
|
+
await this.driver?.close();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function createLazyDriver(resolve: () => any): CacheDriver {
|
|
55
|
+
let cached: CacheDriver | null = null;
|
|
56
|
+
const noop = () => Promise.resolve(null);
|
|
57
|
+
|
|
58
|
+
return new Proxy({} as CacheDriver, {
|
|
59
|
+
get(_, prop) {
|
|
60
|
+
if (!cached) {
|
|
61
|
+
const ns = resolve();
|
|
62
|
+
if (ns) cached = createCloudflareKvDriver(ns);
|
|
63
|
+
}
|
|
64
|
+
if (cached) {
|
|
65
|
+
const val = (cached as any)[prop];
|
|
66
|
+
return typeof val === 'function' ? val.bind(cached) : val;
|
|
67
|
+
}
|
|
68
|
+
return noop;
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import * as plugin from './index';
|
|
3
|
+
|
|
4
|
+
describe('plugin barrel exports', () => {
|
|
5
|
+
it('exports CACHE_GLOBAL_KEY', () => {
|
|
6
|
+
expect(plugin.CACHE_GLOBAL_KEY).toBe('app:cache');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('exports CacheService (class)', () => {
|
|
10
|
+
expect(plugin.CacheService).toBeDefined();
|
|
11
|
+
expect(typeof plugin.CacheService).toBe('function');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('exports types through namespaces', () => {
|
|
15
|
+
expect(typeof plugin.redis.createRedisDriver).toBe('function');
|
|
16
|
+
expect(typeof plugin.cloudflare.createCloudflareKvDriver).toBe('function');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('exports cacheable (function)', () => {
|
|
20
|
+
expect(plugin.cacheable).toBeDefined();
|
|
21
|
+
expect(typeof plugin.cacheable).toBe('function');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('redis exports', () => {
|
|
25
|
+
it('exports RedisPlugin', () => {
|
|
26
|
+
expect(plugin.RedisPlugin).toBeDefined();
|
|
27
|
+
expect(typeof plugin.RedisPlugin).toBe('function');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('exports redis namespace', () => {
|
|
31
|
+
expect(plugin.redis).toBeDefined();
|
|
32
|
+
expect(plugin.redis.RedisPlugin).toBeDefined();
|
|
33
|
+
expect(plugin.redis.createRedisDriver).toBeDefined();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('exports REDIS_CONTEXT_KEY and REDIS_GLOBAL_KEY', () => {
|
|
37
|
+
expect(plugin.REDIS_CONTEXT_KEY).toBe('honest:redis');
|
|
38
|
+
expect(plugin.REDIS_GLOBAL_KEY).toBe('redis');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('exports createRedisDriver (async function)', () => {
|
|
42
|
+
expect(plugin.createRedisDriver).toBeDefined();
|
|
43
|
+
expect(typeof plugin.createRedisDriver).toBe('function');
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('cloudflare exports', () => {
|
|
48
|
+
it('exports CloudflareKvPlugin', () => {
|
|
49
|
+
expect(plugin.CloudflareKvPlugin).toBeDefined();
|
|
50
|
+
expect(typeof plugin.CloudflareKvPlugin).toBe('function');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('exports cloudflare namespace', () => {
|
|
54
|
+
expect(plugin.cloudflare).toBeDefined();
|
|
55
|
+
expect(plugin.cloudflare.CloudflareKvPlugin).toBeDefined();
|
|
56
|
+
expect(plugin.cloudflare.createCloudflareKvDriver).toBeDefined();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('exports CLOUDFLARE_KV_CONTEXT_KEY', () => {
|
|
60
|
+
expect(plugin.CLOUDFLARE_KV_CONTEXT_KEY).toBe('honest:cloudflare-kv');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('exports createCloudflareKvDriver (function)', () => {
|
|
64
|
+
expect(plugin.createCloudflareKvDriver).toBeDefined();
|
|
65
|
+
expect(typeof plugin.createCloudflareKvDriver).toBe('function');
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Shared
|
|
2
|
+
export { CACHE_GLOBAL_KEY, CacheService } from './cache-service';
|
|
3
|
+
export type { CacheDriver } from './types';
|
|
4
|
+
export { cacheable } from './cacheable';
|
|
5
|
+
|
|
6
|
+
// Auto-detect cache plugin (recommended)
|
|
7
|
+
export { CachePlugin, findKvBinding, resolveBinding } from './resolve';
|
|
8
|
+
export type { CachePluginOptions } from './resolve';
|
|
9
|
+
|
|
10
|
+
// Redis driver
|
|
11
|
+
export * from './redis';
|
|
12
|
+
export * as redis from './redis';
|
|
13
|
+
|
|
14
|
+
// Cloudflare KV driver
|
|
15
|
+
export * from './cloudflare';
|
|
16
|
+
export * as cloudflare from './cloudflare';
|
|
17
|
+
|
|
18
|
+
// Local in-memory driver (dev)
|
|
19
|
+
export * from './local';
|
|
20
|
+
export * as local from './local';
|