flongo 1.0.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/README.md ADDED
@@ -0,0 +1,261 @@
1
+ # Flongo ๐Ÿ”ฅ๐Ÿƒ
2
+
3
+ **Firestore-like fluent query API for MongoDB**
4
+
5
+ Flongo brings the intuitive, chainable query syntax of Firestore to MongoDB, making database operations more readable and developer-friendly.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install flongo
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { initializeFlongo, FlongoCollection, FlongoQuery } from 'flongo';
17
+
18
+ // Initialize MongoDB connection
19
+ initializeFlongo({
20
+ connectionString: 'mongodb://localhost:27017',
21
+ dbName: 'myapp'
22
+ });
23
+
24
+ // Define your data interface
25
+ interface User {
26
+ name: string;
27
+ email: string;
28
+ age: number;
29
+ tags: string[];
30
+ }
31
+
32
+ // Create a collection
33
+ const users = new FlongoCollection<User>('users');
34
+
35
+ // Query with fluent API
36
+ const adults = await users.getAll(
37
+ new FlongoQuery()
38
+ .where('age').gtEq(18)
39
+ .and('tags').arrContainsAny(['developer', 'designer'])
40
+ .orderBy('name')
41
+ );
42
+ ```
43
+
44
+ ## Features
45
+
46
+ - ๐Ÿ”— **Chainable queries** - Firestore-like fluent API
47
+ - ๐Ÿš€ **TypeScript support** - Full type safety and IntelliSense
48
+ - ๐Ÿ“Š **Rich query operations** - Comparisons, arrays, geospatial, text search
49
+ - ๐ŸŽ›๏ธ **Configurable** - Optional event logging, custom error handling
50
+ - ๐Ÿงช **Battle-tested** - Extracted from production codebase
51
+ - ๐Ÿ“ฆ **Zero config** - Works with existing MongoDB setup
52
+
53
+ ## API Reference
54
+
55
+ ### Basic Queries
56
+
57
+ ```typescript
58
+ const query = new FlongoQuery();
59
+
60
+ // Equality
61
+ query.where('status').eq('active')
62
+
63
+ // Comparisons
64
+ query.where('age').gt(21).and('age').lt(65)
65
+ query.where('score').gtEq(80).and('score').ltEq(100)
66
+ query.where('status').neq('deleted')
67
+
68
+ // Arrays
69
+ query.where('tags').arrContains('featured')
70
+ query.where('categories').arrContainsAny(['tech', 'design'])
71
+ query.where('skills').arrContainsAll(['js', 'react'])
72
+ query.where('id').in(['user1', 'user2', 'user3'])
73
+ query.where('role').notIn(['admin', 'banned'])
74
+
75
+ // Text search
76
+ query.where('name').startsWith('John')
77
+ query.where('email').endsWith('@gmail.com')
78
+ query.where('bio').strContains('developer')
79
+
80
+ // Ranges
81
+ query.inRange('price', 100, 500)
82
+ ```
83
+
84
+ ### Advanced Queries
85
+
86
+ ```typescript
87
+ // OR queries
88
+ const activeOrFeatured = new FlongoQuery()
89
+ .where('status').eq('active')
90
+ .or(
91
+ new FlongoQuery().where('featured').eq(true)
92
+ );
93
+
94
+ // Geospatial queries
95
+ query.where('location').geoWithin({
96
+ ne: { latitude: 40.7829, longitude: -73.9441 },
97
+ sw: { latitude: 40.7489, longitude: -73.9441 }
98
+ });
99
+
100
+ // Radius search
101
+ query.inRadius('location', { latitude: 40.7589, longitude: -73.9851 }, 1000);
102
+
103
+ // Sorting and pagination
104
+ const results = await collection.getAll(
105
+ query.orderBy('createdAt', SortDirection.Descending),
106
+ { offset: 0, count: 20 }
107
+ );
108
+ ```
109
+
110
+ ### Collection Operations
111
+
112
+ ```typescript
113
+ const collection = new FlongoCollection<User>('users');
114
+
115
+ // Create
116
+ const user = await collection.create({
117
+ name: 'John Doe',
118
+ email: 'john@example.com',
119
+ age: 30,
120
+ tags: ['developer']
121
+ });
122
+
123
+ // Read
124
+ const user = await collection.get('user123');
125
+ const users = await collection.getAll(query);
126
+ const first = await collection.getFirst(query);
127
+ const count = await collection.count(query);
128
+ const exists = await collection.exists(query);
129
+
130
+ // Update
131
+ await collection.update('user123', { age: 31 });
132
+ await collection.updateAll({ status: 'verified' }, query);
133
+ await collection.increment('user123', 'loginCount', 1);
134
+ await collection.append('user123', 'tags', ['admin']);
135
+
136
+ // Delete
137
+ await collection.delete('user123');
138
+ await collection.batchDelete(['user1', 'user2']);
139
+
140
+ // Batch operations
141
+ await collection.batchCreate([user1, user2, user3]);
142
+ ```
143
+
144
+ ### Configuration
145
+
146
+ ```typescript
147
+ // Disable event logging
148
+ const collection = new FlongoCollection<User>('users', {
149
+ enableEventLogging: false
150
+ });
151
+
152
+ // Custom events collection
153
+ const collection = new FlongoCollection<User>('users', {
154
+ enableEventLogging: true,
155
+ eventsCollectionName: 'audit_logs'
156
+ });
157
+ ```
158
+
159
+ ## Examples
160
+
161
+ ### E-commerce Product Search
162
+
163
+ ```typescript
164
+ interface Product {
165
+ name: string;
166
+ price: number;
167
+ category: string;
168
+ tags: string[];
169
+ inStock: boolean;
170
+ rating: number;
171
+ }
172
+
173
+ const products = new FlongoCollection<Product>('products');
174
+
175
+ // Find affordable, well-rated tech products in stock
176
+ const results = await products.getAll(
177
+ new FlongoQuery()
178
+ .where('category').eq('electronics')
179
+ .and('price').ltEq(500)
180
+ .and('rating').gtEq(4.0)
181
+ .and('inStock').eq(true)
182
+ .and('tags').arrContainsAny(['laptop', 'phone', 'tablet'])
183
+ .orderBy('rating', SortDirection.Descending)
184
+ );
185
+ ```
186
+
187
+ ### User Management
188
+
189
+ ```typescript
190
+ interface User {
191
+ email: string;
192
+ role: string;
193
+ lastLogin: number;
194
+ preferences: string[];
195
+ location?: Coordinates;
196
+ }
197
+
198
+ const users = new FlongoCollection<User>('users');
199
+
200
+ // Find active users who haven't logged in recently
201
+ const staleUsers = await users.getAll(
202
+ new FlongoQuery()
203
+ .where('role').neq('admin')
204
+ .and('lastLogin').lt(Date.now() - 30 * 24 * 60 * 60 * 1000) // 30 days ago
205
+ .orderBy('lastLogin', SortDirection.Ascending)
206
+ );
207
+
208
+ // Find users interested in specific topics near a location
209
+ const nearbyDevs = await users.getAll(
210
+ new FlongoQuery()
211
+ .where('preferences').arrContainsAny(['javascript', 'react', 'node'])
212
+ .and('location').inRadius(
213
+ 'location',
214
+ { latitude: 37.7749, longitude: -122.4194 }, // San Francisco
215
+ 10000 // 10km radius
216
+ )
217
+ );
218
+ ```
219
+
220
+ ## Error Handling
221
+
222
+ ```typescript
223
+ import { Error404, Error400 } from 'flongo';
224
+
225
+ try {
226
+ const user = await users.get('nonexistent-id');
227
+ } catch (error) {
228
+ if (error instanceof Error404) {
229
+ console.log('User not found');
230
+ }
231
+ }
232
+ ```
233
+
234
+ ## Migration from Firestore
235
+
236
+ Flongo's API closely mirrors Firestore, making migration straightforward:
237
+
238
+ ```typescript
239
+ // Firestore
240
+ const snapshot = await db.collection('users')
241
+ .where('age', '>=', 18)
242
+ .where('tags', 'array-contains-any', ['developer', 'designer'])
243
+ .orderBy('name')
244
+ .get();
245
+
246
+ // Flongo
247
+ const users = await collection.getAll(
248
+ new FlongoQuery()
249
+ .where('age').gtEq(18)
250
+ .and('tags').arrContainsAny(['developer', 'designer'])
251
+ .orderBy('name')
252
+ );
253
+ ```
254
+
255
+ ## License
256
+
257
+ MIT
258
+
259
+ ## Contributing
260
+
261
+ Contributions are welcome! Please feel free to submit a Pull Request.
@@ -0,0 +1,7 @@
1
+ export declare class Error404 extends Error {
2
+ constructor(message?: string);
3
+ }
4
+ export declare class Error400 extends Error {
5
+ constructor(message?: string);
6
+ }
7
+ //# sourceMappingURL=errors.d.ts.map
package/dist/errors.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ // Simple error classes for flongo
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.Error400 = exports.Error404 = void 0;
5
+ class Error404 extends Error {
6
+ constructor(message = "Not found") {
7
+ super(message);
8
+ this.name = "Error404";
9
+ }
10
+ }
11
+ exports.Error404 = Error404;
12
+ class Error400 extends Error {
13
+ constructor(message = "Bad request") {
14
+ super(message);
15
+ this.name = "Error400";
16
+ }
17
+ }
18
+ exports.Error400 = Error400;
19
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1,9 @@
1
+ import { Db, MongoClient } from "mongodb";
2
+ export declare let flongoClient: MongoClient;
3
+ export declare let flongoDb: Db;
4
+ export interface FlongoConfig {
5
+ connectionString: string;
6
+ dbName: string;
7
+ }
8
+ export declare function initializeFlongo(config: FlongoConfig): void;
9
+ //# sourceMappingURL=flongo.d.ts.map
package/dist/flongo.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.flongoDb = exports.flongoClient = void 0;
4
+ exports.initializeFlongo = initializeFlongo;
5
+ const mongodb_1 = require("mongodb");
6
+ function initializeFlongo(config) {
7
+ exports.flongoClient = new mongodb_1.MongoClient(config.connectionString);
8
+ exports.flongoDb = exports.flongoClient.db(config.dbName);
9
+ }
10
+ //# sourceMappingURL=flongo.js.map
@@ -0,0 +1,192 @@
1
+ import { FlongoQuery } from "./flongoQuery";
2
+ import { Entity, Event, EventName, Pagination, Repository } from "./types";
3
+ /**
4
+ * Configuration options for FlongoCollection instances
5
+ */
6
+ export interface FlongoCollectionOptions {
7
+ /** Whether to enable automatic event logging for CRUD operations */
8
+ enableEventLogging?: boolean;
9
+ /** Name of the collection to store events in (defaults to "events") */
10
+ eventsCollectionName?: string;
11
+ }
12
+ /**
13
+ * FlongoCollection provides a high-level interface for MongoDB collection operations
14
+ * with automatic entity management, event logging, and fluent query support.
15
+ *
16
+ * This class abstracts MongoDB collection operations and provides:
17
+ * - Automatic _id conversion between ObjectId and string
18
+ * - Timestamps management (createdAt, updatedAt)
19
+ * - Optional event logging for audit trails
20
+ * - Integration with FlongoQuery for fluent querying
21
+ * - Type-safe operations with TypeScript generics
22
+ *
23
+ * Example usage:
24
+ * ```typescript
25
+ * interface User {
26
+ * name: string;
27
+ * email: string;
28
+ * age: number;
29
+ * }
30
+ *
31
+ * const users = new FlongoCollection<User>('users');
32
+ *
33
+ * // Create a user
34
+ * const user = await users.create({ name: 'John', email: 'john@example.com', age: 30 });
35
+ *
36
+ * // Query users
37
+ * const adults = await users.getAll(
38
+ * new FlongoQuery().where('age').gtEq(18)
39
+ * );
40
+ * ```
41
+ */
42
+ export declare class FlongoCollection<T> {
43
+ /** The underlying MongoDB collection */
44
+ private collection;
45
+ /** Events collection for audit logging (null if logging disabled) */
46
+ private events;
47
+ /** Collection name for event logging */
48
+ private name;
49
+ /** Configuration options */
50
+ private options;
51
+ /**
52
+ * Creates a new FlongoCollection instance
53
+ * @param collectionName - Name of the MongoDB collection
54
+ * @param options - Configuration options for the collection
55
+ */
56
+ constructor(collectionName: Repository, options?: FlongoCollectionOptions);
57
+ /**
58
+ * Retrieves a single document by its ID
59
+ * @param id - Document ID (string that will be converted to ObjectId)
60
+ * @returns Promise resolving to the document with Entity metadata
61
+ * @throws Error404 if document is not found
62
+ */
63
+ get(id: string): Promise<Entity & T>;
64
+ /**
65
+ * Converts MongoDB document to Entity format
66
+ * Converts ObjectId _id to string and ensures proper typing
67
+ * @private
68
+ * @param obj - Raw MongoDB document
69
+ * @returns Document with string _id and Entity typing
70
+ */
71
+ private toEntity;
72
+ /**
73
+ * Retrieves multiple documents based on query and pagination
74
+ * @param query - Optional FlongoQuery for filtering
75
+ * @param pagination - Optional pagination settings
76
+ * @returns Promise resolving to array of documents
77
+ */
78
+ getAll(query?: FlongoQuery, pagination?: Pagination): Promise<(Entity & T)[]>;
79
+ /**
80
+ * Retrieves a subset of documents with required query and pagination
81
+ * Similar to getAll but requires both query and pagination parameters
82
+ * @param query - FlongoQuery for filtering (required)
83
+ * @param pagination - Pagination settings (required)
84
+ * @returns Promise resolving to array of documents
85
+ */
86
+ getSome(query: FlongoQuery, pagination: Pagination): Promise<(Entity & T)[]>;
87
+ /**
88
+ * Retrieves the first document matching the query
89
+ * @param query - FlongoQuery for filtering
90
+ * @returns Promise resolving to the first matching document
91
+ */
92
+ getFirst(query: FlongoQuery): Promise<Entity & T>;
93
+ /**
94
+ * Counts documents matching the query
95
+ * @param query - Optional FlongoQuery for filtering
96
+ * @returns Promise resolving to the count of matching documents
97
+ */
98
+ count(query?: FlongoQuery): Promise<number>;
99
+ /**
100
+ * Checks if any documents match the query
101
+ * @param query - FlongoQuery for filtering
102
+ * @returns Promise resolving to true if any documents match, false otherwise
103
+ */
104
+ exists(query: FlongoQuery): Promise<boolean>;
105
+ /**
106
+ * Deletes a single document by ID
107
+ * Logs the deletion event with backup data before deleting
108
+ * @param id - Document ID to delete
109
+ * @param clientId - ID of the client performing the deletion (for audit trail)
110
+ */
111
+ delete(id: string, clientId: string): Promise<void>;
112
+ /**
113
+ * Deletes multiple documents by their IDs
114
+ * Logs batch deletion event with backup data before deleting
115
+ * @param ids - Array of document IDs to delete
116
+ * @param clientId - ID of the client performing the deletion
117
+ */
118
+ batchDelete(ids: string[], clientId: string): Promise<void>;
119
+ /**
120
+ * Creates a new document in the collection
121
+ * Automatically adds timestamps and logs the creation event
122
+ * @param attributes - Document data (without Entity metadata)
123
+ * @param clientId - Optional client ID for audit trail
124
+ * @returns Promise resolving to the created document with Entity metadata
125
+ */
126
+ create(attributes: T, clientId?: string): Promise<Entity & T>;
127
+ /**
128
+ * Creates multiple documents in a single batch operation
129
+ * More efficient than individual creates for large datasets
130
+ * @param attributes - Array of document data
131
+ * @param clientId - Optional client ID for audit trail
132
+ */
133
+ batchCreate(attributes: T[], clientId?: string): Promise<void>;
134
+ /**
135
+ * Updates multiple documents matching the query
136
+ * @param attributes - Fields to update
137
+ * @param query - Optional FlongoQuery to filter documents (updates all if not provided)
138
+ * @param clientId - Optional client ID for audit trail
139
+ */
140
+ updateAll(attributes: any, query?: FlongoQuery, clientId?: string): Promise<void>;
141
+ /**
142
+ * Updates a single document by ID
143
+ * @param id - Document ID to update
144
+ * @param attributes - Fields to update
145
+ * @param clientId - Optional client ID for audit trail
146
+ */
147
+ update(id: string, attributes: any, clientId?: string): Promise<void>;
148
+ /**
149
+ * Updates the first document matching the query
150
+ * @param attributes - Fields to update
151
+ * @param query - Optional FlongoQuery to filter documents
152
+ * @param clientId - Optional client ID for audit trail
153
+ * @returns Promise resolving to the updated document
154
+ */
155
+ updateFirst(attributes: any, query?: FlongoQuery, clientId?: string): Promise<Entity & T>;
156
+ /**
157
+ * Atomically increments a numeric field
158
+ * @param id - Document ID
159
+ * @param key - Field name to increment
160
+ * @param amt - Amount to increment by (defaults to 1)
161
+ */
162
+ increment(id: string, key: string, amt?: number): Promise<void>;
163
+ /**
164
+ * Atomically decrements a numeric field
165
+ * @param id - Document ID
166
+ * @param key - Field name to decrement
167
+ * @param amt - Amount to decrement by (defaults to 1)
168
+ */
169
+ decrement(id: string, key: string, amt?: number): Promise<void>;
170
+ /**
171
+ * Atomically appends items to an array field
172
+ * @param id - Document ID
173
+ * @param key - Array field name
174
+ * @param items - Items to append to the array
175
+ */
176
+ append(id: string, key: string, items: any[]): Promise<void>;
177
+ /**
178
+ * Atomically removes items from an array field
179
+ * @param id - Document ID
180
+ * @param key - Array field name
181
+ * @param items - Items to remove from the array
182
+ */
183
+ arrRemove(id: string, key: string, items: any[]): Promise<void>;
184
+ /**
185
+ * Logs an event to the events collection for audit trails
186
+ * Events are only logged if event logging is enabled in options
187
+ * @param event - Event object with name and metadata
188
+ * @returns Promise resolving to the event ID (or null if logging disabled)
189
+ */
190
+ logEvent<T extends EventName>(event: Event<T>): Promise<string | null>;
191
+ }
192
+ //# sourceMappingURL=flongoCollection.d.ts.map