@docstack/pouchdb-adapter-googledrive 0.0.3 → 0.0.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.
- package/README.md +26 -16
- package/lib/client.d.ts +29 -0
- package/lib/client.js +119 -0
- package/lib/drive.d.ts +1 -1
- package/lib/drive.js +35 -73
- package/lib/types.d.ts +2 -5
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -8,6 +8,11 @@ A persistent, serverless PouchDB adapter that uses Google Drive as a backend sto
|
|
|
8
8
|
- **⚡ Lazy Loading**: Optimizes memory and bandwidth by loading only the **Index** into memory. Document bodies are fetched on-demand.
|
|
9
9
|
- **🛡️ Optimistic Concurrency Control**: Uses ETag-based locking on metadata to prevent race conditions.
|
|
10
10
|
- **📦 Auto-Compaction**: Automatically merges logs for performance.
|
|
11
|
+
- **🌍 Universal**: Works natively in Node.js 18+, Browsers, and Edge environments (no `googleapis` dependency).
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- **Node.js 18+** (for global `fetch` support) or a modern browser.
|
|
11
16
|
|
|
12
17
|
## Installation
|
|
13
18
|
|
|
@@ -17,32 +22,23 @@ npm install @docstack/pouchdb-adapter-googledrive
|
|
|
17
22
|
|
|
18
23
|
## Usage
|
|
19
24
|
|
|
20
|
-
The adapter is initialized as a plugin with your Google Drive
|
|
25
|
+
The adapter is initialized as a plugin with your Google Drive access token.
|
|
21
26
|
|
|
22
27
|
```typescript
|
|
23
28
|
import PouchDB from 'pouchdb-core';
|
|
24
29
|
import GoogleDriveAdapter from '@docstack/pouchdb-adapter-googledrive';
|
|
25
|
-
import { google } from 'googleapis';
|
|
26
|
-
|
|
27
|
-
// 1. Setup Google Drive Client
|
|
28
|
-
const oauth2Client = new google.auth.OAuth2(CLIENT_ID, SECRET, REDIRECT);
|
|
29
|
-
oauth2Client.setCredentials({ access_token: '...' });
|
|
30
|
-
const drive = google.drive({ version: 'v3', auth: oauth2Client });
|
|
31
30
|
|
|
32
|
-
//
|
|
31
|
+
// 1. Initialize the Adapter Plugin Factory
|
|
33
32
|
const adapterPlugin = GoogleDriveAdapter({
|
|
34
|
-
|
|
35
|
-
folderName: 'my-app-db-folder', // Root folder
|
|
36
|
-
pollingIntervalMs: 2000
|
|
33
|
+
accessToken: 'YOUR_GOOGLE_ACCESS_TOKEN',
|
|
34
|
+
folderName: 'my-app-db-folder', // Root folder in Drive
|
|
35
|
+
pollingIntervalMs: 2000 // Optional: check for remote changes
|
|
37
36
|
});
|
|
38
37
|
|
|
39
|
-
//
|
|
38
|
+
// 2. Register Plugin
|
|
40
39
|
PouchDB.plugin(adapterPlugin);
|
|
41
|
-
// Also needs replication plugin if using replicate()
|
|
42
|
-
// PouchDB.plugin(require('pouchdb-replication'));
|
|
43
40
|
|
|
44
|
-
//
|
|
45
|
-
// No need to pass 'drive' here anymore!
|
|
41
|
+
// 3. Create Database
|
|
46
42
|
const db = new PouchDB('user_db', {
|
|
47
43
|
adapter: 'googledrive'
|
|
48
44
|
});
|
|
@@ -50,6 +46,20 @@ const db = new PouchDB('user_db', {
|
|
|
50
46
|
await db.post({ title: 'Hello World' });
|
|
51
47
|
```
|
|
52
48
|
|
|
49
|
+
### Dynamic Tokens
|
|
50
|
+
|
|
51
|
+
If your token expires, you can provide an async function that returns a valid token:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const adapterPlugin = GoogleDriveAdapter({
|
|
55
|
+
accessToken: async () => {
|
|
56
|
+
const session = await getMySession();
|
|
57
|
+
return session.accessToken;
|
|
58
|
+
},
|
|
59
|
+
folderName: 'my-app-db'
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
53
63
|
## Architecture
|
|
54
64
|
|
|
55
65
|
The adapter implements a **"Remote-First"** architecture:
|
package/lib/client.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
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
|
+
if (!res.ok) {
|
|
22
|
+
// Basic error handling
|
|
23
|
+
const text = await res.text();
|
|
24
|
+
let errorMsg = `Drive API Error: ${res.status} ${res.statusText}`;
|
|
25
|
+
try {
|
|
26
|
+
const json = JSON.parse(text);
|
|
27
|
+
if (json.error && json.error.message) {
|
|
28
|
+
errorMsg += ` - ${json.error.message}`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch { }
|
|
32
|
+
const err = new Error(errorMsg);
|
|
33
|
+
err.status = res.status;
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
return res;
|
|
37
|
+
}
|
|
38
|
+
async listFiles(q) {
|
|
39
|
+
const params = new URLSearchParams({
|
|
40
|
+
q,
|
|
41
|
+
fields: 'files(id, name, mimeType, parents, etag)',
|
|
42
|
+
spaces: 'drive',
|
|
43
|
+
pageSize: '1000' // Ensure we get enough
|
|
44
|
+
});
|
|
45
|
+
const res = await this.fetch(`${BASE_URL}?${params.toString()}`, { method: 'GET' });
|
|
46
|
+
const data = await res.json();
|
|
47
|
+
return data.files || [];
|
|
48
|
+
}
|
|
49
|
+
async getFile(fileId) {
|
|
50
|
+
// Try getting media
|
|
51
|
+
try {
|
|
52
|
+
const params = new URLSearchParams({ alt: 'media' });
|
|
53
|
+
const res = await this.fetch(`${BASE_URL}/${fileId}?${params.toString()}`, { method: 'GET' });
|
|
54
|
+
// Standard fetch handles JSON/Text transparency?
|
|
55
|
+
// We expect JSON mostly, but sometimes we might want text.
|
|
56
|
+
// PouchDB adapter flow: downloadJson, downloadNdjson
|
|
57
|
+
// Let's rely on content-type or caller expectation?
|
|
58
|
+
// The usage in `drive.ts` expects parsed JSON/NDJSON lines.
|
|
59
|
+
// Let's return the raw Text or JSON based on Content-Type?
|
|
60
|
+
const contentType = res.headers.get('content-type');
|
|
61
|
+
if (contentType && contentType.includes('application/json')) {
|
|
62
|
+
return await res.json();
|
|
63
|
+
}
|
|
64
|
+
return await res.text();
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
throw e;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Single metadata get (for etag check)
|
|
71
|
+
async getFileMetadata(fileId) {
|
|
72
|
+
const params = new URLSearchParams({ fields: 'id, name, mimeType, parents, etag' });
|
|
73
|
+
const res = await this.fetch(`${BASE_URL}/${fileId}?${params.toString()}`, { method: 'GET' });
|
|
74
|
+
return await res.json();
|
|
75
|
+
}
|
|
76
|
+
async createFile(name, parents, mimeType, content) {
|
|
77
|
+
const metadata = {
|
|
78
|
+
name,
|
|
79
|
+
mimeType,
|
|
80
|
+
parents
|
|
81
|
+
};
|
|
82
|
+
const multipartBody = this.buildMultipart(metadata, content, mimeType);
|
|
83
|
+
const res = await this.fetch(`${UPLOAD_URL}?uploadType=multipart&fields=id,etag`, {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: {
|
|
86
|
+
'Content-Type': `multipart/related; boundary=${multipartBody.boundary}`
|
|
87
|
+
},
|
|
88
|
+
body: multipartBody.body
|
|
89
|
+
});
|
|
90
|
+
return await res.json();
|
|
91
|
+
}
|
|
92
|
+
async updateFile(fileId, content, expectedEtag) {
|
|
93
|
+
// Update content (media) usually, but sometimes meta?
|
|
94
|
+
// In our usage (saveMeta), we update body.
|
|
95
|
+
const res = await this.fetch(`${UPLOAD_URL}/${fileId}?uploadType=media&fields=id,etag`, {
|
|
96
|
+
method: 'PATCH',
|
|
97
|
+
headers: expectedEtag ? { 'If-Match': expectedEtag, 'Content-Type': 'application/json' } : { 'Content-Type': 'application/json' },
|
|
98
|
+
body: content
|
|
99
|
+
});
|
|
100
|
+
return await res.json();
|
|
101
|
+
}
|
|
102
|
+
async deleteFile(fileId) {
|
|
103
|
+
await this.fetch(`${BASE_URL}/${fileId}`, { method: 'DELETE' });
|
|
104
|
+
}
|
|
105
|
+
buildMultipart(metadata, content, contentType) {
|
|
106
|
+
const boundary = '-------' + Math.random().toString(36).substring(2);
|
|
107
|
+
const delimiter = `\r\n--${boundary}\r\n`;
|
|
108
|
+
const closeDelimiter = `\r\n--${boundary}--`;
|
|
109
|
+
const body = delimiter +
|
|
110
|
+
'Content-Type: application/json\r\n\r\n' +
|
|
111
|
+
JSON.stringify(metadata) +
|
|
112
|
+
delimiter +
|
|
113
|
+
`Content-Type: ${contentType}\r\n\r\n` +
|
|
114
|
+
content +
|
|
115
|
+
closeDelimiter;
|
|
116
|
+
return { body, boundary };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
exports.GoogleDriveClient = GoogleDriveClient;
|
package/lib/drive.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { GoogleDriveAdapterOptions, ChangeEntry, IndexEntry } from './types';
|
|
|
10
10
|
* └── changes-*.ndjson # Append logs
|
|
11
11
|
*/
|
|
12
12
|
export declare class DriveHandler {
|
|
13
|
-
private
|
|
13
|
+
private client;
|
|
14
14
|
private folderId;
|
|
15
15
|
private folderName;
|
|
16
16
|
private parents;
|
package/lib/drive.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DriveHandler = void 0;
|
|
4
4
|
const cache_1 = require("./cache");
|
|
5
|
+
const client_1 = require("./client");
|
|
5
6
|
const DEFAULT_COMPACTION_THRESHOLD = 100; // entries
|
|
6
7
|
const DEFAULT_SIZE_THRESHOLD = 1024 * 1024; // 1MB
|
|
7
8
|
const DEFAULT_CACHE_SIZE = 1000; // Number of docs
|
|
@@ -32,7 +33,7 @@ class DriveHandler {
|
|
|
32
33
|
this.currentLogSizeEstimate = 0;
|
|
33
34
|
this.listeners = [];
|
|
34
35
|
this.pollingInterval = null;
|
|
35
|
-
this.
|
|
36
|
+
this.client = new client_1.GoogleDriveClient(options);
|
|
36
37
|
this.folderId = options.folderId || null;
|
|
37
38
|
this.folderName = options.folderName || dbName;
|
|
38
39
|
this.parents = options.parents || [];
|
|
@@ -267,7 +268,7 @@ class DriveHandler {
|
|
|
267
268
|
return await this.tryAppendChanges(changes);
|
|
268
269
|
}
|
|
269
270
|
catch (err) {
|
|
270
|
-
if (err.
|
|
271
|
+
if (err.status === 412 || err.status === 409) {
|
|
271
272
|
// Reload and RETRY
|
|
272
273
|
await this.load();
|
|
273
274
|
// Check conflicts against Index (Metadata sufficient)
|
|
@@ -359,16 +360,8 @@ class DriveHandler {
|
|
|
359
360
|
});
|
|
360
361
|
// 2. Upload Data File
|
|
361
362
|
const dataContent = JSON.stringify(snapshotData);
|
|
362
|
-
const dataRes = await this.
|
|
363
|
-
|
|
364
|
-
name: `snapshot-data-${Date.now()}.json`,
|
|
365
|
-
parents: [this.folderId],
|
|
366
|
-
mimeType: 'application/json'
|
|
367
|
-
},
|
|
368
|
-
media: { mimeType: 'application/json', body: dataContent },
|
|
369
|
-
fields: 'id'
|
|
370
|
-
});
|
|
371
|
-
const dataFileId = dataRes.data.id;
|
|
363
|
+
const dataRes = await this.client.createFile(`snapshot-data-${Date.now()}.json`, [this.folderId], 'application/json', dataContent);
|
|
364
|
+
const dataFileId = dataRes.id;
|
|
372
365
|
// 3. Create Index pointing to this Data File
|
|
373
366
|
const newIndexEntries = {};
|
|
374
367
|
for (const id of Object.keys(snapshotData.docs)) {
|
|
@@ -384,16 +377,8 @@ class DriveHandler {
|
|
|
384
377
|
createdAt: Date.now()
|
|
385
378
|
};
|
|
386
379
|
const indexContent = JSON.stringify(snapshotIndex);
|
|
387
|
-
const indexRes = await this.
|
|
388
|
-
|
|
389
|
-
name: `snapshot-index-${Date.now()}.json`,
|
|
390
|
-
parents: [this.folderId],
|
|
391
|
-
mimeType: 'application/json'
|
|
392
|
-
},
|
|
393
|
-
media: { mimeType: 'application/json', body: indexContent },
|
|
394
|
-
fields: 'id'
|
|
395
|
-
});
|
|
396
|
-
const newIndexId = indexRes.data.id;
|
|
380
|
+
const indexRes = await this.client.createFile(`snapshot-index-${Date.now()}.json`, [this.folderId], 'application/json', indexContent);
|
|
381
|
+
const newIndexId = indexRes.id;
|
|
397
382
|
// 4. Update Meta
|
|
398
383
|
await this.atomicUpdateMeta((latest) => {
|
|
399
384
|
const remainingLogs = latest.changeLogIds.filter(id => !oldLogIds.includes(id));
|
|
@@ -424,7 +409,7 @@ class DriveHandler {
|
|
|
424
409
|
return;
|
|
425
410
|
}
|
|
426
411
|
catch (err) {
|
|
427
|
-
if (err.
|
|
412
|
+
if (err.status === 412 || err.status === 409) {
|
|
428
413
|
attempt++;
|
|
429
414
|
await new Promise(r => setTimeout(r, Math.random() * 500 + 100));
|
|
430
415
|
continue;
|
|
@@ -436,44 +421,33 @@ class DriveHandler {
|
|
|
436
421
|
// Reused helpers
|
|
437
422
|
async findOrCreateFolder() {
|
|
438
423
|
const q = `name = '${this.folderName}' and mimeType = 'application/vnd.google-apps.folder' and trashed = false`;
|
|
439
|
-
const
|
|
440
|
-
if (
|
|
441
|
-
return
|
|
442
|
-
const createRes = await this.
|
|
443
|
-
|
|
444
|
-
fields: 'id'
|
|
445
|
-
});
|
|
446
|
-
return createRes.data.id;
|
|
424
|
+
const files = await this.client.listFiles(q);
|
|
425
|
+
if (files.length > 0)
|
|
426
|
+
return files[0].id;
|
|
427
|
+
const createRes = await this.client.createFile(this.folderName, this.parents.length ? this.parents : undefined, 'application/vnd.google-apps.folder', '');
|
|
428
|
+
return createRes.id;
|
|
447
429
|
}
|
|
448
430
|
async findFile(name) {
|
|
449
431
|
const q = `name = '${name}' and '${this.folderId}' in parents and trashed = false`;
|
|
450
|
-
const
|
|
451
|
-
if (
|
|
452
|
-
return { id:
|
|
432
|
+
const files = await this.client.listFiles(q);
|
|
433
|
+
if (files.length > 0)
|
|
434
|
+
return { id: files[0].id, etag: files[0].etag || '' };
|
|
453
435
|
return null;
|
|
454
436
|
}
|
|
455
437
|
async downloadJson(fileId) {
|
|
456
|
-
|
|
457
|
-
return res.data;
|
|
438
|
+
return await this.client.getFile(fileId);
|
|
458
439
|
}
|
|
459
440
|
async downloadFileAny(fileId) {
|
|
460
|
-
|
|
461
|
-
if (typeof res.data === 'string') {
|
|
462
|
-
// NDJSON or JSON string
|
|
463
|
-
try {
|
|
464
|
-
return JSON.parse(res.data);
|
|
465
|
-
}
|
|
466
|
-
catch {
|
|
467
|
-
// NDJSON?
|
|
468
|
-
const lines = res.data.trim().split('\n').filter((l) => l);
|
|
469
|
-
return lines.map((line) => JSON.parse(line));
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
return res.data;
|
|
441
|
+
return await this.client.getFile(fileId);
|
|
473
442
|
}
|
|
474
443
|
async downloadNdjson(fileId) {
|
|
475
|
-
const
|
|
476
|
-
|
|
444
|
+
const data = await this.client.getFile(fileId);
|
|
445
|
+
// data will likely be a string if NDJSON is returned and getFile sees weird content-type
|
|
446
|
+
// Or if getFile auto-parsed standard "application/json" but NDJSON is just text.
|
|
447
|
+
// Google Drive might return application/json for everything if we aren't careful?
|
|
448
|
+
// Actually .ndjson is separate.
|
|
449
|
+
// Safest: Handle string or object.
|
|
450
|
+
const content = typeof data === 'string' ? data : JSON.stringify(data);
|
|
477
451
|
const lines = content.trim().split('\n').filter((l) => l);
|
|
478
452
|
return lines.map((line) => JSON.parse(line));
|
|
479
453
|
}
|
|
@@ -481,33 +455,20 @@ class DriveHandler {
|
|
|
481
455
|
const lines = changes.map(c => JSON.stringify(c)).join('\n') + '\n';
|
|
482
456
|
const startSeq = changes[0].seq;
|
|
483
457
|
const name = `changes-${startSeq}-${Math.random().toString(36).substring(7)}.ndjson`;
|
|
484
|
-
const res = await this.
|
|
485
|
-
requestBody: { name, parents: [this.folderId], mimeType: 'application/x-ndjson' },
|
|
486
|
-
media: { mimeType: 'application/x-ndjson', body: lines },
|
|
487
|
-
fields: 'id'
|
|
488
|
-
});
|
|
458
|
+
const res = await this.client.createFile(name, [this.folderId], 'application/x-ndjson', lines);
|
|
489
459
|
this.currentLogSizeEstimate += new Blob([lines]).size;
|
|
490
|
-
return res.
|
|
460
|
+
return res.id;
|
|
491
461
|
}
|
|
492
462
|
async saveMeta(meta, expectedEtag = null) {
|
|
493
463
|
const content = JSON.stringify(meta);
|
|
494
464
|
const metaFile = await this.findFile('_meta.json');
|
|
495
465
|
if (metaFile) {
|
|
496
|
-
const res = await this.
|
|
497
|
-
|
|
498
|
-
headers: expectedEtag ? { 'If-Match': expectedEtag } : undefined,
|
|
499
|
-
media: { mimeType: 'application/json', body: content },
|
|
500
|
-
fields: 'id, etag'
|
|
501
|
-
});
|
|
502
|
-
this.metaEtag = res.data.etag;
|
|
466
|
+
const res = await this.client.updateFile(metaFile.id, content, expectedEtag || undefined);
|
|
467
|
+
this.metaEtag = res.etag;
|
|
503
468
|
}
|
|
504
469
|
else {
|
|
505
|
-
const res = await this.
|
|
506
|
-
|
|
507
|
-
media: { mimeType: 'application/json', body: content },
|
|
508
|
-
fields: 'id, etag'
|
|
509
|
-
});
|
|
510
|
-
this.metaEtag = res.data.etag;
|
|
470
|
+
const res = await this.client.createFile('_meta.json', [this.folderId], 'application/json', content);
|
|
471
|
+
this.metaEtag = res.etag;
|
|
511
472
|
}
|
|
512
473
|
}
|
|
513
474
|
async countTotalChanges() {
|
|
@@ -521,12 +482,12 @@ class DriveHandler {
|
|
|
521
482
|
async cleanupOldFiles(oldIndexId, oldLogIds) {
|
|
522
483
|
if (oldIndexId)
|
|
523
484
|
try {
|
|
524
|
-
await this.
|
|
485
|
+
await this.client.deleteFile(oldIndexId);
|
|
525
486
|
}
|
|
526
487
|
catch { }
|
|
527
488
|
for (const id of oldLogIds)
|
|
528
489
|
try {
|
|
529
|
-
await this.
|
|
490
|
+
await this.client.deleteFile(id);
|
|
530
491
|
}
|
|
531
492
|
catch { }
|
|
532
493
|
}
|
|
@@ -538,6 +499,7 @@ class DriveHandler {
|
|
|
538
499
|
const metaFile = await this.findFile('_meta.json');
|
|
539
500
|
if (!metaFile)
|
|
540
501
|
return;
|
|
502
|
+
// Etag check
|
|
541
503
|
if (metaFile.etag !== this.metaEtag) {
|
|
542
504
|
await this.load();
|
|
543
505
|
this.notifyListeners();
|
|
@@ -567,7 +529,7 @@ class DriveHandler {
|
|
|
567
529
|
stopPolling() { if (this.pollingInterval)
|
|
568
530
|
clearInterval(this.pollingInterval); }
|
|
569
531
|
async deleteFolder() { if (this.folderId)
|
|
570
|
-
await this.
|
|
532
|
+
await this.client.deleteFile(this.folderId); }
|
|
571
533
|
getNextSeq() { return this.meta.seq + 1; }
|
|
572
534
|
}
|
|
573
535
|
exports.DriveHandler = DriveHandler;
|
package/lib/types.d.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export type DriveClient = any;
|
|
1
|
+
import { DriveClientOptions } from './client';
|
|
3
2
|
/** Options for configuring the Google Drive adapter */
|
|
4
|
-
export interface GoogleDriveAdapterOptions {
|
|
5
|
-
/** Configured Google Drive client (googleapis) */
|
|
6
|
-
drive: DriveClient;
|
|
3
|
+
export interface GoogleDriveAdapterOptions extends DriveClientOptions {
|
|
7
4
|
/** Specific folder ID to use as the DB root */
|
|
8
5
|
folderId?: string;
|
|
9
6
|
/** Folder name to search/create if no ID provided */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docstack/pouchdb-adapter-googledrive",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "PouchDB adapter for Google Drive",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -23,8 +23,10 @@
|
|
|
23
23
|
"url": "https://github.com/onyx-ac/docstack-pouchdb-adapter-gdrive/issues"
|
|
24
24
|
},
|
|
25
25
|
"homepage": "https://onyx.ac/docstack",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18"
|
|
28
|
+
},
|
|
26
29
|
"dependencies": {
|
|
27
|
-
"googleapis": "^126.0.0",
|
|
28
30
|
"pouchdb-core": "^7.3.1"
|
|
29
31
|
},
|
|
30
32
|
"devDependencies": {
|