@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,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/adapters-eventbus-cache
|
|
3
|
+
* EventBus adapter using ICache for persistent event storage.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* import { createAdapter } from '@kb-labs/adapters-eventbus-cache';
|
|
8
|
+
* import { MemoryCache } from '@kb-labs/core-platform/noop';
|
|
9
|
+
*
|
|
10
|
+
* // When loaded by platform, deps are injected automatically.
|
|
11
|
+
* // For manual usage:
|
|
12
|
+
* const cache = new MemoryCache();
|
|
13
|
+
* const eventBus = createAdapter(
|
|
14
|
+
* { pollIntervalMs: 500, eventTtlMs: 3600000 },
|
|
15
|
+
* { cache }
|
|
16
|
+
* );
|
|
17
|
+
*
|
|
18
|
+
* // Subscribe to events
|
|
19
|
+
* const unsubscribe = eventBus.subscribe('user.created', async (event) => {
|
|
20
|
+
* console.log('User created:', event);
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Publish event
|
|
24
|
+
* await eventBus.publish('user.created', { id: '123', name: 'Alice' });
|
|
25
|
+
*
|
|
26
|
+
* // Cleanup
|
|
27
|
+
* unsubscribe();
|
|
28
|
+
* eventBus.disconnect();
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { randomUUID } from 'node:crypto';
|
|
33
|
+
import type { IEventBus, ICache, EventHandler, Unsubscribe } from '@kb-labs/core-platform';
|
|
34
|
+
import type { CacheEventBusConfig, StoredEvent, Subscription, CacheEventBusDeps } from './types.js';
|
|
35
|
+
|
|
36
|
+
// Re-export manifest and types
|
|
37
|
+
export { manifest } from './manifest.js';
|
|
38
|
+
export type { CacheEventBusConfig, StoredEvent, CacheEventBusDeps } from './types.js';
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* EventBus implementation using ICache for persistent storage.
|
|
42
|
+
*
|
|
43
|
+
* Features:
|
|
44
|
+
* - Persistent events survive restarts (if cache is persistent)
|
|
45
|
+
* - Distributed across processes (if cache is Redis)
|
|
46
|
+
* - Automatic cleanup of old events via TTL
|
|
47
|
+
* - Polling-based subscription mechanism
|
|
48
|
+
*/
|
|
49
|
+
export class CacheEventBusAdapter implements IEventBus {
|
|
50
|
+
private readonly cache: ICache;
|
|
51
|
+
private readonly config: Required<CacheEventBusConfig>;
|
|
52
|
+
private readonly subscriptions = new Map<string, Subscription>();
|
|
53
|
+
|
|
54
|
+
constructor(cache: ICache, config: CacheEventBusConfig = {}) {
|
|
55
|
+
this.cache = cache;
|
|
56
|
+
this.config = {
|
|
57
|
+
pollIntervalMs: config.pollIntervalMs ?? 1000,
|
|
58
|
+
eventTtlMs: config.eventTtlMs ?? 86400000, // 24 hours
|
|
59
|
+
keyPrefix: config.keyPrefix ?? 'eventbus:',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Publish an event to a topic.
|
|
65
|
+
* Events are stored in a sorted set with timestamp as score.
|
|
66
|
+
*/
|
|
67
|
+
async publish<T>(topic: string, event: T): Promise<void> {
|
|
68
|
+
const storedEvent: StoredEvent<T> = {
|
|
69
|
+
id: randomUUID(),
|
|
70
|
+
topic,
|
|
71
|
+
data: event,
|
|
72
|
+
timestamp: Date.now(),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const key = `${this.config.keyPrefix}${topic}`;
|
|
76
|
+
|
|
77
|
+
// Add to sorted set with timestamp as score
|
|
78
|
+
await this.cache.zadd(key, storedEvent.timestamp, JSON.stringify(storedEvent));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Subscribe to events on a topic.
|
|
83
|
+
* Uses polling to check for new events at configurable intervals.
|
|
84
|
+
*/
|
|
85
|
+
subscribe<T>(topic: string, handler: EventHandler<T>): Unsubscribe {
|
|
86
|
+
const subscriberId = randomUUID();
|
|
87
|
+
const key = `${this.config.keyPrefix}${topic}`;
|
|
88
|
+
|
|
89
|
+
const subscription: Subscription = {
|
|
90
|
+
topic,
|
|
91
|
+
handler: handler as (event: unknown) => Promise<void>,
|
|
92
|
+
timer: null,
|
|
93
|
+
subscriberId,
|
|
94
|
+
lastTimestamp: Date.now(),
|
|
95
|
+
seenIds: new Set(),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Polling function
|
|
99
|
+
const poll = async (): Promise<void> => {
|
|
100
|
+
try {
|
|
101
|
+
const now = Date.now();
|
|
102
|
+
|
|
103
|
+
// Get events from lastTimestamp (inclusive) to catch events with same ms timestamp
|
|
104
|
+
const events = await this.cache.zrangebyscore(
|
|
105
|
+
key,
|
|
106
|
+
subscription.lastTimestamp,
|
|
107
|
+
now
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
let maxTimestamp = subscription.lastTimestamp;
|
|
111
|
+
|
|
112
|
+
// Process events sequentially to preserve order.
|
|
113
|
+
for (const eventJson of events) {
|
|
114
|
+
try {
|
|
115
|
+
const storedEvent = JSON.parse(eventJson) as StoredEvent<T>;
|
|
116
|
+
|
|
117
|
+
// Skip already-delivered events (dedup by ID)
|
|
118
|
+
if (subscription.seenIds.has(storedEvent.id)) {continue;}
|
|
119
|
+
|
|
120
|
+
subscription.seenIds.add(storedEvent.id);
|
|
121
|
+
await handler(storedEvent.data);
|
|
122
|
+
|
|
123
|
+
if (storedEvent.timestamp > maxTimestamp) {
|
|
124
|
+
maxTimestamp = storedEvent.timestamp;
|
|
125
|
+
}
|
|
126
|
+
} catch (err) {
|
|
127
|
+
console.error(`[CacheEventBus] Handler error for topic "${topic}":`, err);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Advance cursor — purge seenIds for timestamps we've moved past
|
|
132
|
+
if (maxTimestamp > subscription.lastTimestamp) {
|
|
133
|
+
subscription.seenIds.clear();
|
|
134
|
+
// Re-add IDs at the new maxTimestamp boundary (they may repeat next poll)
|
|
135
|
+
for (const eventJson of events) {
|
|
136
|
+
try {
|
|
137
|
+
const ev = JSON.parse(eventJson) as StoredEvent<T>;
|
|
138
|
+
if (ev.timestamp === maxTimestamp) {
|
|
139
|
+
subscription.seenIds.add(ev.id);
|
|
140
|
+
}
|
|
141
|
+
} catch { /* ignore parse errors */ }
|
|
142
|
+
}
|
|
143
|
+
subscription.lastTimestamp = maxTimestamp;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Cleanup old events (older than TTL)
|
|
147
|
+
await this.cleanupOldEvents(key);
|
|
148
|
+
} catch (err) {
|
|
149
|
+
console.error(`[CacheEventBus] Poll error for topic "${topic}":`, err);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Start polling timer
|
|
154
|
+
subscription.timer = setInterval(() => void poll(), this.config.pollIntervalMs);
|
|
155
|
+
this.subscriptions.set(subscriberId, subscription);
|
|
156
|
+
|
|
157
|
+
// Return unsubscribe function
|
|
158
|
+
return () => {
|
|
159
|
+
const sub = this.subscriptions.get(subscriberId);
|
|
160
|
+
if (sub?.timer) {
|
|
161
|
+
clearInterval(sub.timer);
|
|
162
|
+
}
|
|
163
|
+
this.subscriptions.delete(subscriberId);
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Remove events older than TTL from a topic.
|
|
169
|
+
*/
|
|
170
|
+
private async cleanupOldEvents(key: string): Promise<void> {
|
|
171
|
+
const cutoff = Date.now() - this.config.eventTtlMs;
|
|
172
|
+
const oldEvents = await this.cache.zrangebyscore(key, 0, cutoff);
|
|
173
|
+
|
|
174
|
+
// Cleanup old events
|
|
175
|
+
await Promise.all(oldEvents.map(oldEvent => this.cache.zrem(key, oldEvent)));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Stop all subscriptions and cleanup.
|
|
180
|
+
* Call this on shutdown to prevent memory leaks.
|
|
181
|
+
*/
|
|
182
|
+
disconnect(): void {
|
|
183
|
+
for (const sub of this.subscriptions.values()) {
|
|
184
|
+
if (sub.timer) {
|
|
185
|
+
clearInterval(sub.timer);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
this.subscriptions.clear();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get the number of active subscriptions (for testing/debugging).
|
|
193
|
+
*/
|
|
194
|
+
get subscriptionCount(): number {
|
|
195
|
+
return this.subscriptions.size;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Factory function for adapter loading.
|
|
201
|
+
* Called by initPlatform() when loading adapters from config.
|
|
202
|
+
*
|
|
203
|
+
* @param config - EventBus configuration
|
|
204
|
+
* @param deps - Required dependencies (cache)
|
|
205
|
+
* @returns CacheEventBusAdapter instance
|
|
206
|
+
*/
|
|
207
|
+
export function createAdapter(
|
|
208
|
+
config: CacheEventBusConfig = {},
|
|
209
|
+
deps: CacheEventBusDeps,
|
|
210
|
+
): CacheEventBusAdapter {
|
|
211
|
+
return new CacheEventBusAdapter(deps.cache, config);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Default export for direct import
|
|
215
|
+
export default createAdapter;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/adapters-eventbus-cache/manifest
|
|
3
|
+
* Adapter manifest for Cache-backed EventBus.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { AdapterManifest } from '@kb-labs/core-platform';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Adapter manifest for Cache-backed EventBus.
|
|
10
|
+
*/
|
|
11
|
+
export const manifest: AdapterManifest = {
|
|
12
|
+
manifestVersion: '1.0.0',
|
|
13
|
+
id: 'eventbus-cache',
|
|
14
|
+
name: 'Cache-backed EventBus',
|
|
15
|
+
version: '1.0.0',
|
|
16
|
+
description: 'EventBus using ICache for persistent event storage with polling-based subscriptions',
|
|
17
|
+
author: 'KB Labs Team',
|
|
18
|
+
license: 'KBPL-1.1',
|
|
19
|
+
type: 'core',
|
|
20
|
+
implements: 'IEventBus',
|
|
21
|
+
requires: {
|
|
22
|
+
adapters: [{ id: 'cache', alias: 'cache' }],
|
|
23
|
+
platform: '>= 1.0.0',
|
|
24
|
+
},
|
|
25
|
+
capabilities: {
|
|
26
|
+
custom: {
|
|
27
|
+
persistence: true,
|
|
28
|
+
distributed: true,
|
|
29
|
+
ttl: true,
|
|
30
|
+
polling: true,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
configSchema: {
|
|
34
|
+
pollIntervalMs: {
|
|
35
|
+
type: 'number',
|
|
36
|
+
default: 1000,
|
|
37
|
+
description: 'Polling interval in milliseconds',
|
|
38
|
+
},
|
|
39
|
+
eventTtlMs: {
|
|
40
|
+
type: 'number',
|
|
41
|
+
default: 86400000,
|
|
42
|
+
description: 'Event TTL in milliseconds (default: 24 hours)',
|
|
43
|
+
},
|
|
44
|
+
keyPrefix: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
default: 'eventbus:',
|
|
47
|
+
description: 'Prefix for all cache keys',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/adapters-eventbus-cache/types
|
|
3
|
+
* Type definitions for Cache-backed EventBus adapter.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ICache } from '@kb-labs/core-platform';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Dependencies for Cache-backed EventBus adapter.
|
|
10
|
+
* Matches manifest.requires.adapters: [{ id: 'cache', alias: 'cache' }]
|
|
11
|
+
*/
|
|
12
|
+
export interface CacheEventBusDeps {
|
|
13
|
+
cache: ICache;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Configuration for Cache-backed EventBus adapter.
|
|
18
|
+
*/
|
|
19
|
+
export interface CacheEventBusConfig {
|
|
20
|
+
/** Polling interval in milliseconds (default: 1000) */
|
|
21
|
+
pollIntervalMs?: number;
|
|
22
|
+
/** Event TTL in milliseconds (default: 24 hours) */
|
|
23
|
+
eventTtlMs?: number;
|
|
24
|
+
/** Key prefix for cache keys (default: "eventbus:") */
|
|
25
|
+
keyPrefix?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Stored event structure in cache.
|
|
30
|
+
*/
|
|
31
|
+
export interface StoredEvent<T = unknown> {
|
|
32
|
+
/** Unique event ID */
|
|
33
|
+
id: string;
|
|
34
|
+
/** Event topic */
|
|
35
|
+
topic: string;
|
|
36
|
+
/** Event payload */
|
|
37
|
+
data: T;
|
|
38
|
+
/** Event timestamp (ms since epoch) */
|
|
39
|
+
timestamp: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Internal subscription tracking.
|
|
44
|
+
*/
|
|
45
|
+
export interface Subscription {
|
|
46
|
+
/** Topic name */
|
|
47
|
+
topic: string;
|
|
48
|
+
/** Event handler function */
|
|
49
|
+
handler: (event: unknown) => Promise<void>;
|
|
50
|
+
/** Polling timer reference */
|
|
51
|
+
timer: ReturnType<typeof setInterval> | null;
|
|
52
|
+
/** Unique subscriber ID */
|
|
53
|
+
subscriberId: string;
|
|
54
|
+
/** Last processed event timestamp */
|
|
55
|
+
lastTimestamp: number;
|
|
56
|
+
/** IDs of events already delivered (for dedup within same timestamp) */
|
|
57
|
+
seenIds: Set<string>;
|
|
58
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# @kb-labs/adapters-fs
|
|
2
|
+
|
|
3
|
+
> Part of [KB Labs](https://github.com/KirillBaranov/kb-labs) ecosystem. Works exclusively within KB Labs platform.
|
|
4
|
+
|
|
5
|
+
Filesystem storage adapter for local file operations with path security and glob pattern support.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
| Property | Value |
|
|
10
|
+
|----------|-------|
|
|
11
|
+
| **Implements** | `IStorage` |
|
|
12
|
+
| **Type** | `core` |
|
|
13
|
+
| **Requires** | None |
|
|
14
|
+
| **Category** | Storage |
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **Path Security** - Sandboxed file access within configured base directory
|
|
19
|
+
- **Streaming Support** - Efficient large file handling with streams
|
|
20
|
+
- **Glob Patterns** - Find files using glob patterns
|
|
21
|
+
- **Metadata Access** - Get file stats, size, modification time
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add @kb-labs/adapters-fs
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
Add to your `kb.config.json`:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"platform": {
|
|
36
|
+
"adapters": {
|
|
37
|
+
"storage": "@kb-labs/adapters-fs"
|
|
38
|
+
},
|
|
39
|
+
"adapterOptions": {
|
|
40
|
+
"storage": {
|
|
41
|
+
"baseDir": ".kb/data"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Options
|
|
49
|
+
|
|
50
|
+
| Option | Type | Default | Description |
|
|
51
|
+
|--------|------|---------|-------------|
|
|
52
|
+
| `baseDir` | `string` | `process.cwd()` | Base directory for all file operations |
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### Via Platform (Recommended)
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { usePlatform } from '@kb-labs/sdk';
|
|
60
|
+
|
|
61
|
+
const platform = usePlatform();
|
|
62
|
+
|
|
63
|
+
// Read file
|
|
64
|
+
const content = await platform.storage.read('config.json');
|
|
65
|
+
|
|
66
|
+
// Write file
|
|
67
|
+
await platform.storage.write('output.txt', 'Hello, World!');
|
|
68
|
+
|
|
69
|
+
// List files with glob
|
|
70
|
+
const files = await platform.storage.glob('**/*.ts');
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Standalone (Testing/Development)
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { createAdapter } from '@kb-labs/adapters-fs';
|
|
77
|
+
|
|
78
|
+
const storage = createAdapter({ baseDir: '/path/to/data' });
|
|
79
|
+
|
|
80
|
+
await storage.write('test.txt', 'content');
|
|
81
|
+
const content = await storage.read('test.txt');
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## How It Works
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
88
|
+
│ Client │────▶│ FS Adapter │────▶│ File System │
|
|
89
|
+
└─────────────┘ └─────────────┘ └─────────────┘
|
|
90
|
+
│
|
|
91
|
+
Path Validation
|
|
92
|
+
(stays in baseDir)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The adapter validates all paths to ensure they stay within the configured `baseDir`, preventing path traversal attacks (e.g., `../../../etc/passwd`).
|
|
96
|
+
|
|
97
|
+
## Adapter Manifest
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
{
|
|
101
|
+
id: 'fs-storage',
|
|
102
|
+
name: 'Filesystem Storage',
|
|
103
|
+
version: '1.0.0',
|
|
104
|
+
implements: 'IStorage',
|
|
105
|
+
capabilities: {
|
|
106
|
+
streaming: true,
|
|
107
|
+
custom: {
|
|
108
|
+
glob: true,
|
|
109
|
+
metadata: true,
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Performance Considerations
|
|
116
|
+
|
|
117
|
+
- **Memory**: Uses streams for large files to avoid memory issues
|
|
118
|
+
- **Latency**: Depends on disk I/O, SSD recommended for production
|
|
119
|
+
- **Throughput**: Limited by disk speed and OS file system cache
|
|
120
|
+
|
|
121
|
+
## FAQ
|
|
122
|
+
|
|
123
|
+
<details>
|
|
124
|
+
<summary><strong>Q: Can I use this adapter outside KB Labs platform?</strong></summary>
|
|
125
|
+
|
|
126
|
+
No. This adapter is designed specifically for KB Labs ecosystem and depends on platform interfaces and contracts. Use `createAdapter()` for standalone testing only.
|
|
127
|
+
</details>
|
|
128
|
+
|
|
129
|
+
<details>
|
|
130
|
+
<summary><strong>Q: How do I prevent path traversal attacks?</strong></summary>
|
|
131
|
+
|
|
132
|
+
The adapter automatically validates all paths. Any attempt to access files outside `baseDir` will throw an error. You don't need to do anything extra.
|
|
133
|
+
</details>
|
|
134
|
+
|
|
135
|
+
<details>
|
|
136
|
+
<summary><strong>Q: Can I use absolute paths?</strong></summary>
|
|
137
|
+
|
|
138
|
+
No. All paths are relative to `baseDir`. This is by design for security.
|
|
139
|
+
</details>
|
|
140
|
+
|
|
141
|
+
## Related Adapters
|
|
142
|
+
|
|
143
|
+
| Adapter | Use Case |
|
|
144
|
+
|---------|----------|
|
|
145
|
+
| `@kb-labs/adapters-analytics-file` | File-based analytics storage |
|
|
146
|
+
|
|
147
|
+
## Troubleshooting
|
|
148
|
+
|
|
149
|
+
### Error: EACCES permission denied
|
|
150
|
+
|
|
151
|
+
**Cause**: The process doesn't have write permissions to the target directory.
|
|
152
|
+
|
|
153
|
+
**Solution**: Check directory permissions or choose a different `baseDir`:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
chmod 755 /path/to/your/baseDir
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Error: Path outside base directory
|
|
160
|
+
|
|
161
|
+
**Cause**: Attempted to access a file outside the sandboxed `baseDir`.
|
|
162
|
+
|
|
163
|
+
**Solution**: Use relative paths only. Don't use `..` to escape the directory.
|
|
164
|
+
|
|
165
|
+
## Contributing
|
|
166
|
+
|
|
167
|
+
See [CONTRIBUTING.md](../../CONTRIBUTING.md) for development guidelines.
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
[KB Public License v1.1](../../LICENSE) - KB Labs Team
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
content
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
content
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
content
|
|
@@ -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 @@
|
|
|
1
|
+
content
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
content
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
c
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kb-labs/adapters-fs",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Filesystem adapter implementing IStorage 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
|
+
"./secure-storage": {
|
|
14
|
+
"import": "./dist/secure-storage.js",
|
|
15
|
+
"types": "./dist/secure-storage.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"scripts": {
|
|
24
|
+
"clean": "rimraf dist",
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"dev": "tsup --watch",
|
|
27
|
+
"type-check": "tsc --noEmit",
|
|
28
|
+
"test": "vitest run --passWithNoTests",
|
|
29
|
+
"test:watch": "vitest",
|
|
30
|
+
"lint": "eslint src --ext .ts",
|
|
31
|
+
"lint:fix": "eslint . --fix"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"fast-glob": "^3.3.2",
|
|
35
|
+
"fs-extra": "^11.0.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@kb-labs/core-platform": "*"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@kb-labs/core-platform": "link:../../../../platform/kb-labs-core/packages/core-platform",
|
|
42
|
+
"@types/fs-extra": "^11.0.0",
|
|
43
|
+
"@types/node": "^24.3.3",
|
|
44
|
+
"eslint": "^9",
|
|
45
|
+
"tsup": "^8.5.0",
|
|
46
|
+
"typescript": "^5.6.3",
|
|
47
|
+
"vitest": "^3.2.4",
|
|
48
|
+
"@kb-labs/devkit": "link:../../../kb-labs-devkit",
|
|
49
|
+
"rimraf": "^6.0.1"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=20.0.0",
|
|
53
|
+
"pnpm": ">=9.0.0"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
content
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
secret
|