@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,428 @@
1
+ /**
2
+ * @module @kb-labs/adapters-mongodb
3
+ * MongoDB adapter implementing IDocumentDatabase interface.
4
+ *
5
+ * Features:
6
+ * - Based on official MongoDB Node.js driver
7
+ * - Connection pooling (automatic)
8
+ * - Type-safe document operations
9
+ * - Query operators ($eq, $ne, $gt, etc.)
10
+ * - Projection and sorting support
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { createAdapter } from '@kb-labs/adapters-mongodb';
15
+ *
16
+ * const db = createAdapter({
17
+ * uri: 'mongodb://localhost:27017',
18
+ * database: 'myapp',
19
+ * });
20
+ *
21
+ * // Find documents
22
+ * const users = await db.find('users', { age: { $gt: 18 } }, { limit: 10 });
23
+ *
24
+ * // Insert document
25
+ * await db.insertOne('users', { name: 'Alice', age: 25 });
26
+ *
27
+ * // Update documents
28
+ * await db.updateMany('users', { age: { $lt: 18 } }, { $set: { minor: true } });
29
+ *
30
+ * // Close connection
31
+ * await db.close();
32
+ * ```
33
+ */
34
+
35
+ import { randomUUID } from "node:crypto";
36
+ import { MongoClient, type Db, type Collection } from "mongodb";
37
+ import type {
38
+ IDocumentDatabase,
39
+ BaseDocument,
40
+ DocumentFilter,
41
+ DocumentUpdate,
42
+ FindOptions,
43
+ } from "@kb-labs/core-platform/adapters";
44
+
45
+ // Re-export manifest
46
+ export { manifest } from "./manifest.js";
47
+
48
+ /**
49
+ * Configuration for MongoDB adapter.
50
+ */
51
+ export interface MongoDBConfig {
52
+ /**
53
+ * MongoDB connection URI.
54
+ * @example 'mongodb://localhost:27017'
55
+ * @example 'mongodb+srv://user:pass@cluster.mongodb.net'
56
+ */
57
+ uri: string;
58
+
59
+ /**
60
+ * Database name.
61
+ */
62
+ database: string;
63
+
64
+ /**
65
+ * Connection options (optional).
66
+ */
67
+ options?: {
68
+ /** Max pool size (default: 10) */
69
+ maxPoolSize?: number;
70
+ /** Server selection timeout in ms (default: 30000) */
71
+ serverSelectionTimeoutMS?: number;
72
+ };
73
+ }
74
+
75
+ /**
76
+ * MongoDB implementation of IDocumentDatabase interface.
77
+ *
78
+ * Design:
79
+ * - Uses official MongoDB Node.js driver
80
+ * - Connection pooling handled automatically
81
+ * - Type-safe operations with generics
82
+ * - Maps MongoDB operators to DocumentFilter format
83
+ */
84
+ export class MongoDBAdapter implements IDocumentDatabase {
85
+ private client: MongoClient;
86
+ private db: Db;
87
+ private closed = false;
88
+
89
+ constructor(private config: MongoDBConfig) {
90
+ this.client = new MongoClient(config.uri, {
91
+ maxPoolSize: config.options?.maxPoolSize ?? 10,
92
+ serverSelectionTimeoutMS:
93
+ config.options?.serverSelectionTimeoutMS ?? 30000,
94
+ });
95
+
96
+ // Will connect lazily on first operation
97
+ this.db = this.client.db(config.database);
98
+ }
99
+
100
+ /**
101
+ * Ensure connection is established.
102
+ */
103
+ private async ensureConnected(): Promise<void> {
104
+ if (this.closed) {
105
+ throw new Error("Database connection is closed");
106
+ }
107
+
108
+ // MongoDB driver connects lazily, but we can trigger it explicitly
109
+ await this.client.connect();
110
+ }
111
+
112
+ /**
113
+ * Get collection reference.
114
+ */
115
+ private getCollection<T extends BaseDocument>(
116
+ collection: string,
117
+ ): Collection<T> {
118
+ return this.db.collection<T>(collection);
119
+ }
120
+
121
+ /**
122
+ * Find documents matching a filter.
123
+ *
124
+ * @param collection - Collection name
125
+ * @param filter - Query filter
126
+ * @param options - Find options (limit, skip, sort, projection)
127
+ * @returns Array of matching documents
128
+ */
129
+ async find<T extends BaseDocument>(
130
+ collection: string,
131
+ filter: DocumentFilter<T>,
132
+ options?: FindOptions,
133
+ ): Promise<T[]> {
134
+ await this.ensureConnected();
135
+
136
+ const col = this.getCollection<T>(collection);
137
+ let cursor = col.find(filter as any);
138
+
139
+ // Apply options
140
+ if (options?.limit) {
141
+ cursor = cursor.limit(options.limit);
142
+ }
143
+ if (options?.skip) {
144
+ cursor = cursor.skip(options.skip);
145
+ }
146
+ if (options?.sort) {
147
+ cursor = cursor.sort(options.sort as any);
148
+ }
149
+
150
+ return cursor.toArray() as Promise<T[]>;
151
+ }
152
+
153
+ /**
154
+ * Find a single document by ID.
155
+ *
156
+ * @param collection - Collection name
157
+ * @param id - Document ID
158
+ * @returns Document or null if not found
159
+ */
160
+ async findById<T extends BaseDocument>(
161
+ collection: string,
162
+ id: string,
163
+ ): Promise<T | null> {
164
+ await this.ensureConnected();
165
+
166
+ const col = this.getCollection<T>(collection);
167
+ const doc = await col.findOne({ _id: id } as any);
168
+
169
+ return doc as T | null;
170
+ }
171
+
172
+ /**
173
+ * Insert a single document.
174
+ *
175
+ * @param collection - Collection name
176
+ * @param document - Document to insert
177
+ * @returns Inserted document with generated fields
178
+ */
179
+ async insertOne<T extends BaseDocument>(
180
+ collection: string,
181
+ document: Omit<T, "id" | "createdAt" | "updatedAt">,
182
+ ): Promise<T> {
183
+ await this.ensureConnected();
184
+
185
+ const col = this.getCollection<T>(collection);
186
+
187
+ // Add generated fields (timestamps are Unix timestamps in milliseconds)
188
+ const now = Date.now();
189
+ const docWithMeta = {
190
+ ...document,
191
+ id: randomUUID(),
192
+ createdAt: now,
193
+ updatedAt: now,
194
+ };
195
+
196
+ await col.insertOne(docWithMeta as any);
197
+
198
+ return docWithMeta as T;
199
+ }
200
+
201
+ /**
202
+ * Insert multiple documents.
203
+ *
204
+ * @param collection - Collection name
205
+ * @param documents - Documents to insert
206
+ * @returns Array of inserted document IDs
207
+ */
208
+ async insertMany<T extends BaseDocument>(
209
+ collection: string,
210
+ documents: Array<Omit<T, "_id">>,
211
+ ): Promise<string[]> {
212
+ await this.ensureConnected();
213
+
214
+ if (documents.length === 0) {
215
+ return [];
216
+ }
217
+
218
+ const col = this.getCollection<T>(collection);
219
+ const result = await col.insertMany(documents as any);
220
+
221
+ return Object.values(result.insertedIds).map(String);
222
+ }
223
+
224
+ /**
225
+ * Update a single document.
226
+ *
227
+ * @param collection - Collection name
228
+ * @param filter - Query filter
229
+ * @param update - Update operations
230
+ * @returns Number of documents modified
231
+ */
232
+ async updateOne<T extends BaseDocument>(
233
+ collection: string,
234
+ filter: DocumentFilter<T>,
235
+ update: DocumentUpdate<T>,
236
+ ): Promise<number> {
237
+ await this.ensureConnected();
238
+
239
+ const col = this.getCollection<T>(collection);
240
+ const result = await col.updateOne(filter as any, update as any);
241
+
242
+ return result.modifiedCount;
243
+ }
244
+
245
+ /**
246
+ * Update multiple documents.
247
+ *
248
+ * @param collection - Collection name
249
+ * @param filter - Query filter
250
+ * @param update - Update operations
251
+ * @returns Number of documents modified
252
+ */
253
+ async updateMany<T extends BaseDocument>(
254
+ collection: string,
255
+ filter: DocumentFilter<T>,
256
+ update: DocumentUpdate<T>,
257
+ ): Promise<number> {
258
+ await this.ensureConnected();
259
+
260
+ const col = this.getCollection<T>(collection);
261
+ const result = await col.updateMany(filter as any, update as any);
262
+
263
+ return result.modifiedCount;
264
+ }
265
+
266
+ /**
267
+ * Update a single document by ID.
268
+ *
269
+ * @param collection - Collection name
270
+ * @param id - Document ID
271
+ * @param update - Update operations
272
+ * @returns Updated document or null if not found
273
+ */
274
+ async updateById<T extends BaseDocument>(
275
+ collection: string,
276
+ id: string,
277
+ update: DocumentUpdate<T>,
278
+ ): Promise<T | null> {
279
+ await this.ensureConnected();
280
+
281
+ const col = this.getCollection<T>(collection);
282
+
283
+ // findOneAndUpdate returns the updated document
284
+ const result = await col.findOneAndUpdate(
285
+ { id } as any,
286
+ {
287
+ ...update,
288
+ $set: { ...((update as any).$set || {}), updatedAt: Date.now() },
289
+ } as any,
290
+ { returnDocument: "after" },
291
+ );
292
+
293
+ return result as T | null;
294
+ }
295
+
296
+ /**
297
+ * Delete a single document.
298
+ *
299
+ * @param collection - Collection name
300
+ * @param filter - Query filter
301
+ * @returns Number of documents deleted
302
+ */
303
+ async deleteOne<T extends BaseDocument>(
304
+ collection: string,
305
+ filter: DocumentFilter<T>,
306
+ ): Promise<number> {
307
+ await this.ensureConnected();
308
+
309
+ const col = this.getCollection<T>(collection);
310
+ const result = await col.deleteOne(filter as any);
311
+
312
+ return result.deletedCount ?? 0;
313
+ }
314
+
315
+ /**
316
+ * Delete multiple documents.
317
+ *
318
+ * @param collection - Collection name
319
+ * @param filter - Query filter
320
+ * @returns Number of documents deleted
321
+ */
322
+ async deleteMany<T extends BaseDocument>(
323
+ collection: string,
324
+ filter: DocumentFilter<T>,
325
+ ): Promise<number> {
326
+ await this.ensureConnected();
327
+
328
+ const col = this.getCollection<T>(collection);
329
+ const result = await col.deleteMany(filter as any);
330
+
331
+ return result.deletedCount ?? 0;
332
+ }
333
+
334
+ /**
335
+ * Delete a single document by ID.
336
+ *
337
+ * @param collection - Collection name
338
+ * @param id - Document ID
339
+ * @returns True if deleted, false if not found
340
+ */
341
+ async deleteById(collection: string, id: string): Promise<boolean> {
342
+ await this.ensureConnected();
343
+
344
+ const col = this.getCollection(collection);
345
+ const result = await col.deleteOne({ id } as any);
346
+
347
+ return (result.deletedCount ?? 0) > 0;
348
+ }
349
+
350
+ /**
351
+ * Count documents matching a filter.
352
+ *
353
+ * @param collection - Collection name
354
+ * @param filter - Query filter
355
+ * @returns Number of matching documents
356
+ */
357
+ async count<T extends BaseDocument>(
358
+ collection: string,
359
+ filter: DocumentFilter<T>,
360
+ ): Promise<number> {
361
+ await this.ensureConnected();
362
+
363
+ const col = this.getCollection<T>(collection);
364
+ return col.countDocuments(filter as any);
365
+ }
366
+
367
+ /**
368
+ * Close the database connection.
369
+ */
370
+ async close(): Promise<void> {
371
+ if (!this.closed) {
372
+ await this.client.close();
373
+ this.closed = true;
374
+ }
375
+ }
376
+
377
+ // ═══════════════════════════════════════════════════════════════════════
378
+ // Utility methods (not part of IDocumentDatabase interface)
379
+ // ═══════════════════════════════════════════════════════════════════════
380
+
381
+ /**
382
+ * Check if database is open.
383
+ */
384
+ isOpen(): boolean {
385
+ return !this.closed;
386
+ }
387
+
388
+ /**
389
+ * Get underlying MongoDB client (for advanced usage).
390
+ * Use with caution - bypasses adapter interface.
391
+ */
392
+ getRawClient(): MongoClient {
393
+ return this.client;
394
+ }
395
+
396
+ /**
397
+ * Get underlying MongoDB Db instance (for advanced usage).
398
+ * Use with caution - bypasses adapter interface.
399
+ */
400
+ getRawDatabase(): Db {
401
+ return this.db;
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Create MongoDB database adapter.
407
+ * This is the factory function called by initPlatform() when loading adapters.
408
+ *
409
+ * @param config - MongoDB configuration
410
+ * @returns MongoDB adapter instance
411
+ *
412
+ * @example
413
+ * ```typescript
414
+ * const db = createAdapter({
415
+ * uri: 'mongodb://localhost:27017',
416
+ * database: 'myapp',
417
+ * options: {
418
+ * maxPoolSize: 20,
419
+ * },
420
+ * });
421
+ * ```
422
+ */
423
+ export function createAdapter(config: MongoDBConfig): MongoDBAdapter {
424
+ return new MongoDBAdapter(config);
425
+ }
426
+
427
+ // Default export for direct import
428
+ export default createAdapter;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @module @kb-labs/adapters-mongodb/manifest
3
+ * Adapter manifest for MongoDB document database.
4
+ */
5
+
6
+ import type { AdapterManifest } from "@kb-labs/core-platform";
7
+
8
+ /**
9
+ * Adapter manifest for MongoDB document database.
10
+ */
11
+ export const manifest: AdapterManifest = {
12
+ manifestVersion: "1.0.0",
13
+ id: "mongodb-documentdb",
14
+ name: "MongoDB Document Database",
15
+ version: "1.0.0",
16
+ description: "NoSQL document database using MongoDB",
17
+ author: "KB Labs Team",
18
+ license: "KBPL-1.1",
19
+ type: "core",
20
+ implements: "IDocumentDatabase",
21
+ capabilities: {
22
+ transactions: true,
23
+ search: true,
24
+ custom: {
25
+ aggregation: true,
26
+ indexes: true,
27
+ fullText: true,
28
+ },
29
+ },
30
+ configSchema: {
31
+ uri: {
32
+ type: "string",
33
+ description: "MongoDB connection URI (e.g., mongodb://localhost:27017)",
34
+ },
35
+ database: {
36
+ type: "string",
37
+ description: "Database name",
38
+ },
39
+ poolSize: {
40
+ type: "number",
41
+ default: 10,
42
+ description: "Connection pool size",
43
+ },
44
+ },
45
+ };
@@ -0,0 +1,231 @@
1
+ /**
2
+ * @module @kb-labs/adapters-mongodb/secure-document
3
+ * SecureDocumentAdapter - IDocumentDatabase wrapper with permission validation.
4
+ *
5
+ * Design Philosophy: Validation-only security (like fs-shim)
6
+ * - Validates collection access against allowlists/denylists
7
+ * - Does NOT rewrite queries or filters
8
+ * - Fails fast with clear errors
9
+ * - Transparent pass-through when permitted
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { createAdapter } from '@kb-labs/adapters-mongodb';
14
+ * import { SecureDocumentAdapter } from '@kb-labs/adapters-mongodb/secure-document';
15
+ *
16
+ * const base = createAdapter({ uri: 'mongodb://localhost:27017', database: 'myapp' });
17
+ * const secure = new SecureDocumentAdapter(base, {
18
+ * allowlist: ['users', 'posts', 'comments'],
19
+ * denylist: ['admin_users', 'secrets'],
20
+ * });
21
+ *
22
+ * await secure.find('users', { age: { $gt: 18 } }); // ✅ Allowed
23
+ * await secure.find('admin_users', {}); // ❌ Denied
24
+ * ```
25
+ */
26
+
27
+ import type {
28
+ IDocumentDatabase,
29
+ BaseDocument,
30
+ DocumentFilter,
31
+ DocumentUpdate,
32
+ FindOptions,
33
+ } from "@kb-labs/core-platform/adapters";
34
+
35
+ /**
36
+ * Permission configuration for document database access.
37
+ */
38
+ export interface DocumentPermissions {
39
+ /**
40
+ * Allowed collection names (e.g., ['users', 'posts']).
41
+ * If empty or undefined, all collections are allowed (unless denied).
42
+ */
43
+ allowlist?: string[];
44
+
45
+ /**
46
+ * Denied collection names (e.g., ['admin_users', 'secrets']).
47
+ * Takes precedence over allowlist.
48
+ */
49
+ denylist?: string[];
50
+
51
+ /**
52
+ * Allow read operations (find, findById, count) (default: true)
53
+ */
54
+ read?: boolean;
55
+
56
+ /**
57
+ * Allow write operations (insert, update) (default: true)
58
+ */
59
+ write?: boolean;
60
+
61
+ /**
62
+ * Allow delete operations (deleteOne, deleteMany) (default: false - safer default)
63
+ */
64
+ delete?: boolean;
65
+ }
66
+
67
+ /**
68
+ * Error thrown when document database access is denied.
69
+ */
70
+ export class DocumentPermissionError extends Error {
71
+ constructor(
72
+ public readonly operation: string,
73
+ public readonly collection: string,
74
+ public readonly reason: string,
75
+ ) {
76
+ super(
77
+ `Document database access denied: ${operation} on '${collection}' - ${reason}`,
78
+ );
79
+ this.name = "DocumentPermissionError";
80
+ }
81
+ }
82
+
83
+ /**
84
+ * SecureDocumentAdapter - validates permissions before delegating to base database.
85
+ *
86
+ * Design:
87
+ * - Validation-only (no query rewriting)
88
+ * - Fails fast with clear errors
89
+ * - Transparent pass-through when permitted
90
+ * - Supports both coarse (read/write/delete) and fine (collection-based) permissions
91
+ */
92
+ export class SecureDocumentAdapter implements IDocumentDatabase {
93
+ constructor(
94
+ private readonly baseDb: IDocumentDatabase,
95
+ private readonly permissions: DocumentPermissions,
96
+ ) {}
97
+
98
+ /**
99
+ * Check if a collection is allowed by permissions.
100
+ */
101
+ private checkCollection(
102
+ collection: string,
103
+ operation: "read" | "write" | "delete",
104
+ ): void {
105
+ // Check operation-level permissions
106
+ const operationAllowed = this.permissions[operation] !== false;
107
+ if (!operationAllowed) {
108
+ throw new DocumentPermissionError(
109
+ operation,
110
+ collection,
111
+ `${operation} operations are disabled`,
112
+ );
113
+ }
114
+
115
+ // Check denylist first (takes precedence)
116
+ if (this.permissions.denylist?.includes(collection)) {
117
+ throw new DocumentPermissionError(
118
+ operation,
119
+ collection,
120
+ `collection is in denylist`,
121
+ );
122
+ }
123
+
124
+ // Check allowlist (if defined)
125
+ if (this.permissions.allowlist && this.permissions.allowlist.length > 0 && !this.permissions.allowlist.includes(collection)) {
126
+ throw new DocumentPermissionError(
127
+ operation,
128
+ collection,
129
+ `collection not in allowlist: [${this.permissions.allowlist.join(", ")}]`,
130
+ );
131
+ }
132
+ }
133
+
134
+ // ═══════════════════════════════════════════════════════════════════════
135
+ // IDocumentDatabase methods with permission checks
136
+ // ═══════════════════════════════════════════════════════════════════════
137
+
138
+ async find<T extends BaseDocument>(
139
+ collection: string,
140
+ filter: DocumentFilter<T>,
141
+ options?: FindOptions,
142
+ ): Promise<T[]> {
143
+ this.checkCollection(collection, "read");
144
+ return this.baseDb.find<T>(collection, filter, options);
145
+ }
146
+
147
+ async findById<T extends BaseDocument>(
148
+ collection: string,
149
+ id: string,
150
+ ): Promise<T | null> {
151
+ this.checkCollection(collection, "read");
152
+ return this.baseDb.findById<T>(collection, id);
153
+ }
154
+
155
+ async insertOne<T extends BaseDocument>(
156
+ collection: string,
157
+ document: Omit<T, "id" | "createdAt" | "updatedAt">,
158
+ ): Promise<T> {
159
+ this.checkCollection(collection, "write");
160
+ return this.baseDb.insertOne<T>(collection, document);
161
+ }
162
+
163
+ async updateMany<T extends BaseDocument>(
164
+ collection: string,
165
+ filter: DocumentFilter<T>,
166
+ update: DocumentUpdate<T>,
167
+ ): Promise<number> {
168
+ this.checkCollection(collection, "write");
169
+ return this.baseDb.updateMany<T>(collection, filter, update);
170
+ }
171
+
172
+ async updateById<T extends BaseDocument>(
173
+ collection: string,
174
+ id: string,
175
+ update: DocumentUpdate<T>,
176
+ ): Promise<T | null> {
177
+ this.checkCollection(collection, "write");
178
+ return this.baseDb.updateById<T>(collection, id, update);
179
+ }
180
+
181
+ async deleteMany<T extends BaseDocument>(
182
+ collection: string,
183
+ filter: DocumentFilter<T>,
184
+ ): Promise<number> {
185
+ this.checkCollection(collection, "delete");
186
+ return this.baseDb.deleteMany<T>(collection, filter);
187
+ }
188
+
189
+ async deleteById(collection: string, id: string): Promise<boolean> {
190
+ this.checkCollection(collection, "delete");
191
+ return this.baseDb.deleteById(collection, id);
192
+ }
193
+
194
+ async count<T extends BaseDocument>(
195
+ collection: string,
196
+ filter: DocumentFilter<T>,
197
+ ): Promise<number> {
198
+ this.checkCollection(collection, "read");
199
+ return this.baseDb.count<T>(collection, filter);
200
+ }
201
+
202
+ async close(): Promise<void> {
203
+ await this.baseDb.close();
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Create secure document adapter with permission validation.
209
+ *
210
+ * @param baseDb - Base document adapter (MongoDB, etc.)
211
+ * @param permissions - Permission configuration
212
+ * @returns Wrapped document adapter with permission checks
213
+ *
214
+ * @example
215
+ * ```typescript
216
+ * const secure = createSecureDocument(base, {
217
+ * allowlist: ['users', 'posts'],
218
+ * denylist: ['admin_users'],
219
+ * delete: false, // Prevent deletions
220
+ * });
221
+ * ```
222
+ */
223
+ export function createSecureDocument(
224
+ baseDb: IDocumentDatabase,
225
+ permissions: DocumentPermissions,
226
+ ): SecureDocumentAdapter {
227
+ return new SecureDocumentAdapter(baseDb, permissions);
228
+ }
229
+
230
+ // Default export for direct import
231
+ export default createSecureDocument;
@@ -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
+ }