@procedures/mongo 0.0.0-stage → 0.1.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 (150) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1322 -2
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/connection.d.ts +93 -0
  6. package/dist/connection.d.ts.map +1 -0
  7. package/dist/connection.js +147 -0
  8. package/dist/connection.js.map +1 -0
  9. package/dist/index.d.ts +34 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +45 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/module.d.ts +20 -0
  14. package/dist/module.d.ts.map +1 -0
  15. package/dist/module.js +58 -0
  16. package/dist/module.js.map +1 -0
  17. package/dist/procedures/collections.create.d.ts +20 -0
  18. package/dist/procedures/collections.create.d.ts.map +1 -0
  19. package/dist/procedures/collections.create.js +43 -0
  20. package/dist/procedures/collections.create.js.map +1 -0
  21. package/dist/procedures/collections.drop.d.ts +19 -0
  22. package/dist/procedures/collections.drop.d.ts.map +1 -0
  23. package/dist/procedures/collections.drop.js +36 -0
  24. package/dist/procedures/collections.drop.js.map +1 -0
  25. package/dist/procedures/collections.list.d.ts +16 -0
  26. package/dist/procedures/collections.list.d.ts.map +1 -0
  27. package/dist/procedures/collections.list.js +31 -0
  28. package/dist/procedures/collections.list.js.map +1 -0
  29. package/dist/procedures/collections.stats.d.ts +15 -0
  30. package/dist/procedures/collections.stats.d.ts.map +1 -0
  31. package/dist/procedures/collections.stats.js +38 -0
  32. package/dist/procedures/collections.stats.js.map +1 -0
  33. package/dist/procedures/database.info.d.ts +15 -0
  34. package/dist/procedures/database.info.d.ts.map +1 -0
  35. package/dist/procedures/database.info.js +38 -0
  36. package/dist/procedures/database.info.js.map +1 -0
  37. package/dist/procedures/database.ping.d.ts +17 -0
  38. package/dist/procedures/database.ping.d.ts.map +1 -0
  39. package/dist/procedures/database.ping.js +31 -0
  40. package/dist/procedures/database.ping.js.map +1 -0
  41. package/dist/procedures/documents.aggregate.d.ts +19 -0
  42. package/dist/procedures/documents.aggregate.d.ts.map +1 -0
  43. package/dist/procedures/documents.aggregate.js +39 -0
  44. package/dist/procedures/documents.aggregate.js.map +1 -0
  45. package/dist/procedures/documents.count.d.ts +18 -0
  46. package/dist/procedures/documents.count.d.ts.map +1 -0
  47. package/dist/procedures/documents.count.js +29 -0
  48. package/dist/procedures/documents.count.js.map +1 -0
  49. package/dist/procedures/documents.delete.d.ts +17 -0
  50. package/dist/procedures/documents.delete.d.ts.map +1 -0
  51. package/dist/procedures/documents.delete.js +31 -0
  52. package/dist/procedures/documents.delete.js.map +1 -0
  53. package/dist/procedures/documents.find.d.ts +23 -0
  54. package/dist/procedures/documents.find.d.ts.map +1 -0
  55. package/dist/procedures/documents.find.js +103 -0
  56. package/dist/procedures/documents.find.js.map +1 -0
  57. package/dist/procedures/documents.get.d.ts +20 -0
  58. package/dist/procedures/documents.get.d.ts.map +1 -0
  59. package/dist/procedures/documents.get.js +54 -0
  60. package/dist/procedures/documents.get.js.map +1 -0
  61. package/dist/procedures/documents.insert.d.ts +21 -0
  62. package/dist/procedures/documents.insert.d.ts.map +1 -0
  63. package/dist/procedures/documents.insert.js +55 -0
  64. package/dist/procedures/documents.insert.js.map +1 -0
  65. package/dist/procedures/documents.update.d.ts +26 -0
  66. package/dist/procedures/documents.update.d.ts.map +1 -0
  67. package/dist/procedures/documents.update.js +61 -0
  68. package/dist/procedures/documents.update.js.map +1 -0
  69. package/dist/procedures/index.d.ts +21 -0
  70. package/dist/procedures/index.d.ts.map +1 -0
  71. package/dist/procedures/index.js +26 -0
  72. package/dist/procedures/index.js.map +1 -0
  73. package/dist/procedures/indexes.create.d.ts +20 -0
  74. package/dist/procedures/indexes.create.d.ts.map +1 -0
  75. package/dist/procedures/indexes.create.js +44 -0
  76. package/dist/procedures/indexes.create.js.map +1 -0
  77. package/dist/procedures/indexes.drop.d.ts +17 -0
  78. package/dist/procedures/indexes.drop.d.ts.map +1 -0
  79. package/dist/procedures/indexes.drop.js +30 -0
  80. package/dist/procedures/indexes.drop.js.map +1 -0
  81. package/dist/procedures/indexes.list.d.ts +17 -0
  82. package/dist/procedures/indexes.list.d.ts.map +1 -0
  83. package/dist/procedures/indexes.list.js +40 -0
  84. package/dist/procedures/indexes.list.js.map +1 -0
  85. package/dist/procedures/input-schemas.d.ts +45 -0
  86. package/dist/procedures/input-schemas.d.ts.map +1 -0
  87. package/dist/procedures/input-schemas.js +52 -0
  88. package/dist/procedures/input-schemas.js.map +1 -0
  89. package/dist/procedures/schema.d.ts +2 -0
  90. package/dist/procedures/schema.d.ts.map +1 -0
  91. package/dist/procedures/schema.js +2 -0
  92. package/dist/procedures/schema.js.map +1 -0
  93. package/dist/procedures/target.d.ts +44 -0
  94. package/dist/procedures/target.d.ts.map +1 -0
  95. package/dist/procedures/target.js +98 -0
  96. package/dist/procedures/target.js.map +1 -0
  97. package/dist/procedures.types.d.ts +129 -0
  98. package/dist/procedures.types.d.ts.map +1 -0
  99. package/dist/procedures.types.js +24 -0
  100. package/dist/procedures.types.js.map +1 -0
  101. package/dist/register.d.ts +8 -0
  102. package/dist/register.d.ts.map +1 -0
  103. package/dist/register.js +14 -0
  104. package/dist/register.js.map +1 -0
  105. package/dist/scope.d.ts +33 -0
  106. package/dist/scope.d.ts.map +1 -0
  107. package/dist/scope.js +52 -0
  108. package/dist/scope.js.map +1 -0
  109. package/dist/storage/index.d.ts +6 -0
  110. package/dist/storage/index.d.ts.map +1 -0
  111. package/dist/storage/index.js +5 -0
  112. package/dist/storage/index.js.map +1 -0
  113. package/dist/storage/mongo-storage.d.ts +50 -0
  114. package/dist/storage/mongo-storage.d.ts.map +1 -0
  115. package/dist/storage/mongo-storage.js +109 -0
  116. package/dist/storage/mongo-storage.js.map +1 -0
  117. package/dist/types.d.ts +239 -0
  118. package/dist/types.d.ts.map +1 -0
  119. package/dist/types.js +58 -0
  120. package/dist/types.js.map +1 -0
  121. package/package.json +85 -3
  122. package/src/connection.ts +198 -0
  123. package/src/index.ts +159 -0
  124. package/src/module.ts +62 -0
  125. package/src/procedures/collections.create.ts +69 -0
  126. package/src/procedures/collections.drop.ts +62 -0
  127. package/src/procedures/collections.list.ts +53 -0
  128. package/src/procedures/collections.stats.ts +57 -0
  129. package/src/procedures/database.info.ts +58 -0
  130. package/src/procedures/database.ping.ts +54 -0
  131. package/src/procedures/documents.aggregate.ts +68 -0
  132. package/src/procedures/documents.count.ts +52 -0
  133. package/src/procedures/documents.delete.ts +54 -0
  134. package/src/procedures/documents.find.ts +147 -0
  135. package/src/procedures/documents.get.ts +98 -0
  136. package/src/procedures/documents.insert.ts +83 -0
  137. package/src/procedures/documents.update.ts +93 -0
  138. package/src/procedures/index.ts +30 -0
  139. package/src/procedures/indexes.create.ts +70 -0
  140. package/src/procedures/indexes.drop.ts +53 -0
  141. package/src/procedures/indexes.list.ts +64 -0
  142. package/src/procedures/input-schemas.ts +63 -0
  143. package/src/procedures/schema.ts +1 -0
  144. package/src/procedures/target.ts +123 -0
  145. package/src/procedures.types.ts +162 -0
  146. package/src/register.ts +17 -0
  147. package/src/scope.ts +69 -0
  148. package/src/storage/index.ts +6 -0
  149. package/src/storage/mongo-storage.ts +151 -0
  150. package/src/types.ts +337 -0
package/README.md CHANGED
@@ -1,3 +1,1323 @@
1
- # Temporary Holding Version
1
+ # @procedures/mongo
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm version](https://img.shields.io/npm/v/@procedures/mongo.svg)](https://www.npmjs.com/package/@procedures/mongo)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
6
+ [![Node.js](https://img.shields.io/badge/Node.js-20+-green.svg)](https://nodejs.org/)
7
+
8
+ MongoDB client wrapper with client procedures for local or RPC access. Provides a comprehensive set of procedures for database operations, collection management, document CRUD, indexing, and aggregation pipeline support.
9
+
10
+ ## Overview
11
+
12
+ `client-mongo` bridges MongoDB operations into the client procedure ecosystem, enabling both:
13
+ - **Local usage**: Direct MongoDB connection with native driver
14
+ - **RPC usage**: Remote procedure calls via `client.call()`
15
+
16
+ The package follows the client procedure pattern, auto-registering 16 procedures organized into 4 categories:
17
+ - **Database**: `ping`, `info`
18
+ - **Collections**: `list`, `create`, `drop`, `stats`
19
+ - **Documents**: `find`, `get`, `insert`, `update`, `delete`, `count`, `aggregate`
20
+ - **Indexes**: `list`, `create`, `drop`
21
+
22
+ ## Architecture
23
+
24
+ ```mermaid
25
+ graph TB
26
+ subgraph "Client Ecosystem"
27
+ Client["@procedures/client"]
28
+ Registry["Procedure Registry"]
29
+ end
30
+
31
+ subgraph "client-mongo Package"
32
+ Register["register.ts<br/>(Auto-registers procedures)"]
33
+ Connection["connection.ts<br/>(Connection Management)"]
34
+
35
+ subgraph "Procedure Categories"
36
+ DB["Database Procedures<br/>ping, info"]
37
+ Collections["Collection Procedures<br/>list, create, drop, stats"]
38
+ Documents["Document Procedures<br/>find, get, insert, update, delete, count, aggregate"]
39
+ Indexes["Index Procedures<br/>list, create, drop"]
40
+ end
41
+
42
+ Types["types.ts<br/>(Type Definitions)"]
43
+ ProcTypes["procedures.types.ts<br/>(Typed Interface)"]
44
+ end
45
+
46
+ subgraph "MongoDB Driver"
47
+ MongoDB["mongodb npm package"]
48
+ MongoClient["MongoClient"]
49
+ Db["Db Instance"]
50
+ end
51
+
52
+ subgraph "External Systems"
53
+ MongoServer["MongoDB Server<br/>(local or remote)"]
54
+ end
55
+
56
+ Register --> Client
57
+ Register --> Registry
58
+
59
+ DB --> Connection
60
+ Collections --> Connection
61
+ Documents --> Connection
62
+ Indexes --> Connection
63
+
64
+ Connection --> MongoClient
65
+ MongoClient --> Db
66
+ Db --> MongoServer
67
+
68
+ Types -.-> DB
69
+ Types -.-> Collections
70
+ Types -.-> Documents
71
+ Types -.-> Indexes
72
+
73
+ ProcTypes -.provides typing.-> Client
74
+
75
+ style Register fill:#e1f5ff
76
+ style Connection fill:#fff4e1
77
+ style Types fill:#f0f0f0
78
+ ```
79
+
80
+ ### Package Dependency Flow
81
+
82
+ ```mermaid
83
+ graph LR
84
+ App["Your Application"]
85
+ ClientMongo["@procedures/mongo"]
86
+ Client["@procedures/client"]
87
+ MongoDB["mongodb"]
88
+
89
+ App --> ClientMongo
90
+ ClientMongo --> Client
91
+ ClientMongo --> MongoDB
92
+
93
+ App -.client.call.-> Client
94
+ Client -.executes.-> ClientMongo
95
+ ClientMongo -.queries.-> MongoDB
96
+ ```
97
+
98
+ ### Connection Management Flow
99
+
100
+ ```mermaid
101
+ sequenceDiagram
102
+ participant App
103
+ participant Connection
104
+ participant MongoClient
105
+ participant MongoDB
106
+
107
+ App->>Connection: connect({ uri, database })
108
+ Connection->>MongoClient: new MongoClient(uri)
109
+ MongoClient->>MongoDB: Connect
110
+ MongoDB-->>MongoClient: Connected
111
+ Connection->>Connection: setDefaultConnection()
112
+ Connection-->>App: MongoConnection
113
+
114
+ App->>Connection: getDb()
115
+ Connection-->>App: Db instance
116
+
117
+ App->>Connection: disconnect()
118
+ Connection->>MongoClient: close()
119
+ MongoClient->>MongoDB: Disconnect
120
+ ```
121
+
122
+ ## Installation
123
+
124
+ ```bash
125
+ npm install @procedures/mongo
126
+ ```
127
+
128
+ ### Peer Dependencies
129
+
130
+ ```json
131
+ {
132
+ "@procedures/client": "github:mark1russell7/client#main",
133
+ "mongodb": "^7.0.0"
134
+ }
135
+ ```
136
+
137
+ ## Configuration
138
+
139
+ ### Environment Variables
140
+
141
+ ```bash
142
+ # MongoDB connection URI (optional, defaults to mongodb://localhost:27017)
143
+ MONGODB_URI=mongodb://localhost:27017
144
+
145
+ # Default database name (optional, defaults to "test")
146
+ MONGODB_DATABASE=myapp
147
+ ```
148
+
149
+ ### Programmatic Configuration
150
+
151
+ ```typescript
152
+ import { connect } from "@procedures/mongo";
153
+
154
+ // Using environment variables
155
+ await connect();
156
+
157
+ // With explicit options
158
+ await connect({
159
+ uri: "mongodb://localhost:27017",
160
+ database: "myapp",
161
+ clientOptions: {
162
+ // Any MongoDB client options
163
+ maxPoolSize: 10,
164
+ minPoolSize: 2,
165
+ }
166
+ });
167
+ ```
168
+
169
+ ### The first call connects
170
+
171
+ A host does not have to call `connect()`. When there is no open connection, the first procedure call connects with the options of `configure()`, or with the environment variables. Concurrent first calls share one attempt.
172
+
173
+ ```typescript
174
+ import { configure } from "@procedures/mongo";
175
+
176
+ configure({ uri: "mongodb://localhost:27017", database: "myapp" });
177
+ ```
178
+
179
+ ### The collection and the database of a call
180
+
181
+ Each procedure reads `collection` and `database` from its input. An MCP tool call can set them. The call metadata is the fallback, for the clients that set it.
182
+
183
+ ```typescript
184
+ await client.exec(["mongo", "documents", "count"], { collection: "users", query: { active: true } });
185
+ ```
186
+
187
+ ## API Reference
188
+
189
+ ### Connection Management
190
+
191
+ #### `connect(options?): Promise<MongoConnection>`
192
+
193
+ Create and connect to MongoDB. Sets the default connection used by all procedures.
194
+
195
+ ```typescript
196
+ interface MongoConnectionOptions {
197
+ uri?: string; // Default: MONGODB_URI or mongodb://localhost:27017
198
+ database?: string; // Default: MONGODB_DATABASE or "test"
199
+ clientOptions?: MongoClientOptions;
200
+ }
201
+
202
+ interface MongoConnection {
203
+ getDb(): Db;
204
+ getClient(): MongoClient;
205
+ connect(): Promise<void>;
206
+ disconnect(): Promise<void>;
207
+ isConnected(): boolean;
208
+ }
209
+ ```
210
+
211
+ **Example:**
212
+ ```typescript
213
+ const conn = await connect({
214
+ uri: "mongodb://localhost:27017",
215
+ database: "myapp"
216
+ });
217
+ ```
218
+
219
+ #### `disconnect(): Promise<void>`
220
+
221
+ Disconnect the default connection.
222
+
223
+ ```typescript
224
+ await disconnect();
225
+ ```
226
+
227
+ #### `getDb(): Db`
228
+
229
+ Get the database instance from the default connection.
230
+
231
+ ```typescript
232
+ const db = getDb();
233
+ const collection = db.collection("users");
234
+ ```
235
+
236
+ #### `getClient(): MongoClient`
237
+
238
+ Get the MongoClient from the default connection.
239
+
240
+ ```typescript
241
+ const client = getClient();
242
+ ```
243
+
244
+ #### `getDefaultConnection(): MongoConnection`
245
+
246
+ Get the default connection instance. Throws if not connected.
247
+
248
+ #### `setDefaultConnection(conn: MongoConnection): void`
249
+
250
+ Set the default connection (advanced usage).
251
+
252
+ #### `hasDefaultConnection(): boolean`
253
+
254
+ Check if a default connection exists.
255
+
256
+ ### Registered Procedures
257
+
258
+ All procedures are automatically registered when the package is imported. They can be called via `client.call()` or used directly.
259
+
260
+ #### Database Procedures
261
+
262
+ ##### `mongo.database.ping`
263
+
264
+ Test database connectivity and measure latency.
265
+
266
+ ```typescript
267
+ // Via RPC
268
+ const result = await client.exec(
269
+ ["mongo", "database", "ping"],
270
+ { timeout: 5000 }
271
+ );
272
+
273
+ // Direct import
274
+ import { pingProcedure } from "@procedures/mongo";
275
+ ```
276
+
277
+ **Input:**
278
+ ```typescript
279
+ interface PingInput {
280
+ timeout?: number; // Optional timeout in milliseconds
281
+ }
282
+ ```
283
+
284
+ **Output:**
285
+ ```typescript
286
+ interface PingOutput {
287
+ latencyMs: number; // Response time in milliseconds
288
+ ok: boolean; // Server status
289
+ }
290
+ ```
291
+
292
+ ##### `mongo.database.info`
293
+
294
+ Get database information and statistics.
295
+
296
+ ```typescript
297
+ const result = await client.exec(
298
+ ["mongo", "database", "info"],
299
+ { includeStats: true }
300
+ );
301
+ ```
302
+
303
+ **Input:**
304
+ ```typescript
305
+ interface InfoInput {
306
+ includeStats?: boolean;
307
+ }
308
+ ```
309
+
310
+ **Output:**
311
+ ```typescript
312
+ interface DatabaseInfo {
313
+ name: string; // Database name
314
+ collections: number; // Number of collections
315
+ views: number; // Number of views
316
+ sizeOnDisk: number; // Size on disk in bytes
317
+ empty: boolean; // Whether database is empty
318
+ }
319
+ ```
320
+
321
+ #### Collection Procedures
322
+
323
+ ##### `mongo.collections.list`
324
+
325
+ List all collections in the database.
326
+
327
+ ```typescript
328
+ const result = await client.exec(
329
+ ["mongo", "collections", "list"],
330
+ { filter: "user" }
331
+ );
332
+ ```
333
+
334
+ **Input:**
335
+ ```typescript
336
+ interface ListCollectionsInput {
337
+ filter?: string; // Filter by name pattern
338
+ }
339
+ ```
340
+
341
+ **Output:**
342
+ ```typescript
343
+ interface ListCollectionsOutput {
344
+ collections: string[]; // Collection names
345
+ }
346
+ ```
347
+
348
+ ##### `mongo.collections.create`
349
+
350
+ Create a new collection.
351
+
352
+ ```typescript
353
+ const result = await client.exec(
354
+ ["mongo", "collections", "create"],
355
+ {
356
+ name: "logs",
357
+ options: {
358
+ capped: true,
359
+ size: 10485760, // 10MB
360
+ max: 1000
361
+ }
362
+ }
363
+ );
364
+ ```
365
+
366
+ **Input:**
367
+ ```typescript
368
+ interface CreateCollectionInput {
369
+ name: string;
370
+ options?: CollectionOptions;
371
+ }
372
+
373
+ interface CollectionOptions {
374
+ capped?: boolean;
375
+ size?: number; // Max size in bytes (for capped)
376
+ max?: number; // Max documents (for capped)
377
+ validator?: Document; // JSON Schema validator
378
+ }
379
+ ```
380
+
381
+ **Output:**
382
+ ```typescript
383
+ interface CreateCollectionOutput {
384
+ created: boolean;
385
+ name: string;
386
+ }
387
+ ```
388
+
389
+ ##### `mongo.collections.drop`
390
+
391
+ Drop a collection.
392
+
393
+ ```typescript
394
+ const result = await client.exec(
395
+ ["mongo", "collections", "drop"],
396
+ { name: "old_collection" }
397
+ );
398
+ ```
399
+
400
+ **Input:**
401
+ ```typescript
402
+ interface DropCollectionInput {
403
+ name: string;
404
+ }
405
+ ```
406
+
407
+ **Output:**
408
+ ```typescript
409
+ interface DropCollectionOutput {
410
+ dropped: boolean;
411
+ }
412
+ ```
413
+
414
+ ##### `mongo.collections.stats`
415
+
416
+ Get collection statistics.
417
+
418
+ ```typescript
419
+ const result = await client.exec(
420
+ ["mongo", "collections", "stats"],
421
+ {},
422
+ { metadata: { collection: "users" } }
423
+ );
424
+ ```
425
+
426
+ **Input:**
427
+ ```typescript
428
+ interface StatsInput {
429
+ // Collection from metadata
430
+ }
431
+ ```
432
+
433
+ **Output:**
434
+ ```typescript
435
+ interface CollectionStats {
436
+ count: number; // Number of documents
437
+ size: number; // Size of documents in bytes
438
+ avgObjSize: number; // Average document size
439
+ storageSize: number; // Total storage size
440
+ nindexes: number; // Number of indexes
441
+ totalIndexSize: number; // Total index size
442
+ }
443
+ ```
444
+
445
+ #### Document Procedures
446
+
447
+ ##### `mongo.documents.find`
448
+
449
+ Find documents with pagination, filtering, sorting, and projection.
450
+
451
+ ```typescript
452
+ const result = await client.exec(
453
+ ["mongo", "documents", "find"],
454
+ {
455
+ query: { status: "active", age: { $gte: 18 } },
456
+ projection: { name: 1, email: 1 },
457
+ sort: { createdAt: -1 },
458
+ page: 1,
459
+ limit: 20
460
+ },
461
+ { metadata: { collection: "users" } }
462
+ );
463
+ ```
464
+
465
+ **Input:**
466
+ ```typescript
467
+ interface FindInput {
468
+ query?: DocumentQuery; // MongoDB filter
469
+ projection?: Record<string, 0 | 1>; // Field projection
470
+ sort?: SortSpec; // Sort specification
471
+ page?: number; // Page number (1-indexed)
472
+ limit?: number; // Items per page (max 100)
473
+ }
474
+
475
+ type DocumentQuery = Filter<Document>;
476
+ type SortSpec = Record<string, 1 | -1>;
477
+ ```
478
+
479
+ **Output:**
480
+ ```typescript
481
+ interface FindOutput {
482
+ documents: MongoDocument[];
483
+ pagination: PaginationOutput;
484
+ }
485
+
486
+ interface PaginationOutput {
487
+ page: number;
488
+ limit: number;
489
+ total: number;
490
+ totalPages: number;
491
+ hasNext: boolean;
492
+ hasPrev: boolean;
493
+ }
494
+ ```
495
+
496
+ ##### `mongo.documents.get`
497
+
498
+ Get a single document by ID.
499
+
500
+ ```typescript
501
+ const result = await client.exec(
502
+ ["mongo", "documents", "get"],
503
+ { id: "507f1f77bcf86cd799439011" },
504
+ { metadata: { collection: "users" } }
505
+ );
506
+ ```
507
+
508
+ **Input:**
509
+ ```typescript
510
+ interface GetInput {
511
+ id: string; // Document ID (converted to ObjectId)
512
+ }
513
+ ```
514
+
515
+ **Output:**
516
+ ```typescript
517
+ interface GetOutput {
518
+ document: MongoDocument | null;
519
+ }
520
+ ```
521
+
522
+ ##### `mongo.documents.insert`
523
+
524
+ Insert one or more documents.
525
+
526
+ ```typescript
527
+ // Insert single document
528
+ const result = await client.exec(
529
+ ["mongo", "documents", "insert"],
530
+ {
531
+ documents: {
532
+ name: "John Doe",
533
+ email: "john@example.com",
534
+ createdAt: new Date()
535
+ }
536
+ },
537
+ { metadata: { collection: "users" } }
538
+ );
539
+
540
+ // Insert multiple documents
541
+ const result = await client.exec(
542
+ ["mongo", "documents", "insert"],
543
+ {
544
+ documents: [
545
+ { name: "Alice" },
546
+ { name: "Bob" }
547
+ ]
548
+ },
549
+ { metadata: { collection: "users" } }
550
+ );
551
+ ```
552
+
553
+ **Input:**
554
+ ```typescript
555
+ interface InsertInput {
556
+ documents: MongoDocument | MongoDocument[];
557
+ }
558
+ ```
559
+
560
+ **Output:**
561
+ ```typescript
562
+ interface InsertOutput {
563
+ insertedCount: number;
564
+ insertedIds: string[];
565
+ }
566
+ ```
567
+
568
+ ##### `mongo.documents.update`
569
+
570
+ Update one document by `id`, or the documents that match a `filter`.
571
+
572
+ A write needs exactly one of `id` or `filter`. An unknown field is an error: `query` is the field of `find` and `count`, not of `update`. An empty filter matches every document, so it needs `confirm: true`.
573
+
574
+ ```typescript
575
+ // Update one document by ID
576
+ const result = await client.exec(
577
+ ["mongo", "documents", "update"],
578
+ {
579
+ collection: "users",
580
+ id: "...",
581
+ update: { $set: { status: "inactive" } },
582
+ upsert: false
583
+ }
584
+ );
585
+
586
+ // Update multiple documents
587
+ const result = await client.exec(
588
+ ["mongo", "documents", "update"],
589
+ {
590
+ collection: "users",
591
+ filter: { status: "pending" },
592
+ update: { $set: { status: "active" } },
593
+ multi: true
594
+ }
595
+ );
596
+ ```
597
+
598
+ **Input:**
599
+ ```typescript
600
+ interface UpdateInput {
601
+ collection?: string; // Or metadata.collection
602
+ database?: string; // Or metadata.database
603
+ id?: string; // Exactly one of id and filter
604
+ idType?: "auto" | "objectId" | "string";
605
+ filter?: DocumentQuery;
606
+ update: DocumentUpdate;
607
+ multi?: boolean; // Update all matching documents
608
+ upsert?: boolean; // Insert if not found
609
+ confirm?: boolean; // Required for an empty filter
610
+ }
611
+
612
+ type DocumentUpdate = UpdateFilter<Document>;
613
+ ```
614
+
615
+ An upsert by `id` in the "auto" mode updates the document with that ID in either form. When no document matches, the new document gets the ObjectId form of the ID.
616
+
617
+ **Output:**
618
+ ```typescript
619
+ interface UpdateOutput {
620
+ acknowledged: boolean;
621
+ matchedCount: number;
622
+ modifiedCount: number;
623
+ upsertedId: string | null;
624
+ upsertedCount: number;
625
+ }
626
+ ```
627
+
628
+ ##### `mongo.documents.delete`
629
+
630
+ Delete one document by `id`, or the documents that match a `filter`. The rules of `update` apply: exactly one target, no unknown fields, and `confirm: true` for an empty filter.
631
+
632
+ ```typescript
633
+ // Delete one document by ID
634
+ const result = await client.exec(
635
+ ["mongo", "documents", "delete"],
636
+ { collection: "users", id: "..." }
637
+ );
638
+
639
+ // Delete multiple documents
640
+ const result = await client.exec(
641
+ ["mongo", "documents", "delete"],
642
+ {
643
+ collection: "users",
644
+ filter: { status: "inactive" },
645
+ multi: true
646
+ }
647
+ );
648
+ ```
649
+
650
+ **Input:**
651
+ ```typescript
652
+ interface DeleteInput {
653
+ collection?: string; // Or metadata.collection
654
+ database?: string; // Or metadata.database
655
+ id?: string; // Exactly one of id and filter
656
+ idType?: "auto" | "objectId" | "string";
657
+ filter?: DocumentQuery;
658
+ multi?: boolean; // Delete all matching documents
659
+ confirm?: boolean; // Required for an empty filter
660
+ }
661
+ ```
662
+
663
+ **Output:**
664
+ ```typescript
665
+ interface DeleteOutput {
666
+ acknowledged: boolean;
667
+ deletedCount: number;
668
+ }
669
+ ```
670
+
671
+ ##### `mongo.documents.count`
672
+
673
+ Count documents matching a query.
674
+
675
+ ```typescript
676
+ const result = await client.exec(
677
+ ["mongo", "documents", "count"],
678
+ { query: { status: "active" } },
679
+ { metadata: { collection: "users" } }
680
+ );
681
+ ```
682
+
683
+ **Input:**
684
+ ```typescript
685
+ interface CountInput {
686
+ query?: DocumentQuery;
687
+ }
688
+ ```
689
+
690
+ **Output:**
691
+ ```typescript
692
+ interface CountOutput {
693
+ count: number;
694
+ }
695
+ ```
696
+
697
+ ##### `mongo.documents.aggregate`
698
+
699
+ Execute an aggregation pipeline.
700
+
701
+ ```typescript
702
+ const result = await client.exec(
703
+ ["mongo", "documents", "aggregate"],
704
+ {
705
+ pipeline: [
706
+ { $match: { status: "active" } },
707
+ { $group: { _id: "$country", count: { $sum: 1 } } },
708
+ { $sort: { count: -1 } }
709
+ ],
710
+ options: {
711
+ allowDiskUse: true,
712
+ maxTimeMS: 5000
713
+ }
714
+ },
715
+ { metadata: { collection: "users" } }
716
+ );
717
+ ```
718
+
719
+ **Input:**
720
+ ```typescript
721
+ interface AggregateInput {
722
+ pipeline: AggregationStage[];
723
+ options?: AggregationOptions;
724
+ }
725
+
726
+ type AggregationStage = Document;
727
+
728
+ interface AggregationOptions {
729
+ allowDiskUse?: boolean;
730
+ maxTimeMS?: number;
731
+ }
732
+ ```
733
+
734
+ **Output:**
735
+ ```typescript
736
+ interface AggregateOutput {
737
+ results: MongoDocument[];
738
+ }
739
+ ```
740
+
741
+ #### Index Procedures
742
+
743
+ ##### `mongo.indexes.list`
744
+
745
+ List all indexes on a collection.
746
+
747
+ ```typescript
748
+ const result = await client.exec(
749
+ ["mongo", "indexes", "list"],
750
+ {},
751
+ { metadata: { collection: "users" } }
752
+ );
753
+ ```
754
+
755
+ **Input:**
756
+ ```typescript
757
+ interface ListIndexesInput {
758
+ // Collection from metadata
759
+ }
760
+ ```
761
+
762
+ **Output:**
763
+ ```typescript
764
+ interface ListIndexesOutput {
765
+ indexes: IndexInfo[];
766
+ }
767
+
768
+ interface IndexInfo {
769
+ name: string;
770
+ key: IndexSpec;
771
+ unique?: boolean;
772
+ sparse?: boolean;
773
+ }
774
+
775
+ type IndexSpec = Record<string, 1 | -1 | "text" | "2dsphere">;
776
+ ```
777
+
778
+ ##### `mongo.indexes.create`
779
+
780
+ Create an index on a collection.
781
+
782
+ ```typescript
783
+ // Simple index
784
+ const result = await client.exec(
785
+ ["mongo", "indexes", "create"],
786
+ {
787
+ keys: { email: 1 },
788
+ options: { unique: true }
789
+ },
790
+ { metadata: { collection: "users" } }
791
+ );
792
+
793
+ // Compound index
794
+ const result = await client.exec(
795
+ ["mongo", "indexes", "create"],
796
+ {
797
+ keys: { status: 1, createdAt: -1 },
798
+ options: { name: "status_created_idx" }
799
+ },
800
+ { metadata: { collection: "users" } }
801
+ );
802
+
803
+ // Text index
804
+ const result = await client.exec(
805
+ ["mongo", "indexes", "create"],
806
+ {
807
+ keys: { title: "text", description: "text" }
808
+ },
809
+ { metadata: { collection: "articles" } }
810
+ );
811
+ ```
812
+
813
+ **Input:**
814
+ ```typescript
815
+ interface CreateIndexInput {
816
+ keys: IndexSpec;
817
+ options?: IndexOptions;
818
+ }
819
+
820
+ interface IndexOptions {
821
+ name?: string;
822
+ unique?: boolean;
823
+ sparse?: boolean;
824
+ background?: boolean;
825
+ expireAfterSeconds?: number; // TTL index
826
+ }
827
+ ```
828
+
829
+ **Output:**
830
+ ```typescript
831
+ interface CreateIndexOutput {
832
+ name: string; // Index name
833
+ }
834
+ ```
835
+
836
+ ##### `mongo.indexes.drop`
837
+
838
+ Drop an index from a collection.
839
+
840
+ ```typescript
841
+ const result = await client.exec(
842
+ ["mongo", "indexes", "drop"],
843
+ { name: "email_1" },
844
+ { metadata: { collection: "users" } }
845
+ );
846
+ ```
847
+
848
+ **Input:**
849
+ ```typescript
850
+ interface DropIndexInput {
851
+ name: string; // Index name to drop
852
+ }
853
+ ```
854
+
855
+ **Output:**
856
+ ```typescript
857
+ interface DropIndexOutput {
858
+ dropped: boolean;
859
+ }
860
+ ```
861
+
862
+ ### Type Definitions
863
+
864
+ #### Core Types
865
+
866
+ ```typescript
867
+ // Documents
868
+ type DocumentId = string | ObjectId;
869
+ type BaseDocument = Document;
870
+ type MongoDocument = BaseDocument;
871
+ type DocumentQuery = Filter<Document>;
872
+ type DocumentUpdate = UpdateFilter<Document>;
873
+
874
+ // Metadata
875
+ interface BaseMeta extends Record<string, unknown> {
876
+ database?: string; // Override database name
877
+ }
878
+
879
+ interface CollectionMeta extends BaseMeta {
880
+ collection: string; // Collection name (required)
881
+ }
882
+
883
+ // Pagination
884
+ interface PaginationInput {
885
+ page?: number; // Page number (1-indexed)
886
+ limit?: number; // Items per page
887
+ }
888
+
889
+ interface PaginationOutput {
890
+ page: number;
891
+ limit: number;
892
+ total: number;
893
+ totalPages: number;
894
+ hasNext: boolean;
895
+ hasPrev: boolean;
896
+ }
897
+
898
+ // Sorting
899
+ type SortOrder = 1 | -1;
900
+ type SortSpec = Record<string, SortOrder>;
901
+
902
+ // Indexes
903
+ type IndexSpec = Record<string, 1 | -1 | "text" | "2dsphere">;
904
+
905
+ interface IndexOptions {
906
+ name?: string;
907
+ unique?: boolean;
908
+ sparse?: boolean;
909
+ background?: boolean;
910
+ expireAfterSeconds?: number;
911
+ }
912
+
913
+ interface IndexInfo {
914
+ name: string;
915
+ key: IndexSpec;
916
+ unique?: boolean;
917
+ sparse?: boolean;
918
+ }
919
+
920
+ // Collections
921
+ interface CollectionOptions {
922
+ capped?: boolean;
923
+ size?: number;
924
+ max?: number;
925
+ validator?: Document;
926
+ }
927
+
928
+ interface CollectionStats {
929
+ count: number;
930
+ size: number;
931
+ avgObjSize: number;
932
+ storageSize: number;
933
+ nindexes: number;
934
+ totalIndexSize: number;
935
+ }
936
+
937
+ // Database
938
+ interface DatabaseInfo {
939
+ name: string;
940
+ collections: number;
941
+ views: number;
942
+ sizeOnDisk: number;
943
+ empty: boolean;
944
+ }
945
+
946
+ // Aggregation
947
+ type AggregationStage = Document;
948
+
949
+ interface AggregationOptions {
950
+ allowDiskUse?: boolean;
951
+ maxTimeMS?: number;
952
+ }
953
+
954
+ // Bulk Operations
955
+ interface BulkInsertOne {
956
+ insertOne: { document: Document };
957
+ }
958
+
959
+ interface BulkUpdateOne {
960
+ updateOne: {
961
+ filter: DocumentQuery;
962
+ update: DocumentUpdate;
963
+ upsert?: boolean;
964
+ };
965
+ }
966
+
967
+ interface BulkDeleteOne {
968
+ deleteOne: { filter: DocumentQuery };
969
+ }
970
+
971
+ type BulkOperation = BulkInsertOne | BulkUpdateOne | BulkDeleteOne;
972
+
973
+ interface BulkWriteResult {
974
+ acknowledged: boolean;
975
+ insertedCount: number;
976
+ matchedCount: number;
977
+ modifiedCount: number;
978
+ deletedCount: number;
979
+ upsertedCount: number;
980
+ }
981
+ ```
982
+
983
+ #### Typed Procedures Interface
984
+
985
+ For compile-time autocomplete, use the `MongoProcedures` type:
986
+
987
+ ```typescript
988
+ import type { MongoProcedures } from "@procedures/mongo";
989
+ import { createTypedCaller } from "@procedures/client";
990
+
991
+ const call = createTypedCaller<MongoProcedures>(client.call);
992
+
993
+ // Full autocomplete on path and input!
994
+ const result = await call(
995
+ ["mongo", "documents", "find"],
996
+ { query: { status: "active" } },
997
+ { metadata: { collection: "users" } }
998
+ );
999
+ // result is typed as FindOutput
1000
+ ```
1001
+
1002
+ ### Type Guards and Utilities
1003
+
1004
+ ```typescript
1005
+ // Check if metadata has collection
1006
+ function hasCollection(metadata: Record<string, unknown>): metadata is CollectionMeta;
1007
+
1008
+ // Get collection metadata, throws if missing
1009
+ function requireCollection(metadata: Record<string, unknown>): CollectionMeta;
1010
+
1011
+ // Get base metadata (database override)
1012
+ function getBaseMeta(metadata: Record<string, unknown>): BaseMeta;
1013
+ ```
1014
+
1015
+ ## Usage Examples
1016
+
1017
+ ### Basic CRUD Operations
1018
+
1019
+ ```typescript
1020
+ import { Client, LocalTransport, PROCEDURE_REGISTRY } from "@procedures/client";
1021
+ import { connect } from "@procedures/mongo";
1022
+
1023
+ // Connect to MongoDB
1024
+ await connect({
1025
+ uri: "mongodb://localhost:27017",
1026
+ database: "myapp"
1027
+ });
1028
+
1029
+ const client = new Client(new LocalTransport({ registry: PROCEDURE_REGISTRY }));
1030
+
1031
+ // Create a user
1032
+ const insertResult = await client.exec(
1033
+ ["mongo", "documents", "insert"],
1034
+ {
1035
+ documents: {
1036
+ name: "Alice Johnson",
1037
+ email: "alice@example.com",
1038
+ age: 28,
1039
+ status: "active",
1040
+ createdAt: new Date()
1041
+ }
1042
+ },
1043
+ { metadata: { collection: "users" } }
1044
+ );
1045
+
1046
+ // Find active users
1047
+ const findResult = await client.exec(
1048
+ ["mongo", "documents", "find"],
1049
+ {
1050
+ query: { status: "active" },
1051
+ sort: { createdAt: -1 },
1052
+ limit: 10
1053
+ },
1054
+ { metadata: { collection: "users" } }
1055
+ );
1056
+
1057
+ console.log(findResult.documents);
1058
+ console.log(findResult.pagination);
1059
+
1060
+ // Update a user
1061
+ await client.exec(
1062
+ ["mongo", "documents", "update"],
1063
+ {
1064
+ filter: { email: "alice@example.com" },
1065
+ update: { $set: { status: "inactive" } }
1066
+ },
1067
+ { metadata: { collection: "users" } }
1068
+ );
1069
+
1070
+ // Delete inactive users
1071
+ await client.exec(
1072
+ ["mongo", "documents", "delete"],
1073
+ {
1074
+ filter: { status: "inactive" },
1075
+ multi: true
1076
+ },
1077
+ { metadata: { collection: "users" } }
1078
+ );
1079
+ ```
1080
+
1081
+ ### Aggregation Pipeline
1082
+
1083
+ ```typescript
1084
+ // Group users by country and count
1085
+ const result = await client.exec(
1086
+ ["mongo", "documents", "aggregate"],
1087
+ {
1088
+ pipeline: [
1089
+ { $match: { status: "active" } },
1090
+ { $group: {
1091
+ _id: "$country",
1092
+ count: { $sum: 1 },
1093
+ avgAge: { $avg: "$age" }
1094
+ }
1095
+ },
1096
+ { $sort: { count: -1 } },
1097
+ { $limit: 10 }
1098
+ ]
1099
+ },
1100
+ { metadata: { collection: "users" } }
1101
+ );
1102
+
1103
+ console.log(result.results);
1104
+ ```
1105
+
1106
+ ### Index Management
1107
+
1108
+ ```typescript
1109
+ // Create unique email index
1110
+ await client.exec(
1111
+ ["mongo", "indexes", "create"],
1112
+ {
1113
+ keys: { email: 1 },
1114
+ options: { unique: true, name: "email_unique_idx" }
1115
+ },
1116
+ { metadata: { collection: "users" } }
1117
+ );
1118
+
1119
+ // Create compound index
1120
+ await client.exec(
1121
+ ["mongo", "indexes", "create"],
1122
+ {
1123
+ keys: { status: 1, createdAt: -1 },
1124
+ options: { name: "status_created_idx" }
1125
+ },
1126
+ { metadata: { collection: "users" } }
1127
+ );
1128
+
1129
+ // List all indexes
1130
+ const indexes = await client.exec(
1131
+ ["mongo", "indexes", "list"],
1132
+ {},
1133
+ { metadata: { collection: "users" } }
1134
+ );
1135
+
1136
+ console.log(indexes.indexes);
1137
+
1138
+ // Drop an index
1139
+ await client.exec(
1140
+ ["mongo", "indexes", "drop"],
1141
+ { name: "status_created_idx" },
1142
+ { metadata: { collection: "users" } }
1143
+ );
1144
+ ```
1145
+
1146
+ ### Collection Management
1147
+
1148
+ ```typescript
1149
+ // Create capped collection for logs
1150
+ await client.exec(
1151
+ ["mongo", "collections", "create"],
1152
+ {
1153
+ name: "logs",
1154
+ options: {
1155
+ capped: true,
1156
+ size: 10485760, // 10MB
1157
+ max: 1000
1158
+ }
1159
+ }
1160
+ );
1161
+
1162
+ // List all collections
1163
+ const collections = await client.exec(
1164
+ ["mongo", "collections", "list"],
1165
+ { filter: "user" } // Filter collections containing "user"
1166
+ );
1167
+
1168
+ console.log(collections.collections);
1169
+
1170
+ // Get collection stats
1171
+ const stats = await client.exec(
1172
+ ["mongo", "collections", "stats"],
1173
+ {},
1174
+ { metadata: { collection: "users" } }
1175
+ );
1176
+
1177
+ console.log(stats);
1178
+ ```
1179
+
1180
+ ### Direct Usage (Without RPC)
1181
+
1182
+ ```typescript
1183
+ import {
1184
+ connect,
1185
+ findProcedure,
1186
+ insertProcedure,
1187
+ requireCollection
1188
+ } from "@procedures/mongo";
1189
+
1190
+ await connect({ database: "myapp" });
1191
+
1192
+ // Use procedures directly
1193
+ const findResult = await findProcedure.handler(
1194
+ { query: { status: "active" }, limit: 10 },
1195
+ { metadata: { collection: "users" } }
1196
+ );
1197
+
1198
+ const insertResult = await insertProcedure.handler(
1199
+ { documents: { name: "Bob", email: "bob@example.com" } },
1200
+ { metadata: { collection: "users" } }
1201
+ );
1202
+ ```
1203
+
1204
+ ### Multi-Database Support
1205
+
1206
+ ```typescript
1207
+ // Default database
1208
+ await client.exec(
1209
+ ["mongo", "documents", "find"],
1210
+ { query: {} },
1211
+ { metadata: { collection: "users" } }
1212
+ );
1213
+
1214
+ // Override database in metadata
1215
+ await client.exec(
1216
+ ["mongo", "documents", "find"],
1217
+ { query: {} },
1218
+ { metadata: { database: "analytics", collection: "events" } }
1219
+ );
1220
+ ```
1221
+
1222
+ ## Integration with Ecosystem
1223
+
1224
+ ### Bundle Packages
1225
+
1226
+ `client-mongo` can be included in bundle packages:
1227
+
1228
+ ```typescript
1229
+ // In bundle-data/src/register.ts
1230
+ import "@procedures/mongo"; // registers its procedures
1231
+ import "@procedures/sqlite"; // registers its procedures
1232
+ import "@procedures/s3/register.js";
1233
+ ```
1234
+
1235
+ ### Type-Safe Calling
1236
+
1237
+ ```typescript
1238
+ import type { MongoProcedures } from "@procedures/mongo";
1239
+
1240
+ type AllProcedures = MongoProcedures & OtherProcedures;
1241
+
1242
+ const call = createTypedCaller<AllProcedures>(client.call);
1243
+ ```
1244
+
1245
+ ## Dependencies
1246
+
1247
+ ### Runtime Dependencies
1248
+
1249
+ - **@procedures/client** - Core client procedure system
1250
+ - **mongodb** (^7.0.0) - Official MongoDB Node.js driver
1251
+
1252
+ ### Development Dependencies
1253
+
1254
+ - **@mark1russell7/cue** - Configuration and type utilities
1255
+ - **typescript** (^5.9.3) - TypeScript compiler
1256
+ - **@types/node** (^22.0.0) - Node.js type definitions
1257
+
1258
+ ## Package Structure
1259
+
1260
+ ```
1261
+ client-mongo/
1262
+ ├── src/
1263
+ │ ├── connection.ts # Connection management
1264
+ │ ├── types.ts # Core type definitions
1265
+ │ ├── procedures.types.ts # Typed procedures interface
1266
+ │ ├── register.ts # Auto-registration
1267
+ │ ├── index.ts # Main entry point
1268
+ │ ├── procedures/
1269
+ │ │ ├── schema.ts # Minimal schema helpers
1270
+ │ │ ├── index.ts # Procedure barrel export
1271
+ │ │ ├── database.ping.ts
1272
+ │ │ ├── database.info.ts
1273
+ │ │ ├── collections.list.ts
1274
+ │ │ ├── collections.create.ts
1275
+ │ │ ├── collections.drop.ts
1276
+ │ │ ├── collections.stats.ts
1277
+ │ │ ├── documents.find.ts
1278
+ │ │ ├── documents.get.ts
1279
+ │ │ ├── documents.insert.ts
1280
+ │ │ ├── documents.update.ts
1281
+ │ │ ├── documents.delete.ts
1282
+ │ │ ├── documents.count.ts
1283
+ │ │ ├── documents.aggregate.ts
1284
+ │ │ ├── indexes.list.ts
1285
+ │ │ ├── indexes.create.ts
1286
+ │ │ └── indexes.drop.ts
1287
+ ├── dist/ # Compiled output
1288
+ ├── package.json
1289
+ ├── tsconfig.json
1290
+ └── README.md
1291
+ ```
1292
+
1293
+ ## Development
1294
+
1295
+ ### Build
1296
+
1297
+ ```bash
1298
+ npm run build
1299
+ ```
1300
+
1301
+ ### Type Check
1302
+
1303
+ ```bash
1304
+ npm run typecheck
1305
+ ```
1306
+
1307
+ ### Clean
1308
+
1309
+ ```bash
1310
+ npm run clean
1311
+ ```
1312
+
1313
+ ## License
1314
+
1315
+ MIT
1316
+
1317
+ ## Author
1318
+
1319
+ Mark Russell <marktheprogrammer17@gmail.com>
1320
+
1321
+ ## Repository
1322
+
1323
+ https://github.com/mark1russell7/client-mongo