@treeviz/familysearch-sdk 1.0.25 → 2.0.1
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 +499 -30
- package/dist/auth/index.cjs +102 -0
- package/dist/auth/index.d.cts +1 -124
- package/dist/auth/index.d.ts +1 -124
- package/dist/auth/index.js +102 -1
- package/dist/index-B8IEIPI-.d.cts +5558 -0
- package/dist/index-D-O_e-PM.d.cts +359 -0
- package/dist/index-D-O_e-PM.d.ts +359 -0
- package/dist/index-D1LjJwtP.d.ts +5558 -0
- package/dist/index-DbUKUKnB.d.cts +171 -0
- package/dist/index-Ko_Hpn8j.d.ts +171 -0
- package/dist/index.cjs +5910 -822
- package/dist/index.d.cts +158 -5
- package/dist/index.d.ts +158 -5
- package/dist/index.js +5878 -818
- package/dist/places/index.cjs +303 -80
- package/dist/places/index.d.cts +3 -69
- package/dist/places/index.d.ts +3 -69
- package/dist/places/index.js +290 -78
- package/dist/tree/index.cjs +3412 -170
- package/dist/tree/index.d.cts +3 -50
- package/dist/tree/index.d.ts +3 -50
- package/dist/tree/index.js +3266 -167
- package/dist/tree-Bs2PWco8.d.cts +1335 -0
- package/dist/tree-Bs2PWco8.d.ts +1335 -0
- package/dist/utils/index.cjs +14 -9
- package/dist/utils/index.d.cts +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +14 -9
- package/package.json +7 -3
- package/dist/client-CoN1l8gT.d.ts +0 -276
- package/dist/client-yKRhqQzH.d.cts +0 -276
- package/dist/index-DqPXkWyy.d.cts +0 -671
- package/dist/index-DqPXkWyy.d.ts +0 -671
package/README.md
CHANGED
|
@@ -6,6 +6,61 @@ A modern, TypeScript-first SDK for the FamilySearch API v3.
|
|
|
6
6
|
|
|
7
7
|
> **Note:** This package was previously published as `familysearch-sdk`. It has been moved to the `@treeviz` organization.
|
|
8
8
|
|
|
9
|
+
## ⚠️ Version 2.0.0 Breaking Changes
|
|
10
|
+
|
|
11
|
+
**Version 2.0.0 introduces a major architectural refactor** with breaking changes to the API surface. The SDK now uses a **modular architecture** where API methods are organized into dedicated modules instead of being exposed directly on the SDK instance.
|
|
12
|
+
|
|
13
|
+
### Migration Guide (v1.x → v2.0.0)
|
|
14
|
+
|
|
15
|
+
| Old API (v1.x) | New API (v2.0.0) | Module |
|
|
16
|
+
|----------------|------------------|--------|
|
|
17
|
+
| `sdk.readCurrentUser()` | `sdk.user.readCurrentUser()` | User API |
|
|
18
|
+
| `sdk.readPerson(id)` | `sdk.persons.readPerson(id)` | Persons API |
|
|
19
|
+
| `sdk.searchPersons(query)` | `sdk.search.searchPersons(query)` | Search API |
|
|
20
|
+
| `sdk.matchPerson(person)` | `sdk.matches.matchPerson(person)` | Matches API |
|
|
21
|
+
| `sdk.searchPersonByData(person)` | `sdk.search.searchPersonByData(person)` | Search API |
|
|
22
|
+
| `sdk.searchPlaces(query)` | `sdk.places.searchPlaces(query)` | Places API |
|
|
23
|
+
| `sdk.exportGEDCOM(personId)` | `sdk.persons.exportGEDCOM(personId)` | Persons API |
|
|
24
|
+
| `sdk.getPersonSources(id)` | `sdk.sources.readPersonSources(id)` | Sources API |
|
|
25
|
+
| `sdk.getPersonMemories(id)` | `sdk.persons.readPersonMemories(id)` | Persons API |
|
|
26
|
+
| `sdk.getPersonDiscussions(id)` | `sdk.discussions.readPersonDiscussions(id)` | Discussions API |
|
|
27
|
+
| `sdk.getPersonPortraits(id)` | `sdk.persons.readPersonPortraits(id)` | Persons API |
|
|
28
|
+
| `sdk.getPersonChangeHistory(id)` | `sdk.persons.readPersonChangeHistory(id)` | Persons API |
|
|
29
|
+
|
|
30
|
+
### Why This Change?
|
|
31
|
+
|
|
32
|
+
**Benefits of v2.0.0 architecture:**
|
|
33
|
+
- ✅ **Better organization**: Related functionality grouped into logical modules
|
|
34
|
+
- ✅ **Cleaner client.ts**: Core SDK now only handles HTTP methods and infrastructure
|
|
35
|
+
- ✅ **No duplicates**: Single source of truth for each API method
|
|
36
|
+
- ✅ **Better tree-shaking**: Import only the modules you need
|
|
37
|
+
- ✅ **Easier maintenance**: Changes isolated to specific modules
|
|
38
|
+
- ✅ **Consistent patterns**: All modules follow the same structure
|
|
39
|
+
|
|
40
|
+
### Quick Migration Example
|
|
41
|
+
|
|
42
|
+
**Before (v1.x):**
|
|
43
|
+
```typescript
|
|
44
|
+
const sdk = createFamilySearchSDK({ accessToken: 'token' });
|
|
45
|
+
|
|
46
|
+
const user = await sdk.readCurrentUser();
|
|
47
|
+
const person = await sdk.readPerson('KWQS-BBQ');
|
|
48
|
+
const matches = await sdk.matchPerson({ givenName: 'John', familyName: 'Smith' });
|
|
49
|
+
const places = await sdk.searchPlaces('London, England');
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**After (v2.0.0):**
|
|
53
|
+
```typescript
|
|
54
|
+
const sdk = createFamilySearchSDK({ accessToken: 'token' });
|
|
55
|
+
|
|
56
|
+
const user = await sdk.user.readCurrentUser();
|
|
57
|
+
const person = await sdk.persons.readPerson('KWQS-BBQ');
|
|
58
|
+
const matches = await sdk.matches.matchPerson({ givenName: 'John', familyName: 'Smith' });
|
|
59
|
+
const places = await sdk.places.searchPlaces('London, England');
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Pattern:** `sdk.<method>()` → `sdk.<module>.<method>()`
|
|
63
|
+
|
|
9
64
|
## Features
|
|
10
65
|
|
|
11
66
|
- 🔷 **Full TypeScript support** with comprehensive type definitions
|
|
@@ -15,7 +70,13 @@ A modern, TypeScript-first SDK for the FamilySearch API v3.
|
|
|
15
70
|
- 📝 **GEDCOM export** - Convert FamilySearch data to GEDCOM 5.5 format
|
|
16
71
|
- 📍 **Places API** helpers for location searches
|
|
17
72
|
- 👨👩👧 **Tree/Pedigree API** for ancestry data
|
|
18
|
-
- 📚 **Sources API** - Fetch source references
|
|
73
|
+
- 📚 **Sources API** - Fetch source references and descriptions
|
|
74
|
+
- 💬 **Discussions API** - Access person discussions and comments
|
|
75
|
+
- 🖼️ **Memories API** - Work with photos, documents, and stories
|
|
76
|
+
- 🔄 **Rate Limiting** - Built-in rate limiting with automatic retry on 429 errors
|
|
77
|
+
- ⚡ **Enhanced Error Handling** - Typed error classes for better error management
|
|
78
|
+
- 📜 **Change History** - Access person change history and audit logs
|
|
79
|
+
- 🧩 **Modular Architecture (v2.0.0)** - Organized API modules for better maintainability
|
|
19
80
|
|
|
20
81
|
## Installation
|
|
21
82
|
|
|
@@ -38,8 +99,12 @@ const sdk = createFamilySearchSDK({
|
|
|
38
99
|
accessToken: 'your-oauth-token'
|
|
39
100
|
});
|
|
40
101
|
|
|
102
|
+
// Fetch current user (v2.0.0 - note the module prefix)
|
|
103
|
+
const user = await sdk.user.readCurrentUser();
|
|
104
|
+
console.log('User:', user?.displayName);
|
|
105
|
+
|
|
41
106
|
// Fetch pedigree data
|
|
42
|
-
const pedigree = await fetchPedigree(sdk,
|
|
107
|
+
const pedigree = await fetchPedigree(sdk, user?.personId, {
|
|
43
108
|
generations: 5,
|
|
44
109
|
onProgress: (progress) => {
|
|
45
110
|
console.log(`${progress.percent}% complete`);
|
|
@@ -54,27 +119,98 @@ const gedcom = convertToGedcom(pedigree, {
|
|
|
54
119
|
console.log(gedcom);
|
|
55
120
|
```
|
|
56
121
|
|
|
122
|
+
## SDK Modules (v2.0.0)
|
|
123
|
+
|
|
124
|
+
The SDK is organized into the following modules:
|
|
125
|
+
|
|
126
|
+
| Module | Access via | Purpose |
|
|
127
|
+
|--------|-----------|---------|
|
|
128
|
+
| **User API** | `sdk.user.*` | Current user information |
|
|
129
|
+
| **Persons API** | `sdk.persons.*` | Person CRUD operations, memories, portraits, change history |
|
|
130
|
+
| **Search API** | `sdk.search.*` | Person search by query or structured data |
|
|
131
|
+
| **Matches API** | `sdk.matches.*` | Person matching for duplicate detection |
|
|
132
|
+
| **Relationships API** | `sdk.relationships.*` | Couple and parent-child relationships |
|
|
133
|
+
| **Sources API** | `sdk.sources.*` | Source descriptions and attachments |
|
|
134
|
+
| **Places API** | `sdk.places.*` | Place search and details |
|
|
135
|
+
| **Discussions API** | `sdk.discussions.*` | Person discussions and comments |
|
|
136
|
+
| **Memories API** | `sdk.memories.*` | Photos, documents, and stories |
|
|
137
|
+
| **Notes API** | `sdk.notes.*` | Person notes |
|
|
138
|
+
| **Pedigrees API** | `sdk.pedigrees.*` | Ancestry and descendancy queries |
|
|
139
|
+
| **Dates API** | `sdk.dates.*` | Date standardization |
|
|
140
|
+
| **Names API** | `sdk.names.*` | Name standardization |
|
|
141
|
+
| **Vocabularies API** | `sdk.vocabularies.*` | Controlled vocabularies |
|
|
142
|
+
|
|
143
|
+
### Module Examples
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// User API
|
|
147
|
+
const user = await sdk.user.readCurrentUser();
|
|
148
|
+
|
|
149
|
+
// Persons API
|
|
150
|
+
const person = await sdk.persons.readPerson('KWQS-BBQ');
|
|
151
|
+
const memories = await sdk.persons.readPersonMemories('KWQS-BBQ');
|
|
152
|
+
const changeHistory = await sdk.persons.readPersonChangeHistory('KWQS-BBQ');
|
|
153
|
+
const gedcom = await sdk.persons.exportGEDCOM('KWQS-BBQ');
|
|
154
|
+
|
|
155
|
+
// Search API
|
|
156
|
+
const searchResults = await sdk.search.searchPersons('John Smith');
|
|
157
|
+
const matchResults = await sdk.search.searchPersonByData({
|
|
158
|
+
givenName: 'John',
|
|
159
|
+
familyName: 'Smith',
|
|
160
|
+
birthDate: '1850'
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Matches API
|
|
164
|
+
const matches = await sdk.matches.matchPerson({
|
|
165
|
+
givenName: 'Mary',
|
|
166
|
+
familyName: 'Johnson',
|
|
167
|
+
gender: 'Female',
|
|
168
|
+
birthDate: '1875',
|
|
169
|
+
birthPlace: 'Boston, Massachusetts'
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Places API
|
|
173
|
+
const places = await sdk.places.searchPlaces('London, England');
|
|
174
|
+
const placeDetails = await sdk.places.readPlaceById('12345');
|
|
175
|
+
|
|
176
|
+
// Relationships API
|
|
177
|
+
const ancestry = await sdk.relationships.readPersonAncestry('KWQS-BBQ', { generations: 4 });
|
|
178
|
+
const parents = await sdk.relationships.readPersonParents('KWQS-BBQ');
|
|
179
|
+
|
|
180
|
+
// Sources API
|
|
181
|
+
const sources = await sdk.sources.readPersonSources('KWQS-BBQ');
|
|
182
|
+
const sourceDesc = await sdk.sources.readSourceDescription('SOURCE-123');
|
|
183
|
+
|
|
184
|
+
// Discussions API
|
|
185
|
+
const discussions = await sdk.discussions.readPersonDiscussions('KWQS-BBQ');
|
|
186
|
+
```
|
|
187
|
+
|
|
57
188
|
## OAuth Authentication
|
|
58
189
|
|
|
59
190
|
The SDK provides utilities for OAuth 2.0 authentication with FamilySearch.
|
|
60
191
|
|
|
192
|
+
### Basic OAuth Flow
|
|
193
|
+
|
|
61
194
|
```typescript
|
|
62
195
|
import {
|
|
63
196
|
generateOAuthState,
|
|
64
197
|
buildAuthorizationUrl,
|
|
65
198
|
exchangeCodeForToken,
|
|
66
|
-
validateAccessToken
|
|
67
|
-
|
|
199
|
+
validateAccessToken,
|
|
200
|
+
refreshAccessToken
|
|
201
|
+
} from '@treeviz/familysearch-sdk/auth';
|
|
68
202
|
|
|
69
203
|
// Generate state for CSRF protection
|
|
70
204
|
const state = generateOAuthState();
|
|
71
205
|
|
|
72
|
-
// Build authorization URL
|
|
206
|
+
// Build authorization URL with offline_access scope for refresh tokens
|
|
73
207
|
const authUrl = buildAuthorizationUrl({
|
|
74
208
|
clientId: 'your-client-id',
|
|
75
209
|
redirectUri: 'https://your-app.com/callback',
|
|
76
210
|
environment: 'production'
|
|
77
|
-
}, state
|
|
211
|
+
}, state, {
|
|
212
|
+
scopes: ['offline_access'] // Request refresh token
|
|
213
|
+
});
|
|
78
214
|
|
|
79
215
|
// Redirect user to authUrl...
|
|
80
216
|
|
|
@@ -85,29 +221,112 @@ const tokens = await exchangeCodeForToken(code, {
|
|
|
85
221
|
environment: 'production'
|
|
86
222
|
});
|
|
87
223
|
|
|
224
|
+
console.log('Access token:', tokens.access_token);
|
|
225
|
+
console.log('Refresh token:', tokens.refresh_token); // Available with offline_access scope
|
|
226
|
+
console.log('Expires in:', tokens.expires_in);
|
|
227
|
+
|
|
88
228
|
// Validate token
|
|
89
229
|
const isValid = await validateAccessToken(tokens.access_token, 'production');
|
|
230
|
+
|
|
231
|
+
// Refresh token when it expires
|
|
232
|
+
if (tokens.refresh_token) {
|
|
233
|
+
const newTokens = await refreshAccessToken(tokens.refresh_token, {
|
|
234
|
+
clientId: 'your-client-id',
|
|
235
|
+
redirectUri: 'https://your-app.com/callback',
|
|
236
|
+
environment: 'production'
|
|
237
|
+
});
|
|
238
|
+
console.log('New access token:', newTokens.access_token);
|
|
239
|
+
}
|
|
90
240
|
```
|
|
91
241
|
|
|
242
|
+
### Token Storage (Browser Environment)
|
|
243
|
+
|
|
244
|
+
**Best Practice for storing FamilySearch tokens:**
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
import {
|
|
248
|
+
storeTokens,
|
|
249
|
+
getStoredAccessToken,
|
|
250
|
+
getStoredRefreshToken,
|
|
251
|
+
clearStoredTokens
|
|
252
|
+
} from '@treeviz/familysearch-sdk/auth';
|
|
253
|
+
|
|
254
|
+
// After obtaining tokens from OAuth flow
|
|
255
|
+
await storeTokens('user-id', {
|
|
256
|
+
accessToken: tokens.access_token,
|
|
257
|
+
expiresAt: Date.now() + (tokens.expires_in * 1000),
|
|
258
|
+
refreshToken: tokens.refresh_token, // Store for silent refresh
|
|
259
|
+
environment: 'production'
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Retrieve tokens later
|
|
263
|
+
const accessToken = getStoredAccessToken('user-id');
|
|
264
|
+
const refreshToken = getStoredRefreshToken('user-id');
|
|
265
|
+
|
|
266
|
+
// Clear tokens on sign out
|
|
267
|
+
clearStoredTokens('user-id');
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**Storage Strategy (FamilySearch Compatibility):**
|
|
271
|
+
- ✅ **Access tokens** → `sessionStorage` (temporary, cleared on browser close)
|
|
272
|
+
- ✅ **Refresh tokens** → `localStorage` (persistent, for re-authentication)
|
|
273
|
+
- ✅ **Compliant** with FamilySearch requirement: "No permanent storage of FamilySearch API Session ID"
|
|
274
|
+
|
|
275
|
+
### Refresh Token Best Practices
|
|
276
|
+
|
|
277
|
+
**Always request `offline_access` scope** to ensure you receive a refresh token:
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
const authUrl = buildAuthorizationUrl(config, state, {
|
|
281
|
+
scopes: ['offline_access'] // ← Important!
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Why `offline_access`?**
|
|
286
|
+
- ✅ Guarantees refresh token in response
|
|
287
|
+
- ✅ Refresh token is renewed on each refresh
|
|
288
|
+
- ✅ Prevents popup re-authentication loops
|
|
289
|
+
- ✅ Better user experience (silent token refresh)
|
|
290
|
+
|
|
291
|
+
Without `offline_access`:
|
|
292
|
+
- ❌ FamilySearch may not return refresh token
|
|
293
|
+
- ❌ Subsequent refreshes may not renew refresh token
|
|
294
|
+
- ❌ Eventually requires popup re-authentication
|
|
295
|
+
|
|
296
|
+
See [FamilySearch Refresh Token Implementation](../../docs/FAMILYSEARCH_REFRESH_TOKEN.md) for detailed information about token lifecycle and silent refresh implementation.
|
|
297
|
+
|
|
92
298
|
## Places API
|
|
93
299
|
|
|
94
300
|
Search and retrieve place information from FamilySearch.
|
|
95
301
|
|
|
96
302
|
```typescript
|
|
97
|
-
import { createFamilySearchSDK } from 'familysearch-sdk';
|
|
98
|
-
import { searchPlaces, getPlaceDetails } from 'familysearch-sdk/places';
|
|
303
|
+
import { createFamilySearchSDK } from '@treeviz/familysearch-sdk';
|
|
99
304
|
|
|
100
305
|
const sdk = createFamilySearchSDK({ accessToken: 'token' });
|
|
101
306
|
|
|
102
|
-
// Search for places
|
|
103
|
-
const results = await searchPlaces(
|
|
307
|
+
// Search for places (v2.0.0 - using sdk.places module)
|
|
308
|
+
const results = await sdk.places.searchPlaces('London, England', {
|
|
104
309
|
date: '1850',
|
|
105
310
|
count: 10
|
|
106
311
|
});
|
|
107
312
|
|
|
313
|
+
// Or use structured query parameters
|
|
314
|
+
const structuredResults = await sdk.places.searchPlaces({
|
|
315
|
+
name: 'London',
|
|
316
|
+
parentId: '12345', // Parent place ID
|
|
317
|
+
typeId: '789', // Place type ID (city, county, etc.)
|
|
318
|
+
date: '1850-01-01',
|
|
319
|
+
count: 10
|
|
320
|
+
});
|
|
321
|
+
|
|
108
322
|
// Get place details
|
|
109
|
-
const details = await
|
|
323
|
+
const details = await sdk.places.readPlaceById('place-id');
|
|
110
324
|
console.log(details.name, details.latitude, details.longitude);
|
|
325
|
+
|
|
326
|
+
// Get child places (e.g., counties in a state)
|
|
327
|
+
const children = await sdk.places.readPlaceChildren('place-id', {
|
|
328
|
+
count: 50
|
|
329
|
+
});
|
|
111
330
|
```
|
|
112
331
|
|
|
113
332
|
## Tree/Pedigree API
|
|
@@ -115,17 +334,30 @@ console.log(details.name, details.latitude, details.longitude);
|
|
|
115
334
|
Fetch and manage family tree data.
|
|
116
335
|
|
|
117
336
|
```typescript
|
|
118
|
-
import { createFamilySearchSDK } from 'familysearch-sdk';
|
|
119
|
-
import { fetchPedigree, getCurrentUser } from 'familysearch-sdk/tree';
|
|
337
|
+
import { createFamilySearchSDK } from '@treeviz/familysearch-sdk';
|
|
120
338
|
|
|
121
339
|
const sdk = createFamilySearchSDK({ accessToken: 'token' });
|
|
122
340
|
|
|
123
|
-
// Get current user
|
|
124
|
-
const user = await
|
|
125
|
-
console.log(user?.displayName);
|
|
341
|
+
// Get current user (v2.0.0 - using sdk.user module)
|
|
342
|
+
const user = await sdk.user.readCurrentUser();
|
|
343
|
+
console.log(user?.displayName, user?.personId);
|
|
344
|
+
|
|
345
|
+
// Read a person (v2.0.0 - using sdk.persons module)
|
|
346
|
+
const person = await sdk.persons.readPerson('KWQS-BBQ');
|
|
347
|
+
console.log(person?.display?.name);
|
|
348
|
+
|
|
349
|
+
// Read person with relationships
|
|
350
|
+
const personWithRelations = await sdk.persons.readPersonWithDetails('KWQS-BBQ', {
|
|
351
|
+
sourceDescriptions: true
|
|
352
|
+
});
|
|
126
353
|
|
|
127
|
-
//
|
|
128
|
-
const
|
|
354
|
+
// Get ancestry (v2.0.0 - using sdk.relationships module)
|
|
355
|
+
const ancestry = await sdk.relationships.readPersonAncestry('KWQS-BBQ', {
|
|
356
|
+
generations: 4
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// Fetch pedigree (will use current user's personId if not provided)
|
|
360
|
+
const pedigree = await fetchPedigree(sdk, user?.personId, {
|
|
129
361
|
generations: 4,
|
|
130
362
|
includeDetails: true,
|
|
131
363
|
includeNotes: true
|
|
@@ -137,12 +369,12 @@ const pedigree = await fetchPedigree(sdk, undefined, {
|
|
|
137
369
|
Retrieve source references linked to persons.
|
|
138
370
|
|
|
139
371
|
```typescript
|
|
140
|
-
import { createFamilySearchSDK } from 'familysearch-sdk';
|
|
372
|
+
import { createFamilySearchSDK } from '@treeviz/familysearch-sdk';
|
|
141
373
|
|
|
142
374
|
const sdk = createFamilySearchSDK({ accessToken: 'token' });
|
|
143
375
|
|
|
144
|
-
// Fetch sources for a person
|
|
145
|
-
const sources = await sdk.
|
|
376
|
+
// Fetch sources for a person (v2.0.0 - using sdk.sources module)
|
|
377
|
+
const sources = await sdk.sources.readPersonSources('KWQS-BBQ');
|
|
146
378
|
|
|
147
379
|
// Access source references
|
|
148
380
|
if (sources?.persons?.[0]?.sources) {
|
|
@@ -160,6 +392,10 @@ if (sources?.sourceDescriptions) {
|
|
|
160
392
|
console.log('About:', desc.about);
|
|
161
393
|
});
|
|
162
394
|
}
|
|
395
|
+
|
|
396
|
+
// Get a specific source description
|
|
397
|
+
const sourceDesc = await sdk.sources.readSourceDescription('SOURCE-123');
|
|
398
|
+
console.log('Source title:', sourceDesc?.titles?.[0]?.value);
|
|
163
399
|
```
|
|
164
400
|
|
|
165
401
|
## Person Match API
|
|
@@ -167,12 +403,12 @@ if (sources?.sourceDescriptions) {
|
|
|
167
403
|
Find potential matches in the FamilySearch Tree for persons from external GEDCOM data or manually created trees.
|
|
168
404
|
|
|
169
405
|
```typescript
|
|
170
|
-
import { createFamilySearchSDK } from 'familysearch-sdk';
|
|
406
|
+
import { createFamilySearchSDK } from '@treeviz/familysearch-sdk';
|
|
171
407
|
|
|
172
408
|
const sdk = createFamilySearchSDK({ accessToken: 'token' });
|
|
173
409
|
|
|
174
|
-
// Match a person from external GEDCOM data
|
|
175
|
-
const matches = await sdk.matchPerson({
|
|
410
|
+
// Match a person from external GEDCOM data (v2.0.0 - using sdk.matches module)
|
|
411
|
+
const matches = await sdk.matches.matchPerson({
|
|
176
412
|
givenName: 'John',
|
|
177
413
|
familyName: 'Smith',
|
|
178
414
|
gender: 'Male',
|
|
@@ -199,7 +435,7 @@ if (matches?.entries) {
|
|
|
199
435
|
}
|
|
200
436
|
|
|
201
437
|
// Filter by collection and limit results
|
|
202
|
-
const censusMatches = await sdk.matchPerson({
|
|
438
|
+
const censusMatches = await sdk.matches.matchPerson({
|
|
203
439
|
givenName: 'Mary',
|
|
204
440
|
familyName: 'Johnson',
|
|
205
441
|
birthDate: '1875'
|
|
@@ -207,6 +443,19 @@ const censusMatches = await sdk.matchPerson({
|
|
|
207
443
|
collection: 'census',
|
|
208
444
|
count: 10
|
|
209
445
|
});
|
|
446
|
+
|
|
447
|
+
// Search by structured person data (v2.0.0 - using sdk.search module)
|
|
448
|
+
const searchResults = await sdk.search.searchPersonByData({
|
|
449
|
+
givenName: 'John',
|
|
450
|
+
familyName: 'Smith',
|
|
451
|
+
birthDate: '1850',
|
|
452
|
+
birthPlace: 'London, England',
|
|
453
|
+
fatherGivenName: 'William',
|
|
454
|
+
fatherFamilyName: 'Smith'
|
|
455
|
+
}, {
|
|
456
|
+
count: 20,
|
|
457
|
+
collection: 'tree'
|
|
458
|
+
});
|
|
210
459
|
```
|
|
211
460
|
|
|
212
461
|
## GEDCOM Conversion
|
|
@@ -226,6 +475,149 @@ const gedcom = convertToGedcom(pedigreeData, {
|
|
|
226
475
|
fs.writeFileSync('family.ged', gedcom);
|
|
227
476
|
```
|
|
228
477
|
|
|
478
|
+
## Discussions API
|
|
479
|
+
|
|
480
|
+
Access person discussions and comments.
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
import { createFamilySearchSDK } from 'familysearch-sdk';
|
|
484
|
+
|
|
485
|
+
const sdk = createFamilySearchSDK({ accessToken: 'token' });
|
|
486
|
+
|
|
487
|
+
// Get discussions for a person
|
|
488
|
+
const discussions = await sdk.getPersonDiscussions('KWQS-BBQ');
|
|
489
|
+
|
|
490
|
+
if (discussions?.discussions) {
|
|
491
|
+
discussions.discussions.forEach(discussion => {
|
|
492
|
+
console.log('Title:', discussion.title);
|
|
493
|
+
console.log('Details:', discussion.details);
|
|
494
|
+
console.log('Comments:', discussion.numberOfComments);
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## Portraits API
|
|
500
|
+
|
|
501
|
+
Fetch portrait photos for persons.
|
|
502
|
+
|
|
503
|
+
```typescript
|
|
504
|
+
import { createFamilySearchSDK } from 'familysearch-sdk';
|
|
505
|
+
|
|
506
|
+
const sdk = createFamilySearchSDK({ accessToken: 'token' });
|
|
507
|
+
|
|
508
|
+
// Get portraits for a person
|
|
509
|
+
const portraits = await sdk.getPersonPortraits('KWQS-BBQ');
|
|
510
|
+
|
|
511
|
+
if (portraits?.sourceDescriptions) {
|
|
512
|
+
portraits.sourceDescriptions.forEach(portrait => {
|
|
513
|
+
console.log('Portrait URL:', portrait.about);
|
|
514
|
+
console.log('Title:', portrait.titles?.[0]?.value);
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
## Memories API
|
|
520
|
+
|
|
521
|
+
Work with photos, documents, and stories.
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
import { createFamilySearchSDK } from 'familysearch-sdk';
|
|
525
|
+
|
|
526
|
+
const sdk = createFamilySearchSDK({ accessToken: 'token' });
|
|
527
|
+
|
|
528
|
+
// Get a specific memory
|
|
529
|
+
const memory = await sdk.getMemory('MEM-123');
|
|
530
|
+
|
|
531
|
+
// Get user's uploaded memories
|
|
532
|
+
const userMemories = await sdk.readUserMemories({ count: 50 });
|
|
533
|
+
|
|
534
|
+
// Get comments on a memory
|
|
535
|
+
const comments = await sdk.readMemoryComments('MEM-123');
|
|
536
|
+
if (comments?.discussions?.[0]?.comments) {
|
|
537
|
+
comments.discussions[0].comments.forEach(comment => {
|
|
538
|
+
console.log('Comment:', comment.text);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
## Change History API
|
|
544
|
+
|
|
545
|
+
Access person change history and audit logs.
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
import { createFamilySearchSDK } from 'familysearch-sdk';
|
|
549
|
+
|
|
550
|
+
const sdk = createFamilySearchSDK({ accessToken: 'token' });
|
|
551
|
+
|
|
552
|
+
// Get change history for a person
|
|
553
|
+
const history = await sdk.getPersonChangeHistory('KWQS-BBQ');
|
|
554
|
+
|
|
555
|
+
if (history?.entries) {
|
|
556
|
+
history.entries.forEach(entry => {
|
|
557
|
+
console.log('Change:', entry.title);
|
|
558
|
+
console.log('Date:', new Date(entry.updated || 0));
|
|
559
|
+
entry.changeInfo?.forEach(info => {
|
|
560
|
+
console.log('Operation:', info.operation);
|
|
561
|
+
console.log('Object Type:', info.objectType);
|
|
562
|
+
});
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
## Rate Limiting
|
|
568
|
+
|
|
569
|
+
The SDK includes built-in rate limiting with automatic retry on 429 errors.
|
|
570
|
+
|
|
571
|
+
```typescript
|
|
572
|
+
import { createFamilySearchSDK } from 'familysearch-sdk';
|
|
573
|
+
|
|
574
|
+
const sdk = createFamilySearchSDK({
|
|
575
|
+
accessToken: 'token',
|
|
576
|
+
rateLimiter: {
|
|
577
|
+
requestsPerSecond: 10, // Max requests per second
|
|
578
|
+
maxBurst: 20, // Max burst size
|
|
579
|
+
maxRetries: 3, // Max retry attempts on 429
|
|
580
|
+
initialBackoffMs: 1000, // Initial backoff delay
|
|
581
|
+
maxBackoffMs: 30000 // Max backoff delay
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
// Requests are automatically rate limited and retried on 429 errors
|
|
586
|
+
const person = await sdk.getPerson('KWQS-BBQ');
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
## Error Handling
|
|
590
|
+
|
|
591
|
+
The SDK provides typed error classes for better error management.
|
|
592
|
+
|
|
593
|
+
```typescript
|
|
594
|
+
import {
|
|
595
|
+
createFamilySearchSDK,
|
|
596
|
+
AuthenticationError,
|
|
597
|
+
NotFoundError,
|
|
598
|
+
RateLimitError,
|
|
599
|
+
ValidationError,
|
|
600
|
+
ServerError,
|
|
601
|
+
NetworkError
|
|
602
|
+
} from 'familysearch-sdk';
|
|
603
|
+
|
|
604
|
+
const sdk = createFamilySearchSDK({ accessToken: 'token' });
|
|
605
|
+
|
|
606
|
+
try {
|
|
607
|
+
const person = await sdk.getPerson('INVALID-ID');
|
|
608
|
+
} catch (error) {
|
|
609
|
+
if (error instanceof NotFoundError) {
|
|
610
|
+
console.error('Person not found:', error.resourceId);
|
|
611
|
+
} else if (error instanceof AuthenticationError) {
|
|
612
|
+
console.error('Authentication failed:', error.statusCode);
|
|
613
|
+
} else if (error instanceof RateLimitError) {
|
|
614
|
+
console.error('Rate limit exceeded. Retry after:', error.retryAfter);
|
|
615
|
+
} else if (error instanceof NetworkError) {
|
|
616
|
+
console.error('Network error:', error.originalError);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
229
621
|
## Environment Configuration
|
|
230
622
|
|
|
231
623
|
The SDK supports three FamilySearch environments:
|
|
@@ -285,20 +677,55 @@ const sdk = createFamilySearchSDK({
|
|
|
285
677
|
### Places (`/places`)
|
|
286
678
|
|
|
287
679
|
- `searchPlaces(sdk, query, options)` - Search for places
|
|
288
|
-
- `
|
|
289
|
-
- `
|
|
290
|
-
- `
|
|
680
|
+
- `readPlaceById(sdk, id)` - Get place by ID
|
|
681
|
+
- `readPlaceChildren(sdk, id, options)` - Get child places
|
|
682
|
+
- `readPlaceDetails(sdk, id)` - Get detailed place info
|
|
291
683
|
|
|
292
684
|
### Tree (`/tree`)
|
|
293
685
|
|
|
294
686
|
- `fetchPedigree(sdk, personId, options)` - Fetch ancestry data
|
|
295
|
-
- `
|
|
687
|
+
- `readCurrentUser(sdk)` - Get current user info
|
|
296
688
|
- `getPersonWithDetails(sdk, personId)` - Get person details
|
|
297
689
|
- `fetchMultiplePersons(sdk, personIds)` - Batch fetch persons
|
|
298
690
|
|
|
299
|
-
### Person
|
|
691
|
+
### Person APIs
|
|
300
692
|
|
|
693
|
+
- `sdk.getPerson(personId)` - Get person by ID
|
|
301
694
|
- `sdk.getPersonSources(personId)` - Get source references for a person
|
|
695
|
+
- `sdk.getPersonNotes(personId)` - Get notes for a person
|
|
696
|
+
- `sdk.getPersonMemories(personId)` - Get memories for a person
|
|
697
|
+
- `sdk.getPersonDiscussions(personId)` - Get discussions for a person
|
|
698
|
+
- `sdk.getPersonPortraits(personId)` - Get portrait photos for a person
|
|
699
|
+
- `sdk.getPersonChangeHistory(personId)` - Get change history for a person
|
|
700
|
+
- `sdk.searchPersons(query, options)` - Search for persons
|
|
701
|
+
|
|
702
|
+
### Sources APIs
|
|
703
|
+
|
|
704
|
+
- `sdk.getSourceDescription(sourceId)` - Get source description by ID
|
|
705
|
+
- `sdk.searchSourceDescriptions(query, options)` - Search source descriptions
|
|
706
|
+
|
|
707
|
+
### Memories APIs
|
|
708
|
+
|
|
709
|
+
- `sdk.getMemory(memoryId)` - Get memory by ID
|
|
710
|
+
- `sdk.readUserMemories(options)` - Get user's uploaded memories
|
|
711
|
+
- `sdk.readMemoryComments(memoryId)` - Get comments for a memory
|
|
712
|
+
|
|
713
|
+
### Relationships APIs
|
|
714
|
+
|
|
715
|
+
- `sdk.getCoupleRelationship(relationshipId)` - Get couple relationship details
|
|
716
|
+
- `sdk.getChildAndParentsRelationship(relationshipId)` - Get parent-child relationship details
|
|
717
|
+
- `sdk.getAncestry(personId, generations)` - Get ancestry for a person
|
|
718
|
+
- `sdk.getDescendancy(personId, generations)` - Get descendancy for a person
|
|
719
|
+
|
|
720
|
+
### Error Classes
|
|
721
|
+
|
|
722
|
+
- `FamilySearchError` - Base error class
|
|
723
|
+
- `AuthenticationError` - 401/403 authentication errors
|
|
724
|
+
- `NotFoundError` - 404 resource not found errors
|
|
725
|
+
- `RateLimitError` - 429 rate limit errors
|
|
726
|
+
- `ValidationError` - 400 validation errors
|
|
727
|
+
- `ServerError` - 5xx server errors
|
|
728
|
+
- `NetworkError` - Network/connection errors
|
|
302
729
|
|
|
303
730
|
### Person Matching
|
|
304
731
|
|
|
@@ -309,6 +736,47 @@ const sdk = createFamilySearchSDK({
|
|
|
309
736
|
|
|
310
737
|
- `convertToGedcom(pedigreeData, options)` - Convert to GEDCOM
|
|
311
738
|
|
|
739
|
+
## API Coverage
|
|
740
|
+
|
|
741
|
+
This SDK currently implements approximately **84% (172 endpoints)** of the FamilySearch API. For comprehensive analysis:
|
|
742
|
+
|
|
743
|
+
- 📊 **[API Coverage Analysis](./API_COVERAGE_ANALYSIS.md)** - Detailed English analysis with implementation roadmap
|
|
744
|
+
- 🇭🇺 **[API Coverage Analysis (Hungarian)](./API_COVERAGE_ANALYSIS_HU.md)** - Hungarian summary and recommendations
|
|
745
|
+
- 📋 **[Complete API Endpoints List](./API_ENDPOINTS_COMPLETE.md)** - Full endpoint inventory with implementation status
|
|
746
|
+
- 🎯 **[Latest Session Summary](./docs/SESSION_SUMMARY_2026_01_21.md)** - Recent implementation progress (73% → 84%)
|
|
747
|
+
|
|
748
|
+
### Implementation Status
|
|
749
|
+
|
|
750
|
+
✅ **Fully Implemented Categories (8/19)**:
|
|
751
|
+
- Change History, Discussions, Genealogies (User Trees)
|
|
752
|
+
- Groups, Matches, Notes, Places Standards, Vocabularies
|
|
753
|
+
|
|
754
|
+
🚧 **Partial Implementation**:
|
|
755
|
+
- **Tree Persons**: 34/48 (71%) - Missing: reference deletions, search, matches
|
|
756
|
+
- **Relationships**: 43/48 (90%) - Missing: restore, notes, GedcomX format
|
|
757
|
+
- **Sources**: 16/19 (84%) - Missing: HEAD requests, user sources
|
|
758
|
+
- **Memories**: 4/5 (80%) - Missing: create memory
|
|
759
|
+
- **Other categories**: 90%+ coverage
|
|
760
|
+
|
|
761
|
+
### Key Capabilities
|
|
762
|
+
|
|
763
|
+
✅ **You CAN**:
|
|
764
|
+
- ✅ Read, create, update, delete persons
|
|
765
|
+
- ✅ Create and manage relationships (couple, parent-child)
|
|
766
|
+
- ✅ Read and manage sources (descriptions, attachments, collections)
|
|
767
|
+
- ✅ Search and match persons
|
|
768
|
+
- ✅ Fetch pedigrees and ancestry data
|
|
769
|
+
- ✅ Read memories, discussions, notes
|
|
770
|
+
- ✅ Work with User Trees (Genealogies API)
|
|
771
|
+
|
|
772
|
+
⚠️ **Limited Support**:
|
|
773
|
+
- ⚠️ Reference deletions (discussion, memory, source references)
|
|
774
|
+
- ⚠️ Relationship restore operations
|
|
775
|
+
- ⚠️ Advanced search operations
|
|
776
|
+
- ⚠️ Match management (not-a-match operations)
|
|
777
|
+
|
|
778
|
+
See the [API Coverage Analysis](./API_COVERAGE_ANALYSIS.md) for detailed information about missing endpoints and implementation priorities.
|
|
779
|
+
|
|
312
780
|
## License
|
|
313
781
|
|
|
314
782
|
MIT License - see [LICENSE](./LICENSE) file for details.
|
|
@@ -316,3 +784,4 @@ MIT License - see [LICENSE](./LICENSE) file for details.
|
|
|
316
784
|
## Contributing
|
|
317
785
|
|
|
318
786
|
Contributions are welcome! Please read our contributing guidelines before submitting a pull request.
|
|
787
|
+
|