mindoodb 0.0.9 → 0.0.11
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/dist/appendonlystores/InMemoryContentAddressedStoreFactory.d.ts.map +1 -1
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.js +3 -5
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.js.map +1 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts +7 -2
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js +23 -3
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js.map +1 -1
- package/dist/appendonlystores/network/HttpTransport.d.ts +5 -0
- package/dist/appendonlystores/network/HttpTransport.d.ts.map +1 -1
- package/dist/appendonlystores/network/HttpTransport.js +66 -36
- package/dist/appendonlystores/network/HttpTransport.js.map +1 -1
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts +4 -1
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts.map +1 -1
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js +15 -3
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js.map +1 -1
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.d.ts.map +1 -1
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.js +7 -3
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.js.map +1 -1
- package/dist/browser/index.mjs +1 -0
- package/dist/core/BaseMindooDB.d.ts +44 -5
- package/dist/core/BaseMindooDB.d.ts.map +1 -1
- package/dist/core/BaseMindooDB.js +244 -53
- package/dist/core/BaseMindooDB.js.map +1 -1
- package/dist/core/BaseMindooTenant.d.ts +2 -3
- package/dist/core/BaseMindooTenant.d.ts.map +1 -1
- package/dist/core/BaseMindooTenant.js +9 -18
- package/dist/core/BaseMindooTenant.js.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.d.ts +1 -0
- package/dist/core/BaseMindooTenantDirectory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.js +24 -8
- package/dist/core/BaseMindooTenantDirectory.js.map +1 -1
- package/dist/core/appendonlystores/AttachmentReadPlanner.d.ts.map +1 -1
- package/dist/core/appendonlystores/AttachmentReadPlanner.js +1 -1
- package/dist/core/appendonlystores/AttachmentReadPlanner.js.map +1 -1
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts +4 -2
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts.map +1 -1
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.js +13 -8
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.js.map +1 -1
- package/dist/core/appendonlystores/network/NetworkTransport.d.ts +5 -1
- package/dist/core/appendonlystores/network/NetworkTransport.d.ts.map +1 -1
- package/dist/core/appendonlystores/types.d.ts +14 -10
- package/dist/core/appendonlystores/types.d.ts.map +1 -1
- package/dist/core/appendonlystores/types.js +6 -0
- package/dist/core/appendonlystores/types.js.map +1 -1
- package/dist/core/types.d.ts +24 -12
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +3 -1
- package/dist/core/types.js.map +1 -1
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts +5 -2
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -1
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js +10 -4
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -1
- package/dist/node/server/MindooDBServer.d.ts +105 -0
- package/dist/node/server/MindooDBServer.d.ts.map +1 -1
- package/dist/node/server/MindooDBServer.js +240 -66
- package/dist/node/server/MindooDBServer.js.map +1 -1
- package/dist/node/server/ServerSync.d.ts +1 -1
- package/dist/node/server/ServerSync.d.ts.map +1 -1
- package/dist/node/server/ServerSync.js +2 -1
- package/dist/node/server/ServerSync.js.map +1 -1
- package/dist/node/server/StoreFactory.d.ts +3 -3
- package/dist/node/server/StoreFactory.d.ts.map +1 -1
- package/dist/node/server/StoreFactory.js +8 -8
- package/dist/node/server/StoreFactory.js.map +1 -1
- package/dist/node/server/TenantManager.d.ts +5 -3
- package/dist/node/server/TenantManager.d.ts.map +1 -1
- package/dist/node/server/TenantManager.js +33 -20
- package/dist/node/server/TenantManager.js.map +1 -1
- package/dist/node/server/config.d.ts.map +1 -1
- package/dist/node/server/config.js +4 -0
- package/dist/node/server/config.js.map +1 -1
- package/dist/node/server/server.js +6 -5
- package/dist/node/server/server.js.map +1 -1
- package/dist/node/server/types.d.ts +1 -0
- package/dist/node/server/types.d.ts.map +1 -1
- package/dist/node/server/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ exports.BaseMindooDB = void 0;
|
|
|
6
6
|
// Browser/Node.js: WASM (@automerge/automerge/slim)
|
|
7
7
|
const automerge_adapter_1 = require("./automerge-adapter");
|
|
8
8
|
const uuid_1 = require("uuid");
|
|
9
|
+
const types_1 = require("./appendonlystores/types");
|
|
9
10
|
const bloom_1 = require("./appendonlystores/bloom");
|
|
10
11
|
const MaterializationPlanner_1 = require("./appendonlystores/MaterializationPlanner");
|
|
11
12
|
const DocumentDagAnalysis_1 = require("./DocumentDagAnalysis");
|
|
@@ -17,6 +18,7 @@ const logging_1 = require("./logging");
|
|
|
17
18
|
* Default chunk size for attachments: 256KB
|
|
18
19
|
*/
|
|
19
20
|
const DEFAULT_CHUNK_SIZE_BYTES = 256 * 1024;
|
|
21
|
+
const DEFAULT_ATTACHMENT_STREAM_BATCH_SIZE = 4;
|
|
20
22
|
const DEFAULT_SNAPSHOT_MIN_CHANGES = 100;
|
|
21
23
|
const DEFAULT_SNAPSHOT_COOLDOWN_MS = 10 * 60 * 1000;
|
|
22
24
|
const DEFAULT_MAX_CACHED_DOCS = 128;
|
|
@@ -64,6 +66,12 @@ class BaseMindooDB {
|
|
|
64
66
|
this.cachePrefix = null;
|
|
65
67
|
this.dirtyDocIds = new Set();
|
|
66
68
|
this.cacheMetaDirty = false;
|
|
69
|
+
if (store.getStoreKind() !== types_1.StoreKind.docs) {
|
|
70
|
+
throw new Error(`[BaseMindooDB] Expected primary store kind ${types_1.StoreKind.docs} but received ${store.getStoreKind()}`);
|
|
71
|
+
}
|
|
72
|
+
if (attachmentStore.getStoreKind() !== types_1.StoreKind.attachments) {
|
|
73
|
+
throw new Error(`[BaseMindooDB] Expected attachment store kind ${types_1.StoreKind.attachments} but received ${attachmentStore.getStoreKind()}`);
|
|
74
|
+
}
|
|
67
75
|
this.tenant = tenant;
|
|
68
76
|
this.store = store;
|
|
69
77
|
this.attachmentStore = attachmentStore;
|
|
@@ -738,6 +746,11 @@ class BaseMindooDB {
|
|
|
738
746
|
return null;
|
|
739
747
|
}
|
|
740
748
|
}
|
|
749
|
+
static setSyncAbortSignalOnStore(store, signal) {
|
|
750
|
+
if ('setSyncAbortSignal' in store && typeof store.setSyncAbortSignal === 'function') {
|
|
751
|
+
store.setSyncAbortSignal(signal);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
741
754
|
/**
|
|
742
755
|
* From a list of candidate IDs, return only those the target store is
|
|
743
756
|
* missing. Uses bloom-filter pre-screening when available, then falls
|
|
@@ -766,14 +779,126 @@ class BaseMindooDB {
|
|
|
766
779
|
}
|
|
767
780
|
return missingIds;
|
|
768
781
|
}
|
|
782
|
+
/**
|
|
783
|
+
* Determine how many entry IDs to fetch per `getEntries` call during sync.
|
|
784
|
+
*
|
|
785
|
+
* This is intentionally separate from the metadata scan `pageSize` so that
|
|
786
|
+
* scanning can page through large ID lists quickly while the heavier
|
|
787
|
+
* payload downloads use a smaller batch to keep progress responsive and
|
|
788
|
+
* cancellation timely.
|
|
789
|
+
*
|
|
790
|
+
* Priority: explicit option > attachment default (100) > pageSize fallback.
|
|
791
|
+
*/
|
|
792
|
+
resolveTransferBatchSize(options) {
|
|
793
|
+
if (options?.transferBatchSize && options.transferBatchSize > 0) {
|
|
794
|
+
return options.transferBatchSize;
|
|
795
|
+
}
|
|
796
|
+
if (options?.storeKind === types_1.StoreKind.attachments) {
|
|
797
|
+
return 100;
|
|
798
|
+
}
|
|
799
|
+
return options?.pageSize ?? 1000;
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Transfer a set of entry IDs from source to target in fixed-size batches,
|
|
803
|
+
* emitting progress and checking for cancellation between each batch.
|
|
804
|
+
*
|
|
805
|
+
* Callers (cursor-scan path and legacy path) collect the IDs that need
|
|
806
|
+
* transferring, then delegate to this method instead of issuing one
|
|
807
|
+
* monolithic `getEntries`. This gives three benefits:
|
|
808
|
+
*
|
|
809
|
+
* 1. The UI receives frequent progress updates with batch metadata so long
|
|
810
|
+
* transfers no longer look frozen.
|
|
811
|
+
* 2. Cancellation can interrupt work between batches rather than waiting
|
|
812
|
+
* for one large HTTP response to complete.
|
|
813
|
+
* 3. Each server-side `getEntries` + RSA encryption unit is smaller,
|
|
814
|
+
* reducing the risk of socket timeouts on large payloads.
|
|
815
|
+
*
|
|
816
|
+
* Returns partial progress on cancellation so callers can report how much
|
|
817
|
+
* was actually transferred before the abort.
|
|
818
|
+
*/
|
|
819
|
+
async transferEntriesInBatches(sourceStore, targetStore, entryIds, options, state) {
|
|
820
|
+
if (entryIds.length === 0) {
|
|
821
|
+
return { transferred: state.transferred, cancelled: false };
|
|
822
|
+
}
|
|
823
|
+
const onProgress = options?.onProgress;
|
|
824
|
+
const signal = options?.signal;
|
|
825
|
+
const transferBatchSize = this.resolveTransferBatchSize(options);
|
|
826
|
+
const totalTransferBatches = Math.max(1, Math.ceil(entryIds.length / transferBatchSize));
|
|
827
|
+
let transferred = state.transferred;
|
|
828
|
+
for (let offset = 0; offset < entryIds.length; offset += transferBatchSize) {
|
|
829
|
+
if (signal?.aborted) {
|
|
830
|
+
return { transferred, cancelled: true };
|
|
831
|
+
}
|
|
832
|
+
const currentTransferBatch = Math.floor(offset / transferBatchSize) + 1;
|
|
833
|
+
const batchIds = entryIds.slice(offset, offset + transferBatchSize);
|
|
834
|
+
const pageSummary = state.currentPage ? `page ${state.currentPage}, ` : "";
|
|
835
|
+
onProgress?.({
|
|
836
|
+
phase: "transferring",
|
|
837
|
+
message: `Transferring batch ${currentTransferBatch}/${totalTransferBatches} (${batchIds.length} entries, ${pageSummary}scanned ${state.scanned})...`,
|
|
838
|
+
transferredEntries: transferred,
|
|
839
|
+
scannedEntries: state.scanned,
|
|
840
|
+
totalSourceEntries: state.totalSourceEntries,
|
|
841
|
+
currentPage: state.currentPage,
|
|
842
|
+
currentTransferBatch,
|
|
843
|
+
totalTransferBatches,
|
|
844
|
+
transferBatchSize,
|
|
845
|
+
});
|
|
846
|
+
try {
|
|
847
|
+
const batchEntries = await sourceStore.getEntries(batchIds);
|
|
848
|
+
if (signal?.aborted) {
|
|
849
|
+
return { transferred, cancelled: true };
|
|
850
|
+
}
|
|
851
|
+
await targetStore.putEntries(batchEntries);
|
|
852
|
+
transferred += batchEntries.length;
|
|
853
|
+
}
|
|
854
|
+
catch (error) {
|
|
855
|
+
if (signal?.aborted) {
|
|
856
|
+
return { transferred, cancelled: true };
|
|
857
|
+
}
|
|
858
|
+
throw error;
|
|
859
|
+
}
|
|
860
|
+
onProgress?.({
|
|
861
|
+
phase: "transferring",
|
|
862
|
+
message: `Transferred ${transferred} entries after batch ${currentTransferBatch}/${totalTransferBatches}`,
|
|
863
|
+
transferredEntries: transferred,
|
|
864
|
+
scannedEntries: state.scanned,
|
|
865
|
+
totalSourceEntries: state.totalSourceEntries,
|
|
866
|
+
currentPage: state.currentPage,
|
|
867
|
+
currentTransferBatch,
|
|
868
|
+
totalTransferBatches,
|
|
869
|
+
transferBatchSize,
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
return { transferred, cancelled: false };
|
|
873
|
+
}
|
|
769
874
|
async syncEntriesFromStore(sourceStore, targetStore, options) {
|
|
770
|
-
|
|
771
|
-
|
|
875
|
+
const signal = options?.signal;
|
|
876
|
+
BaseMindooDB.setSyncAbortSignalOnStore(sourceStore, signal);
|
|
877
|
+
BaseMindooDB.setSyncAbortSignalOnStore(targetStore, signal);
|
|
878
|
+
try {
|
|
879
|
+
if (options?.mode === "dense") {
|
|
880
|
+
return await this.syncEntriesFromStoreDense(sourceStore, targetStore, options);
|
|
881
|
+
}
|
|
882
|
+
return await this.syncEntriesFromStoreImpl(sourceStore, targetStore, options);
|
|
883
|
+
}
|
|
884
|
+
catch (error) {
|
|
885
|
+
if (signal?.aborted) {
|
|
886
|
+
this.logger.info("Sync cancelled by abort signal");
|
|
887
|
+
return { transferred: 0, scanned: 0, cancelled: true };
|
|
888
|
+
}
|
|
889
|
+
throw error;
|
|
890
|
+
}
|
|
891
|
+
finally {
|
|
892
|
+
BaseMindooDB.setSyncAbortSignalOnStore(sourceStore, undefined);
|
|
893
|
+
BaseMindooDB.setSyncAbortSignalOnStore(targetStore, undefined);
|
|
772
894
|
}
|
|
895
|
+
}
|
|
896
|
+
async syncEntriesFromStoreImpl(sourceStore, targetStore, options) {
|
|
773
897
|
let transferred = 0;
|
|
774
898
|
let scanned = 0;
|
|
775
899
|
const onProgress = options?.onProgress;
|
|
776
900
|
const pageSize = options?.pageSize ?? 1000;
|
|
901
|
+
const signal = options?.signal;
|
|
777
902
|
const targetBloom = await this.getTargetBloomSummary(targetStore);
|
|
778
903
|
const totalSourceEstimate = targetBloom?.totalIds;
|
|
779
904
|
if (this.supportsCursorScan(sourceStore)) {
|
|
@@ -787,19 +912,40 @@ class BaseMindooDB {
|
|
|
787
912
|
let cursor = null;
|
|
788
913
|
let currentPage = 0;
|
|
789
914
|
while (true) {
|
|
790
|
-
if (
|
|
915
|
+
if (signal?.aborted) {
|
|
791
916
|
return { transferred, scanned, cancelled: true };
|
|
792
917
|
}
|
|
793
918
|
const page = await sourceStore.scanEntriesSince(cursor, pageSize);
|
|
794
919
|
currentPage++;
|
|
795
920
|
scanned += page.entries.length;
|
|
921
|
+
if (signal?.aborted) {
|
|
922
|
+
return { transferred, scanned, cancelled: true };
|
|
923
|
+
}
|
|
796
924
|
if (page.entries.length > 0) {
|
|
925
|
+
onProgress?.({
|
|
926
|
+
phase: 'transferring',
|
|
927
|
+
message: `Scanned ${scanned} entries, checking for changes (page ${currentPage})...`,
|
|
928
|
+
transferredEntries: transferred,
|
|
929
|
+
scannedEntries: scanned,
|
|
930
|
+
totalSourceEntries: totalSourceEstimate,
|
|
931
|
+
currentPage,
|
|
932
|
+
});
|
|
797
933
|
const ids = page.entries.map((m) => m.id);
|
|
798
934
|
const missingIds = await this.filterMissingIds(targetStore, ids, targetBloom);
|
|
935
|
+
if (signal?.aborted) {
|
|
936
|
+
return { transferred, scanned, cancelled: true };
|
|
937
|
+
}
|
|
799
938
|
if (missingIds.length > 0) {
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
|
|
939
|
+
const transferResult = await this.transferEntriesInBatches(sourceStore, targetStore, missingIds, options, {
|
|
940
|
+
transferred,
|
|
941
|
+
scanned,
|
|
942
|
+
totalSourceEntries: totalSourceEstimate,
|
|
943
|
+
currentPage,
|
|
944
|
+
});
|
|
945
|
+
transferred = transferResult.transferred;
|
|
946
|
+
if (transferResult.cancelled) {
|
|
947
|
+
return { transferred, scanned, cancelled: true };
|
|
948
|
+
}
|
|
803
949
|
}
|
|
804
950
|
}
|
|
805
951
|
onProgress?.({
|
|
@@ -834,21 +980,28 @@ class BaseMindooDB {
|
|
|
834
980
|
onProgress?.({
|
|
835
981
|
phase: 'transferring',
|
|
836
982
|
message: `Transferring ${sourceNewMetadata.length} entries...`,
|
|
837
|
-
transferredEntries:
|
|
983
|
+
transferredEntries: transferred,
|
|
838
984
|
scannedEntries: sourceNewMetadata.length,
|
|
839
985
|
totalSourceEntries: sourceNewMetadata.length,
|
|
840
986
|
});
|
|
841
|
-
const
|
|
842
|
-
|
|
987
|
+
const transferResult = await this.transferEntriesInBatches(sourceStore, targetStore, sourceNewMetadata.map((m) => m.id), options, {
|
|
988
|
+
transferred,
|
|
989
|
+
scanned: sourceNewMetadata.length,
|
|
990
|
+
totalSourceEntries: sourceNewMetadata.length,
|
|
991
|
+
});
|
|
992
|
+
transferred = transferResult.transferred;
|
|
993
|
+
if (transferResult.cancelled) {
|
|
994
|
+
return { transferred, scanned: sourceNewMetadata.length, cancelled: true };
|
|
995
|
+
}
|
|
843
996
|
onProgress?.({
|
|
844
997
|
phase: 'transferring',
|
|
845
|
-
message: `Transferred ${
|
|
846
|
-
transferredEntries:
|
|
998
|
+
message: `Transferred ${transferred} entries`,
|
|
999
|
+
transferredEntries: transferred,
|
|
847
1000
|
scannedEntries: sourceNewMetadata.length,
|
|
848
1001
|
totalSourceEntries: sourceNewMetadata.length,
|
|
849
1002
|
});
|
|
850
1003
|
return {
|
|
851
|
-
transferred
|
|
1004
|
+
transferred,
|
|
852
1005
|
scanned: sourceNewMetadata.length,
|
|
853
1006
|
cancelled: false,
|
|
854
1007
|
};
|
|
@@ -3142,10 +3295,10 @@ class BaseMindooDB {
|
|
|
3142
3295
|
/**
|
|
3143
3296
|
* Get an attachment reference by ID from a document's _attachments array.
|
|
3144
3297
|
*/
|
|
3145
|
-
getAttachmentRefInternal(docId, attachmentId) {
|
|
3146
|
-
const internalDoc = this.getCachedDocument(docId);
|
|
3298
|
+
async getAttachmentRefInternal(docId, attachmentId) {
|
|
3299
|
+
const internalDoc = this.getCachedDocument(docId) ?? await this.loadDocumentInternal(docId);
|
|
3147
3300
|
if (!internalDoc) {
|
|
3148
|
-
throw new Error(`Document ${docId} not found
|
|
3301
|
+
throw new Error(`Document ${docId} not found while reading attachment ${attachmentId}`);
|
|
3149
3302
|
}
|
|
3150
3303
|
const payload = internalDoc.doc;
|
|
3151
3304
|
const attachments = payload._attachments || [];
|
|
@@ -3193,7 +3346,7 @@ class BaseMindooDB {
|
|
|
3193
3346
|
*/
|
|
3194
3347
|
async getAttachmentInternal(docId, attachmentId) {
|
|
3195
3348
|
this.logger.debug(`Getting attachment ${attachmentId} from document ${docId}`);
|
|
3196
|
-
const ref = this.getAttachmentRefInternal(docId, attachmentId);
|
|
3349
|
+
const ref = await this.getAttachmentRefInternal(docId, attachmentId);
|
|
3197
3350
|
const store = this.getEffectiveAttachmentStore();
|
|
3198
3351
|
// Resolve dependency chain to get all chunk IDs in order (oldest first)
|
|
3199
3352
|
const chunkIds = await store.resolveDependencies(ref.lastChunkId, { includeStart: true });
|
|
@@ -3226,7 +3379,7 @@ class BaseMindooDB {
|
|
|
3226
3379
|
if (startByte < 0 || endByte <= startByte) {
|
|
3227
3380
|
throw new Error(`Invalid byte range: [${startByte}, ${endByte})`);
|
|
3228
3381
|
}
|
|
3229
|
-
const ref = this.getAttachmentRefInternal(docId, attachmentId);
|
|
3382
|
+
const ref = await this.getAttachmentRefInternal(docId, attachmentId);
|
|
3230
3383
|
const store = this.getEffectiveAttachmentStore();
|
|
3231
3384
|
const readPlan = await this.planAttachmentRead(store, ref, startByte, endByte);
|
|
3232
3385
|
const neededChunkIds = readPlan.chunkPlans.map((chunkPlan) => chunkPlan.id);
|
|
@@ -3258,7 +3411,7 @@ class BaseMindooDB {
|
|
|
3258
3411
|
*/
|
|
3259
3412
|
async *streamAttachmentInternal(docId, attachmentId, startOffset) {
|
|
3260
3413
|
this.logger.debug(`Streaming attachment ${attachmentId} from offset ${startOffset}`);
|
|
3261
|
-
const ref = this.getAttachmentRefInternal(docId, attachmentId);
|
|
3414
|
+
const ref = await this.getAttachmentRefInternal(docId, attachmentId);
|
|
3262
3415
|
if (startOffset < 0 || startOffset > ref.size) {
|
|
3263
3416
|
throw new Error(`Invalid stream offset ${startOffset} for attachment size ${ref.size}`);
|
|
3264
3417
|
}
|
|
@@ -3267,29 +3420,34 @@ class BaseMindooDB {
|
|
|
3267
3420
|
}
|
|
3268
3421
|
const store = this.getEffectiveAttachmentStore();
|
|
3269
3422
|
const readPlan = await this.planAttachmentRead(store, ref, startOffset, ref.size);
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
const
|
|
3273
|
-
const
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3423
|
+
for (let i = 0; i < readPlan.chunkPlans.length; i += DEFAULT_ATTACHMENT_STREAM_BATCH_SIZE) {
|
|
3424
|
+
const chunkPlansBatch = readPlan.chunkPlans.slice(i, i + DEFAULT_ATTACHMENT_STREAM_BATCH_SIZE);
|
|
3425
|
+
const batchIds = chunkPlansBatch.map((chunkPlan) => chunkPlan.id);
|
|
3426
|
+
const chunks = await store.getEntries(batchIds);
|
|
3427
|
+
const chunkById = new Map(chunks.map((chunk) => [chunk.id, chunk]));
|
|
3428
|
+
for (let batchIndex = 0; batchIndex < chunkPlansBatch.length; batchIndex++) {
|
|
3429
|
+
const chunkPlan = chunkPlansBatch[batchIndex];
|
|
3430
|
+
const chunk = chunkById.get(chunkPlan.id);
|
|
3431
|
+
if (!chunk) {
|
|
3432
|
+
throw new Error(`Attachment chunk ${chunkPlan.id} not found in store. The document metadata exists, but the attachment payload may not be synced locally yet.`);
|
|
3433
|
+
}
|
|
3434
|
+
const plaintext = await this.decryptAttachmentChunk(chunk);
|
|
3435
|
+
// For first chunk, skip bytes before startOffset
|
|
3436
|
+
if (i === 0 && batchIndex === 0 && readPlan.offsetInFirstChunk > 0) {
|
|
3437
|
+
yield plaintext.slice(readPlan.offsetInFirstChunk);
|
|
3438
|
+
}
|
|
3439
|
+
else {
|
|
3440
|
+
yield plaintext;
|
|
3441
|
+
}
|
|
3284
3442
|
}
|
|
3285
3443
|
}
|
|
3286
3444
|
this.logger.debug(`Finished streaming attachment ${attachmentId}`);
|
|
3287
3445
|
}
|
|
3288
3446
|
/**
|
|
3289
|
-
* Get the
|
|
3447
|
+
* Get the attachment store for this database.
|
|
3290
3448
|
*/
|
|
3291
3449
|
getEffectiveAttachmentStore() {
|
|
3292
|
-
return this.attachmentStore
|
|
3450
|
+
return this.attachmentStore;
|
|
3293
3451
|
}
|
|
3294
3452
|
/**
|
|
3295
3453
|
* Internal method to add an attachment by chunking the file and storing chunks.
|
|
@@ -3484,9 +3642,14 @@ class BaseMindooDB {
|
|
|
3484
3642
|
* Resolve a sync target to a ContentAddressedStore.
|
|
3485
3643
|
* Accepts either a raw store or a MindooDB instance (calls getStore()).
|
|
3486
3644
|
*/
|
|
3487
|
-
|
|
3645
|
+
getStoreForKind(storeKind = types_1.StoreKind.docs) {
|
|
3646
|
+
return storeKind === types_1.StoreKind.attachments ? this.attachmentStore : this.store;
|
|
3647
|
+
}
|
|
3648
|
+
resolveStore(remote, storeKind = types_1.StoreKind.docs) {
|
|
3488
3649
|
if ('getStore' in remote && typeof remote.getStore === 'function') {
|
|
3489
|
-
return
|
|
3650
|
+
return storeKind === types_1.StoreKind.attachments
|
|
3651
|
+
? remote.getAttachmentStore()
|
|
3652
|
+
: remote.getStore();
|
|
3490
3653
|
}
|
|
3491
3654
|
return remote;
|
|
3492
3655
|
}
|
|
@@ -3546,19 +3709,24 @@ class BaseMindooDB {
|
|
|
3546
3709
|
* 3. Stores them in our local store
|
|
3547
3710
|
* 4. Syncs the local store to process the new entries
|
|
3548
3711
|
*
|
|
3712
|
+
* The optional `storeKind` sync option selects which store is synced.
|
|
3713
|
+
* By default, this method syncs the docs store only.
|
|
3714
|
+
*
|
|
3549
3715
|
* @param remote The remote store or MindooDB instance to pull entries from
|
|
3550
|
-
* @param options Optional sync options for progress tracking, paging, and
|
|
3716
|
+
* @param options Optional sync options for progress tracking, paging, cancellation, and store selection
|
|
3551
3717
|
* @return A promise that resolves with the sync result
|
|
3552
3718
|
*/
|
|
3553
3719
|
async pullChangesFrom(remote, options) {
|
|
3554
|
-
const
|
|
3555
|
-
|
|
3556
|
-
|
|
3720
|
+
const storeKind = options?.storeKind ?? types_1.StoreKind.docs;
|
|
3721
|
+
const localStore = this.getStoreForKind(storeKind);
|
|
3722
|
+
const remoteStore = this.resolveStore(remote, storeKind);
|
|
3723
|
+
if (localStore.getId() !== remoteStore.getId() || localStore.getStoreKind() !== remoteStore.getStoreKind()) {
|
|
3724
|
+
throw new Error(`[BaseMindooDB] Cannot pull entries from the incompatible store ${localStore.getId()}/${localStore.getStoreKind()}`);
|
|
3557
3725
|
}
|
|
3558
|
-
this.logger.info(`Pulling entries from remote store ${remoteStore.getId()}`);
|
|
3726
|
+
this.logger.info(`Pulling entries from remote store ${remoteStore.getId()}/${remoteStore.getStoreKind()}`);
|
|
3559
3727
|
const restoreAuthOverride = await this.applyNetworkAuthOverrideForSync(remoteStore, options);
|
|
3560
3728
|
try {
|
|
3561
|
-
const syncResult = await this.syncEntriesFromStore(remoteStore,
|
|
3729
|
+
const syncResult = await this.syncEntriesFromStore(remoteStore, localStore, options);
|
|
3562
3730
|
this.logger.debug(`Transferred ${syncResult.transferred} entries from remote store`);
|
|
3563
3731
|
if (syncResult.cancelled) {
|
|
3564
3732
|
this.logger.info(`Pull cancelled after transferring ${syncResult.transferred} entries`);
|
|
@@ -3576,17 +3744,35 @@ class BaseMindooDB {
|
|
|
3576
3744
|
phase: 'processing',
|
|
3577
3745
|
message: `Processing ${syncResult.transferred} new entries...`,
|
|
3578
3746
|
transferredEntries: syncResult.transferred,
|
|
3579
|
-
scannedEntries: syncResult.
|
|
3747
|
+
scannedEntries: syncResult.scanned,
|
|
3580
3748
|
});
|
|
3581
3749
|
// Sync the local store to process the new entries
|
|
3582
3750
|
// This will update the index, cache, and processedEntryIds
|
|
3583
|
-
|
|
3751
|
+
if (options?.signal?.aborted) {
|
|
3752
|
+
this.logger.info(`Pull cancelled before local processing after transferring ${syncResult.transferred} entries`);
|
|
3753
|
+
return {
|
|
3754
|
+
transferredEntries: syncResult.transferred,
|
|
3755
|
+
scannedEntries: syncResult.scanned,
|
|
3756
|
+
cancelled: true,
|
|
3757
|
+
};
|
|
3758
|
+
}
|
|
3759
|
+
if (storeKind === types_1.StoreKind.docs) {
|
|
3760
|
+
await this.syncStoreChanges();
|
|
3761
|
+
}
|
|
3762
|
+
if (options?.signal?.aborted) {
|
|
3763
|
+
this.logger.info(`Pull cancelled after local processing for ${storeKind} entries`);
|
|
3764
|
+
return {
|
|
3765
|
+
transferredEntries: syncResult.transferred,
|
|
3766
|
+
scannedEntries: syncResult.scanned,
|
|
3767
|
+
cancelled: true,
|
|
3768
|
+
};
|
|
3769
|
+
}
|
|
3584
3770
|
this.logger.info(`Pull complete, synced ${syncResult.transferred} entries`);
|
|
3585
3771
|
options?.onProgress?.({
|
|
3586
3772
|
phase: 'complete',
|
|
3587
|
-
message: `Pull complete: ${syncResult.transferred} entries synced`,
|
|
3773
|
+
message: `Pull complete: ${syncResult.transferred} ${storeKind} entries synced`,
|
|
3588
3774
|
transferredEntries: syncResult.transferred,
|
|
3589
|
-
scannedEntries: syncResult.
|
|
3775
|
+
scannedEntries: syncResult.scanned,
|
|
3590
3776
|
});
|
|
3591
3777
|
return {
|
|
3592
3778
|
transferredEntries: syncResult.transferred,
|
|
@@ -3606,19 +3792,24 @@ class BaseMindooDB {
|
|
|
3606
3792
|
* 2. Retrieves those entries from our local store
|
|
3607
3793
|
* 3. Stores them in the remote store
|
|
3608
3794
|
*
|
|
3795
|
+
* The optional `storeKind` sync option selects which store is synced.
|
|
3796
|
+
* By default, this method syncs the docs store only.
|
|
3797
|
+
*
|
|
3609
3798
|
* @param remote The remote store or MindooDB instance to push entries to
|
|
3610
|
-
* @param options Optional sync options for progress tracking, paging, and
|
|
3799
|
+
* @param options Optional sync options for progress tracking, paging, cancellation, and store selection
|
|
3611
3800
|
* @return A promise that resolves with the sync result
|
|
3612
3801
|
*/
|
|
3613
3802
|
async pushChangesTo(remote, options) {
|
|
3614
|
-
const
|
|
3615
|
-
|
|
3616
|
-
|
|
3803
|
+
const storeKind = options?.storeKind ?? types_1.StoreKind.docs;
|
|
3804
|
+
const localStore = this.getStoreForKind(storeKind);
|
|
3805
|
+
const remoteStore = this.resolveStore(remote, storeKind);
|
|
3806
|
+
if (localStore.getId() !== remoteStore.getId() || localStore.getStoreKind() !== remoteStore.getStoreKind()) {
|
|
3807
|
+
throw new Error(`[BaseMindooDB] Cannot push entries to the incompatible store ${localStore.getId()}/${localStore.getStoreKind()}`);
|
|
3617
3808
|
}
|
|
3618
|
-
this.logger.info(`Pushing entries to remote store ${remoteStore.getId()}`);
|
|
3809
|
+
this.logger.info(`Pushing entries to remote store ${remoteStore.getId()}/${remoteStore.getStoreKind()}`);
|
|
3619
3810
|
const restoreAuthOverride = await this.applyNetworkAuthOverrideForSync(remoteStore, options);
|
|
3620
3811
|
try {
|
|
3621
|
-
const syncResult = await this.syncEntriesFromStore(
|
|
3812
|
+
const syncResult = await this.syncEntriesFromStore(localStore, remoteStore, options);
|
|
3622
3813
|
this.logger.debug(`Transferred ${syncResult.transferred} entries to remote store`);
|
|
3623
3814
|
if (syncResult.cancelled) {
|
|
3624
3815
|
this.logger.info(`Push cancelled after transferring ${syncResult.transferred} entries`);
|
|
@@ -3638,7 +3829,7 @@ class BaseMindooDB {
|
|
|
3638
3829
|
phase: 'complete',
|
|
3639
3830
|
message: `Push complete: ${syncResult.transferred} entries transferred`,
|
|
3640
3831
|
transferredEntries: syncResult.transferred,
|
|
3641
|
-
scannedEntries: syncResult.
|
|
3832
|
+
scannedEntries: syncResult.scanned,
|
|
3642
3833
|
});
|
|
3643
3834
|
return {
|
|
3644
3835
|
transferredEntries: syncResult.transferred,
|