mindoodb 0.0.10 → 0.0.12
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/network/ClientNetworkContentAddressedStore.d.ts +1 -0
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js +10 -0
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js.map +1 -1
- package/dist/appendonlystores/network/HttpTransport.d.ts +1 -0
- package/dist/appendonlystores/network/HttpTransport.d.ts.map +1 -1
- package/dist/appendonlystores/network/HttpTransport.js +4 -3
- package/dist/appendonlystores/network/HttpTransport.js.map +1 -1
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts +34 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts.map +1 -1
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js +194 -9
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js.map +1 -1
- package/dist/browser/cache/IndexedDBLocalCacheStore.d.ts +22 -0
- package/dist/browser/cache/IndexedDBLocalCacheStore.d.ts.map +1 -1
- package/dist/browser/cache/IndexedDBLocalCacheStore.js +185 -24
- package/dist/browser/cache/IndexedDBLocalCacheStore.js.map +1 -1
- package/dist/browser/index.d.ts +2 -2
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +3 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +2 -0
- package/dist/core/BaseMindooDB.d.ts +30 -0
- package/dist/core/BaseMindooDB.d.ts.map +1 -1
- package/dist/core/BaseMindooDB.js +155 -38
- package/dist/core/BaseMindooDB.js.map +1 -1
- package/dist/core/BaseMindooTenant.d.ts +0 -1
- package/dist/core/BaseMindooTenant.d.ts.map +1 -1
- package/dist/core/BaseMindooTenant.js +3 -13
- 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/types.d.ts +17 -8
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/node/server/MindooDBServer.d.ts +104 -0
- package/dist/node/server/MindooDBServer.d.ts.map +1 -1
- package/dist/node/server/MindooDBServer.js +192 -32
- package/dist/node/server/MindooDBServer.js.map +1 -1
- package/dist/node/server/TenantManager.d.ts +2 -0
- package/dist/node/server/TenantManager.d.ts.map +1 -1
- package/dist/node/server/TenantManager.js +21 -12
- 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 +2 -2
- 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 +2 -1
|
@@ -18,6 +18,7 @@ const logging_1 = require("./logging");
|
|
|
18
18
|
* Default chunk size for attachments: 256KB
|
|
19
19
|
*/
|
|
20
20
|
const DEFAULT_CHUNK_SIZE_BYTES = 256 * 1024;
|
|
21
|
+
const DEFAULT_ATTACHMENT_STREAM_BATCH_SIZE = 4;
|
|
21
22
|
const DEFAULT_SNAPSHOT_MIN_CHANGES = 100;
|
|
22
23
|
const DEFAULT_SNAPSHOT_COOLDOWN_MS = 10 * 60 * 1000;
|
|
23
24
|
const DEFAULT_MAX_CACHED_DOCS = 128;
|
|
@@ -778,6 +779,98 @@ class BaseMindooDB {
|
|
|
778
779
|
}
|
|
779
780
|
return missingIds;
|
|
780
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
|
+
}
|
|
781
874
|
async syncEntriesFromStore(sourceStore, targetStore, options) {
|
|
782
875
|
const signal = options?.signal;
|
|
783
876
|
BaseMindooDB.setSyncAbortSignalOnStore(sourceStore, signal);
|
|
@@ -843,20 +936,16 @@ class BaseMindooDB {
|
|
|
843
936
|
return { transferred, scanned, cancelled: true };
|
|
844
937
|
}
|
|
845
938
|
if (missingIds.length > 0) {
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
transferredEntries: transferred,
|
|
850
|
-
scannedEntries: scanned,
|
|
939
|
+
const transferResult = await this.transferEntriesInBatches(sourceStore, targetStore, missingIds, options, {
|
|
940
|
+
transferred,
|
|
941
|
+
scanned,
|
|
851
942
|
totalSourceEntries: totalSourceEstimate,
|
|
852
943
|
currentPage,
|
|
853
944
|
});
|
|
854
|
-
|
|
855
|
-
if (
|
|
945
|
+
transferred = transferResult.transferred;
|
|
946
|
+
if (transferResult.cancelled) {
|
|
856
947
|
return { transferred, scanned, cancelled: true };
|
|
857
948
|
}
|
|
858
|
-
await targetStore.putEntries(missingEntries);
|
|
859
|
-
transferred += missingEntries.length;
|
|
860
949
|
}
|
|
861
950
|
}
|
|
862
951
|
onProgress?.({
|
|
@@ -891,21 +980,28 @@ class BaseMindooDB {
|
|
|
891
980
|
onProgress?.({
|
|
892
981
|
phase: 'transferring',
|
|
893
982
|
message: `Transferring ${sourceNewMetadata.length} entries...`,
|
|
894
|
-
transferredEntries:
|
|
983
|
+
transferredEntries: transferred,
|
|
895
984
|
scannedEntries: sourceNewMetadata.length,
|
|
896
985
|
totalSourceEntries: sourceNewMetadata.length,
|
|
897
986
|
});
|
|
898
|
-
const
|
|
899
|
-
|
|
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
|
+
}
|
|
900
996
|
onProgress?.({
|
|
901
997
|
phase: 'transferring',
|
|
902
|
-
message: `Transferred ${
|
|
903
|
-
transferredEntries:
|
|
998
|
+
message: `Transferred ${transferred} entries`,
|
|
999
|
+
transferredEntries: transferred,
|
|
904
1000
|
scannedEntries: sourceNewMetadata.length,
|
|
905
1001
|
totalSourceEntries: sourceNewMetadata.length,
|
|
906
1002
|
});
|
|
907
1003
|
return {
|
|
908
|
-
transferred
|
|
1004
|
+
transferred,
|
|
909
1005
|
scanned: sourceNewMetadata.length,
|
|
910
1006
|
cancelled: false,
|
|
911
1007
|
};
|
|
@@ -3199,10 +3295,10 @@ class BaseMindooDB {
|
|
|
3199
3295
|
/**
|
|
3200
3296
|
* Get an attachment reference by ID from a document's _attachments array.
|
|
3201
3297
|
*/
|
|
3202
|
-
getAttachmentRefInternal(docId, attachmentId) {
|
|
3203
|
-
const internalDoc = this.getCachedDocument(docId);
|
|
3298
|
+
async getAttachmentRefInternal(docId, attachmentId) {
|
|
3299
|
+
const internalDoc = this.getCachedDocument(docId) ?? await this.loadDocumentInternal(docId);
|
|
3204
3300
|
if (!internalDoc) {
|
|
3205
|
-
throw new Error(`Document ${docId} not found
|
|
3301
|
+
throw new Error(`Document ${docId} not found while reading attachment ${attachmentId}`);
|
|
3206
3302
|
}
|
|
3207
3303
|
const payload = internalDoc.doc;
|
|
3208
3304
|
const attachments = payload._attachments || [];
|
|
@@ -3250,7 +3346,7 @@ class BaseMindooDB {
|
|
|
3250
3346
|
*/
|
|
3251
3347
|
async getAttachmentInternal(docId, attachmentId) {
|
|
3252
3348
|
this.logger.debug(`Getting attachment ${attachmentId} from document ${docId}`);
|
|
3253
|
-
const ref = this.getAttachmentRefInternal(docId, attachmentId);
|
|
3349
|
+
const ref = await this.getAttachmentRefInternal(docId, attachmentId);
|
|
3254
3350
|
const store = this.getEffectiveAttachmentStore();
|
|
3255
3351
|
// Resolve dependency chain to get all chunk IDs in order (oldest first)
|
|
3256
3352
|
const chunkIds = await store.resolveDependencies(ref.lastChunkId, { includeStart: true });
|
|
@@ -3283,7 +3379,7 @@ class BaseMindooDB {
|
|
|
3283
3379
|
if (startByte < 0 || endByte <= startByte) {
|
|
3284
3380
|
throw new Error(`Invalid byte range: [${startByte}, ${endByte})`);
|
|
3285
3381
|
}
|
|
3286
|
-
const ref = this.getAttachmentRefInternal(docId, attachmentId);
|
|
3382
|
+
const ref = await this.getAttachmentRefInternal(docId, attachmentId);
|
|
3287
3383
|
const store = this.getEffectiveAttachmentStore();
|
|
3288
3384
|
const readPlan = await this.planAttachmentRead(store, ref, startByte, endByte);
|
|
3289
3385
|
const neededChunkIds = readPlan.chunkPlans.map((chunkPlan) => chunkPlan.id);
|
|
@@ -3315,7 +3411,7 @@ class BaseMindooDB {
|
|
|
3315
3411
|
*/
|
|
3316
3412
|
async *streamAttachmentInternal(docId, attachmentId, startOffset) {
|
|
3317
3413
|
this.logger.debug(`Streaming attachment ${attachmentId} from offset ${startOffset}`);
|
|
3318
|
-
const ref = this.getAttachmentRefInternal(docId, attachmentId);
|
|
3414
|
+
const ref = await this.getAttachmentRefInternal(docId, attachmentId);
|
|
3319
3415
|
if (startOffset < 0 || startOffset > ref.size) {
|
|
3320
3416
|
throw new Error(`Invalid stream offset ${startOffset} for attachment size ${ref.size}`);
|
|
3321
3417
|
}
|
|
@@ -3324,20 +3420,25 @@ class BaseMindooDB {
|
|
|
3324
3420
|
}
|
|
3325
3421
|
const store = this.getEffectiveAttachmentStore();
|
|
3326
3422
|
const readPlan = await this.planAttachmentRead(store, ref, startOffset, ref.size);
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
const
|
|
3330
|
-
const
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
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
|
+
}
|
|
3341
3442
|
}
|
|
3342
3443
|
}
|
|
3343
3444
|
this.logger.debug(`Finished streaming attachment ${attachmentId}`);
|
|
@@ -3643,19 +3744,35 @@ class BaseMindooDB {
|
|
|
3643
3744
|
phase: 'processing',
|
|
3644
3745
|
message: `Processing ${syncResult.transferred} new entries...`,
|
|
3645
3746
|
transferredEntries: syncResult.transferred,
|
|
3646
|
-
scannedEntries: syncResult.
|
|
3747
|
+
scannedEntries: syncResult.scanned,
|
|
3647
3748
|
});
|
|
3648
3749
|
// Sync the local store to process the new entries
|
|
3649
3750
|
// This will update the index, cache, and processedEntryIds
|
|
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
|
+
}
|
|
3650
3759
|
if (storeKind === types_1.StoreKind.docs) {
|
|
3651
3760
|
await this.syncStoreChanges();
|
|
3652
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
|
+
}
|
|
3653
3770
|
this.logger.info(`Pull complete, synced ${syncResult.transferred} entries`);
|
|
3654
3771
|
options?.onProgress?.({
|
|
3655
3772
|
phase: 'complete',
|
|
3656
3773
|
message: `Pull complete: ${syncResult.transferred} ${storeKind} entries synced`,
|
|
3657
3774
|
transferredEntries: syncResult.transferred,
|
|
3658
|
-
scannedEntries: syncResult.
|
|
3775
|
+
scannedEntries: syncResult.scanned,
|
|
3659
3776
|
});
|
|
3660
3777
|
return {
|
|
3661
3778
|
transferredEntries: syncResult.transferred,
|
|
@@ -3712,7 +3829,7 @@ class BaseMindooDB {
|
|
|
3712
3829
|
phase: 'complete',
|
|
3713
3830
|
message: `Push complete: ${syncResult.transferred} entries transferred`,
|
|
3714
3831
|
transferredEntries: syncResult.transferred,
|
|
3715
|
-
scannedEntries: syncResult.
|
|
3832
|
+
scannedEntries: syncResult.scanned,
|
|
3716
3833
|
});
|
|
3717
3834
|
return {
|
|
3718
3835
|
transferredEntries: syncResult.transferred,
|