@semics-tech/mongolite 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/LICENSE +21 -0
- package/README.md +179 -0
- package/dist/adapters/browser.d.ts +97 -0
- package/dist/adapters/browser.js +103 -0
- package/dist/adapters/browser.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts +106 -0
- package/dist/adapters/cloudflare.js +94 -0
- package/dist/adapters/cloudflare.js.map +1 -0
- package/dist/bin/mongolite-debug.d.ts +2 -0
- package/dist/bin/mongolite-debug.js +115 -0
- package/dist/bin/mongolite-debug.js.map +1 -0
- package/dist/changeStream.d.ts +114 -0
- package/dist/changeStream.js +338 -0
- package/dist/changeStream.js.map +1 -0
- package/dist/cloudflare.d.ts +39 -0
- package/dist/cloudflare.js +39 -0
- package/dist/cloudflare.js.map +1 -0
- package/dist/collection.d.ts +274 -0
- package/dist/collection.js +1706 -0
- package/dist/collection.js.map +1 -0
- package/dist/cursors/findCursor.d.ts +101 -0
- package/dist/cursors/findCursor.js +757 -0
- package/dist/cursors/findCursor.js.map +1 -0
- package/dist/db.d.ts +116 -0
- package/dist/db.js +254 -0
- package/dist/db.js.map +1 -0
- package/dist/debugger/query-debugger.d.ts +22 -0
- package/dist/debugger/query-debugger.js +258 -0
- package/dist/debugger/query-debugger.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/mongo-client.d.ts +56 -0
- package/dist/mongo-client.js +55 -0
- package/dist/mongo-client.js.map +1 -0
- package/dist/types.d.ts +318 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/indexing.d.ts +4 -0
- package/dist/utils/indexing.js +43 -0
- package/dist/utils/indexing.js.map +1 -0
- package/dist/utils/projection.d.ts +16 -0
- package/dist/utils/projection.js +98 -0
- package/dist/utils/projection.js.map +1 -0
- package/dist/utils/queryOperators.d.ts +16 -0
- package/dist/utils/queryOperators.js +138 -0
- package/dist/utils/queryOperators.js.map +1 -0
- package/package.json +92 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { QueryDebugger } from '../debugger/query-debugger.js';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
// CLI argument parsing
|
|
5
|
+
function parseArgs() {
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const options = {};
|
|
8
|
+
for (let i = 0; i < args.length; i++) {
|
|
9
|
+
const arg = args[i];
|
|
10
|
+
if (arg === '--verbose' || arg === '-v') {
|
|
11
|
+
options.verbose = true;
|
|
12
|
+
}
|
|
13
|
+
else if (arg === '--collection' || arg === '-c') {
|
|
14
|
+
options.collection = args[++i];
|
|
15
|
+
}
|
|
16
|
+
else if (arg === '--database' || arg === '-d') {
|
|
17
|
+
options.database = args[++i];
|
|
18
|
+
}
|
|
19
|
+
else if (arg === '--help' || arg === '-h') {
|
|
20
|
+
options.help = true;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return options;
|
|
24
|
+
}
|
|
25
|
+
function showHelp() {
|
|
26
|
+
console.log('MongoLite Query Debugger');
|
|
27
|
+
console.log('');
|
|
28
|
+
console.log('Interactive debugging tool for MongoLite queries.');
|
|
29
|
+
console.log('');
|
|
30
|
+
console.log('Usage: npx mongolite-debug [options]');
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log('Options:');
|
|
33
|
+
console.log(' -d, --database <path> Database file path (default: ./mongolite.db)');
|
|
34
|
+
console.log(' -c, --collection <name> Initial collection to use');
|
|
35
|
+
console.log(' -v, --verbose Enable verbose output');
|
|
36
|
+
console.log(' -h, --help Show this help');
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log('Interactive Commands:');
|
|
39
|
+
console.log(' .help Show available commands');
|
|
40
|
+
console.log(' .collections List all collections');
|
|
41
|
+
console.log(' .use <collection> Select a collection to work with');
|
|
42
|
+
console.log(' .find <filter> Convert find filter to SQL and execute');
|
|
43
|
+
console.log(' .sql <query> Execute raw SQL query');
|
|
44
|
+
console.log(' .last Show last generated SQL');
|
|
45
|
+
console.log(' .sample [count] Show sample documents from current collection');
|
|
46
|
+
console.log(' .exit Exit the debugger');
|
|
47
|
+
console.log('');
|
|
48
|
+
console.log('Examples:');
|
|
49
|
+
console.log(' npx mongolite-debug -d ./myapp.db -c users');
|
|
50
|
+
console.log(' npx mongolite-debug --verbose');
|
|
51
|
+
console.log('');
|
|
52
|
+
console.log(' # Inside the debugger:');
|
|
53
|
+
console.log(' .use users');
|
|
54
|
+
console.log(' .find {"age": {"$gt": 25}}');
|
|
55
|
+
console.log(' .sql SELECT * FROM users WHERE json_extract(data, "$.age") > 25');
|
|
56
|
+
console.log('');
|
|
57
|
+
}
|
|
58
|
+
async function main() {
|
|
59
|
+
const options = parseArgs();
|
|
60
|
+
if (options.help) {
|
|
61
|
+
showHelp();
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
// Set default database path if not provided
|
|
65
|
+
if (!options.database) {
|
|
66
|
+
// Look for common database files in the current directory
|
|
67
|
+
const cwd = process.cwd();
|
|
68
|
+
const commonNames = [
|
|
69
|
+
'mongolite.db',
|
|
70
|
+
'mongolite.sqlite',
|
|
71
|
+
'database.db',
|
|
72
|
+
'database.sqlite',
|
|
73
|
+
'app.db',
|
|
74
|
+
'app.sqlite',
|
|
75
|
+
];
|
|
76
|
+
// Check if any common database files exist
|
|
77
|
+
const { existsSync } = await import('fs');
|
|
78
|
+
let foundDb = false;
|
|
79
|
+
for (const name of commonNames) {
|
|
80
|
+
const dbPath = path.join(cwd, name);
|
|
81
|
+
if (existsSync(dbPath)) {
|
|
82
|
+
options.database = dbPath;
|
|
83
|
+
foundDb = true;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!foundDb) {
|
|
88
|
+
options.database = path.join(cwd, 'mongolite.db');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
console.log('🔍 MongoLite Query Debugger (npx version)');
|
|
92
|
+
console.log(`📁 Database: ${options.database}`);
|
|
93
|
+
if (options.collection) {
|
|
94
|
+
console.log(`📋 Initial collection: ${options.collection}`);
|
|
95
|
+
}
|
|
96
|
+
console.log('');
|
|
97
|
+
try {
|
|
98
|
+
const queryDebugger = new QueryDebugger(options);
|
|
99
|
+
await queryDebugger.start();
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.error('Fatal error:', error instanceof Error ? error.message : error);
|
|
103
|
+
if (error instanceof Error && error.message.includes('SQLITE_CANTOPEN')) {
|
|
104
|
+
console.error('');
|
|
105
|
+
console.error('💡 Tip: Make sure the database file exists or specify a different path with -d');
|
|
106
|
+
console.error(' Example: npx mongolite-debug -d ./path/to/your/database.db');
|
|
107
|
+
}
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
main().catch((error) => {
|
|
112
|
+
console.error('Unexpected error:', error);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
});
|
|
115
|
+
//# sourceMappingURL=mongolite-debug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongolite-debug.js","sourceRoot":"","sources":["../../src/bin/mongolite-debug.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import type { IDatabaseAdapter } from './db.js';
|
|
3
|
+
import { DocumentWithId, Filter } from './types.js';
|
|
4
|
+
export type ChangeOperationType = 'insert' | 'update' | 'delete' | 'replace';
|
|
5
|
+
export interface ChangeStreamDocument<T extends DocumentWithId = DocumentWithId> {
|
|
6
|
+
_id: string;
|
|
7
|
+
operationType: ChangeOperationType;
|
|
8
|
+
clusterTime?: Date;
|
|
9
|
+
fullDocument?: T;
|
|
10
|
+
fullDocumentBeforeChange?: T;
|
|
11
|
+
documentKey: {
|
|
12
|
+
_id: string;
|
|
13
|
+
};
|
|
14
|
+
ns: {
|
|
15
|
+
db: string;
|
|
16
|
+
coll: string;
|
|
17
|
+
};
|
|
18
|
+
updateDescription?: {
|
|
19
|
+
updatedFields: Record<string, unknown>;
|
|
20
|
+
removedFields: string[];
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface ChangeStreamOptions<T extends DocumentWithId = DocumentWithId> {
|
|
24
|
+
/**
|
|
25
|
+
* Filter to apply to change events
|
|
26
|
+
*/
|
|
27
|
+
filter?: Filter<ChangeStreamDocument<T>>;
|
|
28
|
+
/**
|
|
29
|
+
* Whether to include the full document in insert and update operations
|
|
30
|
+
*/
|
|
31
|
+
fullDocument?: 'default' | 'updateLookup' | 'whenAvailable' | 'required';
|
|
32
|
+
/**
|
|
33
|
+
* Whether to include the full document before the change for update operations
|
|
34
|
+
*/
|
|
35
|
+
fullDocumentBeforeChange?: 'off' | 'whenAvailable' | 'required';
|
|
36
|
+
/**
|
|
37
|
+
* Resume token for resuming change streams (not implemented in this version)
|
|
38
|
+
*/
|
|
39
|
+
resumeAfter?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Maximum number of events to buffer
|
|
42
|
+
*/
|
|
43
|
+
maxBufferSize?: number;
|
|
44
|
+
}
|
|
45
|
+
export declare class ChangeStream<T extends DocumentWithId = DocumentWithId> extends EventEmitter {
|
|
46
|
+
private readonly db;
|
|
47
|
+
private readonly collectionName;
|
|
48
|
+
private readonly options;
|
|
49
|
+
private closed;
|
|
50
|
+
private pollInterval;
|
|
51
|
+
private lastProcessedId;
|
|
52
|
+
private buffer;
|
|
53
|
+
private readonly maxBufferSize;
|
|
54
|
+
constructor(db: IDatabaseAdapter, collectionName: string, options?: ChangeStreamOptions<T>);
|
|
55
|
+
/**
|
|
56
|
+
* Sets up the change tracking infrastructure
|
|
57
|
+
*/
|
|
58
|
+
private setupChangeTracking;
|
|
59
|
+
/**
|
|
60
|
+
* Creates the change log table if it doesn't exist
|
|
61
|
+
*/
|
|
62
|
+
private ensureChangeLogTable;
|
|
63
|
+
/**
|
|
64
|
+
* Sets up triggers for tracking changes on the collection
|
|
65
|
+
*/
|
|
66
|
+
private setupTriggers;
|
|
67
|
+
/**
|
|
68
|
+
* Starts polling for new changes
|
|
69
|
+
*/
|
|
70
|
+
private startPolling;
|
|
71
|
+
/**
|
|
72
|
+
* Polls for new changes from the change log
|
|
73
|
+
*/
|
|
74
|
+
private pollForChanges;
|
|
75
|
+
/**
|
|
76
|
+
* Transforms a raw change event from the database into a ChangeStreamDocument
|
|
77
|
+
*/
|
|
78
|
+
private transformChangeEvent;
|
|
79
|
+
/**
|
|
80
|
+
* Computes the update description by comparing before and after documents
|
|
81
|
+
*/
|
|
82
|
+
private computeUpdateDescription;
|
|
83
|
+
/**
|
|
84
|
+
* Determines if the full document should be included
|
|
85
|
+
*/
|
|
86
|
+
private shouldIncludeFullDocument;
|
|
87
|
+
/**
|
|
88
|
+
* Determines if the full document before change should be included
|
|
89
|
+
*/
|
|
90
|
+
private shouldIncludeFullDocumentBefore;
|
|
91
|
+
/**
|
|
92
|
+
* Checks if a change document passes the filter
|
|
93
|
+
*/
|
|
94
|
+
private passesFilter;
|
|
95
|
+
/**
|
|
96
|
+
* Returns an async iterator for the change stream
|
|
97
|
+
*/
|
|
98
|
+
[Symbol.asyncIterator](): AsyncIterableIterator<ChangeStreamDocument<T>>;
|
|
99
|
+
/**
|
|
100
|
+
* Closes the change stream
|
|
101
|
+
*/
|
|
102
|
+
close(): void;
|
|
103
|
+
/**
|
|
104
|
+
* Returns the next change document
|
|
105
|
+
*/
|
|
106
|
+
next(): Promise<{
|
|
107
|
+
value: ChangeStreamDocument<T>;
|
|
108
|
+
done: boolean;
|
|
109
|
+
}>;
|
|
110
|
+
/**
|
|
111
|
+
* Cleanup triggers when the change stream is destroyed
|
|
112
|
+
*/
|
|
113
|
+
cleanup(): Promise<void>;
|
|
114
|
+
}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
export class ChangeStream extends EventEmitter {
|
|
3
|
+
constructor(db, collectionName, options = {}) {
|
|
4
|
+
super();
|
|
5
|
+
this.db = db;
|
|
6
|
+
this.collectionName = collectionName;
|
|
7
|
+
this.options = options;
|
|
8
|
+
this.closed = false;
|
|
9
|
+
this.pollInterval = null;
|
|
10
|
+
this.lastProcessedId = 0;
|
|
11
|
+
this.buffer = [];
|
|
12
|
+
this.maxBufferSize = options.maxBufferSize || 1000;
|
|
13
|
+
this.setupChangeTracking();
|
|
14
|
+
this.startPolling();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Sets up the change tracking infrastructure
|
|
18
|
+
*/
|
|
19
|
+
async setupChangeTracking() {
|
|
20
|
+
await this.ensureChangeLogTable();
|
|
21
|
+
await this.setupTriggers();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Creates the change log table if it doesn't exist
|
|
25
|
+
*/
|
|
26
|
+
async ensureChangeLogTable() {
|
|
27
|
+
const createTableSQL = `
|
|
28
|
+
CREATE TABLE IF NOT EXISTS __mongolite_changes__ (
|
|
29
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
30
|
+
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
31
|
+
operation_type TEXT NOT NULL,
|
|
32
|
+
collection_name TEXT NOT NULL,
|
|
33
|
+
document_id TEXT NOT NULL,
|
|
34
|
+
full_document TEXT,
|
|
35
|
+
full_document_before TEXT,
|
|
36
|
+
updated_fields TEXT,
|
|
37
|
+
removed_fields TEXT,
|
|
38
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
39
|
+
);
|
|
40
|
+
`;
|
|
41
|
+
const createIndexSQL = `
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_mongolite_changes_collection_timestamp
|
|
43
|
+
ON __mongolite_changes__ (collection_name, timestamp);
|
|
44
|
+
`;
|
|
45
|
+
await this.db.exec(createTableSQL);
|
|
46
|
+
await this.db.exec(createIndexSQL);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Sets up triggers for tracking changes on the collection
|
|
50
|
+
*/
|
|
51
|
+
async setupTriggers() {
|
|
52
|
+
const collectionName = this.collectionName;
|
|
53
|
+
// Drop existing triggers for this collection
|
|
54
|
+
await this.db.exec(`DROP TRIGGER IF EXISTS "${collectionName}_insert_trigger"`);
|
|
55
|
+
await this.db.exec(`DROP TRIGGER IF EXISTS "${collectionName}_update_trigger"`);
|
|
56
|
+
await this.db.exec(`DROP TRIGGER IF EXISTS "${collectionName}_delete_trigger"`);
|
|
57
|
+
// Insert trigger
|
|
58
|
+
const insertTriggerSQL = `
|
|
59
|
+
CREATE TRIGGER "${collectionName}_insert_trigger"
|
|
60
|
+
AFTER INSERT ON "${collectionName}"
|
|
61
|
+
FOR EACH ROW
|
|
62
|
+
BEGIN
|
|
63
|
+
INSERT INTO __mongolite_changes__ (
|
|
64
|
+
operation_type, collection_name, document_id, full_document
|
|
65
|
+
) VALUES (
|
|
66
|
+
'insert', '${collectionName}', NEW._id, NEW.data
|
|
67
|
+
);
|
|
68
|
+
END;
|
|
69
|
+
`;
|
|
70
|
+
// Update trigger
|
|
71
|
+
const updateTriggerSQL = `
|
|
72
|
+
CREATE TRIGGER "${collectionName}_update_trigger"
|
|
73
|
+
AFTER UPDATE ON "${collectionName}"
|
|
74
|
+
FOR EACH ROW
|
|
75
|
+
BEGIN
|
|
76
|
+
INSERT INTO __mongolite_changes__ (
|
|
77
|
+
operation_type, collection_name, document_id,
|
|
78
|
+
full_document, full_document_before
|
|
79
|
+
) VALUES (
|
|
80
|
+
'update', '${collectionName}', NEW._id,
|
|
81
|
+
NEW.data, OLD.data
|
|
82
|
+
);
|
|
83
|
+
END;
|
|
84
|
+
`;
|
|
85
|
+
// Delete trigger
|
|
86
|
+
const deleteTriggerSQL = `
|
|
87
|
+
CREATE TRIGGER "${collectionName}_delete_trigger"
|
|
88
|
+
AFTER DELETE ON "${collectionName}"
|
|
89
|
+
FOR EACH ROW
|
|
90
|
+
BEGIN
|
|
91
|
+
INSERT INTO __mongolite_changes__ (
|
|
92
|
+
operation_type, collection_name, document_id, full_document_before
|
|
93
|
+
) VALUES (
|
|
94
|
+
'delete', '${collectionName}', OLD._id, OLD.data
|
|
95
|
+
);
|
|
96
|
+
END;
|
|
97
|
+
`;
|
|
98
|
+
await this.db.exec(insertTriggerSQL);
|
|
99
|
+
await this.db.exec(updateTriggerSQL);
|
|
100
|
+
await this.db.exec(deleteTriggerSQL);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Starts polling for new changes
|
|
104
|
+
*/
|
|
105
|
+
startPolling() {
|
|
106
|
+
if (this.closed)
|
|
107
|
+
return;
|
|
108
|
+
this.pollInterval = setInterval(async () => {
|
|
109
|
+
try {
|
|
110
|
+
await this.pollForChanges();
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
this.emit('error', error);
|
|
114
|
+
}
|
|
115
|
+
}, 100); // Poll every 100ms
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Polls for new changes from the change log
|
|
119
|
+
*/
|
|
120
|
+
async pollForChanges() {
|
|
121
|
+
if (this.closed)
|
|
122
|
+
return;
|
|
123
|
+
const changes = await this.db.all(`SELECT * FROM __mongolite_changes__
|
|
124
|
+
WHERE collection_name = ? AND id > ?
|
|
125
|
+
ORDER BY id ASC LIMIT 100`, [this.collectionName, this.lastProcessedId]);
|
|
126
|
+
for (const change of changes) {
|
|
127
|
+
if (this.closed)
|
|
128
|
+
break;
|
|
129
|
+
const changeDoc = await this.transformChangeEvent(change);
|
|
130
|
+
if (this.passesFilter(changeDoc)) {
|
|
131
|
+
this.buffer.push(changeDoc);
|
|
132
|
+
// Emit the change event
|
|
133
|
+
this.emit('change', changeDoc);
|
|
134
|
+
// Clean buffer if it gets too large
|
|
135
|
+
if (this.buffer.length > this.maxBufferSize) {
|
|
136
|
+
this.buffer = this.buffer.slice(-this.maxBufferSize);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
this.lastProcessedId = change.id;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Transforms a raw change event from the database into a ChangeStreamDocument
|
|
144
|
+
*/
|
|
145
|
+
async transformChangeEvent(change) {
|
|
146
|
+
const fullDocument = change.full_document ? JSON.parse(change.full_document) : undefined;
|
|
147
|
+
const fullDocumentBefore = change.full_document_before
|
|
148
|
+
? JSON.parse(change.full_document_before)
|
|
149
|
+
: undefined;
|
|
150
|
+
let updateDescription;
|
|
151
|
+
if (change.operation_type === 'update' && fullDocument && fullDocumentBefore) {
|
|
152
|
+
updateDescription = this.computeUpdateDescription(fullDocumentBefore, fullDocument);
|
|
153
|
+
}
|
|
154
|
+
const changeDoc = {
|
|
155
|
+
_id: `${change.id}`, // Use the change log ID as the change stream document ID
|
|
156
|
+
operationType: change.operation_type,
|
|
157
|
+
clusterTime: new Date(change.timestamp),
|
|
158
|
+
documentKey: { _id: change.document_id },
|
|
159
|
+
ns: {
|
|
160
|
+
db: 'mongolite', // You could make this configurable
|
|
161
|
+
coll: change.collection_name,
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
// Add full document based on options
|
|
165
|
+
if (this.shouldIncludeFullDocument(change.operation_type)) {
|
|
166
|
+
changeDoc.fullDocument = fullDocument
|
|
167
|
+
? { _id: change.document_id, ...fullDocument }
|
|
168
|
+
: undefined;
|
|
169
|
+
}
|
|
170
|
+
// Add full document before change based on options
|
|
171
|
+
if (this.shouldIncludeFullDocumentBefore(change.operation_type)) {
|
|
172
|
+
changeDoc.fullDocumentBeforeChange = fullDocumentBefore
|
|
173
|
+
? { _id: change.document_id, ...fullDocumentBefore }
|
|
174
|
+
: undefined;
|
|
175
|
+
}
|
|
176
|
+
if (updateDescription) {
|
|
177
|
+
changeDoc.updateDescription = updateDescription;
|
|
178
|
+
}
|
|
179
|
+
return changeDoc;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Computes the update description by comparing before and after documents
|
|
183
|
+
*/
|
|
184
|
+
computeUpdateDescription(before, after) {
|
|
185
|
+
const updatedFields = {};
|
|
186
|
+
const removedFields = [];
|
|
187
|
+
// Type guard to ensure we have objects to compare
|
|
188
|
+
if (typeof before !== 'object' ||
|
|
189
|
+
before === null ||
|
|
190
|
+
typeof after !== 'object' ||
|
|
191
|
+
after === null) {
|
|
192
|
+
return { updatedFields, removedFields };
|
|
193
|
+
}
|
|
194
|
+
// Simple comparison - this could be made more sophisticated
|
|
195
|
+
const beforeObj = before;
|
|
196
|
+
const afterObj = after;
|
|
197
|
+
const allKeys = new Set([...Object.keys(beforeObj), ...Object.keys(afterObj)]);
|
|
198
|
+
for (const key of allKeys) {
|
|
199
|
+
if (!(key in afterObj)) {
|
|
200
|
+
removedFields.push(key);
|
|
201
|
+
}
|
|
202
|
+
else if (!(key in beforeObj) ||
|
|
203
|
+
JSON.stringify(beforeObj[key]) !== JSON.stringify(afterObj[key])) {
|
|
204
|
+
updatedFields[key] = afterObj[key];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return { updatedFields, removedFields };
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Determines if the full document should be included
|
|
211
|
+
*/
|
|
212
|
+
shouldIncludeFullDocument(operationType) {
|
|
213
|
+
const option = this.options.fullDocument || 'default';
|
|
214
|
+
switch (option) {
|
|
215
|
+
case 'default':
|
|
216
|
+
return operationType === 'insert';
|
|
217
|
+
case 'updateLookup':
|
|
218
|
+
case 'whenAvailable':
|
|
219
|
+
case 'required':
|
|
220
|
+
return (operationType === 'insert' || operationType === 'update' || operationType === 'replace');
|
|
221
|
+
default:
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Determines if the full document before change should be included
|
|
227
|
+
*/
|
|
228
|
+
shouldIncludeFullDocumentBefore(operationType) {
|
|
229
|
+
const option = this.options.fullDocumentBeforeChange || 'off';
|
|
230
|
+
if (option === 'off')
|
|
231
|
+
return false;
|
|
232
|
+
return operationType === 'update' || operationType === 'replace' || operationType === 'delete';
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Checks if a change document passes the filter
|
|
236
|
+
*/
|
|
237
|
+
passesFilter(changeDoc) {
|
|
238
|
+
if (!this.options.filter)
|
|
239
|
+
return true;
|
|
240
|
+
// Simple filter implementation - could be enhanced
|
|
241
|
+
// For now, just check basic equality filters
|
|
242
|
+
for (const [key, value] of Object.entries(this.options.filter)) {
|
|
243
|
+
const docValue = changeDoc[key];
|
|
244
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
245
|
+
// Handle operators like $in, $eq, etc.
|
|
246
|
+
// This is a simplified implementation
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
else if (docValue !== value) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Returns an async iterator for the change stream
|
|
257
|
+
*/
|
|
258
|
+
async *[Symbol.asyncIterator]() {
|
|
259
|
+
while (!this.closed) {
|
|
260
|
+
if (this.buffer.length > 0) {
|
|
261
|
+
yield this.buffer.shift();
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
// Wait for next change
|
|
265
|
+
await new Promise((resolve) => {
|
|
266
|
+
const onChange = () => {
|
|
267
|
+
this.removeListener('change', onChange);
|
|
268
|
+
this.removeListener('close', onClose);
|
|
269
|
+
resolve();
|
|
270
|
+
};
|
|
271
|
+
const onClose = () => {
|
|
272
|
+
this.removeListener('change', onChange);
|
|
273
|
+
this.removeListener('close', onClose);
|
|
274
|
+
resolve();
|
|
275
|
+
};
|
|
276
|
+
this.once('change', onChange);
|
|
277
|
+
this.once('close', onClose);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Closes the change stream
|
|
284
|
+
*/
|
|
285
|
+
close() {
|
|
286
|
+
if (this.closed)
|
|
287
|
+
return;
|
|
288
|
+
this.closed = true;
|
|
289
|
+
if (this.pollInterval) {
|
|
290
|
+
clearInterval(this.pollInterval);
|
|
291
|
+
this.pollInterval = null;
|
|
292
|
+
}
|
|
293
|
+
this.emit('close');
|
|
294
|
+
this.removeAllListeners();
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Returns the next change document
|
|
298
|
+
*/
|
|
299
|
+
async next() {
|
|
300
|
+
if (this.closed) {
|
|
301
|
+
return { value: {}, done: true };
|
|
302
|
+
}
|
|
303
|
+
if (this.buffer.length > 0) {
|
|
304
|
+
return { value: this.buffer.shift(), done: false };
|
|
305
|
+
}
|
|
306
|
+
// Wait for next change
|
|
307
|
+
return new Promise((resolve) => {
|
|
308
|
+
const onChange = (changeDoc) => {
|
|
309
|
+
this.removeListener('change', onChange);
|
|
310
|
+
this.removeListener('close', onClose);
|
|
311
|
+
resolve({ value: changeDoc, done: false });
|
|
312
|
+
};
|
|
313
|
+
const onClose = () => {
|
|
314
|
+
this.removeListener('change', onChange);
|
|
315
|
+
this.removeListener('close', onClose);
|
|
316
|
+
resolve({ value: {}, done: true });
|
|
317
|
+
};
|
|
318
|
+
this.once('change', onChange);
|
|
319
|
+
this.once('close', onClose);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Cleanup triggers when the change stream is destroyed
|
|
324
|
+
*/
|
|
325
|
+
async cleanup() {
|
|
326
|
+
const collectionName = this.collectionName;
|
|
327
|
+
try {
|
|
328
|
+
await this.db.exec(`DROP TRIGGER IF EXISTS "${collectionName}_insert_trigger"`);
|
|
329
|
+
await this.db.exec(`DROP TRIGGER IF EXISTS "${collectionName}_update_trigger"`);
|
|
330
|
+
await this.db.exec(`DROP TRIGGER IF EXISTS "${collectionName}_delete_trigger"`);
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
// Ignore errors during cleanup
|
|
334
|
+
console.warn('Error during change stream cleanup:', error);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
//# sourceMappingURL=changeStream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changeStream.js","sourceRoot":"","sources":["../src/changeStream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare-safe entry point for MongoLite.
|
|
3
|
+
*
|
|
4
|
+
* Import from `@semics-tech/mongolite/cloudflare` inside Cloudflare Workers / Durable Objects.
|
|
5
|
+
* This module does **not** import `better-sqlite3`, so it bundles cleanly with `wrangler`.
|
|
6
|
+
*
|
|
7
|
+
* ## Requirements
|
|
8
|
+
*
|
|
9
|
+
* - Enable **Node.js compatibility** in your Worker configuration (e.g. `nodejs_compat`
|
|
10
|
+
* flag in `wrangler.toml`/`wrangler.jsonc`). The `ChangeStream` class depends on
|
|
11
|
+
* Node's built-in `events` module, which requires this flag even if you don't use
|
|
12
|
+
* change streams directly.
|
|
13
|
+
*
|
|
14
|
+
* ## Usage
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { DurableObject } from 'cloudflare:workers';
|
|
18
|
+
* import { MongoLite, CloudflareDurableObjectAdapter } from '@semics-tech/mongolite/cloudflare';
|
|
19
|
+
*
|
|
20
|
+
* export class MyDurableObject extends DurableObject {
|
|
21
|
+
* private client: MongoLite;
|
|
22
|
+
*
|
|
23
|
+
* constructor(ctx: DurableObjectState, env: Env) {
|
|
24
|
+
* super(ctx, env);
|
|
25
|
+
* this.client = new MongoLite(new CloudflareDurableObjectAdapter(ctx.storage.sql));
|
|
26
|
+
* ctx.blockConcurrencyWhile(() => this.client.collection('users').ensureTable());
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export { MongoLite } from './mongo-client.js';
|
|
32
|
+
export type { MongoLiteBaseOptions } from './mongo-client.js';
|
|
33
|
+
export { CloudflareDurableObjectAdapter } from './adapters/cloudflare.js';
|
|
34
|
+
export type { SqlStorage, SqlStorageCursor, SqlStorageValue, } from './adapters/cloudflare.js';
|
|
35
|
+
export type { IDatabaseAdapter } from './db.js';
|
|
36
|
+
export { MongoLiteCollection } from './collection.js';
|
|
37
|
+
export * from './types.js';
|
|
38
|
+
export { ChangeStream } from './changeStream.js';
|
|
39
|
+
export type { ChangeStreamDocument, ChangeStreamOptions, ChangeOperationType, } from './changeStream.js';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare-safe entry point for MongoLite.
|
|
3
|
+
*
|
|
4
|
+
* Import from `@semics-tech/mongolite/cloudflare` inside Cloudflare Workers / Durable Objects.
|
|
5
|
+
* This module does **not** import `better-sqlite3`, so it bundles cleanly with `wrangler`.
|
|
6
|
+
*
|
|
7
|
+
* ## Requirements
|
|
8
|
+
*
|
|
9
|
+
* - Enable **Node.js compatibility** in your Worker configuration (e.g. `nodejs_compat`
|
|
10
|
+
* flag in `wrangler.toml`/`wrangler.jsonc`). The `ChangeStream` class depends on
|
|
11
|
+
* Node's built-in `events` module, which requires this flag even if you don't use
|
|
12
|
+
* change streams directly.
|
|
13
|
+
*
|
|
14
|
+
* ## Usage
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { DurableObject } from 'cloudflare:workers';
|
|
18
|
+
* import { MongoLite, CloudflareDurableObjectAdapter } from '@semics-tech/mongolite/cloudflare';
|
|
19
|
+
*
|
|
20
|
+
* export class MyDurableObject extends DurableObject {
|
|
21
|
+
* private client: MongoLite;
|
|
22
|
+
*
|
|
23
|
+
* constructor(ctx: DurableObjectState, env: Env) {
|
|
24
|
+
* super(ctx, env);
|
|
25
|
+
* this.client = new MongoLite(new CloudflareDurableObjectAdapter(ctx.storage.sql));
|
|
26
|
+
* ctx.blockConcurrencyWhile(() => this.client.collection('users').ensureTable());
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
// Core MongoLite class — accepts any IDatabaseAdapter, no better-sqlite3 dependency.
|
|
32
|
+
export { MongoLite } from './mongo-client.js';
|
|
33
|
+
// Cloudflare-specific adapter
|
|
34
|
+
export { CloudflareDurableObjectAdapter } from './adapters/cloudflare.js';
|
|
35
|
+
export { MongoLiteCollection } from './collection.js';
|
|
36
|
+
export * from './types.js';
|
|
37
|
+
// Change streams (requires nodejs_compat in Workers)
|
|
38
|
+
export { ChangeStream } from './changeStream.js';
|
|
39
|
+
//# sourceMappingURL=cloudflare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../src/cloudflare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,qFAAqF;AACrF,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAG9C,8BAA8B;AAC9B,OAAO,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAS1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,cAAc,YAAY,CAAC;AAE3B,qDAAqD;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|