@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
@@ -7,94 +7,91 @@ exports.WorkflowsModule = void 0;
7
7
  const planning_center_base_ts_1 = require("@rachelallyson/planning-center-base-ts");
8
8
  class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
9
9
  /**
10
- * Get all workflows
10
+ * Get all workflows across all pages
11
11
  */
12
12
  async getAll(options = {}) {
13
- const params = {};
14
- if (options.where) {
15
- Object.entries(options.where).forEach(([key, value]) => {
16
- params[`where[${key}]`] = value;
17
- });
18
- }
19
- if (options.include) {
20
- params.include = options.include.join(',');
21
- }
22
- if (options.perPage) {
23
- params.per_page = options.perPage;
24
- }
25
- if (options.page) {
26
- params.page = options.page;
27
- }
28
- return this.getList('/workflows', params);
13
+ this.debugLog('workflows.getAll', { options });
14
+ return await this.getAllPages('/workflows', {
15
+ where: options.where,
16
+ include: options.include,
17
+ order: options.order
18
+ });
29
19
  }
30
20
  /**
31
- * Get all workflows across all pages
21
+ * Get a single page of workflows with optional filtering and pagination control
22
+ * Use this when you need a specific page or want to limit the number of results
23
+ * @param options - List options including where, include, perPage, and page
24
+ * @returns A single page of results with meta and links for pagination
32
25
  */
33
- async getAllPagesPaginated(options = {}, paginationOptions) {
34
- const params = {};
35
- if (options.where) {
36
- Object.entries(options.where).forEach(([key, value]) => {
37
- params[`where[${key}]`] = value;
38
- });
39
- }
40
- if (options.include) {
41
- params.include = options.include.join(',');
42
- }
43
- return this.getAllPages('/workflows', params, paginationOptions);
26
+ async getPage(options = {}) {
27
+ this.debugLog('workflows.getPage', { options });
28
+ return this.getList('/workflows', {
29
+ where: options.where,
30
+ include: options.include,
31
+ per_page: options.perPage,
32
+ page: options.page,
33
+ order: options.order
34
+ });
44
35
  }
45
36
  /**
46
37
  * Get a single workflow by ID
47
38
  */
48
39
  async getById(id, include) {
49
- const params = {};
50
- if (include) {
51
- params.include = include.join(',');
52
- }
53
- return this.getSingle(`/workflows/${id}`, params);
40
+ this.debugLog('workflows.getById', { id, include });
41
+ return this.getSingle(`/workflows/${id}`, include);
54
42
  }
55
43
  /**
56
44
  * Create a workflow
57
45
  */
58
46
  async create(data) {
47
+ this.debugLog('workflows.create', { data });
59
48
  return this.createResource('/workflows', data);
60
49
  }
61
50
  /**
62
51
  * Update a workflow
63
52
  */
64
53
  async update(id, data) {
54
+ this.debugLog('workflows.update', { id, data });
65
55
  return this.updateResource(`/workflows/${id}`, data);
66
56
  }
67
57
  /**
68
58
  * Delete a workflow
69
59
  */
70
60
  async delete(id) {
61
+ this.debugLog('workflows.delete', { id });
71
62
  return this.deleteResource(`/workflows/${id}`);
72
63
  }
73
64
  /**
74
65
  * Get workflow cards for a person
66
+ * @param personId - The person ID
67
+ * @param options - Optional pagination options
75
68
  */
76
- async getPersonWorkflowCards(personId) {
77
- return this.getList(`/people/${personId}/workflow_cards`);
69
+ async getPersonWorkflowCards(personId, options) {
70
+ this.debugLog('workflows.getPersonWorkflowCards', { personId, options });
71
+ return this.getList(`/people/${personId}/workflow_cards`, options);
78
72
  }
79
73
  /**
80
74
  * Add a person to a workflow with smart duplicate detection
81
75
  */
82
76
  async addPersonToWorkflow(personId, workflowId, options = {}) {
77
+ this.debugLog('workflows.addPersonToWorkflow', { personId, workflowId, options });
83
78
  const { skipIfExists = true, skipIfActive = true } = options;
84
79
  // Check for existing workflow cards if requested
85
80
  if (skipIfExists || skipIfActive) {
86
81
  const existingCards = await this.getPersonWorkflowCards(personId);
87
82
  const existingCard = existingCards.data.find(card => {
88
- const workflowData = card.relationships?.workflow?.data;
83
+ // workflow may be Relationship (with .data) or flattened resource/identifier; support both
84
+ const raw = card?.workflow;
85
+ const workflowData = raw && 'id' in raw ? raw : raw?.data;
89
86
  return workflowData && !Array.isArray(workflowData) && workflowData.id === workflowId;
90
87
  });
91
- if (existingCard && existingCard.attributes) {
88
+ if (existingCard) {
92
89
  // Check if card is completed or removed
93
- if (skipIfExists && (existingCard.attributes.completed_at || existingCard.attributes.removed_at)) {
90
+ if (skipIfExists && (existingCard.completed_at || existingCard.removed_at)) {
94
91
  throw new Error(`Person already has a completed/removed card in this workflow`);
95
92
  }
96
93
  // Check if card is active
97
- if (skipIfActive && !existingCard.attributes.completed_at && !existingCard.attributes.removed_at) {
94
+ if (skipIfActive && !existingCard.completed_at && !existingCard.removed_at) {
98
95
  throw new Error(`Person already has an active card in this workflow`);
99
96
  }
100
97
  }
@@ -114,6 +111,7 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
114
111
  * Create a workflow card
115
112
  */
116
113
  async createWorkflowCard(workflowId, personId) {
114
+ this.debugLog('workflows.createWorkflowCard', { workflowId, personId });
117
115
  return this.createResource(`/workflows/${workflowId}/cards`, {
118
116
  person_id: personId,
119
117
  });
@@ -122,6 +120,7 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
122
120
  * Update a workflow card
123
121
  */
124
122
  async updateWorkflowCard(workflowCardId, data, personId) {
123
+ this.debugLog('workflows.updateWorkflowCard', { workflowCardId, data, personId });
125
124
  // If personId is provided, use the person-specific endpoint
126
125
  if (personId) {
127
126
  return this.updateResource(`/people/${personId}/workflow_cards/${workflowCardId}`, data);
@@ -133,30 +132,21 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
133
132
  * Get workflow card notes
134
133
  */
135
134
  async getWorkflowCardNotes(personId, workflowCardId) {
135
+ this.debugLog('workflows.getWorkflowCardNotes', { personId, workflowCardId });
136
136
  return this.getList(`/people/${personId}/workflow_cards/${workflowCardId}/notes`);
137
137
  }
138
138
  /**
139
139
  * Create a workflow card note
140
140
  */
141
141
  async createWorkflowCardNote(personId, workflowCardId, data) {
142
+ this.debugLog('workflows.createWorkflowCardNote', { personId, workflowCardId, data });
142
143
  return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/notes`, data);
143
144
  }
144
- /**
145
- * Update a workflow card note
146
- */
147
- async updateWorkflowCardNote(personId, workflowCardId, noteId, data) {
148
- return this.updateResource(`/people/${personId}/workflow_cards/${workflowCardId}/notes/${noteId}`, data);
149
- }
150
- /**
151
- * Delete a workflow card note
152
- */
153
- async deleteWorkflowCardNote(personId, workflowCardId, noteId) {
154
- return this.deleteResource(`/people/${personId}/workflow_cards/${workflowCardId}/notes/${noteId}`);
155
- }
156
145
  /**
157
146
  * Create a workflow card with a note
158
147
  */
159
148
  async createWorkflowCardWithNote(workflowId, personId, noteData) {
149
+ this.debugLog('workflows.createWorkflowCardWithNote', { workflowId, personId, noteData });
160
150
  const workflowCard = await this.createWorkflowCard(workflowId, personId);
161
151
  const note = await this.createWorkflowCardNote(personId, workflowCard.id, noteData);
162
152
  return { workflowCard, note };
@@ -165,48 +155,56 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
165
155
  * Move a workflow card back to the previous step
166
156
  */
167
157
  async goBackWorkflowCard(personId, workflowCardId) {
158
+ this.debugLog('workflows.goBackWorkflowCard', { personId, workflowCardId });
168
159
  return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/go_back`, {});
169
160
  }
170
161
  /**
171
162
  * Move a workflow card to the next step
172
163
  */
173
164
  async promoteWorkflowCard(personId, workflowCardId) {
165
+ this.debugLog('workflows.promoteWorkflowCard', { personId, workflowCardId });
174
166
  return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/promote`, {});
175
167
  }
176
168
  /**
177
169
  * Remove a workflow card
178
170
  */
179
171
  async removeWorkflowCard(personId, workflowCardId) {
172
+ this.debugLog('workflows.removeWorkflowCard', { personId, workflowCardId });
180
173
  return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/remove`, {});
181
174
  }
182
175
  /**
183
176
  * Restore a workflow card
184
177
  */
185
178
  async restoreWorkflowCard(personId, workflowCardId) {
179
+ this.debugLog('workflows.restoreWorkflowCard', { personId, workflowCardId });
186
180
  return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/restore`, {});
187
181
  }
188
182
  /**
189
183
  * Send an email to the subject of the workflow card
190
184
  */
191
185
  async sendEmailWorkflowCard(personId, workflowCardId, data) {
186
+ this.debugLog('workflows.sendEmailWorkflowCard', { personId, workflowCardId, data });
192
187
  return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/send_email`, data);
193
188
  }
194
189
  /**
195
190
  * Move a workflow card to the next step without completing the current step
196
191
  */
197
192
  async skipStepWorkflowCard(personId, workflowCardId) {
193
+ this.debugLog('workflows.skipStepWorkflowCard', { personId, workflowCardId });
198
194
  return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/skip_step`, {});
199
195
  }
200
196
  /**
201
197
  * Snooze a workflow card for a specific duration
202
198
  */
203
199
  async snoozeWorkflowCard(personId, workflowCardId, data) {
200
+ this.debugLog('workflows.snoozeWorkflowCard', { personId, workflowCardId, data });
204
201
  return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/snooze`, data);
205
202
  }
206
203
  /**
207
204
  * Unsnooze a workflow card
208
205
  */
209
206
  async unsnoozeWorkflowCard(personId, workflowCardId) {
207
+ this.debugLog('workflows.unsnoozeWorkflowCard', { personId, workflowCardId });
210
208
  return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/unsnooze`, {});
211
209
  }
212
210
  /**
@@ -214,7 +212,8 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
214
212
  */
215
213
  formatNoteTemplate(template, variables) {
216
214
  return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
217
- return variables[key] || match;
215
+ const value = variables[key];
216
+ return value !== undefined ? String(value) : match;
218
217
  });
219
218
  }
220
219
  }
@@ -4,16 +4,20 @@
4
4
  * This module provides utilities for optimizing performance when working
5
5
  * with large datasets and high-volume API operations.
6
6
  */
7
- import type { PcoClientState } from './core';
8
- import type { EmailResource, PersonResource, PhoneNumberResource } from './types';
7
+ import type { PcoClient } from './client';
8
+ import type { EmailResource, PersonResource, PhoneNumberResource, PeopleList } from './types';
9
+ import type { PersonListOptions, PersonWhereClause } from './types/api-options';
9
10
  /**
10
- * Process items in batches to avoid overwhelming the API
11
+ * Process items in batches to avoid overwhelming the API.
12
+ * Pass `client` in options to log batch progress when debug is enabled.
11
13
  */
12
- export declare function processInBatches<T, R>(items: T[], batchSize: number, processor: (batch: T[]) => Promise<R[]>): Promise<R[]>;
14
+ export declare function processInBatches<T, R>(items: T[], batchSize: number, processor: (batch: T[]) => Promise<R[]>, options?: {
15
+ client?: PcoClient;
16
+ }): Promise<R[]>;
13
17
  /**
14
18
  * Batch fetch person details with related data
15
19
  */
16
- export declare function batchFetchPersonDetails(client: PcoClientState, personIds: string[], options?: {
20
+ export declare function batchFetchPersonDetails(client: PcoClient, personIds: string[], options?: {
17
21
  includeEmails?: boolean;
18
22
  includePhones?: boolean;
19
23
  batchSize?: number;
@@ -35,11 +39,11 @@ export declare class ApiCache {
35
39
  /**
36
40
  * Cached version of getPeople with configurable TTL
37
41
  */
38
- export declare function getCachedPeople(client: PcoClientState, cache: ApiCache, params?: any, ttlMs?: number): Promise<any>;
42
+ export declare function getCachedPeople(client: PcoClient, cache: ApiCache, params?: PersonListOptions, ttlMs?: number): Promise<PeopleList>;
39
43
  /**
40
44
  * Efficiently fetch all pages of data with progress tracking
41
45
  */
42
- export declare function fetchAllPages<T>(client: PcoClientState, fetchFunction: (page: number, perPage: number) => Promise<{
46
+ export declare function fetchAllPages<T>(client: PcoClient, fetchFunction: (page: number, perPage: number) => Promise<{
43
47
  data: T[];
44
48
  links?: {
45
49
  next?: string;
@@ -55,16 +59,16 @@ export declare function fetchAllPages<T>(client: PcoClientState, fetchFunction:
55
59
  /**
56
60
  * Stream large datasets with backpressure control
57
61
  */
58
- export declare function streamPeopleData(client: PcoClientState, options?: {
62
+ export declare function streamPeopleData(client: PcoClient, options?: {
59
63
  perPage?: number;
60
64
  maxConcurrent?: number;
61
- where?: Record<string, any>;
65
+ where?: PersonWhereClause;
62
66
  include?: string[];
63
67
  }): AsyncGenerator<PersonResource[], void, unknown>;
64
68
  /**
65
69
  * Process large datasets without loading everything into memory
66
70
  */
67
- export declare function processLargeDataset<T, R>(client: PcoClientState, fetchFunction: (page: number, perPage: number) => Promise<{
71
+ export declare function processLargeDataset<T, R>(client: PcoClient, fetchFunction: (page: number, perPage: number) => Promise<{
68
72
  data: T[];
69
73
  links?: {
70
74
  next?: string;
@@ -14,17 +14,29 @@ exports.fetchAllPages = fetchAllPages;
14
14
  exports.streamPeopleData = streamPeopleData;
15
15
  exports.processLargeDataset = processLargeDataset;
16
16
  exports.monitorPerformance = monitorPerformance;
17
- const people_1 = require("./people");
17
+ const planning_center_base_ts_1 = require("@rachelallyson/planning-center-base-ts");
18
18
  // ===== Batch Processing =====
19
19
  /**
20
- * Process items in batches to avoid overwhelming the API
20
+ * Process items in batches to avoid overwhelming the API.
21
+ * Pass `client` in options to log batch progress when debug is enabled.
21
22
  */
22
- async function processInBatches(items, batchSize, processor) {
23
+ async function processInBatches(items, batchSize, processor, options) {
24
+ const logger = options?.client && typeof options.client.getConfig === 'function'
25
+ ? (0, planning_center_base_ts_1.createDebugLogger)(options.client.getConfig())
26
+ : null;
27
+ if (logger?.enabled) {
28
+ logger.log('performance.processInBatches', { itemCount: items.length, batchSize, batchCount: Math.ceil(items.length / batchSize) });
29
+ }
23
30
  const results = [];
24
31
  for (let i = 0; i < items.length; i += batchSize) {
25
32
  const batch = items.slice(i, i + batchSize);
33
+ const batchIndex = Math.floor(i / batchSize) + 1;
34
+ if (logger?.enabled)
35
+ logger.log('performance.processInBatches batch start', { batchIndex, batchSize: batch.length });
26
36
  const batchResults = await processor(batch);
27
37
  results.push(...batchResults);
38
+ if (logger?.enabled)
39
+ logger.log('performance.processInBatches batch complete', { batchIndex, resultCount: batchResults.length, totalSoFar: results.length });
28
40
  // Add a small delay between batches to respect rate limits
29
41
  if (i + batchSize < items.length) {
30
42
  await new Promise(resolve => setTimeout(resolve, 100));
@@ -36,31 +48,35 @@ async function processInBatches(items, batchSize, processor) {
36
48
  * Batch fetch person details with related data
37
49
  */
38
50
  async function batchFetchPersonDetails(client, personIds, options = {}) {
51
+ const logger = (0, planning_center_base_ts_1.createDebugLogger)(typeof client.getConfig === 'function'
52
+ ? client.getConfig()
53
+ : undefined);
54
+ if (logger.enabled)
55
+ logger.log('performance.batchFetchPersonDetails', { personCount: personIds.length, options });
39
56
  const { batchSize = 10, includeEmails = true, includePhones = true, } = options;
40
57
  const results = new Map();
41
58
  await processInBatches(personIds, batchSize, async (batch) => {
42
59
  const batchResults = await Promise.all(batch.map(async (personId) => {
43
- const personPromise = (0, people_1.getPeople)(client, {
44
- per_page: 1,
45
- where: { id: personId },
46
- });
47
- const promises_array = [personPromise];
48
- if (includeEmails) {
49
- promises_array.push((0, people_1.getPersonEmails)(client, personId));
50
- }
51
- if (includePhones) {
52
- promises_array.push((0, people_1.getPersonPhoneNumbers)(client, personId));
53
- }
54
- const responses = await Promise.all(promises_array);
55
- const person = responses[0].data[0];
60
+ const personPromise = client.people.getById(personId);
61
+ const emailPromise = includeEmails ? client.people.getEmails(personId) : null;
62
+ const phonePromise = includePhones ? client.people.getPhoneNumbers(personId) : null;
63
+ const promises = [personPromise];
64
+ if (emailPromise)
65
+ promises.push(emailPromise);
66
+ if (phonePromise)
67
+ promises.push(phonePromise);
68
+ const responses = await Promise.all(promises);
69
+ const person = responses[0];
56
70
  if (!person)
57
71
  return null;
58
72
  const result = { person };
59
- if (includeEmails) {
60
- result.emails = responses[1]?.data || [];
73
+ if (includeEmails && emailPromise) {
74
+ const emailsResponse = await emailPromise;
75
+ result.emails = emailsResponse.data || [];
61
76
  }
62
- if (includePhones) {
63
- result.phoneNumbers = responses[includeEmails ? 2 : 1]?.data || [];
77
+ if (includePhones && phonePromise) {
78
+ const phonesResponse = await phonePromise;
79
+ result.phoneNumbers = phonesResponse.data || [];
64
80
  }
65
81
  return { personId, result };
66
82
  }));
@@ -70,7 +86,7 @@ async function batchFetchPersonDetails(client, personIds, options = {}) {
70
86
  }
71
87
  });
72
88
  return batchResults; // Return for processInBatches
73
- });
89
+ }, { client });
74
90
  return results;
75
91
  }
76
92
  // ===== Caching =====
@@ -111,12 +127,17 @@ exports.ApiCache = ApiCache;
111
127
  * Cached version of getPeople with configurable TTL
112
128
  */
113
129
  async function getCachedPeople(client, cache, params = {}, ttlMs = 300000) {
130
+ const logger = (0, planning_center_base_ts_1.createDebugLogger)(typeof client.getConfig === 'function'
131
+ ? client.getConfig()
132
+ : undefined);
133
+ if (logger.enabled)
134
+ logger.log('performance.getCachedPeople', { params, ttlMs });
114
135
  const cacheKey = `people:${JSON.stringify(params)}`;
115
136
  const cached = cache.get(cacheKey);
116
137
  if (cached) {
117
138
  return cached;
118
139
  }
119
- const result = await (0, people_1.getPeople)(client, params);
140
+ const result = await client.people.getAll(params);
120
141
  cache.set(cacheKey, result, ttlMs);
121
142
  return result;
122
143
  }
@@ -125,6 +146,11 @@ async function getCachedPeople(client, cache, params = {}, ttlMs = 300000) {
125
146
  * Efficiently fetch all pages of data with progress tracking
126
147
  */
127
148
  async function fetchAllPages(client, fetchFunction, options = {}) {
149
+ const logger = (0, planning_center_base_ts_1.createDebugLogger)(typeof client.getConfig === 'function'
150
+ ? client.getConfig()
151
+ : undefined);
152
+ if (logger.enabled)
153
+ logger.log('performance.fetchAllPages', { options });
128
154
  const { maxPages = 1000, onProgress, perPage = 100 } = options;
129
155
  const allData = [];
130
156
  let page = 1;
@@ -152,6 +178,11 @@ async function fetchAllPages(client, fetchFunction, options = {}) {
152
178
  * Stream large datasets with backpressure control
153
179
  */
154
180
  async function* streamPeopleData(client, options = {}) {
181
+ const logger = (0, planning_center_base_ts_1.createDebugLogger)(typeof client.getConfig === 'function'
182
+ ? client.getConfig()
183
+ : undefined);
184
+ if (logger.enabled)
185
+ logger.log('performance.streamPeopleData', { options });
155
186
  const { include = [], maxConcurrent = 3, perPage = 50, where = {} } = options;
156
187
  let page = 1;
157
188
  let hasMore = true;
@@ -159,10 +190,10 @@ async function* streamPeopleData(client, options = {}) {
159
190
  while (hasMore) {
160
191
  await semaphore.acquire();
161
192
  try {
162
- const response = await (0, people_1.getPeople)(client, {
163
- include,
193
+ const response = await client.people.getPage({
194
+ include: include,
164
195
  page,
165
- per_page: perPage,
196
+ perPage,
166
197
  where,
167
198
  });
168
199
  yield response.data;
@@ -203,6 +234,11 @@ class Semaphore {
203
234
  * Process large datasets without loading everything into memory
204
235
  */
205
236
  async function processLargeDataset(client, fetchFunction, processor, options = {}) {
237
+ const logger = (0, planning_center_base_ts_1.createDebugLogger)(typeof client.getConfig === 'function'
238
+ ? client.getConfig()
239
+ : undefined);
240
+ if (logger.enabled)
241
+ logger.log('performance.processLargeDataset', { options });
206
242
  const { maxMemoryItems = 1000, onBatchProcessed, perPage = 100 } = options;
207
243
  const allResults = [];
208
244
  let page = 1;
@@ -37,6 +37,7 @@ var __importStar = (this && this.__importStar) || (function () {
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.RequestRecorder = void 0;
40
+ const planning_center_base_ts_1 = require("@rachelallyson/planning-center-base-ts");
40
41
  const fs = __importStar(require("fs"));
41
42
  const path = __importStar(require("path"));
42
43
  class RequestRecorder {
@@ -133,7 +134,11 @@ class RequestRecorder {
133
134
  fs.writeFileSync(this.config.recordPath, JSON.stringify(session, null, 2));
134
135
  }
135
136
  catch (error) {
136
- console.error('Failed to save recording session:', error);
137
+ const logger = (0, planning_center_base_ts_1.createDebugLogger)(this.config.getConfig?.());
138
+ if (logger.enabled)
139
+ logger.log('recorder failed to save recording session', { error: String(error) });
140
+ else
141
+ console.error('Failed to save recording session:', error);
137
142
  }
138
143
  }
139
144
  /**
@@ -148,7 +153,11 @@ class RequestRecorder {
148
153
  return JSON.parse(content);
149
154
  }
150
155
  catch (error) {
151
- console.error('Failed to load recording session:', error);
156
+ const logger = (0, planning_center_base_ts_1.createDebugLogger)(this.config.getConfig?.());
157
+ if (logger.enabled)
158
+ logger.log('recorder failed to load recording session', { error: String(error) });
159
+ else
160
+ console.error('Failed to load recording session:', error);
152
161
  return null;
153
162
  }
154
163
  }
@@ -1,21 +1,23 @@
1
1
  /**
2
2
  * Simplified Mock Response Builders for Testing
3
+ *
4
+ * Builders return flattened shape (same as API): attributes and relationships at top level.
3
5
  */
4
6
  export declare class SimpleMockResponseBuilder {
5
7
  /**
6
- * Build a simple mock person resource
8
+ * Build a simple mock person resource (flattened shape)
7
9
  */
8
10
  static person(overrides?: any): any;
9
11
  /**
10
- * Build a simple mock email resource
12
+ * Build a simple mock email resource (flattened shape)
11
13
  */
12
14
  static email(overrides?: any): any;
13
15
  /**
14
- * Build a simple mock phone number resource
16
+ * Build a simple mock phone number resource (flattened shape)
15
17
  */
16
18
  static phoneNumber(overrides?: any): any;
17
19
  /**
18
- * Build a simple mock workflow resource
20
+ * Build a simple mock workflow resource (flattened shape)
19
21
  */
20
22
  static workflow(overrides?: any): any;
21
23
  /**
@@ -1,85 +1,72 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Simplified Mock Response Builders for Testing
4
+ *
5
+ * Builders return flattened shape (same as API): attributes and relationships at top level.
4
6
  */
5
7
  Object.defineProperty(exports, "__esModule", { value: true });
6
8
  exports.SimpleMockResponseBuilder = void 0;
7
9
  class SimpleMockResponseBuilder {
8
10
  /**
9
- * Build a simple mock person resource
11
+ * Build a simple mock person resource (flattened shape)
10
12
  */
11
13
  static person(overrides = {}) {
14
+ const id = overrides.id || `person_${Date.now()}`;
12
15
  return {
13
16
  type: 'Person',
14
- id: overrides.id || `person_${Date.now()}`,
15
- attributes: {
16
- id: overrides.id || `person_${Date.now()}`,
17
- first_name: overrides.first_name || 'John',
18
- last_name: overrides.last_name || 'Doe',
19
- status: 'active',
20
- ...overrides,
21
- },
22
- relationships: {
23
- emails: { data: [] },
24
- phone_numbers: { data: [] },
25
- field_data: { data: [] },
26
- workflow_cards: { data: [] },
27
- household: { data: null },
28
- },
17
+ id,
18
+ first_name: overrides.first_name ?? 'John',
19
+ last_name: overrides.last_name ?? 'Doe',
20
+ status: 'active',
21
+ ...overrides,
22
+ emails: [],
23
+ phone_numbers: [],
24
+ field_data: [],
25
+ workflow_cards: [],
26
+ household: null,
29
27
  };
30
28
  }
31
29
  /**
32
- * Build a simple mock email resource
30
+ * Build a simple mock email resource (flattened shape)
33
31
  */
34
32
  static email(overrides = {}) {
33
+ const id = overrides.id || `email_${Date.now()}`;
35
34
  return {
36
35
  type: 'Email',
37
- id: overrides.id || `email_${Date.now()}`,
38
- attributes: {
39
- id: overrides.id || `email_${Date.now()}`,
40
- address: overrides.address || 'john@example.com',
41
- location: 'Home',
42
- primary: true,
43
- ...overrides,
44
- },
45
- relationships: {
46
- person: { data: { type: 'Person', id: 'person_123' } },
47
- },
36
+ id,
37
+ address: overrides.address ?? 'john@gmail.com',
38
+ location: 'Home',
39
+ primary: true,
40
+ ...overrides,
41
+ person: { type: 'Person', id: 'person_123' },
48
42
  };
49
43
  }
50
44
  /**
51
- * Build a simple mock phone number resource
45
+ * Build a simple mock phone number resource (flattened shape)
52
46
  */
53
47
  static phoneNumber(overrides = {}) {
48
+ const id = overrides.id || `phone_${Date.now()}`;
54
49
  return {
55
50
  type: 'PhoneNumber',
56
- id: overrides.id || `phone_${Date.now()}`,
57
- attributes: {
58
- id: overrides.id || `phone_${Date.now()}`,
59
- number: overrides.number || '555-1234',
60
- location: 'Mobile',
61
- primary: true,
62
- ...overrides,
63
- },
64
- relationships: {
65
- person: { data: { type: 'Person', id: 'person_123' } },
66
- },
51
+ id,
52
+ number: overrides.number ?? '555-1234',
53
+ location: 'Mobile',
54
+ primary: true,
55
+ ...overrides,
56
+ person: { type: 'Person', id: 'person_123' },
67
57
  };
68
58
  }
69
59
  /**
70
- * Build a simple mock workflow resource
60
+ * Build a simple mock workflow resource (flattened shape)
71
61
  */
72
62
  static workflow(overrides = {}) {
63
+ const id = overrides.id || `workflow_${Date.now()}`;
73
64
  return {
74
65
  type: 'Workflow',
75
- id: overrides.id || `workflow_${Date.now()}`,
76
- attributes: {
77
- id: overrides.id || `workflow_${Date.now()}`,
78
- name: overrides.name || 'New Member Workflow',
79
- description: overrides.description || 'Workflow for new members',
80
- ...overrides,
81
- },
82
- relationships: {},
66
+ id,
67
+ name: overrides.name ?? 'New Member Workflow',
68
+ description: overrides.description ?? 'Workflow for new members',
69
+ ...overrides,
83
70
  };
84
71
  }
85
72
  /**