mindoodb 0.0.2 → 0.0.3
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 +92 -19
- package/dist/__mocks__/expo-standard-web-crypto.d.ts +10 -0
- package/dist/__mocks__/expo-standard-web-crypto.d.ts.map +1 -0
- package/dist/__mocks__/expo-standard-web-crypto.js +22 -0
- package/dist/__mocks__/expo-standard-web-crypto.js.map +1 -0
- package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.d.ts +8 -0
- package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.d.ts.map +1 -0
- package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.js +16 -0
- package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.js.map +1 -0
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.d.ts +1 -1
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.d.ts.map +1 -1
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.js +2 -2
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.js.map +1 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts +11 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js +94 -0
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js.map +1 -1
- package/dist/appendonlystores/network/HttpTransport.d.ts +13 -2
- package/dist/appendonlystores/network/HttpTransport.d.ts.map +1 -1
- package/dist/appendonlystores/network/HttpTransport.js +86 -0
- package/dist/appendonlystores/network/HttpTransport.js.map +1 -1
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts +19 -2
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js +96 -0
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js.map +1 -1
- package/dist/appendonlystores/network/index.d.ts +1 -1
- package/dist/appendonlystores/network/index.d.ts.map +1 -1
- package/dist/appendonlystores/network/index.js.map +1 -1
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts +61 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts.map +1 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js +685 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js.map +1 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.d.ts +25 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.d.ts.map +1 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.js +41 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.js.map +1 -0
- package/dist/browser/index.d.ts +5 -0
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +13 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/core/BaseMindooDB.d.ts +12 -0
- package/dist/core/BaseMindooDB.d.ts.map +1 -1
- package/dist/core/BaseMindooDB.js +210 -108
- package/dist/core/BaseMindooDB.js.map +1 -1
- package/dist/core/BaseMindooTenant.d.ts +40 -6
- package/dist/core/BaseMindooTenant.d.ts.map +1 -1
- package/dist/core/BaseMindooTenant.js +179 -21
- package/dist/core/BaseMindooTenant.js.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.js +10 -0
- package/dist/core/BaseMindooTenantDirectory.js.map +1 -1
- package/dist/core/BaseMindooTenantFactory.d.ts +22 -28
- package/dist/core/BaseMindooTenantFactory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantFactory.js +161 -65
- package/dist/core/BaseMindooTenantFactory.js.map +1 -1
- package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.d.ts +68 -0
- package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -0
- package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.js +659 -0
- package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -0
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts +43 -3
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts.map +1 -1
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.js +162 -23
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.js.map +1 -1
- package/dist/core/appendonlystores/bloom.d.ts +85 -0
- package/dist/core/appendonlystores/bloom.d.ts.map +1 -0
- package/dist/core/appendonlystores/bloom.js +228 -0
- package/dist/core/appendonlystores/bloom.js.map +1 -0
- package/dist/core/appendonlystores/network/NetworkTransport.d.ts +22 -2
- package/dist/core/appendonlystores/network/NetworkTransport.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/types.d.ts +10 -0
- package/dist/core/appendonlystores/network/types.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/types.js.map +1 -1
- package/dist/core/appendonlystores/types.d.ts +123 -0
- package/dist/core/appendonlystores/types.d.ts.map +1 -1
- package/dist/core/automerge-adapter.d.ts +15 -0
- package/dist/core/automerge-adapter.d.ts.map +1 -0
- package/dist/core/automerge-adapter.js +73 -0
- package/dist/core/automerge-adapter.js.map +1 -0
- package/dist/core/crypto/pbkdf2Iterations.d.ts +12 -0
- package/dist/core/crypto/pbkdf2Iterations.d.ts.map +1 -0
- package/dist/core/crypto/pbkdf2Iterations.js +55 -0
- package/dist/core/crypto/pbkdf2Iterations.js.map +1 -0
- package/dist/core/index.d.ts +7 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +30 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/keys/KeyBag.d.ts +32 -12
- package/dist/core/keys/KeyBag.d.ts.map +1 -1
- package/dist/core/keys/KeyBag.js +86 -26
- package/dist/core/keys/KeyBag.js.map +1 -1
- package/dist/core/keys/KeyContext.d.ts +4 -0
- package/dist/core/keys/KeyContext.d.ts.map +1 -0
- package/dist/core/keys/KeyContext.js +11 -0
- package/dist/core/keys/KeyContext.js.map +1 -0
- package/dist/core/types.d.ts +218 -50
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/uri/MindooURI.d.ts +58 -0
- package/dist/core/uri/MindooURI.d.ts.map +1 -0
- package/dist/core/uri/MindooURI.js +169 -0
- package/dist/core/uri/MindooURI.js.map +1 -0
- package/dist/core/userid/index.d.ts +0 -10
- package/dist/core/userid/index.d.ts.map +1 -1
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts +541 -0
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -0
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js +1334 -0
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -0
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +5 -1
- package/dist/node/index.js.map +1 -1
- package/dist/reactnative/crypto/AESAdapter.d.ts +18 -0
- package/dist/reactnative/crypto/AESAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/AESAdapter.js +112 -0
- package/dist/reactnative/crypto/AESAdapter.js.map +1 -0
- package/dist/reactnative/crypto/Ed25519Adapter.d.ts +34 -0
- package/dist/reactnative/crypto/Ed25519Adapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/Ed25519Adapter.js +386 -0
- package/dist/reactnative/crypto/Ed25519Adapter.js.map +1 -0
- package/dist/reactnative/crypto/ExpoGoCryptoAdapter.d.ts +23 -0
- package/dist/reactnative/crypto/ExpoGoCryptoAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/ExpoGoCryptoAdapter.js +51 -0
- package/dist/reactnative/crypto/ExpoGoCryptoAdapter.js.map +1 -0
- package/dist/reactnative/crypto/HMACAdapter.d.ts +13 -0
- package/dist/reactnative/crypto/HMACAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/HMACAdapter.js +50 -0
- package/dist/reactnative/crypto/HMACAdapter.js.map +1 -0
- package/dist/reactnative/crypto/KeyFormatConverter.d.ts +21 -0
- package/dist/reactnative/crypto/KeyFormatConverter.d.ts.map +1 -0
- package/dist/reactnative/crypto/KeyFormatConverter.js +72 -0
- package/dist/reactnative/crypto/KeyFormatConverter.js.map +1 -0
- package/dist/reactnative/crypto/PBKDF2Adapter.d.ts +19 -0
- package/dist/reactnative/crypto/PBKDF2Adapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/PBKDF2Adapter.js +67 -0
- package/dist/reactnative/crypto/PBKDF2Adapter.js.map +1 -0
- package/dist/reactnative/crypto/QuickCryptoAdapter.d.ts +37 -0
- package/dist/reactnative/crypto/QuickCryptoAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/QuickCryptoAdapter.js +72 -0
- package/dist/reactnative/crypto/QuickCryptoAdapter.js.map +1 -0
- package/dist/reactnative/crypto/RSAAdapter.d.ts +38 -0
- package/dist/reactnative/crypto/RSAAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/RSAAdapter.js +184 -0
- package/dist/reactnative/crypto/RSAAdapter.js.map +1 -0
- package/dist/reactnative/crypto/ReactNativeCryptoAdapter.d.ts +33 -0
- package/dist/reactnative/crypto/ReactNativeCryptoAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/ReactNativeCryptoAdapter.js +61 -0
- package/dist/reactnative/crypto/ReactNativeCryptoAdapter.js.map +1 -0
- package/dist/reactnative/crypto/SHA256Adapter.d.ts +9 -0
- package/dist/reactnative/crypto/SHA256Adapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/SHA256Adapter.js +29 -0
- package/dist/reactnative/crypto/SHA256Adapter.js.map +1 -0
- package/dist/reactnative/crypto/SubtleCryptoPolyfill.d.ts +63 -0
- package/dist/reactnative/crypto/SubtleCryptoPolyfill.d.ts.map +1 -0
- package/dist/reactnative/crypto/SubtleCryptoPolyfill.js +350 -0
- package/dist/reactnative/crypto/SubtleCryptoPolyfill.js.map +1 -0
- package/dist/reactnative/index.d.ts +8 -0
- package/dist/reactnative/index.d.ts.map +1 -0
- package/dist/reactnative/index.js +38 -0
- package/dist/reactnative/index.js.map +1 -0
- package/package.json +51 -6
- package/patches/PATCHES.md +111 -0
- package/patches/react-native+0.76.9.patch +34 -0
- package/patches/react-native-quick-crypto+1.0.7.patch +42 -0
- package/scripts/cli.js +21 -0
- package/scripts/setup-react-native.js +228 -0
- package/templates/reactnative/App.js +252 -0
- package/templates/reactnative/app.json +29 -0
- package/templates/reactnative/babel.config.js +6 -0
- package/templates/reactnative/index.js +26 -0
- package/templates/reactnative/metro.config.snippet.js +39 -0
- package/templates/reactnative/mindoodb-polyfills.js +116 -0
|
@@ -1,41 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.BaseMindooDB = void 0;
|
|
37
|
-
|
|
4
|
+
// Import platform-appropriate Automerge implementation
|
|
5
|
+
// React Native: native Rust (react-native-automerge-generated)
|
|
6
|
+
// Browser/Node.js: WASM (@automerge/automerge/slim)
|
|
7
|
+
const automerge_adapter_1 = require("./automerge-adapter");
|
|
38
8
|
const uuid_1 = require("uuid");
|
|
9
|
+
const bloom_1 = require("./appendonlystores/bloom");
|
|
39
10
|
const idGeneration_1 = require("./utils/idGeneration");
|
|
40
11
|
const errors_1 = require("./errors");
|
|
41
12
|
const logging_1 = require("./logging");
|
|
@@ -73,6 +44,7 @@ class BaseMindooDB {
|
|
|
73
44
|
this.docCache = new Map();
|
|
74
45
|
// Track which entry IDs we've already processed
|
|
75
46
|
this.processedEntryIds = [];
|
|
47
|
+
this.processedEntryCursor = null;
|
|
76
48
|
// Index: automergeHash -> entryId for each document
|
|
77
49
|
// Used for resolving Automerge dependency hashes to entry IDs
|
|
78
50
|
this.automergeHashToEntryId = new Map(); // Map<docId, Map<automergeHash, entryId>>
|
|
@@ -265,7 +237,7 @@ class BaseMindooDB {
|
|
|
265
237
|
this.logger.debug(`Syncing store changes for database ${this.store.getId()} in tenant ${this.tenant.getId()}`);
|
|
266
238
|
this.logger.debug(`Already processed ${this.processedEntryIds.length} entry IDs`);
|
|
267
239
|
// Find new entries that we haven't processed yet
|
|
268
|
-
const newEntryMetadata = await this.
|
|
240
|
+
const newEntryMetadata = await this.getNewEntryMetadataForSync();
|
|
269
241
|
this.logger.debug(`Found ${newEntryMetadata.length} new entries`);
|
|
270
242
|
if (newEntryMetadata.length === 0) {
|
|
271
243
|
this.logger.debug(`No new entries to process`);
|
|
@@ -367,6 +339,105 @@ class BaseMindooDB {
|
|
|
367
339
|
this.processedEntryIds.push(...newEntryMetadata.map(em => em.id));
|
|
368
340
|
this.logger.debug(`Synced ${newEntryMetadata.length} new entries, index now has ${this.index.length} documents`);
|
|
369
341
|
}
|
|
342
|
+
supportsCursorScan(store) {
|
|
343
|
+
return typeof store.scanEntriesSince === "function";
|
|
344
|
+
}
|
|
345
|
+
async scanAllMetadata(store, filters) {
|
|
346
|
+
if (!this.supportsCursorScan(store)) {
|
|
347
|
+
if (filters?.docId) {
|
|
348
|
+
return store.findNewEntriesForDoc([], filters.docId);
|
|
349
|
+
}
|
|
350
|
+
return store.findNewEntries([]);
|
|
351
|
+
}
|
|
352
|
+
const all = [];
|
|
353
|
+
let cursor = null;
|
|
354
|
+
while (true) {
|
|
355
|
+
const page = await store.scanEntriesSince(cursor, 1000, filters);
|
|
356
|
+
all.push(...page.entries);
|
|
357
|
+
cursor = page.nextCursor;
|
|
358
|
+
if (!page.hasMore) {
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return all;
|
|
363
|
+
}
|
|
364
|
+
async getNewEntryMetadataForSync() {
|
|
365
|
+
if (!this.supportsCursorScan(this.store)) {
|
|
366
|
+
return this.store.findNewEntries(this.processedEntryIds);
|
|
367
|
+
}
|
|
368
|
+
const allNew = [];
|
|
369
|
+
let cursor = this.processedEntryCursor;
|
|
370
|
+
while (true) {
|
|
371
|
+
const page = await this.store.scanEntriesSince(cursor, 1000);
|
|
372
|
+
allNew.push(...page.entries);
|
|
373
|
+
cursor = page.nextCursor;
|
|
374
|
+
if (!page.hasMore) {
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
this.processedEntryCursor = cursor;
|
|
379
|
+
return allNew;
|
|
380
|
+
}
|
|
381
|
+
async syncEntriesFromStore(sourceStore, targetStore) {
|
|
382
|
+
let transferred = 0;
|
|
383
|
+
let targetBloom = null;
|
|
384
|
+
if (typeof targetStore.getIdBloomSummary === "function") {
|
|
385
|
+
try {
|
|
386
|
+
targetBloom = await targetStore.getIdBloomSummary();
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
this.logger.warn("Failed to get bloom summary from target store, falling back to exact checks", error);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (this.supportsCursorScan(sourceStore)) {
|
|
393
|
+
let cursor = null;
|
|
394
|
+
while (true) {
|
|
395
|
+
const page = await sourceStore.scanEntriesSince(cursor, 1000);
|
|
396
|
+
if (page.entries.length > 0) {
|
|
397
|
+
const ids = page.entries.map((m) => m.id);
|
|
398
|
+
let definitelyMissing = [];
|
|
399
|
+
let maybeExisting = ids;
|
|
400
|
+
if (targetBloom) {
|
|
401
|
+
definitelyMissing = [];
|
|
402
|
+
maybeExisting = [];
|
|
403
|
+
for (const id of ids) {
|
|
404
|
+
if ((0, bloom_1.bloomMightContainId)(targetBloom, id)) {
|
|
405
|
+
maybeExisting.push(id);
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
definitelyMissing.push(id);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
let missingIds = definitelyMissing;
|
|
413
|
+
if (maybeExisting.length > 0) {
|
|
414
|
+
const existing = await targetStore.hasEntries(maybeExisting);
|
|
415
|
+
const existingSet = new Set(existing);
|
|
416
|
+
const maybeMissing = maybeExisting.filter((id) => !existingSet.has(id));
|
|
417
|
+
missingIds = missingIds.concat(maybeMissing);
|
|
418
|
+
}
|
|
419
|
+
if (missingIds.length > 0) {
|
|
420
|
+
const missingEntries = await sourceStore.getEntries(missingIds);
|
|
421
|
+
await targetStore.putEntries(missingEntries);
|
|
422
|
+
transferred += missingEntries.length;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
cursor = page.nextCursor;
|
|
426
|
+
if (!page.hasMore) {
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return transferred;
|
|
431
|
+
}
|
|
432
|
+
const targetIds = await targetStore.getAllIds();
|
|
433
|
+
const sourceNewMetadata = await sourceStore.findNewEntries(targetIds);
|
|
434
|
+
if (sourceNewMetadata.length === 0) {
|
|
435
|
+
return 0;
|
|
436
|
+
}
|
|
437
|
+
const sourceEntries = await sourceStore.getEntries(sourceNewMetadata.map((m) => m.id));
|
|
438
|
+
await targetStore.putEntries(sourceEntries);
|
|
439
|
+
return sourceEntries.length;
|
|
440
|
+
}
|
|
370
441
|
getStore() {
|
|
371
442
|
return this.store;
|
|
372
443
|
}
|
|
@@ -407,18 +478,18 @@ class BaseMindooDB {
|
|
|
407
478
|
const docId = (0, uuid_1.v7)();
|
|
408
479
|
this.logger.debug(`Creating document ${docId} with key ${keyId}${useCustomSigningKey ? ' using custom signing key' : ''}`);
|
|
409
480
|
// Create initial Automerge document
|
|
410
|
-
const initialDoc = Automerge.init();
|
|
481
|
+
const initialDoc = automerge_adapter_1.Automerge.init();
|
|
411
482
|
// Create the first change
|
|
412
483
|
const now = Date.now();
|
|
413
484
|
this.logger.debug(`Creating initial Automerge change for document ${docId}`);
|
|
414
485
|
let newDoc;
|
|
415
486
|
try {
|
|
416
|
-
newDoc = Automerge.change(initialDoc, (doc) => {
|
|
487
|
+
newDoc = automerge_adapter_1.Automerge.change(initialDoc, (doc) => {
|
|
417
488
|
// Store metadata in the document payload
|
|
418
489
|
// We need to modify the document to ensure a change is created
|
|
419
490
|
doc._attachments = [];
|
|
420
491
|
});
|
|
421
|
-
this.logger.debug(`Successfully created Automerge change, document heads: ${JSON.stringify(Automerge.getHeads(newDoc))}`);
|
|
492
|
+
this.logger.debug(`Successfully created Automerge change, document heads: ${JSON.stringify(automerge_adapter_1.Automerge.getHeads(newDoc))}`);
|
|
422
493
|
}
|
|
423
494
|
catch (error) {
|
|
424
495
|
this.logger.error(`Error in Automerge.change for document ${docId}:`, error);
|
|
@@ -426,7 +497,7 @@ class BaseMindooDB {
|
|
|
426
497
|
}
|
|
427
498
|
// Get the change bytes from the document
|
|
428
499
|
this.logger.debug(`Getting change bytes from document ${docId}`);
|
|
429
|
-
const changeBytes = Automerge.getLastLocalChange(newDoc);
|
|
500
|
+
const changeBytes = automerge_adapter_1.Automerge.getLastLocalChange(newDoc);
|
|
430
501
|
if (!changeBytes) {
|
|
431
502
|
throw new Error("Failed to get change bytes from Automerge document");
|
|
432
503
|
}
|
|
@@ -435,7 +506,7 @@ class BaseMindooDB {
|
|
|
435
506
|
this.logger.debug(`Decoding change to get hash and dependencies`);
|
|
436
507
|
let decodedChange;
|
|
437
508
|
try {
|
|
438
|
-
decodedChange = Automerge.decodeChange(changeBytes);
|
|
509
|
+
decodedChange = automerge_adapter_1.Automerge.decodeChange(changeBytes);
|
|
439
510
|
this.logger.debug(`Successfully decoded change, hash: ${decodedChange.hash}, deps: ${decodedChange.deps?.length || 0}`);
|
|
440
511
|
}
|
|
441
512
|
catch (error) {
|
|
@@ -523,7 +594,7 @@ class BaseMindooDB {
|
|
|
523
594
|
async getDocumentAtTimestamp(docId, timestamp) {
|
|
524
595
|
this.logger.debug(`Getting document ${docId} at timestamp ${timestamp}`);
|
|
525
596
|
// Get all entry metadata for this document
|
|
526
|
-
const allEntryMetadata = await this.store
|
|
597
|
+
const allEntryMetadata = await this.scanAllMetadata(this.store, { docId });
|
|
527
598
|
// Filter entries up to the timestamp
|
|
528
599
|
const relevantEntries = allEntryMetadata
|
|
529
600
|
.filter(em => em.createdAt <= timestamp)
|
|
@@ -534,7 +605,7 @@ class BaseMindooDB {
|
|
|
534
605
|
// Load entries
|
|
535
606
|
const entries = await this.store.getEntries(relevantEntries.map(em => em.id));
|
|
536
607
|
// Apply changes in order
|
|
537
|
-
let doc = Automerge.init();
|
|
608
|
+
let doc = automerge_adapter_1.Automerge.init();
|
|
538
609
|
for (const entryData of entries) {
|
|
539
610
|
// Admin-only mode: only accept entries signed by the admin key
|
|
540
611
|
if (this._isAdminOnlyDb && entryData.createdByPublicKey !== this.getAdminPublicKey()) {
|
|
@@ -551,7 +622,7 @@ class BaseMindooDB {
|
|
|
551
622
|
// Decrypt payload (only after signature verification passes)
|
|
552
623
|
const decryptedPayload = await this.tenant.decryptPayload(entryData.encryptedData, entryData.decryptionKeyId);
|
|
553
624
|
// Apply change using loadIncremental - this is the recommended way for binary change data
|
|
554
|
-
doc = Automerge.loadIncremental(doc, decryptedPayload);
|
|
625
|
+
doc = automerge_adapter_1.Automerge.loadIncremental(doc, decryptedPayload);
|
|
555
626
|
// Register the automerge hash -> entry ID mapping for future dependency resolution
|
|
556
627
|
const parsed = (0, idGeneration_1.parseDocEntryId)(entryData.id);
|
|
557
628
|
if (parsed) {
|
|
@@ -581,7 +652,7 @@ class BaseMindooDB {
|
|
|
581
652
|
async *iterateDocumentHistory(docId) {
|
|
582
653
|
this.logger.debug(`Iterating document history for ${docId}`);
|
|
583
654
|
// Get all entry metadata for this document
|
|
584
|
-
const allEntryMetadata = await this.store
|
|
655
|
+
const allEntryMetadata = await this.scanAllMetadata(this.store, { docId });
|
|
585
656
|
// Filter to only doc_create, doc_change, and doc_delete entries (exclude snapshots)
|
|
586
657
|
const relevantEntries = allEntryMetadata
|
|
587
658
|
.filter(em => em.entryType === "doc_create" || em.entryType === "doc_change" || em.entryType === "doc_delete")
|
|
@@ -623,17 +694,17 @@ class BaseMindooDB {
|
|
|
623
694
|
this.logger.warn(`First entry is not doc_create, skipping`);
|
|
624
695
|
continue;
|
|
625
696
|
}
|
|
626
|
-
currentDoc = Automerge.init();
|
|
697
|
+
currentDoc = automerge_adapter_1.Automerge.init();
|
|
627
698
|
createdAt = entryMetadata.createdAt;
|
|
628
699
|
decryptionKeyId = entryMetadata.decryptionKeyId;
|
|
629
700
|
}
|
|
630
701
|
// Check document heads before applying change (for non-first entries)
|
|
631
|
-
const headsBefore = isFirstEntry ? null : (currentDoc ? Automerge.getHeads(currentDoc) : null);
|
|
702
|
+
const headsBefore = isFirstEntry ? null : (currentDoc ? automerge_adapter_1.Automerge.getHeads(currentDoc) : null);
|
|
632
703
|
// Apply change using loadIncremental
|
|
633
704
|
if (currentDoc === null) {
|
|
634
705
|
throw new Error("currentDoc should not be null at this point");
|
|
635
706
|
}
|
|
636
|
-
currentDoc = Automerge.loadIncremental(currentDoc, decryptedPayload);
|
|
707
|
+
currentDoc = automerge_adapter_1.Automerge.loadIncremental(currentDoc, decryptedPayload);
|
|
637
708
|
// Check if document actually changed by comparing heads
|
|
638
709
|
// For first entry (doc_create), always yield since it's the initial creation
|
|
639
710
|
// For delete entries, always yield
|
|
@@ -643,7 +714,7 @@ class BaseMindooDB {
|
|
|
643
714
|
shouldYield = true;
|
|
644
715
|
}
|
|
645
716
|
else {
|
|
646
|
-
const headsAfter = Automerge.getHeads(currentDoc);
|
|
717
|
+
const headsAfter = automerge_adapter_1.Automerge.getHeads(currentDoc);
|
|
647
718
|
const headsChanged = headsBefore !== null && JSON.stringify(headsBefore) !== JSON.stringify(headsAfter);
|
|
648
719
|
shouldYield = headsChanged;
|
|
649
720
|
}
|
|
@@ -655,7 +726,7 @@ class BaseMindooDB {
|
|
|
655
726
|
// Only yield if document actually changed (or if this is create/delete entry)
|
|
656
727
|
if (shouldYield) {
|
|
657
728
|
// Clone the document to ensure independence
|
|
658
|
-
const clonedDoc = Automerge.clone(currentDoc);
|
|
729
|
+
const clonedDoc = automerge_adapter_1.Automerge.clone(currentDoc);
|
|
659
730
|
// Create internal doc representation
|
|
660
731
|
const internalDoc = {
|
|
661
732
|
id: docId,
|
|
@@ -690,10 +761,14 @@ class BaseMindooDB {
|
|
|
690
761
|
return docIds;
|
|
691
762
|
}
|
|
692
763
|
async getAllDocumentIdsAtTimestamp(timestamp) {
|
|
764
|
+
// findEntries() uses an exclusive upper bound (createdAt < creationDateUntil).
|
|
765
|
+
// Query up to timestamp + 1 so entries created exactly at `timestamp` are included
|
|
766
|
+
// for the strict checks below (createTime < timestamp, deleteTime > timestamp).
|
|
767
|
+
const upperBoundExclusive = timestamp === Number.MAX_SAFE_INTEGER ? timestamp : timestamp + 1;
|
|
693
768
|
// Efficiently query for doc_create and doc_delete entries before the timestamp
|
|
694
769
|
const [creates, deletes] = await Promise.all([
|
|
695
|
-
this.store.findEntries("doc_create", null,
|
|
696
|
-
this.store.findEntries("doc_delete", null,
|
|
770
|
+
this.store.findEntries("doc_create", null, upperBoundExclusive),
|
|
771
|
+
this.store.findEntries("doc_delete", null, upperBoundExclusive)
|
|
697
772
|
]);
|
|
698
773
|
// Build sets of docIds for efficient lookup
|
|
699
774
|
const createdDocIds = new Set();
|
|
@@ -722,9 +797,9 @@ class BaseMindooDB {
|
|
|
722
797
|
const createTime = createTimes.get(docId);
|
|
723
798
|
const deleteTime = deleteTimes.get(docId);
|
|
724
799
|
// Document exists at timestamp if:
|
|
725
|
-
// 1. It was created before the timestamp
|
|
800
|
+
// 1. It was created at or before the timestamp
|
|
726
801
|
// 2. Either it was never deleted, or it was deleted after the timestamp
|
|
727
|
-
if (createTime
|
|
802
|
+
if (createTime <= timestamp) {
|
|
728
803
|
if (!deleteTime || deleteTime > timestamp) {
|
|
729
804
|
docIds.push(docId);
|
|
730
805
|
}
|
|
@@ -764,7 +839,7 @@ class BaseMindooDB {
|
|
|
764
839
|
// Create deletion change by clearing all fields from the document
|
|
765
840
|
// This ensures Automerge produces actual change bytes
|
|
766
841
|
// The deletion is also tracked via the "doc_delete" type entry in the append-only store
|
|
767
|
-
const newDoc = Automerge.change(internalDoc.doc, (doc) => {
|
|
842
|
+
const newDoc = automerge_adapter_1.Automerge.change(internalDoc.doc, (doc) => {
|
|
768
843
|
// Remove all fields from the document
|
|
769
844
|
for (const key of Object.keys(doc)) {
|
|
770
845
|
delete doc[key];
|
|
@@ -773,12 +848,12 @@ class BaseMindooDB {
|
|
|
773
848
|
doc._deleted = true;
|
|
774
849
|
});
|
|
775
850
|
// Get the change bytes from the document
|
|
776
|
-
const changeBytes = Automerge.getLastLocalChange(newDoc);
|
|
851
|
+
const changeBytes = automerge_adapter_1.Automerge.getLastLocalChange(newDoc);
|
|
777
852
|
if (!changeBytes) {
|
|
778
853
|
throw new Error("Failed to get change bytes from Automerge document");
|
|
779
854
|
}
|
|
780
855
|
// Decode the change to get hash and dependencies
|
|
781
|
-
const decodedChange = Automerge.decodeChange(changeBytes);
|
|
856
|
+
const decodedChange = automerge_adapter_1.Automerge.decodeChange(changeBytes);
|
|
782
857
|
const automergeHash = decodedChange.hash;
|
|
783
858
|
const automergeDepHashes = decodedChange.deps || []; // Dependencies from the decoded change
|
|
784
859
|
// Encrypt the change payload first
|
|
@@ -873,7 +948,7 @@ class BaseMindooDB {
|
|
|
873
948
|
// Apply the change function
|
|
874
949
|
const now = Date.now();
|
|
875
950
|
this.logger.debug(`Applying change function to document ${docId}`);
|
|
876
|
-
this.logger.debug(`Document state before change: heads=${JSON.stringify(Automerge.getHeads(internalDoc.doc))}`);
|
|
951
|
+
this.logger.debug(`Document state before change: heads=${JSON.stringify(automerge_adapter_1.Automerge.getHeads(internalDoc.doc))}`);
|
|
877
952
|
// For async callbacks, we need to handle document modifications carefully.
|
|
878
953
|
// Automerge.change() requires synchronous modifications, so we'll:
|
|
879
954
|
// 1. Execute the async callback to do any async work (like signing)
|
|
@@ -916,8 +991,8 @@ class BaseMindooDB {
|
|
|
916
991
|
pendingDeletions.delete(prop);
|
|
917
992
|
// Track the change
|
|
918
993
|
pendingChanges.set(prop, value);
|
|
919
|
-
//
|
|
920
|
-
|
|
994
|
+
// NOTE: Don't set on target immediately - Automerge requires changes
|
|
995
|
+
// to be made inside Automerge.change(). Pending changes are applied later.
|
|
921
996
|
}
|
|
922
997
|
return true;
|
|
923
998
|
},
|
|
@@ -928,8 +1003,8 @@ class BaseMindooDB {
|
|
|
928
1003
|
pendingDeletions.add(prop);
|
|
929
1004
|
// Remove from pending changes if it was there
|
|
930
1005
|
pendingChanges.delete(prop);
|
|
931
|
-
//
|
|
932
|
-
|
|
1006
|
+
// NOTE: Don't delete from target immediately - Automerge requires changes
|
|
1007
|
+
// to be made inside Automerge.change(). Pending deletions are applied later.
|
|
933
1008
|
}
|
|
934
1009
|
return true;
|
|
935
1010
|
},
|
|
@@ -1048,7 +1123,7 @@ class BaseMindooDB {
|
|
|
1048
1123
|
// Now apply the collected changes synchronously in Automerge.change()
|
|
1049
1124
|
let newDoc;
|
|
1050
1125
|
try {
|
|
1051
|
-
newDoc = Automerge.change(internalDoc.doc, (automergeDoc) => {
|
|
1126
|
+
newDoc = automerge_adapter_1.Automerge.change(internalDoc.doc, (automergeDoc) => {
|
|
1052
1127
|
// Apply all pending changes (sets/updates)
|
|
1053
1128
|
for (const [key, value] of pendingChanges) {
|
|
1054
1129
|
automergeDoc[key] = value;
|
|
@@ -1087,7 +1162,7 @@ class BaseMindooDB {
|
|
|
1087
1162
|
// Update lastModified timestamp
|
|
1088
1163
|
automergeDoc._lastModified = now;
|
|
1089
1164
|
});
|
|
1090
|
-
this.logger.debug(`Successfully applied change function, new document heads: ${JSON.stringify(Automerge.getHeads(newDoc))}`);
|
|
1165
|
+
this.logger.debug(`Successfully applied change function, new document heads: ${JSON.stringify(automerge_adapter_1.Automerge.getHeads(newDoc))}`);
|
|
1091
1166
|
}
|
|
1092
1167
|
catch (error) {
|
|
1093
1168
|
this.logger.error(`Error in Automerge.change for document ${docId}:`, error);
|
|
@@ -1095,14 +1170,14 @@ class BaseMindooDB {
|
|
|
1095
1170
|
}
|
|
1096
1171
|
// Get the change bytes from the document
|
|
1097
1172
|
this.logger.debug(`Getting change bytes from document ${docId}`);
|
|
1098
|
-
const changeBytes = Automerge.getLastLocalChange(newDoc);
|
|
1173
|
+
const changeBytes = automerge_adapter_1.Automerge.getLastLocalChange(newDoc);
|
|
1099
1174
|
if (!changeBytes) {
|
|
1100
1175
|
//TODO decide if we just exit here or throw an error
|
|
1101
1176
|
throw new Error("Failed to get change bytes from Automerge document");
|
|
1102
1177
|
}
|
|
1103
1178
|
this.logger.debug(`Got change bytes: ${changeBytes.length} bytes`);
|
|
1104
1179
|
// Decode the change to get hash and dependencies
|
|
1105
|
-
const decodedChange = Automerge.decodeChange(changeBytes);
|
|
1180
|
+
const decodedChange = automerge_adapter_1.Automerge.decodeChange(changeBytes);
|
|
1106
1181
|
const automergeHash = decodedChange.hash;
|
|
1107
1182
|
const automergeDepHashes = decodedChange.deps || []; // Dependencies from the decoded change
|
|
1108
1183
|
// Encrypt the change payload first
|
|
@@ -1256,7 +1331,7 @@ class BaseMindooDB {
|
|
|
1256
1331
|
}
|
|
1257
1332
|
this.logger.debug(`Applying ${newEntryMetadata.length} new entries to cached document ${docId}`);
|
|
1258
1333
|
// Get current document heads to check if it changes
|
|
1259
|
-
const headsBefore = Automerge.getHeads(cachedDoc.doc);
|
|
1334
|
+
const headsBefore = automerge_adapter_1.Automerge.getHeads(cachedDoc.doc);
|
|
1260
1335
|
// Filter entries to only include change entries (exclude snapshots)
|
|
1261
1336
|
const entriesToApply = newEntryMetadata.filter(em => em.entryType === "doc_create" || em.entryType === "doc_change" || em.entryType === "doc_delete");
|
|
1262
1337
|
if (entriesToApply.length === 0) {
|
|
@@ -1340,12 +1415,12 @@ class BaseMindooDB {
|
|
|
1340
1415
|
// Clone the cached document to avoid "outdated document" error
|
|
1341
1416
|
// This happens when the document has been wrapped and returned to the user
|
|
1342
1417
|
// Automerge marks documents as outdated when they're accessed, preventing direct mutation
|
|
1343
|
-
const clonedDoc = Automerge.clone(cachedDoc.doc);
|
|
1418
|
+
const clonedDoc = automerge_adapter_1.Automerge.clone(cachedDoc.doc);
|
|
1344
1419
|
// Apply all changes at once (Automerge handles dependency ordering)
|
|
1345
|
-
const result = Automerge.applyChanges(clonedDoc, changeBytes);
|
|
1420
|
+
const result = automerge_adapter_1.Automerge.applyChanges(clonedDoc, changeBytes);
|
|
1346
1421
|
const updatedDoc = result[0];
|
|
1347
1422
|
// Check if document actually changed
|
|
1348
|
-
const headsAfter = Automerge.getHeads(updatedDoc);
|
|
1423
|
+
const headsAfter = automerge_adapter_1.Automerge.getHeads(updatedDoc);
|
|
1349
1424
|
const headsChanged = JSON.stringify(headsBefore) !== JSON.stringify(headsAfter);
|
|
1350
1425
|
if (!headsChanged) {
|
|
1351
1426
|
this.logger.debug(`Document ${docId} heads unchanged after applying new entries`);
|
|
@@ -1385,7 +1460,7 @@ class BaseMindooDB {
|
|
|
1385
1460
|
// Get all entry metadata for this document
|
|
1386
1461
|
// TODO: Implement loading from last snapshot if available
|
|
1387
1462
|
this.logger.debug(`Getting all entry hashes for document ${docId}`);
|
|
1388
|
-
const allEntryMetadata = await this.store
|
|
1463
|
+
const allEntryMetadata = await this.scanAllMetadata(this.store, { docId });
|
|
1389
1464
|
this.logger.debug(`Found ${allEntryMetadata.length} total entry hashes for document ${docId}`);
|
|
1390
1465
|
if (allEntryMetadata.length === 0) {
|
|
1391
1466
|
this.logger.debug(`No entry hashes found for document ${docId}, returning null`);
|
|
@@ -1447,8 +1522,8 @@ class BaseMindooDB {
|
|
|
1447
1522
|
// This deserializes a full document snapshot from binary data
|
|
1448
1523
|
// According to Automerge docs: load() is equivalent to init() followed by loadIncremental()
|
|
1449
1524
|
this.logger.debug(`Loading snapshot into Automerge document`);
|
|
1450
|
-
doc = Automerge.load(decryptedSnapshot);
|
|
1451
|
-
this.logger.debug(`Successfully loaded snapshot, document heads: ${JSON.stringify(Automerge.getHeads(doc))}`);
|
|
1525
|
+
doc = automerge_adapter_1.Automerge.load(decryptedSnapshot);
|
|
1526
|
+
this.logger.debug(`Successfully loaded snapshot, document heads: ${JSON.stringify(automerge_adapter_1.Automerge.getHeads(doc))}`);
|
|
1452
1527
|
// Register the snapshot's automerge hash -> entry ID mapping
|
|
1453
1528
|
const parsed = (0, idGeneration_1.parseDocEntryId)(snapshotData.id);
|
|
1454
1529
|
if (parsed) {
|
|
@@ -1460,8 +1535,8 @@ class BaseMindooDB {
|
|
|
1460
1535
|
// If we don't have a snapshot, start from scratch
|
|
1461
1536
|
if (!doc) {
|
|
1462
1537
|
this.logger.debug(`Initializing new Automerge document for ${docId}`);
|
|
1463
|
-
doc = Automerge.init();
|
|
1464
|
-
this.logger.debug(`Initialized empty document, heads: ${JSON.stringify(Automerge.getHeads(doc))}`);
|
|
1538
|
+
doc = automerge_adapter_1.Automerge.init();
|
|
1539
|
+
this.logger.debug(`Initialized empty document, heads: ${JSON.stringify(automerge_adapter_1.Automerge.getHeads(doc))}`);
|
|
1465
1540
|
}
|
|
1466
1541
|
// Sort entries by timestamp
|
|
1467
1542
|
entriesToLoad.sort((a, b) => a.createdAt - b.createdAt);
|
|
@@ -1472,7 +1547,7 @@ class BaseMindooDB {
|
|
|
1472
1547
|
this.logger.debug(`Retrieved ${entries.length} entries from store for document ${docId}`);
|
|
1473
1548
|
this.logger.debug(`Loading document ${docId}: found ${entries.length} entries to apply (${startFromSnapshot ? 'starting from snapshot' : 'starting from scratch'})`);
|
|
1474
1549
|
// Log current document state before applying entries
|
|
1475
|
-
this.logger.debug(`Document state before applying entries: heads=${JSON.stringify(Automerge.getHeads(doc))}`);
|
|
1550
|
+
this.logger.debug(`Document state before applying entries: heads=${JSON.stringify(automerge_adapter_1.Automerge.getHeads(doc))}`);
|
|
1476
1551
|
// Filter entries for admin-only mode first
|
|
1477
1552
|
const validEntries = entries.filter(entryData => {
|
|
1478
1553
|
if (this._isAdminOnlyDb && entryData.createdByPublicKey !== this.getAdminPublicKey()) {
|
|
@@ -1551,10 +1626,10 @@ class BaseMindooDB {
|
|
|
1551
1626
|
if (changeBytes.length > 0) {
|
|
1552
1627
|
this.logger.debug(`Applying ${changeBytes.length} changes to document ${docId} using batch applyChanges`);
|
|
1553
1628
|
try {
|
|
1554
|
-
const result = Automerge.applyChanges(doc, changeBytes);
|
|
1629
|
+
const result = automerge_adapter_1.Automerge.applyChanges(doc, changeBytes);
|
|
1555
1630
|
doc = result[0];
|
|
1556
1631
|
this.logger.debug(`Successfully applied ${changeBytes.length} changes to document ${docId}`);
|
|
1557
|
-
this.logger.debug(`Document state after applying changes: heads=${JSON.stringify(Automerge.getHeads(doc))}`);
|
|
1632
|
+
this.logger.debug(`Document state after applying changes: heads=${JSON.stringify(automerge_adapter_1.Automerge.getHeads(doc))}`);
|
|
1558
1633
|
}
|
|
1559
1634
|
catch (error) {
|
|
1560
1635
|
this.logger.error(`Error applying changes to document ${docId}:`, error);
|
|
@@ -1566,7 +1641,7 @@ class BaseMindooDB {
|
|
|
1566
1641
|
}
|
|
1567
1642
|
// Extract metadata from document (doc is guaranteed to be defined at this point)
|
|
1568
1643
|
this.logger.debug(`All entries applied successfully for document ${docId}`);
|
|
1569
|
-
this.logger.debug(`Final document heads: ${JSON.stringify(Automerge.getHeads(doc))}`);
|
|
1644
|
+
this.logger.debug(`Final document heads: ${JSON.stringify(automerge_adapter_1.Automerge.getHeads(doc))}`);
|
|
1570
1645
|
const payload = doc;
|
|
1571
1646
|
// Check if document was deleted by looking for a "doc_delete" type entry
|
|
1572
1647
|
const hasDeleteEntry = allEntryMetadata.some(em => em.entryType === "doc_delete");
|
|
@@ -1594,6 +1669,50 @@ class BaseMindooDB {
|
|
|
1594
1669
|
this.logger.debug(`===== Successfully loaded document ${docId} and cached it =====`);
|
|
1595
1670
|
return internalDoc;
|
|
1596
1671
|
}
|
|
1672
|
+
/**
|
|
1673
|
+
* Convert an Automerge document to a plain JS object, converting Text objects to strings.
|
|
1674
|
+
* If using native Automerge (react-native-automerge-generated), this uses the native
|
|
1675
|
+
* materialize() method which properly converts Text objects to strings.
|
|
1676
|
+
* Falls back to direct access if native backend is not available.
|
|
1677
|
+
*/
|
|
1678
|
+
convertAutomergeToJS(doc) {
|
|
1679
|
+
// Check if this document has a native Automerge handle attached
|
|
1680
|
+
// The native implementation attaches metadata with Symbol.for('_am_meta')
|
|
1681
|
+
const STATE = Symbol.for('_am_meta');
|
|
1682
|
+
const meta = doc[STATE];
|
|
1683
|
+
if (meta && meta.handle && typeof meta.handle.materialize === 'function') {
|
|
1684
|
+
// Use native materialize() which properly converts Text objects to strings
|
|
1685
|
+
try {
|
|
1686
|
+
const materialized = meta.handle.materialize('/');
|
|
1687
|
+
return materialized;
|
|
1688
|
+
}
|
|
1689
|
+
catch (error) {
|
|
1690
|
+
console.error('[MindooDB] Failed to materialize document:', error);
|
|
1691
|
+
// Fall through to direct access
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
// Fallback: direct access (for WASM or if native fails)
|
|
1695
|
+
const result = {};
|
|
1696
|
+
const keys = Object.keys(doc);
|
|
1697
|
+
for (const key of keys) {
|
|
1698
|
+
const value = doc[key];
|
|
1699
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Uint8Array)) {
|
|
1700
|
+
result[key] = this.convertAutomergeToJS(value);
|
|
1701
|
+
}
|
|
1702
|
+
else if (Array.isArray(value)) {
|
|
1703
|
+
result[key] = value.map(item => {
|
|
1704
|
+
if (item !== null && typeof item === 'object' && !Array.isArray(item) && !(item instanceof Uint8Array)) {
|
|
1705
|
+
return this.convertAutomergeToJS(item);
|
|
1706
|
+
}
|
|
1707
|
+
return item;
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1710
|
+
else {
|
|
1711
|
+
result[key] = value;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
return result;
|
|
1715
|
+
}
|
|
1597
1716
|
/**
|
|
1598
1717
|
* Wrap an internal document in the MindooDoc interface.
|
|
1599
1718
|
* This is the read-only wrapper - write methods throw errors.
|
|
@@ -1626,7 +1745,11 @@ class BaseMindooDB {
|
|
|
1626
1745
|
getCreatedAt: () => internalDoc.createdAt,
|
|
1627
1746
|
getLastModified: () => internalDoc.lastModified,
|
|
1628
1747
|
isDeleted: () => internalDoc.isDeleted,
|
|
1629
|
-
getData: () =>
|
|
1748
|
+
getData: () => {
|
|
1749
|
+
// Convert Automerge document to plain JS object, converting Text objects to strings
|
|
1750
|
+
const jsDoc = this.convertAutomergeToJS(internalDoc.doc);
|
|
1751
|
+
return createReadOnlyProxy(jsDoc);
|
|
1752
|
+
},
|
|
1630
1753
|
// ========== Attachment Write Methods ==========
|
|
1631
1754
|
// These throw errors in the read-only wrapper
|
|
1632
1755
|
addAttachment: async () => {
|
|
@@ -2019,27 +2142,16 @@ class BaseMindooDB {
|
|
|
2019
2142
|
throw new Error(`[BaseMindooDB] Cannot pull entries from the incompatible store ${this.store.getId()}`);
|
|
2020
2143
|
}
|
|
2021
2144
|
this.logger.info(`Pulling entries from remote store ${remoteStore.getId()}`);
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
// Find entries in the remote store that we don't have
|
|
2026
|
-
const newEntryMetadata = await remoteStore.findNewEntries(localEntryIds);
|
|
2027
|
-
this.logger.debug(`Found ${newEntryMetadata.length} new entries in remote store`);
|
|
2028
|
-
if (newEntryMetadata.length === 0) {
|
|
2145
|
+
const transferred = await this.syncEntriesFromStore(remoteStore, this.store);
|
|
2146
|
+
this.logger.debug(`Transferred ${transferred} entries from remote store`);
|
|
2147
|
+
if (transferred === 0) {
|
|
2029
2148
|
this.logger.debug(`No new entries to pull`);
|
|
2030
2149
|
return;
|
|
2031
2150
|
}
|
|
2032
|
-
// Get the full entries from the remote store
|
|
2033
|
-
const newEntries = await remoteStore.getEntries(newEntryMetadata.map(em => em.id));
|
|
2034
|
-
this.logger.debug(`Retrieved ${newEntries.length} entries from remote store`);
|
|
2035
|
-
// Store all entries in our local store
|
|
2036
|
-
// The putEntries method handles deduplication (no-op if entry already exists)
|
|
2037
|
-
await this.store.putEntries(newEntries);
|
|
2038
|
-
this.logger.debug(`Stored ${newEntries.length} entries in local store`);
|
|
2039
2151
|
// Sync the local store to process the new entries
|
|
2040
2152
|
// This will update the index, cache, and processedEntryIds
|
|
2041
2153
|
await this.syncStoreChanges();
|
|
2042
|
-
this.logger.info(`Pull complete, synced ${
|
|
2154
|
+
this.logger.info(`Pull complete, synced ${transferred} entries`);
|
|
2043
2155
|
}
|
|
2044
2156
|
/**
|
|
2045
2157
|
* Push changes to a remote content-addressed store.
|
|
@@ -2057,23 +2169,13 @@ class BaseMindooDB {
|
|
|
2057
2169
|
throw new Error(`[BaseMindooDB] Cannot push entries to the incompatible store ${this.store.getId()}`);
|
|
2058
2170
|
}
|
|
2059
2171
|
this.logger.info(`Pushing entries to remote store ${remoteStore.getId()}`);
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
// Find entries in our local store that the remote doesn't have
|
|
2064
|
-
const newEntryMetadata = await this.store.findNewEntries(remoteEntryIds);
|
|
2065
|
-
this.logger.debug(`Found ${newEntryMetadata.length} new entries to push`);
|
|
2066
|
-
if (newEntryMetadata.length === 0) {
|
|
2172
|
+
const transferred = await this.syncEntriesFromStore(this.store, remoteStore);
|
|
2173
|
+
this.logger.debug(`Transferred ${transferred} entries to remote store`);
|
|
2174
|
+
if (transferred === 0) {
|
|
2067
2175
|
this.logger.debug(`No new entries to push`);
|
|
2068
2176
|
return;
|
|
2069
2177
|
}
|
|
2070
|
-
|
|
2071
|
-
const newEntries = await this.store.getEntries(newEntryMetadata.map(em => em.id));
|
|
2072
|
-
this.logger.debug(`Retrieved ${newEntries.length} entries from local store`);
|
|
2073
|
-
// Store all entries in the remote store
|
|
2074
|
-
// The putEntries method handles deduplication (no-op if entry already exists)
|
|
2075
|
-
await remoteStore.putEntries(newEntries);
|
|
2076
|
-
this.logger.info(`Pushed ${newEntries.length} entries to remote store`);
|
|
2178
|
+
this.logger.info(`Pushed ${transferred} entries to remote store`);
|
|
2077
2179
|
}
|
|
2078
2180
|
}
|
|
2079
2181
|
exports.BaseMindooDB = BaseMindooDB;
|