@docstack/pouchdb-adapter-googledrive 0.0.5 → 0.0.8
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/.env.example +3 -0
- package/.test-drive-integration/1xssnxhy3akj +1 -0
- package/.test-drive-integration/8p8eymf8afm +1 -0
- package/.test-drive-integration/d1a4udbbuh +1 -0
- package/.test-drive-integration/usx93j46xtl +1 -0
- package/.test-drive-integration/vc0pq3g8vp +1 -0
- package/.test-drive-integration/zenra4v6i +1 -0
- package/.test-drive-root/05wzn5mtlrzt +1 -0
- package/.test-drive-root/0axnnh1gmn2d +1 -0
- package/.test-drive-root/0l2pw73vk4i +1 -0
- package/.test-drive-root/0tzush8sunaj +1 -0
- package/.test-drive-root/143bh2d29rnl +1 -0
- package/.test-drive-root/1synnvu59il +1 -0
- package/.test-drive-root/239cd47eh23i +1 -0
- package/.test-drive-root/275ocoaj6nq +1 -0
- package/.test-drive-root/29d78af16s4 +1 -0
- package/.test-drive-root/2gzvpzclnwu +1 -0
- package/.test-drive-root/2pmk0leotvp +1 -0
- package/.test-drive-root/38ijn3s7p33 +1 -0
- package/.test-drive-root/3jm64la13rp +1 -0
- package/.test-drive-root/3l4zrzi5bm2 +1 -0
- package/.test-drive-root/3of67xqpsfh +1 -0
- package/.test-drive-root/3pi1rk70we5 +1 -0
- package/.test-drive-root/3w3n7tpdn4n +1 -0
- package/.test-drive-root/3xg31irodim +1 -0
- package/.test-drive-root/50asm455hsa +1 -0
- package/.test-drive-root/51ldbder4dm +1 -0
- package/.test-drive-root/5l7dt9r491r +1 -0
- package/.test-drive-root/65mfhb9yuq5 +1 -0
- package/.test-drive-root/6iocm2rs118 +1 -0
- package/.test-drive-root/6o6vlpa6r6j +1 -0
- package/.test-drive-root/6rrcl4sdhoi +1 -0
- package/.test-drive-root/6ucq10yu9bx +1 -0
- package/.test-drive-root/7de4gtsch5b +1 -0
- package/.test-drive-root/7j4j3eq54q +1 -0
- package/.test-drive-root/7n3ai8wgsvy +1 -0
- package/.test-drive-root/7v4b3eyitw3 +1 -0
- package/.test-drive-root/8e15eu4xksq +1 -0
- package/.test-drive-root/8pu2c63yh1j +1 -0
- package/.test-drive-root/8ryxg058q8g +1 -0
- package/.test-drive-root/99l00ber08g +1 -0
- package/.test-drive-root/9gxcumnvepb +1 -0
- package/.test-drive-root/a4xxhwpe2cp +1 -0
- package/.test-drive-root/a57knn0ufon +1 -0
- package/.test-drive-root/a7gszl8y5ir +1 -0
- package/.test-drive-root/b0nddw4fdbu +1 -0
- package/.test-drive-root/b3b1d1yuolh +1 -0
- package/.test-drive-root/b4syq7nig1f +1 -0
- package/.test-drive-root/bcc8mhlffkg +1 -0
- package/.test-drive-root/bmngbpsk34 +1 -0
- package/.test-drive-root/c9i4hvcll2s +1 -0
- package/.test-drive-root/cao688yanw +1 -0
- package/.test-drive-root/cdtduwukrek +1 -0
- package/.test-drive-root/ckzeli8w7ej +1 -0
- package/.test-drive-root/ctywezu2fi5 +1 -0
- package/.test-drive-root/cyxiv5vgtle +1 -0
- package/.test-drive-root/d83g0suoz08 +1 -0
- package/.test-drive-root/d9tefhps5m6 +1 -0
- package/.test-drive-root/dac156l068 +1 -0
- package/.test-drive-root/daqsvyj5cu9 +1 -0
- package/.test-drive-root/df0wbnhhz1 +1 -0
- package/.test-drive-root/difkbp1duv +1 -0
- package/.test-drive-root/djrry1wk98v +1 -0
- package/.test-drive-root/dx42jrz7ty9 +1 -0
- package/.test-drive-root/e0s76kp3ena +1 -0
- package/.test-drive-root/e9xvdv0mqig +1 -0
- package/.test-drive-root/f3xo9hy1956 +1 -0
- package/.test-drive-root/f4scgrpgejw +1 -0
- package/.test-drive-root/f9kbt63p7xp +1 -0
- package/.test-drive-root/fe07yvs6p14 +1 -0
- package/.test-drive-root/ff5suiec8xv +1 -0
- package/.test-drive-root/fwc1qm37as +1 -0
- package/.test-drive-root/gh39v4lzxjp +1 -0
- package/.test-drive-root/gxvkr1qq21b +1 -0
- package/.test-drive-root/h4p64z7djk5 +1 -0
- package/.test-drive-root/habe7525tnl +1 -0
- package/.test-drive-root/hhzddxj31ar +1 -0
- package/.test-drive-root/hyj9crn9sb +1 -0
- package/.test-drive-root/igey5y5f3mi +1 -0
- package/.test-drive-root/isvxwzkgym +1 -0
- package/.test-drive-root/javyqnt07ws +1 -0
- package/.test-drive-root/jevlhmyaczl +1 -0
- package/.test-drive-root/jve7ypin7bb +1 -0
- package/.test-drive-root/koycie6jmyr +1 -0
- package/.test-drive-root/kvlad8ps1l +1 -0
- package/.test-drive-root/l13wzs51qi +1 -0
- package/.test-drive-root/lbwwk07et6i +1 -0
- package/.test-drive-root/m3uoxcsft2g +1 -0
- package/.test-drive-root/m7l8z61tnei +1 -0
- package/.test-drive-root/mmwpyz6aa2e +1 -0
- package/.test-drive-root/mqbyniatwu +1 -0
- package/.test-drive-root/mz3jbwwr12 +1 -0
- package/.test-drive-root/mza04oxze7 +1 -0
- package/.test-drive-root/ndqlafk0c0c +1 -0
- package/.test-drive-root/noojz8gst2 +1 -0
- package/.test-drive-root/o8ujk9otq7 +1 -0
- package/.test-drive-root/oa54zbip13m +1 -0
- package/.test-drive-root/ore7c15j64a +1 -0
- package/.test-drive-root/oxj3tt9q16 +1 -0
- package/.test-drive-root/pac4f4x1r9 +1 -0
- package/.test-drive-root/pqawj7k06i +1 -0
- package/.test-drive-root/qujzt0hpj6i +1 -0
- package/.test-drive-root/r5sdb4w0d3 +1 -0
- package/.test-drive-root/r8pybcnme1r +1 -0
- package/.test-drive-root/rpht4r0nk9 +1 -0
- package/.test-drive-root/rru3wcrfved +1 -0
- package/.test-drive-root/rtq2tifpvgt +1 -0
- package/.test-drive-root/ru00ubalaar +1 -0
- package/.test-drive-root/rx65wqybqlm +1 -0
- package/.test-drive-root/s585nksqc5 +1 -0
- package/.test-drive-root/scvyemdb9gh +1 -0
- package/.test-drive-root/si2b3ac0lve +1 -0
- package/.test-drive-root/sjbanmc5v5k +1 -0
- package/.test-drive-root/sl2coirs8zo +1 -0
- package/.test-drive-root/so3w2nrxm7 +1 -0
- package/.test-drive-root/t3y2vj69hz9 +1 -0
- package/.test-drive-root/t95zgk2fl8f +1 -0
- package/.test-drive-root/t9f08z6ayj +1 -0
- package/.test-drive-root/ta501uh71xi +1 -0
- package/.test-drive-root/taqhm9suyu +1 -0
- package/.test-drive-root/tya2ijynx5k +1 -0
- package/.test-drive-root/u4llyu4o7en +1 -0
- package/.test-drive-root/u5wco26a6lb +1 -0
- package/.test-drive-root/u6sricrto +1 -0
- package/.test-drive-root/ukcmmzglxa +1 -0
- package/.test-drive-root/uvsiktw52p +1 -0
- package/.test-drive-root/uylv1bn9swb +1 -0
- package/.test-drive-root/uzkzgxo3k5b +1 -0
- package/.test-drive-root/vf68w1pfrv +1 -0
- package/.test-drive-root/vp4g881bu7 +1 -0
- package/.test-drive-root/vuv8aavujb +1 -0
- package/.test-drive-root/wg24h3o6df +1 -0
- package/.test-drive-root/wyi5oftc8nh +1 -0
- package/.test-drive-root/x5zl3x2eh +1 -0
- package/.test-drive-root/xb3bpq9xtk +1 -0
- package/.test-drive-root/xfn089pthf9 +1 -0
- package/.test-drive-root/xrs7jul0mdl +1 -0
- package/.test-drive-root/y5qtilrbuzq +1 -0
- package/.test-drive-root/ysxlnyjuav +1 -0
- package/.test-drive-root/z2loakinuyk +1 -0
- package/.test-drive-root/z6lxmw8opjo +1 -0
- package/.test-drive-root/z74cnyignf9 +1 -0
- package/.test-drive-root/zi9agjhrfw +1 -0
- package/.test-drive-root/zspmm1wmo5l +1 -0
- package/README.md +72 -72
- package/jest.config.js +8 -8
- package/package.json +47 -40
- package/DOCUMENTATION.md +0 -54
- package/lib/adapter.d.ts +0 -17
- package/lib/adapter.js +0 -440
- package/lib/cache.d.ts +0 -12
- package/lib/cache.js +0 -42
- package/lib/client.d.ts +0 -29
- package/lib/client.js +0 -129
- package/lib/drive.d.ts +0 -71
- package/lib/drive.js +0 -545
- package/lib/index.d.ts +0 -10
- package/lib/index.js +0 -55
- package/lib/types.d.ts +0 -86
- package/lib/types.js +0 -2
package/lib/adapter.js
DELETED
|
@@ -1,440 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GoogleDriveAdapter = GoogleDriveAdapter;
|
|
4
|
-
const drive_1 = require("./drive");
|
|
5
|
-
/**
|
|
6
|
-
* Schedule a function to run asynchronously.
|
|
7
|
-
*/
|
|
8
|
-
function nextTick(fn) {
|
|
9
|
-
queueMicrotask(fn);
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* GoogleDriveAdapter - PouchDB adapter for Google Drive storage.
|
|
13
|
-
* Updated for Lazy Loading (Async Access).
|
|
14
|
-
*/
|
|
15
|
-
function GoogleDriveAdapter(PouchDB) {
|
|
16
|
-
function GoogleDrivePouch(opts, callback) {
|
|
17
|
-
const api = this;
|
|
18
|
-
const name = opts.name;
|
|
19
|
-
// Clone options to avoid mutation
|
|
20
|
-
const adapterOpts = Object.assign({}, opts);
|
|
21
|
-
// Internal state
|
|
22
|
-
let instanceId;
|
|
23
|
-
let db;
|
|
24
|
-
// Initialize DriveHandler
|
|
25
|
-
db = new drive_1.DriveHandler(adapterOpts, name);
|
|
26
|
-
// After database is initialized
|
|
27
|
-
function afterDBCreated() {
|
|
28
|
-
instanceId = 'gdrive-' + name + '-' + Date.now().toString(36);
|
|
29
|
-
nextTick(function () {
|
|
30
|
-
callback(null, api);
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
// Load data from Drive and initialize
|
|
34
|
-
db.load().then(() => {
|
|
35
|
-
afterDBCreated();
|
|
36
|
-
}).catch((err) => {
|
|
37
|
-
callback(err);
|
|
38
|
-
});
|
|
39
|
-
// ============ PouchDB Adapter API Methods ============
|
|
40
|
-
api._remote = false;
|
|
41
|
-
api.type = function () {
|
|
42
|
-
return 'googledrive';
|
|
43
|
-
};
|
|
44
|
-
api._id = function (callback) {
|
|
45
|
-
callback(null, instanceId);
|
|
46
|
-
};
|
|
47
|
-
// Info now must be async-ish (calculated from Index)
|
|
48
|
-
api._info = function (callback) {
|
|
49
|
-
const keys = db.getIndexKeys();
|
|
50
|
-
const docCount = keys.length; // Approximate (doesn't account for deleted unless filtered)
|
|
51
|
-
// Filter deleted for accurate count
|
|
52
|
-
let alive = 0;
|
|
53
|
-
for (const k of keys) {
|
|
54
|
-
const entry = db.getIndexEntry(k);
|
|
55
|
-
if (entry && !entry.deleted)
|
|
56
|
-
alive++;
|
|
57
|
-
}
|
|
58
|
-
const res = {
|
|
59
|
-
doc_count: alive,
|
|
60
|
-
update_seq: db.seq,
|
|
61
|
-
backend_adapter: 'googledrive'
|
|
62
|
-
};
|
|
63
|
-
nextTick(function () {
|
|
64
|
-
callback(null, res);
|
|
65
|
-
});
|
|
66
|
-
};
|
|
67
|
-
// Get a single document by ID (Async fetch)
|
|
68
|
-
api._get = function (id, opts, callback) {
|
|
69
|
-
if (typeof opts === 'function') {
|
|
70
|
-
callback = opts;
|
|
71
|
-
opts = {};
|
|
72
|
-
}
|
|
73
|
-
db.get(id).then(doc => {
|
|
74
|
-
if (!doc) {
|
|
75
|
-
return callback({
|
|
76
|
-
status: 404,
|
|
77
|
-
error: true,
|
|
78
|
-
name: 'not_found',
|
|
79
|
-
message: 'missing',
|
|
80
|
-
reason: 'missing'
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
const metadata = {
|
|
84
|
-
id: doc._id,
|
|
85
|
-
rev: doc._rev,
|
|
86
|
-
winningRev: doc._rev,
|
|
87
|
-
deleted: !!doc._deleted
|
|
88
|
-
};
|
|
89
|
-
callback(null, { doc, metadata });
|
|
90
|
-
}).catch(err => {
|
|
91
|
-
callback(err);
|
|
92
|
-
});
|
|
93
|
-
};
|
|
94
|
-
// Get all documents (Lazy stream or fetch)
|
|
95
|
-
api._allDocs = function (opts, callback) {
|
|
96
|
-
if (typeof opts === 'function') {
|
|
97
|
-
callback = opts;
|
|
98
|
-
opts = {};
|
|
99
|
-
}
|
|
100
|
-
const keys = db.getIndexKeys();
|
|
101
|
-
const total = keys.length; // Total keys (including deleted?)
|
|
102
|
-
let startIndex = opts.skip || 0;
|
|
103
|
-
let limit = typeof opts.limit === 'number' ? opts.limit : keys.length;
|
|
104
|
-
let filteredKeys = keys;
|
|
105
|
-
if (opts.startkey)
|
|
106
|
-
filteredKeys = filteredKeys.filter(k => k >= opts.startkey);
|
|
107
|
-
if (opts.endkey)
|
|
108
|
-
filteredKeys = filteredKeys.filter(k => k <= opts.endkey);
|
|
109
|
-
if (opts.key)
|
|
110
|
-
filteredKeys = filteredKeys.filter(k => k === opts.key);
|
|
111
|
-
if (opts.keys)
|
|
112
|
-
filteredKeys = opts.keys;
|
|
113
|
-
filteredKeys.sort();
|
|
114
|
-
if (opts.descending)
|
|
115
|
-
filteredKeys.reverse();
|
|
116
|
-
const sliced = filteredKeys.slice(startIndex, startIndex + limit);
|
|
117
|
-
// Fetch actual docs if needed
|
|
118
|
-
if (opts.include_docs) {
|
|
119
|
-
db.getMulti(sliced).then(docs => {
|
|
120
|
-
const rows = sliced.map((id, i) => {
|
|
121
|
-
const doc = docs[i];
|
|
122
|
-
const entry = db.getIndexEntry(id);
|
|
123
|
-
if (!doc && (!entry || entry.deleted))
|
|
124
|
-
return { key: id, error: 'not_found' };
|
|
125
|
-
if (!doc && entry) {
|
|
126
|
-
// This implies fetch failed but exists in index? Or null result.
|
|
127
|
-
return { key: id, error: 'not_found' };
|
|
128
|
-
}
|
|
129
|
-
const row = {
|
|
130
|
-
id,
|
|
131
|
-
key: id,
|
|
132
|
-
value: { rev: entry?.rev || doc._rev }
|
|
133
|
-
};
|
|
134
|
-
row.doc = doc;
|
|
135
|
-
return row;
|
|
136
|
-
});
|
|
137
|
-
const result = {
|
|
138
|
-
total_rows: total,
|
|
139
|
-
offset: startIndex,
|
|
140
|
-
rows: rows.filter(r => !r.error || !opts.keys) // Filter errored unless specifically asked via keys?
|
|
141
|
-
// CouchDB usually returns error row if distinct keys requested.
|
|
142
|
-
};
|
|
143
|
-
if (opts.update_seq)
|
|
144
|
-
result.update_seq = db.seq;
|
|
145
|
-
callback(null, result);
|
|
146
|
-
}).catch(err => callback(err));
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
// Index only (Fast!)
|
|
150
|
-
const rows = sliced.map(id => {
|
|
151
|
-
const entry = db.getIndexEntry(id);
|
|
152
|
-
if (!entry || entry.deleted)
|
|
153
|
-
return { key: id, error: 'not_found' };
|
|
154
|
-
return {
|
|
155
|
-
id,
|
|
156
|
-
key: id,
|
|
157
|
-
value: { rev: entry.rev }
|
|
158
|
-
};
|
|
159
|
-
});
|
|
160
|
-
const result = {
|
|
161
|
-
total_rows: total,
|
|
162
|
-
offset: startIndex,
|
|
163
|
-
rows
|
|
164
|
-
};
|
|
165
|
-
if (opts.update_seq)
|
|
166
|
-
result.update_seq = db.seq;
|
|
167
|
-
nextTick(() => callback(null, result));
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
// Bulk document operations
|
|
171
|
-
api._bulkDocs = function (req, opts, callback) {
|
|
172
|
-
const docs = req.docs;
|
|
173
|
-
const results = [];
|
|
174
|
-
const newEdits = opts.new_edits !== false;
|
|
175
|
-
const changes = [];
|
|
176
|
-
// We need to validate revisions against Index
|
|
177
|
-
// This does NOT require fetching bodies usually
|
|
178
|
-
for (const doc of docs) {
|
|
179
|
-
const id = doc._id;
|
|
180
|
-
const seq = db.getNextSeq() + changes.length;
|
|
181
|
-
const entry = db.getIndexEntry(id);
|
|
182
|
-
if (doc._deleted) {
|
|
183
|
-
if (!entry || entry.deleted) {
|
|
184
|
-
results.push({
|
|
185
|
-
ok: false,
|
|
186
|
-
id,
|
|
187
|
-
error: 'not_found',
|
|
188
|
-
reason: 'missing'
|
|
189
|
-
});
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
// Check rev
|
|
193
|
-
const oldRev = entry.rev || '0-0'; // Index has latest
|
|
194
|
-
// If mismatch? PouchDB handles conflict logic before calling us sometimes?
|
|
195
|
-
// But we should verify.
|
|
196
|
-
// If doc._rev matches entry.rev, we are good.
|
|
197
|
-
const revNum = parseInt(oldRev.split('-')[0], 10) + 1;
|
|
198
|
-
const newRev = revNum + '-' + generateRevId();
|
|
199
|
-
changes.push({
|
|
200
|
-
seq,
|
|
201
|
-
id,
|
|
202
|
-
rev: newRev,
|
|
203
|
-
deleted: true,
|
|
204
|
-
timestamp: Date.now()
|
|
205
|
-
});
|
|
206
|
-
results.push({ ok: true, id, rev: newRev });
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
let newRev;
|
|
210
|
-
if (newEdits) {
|
|
211
|
-
const oldRev = entry?.rev || '0-0';
|
|
212
|
-
const revNum = parseInt(oldRev.split('-')[0], 10) + 1;
|
|
213
|
-
newRev = revNum + '-' + generateRevId();
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
newRev = doc._rev;
|
|
217
|
-
}
|
|
218
|
-
const savedDoc = Object.assign({}, doc, { _rev: newRev });
|
|
219
|
-
changes.push({
|
|
220
|
-
seq,
|
|
221
|
-
id,
|
|
222
|
-
rev: newRev,
|
|
223
|
-
doc: savedDoc,
|
|
224
|
-
timestamp: Date.now()
|
|
225
|
-
});
|
|
226
|
-
results.push({ ok: true, id, rev: newRev });
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
// Append changes to log
|
|
230
|
-
db.appendChanges(changes).then(() => {
|
|
231
|
-
nextTick(() => callback(null, results));
|
|
232
|
-
}).catch((err) => {
|
|
233
|
-
callback(err);
|
|
234
|
-
});
|
|
235
|
-
};
|
|
236
|
-
// Changes feed
|
|
237
|
-
api._changes = function (opts) {
|
|
238
|
-
opts = Object.assign({}, opts);
|
|
239
|
-
const since = opts.since || 0;
|
|
240
|
-
const limit = typeof opts.limit === 'number' ? opts.limit : Infinity;
|
|
241
|
-
const returnDocs = opts.return_docs !== false;
|
|
242
|
-
const results = [];
|
|
243
|
-
let lastSeq = since;
|
|
244
|
-
let complete = false;
|
|
245
|
-
// Should we iterate Index or Logs?
|
|
246
|
-
// "Index" only has LATEST state. _changes usually wants history if `since` is old.
|
|
247
|
-
// But this adapter is an "Index + Log" adapter.
|
|
248
|
-
// If `since` is 0, we can walk the Index.
|
|
249
|
-
// If `since` is recent, we can maybe walk pending changes?
|
|
250
|
-
// Correct implementation of `_changes` with Append-Only Log requires reading the log files essentially.
|
|
251
|
-
// BUT, standard PouchDB `_changes` often just iterates all docs if it can't stream.
|
|
252
|
-
// For now, let's iterate the INDEX (Winning Revisions) which implies "since=0" behavior effectively (State of the World).
|
|
253
|
-
function processChanges() {
|
|
254
|
-
const keys = db.getIndexKeys(); // IDs
|
|
255
|
-
let processed = 0;
|
|
256
|
-
// Index-based iteration
|
|
257
|
-
for (const id of keys) {
|
|
258
|
-
if (complete || processed >= limit)
|
|
259
|
-
break;
|
|
260
|
-
const entry = db.getIndexEntry(id);
|
|
261
|
-
if (!entry)
|
|
262
|
-
continue;
|
|
263
|
-
// Filter by seq?
|
|
264
|
-
if (entry.seq <= since)
|
|
265
|
-
continue; // Already seen
|
|
266
|
-
const change = {
|
|
267
|
-
id: id,
|
|
268
|
-
seq: entry.seq,
|
|
269
|
-
changes: [{ rev: entry.rev }],
|
|
270
|
-
};
|
|
271
|
-
if (opts.include_docs) {
|
|
272
|
-
// We need to fetch it!
|
|
273
|
-
// This makes _changes with include_docs SLOW.
|
|
274
|
-
// We can't do this synchronously here easily because `processChanges` is sync in original code?
|
|
275
|
-
// Wait, original was `nextTick(processChanges)`.
|
|
276
|
-
// We need to be async here.
|
|
277
|
-
}
|
|
278
|
-
// Supporting async processChanges is cleaner.
|
|
279
|
-
}
|
|
280
|
-
// ... This requires rewrite for async iteration ...
|
|
281
|
-
}
|
|
282
|
-
// Simplified Async Version
|
|
283
|
-
async function processChangesAsync() {
|
|
284
|
-
const keys = db.getIndexKeys();
|
|
285
|
-
let processed = 0;
|
|
286
|
-
for (const id of keys) {
|
|
287
|
-
if (complete || processed >= limit)
|
|
288
|
-
break;
|
|
289
|
-
const entry = db.getIndexEntry(id);
|
|
290
|
-
if (!entry || entry.seq <= since)
|
|
291
|
-
continue;
|
|
292
|
-
const change = {
|
|
293
|
-
id: id,
|
|
294
|
-
seq: entry.seq,
|
|
295
|
-
changes: [{ rev: entry.rev }]
|
|
296
|
-
};
|
|
297
|
-
if (opts.include_docs) {
|
|
298
|
-
change.doc = await db.get(id);
|
|
299
|
-
}
|
|
300
|
-
if (opts.onChange)
|
|
301
|
-
opts.onChange(change);
|
|
302
|
-
if (returnDocs)
|
|
303
|
-
results.push(change);
|
|
304
|
-
processed++;
|
|
305
|
-
lastSeq = Math.max(lastSeq, entry.seq);
|
|
306
|
-
}
|
|
307
|
-
if (opts.complete && !complete) {
|
|
308
|
-
opts.complete(null, { results, last_seq: lastSeq });
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
if (opts.live) {
|
|
312
|
-
db.onChange((changes) => {
|
|
313
|
-
// Update feed
|
|
314
|
-
// ...
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
nextTick(() => {
|
|
318
|
-
processChangesAsync().catch(err => {
|
|
319
|
-
console.error('Changes feed error', err);
|
|
320
|
-
if (opts.complete)
|
|
321
|
-
opts.complete(err);
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
return {
|
|
325
|
-
cancel() {
|
|
326
|
-
complete = true;
|
|
327
|
-
}
|
|
328
|
-
};
|
|
329
|
-
};
|
|
330
|
-
// Manual compaction trigger
|
|
331
|
-
api._compact = function (callback) {
|
|
332
|
-
db.compact().then(() => {
|
|
333
|
-
callback(null, { ok: true });
|
|
334
|
-
}).catch((err) => {
|
|
335
|
-
callback(err);
|
|
336
|
-
});
|
|
337
|
-
};
|
|
338
|
-
api._getRevisionTree = function (docId, callback) {
|
|
339
|
-
db.get(docId).then(doc => {
|
|
340
|
-
if (!doc) {
|
|
341
|
-
return callback({ status: 404, error: true, name: 'not_found', message: 'missing' });
|
|
342
|
-
}
|
|
343
|
-
const revTree = [{
|
|
344
|
-
pos: 1,
|
|
345
|
-
ids: [doc._rev.split('-')[1], { status: 'available' }, []]
|
|
346
|
-
}];
|
|
347
|
-
callback(null, revTree);
|
|
348
|
-
}).catch(callback);
|
|
349
|
-
};
|
|
350
|
-
api._close = function (callback) {
|
|
351
|
-
db.stopPolling();
|
|
352
|
-
nextTick(callback);
|
|
353
|
-
};
|
|
354
|
-
api._destroy = function (opts, callback) {
|
|
355
|
-
if (typeof opts === 'function') {
|
|
356
|
-
callback = opts;
|
|
357
|
-
opts = {};
|
|
358
|
-
}
|
|
359
|
-
db.stopPolling();
|
|
360
|
-
if (opts.deleteFolder) {
|
|
361
|
-
db.deleteFolder().then(() => {
|
|
362
|
-
callback(null, { ok: true });
|
|
363
|
-
}).catch((err) => {
|
|
364
|
-
callback(err);
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
else {
|
|
368
|
-
nextTick(() => callback(null, { ok: true }));
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
api._putLocal = function (doc, opts, callback) {
|
|
372
|
-
if (typeof opts === 'function') {
|
|
373
|
-
callback = opts;
|
|
374
|
-
opts = {};
|
|
375
|
-
}
|
|
376
|
-
const id = doc._id;
|
|
377
|
-
const rev = '0-1';
|
|
378
|
-
const savedDoc = Object.assign({}, doc, { _rev: rev });
|
|
379
|
-
const change = {
|
|
380
|
-
seq: db.getNextSeq(),
|
|
381
|
-
id,
|
|
382
|
-
rev,
|
|
383
|
-
doc: savedDoc,
|
|
384
|
-
timestamp: Date.now()
|
|
385
|
-
};
|
|
386
|
-
db.appendChanges([change]).then(() => {
|
|
387
|
-
callback(null, { ok: true, id, rev });
|
|
388
|
-
}).catch((err) => {
|
|
389
|
-
callback(err);
|
|
390
|
-
});
|
|
391
|
-
};
|
|
392
|
-
api._getLocal = function (id, callback) {
|
|
393
|
-
db.get(id).then(doc => {
|
|
394
|
-
if (!doc)
|
|
395
|
-
return callback({ status: 404, error: true, name: 'not_found' });
|
|
396
|
-
callback(null, doc);
|
|
397
|
-
}).catch(callback);
|
|
398
|
-
};
|
|
399
|
-
api._removeLocal = function (doc, opts, callback) {
|
|
400
|
-
// ... Similar async update ...
|
|
401
|
-
if (typeof opts === 'function') {
|
|
402
|
-
callback = opts;
|
|
403
|
-
opts = {};
|
|
404
|
-
}
|
|
405
|
-
const id = doc._id;
|
|
406
|
-
// Check existence async if we want to be strict, but index check is ok
|
|
407
|
-
if (!db.getIndexEntry(id)) {
|
|
408
|
-
return callback({ status: 404, error: true, name: 'not_found' });
|
|
409
|
-
}
|
|
410
|
-
// ...
|
|
411
|
-
// Simplified removeLocal
|
|
412
|
-
const change = {
|
|
413
|
-
seq: db.getNextSeq(),
|
|
414
|
-
id,
|
|
415
|
-
rev: '0-0',
|
|
416
|
-
deleted: true,
|
|
417
|
-
timestamp: Date.now()
|
|
418
|
-
};
|
|
419
|
-
db.appendChanges([change]).then(() => {
|
|
420
|
-
callback(null, { ok: true, id, rev: '0-0' });
|
|
421
|
-
}).catch((err) => {
|
|
422
|
-
callback(err);
|
|
423
|
-
});
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
// Static properties
|
|
427
|
-
GoogleDrivePouch.valid = function () {
|
|
428
|
-
return true;
|
|
429
|
-
};
|
|
430
|
-
GoogleDrivePouch.use_prefix = false;
|
|
431
|
-
return GoogleDrivePouch;
|
|
432
|
-
}
|
|
433
|
-
/**
|
|
434
|
-
* Generate a random revision ID
|
|
435
|
-
*/
|
|
436
|
-
function generateRevId() {
|
|
437
|
-
return Math.random().toString(36).substring(2, 11) +
|
|
438
|
-
Math.random().toString(36).substring(2, 11);
|
|
439
|
-
}
|
|
440
|
-
exports.default = GoogleDriveAdapter;
|
package/lib/cache.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simple Least Recently Used (LRU) Cache
|
|
3
|
-
*/
|
|
4
|
-
export declare class LRUCache<K, V> {
|
|
5
|
-
private capacity;
|
|
6
|
-
private map;
|
|
7
|
-
constructor(capacity: number);
|
|
8
|
-
get(key: K): V | undefined;
|
|
9
|
-
put(key: K, value: V): void;
|
|
10
|
-
remove(key: K): void;
|
|
11
|
-
clear(): void;
|
|
12
|
-
}
|
package/lib/cache.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LRUCache = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Simple Least Recently Used (LRU) Cache
|
|
6
|
-
*/
|
|
7
|
-
class LRUCache {
|
|
8
|
-
constructor(capacity) {
|
|
9
|
-
this.capacity = capacity;
|
|
10
|
-
this.map = new Map();
|
|
11
|
-
}
|
|
12
|
-
get(key) {
|
|
13
|
-
const value = this.map.get(key);
|
|
14
|
-
if (value !== undefined) {
|
|
15
|
-
// Refresh: delete and re-add to end (most contiguous)
|
|
16
|
-
this.map.delete(key);
|
|
17
|
-
this.map.set(key, value);
|
|
18
|
-
}
|
|
19
|
-
return value;
|
|
20
|
-
}
|
|
21
|
-
put(key, value) {
|
|
22
|
-
if (this.map.has(key)) {
|
|
23
|
-
// Update existing
|
|
24
|
-
this.map.delete(key);
|
|
25
|
-
}
|
|
26
|
-
else if (this.map.size >= this.capacity) {
|
|
27
|
-
// Evict least recently used (first item)
|
|
28
|
-
const firstKey = this.map.keys().next().value;
|
|
29
|
-
if (firstKey !== undefined) {
|
|
30
|
-
this.map.delete(firstKey);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
this.map.set(key, value);
|
|
34
|
-
}
|
|
35
|
-
remove(key) {
|
|
36
|
-
this.map.delete(key);
|
|
37
|
-
}
|
|
38
|
-
clear() {
|
|
39
|
-
this.map.clear();
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
exports.LRUCache = LRUCache;
|
package/lib/client.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export interface DriveFile {
|
|
2
|
-
id: string;
|
|
3
|
-
name: string;
|
|
4
|
-
mimeType: string;
|
|
5
|
-
parents?: string[];
|
|
6
|
-
etag?: string;
|
|
7
|
-
}
|
|
8
|
-
export interface DriveClientOptions {
|
|
9
|
-
accessToken: string | (() => Promise<string>);
|
|
10
|
-
}
|
|
11
|
-
export declare class GoogleDriveClient {
|
|
12
|
-
private options;
|
|
13
|
-
constructor(options: DriveClientOptions);
|
|
14
|
-
private getToken;
|
|
15
|
-
private fetch;
|
|
16
|
-
listFiles(q: string): Promise<DriveFile[]>;
|
|
17
|
-
getFile(fileId: string): Promise<any>;
|
|
18
|
-
getFileMetadata(fileId: string): Promise<DriveFile>;
|
|
19
|
-
createFile(name: string, parents: string[] | undefined, mimeType: string, content: string): Promise<{
|
|
20
|
-
id: string;
|
|
21
|
-
etag: string;
|
|
22
|
-
}>;
|
|
23
|
-
updateFile(fileId: string, content: string, expectedEtag?: string): Promise<{
|
|
24
|
-
id: string;
|
|
25
|
-
etag: string;
|
|
26
|
-
}>;
|
|
27
|
-
deleteFile(fileId: string): Promise<void>;
|
|
28
|
-
private buildMultipart;
|
|
29
|
-
}
|
package/lib/client.js
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GoogleDriveClient = void 0;
|
|
4
|
-
const BASE_URL = 'https://www.googleapis.com/drive/v3/files';
|
|
5
|
-
const UPLOAD_URL = 'https://www.googleapis.com/upload/drive/v3/files';
|
|
6
|
-
class GoogleDriveClient {
|
|
7
|
-
constructor(options) {
|
|
8
|
-
this.options = options;
|
|
9
|
-
}
|
|
10
|
-
async getToken() {
|
|
11
|
-
if (typeof this.options.accessToken === 'function') {
|
|
12
|
-
return await this.options.accessToken();
|
|
13
|
-
}
|
|
14
|
-
return this.options.accessToken;
|
|
15
|
-
}
|
|
16
|
-
async fetch(url, init) {
|
|
17
|
-
const token = await this.getToken();
|
|
18
|
-
const headers = new Headers(init.headers);
|
|
19
|
-
headers.set('Authorization', `Bearer ${token}`);
|
|
20
|
-
const res = await fetch(url, { ...init, headers });
|
|
21
|
-
const method = init.method || 'GET';
|
|
22
|
-
if (!res.ok) {
|
|
23
|
-
// Basic error handling
|
|
24
|
-
const text = await res.text();
|
|
25
|
-
let errorMsg = `Drive API Error: ${res.status} ${res.statusText} (${method} ${url})`;
|
|
26
|
-
try {
|
|
27
|
-
const json = JSON.parse(text);
|
|
28
|
-
if (json.error && json.error.message) {
|
|
29
|
-
errorMsg += ` - ${json.error.message}`;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
catch { }
|
|
33
|
-
const err = new Error(errorMsg);
|
|
34
|
-
err.status = res.status;
|
|
35
|
-
throw err;
|
|
36
|
-
}
|
|
37
|
-
return res;
|
|
38
|
-
}
|
|
39
|
-
async listFiles(q) {
|
|
40
|
-
const params = new URLSearchParams({
|
|
41
|
-
q,
|
|
42
|
-
fields: 'files(id, name, mimeType, parents, etag)',
|
|
43
|
-
spaces: 'drive',
|
|
44
|
-
pageSize: '1000' // Ensure we get enough
|
|
45
|
-
});
|
|
46
|
-
const res = await this.fetch(`${BASE_URL}?${params.toString()}`, { method: 'GET' });
|
|
47
|
-
const data = await res.json();
|
|
48
|
-
return data.files || [];
|
|
49
|
-
}
|
|
50
|
-
async getFile(fileId) {
|
|
51
|
-
// Try getting media
|
|
52
|
-
try {
|
|
53
|
-
const params = new URLSearchParams({ alt: 'media' });
|
|
54
|
-
const res = await this.fetch(`${BASE_URL}/${fileId}?${params.toString()}`, { method: 'GET' });
|
|
55
|
-
// Standard fetch handles JSON/Text transparency?
|
|
56
|
-
// We expect JSON mostly, but sometimes we might want text.
|
|
57
|
-
// PouchDB adapter flow: downloadJson, downloadNdjson
|
|
58
|
-
// Let's rely on content-type or caller expectation?
|
|
59
|
-
// The usage in `drive.ts` expects parsed JSON/NDJSON lines.
|
|
60
|
-
// Let's return the raw Text or JSON based on Content-Type?
|
|
61
|
-
const contentType = res.headers.get('content-type');
|
|
62
|
-
if (contentType && contentType.includes('application/json')) {
|
|
63
|
-
return await res.json();
|
|
64
|
-
}
|
|
65
|
-
return await res.text();
|
|
66
|
-
}
|
|
67
|
-
catch (e) {
|
|
68
|
-
throw e;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
// Single metadata get (for etag check)
|
|
72
|
-
async getFileMetadata(fileId) {
|
|
73
|
-
const params = new URLSearchParams({ fields: 'id, name, mimeType, parents, etag' });
|
|
74
|
-
const res = await this.fetch(`${BASE_URL}/${fileId}?${params.toString()}`, { method: 'GET' });
|
|
75
|
-
return await res.json();
|
|
76
|
-
}
|
|
77
|
-
async createFile(name, parents, mimeType, content) {
|
|
78
|
-
const metadata = {
|
|
79
|
-
name,
|
|
80
|
-
mimeType,
|
|
81
|
-
parents
|
|
82
|
-
};
|
|
83
|
-
// Folders or empty content can use simple metadata-only POST
|
|
84
|
-
if (!content && mimeType === 'application/vnd.google-apps.folder') {
|
|
85
|
-
const res = await this.fetch(`${BASE_URL}?fields=id,etag`, {
|
|
86
|
-
method: 'POST',
|
|
87
|
-
headers: { 'Content-Type': 'application/json' },
|
|
88
|
-
body: JSON.stringify(metadata)
|
|
89
|
-
});
|
|
90
|
-
return await res.json();
|
|
91
|
-
}
|
|
92
|
-
const multipartBody = this.buildMultipart(metadata, content, mimeType);
|
|
93
|
-
const res = await this.fetch(`${UPLOAD_URL}?uploadType=multipart&fields=id,etag`, {
|
|
94
|
-
method: 'POST',
|
|
95
|
-
headers: {
|
|
96
|
-
'Content-Type': `multipart/related; boundary=${multipartBody.boundary}`
|
|
97
|
-
},
|
|
98
|
-
body: multipartBody.body
|
|
99
|
-
});
|
|
100
|
-
return await res.json();
|
|
101
|
-
}
|
|
102
|
-
async updateFile(fileId, content, expectedEtag) {
|
|
103
|
-
// Update content (media) usually, but sometimes meta?
|
|
104
|
-
// In our usage (saveMeta), we update body.
|
|
105
|
-
const res = await this.fetch(`${UPLOAD_URL}/${fileId}?uploadType=media&fields=id,etag`, {
|
|
106
|
-
method: 'PATCH',
|
|
107
|
-
headers: expectedEtag ? { 'If-Match': expectedEtag, 'Content-Type': 'application/json' } : { 'Content-Type': 'application/json' },
|
|
108
|
-
body: content
|
|
109
|
-
});
|
|
110
|
-
return await res.json();
|
|
111
|
-
}
|
|
112
|
-
async deleteFile(fileId) {
|
|
113
|
-
await this.fetch(`${BASE_URL}/${fileId}`, { method: 'DELETE' });
|
|
114
|
-
}
|
|
115
|
-
buildMultipart(metadata, content, contentType) {
|
|
116
|
-
const boundary = '-------' + Math.random().toString(36).substring(2);
|
|
117
|
-
const delimiter = `\r\n--${boundary}\r\n`;
|
|
118
|
-
const closeDelimiter = `\r\n--${boundary}--`;
|
|
119
|
-
const body = delimiter +
|
|
120
|
-
'Content-Type: application/json\r\n\r\n' +
|
|
121
|
-
JSON.stringify(metadata) +
|
|
122
|
-
delimiter +
|
|
123
|
-
`Content-Type: ${contentType}\r\n\r\n` +
|
|
124
|
-
content +
|
|
125
|
-
closeDelimiter;
|
|
126
|
-
return { body, boundary };
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
exports.GoogleDriveClient = GoogleDriveClient;
|