@bobfrankston/gcards 0.1.2 → 0.1.5
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/.claude/settings.local.json +9 -1
- package/.gitattributes +1 -0
- package/.vscode/settings.json +22 -2
- package/README.md +229 -61
- package/gcards.ts +728 -193
- package/gfix.ts +1585 -0
- package/glib/gctypes.ts +132 -0
- package/glib/gmerge.ts +290 -0
- package/glib/gutils.ts +143 -0
- package/{cli.ts → glib/parsecli.ts} +45 -5
- package/{types.ts → glib/types.ts} +305 -305
- package/package.json +6 -4
|
@@ -1,305 +1,305 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Google People API Type Definitions
|
|
3
|
-
* Reference: https://developers.google.com/people/api/rest/v1/people
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// ============================================================
|
|
7
|
-
// Google People API Response Types (from API)
|
|
8
|
-
// ============================================================
|
|
9
|
-
|
|
10
|
-
export interface GooglePersonMetadata {
|
|
11
|
-
sources?: Array<{
|
|
12
|
-
type: string; // "CONTACT", "PROFILE", "DOMAIN_PROFILE"
|
|
13
|
-
id: string;
|
|
14
|
-
etag?: string;
|
|
15
|
-
updateTime?: string;
|
|
16
|
-
}>;
|
|
17
|
-
previousResourceNames?: string[];
|
|
18
|
-
linkedPeopleResourceNames?: string[];
|
|
19
|
-
deleted?: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface GoogleName {
|
|
23
|
-
metadata?: FieldMetadata;
|
|
24
|
-
displayName?: string;
|
|
25
|
-
displayNameLastFirst?: string;
|
|
26
|
-
familyName?: string;
|
|
27
|
-
givenName?: string;
|
|
28
|
-
middleName?: string;
|
|
29
|
-
honorificPrefix?: string;
|
|
30
|
-
honorificSuffix?: string;
|
|
31
|
-
phoneticFullName?: string;
|
|
32
|
-
phoneticFamilyName?: string;
|
|
33
|
-
phoneticGivenName?: string;
|
|
34
|
-
phoneticMiddleName?: string;
|
|
35
|
-
phoneticHonorificPrefix?: string;
|
|
36
|
-
phoneticHonorificSuffix?: string;
|
|
37
|
-
unstructuredName?: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface GoogleNickname {
|
|
41
|
-
metadata?: FieldMetadata;
|
|
42
|
-
value?: string;
|
|
43
|
-
type?: string; // "DEFAULT", "MAIDEN_NAME", "INITIALS", etc.
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface GoogleEmailAddress {
|
|
47
|
-
metadata?: FieldMetadata;
|
|
48
|
-
value?: string;
|
|
49
|
-
type?: string; // "home", "work", "other"
|
|
50
|
-
formattedType?: string;
|
|
51
|
-
displayName?: string;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface GooglePhoneNumber {
|
|
55
|
-
metadata?: FieldMetadata;
|
|
56
|
-
value?: string;
|
|
57
|
-
canonicalForm?: string; // E.164 format
|
|
58
|
-
type?: string; // "home", "work", "mobile", etc.
|
|
59
|
-
formattedType?: string;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export interface GoogleAddress {
|
|
63
|
-
metadata?: FieldMetadata;
|
|
64
|
-
formattedValue?: string;
|
|
65
|
-
type?: string;
|
|
66
|
-
formattedType?: string;
|
|
67
|
-
poBox?: string;
|
|
68
|
-
streetAddress?: string;
|
|
69
|
-
extendedAddress?: string;
|
|
70
|
-
city?: string;
|
|
71
|
-
region?: string;
|
|
72
|
-
postalCode?: string;
|
|
73
|
-
country?: string;
|
|
74
|
-
countryCode?: string;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface GoogleOrganization {
|
|
78
|
-
metadata?: FieldMetadata;
|
|
79
|
-
type?: string;
|
|
80
|
-
formattedType?: string;
|
|
81
|
-
name?: string;
|
|
82
|
-
department?: string;
|
|
83
|
-
title?: string;
|
|
84
|
-
jobDescription?: string;
|
|
85
|
-
symbol?: string;
|
|
86
|
-
phoneticName?: string;
|
|
87
|
-
location?: string;
|
|
88
|
-
startDate?: GoogleDate;
|
|
89
|
-
endDate?: GoogleDate;
|
|
90
|
-
current?: boolean;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export interface GoogleBiography {
|
|
94
|
-
metadata?: FieldMetadata;
|
|
95
|
-
value?: string;
|
|
96
|
-
contentType?: string; // "TEXT_PLAIN", "TEXT_HTML"
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export interface GoogleUrl {
|
|
100
|
-
metadata?: FieldMetadata;
|
|
101
|
-
value?: string;
|
|
102
|
-
type?: string;
|
|
103
|
-
formattedType?: string;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface GooglePhoto {
|
|
107
|
-
metadata?: FieldMetadata;
|
|
108
|
-
url?: string;
|
|
109
|
-
default?: boolean;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export interface GoogleBirthday {
|
|
113
|
-
metadata?: FieldMetadata;
|
|
114
|
-
date?: GoogleDate;
|
|
115
|
-
text?: string;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export interface GoogleDate {
|
|
119
|
-
year?: number;
|
|
120
|
-
month?: number;
|
|
121
|
-
day?: number;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export interface GoogleEvent {
|
|
125
|
-
metadata?: FieldMetadata;
|
|
126
|
-
date?: GoogleDate;
|
|
127
|
-
type?: string;
|
|
128
|
-
formattedType?: string;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export interface GoogleRelation {
|
|
132
|
-
metadata?: FieldMetadata;
|
|
133
|
-
person?: string;
|
|
134
|
-
type?: string;
|
|
135
|
-
formattedType?: string;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export interface GoogleMembership {
|
|
139
|
-
metadata?: FieldMetadata;
|
|
140
|
-
contactGroupMembership?: {
|
|
141
|
-
contactGroupId?: string;
|
|
142
|
-
contactGroupResourceName?: string;
|
|
143
|
-
};
|
|
144
|
-
domainMembership?: {
|
|
145
|
-
inViewerDomain?: boolean;
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export interface GoogleUserDefined {
|
|
150
|
-
metadata?: FieldMetadata;
|
|
151
|
-
key?: string;
|
|
152
|
-
value?: string;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export interface FieldMetadata {
|
|
156
|
-
primary?: boolean;
|
|
157
|
-
verified?: boolean;
|
|
158
|
-
source?: {
|
|
159
|
-
type: string;
|
|
160
|
-
id: string;
|
|
161
|
-
};
|
|
162
|
-
sourcePrimary?: boolean;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Full Google Person resource as returned by People API
|
|
167
|
-
*/
|
|
168
|
-
export interface GooglePerson {
|
|
169
|
-
resourceName: string; // e.g., "people/c1234567890"
|
|
170
|
-
etag?: string;
|
|
171
|
-
metadata?: GooglePersonMetadata;
|
|
172
|
-
names?: GoogleName[];
|
|
173
|
-
nicknames?: GoogleNickname[];
|
|
174
|
-
photos?: GooglePhoto[];
|
|
175
|
-
birthdays?: GoogleBirthday[];
|
|
176
|
-
addresses?: GoogleAddress[];
|
|
177
|
-
emailAddresses?: GoogleEmailAddress[];
|
|
178
|
-
phoneNumbers?: GooglePhoneNumber[];
|
|
179
|
-
organizations?: GoogleOrganization[];
|
|
180
|
-
biographies?: GoogleBiography[];
|
|
181
|
-
urls?: GoogleUrl[];
|
|
182
|
-
events?: GoogleEvent[];
|
|
183
|
-
relations?: GoogleRelation[];
|
|
184
|
-
memberships?: GoogleMembership[];
|
|
185
|
-
userDefined?: GoogleUserDefined[];
|
|
186
|
-
imClients?: Array<{
|
|
187
|
-
metadata?: FieldMetadata;
|
|
188
|
-
username?: string;
|
|
189
|
-
type?: string;
|
|
190
|
-
formattedType?: string;
|
|
191
|
-
protocol?: string;
|
|
192
|
-
formattedProtocol?: string;
|
|
193
|
-
}>;
|
|
194
|
-
sipAddresses?: Array<{
|
|
195
|
-
metadata?: FieldMetadata;
|
|
196
|
-
value?: string;
|
|
197
|
-
type?: string;
|
|
198
|
-
formattedType?: string;
|
|
199
|
-
}>;
|
|
200
|
-
fileAses?: Array<{
|
|
201
|
-
metadata?: FieldMetadata;
|
|
202
|
-
value?: string;
|
|
203
|
-
}>;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export interface GoogleConnectionsResponse {
|
|
207
|
-
connections?: GooglePerson[];
|
|
208
|
-
nextPageToken?: string;
|
|
209
|
-
nextSyncToken?: string;
|
|
210
|
-
totalPeople?: number;
|
|
211
|
-
totalItems?: number;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// ============================================================
|
|
215
|
-
// Local Simplified Format (for analysis/cleanup)
|
|
216
|
-
// ============================================================
|
|
217
|
-
|
|
218
|
-
export interface LocalContact {
|
|
219
|
-
// Identity & reconciliation
|
|
220
|
-
resourceName: string; // From Google - used for updates
|
|
221
|
-
etag?: string; // For conflict detection
|
|
222
|
-
|
|
223
|
-
// Core identity
|
|
224
|
-
displayName: string;
|
|
225
|
-
givenName?: string;
|
|
226
|
-
familyName?: string;
|
|
227
|
-
nickname?: string;
|
|
228
|
-
|
|
229
|
-
// Contact info (flattened, typed arrays)
|
|
230
|
-
emails: Array<{ value: string; type?: string; primary?: boolean }>;
|
|
231
|
-
phones: Array<{ value: string; type?: string; primary?: boolean }>;
|
|
232
|
-
addresses: Array<{
|
|
233
|
-
formatted?: string;
|
|
234
|
-
street?: string;
|
|
235
|
-
city?: string;
|
|
236
|
-
region?: string;
|
|
237
|
-
postalCode?: string;
|
|
238
|
-
country?: string;
|
|
239
|
-
type?: string;
|
|
240
|
-
}>;
|
|
241
|
-
|
|
242
|
-
// Professional
|
|
243
|
-
organizations: Array<{
|
|
244
|
-
name?: string;
|
|
245
|
-
title?: string;
|
|
246
|
-
department?: string;
|
|
247
|
-
}>;
|
|
248
|
-
|
|
249
|
-
// Social/web
|
|
250
|
-
urls: Array<{ value: string; type?: string }>;
|
|
251
|
-
|
|
252
|
-
// Dates
|
|
253
|
-
birthday?: { year?: number; month?: number; day?: number };
|
|
254
|
-
|
|
255
|
-
// Notes
|
|
256
|
-
notes?: string;
|
|
257
|
-
|
|
258
|
-
// Groups
|
|
259
|
-
groups: string[]; // contactGroupResourceNames
|
|
260
|
-
|
|
261
|
-
// Metadata for cleanup
|
|
262
|
-
_meta: {
|
|
263
|
-
sourceType: string; // "CONTACT", "PROFILE", etc.
|
|
264
|
-
updateTime?: string;
|
|
265
|
-
isTestEntry?: boolean; // Flagged as test (m-XXXX@bob.ma pattern)
|
|
266
|
-
duplicateOf?: string[]; // resourceNames of potential duplicates
|
|
267
|
-
mergeConfidence?: number; // 0-1 confidence for merge candidates
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// ============================================================
|
|
272
|
-
// Cleanup Analysis Types
|
|
273
|
-
// ============================================================
|
|
274
|
-
|
|
275
|
-
export interface DuplicateGroup {
|
|
276
|
-
primary: string; // resourceName of "best" contact
|
|
277
|
-
duplicates: string[]; // resourceNames to merge into primary
|
|
278
|
-
confidence: number; // 0-1 merge confidence
|
|
279
|
-
matchReasons: string[]; // Why these are considered duplicates
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
export interface CleanupReport {
|
|
283
|
-
totalContacts: number;
|
|
284
|
-
testEntries: LocalContact[]; // To be deleted
|
|
285
|
-
duplicateGroups: DuplicateGroup[];
|
|
286
|
-
emptyContacts: LocalContact[]; // No useful info
|
|
287
|
-
suggestions: string[]; // Human-readable recommendations
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// ============================================================
|
|
291
|
-
// Deletion Logging (for tracking cleanup history)
|
|
292
|
-
// ============================================================
|
|
293
|
-
|
|
294
|
-
export interface DeletionRecord {
|
|
295
|
-
resourceName: string;
|
|
296
|
-
displayName: string;
|
|
297
|
-
reason: string; // "test_entry", "duplicate_of:people/cXXX", "empty", etc.
|
|
298
|
-
deletedAt: string; // ISO timestamp
|
|
299
|
-
originalData: LocalContact; // Snapshot before deletion
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
export interface DeletionLog {
|
|
303
|
-
deletions: DeletionRecord[];
|
|
304
|
-
lastUpdated: string; // ISO timestamp
|
|
305
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Google People API Type Definitions
|
|
3
|
+
* Reference: https://developers.google.com/people/api/rest/v1/people
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================
|
|
7
|
+
// Google People API Response Types (from API)
|
|
8
|
+
// ============================================================
|
|
9
|
+
|
|
10
|
+
export interface GooglePersonMetadata {
|
|
11
|
+
sources?: Array<{
|
|
12
|
+
type: string; // "CONTACT", "PROFILE", "DOMAIN_PROFILE"
|
|
13
|
+
id: string;
|
|
14
|
+
etag?: string;
|
|
15
|
+
updateTime?: string;
|
|
16
|
+
}>;
|
|
17
|
+
previousResourceNames?: string[];
|
|
18
|
+
linkedPeopleResourceNames?: string[];
|
|
19
|
+
deleted?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface GoogleName {
|
|
23
|
+
metadata?: FieldMetadata;
|
|
24
|
+
displayName?: string;
|
|
25
|
+
displayNameLastFirst?: string;
|
|
26
|
+
familyName?: string;
|
|
27
|
+
givenName?: string;
|
|
28
|
+
middleName?: string;
|
|
29
|
+
honorificPrefix?: string;
|
|
30
|
+
honorificSuffix?: string;
|
|
31
|
+
phoneticFullName?: string;
|
|
32
|
+
phoneticFamilyName?: string;
|
|
33
|
+
phoneticGivenName?: string;
|
|
34
|
+
phoneticMiddleName?: string;
|
|
35
|
+
phoneticHonorificPrefix?: string;
|
|
36
|
+
phoneticHonorificSuffix?: string;
|
|
37
|
+
unstructuredName?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface GoogleNickname {
|
|
41
|
+
metadata?: FieldMetadata;
|
|
42
|
+
value?: string;
|
|
43
|
+
type?: string; // "DEFAULT", "MAIDEN_NAME", "INITIALS", etc.
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface GoogleEmailAddress {
|
|
47
|
+
metadata?: FieldMetadata;
|
|
48
|
+
value?: string;
|
|
49
|
+
type?: string; // "home", "work", "other"
|
|
50
|
+
formattedType?: string;
|
|
51
|
+
displayName?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface GooglePhoneNumber {
|
|
55
|
+
metadata?: FieldMetadata;
|
|
56
|
+
value?: string;
|
|
57
|
+
canonicalForm?: string; // E.164 format
|
|
58
|
+
type?: string; // "home", "work", "mobile", etc.
|
|
59
|
+
formattedType?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface GoogleAddress {
|
|
63
|
+
metadata?: FieldMetadata;
|
|
64
|
+
formattedValue?: string;
|
|
65
|
+
type?: string;
|
|
66
|
+
formattedType?: string;
|
|
67
|
+
poBox?: string;
|
|
68
|
+
streetAddress?: string;
|
|
69
|
+
extendedAddress?: string;
|
|
70
|
+
city?: string;
|
|
71
|
+
region?: string;
|
|
72
|
+
postalCode?: string;
|
|
73
|
+
country?: string;
|
|
74
|
+
countryCode?: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface GoogleOrganization {
|
|
78
|
+
metadata?: FieldMetadata;
|
|
79
|
+
type?: string;
|
|
80
|
+
formattedType?: string;
|
|
81
|
+
name?: string;
|
|
82
|
+
department?: string;
|
|
83
|
+
title?: string;
|
|
84
|
+
jobDescription?: string;
|
|
85
|
+
symbol?: string;
|
|
86
|
+
phoneticName?: string;
|
|
87
|
+
location?: string;
|
|
88
|
+
startDate?: GoogleDate;
|
|
89
|
+
endDate?: GoogleDate;
|
|
90
|
+
current?: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface GoogleBiography {
|
|
94
|
+
metadata?: FieldMetadata;
|
|
95
|
+
value?: string;
|
|
96
|
+
contentType?: string; // "TEXT_PLAIN", "TEXT_HTML"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface GoogleUrl {
|
|
100
|
+
metadata?: FieldMetadata;
|
|
101
|
+
value?: string;
|
|
102
|
+
type?: string;
|
|
103
|
+
formattedType?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface GooglePhoto {
|
|
107
|
+
metadata?: FieldMetadata;
|
|
108
|
+
url?: string;
|
|
109
|
+
default?: boolean;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface GoogleBirthday {
|
|
113
|
+
metadata?: FieldMetadata;
|
|
114
|
+
date?: GoogleDate;
|
|
115
|
+
text?: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface GoogleDate {
|
|
119
|
+
year?: number;
|
|
120
|
+
month?: number;
|
|
121
|
+
day?: number;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface GoogleEvent {
|
|
125
|
+
metadata?: FieldMetadata;
|
|
126
|
+
date?: GoogleDate;
|
|
127
|
+
type?: string;
|
|
128
|
+
formattedType?: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface GoogleRelation {
|
|
132
|
+
metadata?: FieldMetadata;
|
|
133
|
+
person?: string;
|
|
134
|
+
type?: string;
|
|
135
|
+
formattedType?: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface GoogleMembership {
|
|
139
|
+
metadata?: FieldMetadata;
|
|
140
|
+
contactGroupMembership?: {
|
|
141
|
+
contactGroupId?: string;
|
|
142
|
+
contactGroupResourceName?: string;
|
|
143
|
+
};
|
|
144
|
+
domainMembership?: {
|
|
145
|
+
inViewerDomain?: boolean;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface GoogleUserDefined {
|
|
150
|
+
metadata?: FieldMetadata;
|
|
151
|
+
key?: string;
|
|
152
|
+
value?: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface FieldMetadata {
|
|
156
|
+
primary?: boolean;
|
|
157
|
+
verified?: boolean;
|
|
158
|
+
source?: {
|
|
159
|
+
type: string;
|
|
160
|
+
id: string;
|
|
161
|
+
};
|
|
162
|
+
sourcePrimary?: boolean;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Full Google Person resource as returned by People API
|
|
167
|
+
*/
|
|
168
|
+
export interface GooglePerson {
|
|
169
|
+
resourceName: string; // e.g., "people/c1234567890"
|
|
170
|
+
etag?: string;
|
|
171
|
+
metadata?: GooglePersonMetadata;
|
|
172
|
+
names?: GoogleName[];
|
|
173
|
+
nicknames?: GoogleNickname[];
|
|
174
|
+
photos?: GooglePhoto[];
|
|
175
|
+
birthdays?: GoogleBirthday[];
|
|
176
|
+
addresses?: GoogleAddress[];
|
|
177
|
+
emailAddresses?: GoogleEmailAddress[];
|
|
178
|
+
phoneNumbers?: GooglePhoneNumber[];
|
|
179
|
+
organizations?: GoogleOrganization[];
|
|
180
|
+
biographies?: GoogleBiography[];
|
|
181
|
+
urls?: GoogleUrl[];
|
|
182
|
+
events?: GoogleEvent[];
|
|
183
|
+
relations?: GoogleRelation[];
|
|
184
|
+
memberships?: GoogleMembership[];
|
|
185
|
+
userDefined?: GoogleUserDefined[];
|
|
186
|
+
imClients?: Array<{
|
|
187
|
+
metadata?: FieldMetadata;
|
|
188
|
+
username?: string;
|
|
189
|
+
type?: string;
|
|
190
|
+
formattedType?: string;
|
|
191
|
+
protocol?: string;
|
|
192
|
+
formattedProtocol?: string;
|
|
193
|
+
}>;
|
|
194
|
+
sipAddresses?: Array<{
|
|
195
|
+
metadata?: FieldMetadata;
|
|
196
|
+
value?: string;
|
|
197
|
+
type?: string;
|
|
198
|
+
formattedType?: string;
|
|
199
|
+
}>;
|
|
200
|
+
fileAses?: Array<{
|
|
201
|
+
metadata?: FieldMetadata;
|
|
202
|
+
value?: string;
|
|
203
|
+
}>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface GoogleConnectionsResponse {
|
|
207
|
+
connections?: GooglePerson[];
|
|
208
|
+
nextPageToken?: string;
|
|
209
|
+
nextSyncToken?: string;
|
|
210
|
+
totalPeople?: number;
|
|
211
|
+
totalItems?: number;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ============================================================
|
|
215
|
+
// Local Simplified Format (for analysis/cleanup)
|
|
216
|
+
// ============================================================
|
|
217
|
+
|
|
218
|
+
export interface LocalContact {
|
|
219
|
+
// Identity & reconciliation
|
|
220
|
+
resourceName: string; // From Google - used for updates
|
|
221
|
+
etag?: string; // For conflict detection
|
|
222
|
+
|
|
223
|
+
// Core identity
|
|
224
|
+
displayName: string;
|
|
225
|
+
givenName?: string;
|
|
226
|
+
familyName?: string;
|
|
227
|
+
nickname?: string;
|
|
228
|
+
|
|
229
|
+
// Contact info (flattened, typed arrays)
|
|
230
|
+
emails: Array<{ value: string; type?: string; primary?: boolean }>;
|
|
231
|
+
phones: Array<{ value: string; type?: string; primary?: boolean }>;
|
|
232
|
+
addresses: Array<{
|
|
233
|
+
formatted?: string;
|
|
234
|
+
street?: string;
|
|
235
|
+
city?: string;
|
|
236
|
+
region?: string;
|
|
237
|
+
postalCode?: string;
|
|
238
|
+
country?: string;
|
|
239
|
+
type?: string;
|
|
240
|
+
}>;
|
|
241
|
+
|
|
242
|
+
// Professional
|
|
243
|
+
organizations: Array<{
|
|
244
|
+
name?: string;
|
|
245
|
+
title?: string;
|
|
246
|
+
department?: string;
|
|
247
|
+
}>;
|
|
248
|
+
|
|
249
|
+
// Social/web
|
|
250
|
+
urls: Array<{ value: string; type?: string }>;
|
|
251
|
+
|
|
252
|
+
// Dates
|
|
253
|
+
birthday?: { year?: number; month?: number; day?: number };
|
|
254
|
+
|
|
255
|
+
// Notes
|
|
256
|
+
notes?: string;
|
|
257
|
+
|
|
258
|
+
// Groups
|
|
259
|
+
groups: string[]; // contactGroupResourceNames
|
|
260
|
+
|
|
261
|
+
// Metadata for cleanup
|
|
262
|
+
_meta: {
|
|
263
|
+
sourceType: string; // "CONTACT", "PROFILE", etc.
|
|
264
|
+
updateTime?: string;
|
|
265
|
+
isTestEntry?: boolean; // Flagged as test (m-XXXX@bob.ma pattern)
|
|
266
|
+
duplicateOf?: string[]; // resourceNames of potential duplicates
|
|
267
|
+
mergeConfidence?: number; // 0-1 confidence for merge candidates
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ============================================================
|
|
272
|
+
// Cleanup Analysis Types
|
|
273
|
+
// ============================================================
|
|
274
|
+
|
|
275
|
+
export interface DuplicateGroup {
|
|
276
|
+
primary: string; // resourceName of "best" contact
|
|
277
|
+
duplicates: string[]; // resourceNames to merge into primary
|
|
278
|
+
confidence: number; // 0-1 merge confidence
|
|
279
|
+
matchReasons: string[]; // Why these are considered duplicates
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export interface CleanupReport {
|
|
283
|
+
totalContacts: number;
|
|
284
|
+
testEntries: LocalContact[]; // To be deleted
|
|
285
|
+
duplicateGroups: DuplicateGroup[];
|
|
286
|
+
emptyContacts: LocalContact[]; // No useful info
|
|
287
|
+
suggestions: string[]; // Human-readable recommendations
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ============================================================
|
|
291
|
+
// Deletion Logging (for tracking cleanup history)
|
|
292
|
+
// ============================================================
|
|
293
|
+
|
|
294
|
+
export interface DeletionRecord {
|
|
295
|
+
resourceName: string;
|
|
296
|
+
displayName: string;
|
|
297
|
+
reason: string; // "test_entry", "duplicate_of:people/cXXX", "empty", etc.
|
|
298
|
+
deletedAt: string; // ISO timestamp
|
|
299
|
+
originalData: LocalContact; // Snapshot before deletion
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export interface DeletionLog {
|
|
303
|
+
deletions: DeletionRecord[];
|
|
304
|
+
lastUpdated: string; // ISO timestamp
|
|
305
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/gcards",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Google Contacts cleanup and management tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "gcards.ts",
|
|
7
7
|
"bin": {
|
|
8
|
-
"gcards": "gcards.ts"
|
|
8
|
+
"gcards": "gcards.ts",
|
|
9
|
+
"gfix": "gfix.ts"
|
|
9
10
|
},
|
|
10
11
|
"repository": {
|
|
11
12
|
"type": "git",
|
|
@@ -16,7 +17,7 @@
|
|
|
16
17
|
"prerelease:local": "git add -A && (git diff-index --quiet HEAD || git commit -m \"Pre-release commit\")",
|
|
17
18
|
"preversion": "npm run check && git add -A",
|
|
18
19
|
"postversion": "git push && git push --tags",
|
|
19
|
-
"release": "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"
|
|
20
21
|
},
|
|
21
22
|
"keywords": [
|
|
22
23
|
"google",
|
|
@@ -27,7 +28,8 @@
|
|
|
27
28
|
"author": "Bob Frankston",
|
|
28
29
|
"license": "MIT",
|
|
29
30
|
"devDependencies": {
|
|
30
|
-
"@types/node": "^22.10.1"
|
|
31
|
+
"@types/node": "^22.10.1",
|
|
32
|
+
"tsx": "^4.21.0"
|
|
31
33
|
},
|
|
32
34
|
"dependencies": {
|
|
33
35
|
"@bobfrankston/miscassists": "file:../../../projects/NodeJS/miscassists"
|