@rachelallyson/planning-center-people-ts 2.14.1 → 3.1.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +82 -7
  2. package/README.md +42 -4
  3. package/dist/auth.d.ts +1 -1
  4. package/dist/auth.js +14 -6
  5. package/dist/client.d.ts +33 -8
  6. package/dist/client.js +47 -22
  7. package/dist/core.d.ts +4 -2
  8. package/dist/core.js +3 -2
  9. package/dist/error-handling.d.ts +4 -4
  10. package/dist/error-handling.js +13 -2
  11. package/dist/error-scenarios.d.ts +11 -7
  12. package/dist/error-scenarios.js +26 -10
  13. package/dist/helpers.d.ts +124 -48
  14. package/dist/helpers.js +237 -93
  15. package/dist/index.d.ts +10 -8
  16. package/dist/index.js +31 -72
  17. package/dist/matching/matcher.d.ts +8 -4
  18. package/dist/matching/matcher.js +51 -58
  19. package/dist/matching/scoring.d.ts +9 -6
  20. package/dist/matching/scoring.js +18 -14
  21. package/dist/modules/campus.d.ts +31 -36
  22. package/dist/modules/campus.js +36 -49
  23. package/dist/modules/contacts.d.ts +33 -29
  24. package/dist/modules/contacts.js +36 -12
  25. package/dist/modules/fields.d.ts +39 -55
  26. package/dist/modules/fields.js +65 -105
  27. package/dist/modules/forms.d.ts +35 -24
  28. package/dist/modules/forms.js +41 -23
  29. package/dist/modules/households.d.ts +17 -19
  30. package/dist/modules/households.js +25 -34
  31. package/dist/modules/lists.d.ts +38 -28
  32. package/dist/modules/lists.js +62 -42
  33. package/dist/modules/notes.d.ts +32 -30
  34. package/dist/modules/notes.js +40 -52
  35. package/dist/modules/people.d.ts +83 -71
  36. package/dist/modules/people.js +323 -172
  37. package/dist/modules/reports.d.ts +18 -32
  38. package/dist/modules/reports.js +28 -40
  39. package/dist/modules/service-time.d.ts +19 -24
  40. package/dist/modules/service-time.js +28 -28
  41. package/dist/modules/workflows.d.ts +42 -47
  42. package/dist/modules/workflows.js +52 -53
  43. package/dist/performance.d.ts +14 -10
  44. package/dist/performance.js +61 -25
  45. package/dist/testing/recorder.js +11 -2
  46. package/dist/testing/simple-builders.d.ts +6 -4
  47. package/dist/testing/simple-builders.js +36 -49
  48. package/dist/testing/types.d.ts +4 -0
  49. package/dist/types/api-options.d.ts +380 -0
  50. package/dist/types/api-options.js +6 -0
  51. package/dist/types/client.d.ts +4 -2
  52. package/dist/types/client.js +1 -1
  53. package/dist/types/index.d.ts +1 -1
  54. package/dist/types/people.d.ts +61 -9
  55. package/package.json +7 -7
  56. package/dist/core/http.d.ts +0 -56
  57. package/dist/core/http.js +0 -360
  58. package/dist/core/pagination.d.ts +0 -34
  59. package/dist/core/pagination.js +0 -178
  60. package/dist/people/contacts.d.ts +0 -43
  61. package/dist/people/contacts.js +0 -122
  62. package/dist/people/core.d.ts +0 -28
  63. package/dist/people/core.js +0 -69
  64. package/dist/people/fields.d.ts +0 -68
  65. package/dist/people/fields.js +0 -305
  66. package/dist/people/households.d.ts +0 -15
  67. package/dist/people/households.js +0 -31
  68. package/dist/people/index.d.ts +0 -8
  69. package/dist/people/index.js +0 -25
  70. package/dist/people/lists.d.ts +0 -34
  71. package/dist/people/lists.js +0 -48
  72. package/dist/people/notes.d.ts +0 -30
  73. package/dist/people/notes.js +0 -37
  74. package/dist/people/organization.d.ts +0 -12
  75. package/dist/people/organization.js +0 -15
  76. package/dist/people/workflows.d.ts +0 -37
  77. package/dist/people/workflows.js +0 -75
package/dist/core/http.js DELETED
@@ -1,360 +0,0 @@
1
- "use strict";
2
- /**
3
- * v2.0.0 HTTP Client
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.PcoHttpClient = void 0;
7
- const planning_center_base_ts_1 = require("@rachelallyson/planning-center-base-ts");
8
- const planning_center_base_ts_2 = require("@rachelallyson/planning-center-base-ts");
9
- const planning_center_base_ts_3 = require("@rachelallyson/planning-center-base-ts");
10
- class PcoHttpClient {
11
- constructor(config, eventEmitter) {
12
- this.config = config;
13
- this.eventEmitter = eventEmitter;
14
- this.requestIdGenerator = new planning_center_base_ts_1.RequestIdGenerator();
15
- this.performanceMetrics = new planning_center_base_ts_1.PerformanceMetrics();
16
- this.rateLimitTracker = new planning_center_base_ts_1.RateLimitTracker();
17
- // Initialize rate limiter
18
- this.rateLimiter = new planning_center_base_ts_2.PcoRateLimiter(100, 20000); // 100 requests per 20 seconds
19
- }
20
- async request(options) {
21
- const requestId = this.requestIdGenerator.generate();
22
- const startTime = Date.now();
23
- // Emit request start event
24
- this.eventEmitter.emit({
25
- type: 'request:start',
26
- endpoint: options.endpoint,
27
- method: options.method,
28
- requestId,
29
- timestamp: new Date().toISOString(),
30
- });
31
- try {
32
- // Wait for rate limiter
33
- await this.rateLimiter.waitForAvailability();
34
- const response = await this.makeRequest(options, requestId);
35
- const duration = Date.now() - startTime;
36
- // Record performance metrics
37
- this.performanceMetrics.record(`${options.method} ${options.endpoint}`, duration, true);
38
- // Update rate limit tracking
39
- this.updateRateLimitTracking(options.endpoint, response.headers);
40
- // Emit request complete event
41
- this.eventEmitter.emit({
42
- type: 'request:complete',
43
- endpoint: options.endpoint,
44
- method: options.method,
45
- status: response.status,
46
- duration,
47
- requestId,
48
- timestamp: new Date().toISOString(),
49
- });
50
- return response;
51
- }
52
- catch (error) {
53
- const duration = Date.now() - startTime;
54
- // Record performance metrics
55
- this.performanceMetrics.record(`${options.method} ${options.endpoint}`, duration, false);
56
- // Emit request error event
57
- this.eventEmitter.emit({
58
- type: 'request:error',
59
- endpoint: options.endpoint,
60
- method: options.method,
61
- error: error,
62
- requestId,
63
- timestamp: new Date().toISOString(),
64
- });
65
- throw error;
66
- }
67
- }
68
- async makeRequest(options, requestId, retryCount = 0) {
69
- const baseURL = this.config.baseURL || 'https://api.planningcenteronline.com/people/v2';
70
- let url = options.endpoint.startsWith('http') ? options.endpoint : `${baseURL}${options.endpoint}`;
71
- // Add query parameters
72
- if (options.params) {
73
- const searchParams = new URLSearchParams();
74
- Object.entries(options.params).forEach(([key, value]) => {
75
- if (value !== undefined && value !== null) {
76
- searchParams.append(key, String(value));
77
- }
78
- });
79
- const queryString = searchParams.toString();
80
- if (queryString) {
81
- url += url.includes('?') ? `&${queryString}` : `?${queryString}`;
82
- }
83
- }
84
- // Prepare headers
85
- const headers = {
86
- 'Accept': 'application/json',
87
- 'Content-Type': 'application/json',
88
- ...this.config.headers,
89
- ...options.headers,
90
- };
91
- // Add authentication
92
- this.addAuthentication(headers);
93
- // Prepare request options
94
- const requestOptions = {
95
- headers,
96
- method: options.method,
97
- };
98
- // Add body for POST/PATCH requests
99
- if ((options.method === 'POST' || options.method === 'PATCH') && options.data) {
100
- // Determine resource type from endpoint
101
- const resourceType = this.getResourceTypeFromEndpoint(options.endpoint);
102
- // Separate attributes and relationships
103
- const { relationships, ...attributes } = options.data;
104
- const jsonApiData = {
105
- data: {
106
- type: resourceType,
107
- attributes
108
- }
109
- };
110
- // Add relationships if present
111
- if (relationships) {
112
- jsonApiData.data.relationships = relationships;
113
- }
114
- requestOptions.body = JSON.stringify(jsonApiData);
115
- }
116
- // Add timeout
117
- const timeout = options.timeout || this.config.timeout || 30000;
118
- const controller = new AbortController();
119
- const timeoutId = setTimeout(() => controller.abort(), timeout);
120
- requestOptions.signal = controller.signal;
121
- try {
122
- const response = await fetch(url, requestOptions);
123
- clearTimeout(timeoutId);
124
- // Update rate limiter from headers
125
- const rateLimitHeaders = {
126
- 'Retry-After': response.headers.get('retry-after') || undefined,
127
- 'X-PCO-API-Request-Rate-Count': response.headers.get('x-pco-api-request-rate-count') || undefined,
128
- 'X-PCO-API-Request-Rate-Limit': response.headers.get('x-pco-api-request-rate-limit') || undefined,
129
- 'X-PCO-API-Request-Rate-Period': response.headers.get('x-pco-api-request-rate-period') || undefined,
130
- };
131
- this.rateLimiter.updateFromHeaders(rateLimitHeaders);
132
- this.rateLimiter.recordRequest();
133
- // Handle 429 responses
134
- if (response.status === 429) {
135
- if (retryCount >= 5) {
136
- throw new Error(`Rate limit exceeded after ${retryCount} retries`);
137
- }
138
- await this.rateLimiter.waitForAvailability();
139
- return this.makeRequest(options, requestId, retryCount + 1);
140
- }
141
- // Handle other errors
142
- if (!response.ok) {
143
- // Handle 401 errors with token refresh if available
144
- if (response.status === 401 && this.config.auth.type === 'oauth') {
145
- if (retryCount >= 3) {
146
- throw new Error(`Authentication failed after ${retryCount} retries`);
147
- }
148
- try {
149
- await this.attemptTokenRefresh();
150
- return this.makeRequest(options, requestId, retryCount + 1);
151
- }
152
- catch (refreshError) {
153
- console.warn('Token refresh failed:', refreshError);
154
- // Call the onRefreshFailure callback
155
- await this.config.auth.onRefreshFailure?.(refreshError);
156
- throw refreshError;
157
- }
158
- }
159
- let errorData;
160
- try {
161
- errorData = await response.json();
162
- }
163
- catch {
164
- errorData = {};
165
- }
166
- throw planning_center_base_ts_3.PcoApiError.fromFetchError(response, errorData);
167
- }
168
- // Parse response
169
- if (options.method === 'DELETE') {
170
- return {
171
- data: undefined,
172
- status: response.status,
173
- headers: this.extractHeaders(response),
174
- requestId,
175
- duration: 0, // Will be set by caller
176
- };
177
- }
178
- const data = await response.json();
179
- return {
180
- data,
181
- status: response.status,
182
- headers: this.extractHeaders(response),
183
- requestId,
184
- duration: 0, // Will be set by caller
185
- };
186
- }
187
- catch (error) {
188
- clearTimeout(timeoutId);
189
- // Handle timeout/abort errors
190
- if (error instanceof Error && error.name === 'AbortError') {
191
- throw new Error(`Request timeout after ${timeout}ms`);
192
- }
193
- throw error;
194
- }
195
- }
196
- addAuthentication(headers) {
197
- if (this.config.auth.type === 'personal_access_token') {
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}`;
212
- }
213
- else if (this.config.auth.type === 'oauth') {
214
- headers.Authorization = `Bearer ${this.config.auth.accessToken}`;
215
- }
216
- else if (this.config.auth.type === 'basic') {
217
- // Basic auth with app credentials
218
- const credentials = Buffer.from(`${this.config.auth.appId}:${this.config.auth.appSecret}`).toString('base64');
219
- headers.Authorization = `Basic ${credentials}`;
220
- }
221
- }
222
- getResourceTypeFromEndpoint(endpoint) {
223
- // Extract resource type from endpoint
224
- // /households -> Household
225
- // /people -> Person
226
- // /emails -> Email
227
- // etc.
228
- const pathParts = endpoint.split('/').filter(part => part.length > 0);
229
- const resourcePath = pathParts[pathParts.length - 1];
230
- // Convert kebab-case to PascalCase and make singular
231
- const pascalCase = resourcePath
232
- .split('-')
233
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
234
- .join('');
235
- // Make singular (remove trailing 's' if it exists and the word is longer than 3 characters)
236
- if (pascalCase.endsWith('s') && pascalCase.length > 3) {
237
- return pascalCase.slice(0, -1);
238
- }
239
- return pascalCase;
240
- }
241
- extractHeaders(response) {
242
- const headers = {};
243
- response.headers.forEach((value, key) => {
244
- headers[key] = value;
245
- });
246
- return headers;
247
- }
248
- async attemptTokenRefresh() {
249
- if (this.config.auth.type !== 'oauth') {
250
- throw new Error('Token refresh is only available for OAuth authentication');
251
- }
252
- const baseURL = this.config.baseURL || 'https://api.planningcenteronline.com/people/v2';
253
- const tokenUrl = baseURL.replace('/people/v2', '/oauth/token');
254
- // Prepare the request body for token refresh
255
- const body = new URLSearchParams({
256
- grant_type: 'refresh_token',
257
- refresh_token: this.config.auth.refreshToken || '',
258
- });
259
- // Add client credentials if available from the config or environment
260
- const clientId = this.config.auth.appId || process.env.PCO_APP_ID;
261
- const clientSecret = this.config.auth.appSecret || process.env.PCO_APP_SECRET;
262
- if (clientId && clientSecret) {
263
- body.append('client_id', clientId);
264
- body.append('client_secret', clientSecret);
265
- }
266
- const response = await fetch(tokenUrl, {
267
- method: 'POST',
268
- headers: {
269
- 'Content-Type': 'application/x-www-form-urlencoded',
270
- 'Accept': 'application/json',
271
- },
272
- body: body.toString(),
273
- });
274
- if (!response.ok) {
275
- const errorData = await response.json().catch(() => ({}));
276
- throw new Error(`Token refresh failed: ${response.status} ${response.statusText}. ${JSON.stringify(errorData)}`);
277
- }
278
- const tokens = await response.json();
279
- // Update the config with new tokens
280
- this.config.auth.accessToken = tokens.access_token;
281
- this.config.auth.refreshToken = tokens.refresh_token || this.config.auth.refreshToken;
282
- // Call the onRefresh callback
283
- await this.config.auth.onRefresh?.({
284
- accessToken: tokens.access_token,
285
- refreshToken: tokens.refresh_token || this.config.auth.refreshToken
286
- });
287
- }
288
- updateRateLimitTracking(endpoint, headers) {
289
- const limit = headers['x-pco-api-request-rate-limit'];
290
- const remaining = headers['x-pco-api-request-rate-count'];
291
- const resetTime = headers['retry-after'];
292
- if (limit && remaining && resetTime) {
293
- this.rateLimitTracker.update(endpoint, parseInt(limit), parseInt(remaining), Date.now() + parseInt(resetTime) * 1000);
294
- }
295
- }
296
- getPerformanceMetrics() {
297
- return this.performanceMetrics.getMetrics();
298
- }
299
- getRateLimitInfo() {
300
- return this.rateLimitTracker.getAllLimits();
301
- }
302
- /**
303
- * Get authentication header for external services (like file uploads)
304
- */
305
- getAuthHeader() {
306
- // The base package's PcoHttpClient handles authentication, so this method should delegate
307
- // But for backward compatibility, we'll implement it
308
- if (this.config.auth.type === 'personal_access_token') {
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')}`;
311
- }
312
- else if (this.config.auth.type === 'oauth') {
313
- return `Bearer ${this.config.auth.accessToken}`;
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
- }
318
- return '';
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
- }
359
- }
360
- exports.PcoHttpClient = PcoHttpClient;
@@ -1,34 +0,0 @@
1
- /**
2
- * v2.0.0 Pagination Utilities
3
- */
4
- import type { ResourceObject, Paginated } from '../types';
5
- import type { PcoHttpClient } from './http';
6
- export interface PaginationOptions {
7
- /** Maximum number of pages to fetch */
8
- maxPages?: number;
9
- /** Items per page */
10
- perPage?: number;
11
- /** Progress callback */
12
- onProgress?: (current: number, total: number) => void;
13
- /** Delay between requests in milliseconds */
14
- delay?: number;
15
- }
16
- export interface PaginationResult<T> {
17
- data: T[];
18
- totalCount: number;
19
- pagesFetched: number;
20
- duration: number;
21
- }
22
- export declare class PaginationHelper {
23
- private httpClient;
24
- constructor(httpClient: PcoHttpClient);
25
- getAllPages<T extends ResourceObject<string, any, any>>(endpoint: string, params?: Record<string, any>, options?: PaginationOptions): Promise<PaginationResult<T>>;
26
- getPage<T extends ResourceObject<string, any, any>>(endpoint: string, page?: number, perPage?: number, params?: Record<string, any>): Promise<Paginated<T>>;
27
- streamPages<T extends ResourceObject<string, any, any>>(endpoint: string, params?: Record<string, any>, options?: PaginationOptions): AsyncGenerator<T[], void, unknown>;
28
- /**
29
- * Get all pages with parallel processing for better performance
30
- */
31
- getAllPagesParallel<T extends ResourceObject<string, any, any>>(endpoint: string, params?: Record<string, any>, options?: PaginationOptions & {
32
- maxConcurrency?: number;
33
- }): Promise<PaginationResult<T>>;
34
- }
@@ -1,178 +0,0 @@
1
- "use strict";
2
- /**
3
- * v2.0.0 Pagination Utilities
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.PaginationHelper = void 0;
7
- class PaginationHelper {
8
- constructor(httpClient) {
9
- this.httpClient = httpClient;
10
- }
11
- async getAllPages(endpoint, params = {}, options = {}) {
12
- // Ensure endpoint is a string
13
- if (typeof endpoint !== 'string') {
14
- throw new Error(`Expected endpoint to be a string, got ${typeof endpoint}`);
15
- }
16
- const { maxPages = 1000, perPage = 100, onProgress, delay = 50, } = options;
17
- const startTime = Date.now();
18
- const allData = [];
19
- let page = 1;
20
- let hasMore = true;
21
- let totalCount = 0;
22
- while (hasMore && page <= maxPages) {
23
- const response = await this.httpClient.request({
24
- method: 'GET',
25
- endpoint,
26
- params: {
27
- ...params,
28
- page,
29
- per_page: perPage,
30
- },
31
- });
32
- if (response.data.data && Array.isArray(response.data.data)) {
33
- allData.push(...response.data.data);
34
- }
35
- if (response.data.meta?.total_count) {
36
- totalCount = Number(response.data.meta.total_count) || 0;
37
- }
38
- // Check if we have a next link and if it's different from current page
39
- const nextLink = response.data.links?.next;
40
- hasMore = !!nextLink;
41
- // Additional safeguard: if we're getting the same page repeatedly, break the loop
42
- if (hasMore && nextLink && nextLink.includes(`page=${page}`)) {
43
- console.warn(`Pagination loop detected: next link points to same page ${page}. Breaking loop.`);
44
- hasMore = false;
45
- }
46
- page++;
47
- if (onProgress) {
48
- onProgress(allData.length, totalCount || allData.length);
49
- }
50
- // Add delay between requests to respect rate limits
51
- if (hasMore && delay > 0) {
52
- await new Promise(resolve => setTimeout(resolve, delay));
53
- }
54
- }
55
- return {
56
- data: allData,
57
- totalCount,
58
- pagesFetched: page - 1,
59
- duration: Date.now() - startTime,
60
- };
61
- }
62
- async getPage(endpoint, page = 1, perPage = 100, params = {}) {
63
- const response = await this.httpClient.request({
64
- method: 'GET',
65
- endpoint,
66
- params: {
67
- ...params,
68
- page,
69
- per_page: perPage,
70
- },
71
- });
72
- return response.data;
73
- }
74
- async *streamPages(endpoint, params = {}, options = {}) {
75
- const { maxPages = 1000, perPage = 100, delay = 50, } = options;
76
- let page = 1;
77
- let hasMore = true;
78
- while (hasMore && page <= maxPages) {
79
- const response = await this.httpClient.request({
80
- method: 'GET',
81
- endpoint,
82
- params: {
83
- ...params,
84
- page,
85
- per_page: perPage,
86
- },
87
- });
88
- if (response.data.data && Array.isArray(response.data.data)) {
89
- yield response.data.data;
90
- }
91
- // Check if we have a next link and if it's different from current page
92
- const nextLink = response.data.links?.next;
93
- hasMore = !!nextLink;
94
- // Additional safeguard: if we're getting the same page repeatedly, break the loop
95
- if (hasMore && nextLink && nextLink.includes(`page=${page}`)) {
96
- console.warn(`Pagination loop detected: next link points to same page ${page}. Breaking loop.`);
97
- hasMore = false;
98
- }
99
- page++;
100
- if (hasMore && delay > 0) {
101
- await new Promise(resolve => setTimeout(resolve, delay));
102
- }
103
- }
104
- }
105
- /**
106
- * Get all pages with parallel processing for better performance
107
- */
108
- async getAllPagesParallel(endpoint, params = {}, options = {}) {
109
- const { maxPages = 1000, perPage = 100, maxConcurrency = 3, onProgress, } = options;
110
- const startTime = Date.now();
111
- // First, get the first page to determine total count
112
- const firstPage = await this.getPage(endpoint, 1, perPage, params);
113
- const totalCount = Number(firstPage.meta?.total_count) || 0;
114
- const totalPages = Math.min(Math.ceil(totalCount / perPage), maxPages);
115
- const allData = [...(firstPage.data || [])];
116
- if (totalPages <= 1) {
117
- return {
118
- data: allData,
119
- totalCount,
120
- pagesFetched: 1,
121
- duration: Date.now() - startTime,
122
- };
123
- }
124
- // Process remaining pages in parallel batches
125
- const remainingPages = Array.from({ length: totalPages - 1 }, (_, i) => i + 2);
126
- const semaphore = new Semaphore(maxConcurrency);
127
- const pagePromises = remainingPages.map(async (pageNum) => {
128
- await semaphore.acquire();
129
- try {
130
- const page = await this.getPage(endpoint, pageNum, perPage, params);
131
- return page.data || [];
132
- }
133
- finally {
134
- semaphore.release();
135
- }
136
- });
137
- const pageResults = await Promise.all(pagePromises);
138
- for (const pageData of pageResults) {
139
- allData.push(...pageData);
140
- if (onProgress) {
141
- onProgress(allData.length, totalCount);
142
- }
143
- }
144
- return {
145
- data: allData,
146
- totalCount,
147
- pagesFetched: totalPages,
148
- duration: Date.now() - startTime,
149
- };
150
- }
151
- }
152
- exports.PaginationHelper = PaginationHelper;
153
- /**
154
- * Semaphore for controlling concurrency
155
- */
156
- class Semaphore {
157
- constructor(permits) {
158
- this.waiting = [];
159
- this.permits = permits;
160
- }
161
- async acquire() {
162
- if (this.permits > 0) {
163
- this.permits--;
164
- return;
165
- }
166
- return new Promise(resolve => {
167
- this.waiting.push(resolve);
168
- });
169
- }
170
- release() {
171
- this.permits++;
172
- if (this.waiting.length > 0) {
173
- const resolve = this.waiting.shift();
174
- this.permits--;
175
- resolve();
176
- }
177
- }
178
- }
@@ -1,43 +0,0 @@
1
- import { PcoClientState } from '../core';
2
- import type { ErrorContext } from '@rachelallyson/planning-center-base-ts';
3
- import { AddressAttributes, AddressesList, AddressSingle, EmailAttributes, EmailSingle, EmailsList, PhoneNumberAttributes, PhoneNumberSingle, PhoneNumbersList, SocialProfileAttributes, SocialProfileSingle, SocialProfilesList } from '../types';
4
- /**
5
- * Get all emails for a person
6
- */
7
- export declare function getPersonEmails(client: PcoClientState, personId: string, context?: Partial<ErrorContext>): Promise<EmailsList>;
8
- /**
9
- * Create an email for a person
10
- */
11
- export declare function createPersonEmail(client: PcoClientState, personId: string, data: Partial<EmailAttributes>, context?: Partial<ErrorContext>): Promise<EmailSingle>;
12
- /**
13
- * Get all phone numbers for a person
14
- */
15
- export declare function getPersonPhoneNumbers(client: PcoClientState, personId: string, context?: Partial<ErrorContext>): Promise<PhoneNumbersList>;
16
- /**
17
- * Create a phone number for a person
18
- */
19
- export declare function createPersonPhoneNumber(client: PcoClientState, personId: string, data: Partial<PhoneNumberAttributes>, context?: Partial<ErrorContext>): Promise<PhoneNumberSingle>;
20
- /**
21
- * Get all addresses for a person
22
- */
23
- export declare function getPersonAddresses(client: PcoClientState, personId: string, context?: Partial<ErrorContext>): Promise<AddressesList>;
24
- /**
25
- * Create an address for a person
26
- */
27
- export declare function createPersonAddress(client: PcoClientState, personId: string, data: Partial<AddressAttributes>, context?: Partial<ErrorContext>): Promise<AddressSingle>;
28
- /**
29
- * Update an address for a person
30
- */
31
- export declare function updatePersonAddress(client: PcoClientState, personId: string, addressId: string, data: Partial<AddressAttributes>, context?: Partial<ErrorContext>): Promise<AddressSingle>;
32
- /**
33
- * Get social profiles for a person
34
- */
35
- export declare function getPersonSocialProfiles(client: PcoClientState, personId: string, context?: Partial<ErrorContext>): Promise<SocialProfilesList>;
36
- /**
37
- * Create a social profile for a person
38
- */
39
- export declare function createPersonSocialProfile(client: PcoClientState, personId: string, data: Partial<SocialProfileAttributes>, context?: Partial<ErrorContext>): Promise<SocialProfileSingle>;
40
- /**
41
- * Delete a social profile
42
- */
43
- export declare function deleteSocialProfile(client: PcoClientState, socialProfileId: string, context?: Partial<ErrorContext>): Promise<void>;