@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,242 @@
1
+ # @kb-labs/adapters-eventbus-cache
2
+
3
+ > Part of [KB Labs](https://github.com/KirillBaranov/kb-labs) ecosystem. Works exclusively within KB Labs platform.
4
+
5
+ EventBus adapter that uses platform cache (`ICache`) for persistent event storage with polling-based subscriptions.
6
+
7
+ ## Overview
8
+
9
+ | Property | Value |
10
+ |----------|-------|
11
+ | **Implements** | `IEventBus` |
12
+ | **Type** | `core` |
13
+ | **Requires** | `cache` |
14
+ | **Category** | EventBus |
15
+
16
+ ## Features
17
+
18
+ - **Persistent events** - Events survive process restarts (if cache backend is persistent)
19
+ - **Distributed** - Works across multiple processes (if cache is Redis)
20
+ - **Automatic cleanup** - Old events removed via configurable TTL
21
+ - **Polling-based** - Configurable polling interval for subscribers
22
+ - **Ordered delivery** - Events delivered in timestamp order
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pnpm add @kb-labs/adapters-eventbus-cache
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
+ "eventBus": "@kb-labs/adapters-eventbus-cache"
40
+ },
41
+ "adapterOptions": {
42
+ "eventBus": {
43
+ "pollIntervalMs": 1000,
44
+ "eventTtlMs": 86400000,
45
+ "keyPrefix": "kb:eventbus:"
46
+ }
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ ### Options
53
+
54
+ | Option | Type | Default | Description |
55
+ |--------|------|---------|-------------|
56
+ | `pollIntervalMs` | `number` | `1000` | Polling interval in milliseconds |
57
+ | `eventTtlMs` | `number` | `86400000` | Event TTL (24 hours by default) |
58
+ | `keyPrefix` | `string` | `"eventbus:"` | Prefix for cache keys |
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
+ // Subscribe to events
70
+ const unsubscribe = platform.eventBus.subscribe('user.created', async (event) => {
71
+ console.log('User created:', event);
72
+ });
73
+
74
+ // Publish event
75
+ await platform.eventBus.publish('user.created', { id: '123', name: 'Alice' });
76
+
77
+ // Cleanup on shutdown
78
+ unsubscribe();
79
+ ```
80
+
81
+ ### Standalone (Testing/Development)
82
+
83
+ ```typescript
84
+ import { createAdapter } from '@kb-labs/adapters-eventbus-cache';
85
+ import { MemoryCache } from '@kb-labs/core-platform/noop';
86
+
87
+ const cache = new MemoryCache();
88
+ const eventBus = createAdapter(
89
+ { pollIntervalMs: 500, eventTtlMs: 3600000 },
90
+ { cache },
91
+ );
92
+
93
+ // Don't forget to disconnect on shutdown
94
+ eventBus.disconnect();
95
+ ```
96
+
97
+ ## How It Works
98
+
99
+ Events are stored in sorted sets using cache's `zadd`/`zrangebyscore` with timestamp as score:
100
+
101
+ ```
102
+ ┌─────────────┐ ┌─────────────────────┐ ┌─────────────┐
103
+ │ Publisher │────▶│ CacheEventBusAdapter│────▶│ Cache │
104
+ └─────────────┘ └─────────────────────┘ └─────────────┘
105
+
106
+ │ poll (interval)
107
+
108
+ ┌─────────────────────┐
109
+ │ Subscribers │
110
+ └─────────────────────┘
111
+ ```
112
+
113
+ **Storage structure:**
114
+ ```
115
+ eventbus:user.created -> [
116
+ { score: 1706745600000, member: '{"id":"evt-1","topic":"user.created","data":{...},"timestamp":1706745600000}' },
117
+ { score: 1706745601000, member: '{"id":"evt-2","topic":"user.created","data":{...},"timestamp":1706745601000}' },
118
+ ]
119
+ ```
120
+
121
+ **Subscription flow:**
122
+ 1. Subscriber registers with `lastTimestamp = Date.now()`
123
+ 2. Polling timer fires every `pollIntervalMs`
124
+ 3. Adapter queries `zrangebyscore(key, lastTimestamp + 1, now)`
125
+ 4. Events processed sequentially, `lastTimestamp` updated
126
+ 5. Old events (> TTL) cleaned up automatically
127
+
128
+ ## Dependencies
129
+
130
+ This adapter requires the following adapters to be configured:
131
+
132
+ | Dependency | Adapter Key | Description |
133
+ |------------|-------------|-------------|
134
+ | `cache` | `cache` | Cache backend for event storage (Redis, Memory, etc.) |
135
+
136
+ > Dependencies are automatically resolved by the platform's AdapterLoader.
137
+
138
+ ## Adapter Manifest
139
+
140
+ ```typescript
141
+ {
142
+ id: 'eventbus-cache',
143
+ name: 'Cache-backed EventBus',
144
+ version: '1.0.0',
145
+ implements: 'IEventBus',
146
+ requires: {
147
+ adapters: [{ id: 'cache', alias: 'cache' }],
148
+ platform: '>= 1.0.0',
149
+ },
150
+ capabilities: {
151
+ custom: {
152
+ persistence: true,
153
+ distributed: true,
154
+ ttl: true,
155
+ polling: true,
156
+ },
157
+ },
158
+ }
159
+ ```
160
+
161
+ ## Performance Considerations
162
+
163
+ - **Memory**: Depends on cache backend; events are JSON-serialized (~200-500 bytes per event)
164
+ - **Latency**: Polling-based, so delivery latency is up to `pollIntervalMs`
165
+ - **Throughput**: Limited by cache backend; Redis handles ~100K ops/sec
166
+
167
+ **Tuning tips:**
168
+ - Lower `pollIntervalMs` for faster delivery (more CPU/network)
169
+ - Shorter `eventTtlMs` for lower memory usage
170
+ - Use Redis cache for distributed deployments
171
+
172
+ ## FAQ
173
+
174
+ <details>
175
+ <summary><strong>Q: Can I use this adapter outside KB Labs platform?</strong></summary>
176
+
177
+ No. This adapter is designed specifically for KB Labs ecosystem and depends on platform interfaces (`IEventBus`, `ICache`). Use `createAdapter()` with mock cache for standalone testing only.
178
+ </details>
179
+
180
+ <details>
181
+ <summary><strong>Q: Why polling instead of push notifications?</strong></summary>
182
+
183
+ Polling provides simpler implementation that works with any cache backend. For real-time requirements (< 100ms), consider using Redis pub/sub directly or a dedicated message broker.
184
+ </details>
185
+
186
+ <details>
187
+ <summary><strong>Q: What happens if a subscriber is slow?</strong></summary>
188
+
189
+ Events are processed sequentially per subscriber. If a handler is slow, that subscriber will lag behind. Other subscribers are not affected. Events are retained until TTL expires, so slow subscribers can catch up.
190
+ </details>
191
+
192
+ <details>
193
+ <summary><strong>Q: Are events guaranteed to be delivered exactly once?</strong></summary>
194
+
195
+ No. This is an at-least-once delivery system. If a process crashes mid-processing, events may be redelivered on restart. Design handlers to be idempotent.
196
+ </details>
197
+
198
+ <details>
199
+ <summary><strong>Q: Can I use MemoryCache in production?</strong></summary>
200
+
201
+ Not recommended. MemoryCache is single-process and loses data on restart. Use Redis for production deployments requiring persistence and distribution.
202
+ </details>
203
+
204
+ ## Related Adapters
205
+
206
+ | Adapter | Use Case |
207
+ |---------|----------|
208
+ | `@kb-labs/adapters-redis` | Cache backend for distributed EventBus |
209
+ | `@kb-labs/core-platform/noop` | MemoryCache for testing/development |
210
+
211
+ ## Troubleshooting
212
+
213
+ ### Events not being received
214
+
215
+ **Cause**: Subscriber registered after events were published; `lastTimestamp` is newer than event timestamps.
216
+
217
+ **Solution**: Ensure subscribers are registered before publishers start, or adjust subscription logic.
218
+
219
+ ### High memory usage
220
+
221
+ **Cause**: Long `eventTtlMs` with high event volume.
222
+
223
+ **Solution**: Reduce `eventTtlMs` or implement event archiving.
224
+
225
+ ### Slow event delivery
226
+
227
+ **Cause**: `pollIntervalMs` too high.
228
+
229
+ **Solution**: Reduce `pollIntervalMs` (e.g., 100ms for near-real-time).
230
+
231
+ ```bash
232
+ # Verify adapter is loaded
233
+ pnpm kb plugins list
234
+ ```
235
+
236
+ ## Contributing
237
+
238
+ See [CONTRIBUTING.md](../../CONTRIBUTING.md) for development guidelines.
239
+
240
+ ## License
241
+
242
+ [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,46 @@
1
+ {
2
+ "name": "@kb-labs/adapters-eventbus-cache",
3
+ "version": "0.5.0",
4
+ "description": "EventBus adapter using ICache for persistent event storage",
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
+ "peerDependencies": {
30
+ "@kb-labs/core-platform": "*"
31
+ },
32
+ "devDependencies": {
33
+ "@kb-labs/core-platform": "link:../../../../platform/kb-labs-core/packages/core-platform",
34
+ "@types/node": "^24.3.3",
35
+ "eslint": "^9",
36
+ "tsup": "^8.5.0",
37
+ "typescript": "^5.6.3",
38
+ "vitest": "^3.2.4",
39
+ "@kb-labs/devkit": "link:../../../kb-labs-devkit",
40
+ "rimraf": "^6.0.1"
41
+ },
42
+ "engines": {
43
+ "node": ">=20.0.0",
44
+ "pnpm": ">=9.0.0"
45
+ }
46
+ }
@@ -0,0 +1,235 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { CacheEventBusAdapter, createAdapter } from './index.js';
3
+ import type { ICache } from '@kb-labs/core-platform';
4
+
5
+ /**
6
+ * Mock ICache implementation for testing.
7
+ */
8
+ class MockCache implements ICache {
9
+ private store = new Map<string, unknown>();
10
+ private sortedSets = new Map<string, Array<{ score: number; member: string }>>();
11
+
12
+ async get<T>(key: string): Promise<T | null> {
13
+ return (this.store.get(key) as T) ?? null;
14
+ }
15
+
16
+ async set<T>(key: string, value: T, _ttl?: number): Promise<void> {
17
+ this.store.set(key, value);
18
+ }
19
+
20
+ async delete(key: string): Promise<void> {
21
+ this.store.delete(key);
22
+ }
23
+
24
+ async clear(_pattern?: string): Promise<void> {
25
+ this.store.clear();
26
+ this.sortedSets.clear();
27
+ }
28
+
29
+ async zadd(key: string, score: number, member: string): Promise<void> {
30
+ let set = this.sortedSets.get(key);
31
+ if (!set) {
32
+ set = [];
33
+ this.sortedSets.set(key, set);
34
+ }
35
+
36
+ // Remove existing member if present
37
+ const existingIndex = set.findIndex(m => m.member === member);
38
+ if (existingIndex !== -1) {
39
+ set.splice(existingIndex, 1);
40
+ }
41
+
42
+ // Add new member and sort by score
43
+ set.push({ score, member });
44
+ set.sort((a, b) => a.score - b.score);
45
+ }
46
+
47
+ async zrangebyscore(key: string, min: number, max: number): Promise<string[]> {
48
+ const set = this.sortedSets.get(key);
49
+ if (!set) {
50
+ return [];
51
+ }
52
+
53
+ return set
54
+ .filter(m => m.score >= min && m.score <= max)
55
+ .map(m => m.member);
56
+ }
57
+
58
+ async zrem(key: string, member: string): Promise<void> {
59
+ const set = this.sortedSets.get(key);
60
+ if (!set) {
61
+ return;
62
+ }
63
+
64
+ const index = set.findIndex(m => m.member === member);
65
+ if (index !== -1) {
66
+ set.splice(index, 1);
67
+ }
68
+ }
69
+
70
+ async setIfNotExists<T>(key: string, value: T, _ttl?: number): Promise<boolean> {
71
+ if (this.store.has(key)) {
72
+ return false;
73
+ }
74
+ this.store.set(key, value);
75
+ return true;
76
+ }
77
+ }
78
+
79
+ const TEST_TOPIC = 'test.topic';
80
+
81
+ describe('CacheEventBusAdapter', () => {
82
+ let cache: MockCache;
83
+ let eventBus: CacheEventBusAdapter;
84
+
85
+ beforeEach(() => {
86
+ vi.useFakeTimers();
87
+ cache = new MockCache();
88
+ eventBus = new CacheEventBusAdapter(cache, {
89
+ pollIntervalMs: 100,
90
+ eventTtlMs: 60000, // 1 minute
91
+ keyPrefix: 'test:eventbus:',
92
+ });
93
+ });
94
+
95
+ afterEach(() => {
96
+ eventBus.disconnect();
97
+ vi.useRealTimers();
98
+ });
99
+
100
+ describe('publish', () => {
101
+ it('should store event in cache', async () => {
102
+ await eventBus.publish(TEST_TOPIC, { message: 'hello' });
103
+
104
+ const events = await cache.zrangebyscore('test:eventbus:test.topic', 0, Date.now() + 1000);
105
+ expect(events).toHaveLength(1);
106
+
107
+ const storedEvent = JSON.parse(events[0]!);
108
+ expect(storedEvent.topic).toBe(TEST_TOPIC);
109
+ expect(storedEvent.data).toEqual({ message: 'hello' });
110
+ expect(storedEvent.id).toBeDefined();
111
+ expect(storedEvent.timestamp).toBeDefined();
112
+ });
113
+
114
+ it('should store multiple events in order', async () => {
115
+ await eventBus.publish(TEST_TOPIC, { order: 1 });
116
+ vi.advanceTimersByTime(10);
117
+ await eventBus.publish(TEST_TOPIC, { order: 2 });
118
+ vi.advanceTimersByTime(10);
119
+ await eventBus.publish(TEST_TOPIC, { order: 3 });
120
+
121
+ const events = await cache.zrangebyscore('test:eventbus:test.topic', 0, Date.now() + 1000);
122
+ expect(events).toHaveLength(3);
123
+
124
+ const parsed = events.map(e => JSON.parse(e));
125
+ expect(parsed[0].data.order).toBe(1);
126
+ expect(parsed[1].data.order).toBe(2);
127
+ expect(parsed[2].data.order).toBe(3);
128
+ });
129
+ });
130
+
131
+ describe('subscribe', () => {
132
+ it('should receive published events via polling', async () => {
133
+ const received: unknown[] = [];
134
+
135
+ eventBus.subscribe(TEST_TOPIC, async (event) => {
136
+ received.push(event);
137
+ });
138
+
139
+ // Advance time so event timestamp > subscription lastTimestamp
140
+ await vi.advanceTimersByTimeAsync(10);
141
+
142
+ // Publish an event
143
+ await eventBus.publish(TEST_TOPIC, { message: 'hello' });
144
+
145
+ // Advance timer to trigger poll
146
+ await vi.advanceTimersByTimeAsync(150);
147
+
148
+ expect(received).toHaveLength(1);
149
+ expect(received[0]).toEqual({ message: 'hello' });
150
+ });
151
+
152
+ it('should return unsubscribe function', () => {
153
+ const unsubscribe = eventBus.subscribe(TEST_TOPIC, async () => {});
154
+
155
+ expect(eventBus.subscriptionCount).toBe(1);
156
+
157
+ unsubscribe();
158
+
159
+ expect(eventBus.subscriptionCount).toBe(0);
160
+ });
161
+
162
+ it('should support multiple subscribers on same topic', async () => {
163
+ const received1: unknown[] = [];
164
+ const received2: unknown[] = [];
165
+
166
+ eventBus.subscribe(TEST_TOPIC, async (event) => {
167
+ received1.push(event);
168
+ });
169
+
170
+ eventBus.subscribe(TEST_TOPIC, async (event) => {
171
+ received2.push(event);
172
+ });
173
+
174
+ // Advance time so event timestamp > subscription lastTimestamp
175
+ await vi.advanceTimersByTimeAsync(10);
176
+
177
+ await eventBus.publish(TEST_TOPIC, { message: 'hello' });
178
+
179
+ await vi.advanceTimersByTimeAsync(150);
180
+
181
+ expect(received1).toHaveLength(1);
182
+ expect(received2).toHaveLength(1);
183
+ });
184
+
185
+ it('should not receive events from other topics', async () => {
186
+ const received: unknown[] = [];
187
+
188
+ eventBus.subscribe('topic.a', async (event) => {
189
+ received.push(event);
190
+ });
191
+
192
+ await eventBus.publish('topic.b', { message: 'wrong topic' });
193
+
194
+ await vi.advanceTimersByTimeAsync(150);
195
+
196
+ expect(received).toHaveLength(0);
197
+ });
198
+ });
199
+
200
+ describe('disconnect', () => {
201
+ it('should stop all subscriptions', () => {
202
+ eventBus.subscribe('topic.a', async () => {});
203
+ eventBus.subscribe('topic.b', async () => {});
204
+
205
+ expect(eventBus.subscriptionCount).toBe(2);
206
+
207
+ eventBus.disconnect();
208
+
209
+ expect(eventBus.subscriptionCount).toBe(0);
210
+ });
211
+ });
212
+
213
+ describe('createAdapter', () => {
214
+ it('should create adapter with default config', () => {
215
+ const adapter = createAdapter({}, { cache });
216
+
217
+ expect(adapter).toBeInstanceOf(CacheEventBusAdapter);
218
+ adapter.disconnect();
219
+ });
220
+
221
+ it('should create adapter with custom config', () => {
222
+ const adapter = createAdapter(
223
+ {
224
+ pollIntervalMs: 500,
225
+ eventTtlMs: 3600000,
226
+ keyPrefix: 'custom:',
227
+ },
228
+ { cache },
229
+ );
230
+
231
+ expect(adapter).toBeInstanceOf(CacheEventBusAdapter);
232
+ adapter.disconnect();
233
+ });
234
+ });
235
+ });