@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.
Files changed (276) hide show
  1. package/.cursorrules +32 -0
  2. package/.github/workflows/ci.yml +13 -0
  3. package/.github/workflows/deploy.yml +28 -0
  4. package/.github/workflows/docker-build.yml +25 -0
  5. package/.github/workflows/drift-check.yml +10 -0
  6. package/.github/workflows/profiles-validate.yml +16 -0
  7. package/.github/workflows/release.yml +8 -0
  8. package/.kb/devkit/agents/devkit-maintainer/context.globs +15 -0
  9. package/.kb/devkit/agents/devkit-maintainer/permissions.yml +17 -0
  10. package/.kb/devkit/agents/devkit-maintainer/prompt.md +28 -0
  11. package/.kb/devkit/agents/devkit-maintainer/runbook.md +31 -0
  12. package/.kb/devkit/agents/docs-crafter/prompt.md +24 -0
  13. package/.kb/devkit/agents/docs-crafter/runbook.md +18 -0
  14. package/.kb/devkit/agents/release-manager/context.globs +7 -0
  15. package/.kb/devkit/agents/release-manager/prompt.md +27 -0
  16. package/.kb/devkit/agents/release-manager/runbook.md +17 -0
  17. package/.kb/devkit/agents/test-generator/context.globs +7 -0
  18. package/.kb/devkit/agents/test-generator/prompt.md +27 -0
  19. package/.kb/devkit/agents/test-generator/runbook.md +18 -0
  20. package/CONTRIBUTING.md +90 -0
  21. package/IMPLEMENTATION_COMPLETE.md +416 -0
  22. package/LICENSE +186 -0
  23. package/README-TEMPLATE.md +179 -0
  24. package/README.md +306 -0
  25. package/docs/DOCUMENTATION.md +74 -0
  26. package/docs/adr/0000-template.md +49 -0
  27. package/docs/adr/0001-architecture-and-repository-layout.md +33 -0
  28. package/docs/adr/0002-plugins-and-extensibility.md +46 -0
  29. package/docs/adr/0003-package-and-module-boundaries.md +37 -0
  30. package/docs/adr/0004-versioning-and-release-policy.md +38 -0
  31. package/docs/adr/0005-use-devkit-for-shared-tooling.md +48 -0
  32. package/docs/adr/0006-adopt-devkit-sync.md +47 -0
  33. package/docs/adr/0007-drift-kit-check.md +72 -0
  34. package/docs/adr/0008-devkit-sync-wrapper-strategy.md +67 -0
  35. package/docs/naming-convention.md +272 -0
  36. package/eslint.config.js +27 -0
  37. package/kb-labs.config.json +5 -0
  38. package/package.json +84 -0
  39. package/package.json.bin +25 -0
  40. package/package.json.lib +30 -0
  41. package/packages/adapters-analytics-duckdb/package.json +54 -0
  42. package/packages/adapters-analytics-duckdb/scripts/migrate-from-jsonl.mjs +253 -0
  43. package/packages/adapters-analytics-duckdb/src/index.ts +380 -0
  44. package/packages/adapters-analytics-duckdb/src/manifest.ts +36 -0
  45. package/packages/adapters-analytics-duckdb/src/schema.ts +161 -0
  46. package/packages/adapters-analytics-duckdb/tsconfig.build.json +15 -0
  47. package/packages/adapters-analytics-duckdb/tsconfig.json +9 -0
  48. package/packages/adapters-analytics-duckdb/tsup.config.ts +9 -0
  49. package/packages/adapters-analytics-file/README.md +32 -0
  50. package/packages/adapters-analytics-file/eslint.config.js +27 -0
  51. package/packages/adapters-analytics-file/package.json +50 -0
  52. package/packages/adapters-analytics-file/src/__tests__/daily-stats.spec.ts +287 -0
  53. package/packages/adapters-analytics-file/src/__tests__/scoped-analytics.test.ts +233 -0
  54. package/packages/adapters-analytics-file/src/index.test.ts +214 -0
  55. package/packages/adapters-analytics-file/src/index.ts +830 -0
  56. package/packages/adapters-analytics-file/src/manifest.ts +45 -0
  57. package/packages/adapters-analytics-file/tsconfig.build.json +15 -0
  58. package/packages/adapters-analytics-file/tsconfig.json +9 -0
  59. package/packages/adapters-analytics-file/tsup.config.ts +9 -0
  60. package/packages/adapters-analytics-sqlite/package.json +55 -0
  61. package/packages/adapters-analytics-sqlite/scripts/migrate-from-jsonl.mjs +194 -0
  62. package/packages/adapters-analytics-sqlite/src/index.ts +460 -0
  63. package/packages/adapters-analytics-sqlite/src/manifest.ts +41 -0
  64. package/packages/adapters-analytics-sqlite/tsconfig.build.json +15 -0
  65. package/packages/adapters-analytics-sqlite/tsconfig.json +9 -0
  66. package/packages/adapters-analytics-sqlite/tsup.config.ts +9 -0
  67. package/packages/adapters-environment-docker/README.md +28 -0
  68. package/packages/adapters-environment-docker/eslint.config.js +5 -0
  69. package/packages/adapters-environment-docker/package.json +49 -0
  70. package/packages/adapters-environment-docker/src/index.test.ts +138 -0
  71. package/packages/adapters-environment-docker/src/index.ts +439 -0
  72. package/packages/adapters-environment-docker/src/manifest.ts +65 -0
  73. package/packages/adapters-environment-docker/tsconfig.build.json +15 -0
  74. package/packages/adapters-environment-docker/tsconfig.json +16 -0
  75. package/packages/adapters-environment-docker/tsup.config.ts +9 -0
  76. package/packages/adapters-eventbus-cache/README.md +242 -0
  77. package/packages/adapters-eventbus-cache/eslint.config.js +27 -0
  78. package/packages/adapters-eventbus-cache/package.json +46 -0
  79. package/packages/adapters-eventbus-cache/src/index.test.ts +235 -0
  80. package/packages/adapters-eventbus-cache/src/index.ts +215 -0
  81. package/packages/adapters-eventbus-cache/src/manifest.ts +50 -0
  82. package/packages/adapters-eventbus-cache/src/types.ts +58 -0
  83. package/packages/adapters-eventbus-cache/tsconfig.build.json +15 -0
  84. package/packages/adapters-eventbus-cache/tsconfig.json +9 -0
  85. package/packages/adapters-eventbus-cache/tsup.config.ts +9 -0
  86. package/packages/adapters-fs/README.md +171 -0
  87. package/packages/adapters-fs/allowed.txt +1 -0
  88. package/packages/adapters-fs/conflict.txt +1 -0
  89. package/packages/adapters-fs/dest.txt +1 -0
  90. package/packages/adapters-fs/eslint.config.js +27 -0
  91. package/packages/adapters-fs/exists.txt +1 -0
  92. package/packages/adapters-fs/not-allowed.txt +1 -0
  93. package/packages/adapters-fs/other.txt +1 -0
  94. package/packages/adapters-fs/package.json +55 -0
  95. package/packages/adapters-fs/public/file1.txt +1 -0
  96. package/packages/adapters-fs/public/file2.txt +1 -0
  97. package/packages/adapters-fs/secret.txt +1 -0
  98. package/packages/adapters-fs/secrets/key.txt +1 -0
  99. package/packages/adapters-fs/src/index.test.ts +243 -0
  100. package/packages/adapters-fs/src/index.ts +258 -0
  101. package/packages/adapters-fs/src/manifest.ts +35 -0
  102. package/packages/adapters-fs/src/secure-storage.test.ts +380 -0
  103. package/packages/adapters-fs/src/secure-storage.ts +268 -0
  104. package/packages/adapters-fs/test.json +1 -0
  105. package/packages/adapters-fs/test.txt +1 -0
  106. package/packages/adapters-fs/test.xyz +1 -0
  107. package/packages/adapters-fs/test1.txt +1 -0
  108. package/packages/adapters-fs/test2.txt +1 -0
  109. package/packages/adapters-fs/tsconfig.build.json +15 -0
  110. package/packages/adapters-fs/tsconfig.json +9 -0
  111. package/packages/adapters-fs/tsup.config.ts +8 -0
  112. package/packages/adapters-fs/vitest.config.ts +19 -0
  113. package/packages/adapters-log-ringbuffer/README.md +228 -0
  114. package/packages/adapters-log-ringbuffer/eslint.config.js +27 -0
  115. package/packages/adapters-log-ringbuffer/package.json +47 -0
  116. package/packages/adapters-log-ringbuffer/src/__tests__/ring-buffer.test.ts +450 -0
  117. package/packages/adapters-log-ringbuffer/src/index.ts +212 -0
  118. package/packages/adapters-log-ringbuffer/src/manifest.ts +30 -0
  119. package/packages/adapters-log-ringbuffer/tsconfig.build.json +15 -0
  120. package/packages/adapters-log-ringbuffer/tsconfig.json +9 -0
  121. package/packages/adapters-log-ringbuffer/tsup.config.ts +9 -0
  122. package/packages/adapters-log-ringbuffer/vitest.config.ts +14 -0
  123. package/packages/adapters-log-sqlite/README.md +396 -0
  124. package/packages/adapters-log-sqlite/eslint.config.js +27 -0
  125. package/packages/adapters-log-sqlite/package.json +49 -0
  126. package/packages/adapters-log-sqlite/src/__tests__/log-persistence.test.ts +718 -0
  127. package/packages/adapters-log-sqlite/src/index.ts +1068 -0
  128. package/packages/adapters-log-sqlite/src/manifest.ts +36 -0
  129. package/packages/adapters-log-sqlite/src/schema.sql +46 -0
  130. package/packages/adapters-log-sqlite/tsconfig.build.json +15 -0
  131. package/packages/adapters-log-sqlite/tsconfig.json +9 -0
  132. package/packages/adapters-log-sqlite/tsup.config.ts +9 -0
  133. package/packages/adapters-log-sqlite/vitest.config.ts +15 -0
  134. package/packages/adapters-mongodb/README.md +147 -0
  135. package/packages/adapters-mongodb/eslint.config.js +27 -0
  136. package/packages/adapters-mongodb/package.json +53 -0
  137. package/packages/adapters-mongodb/src/index.ts +428 -0
  138. package/packages/adapters-mongodb/src/manifest.ts +45 -0
  139. package/packages/adapters-mongodb/src/secure-document.ts +231 -0
  140. package/packages/adapters-mongodb/tsconfig.build.json +15 -0
  141. package/packages/adapters-mongodb/tsconfig.json +9 -0
  142. package/packages/adapters-mongodb/tsup.config.ts +8 -0
  143. package/packages/adapters-openai/README.md +151 -0
  144. package/packages/adapters-openai/embeddings.ts +37 -0
  145. package/packages/adapters-openai/eslint.config.js +26 -0
  146. package/packages/adapters-openai/index.ts +22 -0
  147. package/packages/adapters-openai/package.json +57 -0
  148. package/packages/adapters-openai/src/embeddings-manifest.ts +45 -0
  149. package/packages/adapters-openai/src/embeddings.ts +104 -0
  150. package/packages/adapters-openai/src/index.ts +13 -0
  151. package/packages/adapters-openai/src/llm.ts +304 -0
  152. package/packages/adapters-openai/src/manifest.ts +47 -0
  153. package/packages/adapters-openai/tsconfig.build.json +15 -0
  154. package/packages/adapters-openai/tsconfig.json +9 -0
  155. package/packages/adapters-openai/tsup.config.ts +8 -0
  156. package/packages/adapters-pino/README.md +152 -0
  157. package/packages/adapters-pino/eslint.config.js +27 -0
  158. package/packages/adapters-pino/package.json +49 -0
  159. package/packages/adapters-pino/src/index.test.ts +44 -0
  160. package/packages/adapters-pino/src/index.ts +322 -0
  161. package/packages/adapters-pino/src/log-ring-buffer.ts +142 -0
  162. package/packages/adapters-pino/src/manifest.ts +49 -0
  163. package/packages/adapters-pino/tsconfig.build.json +15 -0
  164. package/packages/adapters-pino/tsconfig.json +9 -0
  165. package/packages/adapters-pino/tsup.config.ts +9 -0
  166. package/packages/adapters-pino-http/README.md +141 -0
  167. package/packages/adapters-pino-http/eslint.config.js +27 -0
  168. package/packages/adapters-pino-http/package.json +46 -0
  169. package/packages/adapters-pino-http/src/index.ts +229 -0
  170. package/packages/adapters-pino-http/tsconfig.build.json +15 -0
  171. package/packages/adapters-pino-http/tsconfig.json +9 -0
  172. package/packages/adapters-pino-http/tsup.config.ts +9 -0
  173. package/packages/adapters-qdrant/README.md +166 -0
  174. package/packages/adapters-qdrant/eslint.config.js +27 -0
  175. package/packages/adapters-qdrant/package.json +49 -0
  176. package/packages/adapters-qdrant/src/index.ts +490 -0
  177. package/packages/adapters-qdrant/src/manifest.ts +54 -0
  178. package/packages/adapters-qdrant/src/retry.ts +204 -0
  179. package/packages/adapters-qdrant/tsconfig.build.json +15 -0
  180. package/packages/adapters-qdrant/tsconfig.json +9 -0
  181. package/packages/adapters-qdrant/tsup.config.ts +9 -0
  182. package/packages/adapters-redis/README.md +159 -0
  183. package/packages/adapters-redis/eslint.config.js +27 -0
  184. package/packages/adapters-redis/package.json +49 -0
  185. package/packages/adapters-redis/src/index.ts +164 -0
  186. package/packages/adapters-redis/src/manifest.ts +49 -0
  187. package/packages/adapters-redis/tsconfig.build.json +15 -0
  188. package/packages/adapters-redis/tsconfig.json +9 -0
  189. package/packages/adapters-redis/tsup.config.ts +9 -0
  190. package/packages/adapters-snapshot-localfs/README.md +10 -0
  191. package/packages/adapters-snapshot-localfs/eslint.config.js +2 -0
  192. package/packages/adapters-snapshot-localfs/package.json +46 -0
  193. package/packages/adapters-snapshot-localfs/src/index.test.ts +40 -0
  194. package/packages/adapters-snapshot-localfs/src/index.ts +292 -0
  195. package/packages/adapters-snapshot-localfs/src/manifest.ts +32 -0
  196. package/packages/adapters-snapshot-localfs/tsconfig.build.json +15 -0
  197. package/packages/adapters-snapshot-localfs/tsconfig.json +16 -0
  198. package/packages/adapters-snapshot-localfs/tsup.config.ts +11 -0
  199. package/packages/adapters-sqlite/README.md +163 -0
  200. package/packages/adapters-sqlite/eslint.config.js +27 -0
  201. package/packages/adapters-sqlite/package.json +54 -0
  202. package/packages/adapters-sqlite/src/index.test.ts +245 -0
  203. package/packages/adapters-sqlite/src/index.ts +382 -0
  204. package/packages/adapters-sqlite/src/manifest.ts +47 -0
  205. package/packages/adapters-sqlite/src/secure-sql.test.ts +290 -0
  206. package/packages/adapters-sqlite/src/secure-sql.ts +281 -0
  207. package/packages/adapters-sqlite/tsconfig.build.json +15 -0
  208. package/packages/adapters-sqlite/tsconfig.json +9 -0
  209. package/packages/adapters-sqlite/tsup.config.ts +8 -0
  210. package/packages/adapters-sqlite/vitest.config.ts +19 -0
  211. package/packages/adapters-transport/README.md +170 -0
  212. package/packages/adapters-transport/eslint.config.js +27 -0
  213. package/packages/adapters-transport/package.json +49 -0
  214. package/packages/adapters-transport/src/__tests__/unix-socket-server.test.ts +550 -0
  215. package/packages/adapters-transport/src/index.ts +101 -0
  216. package/packages/adapters-transport/src/ipc-transport.ts +228 -0
  217. package/packages/adapters-transport/src/transport.ts +224 -0
  218. package/packages/adapters-transport/src/types.ts +92 -0
  219. package/packages/adapters-transport/src/unix-socket-server.ts +193 -0
  220. package/packages/adapters-transport/src/unix-socket-transport.ts +280 -0
  221. package/packages/adapters-transport/tsconfig.build.json +15 -0
  222. package/packages/adapters-transport/tsconfig.json +9 -0
  223. package/packages/adapters-transport/tsup.config.ts +9 -0
  224. package/packages/adapters-vibeproxy/README.md +159 -0
  225. package/packages/adapters-vibeproxy/eslint.config.js +27 -0
  226. package/packages/adapters-vibeproxy/package.json +51 -0
  227. package/packages/adapters-vibeproxy/src/index.ts +13 -0
  228. package/packages/adapters-vibeproxy/src/llm.ts +437 -0
  229. package/packages/adapters-vibeproxy/src/manifest.ts +51 -0
  230. package/packages/adapters-vibeproxy/tsconfig.build.json +15 -0
  231. package/packages/adapters-vibeproxy/tsconfig.json +9 -0
  232. package/packages/adapters-vibeproxy/tsup.config.ts +8 -0
  233. package/packages/adapters-workspace-agent/package.json +46 -0
  234. package/packages/adapters-workspace-agent/src/__tests__/adapter.test.ts +212 -0
  235. package/packages/adapters-workspace-agent/src/index.ts +220 -0
  236. package/packages/adapters-workspace-agent/src/manifest.ts +36 -0
  237. package/packages/adapters-workspace-agent/tsconfig.build.json +15 -0
  238. package/packages/adapters-workspace-agent/tsconfig.json +16 -0
  239. package/packages/adapters-workspace-agent/tsup.config.ts +11 -0
  240. package/packages/adapters-workspace-localfs/README.md +9 -0
  241. package/packages/adapters-workspace-localfs/eslint.config.js +2 -0
  242. package/packages/adapters-workspace-localfs/package.json +46 -0
  243. package/packages/adapters-workspace-localfs/src/index.test.ts +27 -0
  244. package/packages/adapters-workspace-localfs/src/index.ts +172 -0
  245. package/packages/adapters-workspace-localfs/src/manifest.ts +32 -0
  246. package/packages/adapters-workspace-localfs/tsconfig.build.json +15 -0
  247. package/packages/adapters-workspace-localfs/tsconfig.json +16 -0
  248. package/packages/adapters-workspace-localfs/tsup.config.ts +11 -0
  249. package/packages/adapters-workspace-worktree/README.md +9 -0
  250. package/packages/adapters-workspace-worktree/eslint.config.js +2 -0
  251. package/packages/adapters-workspace-worktree/package.json +46 -0
  252. package/packages/adapters-workspace-worktree/src/index.test.ts +38 -0
  253. package/packages/adapters-workspace-worktree/src/index.ts +245 -0
  254. package/packages/adapters-workspace-worktree/src/manifest.ts +38 -0
  255. package/packages/adapters-workspace-worktree/tsconfig.build.json +15 -0
  256. package/packages/adapters-workspace-worktree/tsconfig.json +16 -0
  257. package/packages/adapters-workspace-worktree/tsup.config.ts +11 -0
  258. package/pnpm-workspace.yaml +2800 -0
  259. package/prettierrc.json +1 -0
  260. package/scripts/devkit-sync.mjs +37 -0
  261. package/scripts/hooks/post-push +9 -0
  262. package/scripts/hooks/pre-commit +9 -0
  263. package/scripts/hooks/pre-push +9 -0
  264. package/test-integration.ts +242 -0
  265. package/test.txt +1 -0
  266. package/tsconfig.base.json +6 -0
  267. package/tsconfig.build.json +15 -0
  268. package/tsconfig.json +9 -0
  269. package/tsconfig.paths.json +26 -0
  270. package/tsconfig.tools.json +17 -0
  271. package/tsup.config.bin.ts +34 -0
  272. package/tsup.config.cli.ts +41 -0
  273. package/tsup.config.dual.ts +46 -0
  274. package/tsup.config.ts +36 -0
  275. package/tsup.external.json +103 -0
  276. 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,15 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "baseUrl": ".",
6
+ "paths": {}
7
+ },
8
+ "include": [
9
+ "src/**/*"
10
+ ],
11
+ "exclude": [
12
+ "dist",
13
+ "node_modules"
14
+ ]
15
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "@kb-labs/devkit/tsconfig/node.json",
4
+ "compilerOptions": {
5
+ "rootDir": "src",
6
+ "outDir": "dist"
7
+ },
8
+ "include": ["src"]
9
+ }
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from 'tsup';
2
+ import nodePreset from '@kb-labs/devkit/tsup/node';
3
+
4
+ export default defineConfig({
5
+ ...nodePreset,
6
+ tsconfig: 'tsconfig.build.json',
7
+ entry: ['src/index.ts', 'src/manifest.ts'],
8
+ dts: true,
9
+ });
@@ -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
+ };
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "baseUrl": ".",
6
+ "paths": {}
7
+ },
8
+ "include": [
9
+ "src/**/*"
10
+ ],
11
+ "exclude": [
12
+ "dist",
13
+ "node_modules"
14
+ ]
15
+ }