@bobfrankston/gcards 0.1.31 → 0.1.32
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.ts +2 -2
- package/gfix.ts +49 -28
- package/package.json +2 -2
package/gcards.ts
CHANGED
|
@@ -129,7 +129,7 @@ async function refreshAccessToken(): Promise<string> {
|
|
|
129
129
|
return currentAccessToken;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
/** Get sort key from contact: fileAs (First
|
|
132
|
+
/** Get sort key from contact: fileAs (Last, First) > displayNameLastFirst (Last, First) > displayName */
|
|
133
133
|
function getSortKey(person: GooglePerson): string {
|
|
134
134
|
const fileAs = person.fileAses?.[0]?.value;
|
|
135
135
|
if (fileAs) return fileAs;
|
|
@@ -139,7 +139,7 @@ function getSortKey(person: GooglePerson): string {
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
function saveIndex(paths: UserPaths, index: ContactIndex): void {
|
|
142
|
-
// Sort contacts by sortKey (fileAs='First
|
|
142
|
+
// Sort contacts by sortKey (fileAs='Last, First' > displayNameLastFirst='Last, First' > displayName)
|
|
143
143
|
const sortedContacts: Record<string, IndexEntry> = {};
|
|
144
144
|
const entries = Object.entries(index.contacts);
|
|
145
145
|
entries.sort((a, b) => {
|
package/gfix.ts
CHANGED
|
@@ -395,7 +395,7 @@ async function runBirthdayExtract(user: string, mode: 'inspect' | 'apply'): Prom
|
|
|
395
395
|
}
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
-
/** Fix fileAs fields: normalize "Last, First"
|
|
398
|
+
/** Fix fileAs fields: normalize to "Last, First" format and clean spurious characters
|
|
399
399
|
* Also fix displayNameLastFirst to proper "Last, First" format */
|
|
400
400
|
async function runFileAsFix(user: string, mode: 'inspect' | 'apply'): Promise<void> {
|
|
401
401
|
const paths = getUserPaths(user);
|
|
@@ -438,42 +438,63 @@ async function runFileAsFix(user: string, mode: 'inspect' | 'apply'): Promise<vo
|
|
|
438
438
|
}
|
|
439
439
|
}
|
|
440
440
|
|
|
441
|
-
// Fix fileAs: "Last, First"
|
|
441
|
+
// Fix fileAs: should be "Last, First" format
|
|
442
442
|
if (contact.fileAses?.length) {
|
|
443
443
|
const fileAs = contact.fileAses[0].value || '';
|
|
444
|
+
const expectedFileAs = givenName && familyName ? `${familyName}, ${givenName}` : '';
|
|
444
445
|
|
|
445
|
-
// Check
|
|
446
|
-
const
|
|
447
|
-
if (
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
}
|
|
467
|
-
|
|
446
|
+
// Check for malformed fileAs (e.g., "-, Eric Giler", "- Eric", etc.)
|
|
447
|
+
const malformedMatch = fileAs.match(/^[-,\s]+(.+)$/);
|
|
448
|
+
if (malformedMatch && givenName && familyName) {
|
|
449
|
+
// Spurious dash/comma prefix
|
|
450
|
+
console.log(` ${displayName}: fileAs "${fileAs}" -> "${expectedFileAs}" (cleaned malformed)`);
|
|
451
|
+
if (mode === 'apply') {
|
|
452
|
+
contact.fileAses[0].value = expectedFileAs;
|
|
453
|
+
modified = true;
|
|
454
|
+
}
|
|
455
|
+
fileAsFixedCount++;
|
|
456
|
+
} else if (expectedFileAs && fileAs !== expectedFileAs) {
|
|
457
|
+
// Check if fileAs is "First Last" format (wrong) or just doesn't match
|
|
458
|
+
const isFirstLast = fileAs === `${givenName} ${familyName}`;
|
|
459
|
+
const commaMatch = fileAs.match(/^(.+),\s*(.+)$/);
|
|
460
|
+
|
|
461
|
+
if (isFirstLast) {
|
|
462
|
+
// Wrong format: "First Last" instead of "Last, First"
|
|
463
|
+
console.log(` ${displayName}: fileAs "${fileAs}" -> "${expectedFileAs}" (wrong format)`);
|
|
464
|
+
if (mode === 'apply') {
|
|
465
|
+
contact.fileAses[0].value = expectedFileAs;
|
|
466
|
+
modified = true;
|
|
467
|
+
}
|
|
468
|
+
fileAsFixedCount++;
|
|
469
|
+
} else if (commaMatch) {
|
|
470
|
+
const [, lastPart, firstPart] = commaMatch;
|
|
471
|
+
// Verify the parts match
|
|
472
|
+
const firstMatches = firstPart.trim().toLowerCase() === givenName.toLowerCase();
|
|
473
|
+
const lastMatches = lastPart.trim().toLowerCase() === familyName.toLowerCase();
|
|
474
|
+
|
|
475
|
+
if (!firstMatches || !lastMatches) {
|
|
476
|
+
// Parts don't match - needs review
|
|
468
477
|
needsReview.push({ givenName, familyName, fileAs, jsonFile: file });
|
|
469
478
|
skippedCount++;
|
|
470
479
|
}
|
|
471
|
-
} else {
|
|
472
|
-
//
|
|
473
|
-
needsReview.push({ givenName
|
|
480
|
+
} else if (fileAs && !commaMatch) {
|
|
481
|
+
// Not in "Last, First" format at all
|
|
482
|
+
needsReview.push({ givenName, familyName, fileAs, jsonFile: file });
|
|
474
483
|
skippedCount++;
|
|
475
484
|
}
|
|
476
485
|
}
|
|
486
|
+
} else if (givenName && familyName) {
|
|
487
|
+
// Missing fileAs - add it
|
|
488
|
+
const expectedFileAs = `${familyName}, ${givenName}`;
|
|
489
|
+
console.log(` ${displayName}: Adding fileAs "${expectedFileAs}"`);
|
|
490
|
+
if (mode === 'apply') {
|
|
491
|
+
contact.fileAses = [{
|
|
492
|
+
metadata: { primary: true },
|
|
493
|
+
value: expectedFileAs
|
|
494
|
+
}];
|
|
495
|
+
modified = true;
|
|
496
|
+
}
|
|
497
|
+
fileAsFixedCount++;
|
|
477
498
|
}
|
|
478
499
|
|
|
479
500
|
if (modified) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/gcards",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"description": "Google Contacts cleanup and management tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "gcards.ts",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"author": "Bob Frankston",
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@types/node": "^
|
|
32
|
+
"@types/node": "^25.0.3",
|
|
33
33
|
"tsx": "^4.21.0"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|