@rachelallyson/planning-center-people-ts 2.12.1 → 2.13.0
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/CHANGELOG.md +17 -0
- package/dist/client-manager.js +4 -0
- package/dist/client.d.ts +0 -1
- package/dist/client.js +0 -19
- package/dist/core/http.d.ts +4 -0
- package/dist/core/http.js +67 -11
- package/dist/core.d.ts +3 -1
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +6 -1
- package/dist/matching/matcher.js +2 -1
- package/dist/matching/scoring.js +2 -1
- package/dist/modules/people.d.ts +6 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.13.0] - 2026-01-20
|
|
9
|
+
|
|
10
|
+
### ✨ **New Features**
|
|
11
|
+
|
|
12
|
+
- **🎯 Lenient Age Preference Filtering**: Added `agePreferenceLenient` option for flexible age-based filtering
|
|
13
|
+
- When `agePreferenceLenient: true`, age preferences only filter profiles that have birthdates
|
|
14
|
+
- Profiles without birthdates are included regardless of `agePreference` setting
|
|
15
|
+
- Prevents false negatives when searching for existing people with incomplete age data
|
|
16
|
+
- Backward compatible - default behavior remains strict filtering (only `agePreference: 'any'` includes profiles without birthdates)
|
|
17
|
+
|
|
18
|
+
## [2.12.2] - 2026-01-20
|
|
19
|
+
|
|
20
|
+
### ✨ **New Features**
|
|
21
|
+
|
|
22
|
+
- **Direct Personal Access Token Configuration**: Added support for passing `personalAccessTokenSecret` directly in config (alternative to environment variables)
|
|
23
|
+
- **Flexible Authentication**: Choose between environment variables or direct config based on your needs
|
|
24
|
+
|
|
8
25
|
## [2.12.1] - 2026-01-20
|
|
9
26
|
|
|
10
27
|
### ✨ **New Features**
|
package/dist/client-manager.js
CHANGED
|
@@ -151,6 +151,10 @@ class PcoClientManager {
|
|
|
151
151
|
if (oldConfig.auth.type === 'personal_access_token' && newConfig.auth.type === 'personal_access_token') {
|
|
152
152
|
return oldConfig.auth.personalAccessToken !== newConfig.auth.personalAccessToken;
|
|
153
153
|
}
|
|
154
|
+
if (oldConfig.auth.type === 'basic' && newConfig.auth.type === 'basic') {
|
|
155
|
+
return (oldConfig.auth.appId !== newConfig.auth.appId ||
|
|
156
|
+
oldConfig.auth.appSecret !== newConfig.auth.appSecret);
|
|
157
|
+
}
|
|
154
158
|
return (oldConfig.baseURL !== newConfig.baseURL ||
|
|
155
159
|
oldConfig.timeout !== newConfig.timeout);
|
|
156
160
|
}
|
package/dist/client.d.ts
CHANGED
package/dist/client.js
CHANGED
|
@@ -36,7 +36,6 @@ class PcoClient {
|
|
|
36
36
|
this.reports = new reports_1.ReportsModule(this.httpClient, this.paginationHelper, this.eventEmitter);
|
|
37
37
|
this.batch = new planning_center_base_ts_1.BatchExecutor(this, this.eventEmitter);
|
|
38
38
|
// Set up event handlers from config
|
|
39
|
-
this.setupEventHandlers();
|
|
40
39
|
}
|
|
41
40
|
// EventEmitter implementation
|
|
42
41
|
on(eventType, handler) {
|
|
@@ -95,24 +94,6 @@ class PcoClient {
|
|
|
95
94
|
eventTypes() {
|
|
96
95
|
return this.eventEmitter.eventTypes();
|
|
97
96
|
}
|
|
98
|
-
setupEventHandlers() {
|
|
99
|
-
// Set up config event handlers
|
|
100
|
-
if (this.config.events?.onError) {
|
|
101
|
-
this.on('error', this.config.events.onError);
|
|
102
|
-
}
|
|
103
|
-
if (this.config.events?.onAuthFailure) {
|
|
104
|
-
this.on('auth:failure', this.config.events.onAuthFailure);
|
|
105
|
-
}
|
|
106
|
-
if (this.config.events?.onRequestStart) {
|
|
107
|
-
this.on('request:start', this.config.events.onRequestStart);
|
|
108
|
-
}
|
|
109
|
-
if (this.config.events?.onRequestComplete) {
|
|
110
|
-
this.on('request:complete', this.config.events.onRequestComplete);
|
|
111
|
-
}
|
|
112
|
-
if (this.config.events?.onRateLimit) {
|
|
113
|
-
this.on('rate:limit', this.config.events.onRateLimit);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
97
|
updateModules() {
|
|
117
98
|
// Recreate modules with new HTTP client
|
|
118
99
|
this.people = new people_1.PeopleModule(this.httpClient, this.paginationHelper, this.eventEmitter);
|
package/dist/core/http.d.ts
CHANGED
|
@@ -49,4 +49,8 @@ export declare class PcoHttpClient {
|
|
|
49
49
|
* Get authentication header for external services (like file uploads)
|
|
50
50
|
*/
|
|
51
51
|
getAuthHeader(): string;
|
|
52
|
+
/**
|
|
53
|
+
* Make HTTPS request using Node.js HTTPS module (fallback when fetch is unavailable)
|
|
54
|
+
*/
|
|
55
|
+
private makeHttpsRequest;
|
|
52
56
|
}
|
package/dist/core/http.js
CHANGED
|
@@ -152,7 +152,7 @@ class PcoHttpClient {
|
|
|
152
152
|
catch (refreshError) {
|
|
153
153
|
console.warn('Token refresh failed:', refreshError);
|
|
154
154
|
// Call the onRefreshFailure callback
|
|
155
|
-
await this.config.auth.onRefreshFailure(refreshError);
|
|
155
|
+
await this.config.auth.onRefreshFailure?.(refreshError);
|
|
156
156
|
throw refreshError;
|
|
157
157
|
}
|
|
158
158
|
}
|
|
@@ -195,9 +195,20 @@ class PcoHttpClient {
|
|
|
195
195
|
}
|
|
196
196
|
addAuthentication(headers) {
|
|
197
197
|
if (this.config.auth.type === 'personal_access_token') {
|
|
198
|
-
// Personal Access Tokens use HTTP Basic Auth
|
|
199
|
-
//
|
|
200
|
-
|
|
198
|
+
// Personal Access Tokens use client_id:secret format with HTTP Basic Auth
|
|
199
|
+
// Get client ID from config (required)
|
|
200
|
+
const clientId = this.config.auth.personalAccessToken;
|
|
201
|
+
// Get client secret from config or environment (with config taking precedence)
|
|
202
|
+
const clientSecret = this.config.auth.personalAccessTokenSecret ||
|
|
203
|
+
process.env.PCO_PERSONAL_ACCESS_SECRET;
|
|
204
|
+
if (!clientId) {
|
|
205
|
+
throw new Error('personalAccessToken is required for personal access token authentication');
|
|
206
|
+
}
|
|
207
|
+
if (!clientSecret) {
|
|
208
|
+
throw new Error('personalAccessTokenSecret (in config) or PCO_PERSONAL_ACCESS_SECRET environment variable is required for personal access token authentication');
|
|
209
|
+
}
|
|
210
|
+
const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
|
|
211
|
+
headers.Authorization = `Basic ${credentials}`;
|
|
201
212
|
}
|
|
202
213
|
else if (this.config.auth.type === 'oauth') {
|
|
203
214
|
headers.Authorization = `Bearer ${this.config.auth.accessToken}`;
|
|
@@ -243,11 +254,11 @@ class PcoHttpClient {
|
|
|
243
254
|
// Prepare the request body for token refresh
|
|
244
255
|
const body = new URLSearchParams({
|
|
245
256
|
grant_type: 'refresh_token',
|
|
246
|
-
refresh_token: this.config.auth.refreshToken,
|
|
257
|
+
refresh_token: this.config.auth.refreshToken || '',
|
|
247
258
|
});
|
|
248
259
|
// Add client credentials if available from the config or environment
|
|
249
|
-
const clientId = this.config.auth.
|
|
250
|
-
const clientSecret = this.config.auth.
|
|
260
|
+
const clientId = this.config.auth.appId || process.env.PCO_APP_ID;
|
|
261
|
+
const clientSecret = this.config.auth.appSecret || process.env.PCO_APP_SECRET;
|
|
251
262
|
if (clientId && clientSecret) {
|
|
252
263
|
body.append('client_id', clientId);
|
|
253
264
|
body.append('client_secret', clientSecret);
|
|
@@ -268,10 +279,10 @@ class PcoHttpClient {
|
|
|
268
279
|
// Update the config with new tokens
|
|
269
280
|
this.config.auth.accessToken = tokens.access_token;
|
|
270
281
|
this.config.auth.refreshToken = tokens.refresh_token || this.config.auth.refreshToken;
|
|
271
|
-
// Call the onRefresh callback
|
|
272
|
-
await this.config.auth.onRefresh({
|
|
282
|
+
// Call the onRefresh callback
|
|
283
|
+
await this.config.auth.onRefresh?.({
|
|
273
284
|
accessToken: tokens.access_token,
|
|
274
|
-
refreshToken: tokens.refresh_token || this.config.auth.refreshToken
|
|
285
|
+
refreshToken: tokens.refresh_token || this.config.auth.refreshToken
|
|
275
286
|
});
|
|
276
287
|
}
|
|
277
288
|
updateRateLimitTracking(endpoint, headers) {
|
|
@@ -292,13 +303,58 @@ class PcoHttpClient {
|
|
|
292
303
|
* Get authentication header for external services (like file uploads)
|
|
293
304
|
*/
|
|
294
305
|
getAuthHeader() {
|
|
306
|
+
// The base package's PcoHttpClient handles authentication, so this method should delegate
|
|
307
|
+
// But for backward compatibility, we'll implement it
|
|
295
308
|
if (this.config.auth.type === 'personal_access_token') {
|
|
296
|
-
|
|
309
|
+
const clientSecret = this.config.auth.personalAccessTokenSecret || process.env.PCO_PERSONAL_ACCESS_SECRET;
|
|
310
|
+
return `Basic ${Buffer.from(`${this.config.auth.personalAccessToken}:${clientSecret}`).toString('base64')}`;
|
|
297
311
|
}
|
|
298
312
|
else if (this.config.auth.type === 'oauth') {
|
|
299
313
|
return `Bearer ${this.config.auth.accessToken}`;
|
|
300
314
|
}
|
|
315
|
+
else if (this.config.auth.type === 'basic') {
|
|
316
|
+
return `Basic ${Buffer.from(`${this.config.auth.appId}:${this.config.auth.appSecret}`).toString('base64')}`;
|
|
317
|
+
}
|
|
301
318
|
return '';
|
|
302
319
|
}
|
|
320
|
+
/**
|
|
321
|
+
* Make HTTPS request using Node.js HTTPS module (fallback when fetch is unavailable)
|
|
322
|
+
*/
|
|
323
|
+
async makeHttpsRequest(url, options) {
|
|
324
|
+
const https = require('https');
|
|
325
|
+
const urlObj = new URL(url);
|
|
326
|
+
const requestOptions = {
|
|
327
|
+
hostname: urlObj.hostname,
|
|
328
|
+
port: urlObj.port || 443,
|
|
329
|
+
path: urlObj.pathname + urlObj.search,
|
|
330
|
+
method: options.method || 'GET',
|
|
331
|
+
headers: options.headers,
|
|
332
|
+
};
|
|
333
|
+
return new Promise((resolve, reject) => {
|
|
334
|
+
const req = https.request(requestOptions, (res) => {
|
|
335
|
+
let data = '';
|
|
336
|
+
res.on('data', (chunk) => {
|
|
337
|
+
data += chunk;
|
|
338
|
+
});
|
|
339
|
+
res.on('end', () => {
|
|
340
|
+
// Create a response-like object
|
|
341
|
+
const response = {
|
|
342
|
+
ok: res.statusCode >= 200 && res.statusCode < 300,
|
|
343
|
+
status: res.statusCode,
|
|
344
|
+
statusText: res.statusMessage,
|
|
345
|
+
headers: res.headers,
|
|
346
|
+
text: () => Promise.resolve(data),
|
|
347
|
+
json: () => Promise.resolve(JSON.parse(data)),
|
|
348
|
+
};
|
|
349
|
+
resolve(response);
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
req.on('error', reject);
|
|
353
|
+
if (options.body) {
|
|
354
|
+
req.write(options.body);
|
|
355
|
+
}
|
|
356
|
+
req.end();
|
|
357
|
+
});
|
|
358
|
+
}
|
|
303
359
|
}
|
|
304
360
|
exports.PcoHttpClient = PcoHttpClient;
|
package/dist/core.d.ts
CHANGED
|
@@ -3,8 +3,10 @@ import { PcoRateLimiter } from '@rachelallyson/planning-center-base-ts';
|
|
|
3
3
|
import { Paginated, ResourceObject, Response as JsonApiResponse } from './types';
|
|
4
4
|
import { type TokenRefreshCallback, type TokenRefreshFailureCallback } from './auth';
|
|
5
5
|
export interface PcoClientConfig {
|
|
6
|
-
/** Personal Access Token (for single-user apps) */
|
|
6
|
+
/** Personal Access Token Client ID (for single-user apps) */
|
|
7
7
|
personalAccessToken?: string;
|
|
8
|
+
/** Personal Access Token Client Secret (alternative to PCO_PERSONAL_ACCESS_SECRET env var) */
|
|
9
|
+
personalAccessTokenSecret?: string;
|
|
8
10
|
/** OAuth 2.0 Access Token (for multi-user apps) */
|
|
9
11
|
accessToken?: string;
|
|
10
12
|
/** OAuth 2.0 Refresh Token (for multi-user apps) */
|
package/dist/helpers.d.ts
CHANGED
package/dist/helpers.js
CHANGED
|
@@ -103,8 +103,13 @@ function isChild(birthdate) {
|
|
|
103
103
|
*/
|
|
104
104
|
function matchesAgeCriteria(birthdate, criteria) {
|
|
105
105
|
const age = calculateAgeSafe(birthdate);
|
|
106
|
-
// If no birthdate,
|
|
106
|
+
// If no birthdate, match based on lenient setting
|
|
107
107
|
if (age === null) {
|
|
108
|
+
if (criteria.agePreferenceLenient) {
|
|
109
|
+
// Lenient mode: include profiles without birthdates regardless of agePreference
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
// Strict mode (default): only match if preference is 'any'
|
|
108
113
|
return criteria.agePreference === 'any' || criteria.agePreference === undefined;
|
|
109
114
|
}
|
|
110
115
|
// Check age preference
|
package/dist/matching/matcher.js
CHANGED
|
@@ -377,7 +377,8 @@ class PersonMatcher {
|
|
|
377
377
|
agePreference: options.agePreference,
|
|
378
378
|
minAge: options.minAge,
|
|
379
379
|
maxAge: options.maxAge,
|
|
380
|
-
birthYear: options.birthYear
|
|
380
|
+
birthYear: options.birthYear,
|
|
381
|
+
agePreferenceLenient: options.agePreferenceLenient
|
|
381
382
|
});
|
|
382
383
|
});
|
|
383
384
|
}
|
package/dist/matching/scoring.js
CHANGED
|
@@ -164,7 +164,8 @@ class MatchScorer {
|
|
|
164
164
|
agePreference: options.agePreference,
|
|
165
165
|
minAge: options.minAge,
|
|
166
166
|
maxAge: options.maxAge,
|
|
167
|
-
birthYear: options.birthYear
|
|
167
|
+
birthYear: options.birthYear,
|
|
168
|
+
agePreferenceLenient: options.agePreferenceLenient
|
|
168
169
|
});
|
|
169
170
|
if (!matches) {
|
|
170
171
|
return 0; // No match
|
package/dist/modules/people.d.ts
CHANGED
|
@@ -70,6 +70,12 @@ export interface PersonMatchOptions {
|
|
|
70
70
|
addMissingContactInfo?: boolean;
|
|
71
71
|
/** Age preference filter: 'adults' (18+), 'children' (<18), or 'any' */
|
|
72
72
|
agePreference?: 'adults' | 'children' | 'any';
|
|
73
|
+
/**
|
|
74
|
+
* When true, age preference filters only apply to profiles with birthdates.
|
|
75
|
+
* Profiles without birthdates are included regardless of agePreference.
|
|
76
|
+
* When false (default), profiles without birthdates only match when agePreference is 'any'.
|
|
77
|
+
*/
|
|
78
|
+
agePreferenceLenient?: boolean;
|
|
73
79
|
/** Minimum age filter */
|
|
74
80
|
minAge?: number;
|
|
75
81
|
/** Maximum age filter */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rachelallyson/planning-center-people-ts",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"description": "A strictly typed TypeScript client for Planning Center Online People API with comprehensive functionality and enhanced developer experience",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|