@bobfrankston/gcards 0.1.18 → 0.1.20
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/gcards.code-workspace +33 -0
- package/gcards.ts +35 -6
- package/package.json +3 -2
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"folders": [
|
|
3
|
+
{
|
|
4
|
+
"path": "."
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"path": "../../../projects/OAuth/OauthSupport"
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"settings": {
|
|
11
|
+
"workbench.colorTheme": "Visual Studio 2019 Dark",
|
|
12
|
+
"workbench.colorCustomizations": {
|
|
13
|
+
"activityBar.activeBackground": "#0ae04a",
|
|
14
|
+
"activityBar.background": "#0ae04a",
|
|
15
|
+
"activityBar.foreground": "#15202b",
|
|
16
|
+
"activityBar.inactiveForeground": "#15202b99",
|
|
17
|
+
"activityBarBadge.background": "#9266f8",
|
|
18
|
+
"activityBarBadge.foreground": "#15202b",
|
|
19
|
+
"commandCenter.border": "#e7e7e799",
|
|
20
|
+
"sash.hoverBorder": "#0ae04a",
|
|
21
|
+
"statusBar.background": "#08af3a",
|
|
22
|
+
"statusBar.foreground": "#e7e7e7",
|
|
23
|
+
"statusBarItem.hoverBackground": "#0ae04a",
|
|
24
|
+
"statusBarItem.remoteBackground": "#08af3a",
|
|
25
|
+
"statusBarItem.remoteForeground": "#e7e7e7",
|
|
26
|
+
"titleBar.activeBackground": "#08af3a",
|
|
27
|
+
"titleBar.activeForeground": "#e7e7e7",
|
|
28
|
+
"titleBar.inactiveBackground": "#08af3a99",
|
|
29
|
+
"titleBar.inactiveForeground": "#e7e7e799"
|
|
30
|
+
},
|
|
31
|
+
"peacock.color": "#08af3a"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/gcards.ts
CHANGED
|
@@ -79,7 +79,9 @@ function cleanupEscapeHandler(): void {
|
|
|
79
79
|
process.stdin.pause();
|
|
80
80
|
process.stdin.removeAllListeners('data');
|
|
81
81
|
}
|
|
82
|
-
process.stdin.unref
|
|
82
|
+
if (typeof process.stdin.unref === 'function') {
|
|
83
|
+
process.stdin.unref(); // Allow Node to exit even if stdin is open
|
|
84
|
+
}
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
async function getAccessToken(user: string, writeAccess = false, forceRefresh = false): Promise<string> {
|
|
@@ -297,8 +299,15 @@ async function fetchContactsWithRetry(
|
|
|
297
299
|
});
|
|
298
300
|
|
|
299
301
|
if (response.status === 410) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
+
throw new Error('SYNC_TOKEN_EXPIRED');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (response.status === 400) {
|
|
306
|
+
const text = await response.text();
|
|
307
|
+
if (text.includes('EXPIRED_SYNC_TOKEN')) {
|
|
308
|
+
throw new Error('SYNC_TOKEN_EXPIRED');
|
|
309
|
+
}
|
|
310
|
+
throw new Error(`API error: ${response.status} ${text}`);
|
|
302
311
|
}
|
|
303
312
|
|
|
304
313
|
if (response.status === 429) {
|
|
@@ -391,12 +400,30 @@ async function syncContacts(user: string, options: { full: boolean; verbose: boo
|
|
|
391
400
|
let deleted = 0;
|
|
392
401
|
let conflicts = 0;
|
|
393
402
|
let pageNum = 0;
|
|
403
|
+
let hasMore = true;
|
|
394
404
|
|
|
395
|
-
|
|
405
|
+
while (hasMore) {
|
|
396
406
|
pageNum++;
|
|
397
407
|
process.stdout.write(`\rFetching page ${pageNum}... (${totalProcessed} contacts so far)`);
|
|
398
408
|
|
|
399
|
-
|
|
409
|
+
let response: GoogleConnectionsResponse;
|
|
410
|
+
try {
|
|
411
|
+
response = await fetchContactsWithRetry(accessToken, syncToken || undefined, pageToken);
|
|
412
|
+
} catch (err) {
|
|
413
|
+
if (err instanceof Error && err.message === 'SYNC_TOKEN_EXPIRED') {
|
|
414
|
+
console.log('\nSync token expired, restarting with full sync...');
|
|
415
|
+
syncToken = null;
|
|
416
|
+
pageToken = undefined;
|
|
417
|
+
pageNum = 0;
|
|
418
|
+
totalProcessed = 0;
|
|
419
|
+
added = 0;
|
|
420
|
+
updated = 0;
|
|
421
|
+
deleted = 0;
|
|
422
|
+
conflicts = 0;
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
throw err;
|
|
426
|
+
}
|
|
400
427
|
|
|
401
428
|
if (response.connections) {
|
|
402
429
|
for (const person of response.connections) {
|
|
@@ -520,7 +547,9 @@ async function syncContacts(user: string, options: { full: boolean; verbose: boo
|
|
|
520
547
|
console.log('\n\nStopped by user. Progress saved.');
|
|
521
548
|
break;
|
|
522
549
|
}
|
|
523
|
-
|
|
550
|
+
|
|
551
|
+
hasMore = !!pageToken;
|
|
552
|
+
}
|
|
524
553
|
|
|
525
554
|
const activeContacts = Object.keys(index.contacts).length;
|
|
526
555
|
const tombstones = Object.keys(deletedIndex.deleted).length;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/gcards",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"description": "Google Contacts cleanup and management tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "gcards.ts",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"prerelease:local": "git add -A && (git diff-index --quiet HEAD || git commit -m \"Pre-release commit\")",
|
|
18
18
|
"preversion": "npm run check && git add -A",
|
|
19
19
|
"postversion": "git push && git push --tags",
|
|
20
|
-
"release": "npm whoami && npm run prerelease:local && npm version patch && npm publish --access public"
|
|
20
|
+
"release": "npm whoami && npm run prerelease:local && npm version patch && npm publish --access public",
|
|
21
|
+
"installer": "npm run release && npm install -g @bobfrankston/gcards"
|
|
21
22
|
},
|
|
22
23
|
"keywords": [
|
|
23
24
|
"google",
|