pomegranate-db 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.
- package/LICENSE +21 -0
- package/NOTICE.md +38 -0
- package/PomegranateDB.podspec +67 -0
- package/README.md +122 -0
- package/dist/adapters/expo-sqlite/ExpoSQLiteDriver.d.ts +34 -0
- package/dist/adapters/expo-sqlite/ExpoSQLiteDriver.js +155 -0
- package/dist/adapters/expo-sqlite/index.d.ts +2 -0
- package/dist/adapters/expo-sqlite/index.js +6 -0
- package/dist/adapters/index.d.ts +7 -0
- package/dist/adapters/index.js +13 -0
- package/dist/adapters/loki/LokiAdapter.d.ts +100 -0
- package/dist/adapters/loki/LokiAdapter.js +144 -0
- package/dist/adapters/loki/index.d.ts +6 -0
- package/dist/adapters/loki/index.js +12 -0
- package/dist/adapters/loki/worker/LokiDispatcher.d.ts +21 -0
- package/dist/adapters/loki/worker/LokiDispatcher.js +63 -0
- package/dist/adapters/loki/worker/LokiExecutor.d.ts +96 -0
- package/dist/adapters/loki/worker/LokiExecutor.js +462 -0
- package/dist/adapters/loki/worker/SynchronousWorker.d.ts +22 -0
- package/dist/adapters/loki/worker/SynchronousWorker.js +76 -0
- package/dist/adapters/loki/worker/loki.worker.d.ts +14 -0
- package/dist/adapters/loki/worker/loki.worker.js +112 -0
- package/dist/adapters/loki/worker/types.d.ts +44 -0
- package/dist/adapters/loki/worker/types.js +11 -0
- package/dist/adapters/native-sqlite/NativeSQLiteDriver.d.ts +55 -0
- package/dist/adapters/native-sqlite/NativeSQLiteDriver.js +145 -0
- package/dist/adapters/native-sqlite/index.d.ts +2 -0
- package/dist/adapters/native-sqlite/index.js +6 -0
- package/dist/adapters/op-sqlite/OpSQLiteDriver.d.ts +49 -0
- package/dist/adapters/op-sqlite/OpSQLiteDriver.js +140 -0
- package/dist/adapters/op-sqlite/index.d.ts +2 -0
- package/dist/adapters/op-sqlite/index.js +6 -0
- package/dist/adapters/sqlite/SQLiteAdapter.d.ts +70 -0
- package/dist/adapters/sqlite/SQLiteAdapter.js +264 -0
- package/dist/adapters/sqlite/index.d.ts +2 -0
- package/dist/adapters/sqlite/index.js +6 -0
- package/dist/adapters/sqlite/sql.d.ts +35 -0
- package/dist/adapters/sqlite/sql.js +258 -0
- package/dist/adapters/types.d.ts +93 -0
- package/dist/adapters/types.js +9 -0
- package/dist/collection/Collection.d.ts +103 -0
- package/dist/collection/Collection.js +245 -0
- package/dist/collection/index.d.ts +2 -0
- package/dist/collection/index.js +6 -0
- package/dist/database/Database.d.ts +128 -0
- package/dist/database/Database.js +245 -0
- package/dist/database/index.d.ts +2 -0
- package/dist/database/index.js +6 -0
- package/dist/encryption/index.d.ts +62 -0
- package/dist/encryption/index.js +276 -0
- package/dist/encryption/nodeCrypto.d.ts +18 -0
- package/dist/encryption/nodeCrypto.js +25 -0
- package/dist/encryption/nodeCrypto.native.d.ts +13 -0
- package/dist/encryption/nodeCrypto.native.js +26 -0
- package/dist/expo.d.ts +12 -0
- package/dist/expo.js +32 -0
- package/dist/hooks/index.d.ts +115 -0
- package/dist/hooks/index.js +285 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +57 -0
- package/dist/model/Model.d.ts +92 -0
- package/dist/model/Model.js +251 -0
- package/dist/model/index.d.ts +2 -0
- package/dist/model/index.js +7 -0
- package/dist/observable/Subject.d.ts +60 -0
- package/dist/observable/Subject.js +132 -0
- package/dist/observable/index.d.ts +2 -0
- package/dist/observable/index.js +10 -0
- package/dist/query/QueryBuilder.d.ts +51 -0
- package/dist/query/QueryBuilder.js +165 -0
- package/dist/query/index.d.ts +2 -0
- package/dist/query/index.js +7 -0
- package/dist/query/types.d.ts +60 -0
- package/dist/query/types.js +9 -0
- package/dist/schema/builder.d.ts +68 -0
- package/dist/schema/builder.js +168 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +7 -0
- package/dist/schema/types.d.ts +108 -0
- package/dist/schema/types.js +9 -0
- package/dist/sync/index.d.ts +2 -0
- package/dist/sync/index.js +6 -0
- package/dist/sync/sync.d.ts +15 -0
- package/dist/sync/sync.js +182 -0
- package/dist/sync/types.d.ts +41 -0
- package/dist/sync/types.js +6 -0
- package/dist/utils/index.d.ts +45 -0
- package/dist/utils/index.js +99 -0
- package/expo-plugin/index.d.ts +68 -0
- package/expo-plugin/index.js +83 -0
- package/native/android-jsi/build.gradle +45 -0
- package/native/android-jsi/src/main/AndroidManifest.xml +2 -0
- package/native/android-jsi/src/main/cpp/CMakeLists.txt +73 -0
- package/native/android-jsi/src/main/cpp/DatabasePlatformAndroid.cpp +107 -0
- package/native/android-jsi/src/main/cpp/DatabasePlatformAndroid.h +16 -0
- package/native/android-jsi/src/main/cpp/JSIInstaller.cpp +27 -0
- package/native/android-jsi/src/main/java/com/pomegranate/jsi/JSIInstaller.kt +43 -0
- package/native/android-jsi/src/main/java/com/pomegranate/jsi/PomegranateJSIModule.kt +39 -0
- package/native/android-jsi/src/main/java/com/pomegranate/jsi/PomegranateJSIPackage.kt +17 -0
- package/native/ios/DatabasePlatformIOS.mm +83 -0
- package/native/ios/PomegranateJSI.h +15 -0
- package/native/ios/PomegranateJSI.mm +59 -0
- package/native/shared/Database.cpp +283 -0
- package/native/shared/Database.h +84 -0
- package/native/shared/Sqlite.cpp +61 -0
- package/native/shared/Sqlite.h +67 -0
- package/native/shared/sqlite3/sqlite3.c +260493 -0
- package/native/shared/sqlite3/sqlite3.h +13583 -0
- package/package.json +127 -0
- package/react-native.config.js +28 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LokiJS Executor.
|
|
4
|
+
*
|
|
5
|
+
* Contains all actual database operations using LokiJS.
|
|
6
|
+
* Runs either in the main thread (direct mode) or inside a Web Worker.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.LokiExecutor = void 0;
|
|
43
|
+
// ─── LokiJS Executor ─────────────────────────────────────────────────────
|
|
44
|
+
class LokiExecutor {
|
|
45
|
+
_db = null;
|
|
46
|
+
_config;
|
|
47
|
+
_schemaVersion = 0;
|
|
48
|
+
_initialized = false;
|
|
49
|
+
constructor(config) {
|
|
50
|
+
this._config = config;
|
|
51
|
+
}
|
|
52
|
+
// ─── Initialize ──────────────────────────────────────────────────────
|
|
53
|
+
async initialize(schema) {
|
|
54
|
+
if (this._initialized)
|
|
55
|
+
return;
|
|
56
|
+
if (this._config.lokiInstance) {
|
|
57
|
+
this._db = this._config.lokiInstance;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this._db = await this._createLokiDb();
|
|
61
|
+
}
|
|
62
|
+
let metaCollection = this._db.getCollection('__pomegranate_metadata');
|
|
63
|
+
if (!metaCollection) {
|
|
64
|
+
metaCollection = this._db.addCollection('__pomegranate_metadata', { unique: ['key'] });
|
|
65
|
+
}
|
|
66
|
+
const versionDoc = metaCollection.findOne({ key: 'schema_version' });
|
|
67
|
+
const existingVersion = versionDoc ? Number.parseInt(versionDoc.value, 10) : 0;
|
|
68
|
+
if (existingVersion === 0) {
|
|
69
|
+
for (const table of schema.tables) {
|
|
70
|
+
if (!this._db.getCollection(table.name)) {
|
|
71
|
+
const indices = table.columns.filter((c) => c.isIndexed).map((c) => c.name);
|
|
72
|
+
this._db.addCollection(table.name, {
|
|
73
|
+
unique: ['id'],
|
|
74
|
+
indices: ['_status', ...indices],
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const existing = metaCollection.findOne({ key: 'schema_version' });
|
|
79
|
+
if (existing) {
|
|
80
|
+
existing.value = String(schema.version);
|
|
81
|
+
metaCollection.update(existing);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
metaCollection.insert({ key: 'schema_version', value: String(schema.version) });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
this._schemaVersion = schema.version;
|
|
88
|
+
this._initialized = true;
|
|
89
|
+
}
|
|
90
|
+
async _createLokiDb() {
|
|
91
|
+
const { default: Loki } = await Promise.resolve().then(() => __importStar(require('lokijs')));
|
|
92
|
+
const hasPersistence = !!this._config.persistenceAdapter;
|
|
93
|
+
const options = {};
|
|
94
|
+
if (hasPersistence) {
|
|
95
|
+
options.adapter = this._config.persistenceAdapter;
|
|
96
|
+
const strategy = this._config.saveStrategy ?? 'immediate';
|
|
97
|
+
if (strategy === 'auto') {
|
|
98
|
+
options.autosave = true;
|
|
99
|
+
options.autosaveinterval = this._config.autosaveInterval ?? 500;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
if (hasPersistence) {
|
|
104
|
+
options.autoload = true;
|
|
105
|
+
options.autoloadCallback = (err) => {
|
|
106
|
+
if (err)
|
|
107
|
+
reject(err);
|
|
108
|
+
else
|
|
109
|
+
resolve(db);
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
const db = new Loki(this._config.databaseName || 'pomegranate.db', options);
|
|
113
|
+
if (!hasPersistence) {
|
|
114
|
+
resolve(db);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
_getCollection(table) {
|
|
119
|
+
if (!this._db)
|
|
120
|
+
throw new Error('Database not initialized');
|
|
121
|
+
const col = this._db.getCollection(table);
|
|
122
|
+
if (!col)
|
|
123
|
+
throw new Error(`Collection "${table}" not found`);
|
|
124
|
+
return col;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Persist to storage adapter.
|
|
128
|
+
* No-op when: no persistence configured, or saveStrategy is 'auto' (timer handles it).
|
|
129
|
+
*/
|
|
130
|
+
async _save() {
|
|
131
|
+
if (!this._db || !this._config.persistenceAdapter)
|
|
132
|
+
return;
|
|
133
|
+
if ((this._config.saveStrategy ?? 'immediate') !== 'immediate')
|
|
134
|
+
return;
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
this._db.saveDatabase((err) => {
|
|
137
|
+
if (err)
|
|
138
|
+
reject(err);
|
|
139
|
+
else
|
|
140
|
+
resolve();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
// ─── Core sync operations (for batch optimization) ──────────────────
|
|
145
|
+
_doInsert(table, raw) {
|
|
146
|
+
const col = this._getCollection(table);
|
|
147
|
+
col.insert({ ...raw });
|
|
148
|
+
}
|
|
149
|
+
_doUpdate(table, raw) {
|
|
150
|
+
const col = this._getCollection(table);
|
|
151
|
+
const existing = col.findOne({ id: raw.id });
|
|
152
|
+
if (!existing)
|
|
153
|
+
throw new Error(`Record not found: ${table}/${raw.id}`);
|
|
154
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
155
|
+
existing[key] = value;
|
|
156
|
+
}
|
|
157
|
+
col.update(existing);
|
|
158
|
+
}
|
|
159
|
+
_doMarkAsDeleted(table, id) {
|
|
160
|
+
const col = this._getCollection(table);
|
|
161
|
+
const doc = col.findOne({ id });
|
|
162
|
+
if (doc) {
|
|
163
|
+
doc._status = 'deleted';
|
|
164
|
+
col.update(doc);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
_doDestroyPermanently(table, id) {
|
|
168
|
+
const col = this._getCollection(table);
|
|
169
|
+
const doc = col.findOne({ id });
|
|
170
|
+
if (doc) {
|
|
171
|
+
col.remove(doc);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// ─── Query ──────────────────────────────────────────────────────────
|
|
175
|
+
async find(query) {
|
|
176
|
+
const col = this._getCollection(query.table);
|
|
177
|
+
let chain = col.chain();
|
|
178
|
+
if (query.conditions.length > 0) {
|
|
179
|
+
const lokiQuery = conditionsToLoki(query.conditions);
|
|
180
|
+
chain = chain.find(lokiQuery);
|
|
181
|
+
}
|
|
182
|
+
for (const ob of query.orderBy) {
|
|
183
|
+
chain = chain.simplesort(ob.column, { desc: ob.order === 'desc' });
|
|
184
|
+
}
|
|
185
|
+
if (query.offset !== undefined) {
|
|
186
|
+
chain = chain.offset(query.offset);
|
|
187
|
+
}
|
|
188
|
+
if (query.limit !== undefined) {
|
|
189
|
+
chain = chain.limit(query.limit);
|
|
190
|
+
}
|
|
191
|
+
return stripLokiMeta(chain.data());
|
|
192
|
+
}
|
|
193
|
+
async count(query) {
|
|
194
|
+
const col = this._getCollection(query.table);
|
|
195
|
+
if (query.conditions.length > 0) {
|
|
196
|
+
const lokiQuery = conditionsToLoki(query.conditions);
|
|
197
|
+
return col.chain().find(lokiQuery).count();
|
|
198
|
+
}
|
|
199
|
+
return col.count();
|
|
200
|
+
}
|
|
201
|
+
async findById(table, id) {
|
|
202
|
+
const col = this._getCollection(table);
|
|
203
|
+
const doc = col.findOne({ id });
|
|
204
|
+
return doc ? stripLokiMetaSingle(doc) : null;
|
|
205
|
+
}
|
|
206
|
+
// ─── Insert / Update / Delete ────────────────────────────────────────
|
|
207
|
+
async insert(table, raw) {
|
|
208
|
+
this._doInsert(table, raw);
|
|
209
|
+
await this._save();
|
|
210
|
+
}
|
|
211
|
+
async update(table, raw) {
|
|
212
|
+
this._doUpdate(table, raw);
|
|
213
|
+
await this._save();
|
|
214
|
+
}
|
|
215
|
+
async markAsDeleted(table, id) {
|
|
216
|
+
this._doMarkAsDeleted(table, id);
|
|
217
|
+
await this._save();
|
|
218
|
+
}
|
|
219
|
+
async destroyPermanently(table, id) {
|
|
220
|
+
this._doDestroyPermanently(table, id);
|
|
221
|
+
await this._save();
|
|
222
|
+
}
|
|
223
|
+
// ─── Batch (single save at end) ─────────────────────────────────────
|
|
224
|
+
async batch(operations) {
|
|
225
|
+
for (const op of operations) {
|
|
226
|
+
switch (op.type) {
|
|
227
|
+
case 'create':
|
|
228
|
+
this._doInsert(op.table, op.rawRecord);
|
|
229
|
+
break;
|
|
230
|
+
case 'update':
|
|
231
|
+
this._doUpdate(op.table, op.rawRecord);
|
|
232
|
+
break;
|
|
233
|
+
case 'delete':
|
|
234
|
+
this._doMarkAsDeleted(op.table, op.id);
|
|
235
|
+
break;
|
|
236
|
+
case 'destroyPermanently':
|
|
237
|
+
this._doDestroyPermanently(op.table, op.id);
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
await this._save();
|
|
242
|
+
}
|
|
243
|
+
// ─── Search ──────────────────────────────────────────────────────────
|
|
244
|
+
async search(descriptor) {
|
|
245
|
+
const col = this._getCollection(descriptor.table);
|
|
246
|
+
const term = descriptor.term.toLowerCase();
|
|
247
|
+
let results = col.chain().where((doc) => {
|
|
248
|
+
return descriptor.fields.some((field) => {
|
|
249
|
+
const val = doc[field];
|
|
250
|
+
return typeof val === 'string' && val.toLowerCase().includes(term);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
if (descriptor.conditions.length > 0) {
|
|
254
|
+
const lokiQuery = conditionsToLoki(descriptor.conditions);
|
|
255
|
+
results = results.find(lokiQuery);
|
|
256
|
+
}
|
|
257
|
+
const total = results.count();
|
|
258
|
+
for (const ob of descriptor.orderBy) {
|
|
259
|
+
results = results.simplesort(ob.column, { desc: ob.order === 'desc' });
|
|
260
|
+
}
|
|
261
|
+
const data = results.offset(descriptor.offset).limit(descriptor.limit).data();
|
|
262
|
+
return {
|
|
263
|
+
records: stripLokiMeta(data),
|
|
264
|
+
total,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
// ─── Sync helpers ──────────────────────────────────────────────────
|
|
268
|
+
async getLocalChanges(tables) {
|
|
269
|
+
const result = {};
|
|
270
|
+
for (const table of tables) {
|
|
271
|
+
const col = this._getCollection(table);
|
|
272
|
+
const created = col.find({ _status: 'created' });
|
|
273
|
+
const updated = col.find({ _status: 'updated' });
|
|
274
|
+
const deleted = col.find({ _status: 'deleted' });
|
|
275
|
+
result[table] = {
|
|
276
|
+
created: stripLokiMeta(created),
|
|
277
|
+
updated: stripLokiMeta(updated),
|
|
278
|
+
deleted: deleted.map((d) => d.id),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
async applyRemoteChanges(changes) {
|
|
284
|
+
for (const [table, tableChanges] of Object.entries(changes)) {
|
|
285
|
+
const col = this._getCollection(table);
|
|
286
|
+
for (const raw of tableChanges.created) {
|
|
287
|
+
const existing = col.findOne({ id: raw.id });
|
|
288
|
+
if (existing) {
|
|
289
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
290
|
+
existing[key] = value;
|
|
291
|
+
}
|
|
292
|
+
existing._status = 'synced';
|
|
293
|
+
existing._changed = '';
|
|
294
|
+
col.update(existing);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
col.insert({ ...raw, _status: 'synced', _changed: '' });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
for (const raw of tableChanges.updated) {
|
|
301
|
+
const existing = col.findOne({ id: raw.id });
|
|
302
|
+
if (existing) {
|
|
303
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
304
|
+
existing[key] = value;
|
|
305
|
+
}
|
|
306
|
+
existing._status = 'synced';
|
|
307
|
+
existing._changed = '';
|
|
308
|
+
col.update(existing);
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
col.insert({ ...raw, _status: 'synced', _changed: '' });
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
for (const id of tableChanges.deleted) {
|
|
315
|
+
const doc = col.findOne({ id });
|
|
316
|
+
if (doc)
|
|
317
|
+
col.remove(doc);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
async markAsSynced(table, ids) {
|
|
322
|
+
const col = this._getCollection(table);
|
|
323
|
+
for (const id of ids) {
|
|
324
|
+
const doc = col.findOne({ id });
|
|
325
|
+
if (doc) {
|
|
326
|
+
doc._status = 'synced';
|
|
327
|
+
doc._changed = '';
|
|
328
|
+
col.update(doc);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// ─── Schema version ──────────────────────────────────────────────────
|
|
333
|
+
async getSchemaVersion() {
|
|
334
|
+
return this._schemaVersion;
|
|
335
|
+
}
|
|
336
|
+
// ─── Migration ──────────────────────────────────────────────────────
|
|
337
|
+
async migrate(migrations) {
|
|
338
|
+
for (const migration of migrations) {
|
|
339
|
+
for (const step of migration.steps) {
|
|
340
|
+
switch (step.type) {
|
|
341
|
+
case 'createTable':
|
|
342
|
+
if (!this._db.getCollection(step.schema.name)) {
|
|
343
|
+
this._db.addCollection(step.schema.name, { unique: ['id'] });
|
|
344
|
+
}
|
|
345
|
+
break;
|
|
346
|
+
case 'destroyTable':
|
|
347
|
+
this._db.removeCollection(step.table);
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// ─── Reset ──────────────────────────────────────────────────────────
|
|
354
|
+
async reset() {
|
|
355
|
+
if (!this._db)
|
|
356
|
+
return;
|
|
357
|
+
const collections = this._db.listCollections();
|
|
358
|
+
for (const col of collections) {
|
|
359
|
+
const collection = this._db.getCollection(col.name);
|
|
360
|
+
if (collection)
|
|
361
|
+
collection.clear();
|
|
362
|
+
}
|
|
363
|
+
this._initialized = false;
|
|
364
|
+
}
|
|
365
|
+
// ─── Close ──────────────────────────────────────────────────────────
|
|
366
|
+
async close() {
|
|
367
|
+
if (this._db) {
|
|
368
|
+
await new Promise((resolve) => {
|
|
369
|
+
this._db.close(() => resolve());
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
exports.LokiExecutor = LokiExecutor;
|
|
375
|
+
// ─── Loki Query Translation ──────────────────────────────────────────────
|
|
376
|
+
function conditionsToLoki(conditions) {
|
|
377
|
+
if (conditions.length === 1) {
|
|
378
|
+
return conditionToLoki(conditions[0]);
|
|
379
|
+
}
|
|
380
|
+
return { $and: conditions.map(conditionToLoki) };
|
|
381
|
+
}
|
|
382
|
+
function conditionToLoki(condition) {
|
|
383
|
+
switch (condition.type) {
|
|
384
|
+
case 'where': {
|
|
385
|
+
const w = condition;
|
|
386
|
+
return { [w.column]: operatorToLoki(w.operator, w.value) };
|
|
387
|
+
}
|
|
388
|
+
case 'and':
|
|
389
|
+
return { $and: condition.conditions.map(conditionToLoki) };
|
|
390
|
+
case 'or':
|
|
391
|
+
return { $or: condition.conditions.map(conditionToLoki) };
|
|
392
|
+
case 'not': {
|
|
393
|
+
const inner = conditionToLoki(condition.condition);
|
|
394
|
+
const negated = {};
|
|
395
|
+
for (const [key, val] of Object.entries(inner)) {
|
|
396
|
+
if (typeof val === 'object' && val !== null) {
|
|
397
|
+
negated[key] = { $not: val };
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
negated[key] = { $ne: val };
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return negated;
|
|
404
|
+
}
|
|
405
|
+
default:
|
|
406
|
+
throw new Error(`Unknown condition type: ${condition.type}`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function operatorToLoki(op, value) {
|
|
410
|
+
const normalizedValue = typeof value === 'boolean' ? (value ? 1 : 0) : value;
|
|
411
|
+
switch (op) {
|
|
412
|
+
case 'eq':
|
|
413
|
+
return { $eq: normalizedValue };
|
|
414
|
+
case 'neq':
|
|
415
|
+
return { $ne: normalizedValue };
|
|
416
|
+
case 'gt':
|
|
417
|
+
return { $gt: normalizedValue };
|
|
418
|
+
case 'gte':
|
|
419
|
+
return { $gte: normalizedValue };
|
|
420
|
+
case 'lt':
|
|
421
|
+
return { $lt: normalizedValue };
|
|
422
|
+
case 'lte':
|
|
423
|
+
return { $lte: normalizedValue };
|
|
424
|
+
case 'in':
|
|
425
|
+
return {
|
|
426
|
+
$in: Array.isArray(value)
|
|
427
|
+
? value.map((v) => (typeof v === 'boolean' ? (v ? 1 : 0) : v))
|
|
428
|
+
: value,
|
|
429
|
+
};
|
|
430
|
+
case 'notIn':
|
|
431
|
+
return {
|
|
432
|
+
$nin: Array.isArray(value)
|
|
433
|
+
? value.map((v) => (typeof v === 'boolean' ? (v ? 1 : 0) : v))
|
|
434
|
+
: value,
|
|
435
|
+
};
|
|
436
|
+
case 'like':
|
|
437
|
+
return { $regex: new RegExp(String(value).replaceAll('%', '.*').replaceAll('_', '.'), 'i') };
|
|
438
|
+
case 'notLike':
|
|
439
|
+
return {
|
|
440
|
+
$not: { $regex: new RegExp(String(value).replaceAll('%', '.*').replaceAll('_', '.'), 'i') },
|
|
441
|
+
};
|
|
442
|
+
case 'between': {
|
|
443
|
+
const [low, high] = value;
|
|
444
|
+
return { $between: [low, high] };
|
|
445
|
+
}
|
|
446
|
+
case 'isNull':
|
|
447
|
+
return { $eq: null };
|
|
448
|
+
case 'isNotNull':
|
|
449
|
+
return { $ne: null };
|
|
450
|
+
default:
|
|
451
|
+
throw new Error(`Unknown operator: ${op}`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
// ─── Strip LokiJS internal metadata ($loki, meta) ────────────────────────
|
|
455
|
+
function stripLokiMeta(docs) {
|
|
456
|
+
return docs.map(stripLokiMetaSingle);
|
|
457
|
+
}
|
|
458
|
+
function stripLokiMetaSingle(doc) {
|
|
459
|
+
const { $loki, meta, ...rest } = doc;
|
|
460
|
+
return rest;
|
|
461
|
+
}
|
|
462
|
+
//# sourceMappingURL=LokiExecutor.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synchronous Worker.
|
|
3
|
+
*
|
|
4
|
+
* Simulates the Web Worker API entirely in-process. Used for:
|
|
5
|
+
* - Testing the worker message protocol without actual threading
|
|
6
|
+
* - Environments where Web Workers are not available (e.g. Node.js tests)
|
|
7
|
+
*
|
|
8
|
+
* Internally creates a LokiExecutor and processes messages sequentially,
|
|
9
|
+
* cloning data via JSON round-trip to simulate structured cloning.
|
|
10
|
+
*/
|
|
11
|
+
import type { WorkerInterface } from './types';
|
|
12
|
+
export declare class SynchronousWorker implements WorkerInterface {
|
|
13
|
+
onmessage: ((event: {
|
|
14
|
+
data: unknown;
|
|
15
|
+
}) => void) | null;
|
|
16
|
+
private _executor;
|
|
17
|
+
private _queue;
|
|
18
|
+
private _isProcessing;
|
|
19
|
+
postMessage(data: unknown): void;
|
|
20
|
+
terminate(): void;
|
|
21
|
+
private _processNext;
|
|
22
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Synchronous Worker.
|
|
4
|
+
*
|
|
5
|
+
* Simulates the Web Worker API entirely in-process. Used for:
|
|
6
|
+
* - Testing the worker message protocol without actual threading
|
|
7
|
+
* - Environments where Web Workers are not available (e.g. Node.js tests)
|
|
8
|
+
*
|
|
9
|
+
* Internally creates a LokiExecutor and processes messages sequentially,
|
|
10
|
+
* cloning data via JSON round-trip to simulate structured cloning.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.SynchronousWorker = void 0;
|
|
14
|
+
const LokiExecutor_1 = require("./LokiExecutor");
|
|
15
|
+
class SynchronousWorker {
|
|
16
|
+
onmessage = null;
|
|
17
|
+
_executor = null;
|
|
18
|
+
_queue = [];
|
|
19
|
+
_isProcessing = false;
|
|
20
|
+
postMessage(data) {
|
|
21
|
+
// Clone the data to simulate structured cloning across worker boundary
|
|
22
|
+
const action = JSON.parse(JSON.stringify(data));
|
|
23
|
+
this._queue.push(action);
|
|
24
|
+
if (!this._isProcessing) {
|
|
25
|
+
void this._processNext();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
terminate() {
|
|
29
|
+
this._queue = [];
|
|
30
|
+
this._executor = null;
|
|
31
|
+
}
|
|
32
|
+
async _processNext() {
|
|
33
|
+
if (this._queue.length === 0) {
|
|
34
|
+
this._isProcessing = false;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
this._isProcessing = true;
|
|
38
|
+
const action = this._queue[0];
|
|
39
|
+
let result;
|
|
40
|
+
try {
|
|
41
|
+
if (action.type === 'setUp') {
|
|
42
|
+
const [setupPayload, schema] = action.payload;
|
|
43
|
+
this._executor = new LokiExecutor_1.LokiExecutor({
|
|
44
|
+
databaseName: setupPayload.databaseName,
|
|
45
|
+
saveStrategy: setupPayload.saveStrategy,
|
|
46
|
+
autosaveInterval: setupPayload.autosaveInterval,
|
|
47
|
+
});
|
|
48
|
+
await this._executor.initialize(schema);
|
|
49
|
+
result = { value: undefined };
|
|
50
|
+
}
|
|
51
|
+
else if (this._executor) {
|
|
52
|
+
const method = this._executor[action.type];
|
|
53
|
+
if (typeof method !== 'function') {
|
|
54
|
+
throw new TypeError(`Unknown command: ${action.type}`);
|
|
55
|
+
}
|
|
56
|
+
const value = await method.call(this._executor, ...action.payload);
|
|
57
|
+
result = { value };
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
throw new Error('Worker not initialized — call setUp first');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error_) {
|
|
64
|
+
const error = error_ instanceof Error ? error_ : new Error(String(error_));
|
|
65
|
+
result = { error: { message: error.message, stack: error.stack } };
|
|
66
|
+
}
|
|
67
|
+
const response = { id: action.id, result };
|
|
68
|
+
// Clone the response to simulate structured cloning
|
|
69
|
+
const clonedResponse = JSON.parse(JSON.stringify(response));
|
|
70
|
+
this.onmessage?.({ data: clonedResponse });
|
|
71
|
+
this._queue.shift();
|
|
72
|
+
await this._processNext();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.SynchronousWorker = SynchronousWorker;
|
|
76
|
+
//# sourceMappingURL=SynchronousWorker.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LokiJS Web Worker entry point.
|
|
3
|
+
*
|
|
4
|
+
* Minimal bootstrap — creates a LokiExecutor and processes commands
|
|
5
|
+
* sequentially via the message queue. Designed to be loaded as a
|
|
6
|
+
* Web Worker by the browser's Worker constructor.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // In your app (bundler must support worker URLs):
|
|
10
|
+
* const worker = new Worker(
|
|
11
|
+
* new URL('pomegranate-db/dist/adapters/loki/worker/loki.worker.js', import.meta.url),
|
|
12
|
+
* );
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LokiJS Web Worker entry point.
|
|
4
|
+
*
|
|
5
|
+
* Minimal bootstrap — creates a LokiExecutor and processes commands
|
|
6
|
+
* sequentially via the message queue. Designed to be loaded as a
|
|
7
|
+
* Web Worker by the browser's Worker constructor.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // In your app (bundler must support worker URLs):
|
|
11
|
+
* const worker = new Worker(
|
|
12
|
+
* new URL('pomegranate-db/dist/adapters/loki/worker/loki.worker.js', import.meta.url),
|
|
13
|
+
* );
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
const LokiExecutor_1 = require("./LokiExecutor");
|
|
50
|
+
const ctx = self;
|
|
51
|
+
let executor = null;
|
|
52
|
+
const actionQueue = [];
|
|
53
|
+
let isProcessing = false;
|
|
54
|
+
ctx.onmessage = (event) => {
|
|
55
|
+
actionQueue.push(event.data);
|
|
56
|
+
if (!isProcessing) {
|
|
57
|
+
void processNext();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
async function processNext() {
|
|
61
|
+
if (actionQueue.length === 0) {
|
|
62
|
+
isProcessing = false;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
isProcessing = true;
|
|
66
|
+
const action = actionQueue[0];
|
|
67
|
+
let result;
|
|
68
|
+
try {
|
|
69
|
+
if (action.type === 'setUp') {
|
|
70
|
+
const [setupPayload, schema] = action.payload;
|
|
71
|
+
// Auto-create IncrementalIDBAdapter for browser persistence
|
|
72
|
+
let persistenceAdapter;
|
|
73
|
+
try {
|
|
74
|
+
const { default: IncrementalIDBAdapter } = await Promise.resolve().then(() => __importStar(require('lokijs/src/incremental-indexeddb-adapter')));
|
|
75
|
+
persistenceAdapter = new IncrementalIDBAdapter();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// IndexedDB not available — fall back to memory-only
|
|
79
|
+
}
|
|
80
|
+
executor = new LokiExecutor_1.LokiExecutor({
|
|
81
|
+
databaseName: setupPayload.databaseName,
|
|
82
|
+
saveStrategy: setupPayload.saveStrategy,
|
|
83
|
+
autosaveInterval: setupPayload.autosaveInterval,
|
|
84
|
+
persistenceAdapter,
|
|
85
|
+
});
|
|
86
|
+
await executor.initialize(schema);
|
|
87
|
+
result = { value: undefined };
|
|
88
|
+
}
|
|
89
|
+
else if (executor) {
|
|
90
|
+
const method = executor[action.type];
|
|
91
|
+
if (typeof method !== 'function') {
|
|
92
|
+
throw new TypeError(`Unknown command: ${action.type}`);
|
|
93
|
+
}
|
|
94
|
+
const value = await method.call(executor, ...action.payload);
|
|
95
|
+
result = { value };
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
throw new Error('Worker not initialized — call setUp first');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error_) {
|
|
102
|
+
const error = error_ instanceof Error ? error_ : new Error(String(error_));
|
|
103
|
+
// Log for worker-side debugging (stack traces are lost across postMessage)
|
|
104
|
+
console.error(`[LokiWorker] Error in ${action.type}:`, error);
|
|
105
|
+
result = { error: { message: error.message, stack: error.stack } };
|
|
106
|
+
}
|
|
107
|
+
const response = { id: action.id, result };
|
|
108
|
+
ctx.postMessage(response);
|
|
109
|
+
actionQueue.shift();
|
|
110
|
+
await processNext();
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=loki.worker.js.map
|