@docstack/pouchdb-adapter-googledrive 0.1.2 → 0.1.4

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.
Files changed (2) hide show
  1. package/lib/drive.js +67 -56
  2. package/package.json +1 -1
package/lib/drive.js CHANGED
@@ -160,22 +160,31 @@ class DriveHandler {
160
160
  return { id, changes: null };
161
161
  }
162
162
  }));
163
+ const foundNew = {};
163
164
  for (const { id, changes } of logResults) {
164
165
  if (!changes) {
165
166
  this.log(`Skipping failed log ${id}`);
166
167
  continue;
167
168
  }
168
169
  let changesArray = Array.isArray(changes) ? changes : [changes];
169
- this.currentLogSizeEstimate += 100 * changesArray.length;
170
+ this.log('Processing log file', id, 'changes', changesArray.length);
170
171
  for (const change of changesArray) {
171
- this.log('Processing change, sequence', change.seq);
172
172
  this.updateIndex(change, id);
173
173
  if (this.docCache.get(change.id)) {
174
174
  this.docCache.remove(change.id);
175
175
  }
176
+ foundNew[change.id] = {
177
+ _id: change.id,
178
+ _rev: change.rev,
179
+ _deleted: !!change.deleted,
180
+ seq: change.seq
181
+ };
176
182
  }
177
183
  this.processedLogIds.add(id);
178
- this.log('Processed log', id);
184
+ }
185
+ if (Object.keys(foundNew).length > 0) {
186
+ this.log('Load complete, notifying of changes', Object.keys(foundNew).length);
187
+ this.notifyListeners(foundNew);
179
188
  }
180
189
  }
181
190
  // 2. Replay NEW Change Logs (Metadata only updates)
@@ -278,6 +287,12 @@ class DriveHandler {
278
287
  doc = content;
279
288
  }
280
289
  if (doc) {
290
+ // HEAL: Ensure PouchDB core validation doesn't fail due to history corruption
291
+ // This prevents the infinite replicator retry loop when start < ids.length
292
+ if (doc._revisions && doc._revisions.start < doc._revisions.ids.length) {
293
+ const start = doc._revisions.start || 1;
294
+ doc._revisions.ids = doc._revisions.ids.slice(0, start);
295
+ }
281
296
  this.docCache.put(id, doc);
282
297
  doc._rev = entry.rev;
283
298
  }
@@ -508,13 +523,21 @@ class DriveHandler {
508
523
  }
509
524
  this.localDocsEtag = res.etag;
510
525
  // Update Index
526
+ const changedDocs = {};
511
527
  for (const change of changes) {
512
528
  this.updateIndex(change, res.id);
513
529
  if (change.doc)
514
530
  this.docCache.put(change.id, change.doc);
515
531
  else
516
532
  this.docCache.remove(change.id);
533
+ changedDocs[change.id] = {
534
+ _id: change.id,
535
+ _rev: change.rev,
536
+ _deleted: !!change.deleted,
537
+ seq: change.seq
538
+ };
517
539
  }
540
+ this.notifyListeners(changedDocs);
518
541
  return;
519
542
  }
520
543
  catch (err) {
@@ -539,6 +562,7 @@ class DriveHandler {
539
562
  await this.saveMeta(nextMeta, this.metaEtag);
540
563
  // 4. Update Local State
541
564
  this.meta = nextMeta;
565
+ const changedDocs = {};
542
566
  for (const change of changes) {
543
567
  this.updateIndex(change, fileId);
544
568
  if (change.doc) {
@@ -547,9 +571,15 @@ class DriveHandler {
547
571
  else if (change.deleted) {
548
572
  this.docCache.remove(change.id);
549
573
  }
574
+ changedDocs[change.id] = {
575
+ _id: change.id,
576
+ _rev: change.rev,
577
+ _deleted: !!change.deleted,
578
+ seq: change.seq
579
+ };
550
580
  }
551
- // Notify local changes feed listeners about our own write
552
- this.notifyListeners();
581
+ // Notify local changes feed listeners about only what we just wrote
582
+ this.notifyListeners(changedDocs);
553
583
  // 5. Compaction Check
554
584
  const totalChanges = await this.countTotalChanges();
555
585
  if (totalChanges >= this.compactionThreshold ||
@@ -697,42 +727,27 @@ class DriveHandler {
697
727
  return createRes.id;
698
728
  }
699
729
  async findFile(name) {
700
- const pending = this.pendingFinds.get(name);
701
- if (pending) {
702
- this.log('findFile reuse pending search', name);
703
- return await pending;
704
- }
705
- const findPromise = (async () => {
706
- const safeName = this.escapeQuery(name);
707
- const q = `name = '${safeName}' and '${this.folderId}' in parents and trashed = false`;
708
- try {
709
- const files = await this.client.listFiles(q);
710
- if (files.length > 0) {
711
- let file = files[0];
712
- if (!file.etag) {
713
- // Robustness: Fetch metadata for the file if etag is missing from list
714
- try {
715
- file = await this.client.getFileMetadata(file.id);
716
- }
717
- catch (e) {
718
- this.log('Failed to fetch file metadata for etag', file.id, e);
719
- }
720
- }
721
- return {
722
- fileId: file.id,
723
- etag: file.etag,
724
- md5Checksum: file.md5Checksum,
725
- modifiedTime: file.modifiedTime
726
- };
730
+ const safeName = this.escapeQuery(name);
731
+ const q = `name = '${safeName}' and '${this.folderId}' in parents and trashed = false`;
732
+ const files = await this.client.listFiles(q);
733
+ if (files.length > 0) {
734
+ let file = files[0];
735
+ if (!file.etag) {
736
+ try {
737
+ file = await this.client.getFileMetadata(file.id);
738
+ }
739
+ catch (e) {
740
+ this.log('Failed to fetch file metadata for etag', file.id, e);
727
741
  }
728
- return null;
729
- }
730
- finally {
731
- this.pendingFinds.delete(name);
732
742
  }
733
- })();
734
- this.pendingFinds.set(name, findPromise);
735
- return await findPromise;
743
+ return {
744
+ fileId: file.id,
745
+ etag: file.etag,
746
+ md5Checksum: file.md5Checksum,
747
+ modifiedTime: file.modifiedTime
748
+ };
749
+ }
750
+ return null;
736
751
  }
737
752
  async downloadJson(fileId, skipCache = false) {
738
753
  return await this.fetchFile(fileId, skipCache);
@@ -855,24 +870,20 @@ class DriveHandler {
855
870
  }
856
871
  }, intervalMs);
857
872
  }
858
- notifyListeners() {
859
- // Observers expecting 'docs' object might be broken if they expect FULL body.
860
- // We can pass empty object or partials?
861
- // Real PouchDB changes feed calls `db.changes()`.
862
- // Our `adapter.js` uses `db.onChange` effectively.
863
- // We should pass a map of { ID: { _rev, ... } } (Index entries)
864
- // Adapter needs to handle this.
865
- const changes = {};
866
- for (const [id, entry] of Object.entries(this.index)) {
867
- changes[id] = {
868
- _id: id,
869
- _rev: entry.rev,
870
- _deleted: !!entry.deleted,
871
- seq: entry.seq // IMPORTANT: Missing previously, preventing filtered changes from working
872
- };
873
+ notifyListeners(changedDocs) {
874
+ const changes = changedDocs || {};
875
+ // If no specifically changed docs provided, we don't want to firehose the whole index anymore
876
+ // as it causes reprocessing loops in PouchDB sync.
877
+ if (Object.keys(changes).length === 0)
878
+ return;
879
+ for (const cb of this.listeners) {
880
+ try {
881
+ cb(changes);
882
+ }
883
+ catch (e) {
884
+ this.log('Error in change listener callback', e);
885
+ }
873
886
  }
874
- for (const cb of this.listeners)
875
- cb(changes);
876
887
  }
877
888
  // For tests/debug
878
889
  onChange(cb) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docstack/pouchdb-adapter-googledrive",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "PouchDB adapter for Google Drive",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",