@kb-labs/adapters 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursorrules +32 -0
- package/.github/workflows/ci.yml +13 -0
- package/.github/workflows/deploy.yml +28 -0
- package/.github/workflows/docker-build.yml +25 -0
- package/.github/workflows/drift-check.yml +10 -0
- package/.github/workflows/profiles-validate.yml +16 -0
- package/.github/workflows/release.yml +8 -0
- package/.kb/devkit/agents/devkit-maintainer/context.globs +15 -0
- package/.kb/devkit/agents/devkit-maintainer/permissions.yml +17 -0
- package/.kb/devkit/agents/devkit-maintainer/prompt.md +28 -0
- package/.kb/devkit/agents/devkit-maintainer/runbook.md +31 -0
- package/.kb/devkit/agents/docs-crafter/prompt.md +24 -0
- package/.kb/devkit/agents/docs-crafter/runbook.md +18 -0
- package/.kb/devkit/agents/release-manager/context.globs +7 -0
- package/.kb/devkit/agents/release-manager/prompt.md +27 -0
- package/.kb/devkit/agents/release-manager/runbook.md +17 -0
- package/.kb/devkit/agents/test-generator/context.globs +7 -0
- package/.kb/devkit/agents/test-generator/prompt.md +27 -0
- package/.kb/devkit/agents/test-generator/runbook.md +18 -0
- package/CONTRIBUTING.md +90 -0
- package/IMPLEMENTATION_COMPLETE.md +416 -0
- package/LICENSE +186 -0
- package/README-TEMPLATE.md +179 -0
- package/README.md +306 -0
- package/docs/DOCUMENTATION.md +74 -0
- package/docs/adr/0000-template.md +49 -0
- package/docs/adr/0001-architecture-and-repository-layout.md +33 -0
- package/docs/adr/0002-plugins-and-extensibility.md +46 -0
- package/docs/adr/0003-package-and-module-boundaries.md +37 -0
- package/docs/adr/0004-versioning-and-release-policy.md +38 -0
- package/docs/adr/0005-use-devkit-for-shared-tooling.md +48 -0
- package/docs/adr/0006-adopt-devkit-sync.md +47 -0
- package/docs/adr/0007-drift-kit-check.md +72 -0
- package/docs/adr/0008-devkit-sync-wrapper-strategy.md +67 -0
- package/docs/naming-convention.md +272 -0
- package/eslint.config.js +27 -0
- package/kb-labs.config.json +5 -0
- package/package.json +84 -0
- package/package.json.bin +25 -0
- package/package.json.lib +30 -0
- package/packages/adapters-analytics-duckdb/package.json +54 -0
- package/packages/adapters-analytics-duckdb/scripts/migrate-from-jsonl.mjs +253 -0
- package/packages/adapters-analytics-duckdb/src/index.ts +380 -0
- package/packages/adapters-analytics-duckdb/src/manifest.ts +36 -0
- package/packages/adapters-analytics-duckdb/src/schema.ts +161 -0
- package/packages/adapters-analytics-duckdb/tsconfig.build.json +15 -0
- package/packages/adapters-analytics-duckdb/tsconfig.json +9 -0
- package/packages/adapters-analytics-duckdb/tsup.config.ts +9 -0
- package/packages/adapters-analytics-file/README.md +32 -0
- package/packages/adapters-analytics-file/eslint.config.js +27 -0
- package/packages/adapters-analytics-file/package.json +50 -0
- package/packages/adapters-analytics-file/src/__tests__/daily-stats.spec.ts +287 -0
- package/packages/adapters-analytics-file/src/__tests__/scoped-analytics.test.ts +233 -0
- package/packages/adapters-analytics-file/src/index.test.ts +214 -0
- package/packages/adapters-analytics-file/src/index.ts +830 -0
- package/packages/adapters-analytics-file/src/manifest.ts +45 -0
- package/packages/adapters-analytics-file/tsconfig.build.json +15 -0
- package/packages/adapters-analytics-file/tsconfig.json +9 -0
- package/packages/adapters-analytics-file/tsup.config.ts +9 -0
- package/packages/adapters-analytics-sqlite/package.json +55 -0
- package/packages/adapters-analytics-sqlite/scripts/migrate-from-jsonl.mjs +194 -0
- package/packages/adapters-analytics-sqlite/src/index.ts +460 -0
- package/packages/adapters-analytics-sqlite/src/manifest.ts +41 -0
- package/packages/adapters-analytics-sqlite/tsconfig.build.json +15 -0
- package/packages/adapters-analytics-sqlite/tsconfig.json +9 -0
- package/packages/adapters-analytics-sqlite/tsup.config.ts +9 -0
- package/packages/adapters-environment-docker/README.md +28 -0
- package/packages/adapters-environment-docker/eslint.config.js +5 -0
- package/packages/adapters-environment-docker/package.json +49 -0
- package/packages/adapters-environment-docker/src/index.test.ts +138 -0
- package/packages/adapters-environment-docker/src/index.ts +439 -0
- package/packages/adapters-environment-docker/src/manifest.ts +65 -0
- package/packages/adapters-environment-docker/tsconfig.build.json +15 -0
- package/packages/adapters-environment-docker/tsconfig.json +16 -0
- package/packages/adapters-environment-docker/tsup.config.ts +9 -0
- package/packages/adapters-eventbus-cache/README.md +242 -0
- package/packages/adapters-eventbus-cache/eslint.config.js +27 -0
- package/packages/adapters-eventbus-cache/package.json +46 -0
- package/packages/adapters-eventbus-cache/src/index.test.ts +235 -0
- package/packages/adapters-eventbus-cache/src/index.ts +215 -0
- package/packages/adapters-eventbus-cache/src/manifest.ts +50 -0
- package/packages/adapters-eventbus-cache/src/types.ts +58 -0
- package/packages/adapters-eventbus-cache/tsconfig.build.json +15 -0
- package/packages/adapters-eventbus-cache/tsconfig.json +9 -0
- package/packages/adapters-eventbus-cache/tsup.config.ts +9 -0
- package/packages/adapters-fs/README.md +171 -0
- package/packages/adapters-fs/allowed.txt +1 -0
- package/packages/adapters-fs/conflict.txt +1 -0
- package/packages/adapters-fs/dest.txt +1 -0
- package/packages/adapters-fs/eslint.config.js +27 -0
- package/packages/adapters-fs/exists.txt +1 -0
- package/packages/adapters-fs/not-allowed.txt +1 -0
- package/packages/adapters-fs/other.txt +1 -0
- package/packages/adapters-fs/package.json +55 -0
- package/packages/adapters-fs/public/file1.txt +1 -0
- package/packages/adapters-fs/public/file2.txt +1 -0
- package/packages/adapters-fs/secret.txt +1 -0
- package/packages/adapters-fs/secrets/key.txt +1 -0
- package/packages/adapters-fs/src/index.test.ts +243 -0
- package/packages/adapters-fs/src/index.ts +258 -0
- package/packages/adapters-fs/src/manifest.ts +35 -0
- package/packages/adapters-fs/src/secure-storage.test.ts +380 -0
- package/packages/adapters-fs/src/secure-storage.ts +268 -0
- package/packages/adapters-fs/test.json +1 -0
- package/packages/adapters-fs/test.txt +1 -0
- package/packages/adapters-fs/test.xyz +1 -0
- package/packages/adapters-fs/test1.txt +1 -0
- package/packages/adapters-fs/test2.txt +1 -0
- package/packages/adapters-fs/tsconfig.build.json +15 -0
- package/packages/adapters-fs/tsconfig.json +9 -0
- package/packages/adapters-fs/tsup.config.ts +8 -0
- package/packages/adapters-fs/vitest.config.ts +19 -0
- package/packages/adapters-log-ringbuffer/README.md +228 -0
- package/packages/adapters-log-ringbuffer/eslint.config.js +27 -0
- package/packages/adapters-log-ringbuffer/package.json +47 -0
- package/packages/adapters-log-ringbuffer/src/__tests__/ring-buffer.test.ts +450 -0
- package/packages/adapters-log-ringbuffer/src/index.ts +212 -0
- package/packages/adapters-log-ringbuffer/src/manifest.ts +30 -0
- package/packages/adapters-log-ringbuffer/tsconfig.build.json +15 -0
- package/packages/adapters-log-ringbuffer/tsconfig.json +9 -0
- package/packages/adapters-log-ringbuffer/tsup.config.ts +9 -0
- package/packages/adapters-log-ringbuffer/vitest.config.ts +14 -0
- package/packages/adapters-log-sqlite/README.md +396 -0
- package/packages/adapters-log-sqlite/eslint.config.js +27 -0
- package/packages/adapters-log-sqlite/package.json +49 -0
- package/packages/adapters-log-sqlite/src/__tests__/log-persistence.test.ts +718 -0
- package/packages/adapters-log-sqlite/src/index.ts +1068 -0
- package/packages/adapters-log-sqlite/src/manifest.ts +36 -0
- package/packages/adapters-log-sqlite/src/schema.sql +46 -0
- package/packages/adapters-log-sqlite/tsconfig.build.json +15 -0
- package/packages/adapters-log-sqlite/tsconfig.json +9 -0
- package/packages/adapters-log-sqlite/tsup.config.ts +9 -0
- package/packages/adapters-log-sqlite/vitest.config.ts +15 -0
- package/packages/adapters-mongodb/README.md +147 -0
- package/packages/adapters-mongodb/eslint.config.js +27 -0
- package/packages/adapters-mongodb/package.json +53 -0
- package/packages/adapters-mongodb/src/index.ts +428 -0
- package/packages/adapters-mongodb/src/manifest.ts +45 -0
- package/packages/adapters-mongodb/src/secure-document.ts +231 -0
- package/packages/adapters-mongodb/tsconfig.build.json +15 -0
- package/packages/adapters-mongodb/tsconfig.json +9 -0
- package/packages/adapters-mongodb/tsup.config.ts +8 -0
- package/packages/adapters-openai/README.md +151 -0
- package/packages/adapters-openai/embeddings.ts +37 -0
- package/packages/adapters-openai/eslint.config.js +26 -0
- package/packages/adapters-openai/index.ts +22 -0
- package/packages/adapters-openai/package.json +57 -0
- package/packages/adapters-openai/src/embeddings-manifest.ts +45 -0
- package/packages/adapters-openai/src/embeddings.ts +104 -0
- package/packages/adapters-openai/src/index.ts +13 -0
- package/packages/adapters-openai/src/llm.ts +304 -0
- package/packages/adapters-openai/src/manifest.ts +47 -0
- package/packages/adapters-openai/tsconfig.build.json +15 -0
- package/packages/adapters-openai/tsconfig.json +9 -0
- package/packages/adapters-openai/tsup.config.ts +8 -0
- package/packages/adapters-pino/README.md +152 -0
- package/packages/adapters-pino/eslint.config.js +27 -0
- package/packages/adapters-pino/package.json +49 -0
- package/packages/adapters-pino/src/index.test.ts +44 -0
- package/packages/adapters-pino/src/index.ts +322 -0
- package/packages/adapters-pino/src/log-ring-buffer.ts +142 -0
- package/packages/adapters-pino/src/manifest.ts +49 -0
- package/packages/adapters-pino/tsconfig.build.json +15 -0
- package/packages/adapters-pino/tsconfig.json +9 -0
- package/packages/adapters-pino/tsup.config.ts +9 -0
- package/packages/adapters-pino-http/README.md +141 -0
- package/packages/adapters-pino-http/eslint.config.js +27 -0
- package/packages/adapters-pino-http/package.json +46 -0
- package/packages/adapters-pino-http/src/index.ts +229 -0
- package/packages/adapters-pino-http/tsconfig.build.json +15 -0
- package/packages/adapters-pino-http/tsconfig.json +9 -0
- package/packages/adapters-pino-http/tsup.config.ts +9 -0
- package/packages/adapters-qdrant/README.md +166 -0
- package/packages/adapters-qdrant/eslint.config.js +27 -0
- package/packages/adapters-qdrant/package.json +49 -0
- package/packages/adapters-qdrant/src/index.ts +490 -0
- package/packages/adapters-qdrant/src/manifest.ts +54 -0
- package/packages/adapters-qdrant/src/retry.ts +204 -0
- package/packages/adapters-qdrant/tsconfig.build.json +15 -0
- package/packages/adapters-qdrant/tsconfig.json +9 -0
- package/packages/adapters-qdrant/tsup.config.ts +9 -0
- package/packages/adapters-redis/README.md +159 -0
- package/packages/adapters-redis/eslint.config.js +27 -0
- package/packages/adapters-redis/package.json +49 -0
- package/packages/adapters-redis/src/index.ts +164 -0
- package/packages/adapters-redis/src/manifest.ts +49 -0
- package/packages/adapters-redis/tsconfig.build.json +15 -0
- package/packages/adapters-redis/tsconfig.json +9 -0
- package/packages/adapters-redis/tsup.config.ts +9 -0
- package/packages/adapters-snapshot-localfs/README.md +10 -0
- package/packages/adapters-snapshot-localfs/eslint.config.js +2 -0
- package/packages/adapters-snapshot-localfs/package.json +46 -0
- package/packages/adapters-snapshot-localfs/src/index.test.ts +40 -0
- package/packages/adapters-snapshot-localfs/src/index.ts +292 -0
- package/packages/adapters-snapshot-localfs/src/manifest.ts +32 -0
- package/packages/adapters-snapshot-localfs/tsconfig.build.json +15 -0
- package/packages/adapters-snapshot-localfs/tsconfig.json +16 -0
- package/packages/adapters-snapshot-localfs/tsup.config.ts +11 -0
- package/packages/adapters-sqlite/README.md +163 -0
- package/packages/adapters-sqlite/eslint.config.js +27 -0
- package/packages/adapters-sqlite/package.json +54 -0
- package/packages/adapters-sqlite/src/index.test.ts +245 -0
- package/packages/adapters-sqlite/src/index.ts +382 -0
- package/packages/adapters-sqlite/src/manifest.ts +47 -0
- package/packages/adapters-sqlite/src/secure-sql.test.ts +290 -0
- package/packages/adapters-sqlite/src/secure-sql.ts +281 -0
- package/packages/adapters-sqlite/tsconfig.build.json +15 -0
- package/packages/adapters-sqlite/tsconfig.json +9 -0
- package/packages/adapters-sqlite/tsup.config.ts +8 -0
- package/packages/adapters-sqlite/vitest.config.ts +19 -0
- package/packages/adapters-transport/README.md +170 -0
- package/packages/adapters-transport/eslint.config.js +27 -0
- package/packages/adapters-transport/package.json +49 -0
- package/packages/adapters-transport/src/__tests__/unix-socket-server.test.ts +550 -0
- package/packages/adapters-transport/src/index.ts +101 -0
- package/packages/adapters-transport/src/ipc-transport.ts +228 -0
- package/packages/adapters-transport/src/transport.ts +224 -0
- package/packages/adapters-transport/src/types.ts +92 -0
- package/packages/adapters-transport/src/unix-socket-server.ts +193 -0
- package/packages/adapters-transport/src/unix-socket-transport.ts +280 -0
- package/packages/adapters-transport/tsconfig.build.json +15 -0
- package/packages/adapters-transport/tsconfig.json +9 -0
- package/packages/adapters-transport/tsup.config.ts +9 -0
- package/packages/adapters-vibeproxy/README.md +159 -0
- package/packages/adapters-vibeproxy/eslint.config.js +27 -0
- package/packages/adapters-vibeproxy/package.json +51 -0
- package/packages/adapters-vibeproxy/src/index.ts +13 -0
- package/packages/adapters-vibeproxy/src/llm.ts +437 -0
- package/packages/adapters-vibeproxy/src/manifest.ts +51 -0
- package/packages/adapters-vibeproxy/tsconfig.build.json +15 -0
- package/packages/adapters-vibeproxy/tsconfig.json +9 -0
- package/packages/adapters-vibeproxy/tsup.config.ts +8 -0
- package/packages/adapters-workspace-agent/package.json +46 -0
- package/packages/adapters-workspace-agent/src/__tests__/adapter.test.ts +212 -0
- package/packages/adapters-workspace-agent/src/index.ts +220 -0
- package/packages/adapters-workspace-agent/src/manifest.ts +36 -0
- package/packages/adapters-workspace-agent/tsconfig.build.json +15 -0
- package/packages/adapters-workspace-agent/tsconfig.json +16 -0
- package/packages/adapters-workspace-agent/tsup.config.ts +11 -0
- package/packages/adapters-workspace-localfs/README.md +9 -0
- package/packages/adapters-workspace-localfs/eslint.config.js +2 -0
- package/packages/adapters-workspace-localfs/package.json +46 -0
- package/packages/adapters-workspace-localfs/src/index.test.ts +27 -0
- package/packages/adapters-workspace-localfs/src/index.ts +172 -0
- package/packages/adapters-workspace-localfs/src/manifest.ts +32 -0
- package/packages/adapters-workspace-localfs/tsconfig.build.json +15 -0
- package/packages/adapters-workspace-localfs/tsconfig.json +16 -0
- package/packages/adapters-workspace-localfs/tsup.config.ts +11 -0
- package/packages/adapters-workspace-worktree/README.md +9 -0
- package/packages/adapters-workspace-worktree/eslint.config.js +2 -0
- package/packages/adapters-workspace-worktree/package.json +46 -0
- package/packages/adapters-workspace-worktree/src/index.test.ts +38 -0
- package/packages/adapters-workspace-worktree/src/index.ts +245 -0
- package/packages/adapters-workspace-worktree/src/manifest.ts +38 -0
- package/packages/adapters-workspace-worktree/tsconfig.build.json +15 -0
- package/packages/adapters-workspace-worktree/tsconfig.json +16 -0
- package/packages/adapters-workspace-worktree/tsup.config.ts +11 -0
- package/pnpm-workspace.yaml +2800 -0
- package/prettierrc.json +1 -0
- package/scripts/devkit-sync.mjs +37 -0
- package/scripts/hooks/post-push +9 -0
- package/scripts/hooks/pre-commit +9 -0
- package/scripts/hooks/pre-push +9 -0
- package/test-integration.ts +242 -0
- package/test.txt +1 -0
- package/tsconfig.base.json +6 -0
- package/tsconfig.build.json +15 -0
- package/tsconfig.json +9 -0
- package/tsconfig.paths.json +26 -0
- package/tsconfig.tools.json +17 -0
- package/tsup.config.bin.ts +34 -0
- package/tsup.config.cli.ts +41 -0
- package/tsup.config.dual.ts +46 -0
- package/tsup.config.ts +36 -0
- package/tsup.external.json +103 -0
- package/vitest.config.ts +2 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/adapters-qdrant/retry
|
|
3
|
+
* Exponential-backoff retry logic for transient Qdrant errors.
|
|
4
|
+
*
|
|
5
|
+
* Retryable error codes / status codes:
|
|
6
|
+
* - ECONNREFUSED – Qdrant not yet reachable (startup, restart)
|
|
7
|
+
* - ETIMEDOUT – network or request timeout
|
|
8
|
+
* - ECONNRESET – connection reset by peer (proxy / load-balancer glitch)
|
|
9
|
+
* - ENOTFOUND – DNS hiccup (transient in cloud environments)
|
|
10
|
+
* - HTTP 503 – Service Unavailable (Qdrant overloaded / restarting)
|
|
11
|
+
* - HTTP 429 – Too Many Requests (rate-limited; also worth retrying)
|
|
12
|
+
* - HTTP 502 / 504 – upstream gateway errors (common behind a proxy)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Options for {@link withRetry}. */
|
|
16
|
+
export interface RetryOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Maximum number of *attempts* (first call + retries).
|
|
19
|
+
* @default 4
|
|
20
|
+
*/
|
|
21
|
+
maxAttempts?: number;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Base delay in milliseconds between attempts.
|
|
25
|
+
* Actual delay = `baseDelayMs * 2^(attempt - 1)` + jitter.
|
|
26
|
+
* @default 200
|
|
27
|
+
*/
|
|
28
|
+
baseDelayMs?: number;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Maximum delay cap in milliseconds (prevents runaway back-off).
|
|
32
|
+
* @default 10_000
|
|
33
|
+
*/
|
|
34
|
+
maxDelayMs?: number;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Extra predicate called after built-in checks.
|
|
38
|
+
* Return `true` to treat the error as retryable.
|
|
39
|
+
*/
|
|
40
|
+
isRetryable?: (error: unknown) => boolean;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Optional callback invoked before each retry sleep.
|
|
44
|
+
* Useful for logging / metrics.
|
|
45
|
+
*/
|
|
46
|
+
onRetry?: (error: unknown, attempt: number, delayMs: number) => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** @internal The set of retryable Node.js `syscall` / `code` strings. */
|
|
50
|
+
const RETRYABLE_CODES = new Set([
|
|
51
|
+
"ECONNREFUSED",
|
|
52
|
+
"ETIMEDOUT",
|
|
53
|
+
"ECONNRESET",
|
|
54
|
+
"ENOTFOUND",
|
|
55
|
+
"EPIPE",
|
|
56
|
+
"ECONNABORTED",
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
/** @internal HTTP status codes that are safe to retry. */
|
|
60
|
+
const RETRYABLE_HTTP_STATUSES = new Set([429, 502, 503, 504]);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Determine whether `error` represents a transient, retryable failure.
|
|
64
|
+
*
|
|
65
|
+
* Checks:
|
|
66
|
+
* 1. Node.js network error codes (`error.code`)
|
|
67
|
+
* 2. HTTP response status (`error.status`, `error.statusCode`,
|
|
68
|
+
* `error.response?.status`)
|
|
69
|
+
* 3. Error message substrings as a last resort
|
|
70
|
+
*/
|
|
71
|
+
export function isTransientError(error: unknown): boolean {
|
|
72
|
+
if (!(error instanceof Error)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 1. Node.js network error codes
|
|
77
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
78
|
+
if (code && RETRYABLE_CODES.has(code)) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 2. HTTP status codes (Qdrant JS client surfaces these in different ways)
|
|
83
|
+
const anyErr = error as unknown as Record<string, unknown>;
|
|
84
|
+
|
|
85
|
+
const status =
|
|
86
|
+
(typeof anyErr["status"] === "number" ? anyErr["status"] : undefined) ??
|
|
87
|
+
(typeof anyErr["statusCode"] === "number"
|
|
88
|
+
? anyErr["statusCode"]
|
|
89
|
+
: undefined) ??
|
|
90
|
+
(typeof (anyErr["response"] as Record<string, unknown> | undefined)?.[
|
|
91
|
+
"status"
|
|
92
|
+
] === "number"
|
|
93
|
+
? ((anyErr["response"] as Record<string, unknown>)["status"] as number)
|
|
94
|
+
: undefined);
|
|
95
|
+
|
|
96
|
+
if (typeof status === "number" && RETRYABLE_HTTP_STATUSES.has(status)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 3. Message substrings (fallback for environments that don't expose codes)
|
|
101
|
+
const msg = error.message.toLowerCase();
|
|
102
|
+
if (
|
|
103
|
+
msg.includes("econnrefused") ||
|
|
104
|
+
msg.includes("etimedout") ||
|
|
105
|
+
msg.includes("econnreset") ||
|
|
106
|
+
msg.includes("service unavailable") ||
|
|
107
|
+
msg.includes("503")
|
|
108
|
+
) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Sleep for `ms` milliseconds.
|
|
117
|
+
* @internal
|
|
118
|
+
*/
|
|
119
|
+
function sleep(ms: number): Promise<void> {
|
|
120
|
+
return new Promise((resolve) => { setTimeout(resolve, ms); });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Compute the delay before the next attempt using full-jitter exponential
|
|
125
|
+
* back-off: `random(0, min(cap, base * 2^attempt))`.
|
|
126
|
+
* @internal
|
|
127
|
+
*/
|
|
128
|
+
function computeDelay(
|
|
129
|
+
attempt: number,
|
|
130
|
+
baseDelayMs: number,
|
|
131
|
+
maxDelayMs: number,
|
|
132
|
+
): number {
|
|
133
|
+
// attempt is 0-indexed here (0 = first retry after the initial failure)
|
|
134
|
+
const exponential = baseDelayMs * Math.pow(2, attempt);
|
|
135
|
+
const capped = Math.min(exponential, maxDelayMs);
|
|
136
|
+
// Full jitter: uniform in [0, capped]
|
|
137
|
+
return Math.floor(Math.random() * capped);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Wrap an async operation with exponential-backoff retry on transient errors.
|
|
142
|
+
*
|
|
143
|
+
* Only errors identified as transient (ECONNREFUSED, ETIMEDOUT, 503, etc.)
|
|
144
|
+
* are retried. All other errors are re-thrown immediately.
|
|
145
|
+
*
|
|
146
|
+
* @param fn The async operation to execute.
|
|
147
|
+
* @param options Retry configuration.
|
|
148
|
+
* @returns The resolved value of `fn`.
|
|
149
|
+
* @throws The last error if all attempts are exhausted, or the
|
|
150
|
+
* first non-transient error encountered.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* const results = await withRetry(
|
|
155
|
+
* () => this.client.search(this.collectionName, params),
|
|
156
|
+
* { maxAttempts: 4, baseDelayMs: 200, maxDelayMs: 10_000 }
|
|
157
|
+
* );
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
export async function withRetry<T>(
|
|
161
|
+
fn: () => Promise<T>,
|
|
162
|
+
options: RetryOptions = {},
|
|
163
|
+
): Promise<T> {
|
|
164
|
+
const maxAttempts = options.maxAttempts ?? 4;
|
|
165
|
+
const baseDelayMs = options.baseDelayMs ?? 200;
|
|
166
|
+
const maxDelayMs = options.maxDelayMs ?? 10_000;
|
|
167
|
+
const { isRetryable: extraIsRetryable, onRetry } = options;
|
|
168
|
+
|
|
169
|
+
if (maxAttempts < 1) {
|
|
170
|
+
throw new RangeError(`maxAttempts must be >= 1, got ${maxAttempts}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let lastError: unknown;
|
|
174
|
+
|
|
175
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
176
|
+
try {
|
|
177
|
+
return await fn();
|
|
178
|
+
} catch (error) {
|
|
179
|
+
lastError = error;
|
|
180
|
+
|
|
181
|
+
const retryable =
|
|
182
|
+
isTransientError(error) || (extraIsRetryable?.(error) ?? false);
|
|
183
|
+
|
|
184
|
+
// Non-transient error — rethrow immediately without retrying
|
|
185
|
+
if (!retryable) {
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Last attempt exhausted — rethrow
|
|
190
|
+
if (attempt >= maxAttempts) {
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Compute backoff delay (attempt - 1 = retry index)
|
|
195
|
+
const delayMs = computeDelay(attempt - 1, baseDelayMs, maxDelayMs);
|
|
196
|
+
|
|
197
|
+
onRetry?.(error, attempt, delayMs);
|
|
198
|
+
|
|
199
|
+
await sleep(delayMs);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
throw lastError;
|
|
204
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# @kb-labs/adapters-redis
|
|
2
|
+
|
|
3
|
+
> Part of [KB Labs](https://github.com/KirillBaranov/kb-labs) ecosystem. Works exclusively within KB Labs platform.
|
|
4
|
+
|
|
5
|
+
High-performance distributed cache adapter using Redis with TTL, patterns, and atomic operations.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
| Property | Value |
|
|
10
|
+
|----------|-------|
|
|
11
|
+
| **Implements** | `ICache` |
|
|
12
|
+
| **Type** | `core` |
|
|
13
|
+
| **Requires** | None |
|
|
14
|
+
| **Category** | Cache |
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **TTL Support** - Automatic key expiration
|
|
19
|
+
- **Pattern Operations** - Scan and delete by pattern
|
|
20
|
+
- **Atomic Operations** - Increment, decrement, compare-and-set
|
|
21
|
+
- **Key Prefixing** - Namespace isolation
|
|
22
|
+
- **Connection Pooling** - Efficient connection management
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pnpm add @kb-labs/adapters-redis
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Configuration
|
|
31
|
+
|
|
32
|
+
Add to your `kb.config.json`:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"platform": {
|
|
37
|
+
"adapters": {
|
|
38
|
+
"cache": "@kb-labs/adapters-redis"
|
|
39
|
+
},
|
|
40
|
+
"adapterOptions": {
|
|
41
|
+
"cache": {
|
|
42
|
+
"host": "localhost",
|
|
43
|
+
"port": 6379,
|
|
44
|
+
"keyPrefix": "kb:"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Options
|
|
52
|
+
|
|
53
|
+
| Option | Type | Default | Description |
|
|
54
|
+
|--------|------|---------|-------------|
|
|
55
|
+
| `host` | `string` | `"localhost"` | Redis server host |
|
|
56
|
+
| `port` | `number` | `6379` | Redis server port |
|
|
57
|
+
| `keyPrefix` | `string` | `"kb:"` | Prefix for all cache keys |
|
|
58
|
+
| `password` | `string` | - | Redis password (optional) |
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
### Via Platform (Recommended)
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { usePlatform } from '@kb-labs/sdk';
|
|
66
|
+
|
|
67
|
+
const platform = usePlatform();
|
|
68
|
+
|
|
69
|
+
// Set with TTL (60 seconds)
|
|
70
|
+
await platform.cache.set('user:123', { name: 'John' }, 60000);
|
|
71
|
+
|
|
72
|
+
// Get
|
|
73
|
+
const user = await platform.cache.get<User>('user:123');
|
|
74
|
+
|
|
75
|
+
// Delete
|
|
76
|
+
await platform.cache.delete('user:123');
|
|
77
|
+
|
|
78
|
+
// Clear all with pattern
|
|
79
|
+
await platform.cache.deletePattern('user:*');
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Standalone (Testing/Development)
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { createAdapter } from '@kb-labs/adapters-redis';
|
|
86
|
+
|
|
87
|
+
const cache = createAdapter({
|
|
88
|
+
host: 'localhost',
|
|
89
|
+
port: 6379,
|
|
90
|
+
keyPrefix: 'test:'
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await cache.set('key', 'value', 60000);
|
|
94
|
+
const value = await cache.get('key');
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Adapter Manifest
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
{
|
|
101
|
+
id: 'redis-cache',
|
|
102
|
+
name: 'Redis Cache',
|
|
103
|
+
version: '1.0.0',
|
|
104
|
+
implements: 'ICache',
|
|
105
|
+
capabilities: {
|
|
106
|
+
custom: {
|
|
107
|
+
ttl: true,
|
|
108
|
+
patterns: true,
|
|
109
|
+
atomic: true,
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## FAQ
|
|
116
|
+
|
|
117
|
+
<details>
|
|
118
|
+
<summary><strong>Q: How do I connect to Redis with authentication?</strong></summary>
|
|
119
|
+
|
|
120
|
+
Add password to config:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"adapterOptions": {
|
|
125
|
+
"cache": {
|
|
126
|
+
"host": "redis.example.com",
|
|
127
|
+
"port": 6379,
|
|
128
|
+
"password": "your-password"
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
</details>
|
|
134
|
+
|
|
135
|
+
<details>
|
|
136
|
+
<summary><strong>Q: How do I use Redis Cluster?</strong></summary>
|
|
137
|
+
|
|
138
|
+
For cluster mode, use connection string format:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"adapterOptions": {
|
|
143
|
+
"cache": {
|
|
144
|
+
"url": "redis://node1:6379,node2:6379,node3:6379"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
</details>
|
|
150
|
+
|
|
151
|
+
## Related Adapters
|
|
152
|
+
|
|
153
|
+
| Adapter | Use Case |
|
|
154
|
+
|---------|----------|
|
|
155
|
+
| `@kb-labs/adapters-eventbus-cache` | Event bus using cache backend |
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
[KB Public License v1.1](../../LICENSE) - KB Labs Team
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard ESLint configuration template
|
|
3
|
+
*
|
|
4
|
+
* This is the canonical template for all @kb-labs packages.
|
|
5
|
+
* DO NOT modify this file locally - it is synced from @kb-labs/devkit
|
|
6
|
+
*
|
|
7
|
+
* Customization guidelines:
|
|
8
|
+
* - DevKit preset already includes all standard ignores
|
|
9
|
+
* - Only add project-specific ignores if absolutely necessary
|
|
10
|
+
* - Document why custom ignores are needed
|
|
11
|
+
*
|
|
12
|
+
* @see https://github.com/kb-labs/devkit#eslint-configuration
|
|
13
|
+
*/
|
|
14
|
+
import nodePreset from '@kb-labs/devkit/eslint/node.js';
|
|
15
|
+
|
|
16
|
+
export default [
|
|
17
|
+
...nodePreset,
|
|
18
|
+
|
|
19
|
+
// OPTIONAL: Add project-specific ignores only if needed
|
|
20
|
+
// DevKit preset already ignores: dist/, coverage/, node_modules/, *.d.ts, scripts/, etc.
|
|
21
|
+
// {
|
|
22
|
+
// ignores: [
|
|
23
|
+
// // Add ONLY project-specific patterns here
|
|
24
|
+
// // Example: '**/*.generated.ts',
|
|
25
|
+
// ]
|
|
26
|
+
// }
|
|
27
|
+
];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kb-labs/adapters-redis",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Redis adapter implementing ICache interface",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"scripts": {
|
|
20
|
+
"clean": "rimraf dist",
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"type-check": "tsc --noEmit",
|
|
24
|
+
"test": "vitest run --passWithNoTests",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"lint": "eslint src --ext .ts",
|
|
27
|
+
"lint:fix": "eslint . --fix"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"ioredis": "^5.4.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@kb-labs/core-platform": "*"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@kb-labs/core-platform": "link:../../../../platform/kb-labs-core/packages/core-platform",
|
|
37
|
+
"@types/node": "^24.3.3",
|
|
38
|
+
"eslint": "^9",
|
|
39
|
+
"tsup": "^8.5.0",
|
|
40
|
+
"typescript": "^5.6.3",
|
|
41
|
+
"vitest": "^3.2.4",
|
|
42
|
+
"@kb-labs/devkit": "link:../../../kb-labs-devkit",
|
|
43
|
+
"rimraf": "^6.0.1"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=20.0.0",
|
|
47
|
+
"pnpm": ">=9.0.0"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/adapters-redis
|
|
3
|
+
* Redis adapter implementing ICache interface.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* import { createAdapter } from '@kb-labs/adapters-redis';
|
|
8
|
+
*
|
|
9
|
+
* const cache = createAdapter({
|
|
10
|
+
* host: 'localhost',
|
|
11
|
+
* port: 6379,
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* await cache.set('user:123', { name: 'Alice' }, 60000); // TTL 60s
|
|
15
|
+
* const user = await cache.get('user:123');
|
|
16
|
+
* await cache.delete('user:123');
|
|
17
|
+
* await cache.clear('user:*');
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import Redis, { type RedisOptions } from "ioredis";
|
|
22
|
+
import type { ICache } from "@kb-labs/core-platform";
|
|
23
|
+
|
|
24
|
+
// Re-export manifest
|
|
25
|
+
export { manifest } from "./manifest.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Configuration for Redis cache adapter.
|
|
29
|
+
*/
|
|
30
|
+
export interface RedisCacheConfig extends RedisOptions {
|
|
31
|
+
/** Redis host (default: 'localhost') */
|
|
32
|
+
host?: string;
|
|
33
|
+
/** Redis port (default: 6379) */
|
|
34
|
+
port?: number;
|
|
35
|
+
/** Key prefix for all cache keys (default: 'kb:') */
|
|
36
|
+
keyPrefix?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Redis implementation of ICache interface.
|
|
41
|
+
*/
|
|
42
|
+
export class RedisCacheAdapter implements ICache {
|
|
43
|
+
private client: Redis;
|
|
44
|
+
private keyPrefix: string;
|
|
45
|
+
|
|
46
|
+
constructor(config: RedisCacheConfig = {}) {
|
|
47
|
+
this.keyPrefix = config.keyPrefix ?? "kb:";
|
|
48
|
+
|
|
49
|
+
this.client = new Redis({
|
|
50
|
+
host: config.host ?? "localhost",
|
|
51
|
+
port: config.port ?? 6379,
|
|
52
|
+
...config,
|
|
53
|
+
keyPrefix: this.keyPrefix,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async get<T>(key: string): Promise<T | null> {
|
|
58
|
+
const value = await this.client.get(key);
|
|
59
|
+
if (value === null) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(value) as T;
|
|
65
|
+
} catch {
|
|
66
|
+
// If not JSON, return as-is (cast to T)
|
|
67
|
+
return value as T;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
|
|
72
|
+
const serialized = JSON.stringify(value);
|
|
73
|
+
|
|
74
|
+
if (ttl !== undefined) {
|
|
75
|
+
// TTL in milliseconds, Redis uses seconds
|
|
76
|
+
await this.client.setex(key, Math.ceil(ttl / 1000), serialized);
|
|
77
|
+
} else {
|
|
78
|
+
await this.client.set(key, serialized);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async delete(key: string): Promise<void> {
|
|
83
|
+
await this.client.del(key);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async clear(pattern?: string): Promise<void> {
|
|
87
|
+
if (!pattern) {
|
|
88
|
+
// Clear all keys with our prefix
|
|
89
|
+
await this.client.flushdb();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Find keys matching pattern
|
|
94
|
+
const fullPattern = `${this.keyPrefix}${pattern}`;
|
|
95
|
+
const keys = await this.client.keys(fullPattern);
|
|
96
|
+
|
|
97
|
+
if (keys.length > 0) {
|
|
98
|
+
// Remove prefix before deleting (ioredis adds it automatically)
|
|
99
|
+
const keysWithoutPrefix = keys.map((k) => k.slice(this.keyPrefix.length));
|
|
100
|
+
await this.client.del(...keysWithoutPrefix);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
105
|
+
// Sorted Set Operations
|
|
106
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
107
|
+
|
|
108
|
+
async zadd(key: string, score: number, member: string): Promise<void> {
|
|
109
|
+
await this.client.zadd(key, score, member);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async zrangebyscore(
|
|
113
|
+
key: string,
|
|
114
|
+
min: number,
|
|
115
|
+
max: number,
|
|
116
|
+
): Promise<string[]> {
|
|
117
|
+
return this.client.zrangebyscore(key, min, max);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async zrem(key: string, member: string): Promise<void> {
|
|
121
|
+
await this.client.zrem(key, member);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
125
|
+
// Atomic Operations
|
|
126
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
127
|
+
|
|
128
|
+
async setIfNotExists<T>(
|
|
129
|
+
key: string,
|
|
130
|
+
value: T,
|
|
131
|
+
ttl?: number,
|
|
132
|
+
): Promise<boolean> {
|
|
133
|
+
const serialized = JSON.stringify(value);
|
|
134
|
+
|
|
135
|
+
if (ttl !== undefined) {
|
|
136
|
+
// SET key value PX milliseconds NX
|
|
137
|
+
const result = await this.client.set(key, serialized, "PX", ttl, "NX");
|
|
138
|
+
return result === "OK";
|
|
139
|
+
} else {
|
|
140
|
+
// SET key value NX (no TTL)
|
|
141
|
+
const result = await this.client.set(key, serialized, "NX");
|
|
142
|
+
return result === "OK";
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Close Redis connection.
|
|
148
|
+
* Call this on app shutdown.
|
|
149
|
+
*/
|
|
150
|
+
async disconnect(): Promise<void> {
|
|
151
|
+
await this.client.quit();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Create Redis cache adapter.
|
|
157
|
+
* This is the factory function called by initPlatform() when loading adapters.
|
|
158
|
+
*/
|
|
159
|
+
export function createAdapter(config?: RedisCacheConfig): RedisCacheAdapter {
|
|
160
|
+
return new RedisCacheAdapter(config);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Default export for direct import
|
|
164
|
+
export default createAdapter;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/adapters-redis/manifest
|
|
3
|
+
* Adapter manifest for Redis cache.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { AdapterManifest } from "@kb-labs/core-platform";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Adapter manifest for Redis cache.
|
|
10
|
+
*/
|
|
11
|
+
export const manifest: AdapterManifest = {
|
|
12
|
+
manifestVersion: "1.0.0",
|
|
13
|
+
id: "redis-cache",
|
|
14
|
+
name: "Redis Cache",
|
|
15
|
+
version: "1.0.0",
|
|
16
|
+
description: "High-performance distributed cache using Redis",
|
|
17
|
+
author: "KB Labs Team",
|
|
18
|
+
license: "KBPL-1.1",
|
|
19
|
+
type: "core",
|
|
20
|
+
implements: "ICache",
|
|
21
|
+
capabilities: {
|
|
22
|
+
custom: {
|
|
23
|
+
ttl: true,
|
|
24
|
+
patterns: true,
|
|
25
|
+
atomic: true,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
configSchema: {
|
|
29
|
+
host: {
|
|
30
|
+
type: "string",
|
|
31
|
+
default: "localhost",
|
|
32
|
+
description: "Redis server host",
|
|
33
|
+
},
|
|
34
|
+
port: {
|
|
35
|
+
type: "number",
|
|
36
|
+
default: 6379,
|
|
37
|
+
description: "Redis server port",
|
|
38
|
+
},
|
|
39
|
+
keyPrefix: {
|
|
40
|
+
type: "string",
|
|
41
|
+
default: "kb:",
|
|
42
|
+
description: "Prefix for all cache keys",
|
|
43
|
+
},
|
|
44
|
+
password: {
|
|
45
|
+
type: "string",
|
|
46
|
+
description: "Redis password (optional)",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|