@rachelallyson/planning-center-people-ts 2.7.0 → 2.9.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 +174 -0
- package/LICENSE +1 -1
- package/README.md +12 -14
- package/dist/client.d.ts +8 -8
- package/dist/client.js +8 -11
- package/dist/core/http.d.ts +1 -1
- package/dist/core/http.js +18 -11
- package/dist/core/pagination.js +16 -2
- package/dist/core.d.ts +3 -3
- package/dist/core.js +4 -4
- package/dist/error-handling.d.ts +1 -1
- package/dist/error-handling.js +3 -3
- package/dist/error-scenarios.js +4 -4
- package/dist/helpers.js +5 -4
- package/dist/index.d.ts +8 -8
- package/dist/index.js +15 -15
- package/dist/matching/matcher.d.ts +22 -0
- package/dist/matching/matcher.js +260 -27
- package/dist/matching/scoring.d.ts +9 -7
- package/dist/matching/scoring.js +54 -23
- package/dist/matching/strategies.d.ts +1 -0
- package/dist/matching/strategies.js +17 -4
- package/dist/modules/campus.d.ts +5 -5
- package/dist/modules/campus.js +2 -2
- package/dist/modules/contacts.d.ts +1 -1
- package/dist/modules/contacts.js +2 -2
- package/dist/modules/fields.d.ts +4 -4
- package/dist/modules/fields.js +2 -2
- package/dist/modules/forms.d.ts +7 -4
- package/dist/modules/forms.js +5 -2
- package/dist/modules/households.d.ts +2 -2
- package/dist/modules/households.js +2 -2
- package/dist/modules/lists.d.ts +2 -2
- package/dist/modules/lists.js +2 -2
- package/dist/modules/notes.d.ts +2 -2
- package/dist/modules/notes.js +2 -2
- package/dist/modules/people.d.ts +57 -5
- package/dist/modules/people.js +44 -9
- package/dist/modules/reports.d.ts +5 -5
- package/dist/modules/reports.js +2 -2
- package/dist/modules/service-time.d.ts +5 -5
- package/dist/modules/service-time.js +2 -2
- package/dist/modules/workflows.d.ts +2 -2
- package/dist/modules/workflows.js +2 -2
- package/dist/people/contacts.d.ts +1 -1
- package/dist/people/core.d.ts +1 -1
- package/dist/people/fields.d.ts +1 -1
- package/dist/people/fields.js +4 -4
- package/dist/people/households.d.ts +1 -1
- package/dist/people/lists.d.ts +1 -1
- package/dist/people/notes.d.ts +1 -1
- package/dist/people/organization.d.ts +1 -1
- package/dist/people/workflows.d.ts +1 -1
- package/dist/types/client.d.ts +3 -96
- package/dist/types/client.js +2 -0
- package/dist/types/index.d.ts +1 -2
- package/dist/types/index.js +0 -2
- package/package.json +16 -19
- package/dist/api-error.d.ts +0 -10
- package/dist/api-error.js +0 -32
- package/dist/batch.d.ts +0 -47
- package/dist/batch.js +0 -376
- package/dist/modules/base.d.ts +0 -46
- package/dist/modules/base.js +0 -82
- package/dist/monitoring.d.ts +0 -53
- package/dist/monitoring.js +0 -142
- package/dist/rate-limiter.d.ts +0 -79
- package/dist/rate-limiter.js +0 -137
- package/dist/types/batch.d.ts +0 -50
- package/dist/types/batch.js +0 -5
- package/dist/types/events.d.ts +0 -85
- package/dist/types/events.js +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,180 @@ 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.9.0] - 2025-01-14
|
|
9
|
+
|
|
10
|
+
### 🎯 **Matching Logic Improvements**
|
|
11
|
+
|
|
12
|
+
This release significantly improves the accuracy of person matching by verifying email/phone matches and preventing incorrect name-only matches.
|
|
13
|
+
|
|
14
|
+
### 🐛 **Critical Bug Fixes**
|
|
15
|
+
|
|
16
|
+
- **🔍 Email/Phone Verification**: Fixed matching logic that was allowing name-only matches even when email/phone were provided but didn't match
|
|
17
|
+
- Previously, searching with email/phone would match people with different contact info if names matched
|
|
18
|
+
- Now requires actual email/phone verification before considering name matches
|
|
19
|
+
- **✅ Verified Contact Matching**: `scoreEmailMatch()` and `scorePhoneMatch()` now actually fetch and verify contact information instead of assuming matches
|
|
20
|
+
- **🚫 Name-Only Matching Restrictions**: Name-only matching now only occurs when:
|
|
21
|
+
- Multiple people share the same email/phone (name helps distinguish), OR
|
|
22
|
+
- No email/phone was provided in the search
|
|
23
|
+
- **🎯 Exact Match Strategy**: Made "exact" matching strategy stricter, requiring verified email/phone matches unless multiple people share the same contact info
|
|
24
|
+
|
|
25
|
+
### ✨ **New Features**
|
|
26
|
+
|
|
27
|
+
- **📝 Auto-Update Contact Info**: New `addMissingContactInfo` option in `PersonMatchOptions`
|
|
28
|
+
- When enabled, automatically adds missing email/phone to a person's profile when a match is found
|
|
29
|
+
- Missing contacts are added as non-primary to preserve existing primary contacts
|
|
30
|
+
- Helps keep person profiles up-to-date when new contact information is discovered
|
|
31
|
+
|
|
32
|
+
### 🔧 **Technical Improvements**
|
|
33
|
+
|
|
34
|
+
- **🔍 Async Verification**: Email and phone matching now uses async methods to fetch and verify actual contact information
|
|
35
|
+
- **📊 Improved Scoring**: MatchScorer now requires PeopleModule dependency to verify matches
|
|
36
|
+
- **🎯 Better Match Prioritization**: Verified email/phone matches are prioritized over name-only matches
|
|
37
|
+
- **📖 Enhanced Documentation**: Comprehensive JSDoc comments and README examples for all new features
|
|
38
|
+
|
|
39
|
+
### 📚 **Documentation**
|
|
40
|
+
|
|
41
|
+
- **📖 JSDoc Updates**: Added detailed documentation for `PersonMatchOptions` interface and `findOrCreate()` method
|
|
42
|
+
- **📝 README Examples**: Added example showing `addMissingContactInfo` usage
|
|
43
|
+
- **🔍 Method Documentation**: Updated all matching methods with comprehensive parameter descriptions
|
|
44
|
+
|
|
45
|
+
### 🎯 **Impact**
|
|
46
|
+
|
|
47
|
+
This release fixes the issue where:
|
|
48
|
+
|
|
49
|
+
- ❌ Searching with `rachel@onark.app` + `+11233853584` would incorrectly match someone with `rachel@hangar31.dev` + `+16103017206` based on name alone
|
|
50
|
+
- ❌ Email/phone matches were assumed without verification
|
|
51
|
+
- ❌ Name-only matching occurred even when email/phone were provided
|
|
52
|
+
|
|
53
|
+
Now matching:
|
|
54
|
+
|
|
55
|
+
- ✅ Verifies email/phone actually match before considering a match
|
|
56
|
+
- ✅ Only uses name matching when appropriate (multiple people share contact info or no contact info provided)
|
|
57
|
+
- ✅ Can automatically update profiles with missing contact information
|
|
58
|
+
- ✅ Provides stricter exact matching for high-confidence scenarios
|
|
59
|
+
|
|
60
|
+
## [2.8.0] - 2025-01-11
|
|
61
|
+
|
|
62
|
+
### 🎯 **CRITICAL FINDORCREATE BUG FIX**
|
|
63
|
+
|
|
64
|
+
This release fixes a critical bug in the `findOrCreate` function that was causing it to always create new people instead of finding existing ones. This was due to incorrect API parameter names and scoring issues.
|
|
65
|
+
|
|
66
|
+
### 🐛 **Critical Bug Fixes**
|
|
67
|
+
|
|
68
|
+
- **🔍 Search Parameter Names**: Fixed incorrect API parameter names in search methods
|
|
69
|
+
- Changed `where[name]` → `where[search_name]` (API now recognizes this)
|
|
70
|
+
- Changed `where[email]` → `where[search_name_or_email]` (API now recognizes this)
|
|
71
|
+
- Changed `where[phone]` → `where[search_phone_number]` (API now recognizes this)
|
|
72
|
+
- **📊 Scoring System**: Fixed email and phone scoring methods that were returning 0 instead of proper scores
|
|
73
|
+
- **🎯 Matching Thresholds**: Adjusted scoring thresholds for better name-only matching
|
|
74
|
+
- **📞 Contact Creation**: Added required `location: 'Home'` field to email and phone creation
|
|
75
|
+
|
|
76
|
+
### ✨ **New Features**
|
|
77
|
+
|
|
78
|
+
- **🔍 Flexible Search**: Implemented powerful `search_name_or_email_or_phone_number` parameter for broader matching
|
|
79
|
+
- **⚖️ Dynamic Scoring Weights**: Increased name matching weight from 0.2 to 0.4 for name-only matches
|
|
80
|
+
- **📈 Improved Thresholds**: Lowered fuzzy matching threshold from 0.7 to 0.5 for better matching
|
|
81
|
+
|
|
82
|
+
### 🔧 **Technical Improvements**
|
|
83
|
+
|
|
84
|
+
- **📝 Enhanced Error Logging**: Added detailed error messages for contact creation failures
|
|
85
|
+
- **🔍 Better Search Strategies**: Improved `getCandidates` method with better error handling
|
|
86
|
+
- **🎯 Scoring Optimization**: Fixed `scoreEmailMatch` and `scorePhoneMatch` to return 1.0 for perfect matches
|
|
87
|
+
- **🔄 HTTP Client Resilience**: Added retry limits and better error handling for rate limits and authentication failures
|
|
88
|
+
- **📄 Pagination Safety**: Added safeguards against infinite pagination loops
|
|
89
|
+
|
|
90
|
+
### 📊 **Performance & Reliability**
|
|
91
|
+
|
|
92
|
+
- **✅ Duplicate Prevention**: `findOrCreate` now properly finds existing people instead of creating duplicates
|
|
93
|
+
- **📞 Contact Integration**: New people are created with proper email and phone contacts
|
|
94
|
+
- **🔍 Search Accuracy**: All search methods now work correctly with Planning Center API
|
|
95
|
+
- **⚡ API Efficiency**: Uses correct parameter names for optimal API performance
|
|
96
|
+
|
|
97
|
+
### 🧪 **Testing & Verification**
|
|
98
|
+
|
|
99
|
+
- **✅ Real API Testing**: Verified fix works with actual Planning Center API calls
|
|
100
|
+
- **✅ All Tests Pass**: 257/257 tests passing with no regressions
|
|
101
|
+
- **✅ Integration Tests**: Created comprehensive integration tests for `findOrCreate` functionality
|
|
102
|
+
- **✅ Live Verification**: Confirmed fix works in production Planning Center environment
|
|
103
|
+
|
|
104
|
+
### 📚 **Documentation**
|
|
105
|
+
|
|
106
|
+
- **📖 Migration Guide**: Created comprehensive guide for simplifying `getPCOPerson` functions
|
|
107
|
+
- **🔧 Code Examples**: Added examples showing before/after migration patterns
|
|
108
|
+
- **📋 API Documentation**: Updated documentation to reflect correct parameter usage
|
|
109
|
+
- **🧪 Integration Tests**: Added comprehensive integration tests for `findOrCreate` functionality
|
|
110
|
+
|
|
111
|
+
### 🎯 **Impact**
|
|
112
|
+
|
|
113
|
+
This fix resolves the core issue where `findOrCreate` was:
|
|
114
|
+
|
|
115
|
+
- ❌ Always creating new people (instead of finding existing ones)
|
|
116
|
+
- ❌ Creating people without contact information
|
|
117
|
+
- ❌ Using incorrect API parameters that Planning Center ignored
|
|
118
|
+
- ❌ Scoring matches incorrectly (always 0 for email/phone)
|
|
119
|
+
|
|
120
|
+
Now `findOrCreate`:
|
|
121
|
+
|
|
122
|
+
- ✅ Properly finds existing people by email, phone, and name
|
|
123
|
+
- ✅ Creates new people with complete contact information
|
|
124
|
+
- ✅ Uses correct API parameters that Planning Center recognizes
|
|
125
|
+
- ✅ Scores matches accurately for proper duplicate prevention
|
|
126
|
+
|
|
127
|
+
### 🔧 **Additional Improvements**
|
|
128
|
+
|
|
129
|
+
- **🔄 HTTP Client Enhancements**:
|
|
130
|
+
- Added retry limits for rate limit errors (max 5 retries)
|
|
131
|
+
- Added retry limits for authentication failures (max 3 retries)
|
|
132
|
+
- Improved error handling for token refresh failures
|
|
133
|
+
- **📄 Pagination Improvements**:
|
|
134
|
+
- Added safeguards against infinite pagination loops
|
|
135
|
+
- Better detection of same-page pagination issues
|
|
136
|
+
- Enhanced logging for pagination problems
|
|
137
|
+
|
|
138
|
+
### 📁 **Files Modified**
|
|
139
|
+
|
|
140
|
+
**Core Library Files**:
|
|
141
|
+
|
|
142
|
+
- `src/modules/people.ts` - Fixed search parameter names and implemented flexible search
|
|
143
|
+
- `src/helpers.ts` - Updated searchPeople helper with correct parameter names
|
|
144
|
+
- `src/matching/matcher.ts` - Enhanced error logging and contact creation with location field
|
|
145
|
+
- `src/matching/scoring.ts` - Fixed email/phone scoring methods and improved name matching weights
|
|
146
|
+
- `src/matching/strategies.ts` - Adjusted matching thresholds for better accuracy
|
|
147
|
+
- `src/core/http.ts` - Added retry limits and improved error handling
|
|
148
|
+
- `src/core/pagination.ts` - Added safeguards against infinite pagination loops
|
|
149
|
+
|
|
150
|
+
**Documentation & Testing**:
|
|
151
|
+
|
|
152
|
+
- `CHANGELOG.md` - Comprehensive documentation of all changes
|
|
153
|
+
- `package.json` - Updated version to 2.8.0
|
|
154
|
+
- `MIGRATION_GUIDE.md` - Complete guide for simplifying getPCOPerson functions
|
|
155
|
+
- `tests/integration/findorcreate-fix.integration.test.ts` - New integration tests for findOrCreate
|
|
156
|
+
|
|
157
|
+
### 🎯 **Release Summary**
|
|
158
|
+
|
|
159
|
+
This release represents a **major reliability improvement** for the Planning Center People API client. The critical `findOrCreate` bug that was causing duplicate person creation has been completely resolved, along with several additional stability improvements.
|
|
160
|
+
|
|
161
|
+
**Key Metrics**:
|
|
162
|
+
|
|
163
|
+
- ✅ **100% Test Coverage**: All 257 existing tests pass with no regressions
|
|
164
|
+
- ✅ **Real API Verified**: Tested with actual Planning Center API calls
|
|
165
|
+
- ✅ **Production Ready**: Confirmed working in live Planning Center environment
|
|
166
|
+
- ✅ **Backward Compatible**: No breaking changes, existing code works unchanged
|
|
167
|
+
- ✅ **Performance Improved**: Fewer API calls, better error handling, more reliable
|
|
168
|
+
|
|
169
|
+
**For Users**:
|
|
170
|
+
|
|
171
|
+
- **Immediate Benefit**: `findOrCreate` now works as originally intended
|
|
172
|
+
- **No Code Changes Required**: Existing implementations automatically benefit
|
|
173
|
+
- **Better Reliability**: Enhanced error handling and retry logic
|
|
174
|
+
- **Simplified Code**: Can remove complex workarounds (see Migration Guide)
|
|
175
|
+
|
|
176
|
+
### 🚀 **Breaking Changes**
|
|
177
|
+
|
|
178
|
+
- **None**: This is a bug fix release with no breaking changes
|
|
179
|
+
- **Backward Compatible**: All existing code continues to work
|
|
180
|
+
- **Enhanced Functionality**: Existing `findOrCreate` calls now work as originally intended
|
|
181
|
+
|
|
8
182
|
## [2.7.0] - 2025-01-11
|
|
9
183
|
|
|
10
184
|
### 🚀 **RATE LIMITING IMPROVEMENTS**
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A modern, type-safe TypeScript library for interacting with the Planning Center Online People API. Built with a class-based architecture, comprehensive error handling, and advanced features like person matching and batch operations.
|
|
4
4
|
|
|
5
|
-
> **📖 For the latest documentation and examples, see [
|
|
5
|
+
> **📖 For the latest documentation and examples, see the [Monorepo Documentation Site](../../docs/content/index.mdx)**
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
@@ -519,19 +519,17 @@ See [TYPE_VALIDATION_SUMMARY.md](./TYPE_VALIDATION_SUMMARY.md) for detailed docu
|
|
|
519
519
|
|
|
520
520
|
## 📚 Comprehensive Documentation
|
|
521
521
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
- **[📖
|
|
525
|
-
- **[🚀
|
|
526
|
-
- **[
|
|
527
|
-
- **[
|
|
528
|
-
- **[
|
|
529
|
-
- **[
|
|
530
|
-
- **[
|
|
531
|
-
- **[
|
|
532
|
-
- **[
|
|
533
|
-
- **[🔄 Migration Guide](./docs/MIGRATION.md)** - Switching from other libraries
|
|
534
|
-
- **[⭐ Best Practices](./docs/BEST_PRACTICES.md)** - Production-ready patterns and security
|
|
522
|
+
Complete documentation is available in the monorepo documentation site:
|
|
523
|
+
|
|
524
|
+
- **[📖 Documentation Index](../../docs/content/index.mdx)** - Complete documentation entry point
|
|
525
|
+
- **[🚀 Quick Start Guide](../../docs/content/guides/quickstart.mdx)** - Get started in 5 minutes
|
|
526
|
+
- **[📋 API Reference](../../docs/content/api/)** - Complete TypeScript API documentation
|
|
527
|
+
- **[⚙️ Configuration Reference](../../docs/content/reference/config.mdx)** - All configuration options
|
|
528
|
+
- **[💡 Examples & Recipes](../../docs/content/recipes/examples.mdx)** - Copy-paste code snippets
|
|
529
|
+
- **[🛠️ Error Handling Guide](../../docs/content/guides/error-handling.mdx)** - Comprehensive error handling
|
|
530
|
+
- **[📄 Pagination Guide](../../docs/content/guides/pagination.mdx)** - Handling paginated responses
|
|
531
|
+
- **[🔧 Troubleshooting](../../docs/content/troubleshooting.mdx)** - Common issues and solutions
|
|
532
|
+
- **[🏗️ Core Concepts](../../docs/content/concepts.mdx)** - Architecture and mental models
|
|
535
533
|
|
|
536
534
|
## License
|
|
537
535
|
|
package/dist/client.d.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* v2.0.0 Main PcoClient Class
|
|
3
3
|
*/
|
|
4
4
|
import type { PcoClientConfig } from './types/client';
|
|
5
|
-
import type { EventEmitter } from '
|
|
5
|
+
import type { EventEmitter, PcoEvent, EventHandler, EventType } from '@rachelallyson/planning-center-base-ts';
|
|
6
|
+
import { BatchExecutor } from '@rachelallyson/planning-center-base-ts';
|
|
6
7
|
import { PeopleModule } from './modules/people';
|
|
7
8
|
import { FieldsModule } from './modules/fields';
|
|
8
9
|
import { WorkflowsModule } from './modules/workflows';
|
|
@@ -14,7 +15,6 @@ import { CampusModule } from './modules/campus';
|
|
|
14
15
|
import { ServiceTimeModule } from './modules/service-time';
|
|
15
16
|
import { FormsModule } from './modules/forms';
|
|
16
17
|
import { ReportsModule } from './modules/reports';
|
|
17
|
-
import { BatchExecutor } from './batch';
|
|
18
18
|
export declare class PcoClient implements EventEmitter {
|
|
19
19
|
people: PeopleModule;
|
|
20
20
|
fields: FieldsModule;
|
|
@@ -33,9 +33,9 @@ export declare class PcoClient implements EventEmitter {
|
|
|
33
33
|
private eventEmitter;
|
|
34
34
|
private config;
|
|
35
35
|
constructor(config: PcoClientConfig);
|
|
36
|
-
on<T extends
|
|
37
|
-
off<T extends
|
|
38
|
-
emit<T extends
|
|
36
|
+
on<T extends PcoEvent>(eventType: T['type'], handler: EventHandler<T>): void;
|
|
37
|
+
off<T extends PcoEvent>(eventType: T['type'], handler: EventHandler<T>): void;
|
|
38
|
+
emit<T extends PcoEvent>(event: T): void;
|
|
39
39
|
/**
|
|
40
40
|
* Get the current configuration
|
|
41
41
|
*/
|
|
@@ -65,15 +65,15 @@ export declare class PcoClient implements EventEmitter {
|
|
|
65
65
|
/**
|
|
66
66
|
* Clear all event listeners
|
|
67
67
|
*/
|
|
68
|
-
removeAllListeners(eventType?:
|
|
68
|
+
removeAllListeners(eventType?: EventType): void;
|
|
69
69
|
/**
|
|
70
70
|
* Get the number of listeners for an event type
|
|
71
71
|
*/
|
|
72
|
-
listenerCount(eventType:
|
|
72
|
+
listenerCount(eventType: EventType): number;
|
|
73
73
|
/**
|
|
74
74
|
* Get all registered event types
|
|
75
75
|
*/
|
|
76
|
-
eventTypes():
|
|
76
|
+
eventTypes(): EventType[];
|
|
77
77
|
private setupEventHandlers;
|
|
78
78
|
private updateModules;
|
|
79
79
|
}
|
package/dist/client.js
CHANGED
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.PcoClient = void 0;
|
|
7
|
-
const
|
|
8
|
-
const http_1 = require("./core/http");
|
|
9
|
-
const pagination_1 = require("./core/pagination");
|
|
7
|
+
const planning_center_base_ts_1 = require("@rachelallyson/planning-center-base-ts");
|
|
10
8
|
const people_1 = require("./modules/people");
|
|
11
9
|
const fields_1 = require("./modules/fields");
|
|
12
10
|
const workflows_1 = require("./modules/workflows");
|
|
@@ -18,13 +16,12 @@ const campus_1 = require("./modules/campus");
|
|
|
18
16
|
const service_time_1 = require("./modules/service-time");
|
|
19
17
|
const forms_1 = require("./modules/forms");
|
|
20
18
|
const reports_1 = require("./modules/reports");
|
|
21
|
-
const batch_1 = require("./batch");
|
|
22
19
|
class PcoClient {
|
|
23
20
|
constructor(config) {
|
|
24
21
|
this.config = config;
|
|
25
|
-
this.eventEmitter = new
|
|
26
|
-
this.httpClient = new
|
|
27
|
-
this.paginationHelper = new
|
|
22
|
+
this.eventEmitter = new planning_center_base_ts_1.PcoEventEmitter();
|
|
23
|
+
this.httpClient = new planning_center_base_ts_1.PcoHttpClient(config, this.eventEmitter);
|
|
24
|
+
this.paginationHelper = new planning_center_base_ts_1.PaginationHelper(this.httpClient);
|
|
28
25
|
// Initialize modules
|
|
29
26
|
this.people = new people_1.PeopleModule(this.httpClient, this.paginationHelper, this.eventEmitter);
|
|
30
27
|
this.fields = new fields_1.FieldsModule(this.httpClient, this.paginationHelper, this.eventEmitter);
|
|
@@ -37,7 +34,7 @@ class PcoClient {
|
|
|
37
34
|
this.serviceTime = new service_time_1.ServiceTimeModule(this.httpClient, this.paginationHelper, this.eventEmitter);
|
|
38
35
|
this.forms = new forms_1.FormsModule(this.httpClient, this.paginationHelper, this.eventEmitter);
|
|
39
36
|
this.reports = new reports_1.ReportsModule(this.httpClient, this.paginationHelper, this.eventEmitter);
|
|
40
|
-
this.batch = new
|
|
37
|
+
this.batch = new planning_center_base_ts_1.BatchExecutor(this, this.eventEmitter);
|
|
41
38
|
// Set up event handlers from config
|
|
42
39
|
this.setupEventHandlers();
|
|
43
40
|
}
|
|
@@ -63,8 +60,8 @@ class PcoClient {
|
|
|
63
60
|
updateConfig(updates) {
|
|
64
61
|
this.config = { ...this.config, ...updates };
|
|
65
62
|
// Recreate HTTP client with new config
|
|
66
|
-
this.httpClient = new
|
|
67
|
-
this.paginationHelper = new
|
|
63
|
+
this.httpClient = new planning_center_base_ts_1.PcoHttpClient(this.config, this.eventEmitter);
|
|
64
|
+
this.paginationHelper = new planning_center_base_ts_1.PaginationHelper(this.httpClient);
|
|
68
65
|
// Update modules with new HTTP client
|
|
69
66
|
this.updateModules();
|
|
70
67
|
}
|
|
@@ -125,7 +122,7 @@ class PcoClient {
|
|
|
125
122
|
this.households = new households_1.HouseholdsModule(this.httpClient, this.paginationHelper, this.eventEmitter);
|
|
126
123
|
this.notes = new notes_1.NotesModule(this.httpClient, this.paginationHelper, this.eventEmitter);
|
|
127
124
|
this.lists = new lists_1.ListsModule(this.httpClient, this.paginationHelper, this.eventEmitter);
|
|
128
|
-
this.batch = new
|
|
125
|
+
this.batch = new planning_center_base_ts_1.BatchExecutor(this, this.eventEmitter);
|
|
129
126
|
}
|
|
130
127
|
}
|
|
131
128
|
exports.PcoClient = PcoClient;
|
package/dist/core/http.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* v2.0.0 HTTP Client
|
|
3
3
|
*/
|
|
4
4
|
import type { PcoClientConfig } from '../types/client';
|
|
5
|
-
import { PcoEventEmitter } from '
|
|
5
|
+
import { PcoEventEmitter } from '@rachelallyson/planning-center-base-ts';
|
|
6
6
|
export interface HttpRequestOptions {
|
|
7
7
|
method: string;
|
|
8
8
|
endpoint: string;
|
package/dist/core/http.js
CHANGED
|
@@ -4,18 +4,18 @@
|
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.PcoHttpClient = void 0;
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
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
10
|
class PcoHttpClient {
|
|
11
11
|
constructor(config, eventEmitter) {
|
|
12
12
|
this.config = config;
|
|
13
13
|
this.eventEmitter = eventEmitter;
|
|
14
|
-
this.requestIdGenerator = new
|
|
15
|
-
this.performanceMetrics = new
|
|
16
|
-
this.rateLimitTracker = new
|
|
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
17
|
// Initialize rate limiter
|
|
18
|
-
this.rateLimiter = new
|
|
18
|
+
this.rateLimiter = new planning_center_base_ts_2.PcoRateLimiter(100, 20000); // 100 requests per 20 seconds
|
|
19
19
|
}
|
|
20
20
|
async request(options) {
|
|
21
21
|
const requestId = this.requestIdGenerator.generate();
|
|
@@ -65,7 +65,7 @@ class PcoHttpClient {
|
|
|
65
65
|
throw error;
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
-
async makeRequest(options, requestId) {
|
|
68
|
+
async makeRequest(options, requestId, retryCount = 0) {
|
|
69
69
|
const baseURL = this.config.baseURL || 'https://api.planningcenteronline.com/people/v2';
|
|
70
70
|
let url = options.endpoint.startsWith('http') ? options.endpoint : `${baseURL}${options.endpoint}`;
|
|
71
71
|
// Add query parameters
|
|
@@ -132,21 +132,28 @@ class PcoHttpClient {
|
|
|
132
132
|
this.rateLimiter.recordRequest();
|
|
133
133
|
// Handle 429 responses
|
|
134
134
|
if (response.status === 429) {
|
|
135
|
+
if (retryCount >= 5) {
|
|
136
|
+
throw new Error(`Rate limit exceeded after ${retryCount} retries`);
|
|
137
|
+
}
|
|
135
138
|
await this.rateLimiter.waitForAvailability();
|
|
136
|
-
return this.makeRequest(options, requestId);
|
|
139
|
+
return this.makeRequest(options, requestId, retryCount + 1);
|
|
137
140
|
}
|
|
138
141
|
// Handle other errors
|
|
139
142
|
if (!response.ok) {
|
|
140
143
|
// Handle 401 errors with token refresh if available
|
|
141
144
|
if (response.status === 401 && this.config.auth.type === 'oauth') {
|
|
145
|
+
if (retryCount >= 3) {
|
|
146
|
+
throw new Error(`Authentication failed after ${retryCount} retries`);
|
|
147
|
+
}
|
|
142
148
|
try {
|
|
143
149
|
await this.attemptTokenRefresh();
|
|
144
|
-
return this.makeRequest(options, requestId);
|
|
150
|
+
return this.makeRequest(options, requestId, retryCount + 1);
|
|
145
151
|
}
|
|
146
152
|
catch (refreshError) {
|
|
147
153
|
console.warn('Token refresh failed:', refreshError);
|
|
148
154
|
// Call the onRefreshFailure callback
|
|
149
155
|
await this.config.auth.onRefreshFailure(refreshError);
|
|
156
|
+
throw refreshError;
|
|
150
157
|
}
|
|
151
158
|
}
|
|
152
159
|
let errorData;
|
|
@@ -156,7 +163,7 @@ class PcoHttpClient {
|
|
|
156
163
|
catch {
|
|
157
164
|
errorData = {};
|
|
158
165
|
}
|
|
159
|
-
throw
|
|
166
|
+
throw planning_center_base_ts_3.PcoApiError.fromFetchError(response, errorData);
|
|
160
167
|
}
|
|
161
168
|
// Parse response
|
|
162
169
|
if (options.method === 'DELETE') {
|
package/dist/core/pagination.js
CHANGED
|
@@ -35,7 +35,14 @@ class PaginationHelper {
|
|
|
35
35
|
if (response.data.meta?.total_count) {
|
|
36
36
|
totalCount = Number(response.data.meta.total_count) || 0;
|
|
37
37
|
}
|
|
38
|
-
|
|
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
|
+
}
|
|
39
46
|
page++;
|
|
40
47
|
if (onProgress) {
|
|
41
48
|
onProgress(allData.length, totalCount || allData.length);
|
|
@@ -81,7 +88,14 @@ class PaginationHelper {
|
|
|
81
88
|
if (response.data.data && Array.isArray(response.data.data)) {
|
|
82
89
|
yield response.data.data;
|
|
83
90
|
}
|
|
84
|
-
|
|
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
|
+
}
|
|
85
99
|
page++;
|
|
86
100
|
if (hasMore && delay > 0) {
|
|
87
101
|
await new Promise(resolve => setTimeout(resolve, delay));
|
package/dist/core.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ErrorContext, PcoError } from './error-handling';
|
|
2
|
-
import { PcoRateLimiter } from '
|
|
2
|
+
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 {
|
|
@@ -36,7 +36,7 @@ export interface PcoClientConfig {
|
|
|
36
36
|
onRetry?: (error: PcoError, attempt: number) => void;
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
export { PcoApiError } from '
|
|
39
|
+
export { PcoApiError } from '@rachelallyson/planning-center-base-ts';
|
|
40
40
|
export interface PcoClientState {
|
|
41
41
|
config: PcoClientConfig;
|
|
42
42
|
rateLimiter: PcoRateLimiter;
|
|
@@ -72,4 +72,4 @@ export declare function getAllPages<T extends ResourceObject<string, any, any>>(
|
|
|
72
72
|
/**
|
|
73
73
|
* Get rate limit information
|
|
74
74
|
*/
|
|
75
|
-
export declare function getRateLimitInfo(client: PcoClientState): import("
|
|
75
|
+
export declare function getRateLimitInfo(client: PcoClientState): import("@rachelallyson/planning-center-base-ts").RateLimitInfo;
|
package/dist/core.js
CHANGED
|
@@ -10,11 +10,11 @@ exports.del = del;
|
|
|
10
10
|
exports.getAllPages = getAllPages;
|
|
11
11
|
exports.getRateLimitInfo = getRateLimitInfo;
|
|
12
12
|
const error_handling_1 = require("./error-handling");
|
|
13
|
-
const
|
|
13
|
+
const planning_center_base_ts_1 = require("@rachelallyson/planning-center-base-ts");
|
|
14
14
|
const auth_1 = require("./auth");
|
|
15
15
|
// Re-export PcoApiError for convenience
|
|
16
|
-
var
|
|
17
|
-
Object.defineProperty(exports, "PcoApiError", { enumerable: true, get: function () { return
|
|
16
|
+
var planning_center_base_ts_2 = require("@rachelallyson/planning-center-base-ts");
|
|
17
|
+
Object.defineProperty(exports, "PcoApiError", { enumerable: true, get: function () { return planning_center_base_ts_2.PcoApiError; } });
|
|
18
18
|
// ===== Core PCO Client Functions =====
|
|
19
19
|
/**
|
|
20
20
|
* Create a PCO client state
|
|
@@ -25,7 +25,7 @@ function createPcoClient(config) {
|
|
|
25
25
|
maxRequests: 100,
|
|
26
26
|
perMilliseconds: 20000, // 20 seconds
|
|
27
27
|
};
|
|
28
|
-
const rateLimiter = new
|
|
28
|
+
const rateLimiter = new planning_center_base_ts_1.PcoRateLimiter(rateLimitConfig.maxRequests, rateLimitConfig.perMilliseconds);
|
|
29
29
|
return {
|
|
30
30
|
config,
|
|
31
31
|
rateLimiter,
|
package/dist/error-handling.d.ts
CHANGED
package/dist/error-handling.js
CHANGED
|
@@ -7,7 +7,7 @@ exports.withErrorBoundary = withErrorBoundary;
|
|
|
7
7
|
exports.handleValidationError = handleValidationError;
|
|
8
8
|
exports.handleTimeoutError = handleTimeoutError;
|
|
9
9
|
exports.handleNetworkError = handleNetworkError;
|
|
10
|
-
const
|
|
10
|
+
const planning_center_base_ts_1 = require("@rachelallyson/planning-center-base-ts");
|
|
11
11
|
// Error categories for better monitoring
|
|
12
12
|
var ErrorCategory;
|
|
13
13
|
(function (ErrorCategory) {
|
|
@@ -29,7 +29,7 @@ var ErrorSeverity;
|
|
|
29
29
|
ErrorSeverity["CRITICAL"] = "critical";
|
|
30
30
|
})(ErrorSeverity || (exports.ErrorSeverity = ErrorSeverity = {}));
|
|
31
31
|
// Enhanced PCO API Error with additional context
|
|
32
|
-
class PcoError extends
|
|
32
|
+
class PcoError extends planning_center_base_ts_1.PcoApiError {
|
|
33
33
|
constructor(message, status, statusText, errors, rateLimitHeaders, context = {}) {
|
|
34
34
|
super(message, status, statusText, errors, rateLimitHeaders);
|
|
35
35
|
this.name = 'PcoError';
|
|
@@ -170,7 +170,7 @@ function shouldNotRetry(error) {
|
|
|
170
170
|
if (error instanceof PcoError) {
|
|
171
171
|
return !error.shouldRetry();
|
|
172
172
|
}
|
|
173
|
-
if (error instanceof
|
|
173
|
+
if (error instanceof planning_center_base_ts_1.PcoApiError) {
|
|
174
174
|
// Don't retry authentication, authorization, or validation errors
|
|
175
175
|
return (error.status === 401 ||
|
|
176
176
|
error.status === 403 ||
|
package/dist/error-scenarios.js
CHANGED
|
@@ -13,7 +13,7 @@ exports.withTimeout = withTimeout;
|
|
|
13
13
|
exports.classifyError = classifyError;
|
|
14
14
|
exports.attemptRecovery = attemptRecovery;
|
|
15
15
|
exports.createErrorReport = createErrorReport;
|
|
16
|
-
const
|
|
16
|
+
const planning_center_base_ts_1 = require("@rachelallyson/planning-center-base-ts");
|
|
17
17
|
exports.DEFAULT_RETRY_CONFIG = {
|
|
18
18
|
backoffFactor: 2,
|
|
19
19
|
baseDelay: 1000,
|
|
@@ -52,7 +52,7 @@ async function retryWithExponentialBackoff(operation, config = {}) {
|
|
|
52
52
|
* Check if an error is retryable
|
|
53
53
|
*/
|
|
54
54
|
function isRetryableError(error, retryableStatuses) {
|
|
55
|
-
if (error instanceof
|
|
55
|
+
if (error instanceof planning_center_base_ts_1.PcoApiError) {
|
|
56
56
|
return retryableStatuses.includes(error.status);
|
|
57
57
|
}
|
|
58
58
|
// Network errors are generally retryable
|
|
@@ -195,7 +195,7 @@ exports.TIMEOUT_CONFIG = {
|
|
|
195
195
|
* Classify errors for appropriate handling
|
|
196
196
|
*/
|
|
197
197
|
function classifyError(error) {
|
|
198
|
-
if (error instanceof
|
|
198
|
+
if (error instanceof planning_center_base_ts_1.PcoApiError) {
|
|
199
199
|
return classifyPcoError(error);
|
|
200
200
|
}
|
|
201
201
|
if (error.name === 'TypeError' && error.message.includes('fetch')) {
|
|
@@ -300,7 +300,7 @@ async function attemptRecovery(operation, error, context) {
|
|
|
300
300
|
}
|
|
301
301
|
// Rate limit recovery
|
|
302
302
|
if (classification.category === 'rate_limit' &&
|
|
303
|
-
error instanceof
|
|
303
|
+
error instanceof planning_center_base_ts_1.PcoApiError) {
|
|
304
304
|
const retryAfter = error.rateLimitHeaders?.['Retry-After'];
|
|
305
305
|
if (retryAfter) {
|
|
306
306
|
const delay = parseInt(retryAfter) * 1000;
|
package/dist/helpers.js
CHANGED
|
@@ -249,11 +249,12 @@ async function searchPeople(client, criteria, context) {
|
|
|
249
249
|
if (criteria.status) {
|
|
250
250
|
where.status = criteria.status;
|
|
251
251
|
}
|
|
252
|
-
|
|
253
|
-
where.name = criteria.name;
|
|
254
|
-
}
|
|
252
|
+
// Use flexible search when we have email, otherwise use specific name search
|
|
255
253
|
if (criteria.email) {
|
|
256
|
-
where.
|
|
254
|
+
where.search_name_or_email_or_phone_number = criteria.email;
|
|
255
|
+
}
|
|
256
|
+
else if (criteria.name) {
|
|
257
|
+
where.search_name = criteria.name;
|
|
257
258
|
}
|
|
258
259
|
return (0, people_1.getPeople)(client, {
|
|
259
260
|
where,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { PcoClient } from './client';
|
|
2
2
|
export { PcoClientManager } from './client-manager';
|
|
3
3
|
export type { PcoClientConfig, PcoAuthConfig, PersonalAccessTokenAuth, OAuthAuth } from './types/client';
|
|
4
|
-
export type { PcoEvent, EventHandler, EventType } from '
|
|
5
|
-
export type { BatchOperation, BatchResult, BatchOptions, BatchSummary } from '
|
|
6
|
-
export type { Paginated, Relationship, ResourceIdentifier, ResourceObject, } from '
|
|
4
|
+
export type { PcoEvent, EventHandler, EventType } from '@rachelallyson/planning-center-base-ts';
|
|
5
|
+
export type { BatchOperation, BatchResult, BatchOptions, BatchSummary } from '@rachelallyson/planning-center-base-ts';
|
|
6
|
+
export type { Paginated, Relationship, ResourceIdentifier, ResourceObject, } from '@rachelallyson/planning-center-base-ts';
|
|
7
7
|
export type { PersonResource, PersonAttributes, PersonSingle, PeopleList, EmailResource, EmailAttributes, PhoneNumberResource, PhoneNumberAttributes, AddressResource, AddressAttributes, SocialProfileResource, SocialProfileAttributes, } from './types';
|
|
8
8
|
export type { FieldDefinitionResource, FieldDefinitionAttributes, FieldDatumResource, FieldDatumAttributes, FieldOptionResource, FieldOptionAttributes, TabResource, TabAttributes, } from './types';
|
|
9
9
|
export type { WorkflowResource, WorkflowAttributes, WorkflowCardResource, WorkflowCardAttributes, WorkflowCardNoteResource, WorkflowCardNoteAttributes, } from './types';
|
|
@@ -13,11 +13,11 @@ export type { PcoClientConfig as PcoClientConfigV1, PcoClientState } from './cor
|
|
|
13
13
|
export { createPcoClient, del, getAllPages, getList, getRateLimitInfo, getSingle, patch, post, } from './core';
|
|
14
14
|
export type { TokenResponse, TokenRefreshCallback, TokenRefreshFailureCallback } from './auth';
|
|
15
15
|
export { attemptTokenRefresh, hasRefreshTokenCapability, refreshAccessToken, updateClientTokens, } from './auth';
|
|
16
|
-
export { PcoApiError } from '
|
|
17
|
-
export type { RateLimitHeaders, RateLimitInfo } from '
|
|
18
|
-
export { PcoRateLimiter } from '
|
|
19
|
-
export type { ErrorContext } from '
|
|
20
|
-
export { ErrorCategory, ErrorSeverity, handleNetworkError, handleTimeoutError, handleValidationError, PcoError, retryWithBackoff, shouldNotRetry, withErrorBoundary, } from '
|
|
16
|
+
export { PcoApiError } from '@rachelallyson/planning-center-base-ts';
|
|
17
|
+
export type { RateLimitHeaders, RateLimitInfo } from '@rachelallyson/planning-center-base-ts';
|
|
18
|
+
export { PcoRateLimiter } from '@rachelallyson/planning-center-base-ts';
|
|
19
|
+
export type { ErrorContext } from '@rachelallyson/planning-center-base-ts';
|
|
20
|
+
export { ErrorCategory, ErrorSeverity, handleNetworkError, handleTimeoutError, handleValidationError, PcoError, retryWithBackoff, shouldNotRetry, withErrorBoundary, } from '@rachelallyson/planning-center-base-ts';
|
|
21
21
|
export { createFieldDefinition, createFieldOption, createPerson, createPersonAddress, createPersonEmail, createPersonFieldData, createPersonPhoneNumber, createPersonSocialProfile, createWorkflowCard, createWorkflowCardNote, deleteFieldDefinition, deletePerson, deletePersonFieldData, deleteSocialProfile, getFieldDefinitions, getFieldOptions, getHousehold, getHouseholds, getTabs, getListById, getListCategories, getLists, getNote, getNoteCategories, getNotes, getOrganization, getPeople, getPerson, getPersonAddresses, getPersonEmails, getPersonFieldData, getPersonPhoneNumbers, getPersonSocialProfiles, getWorkflow, getWorkflowCardNotes, getWorkflowCards, getWorkflows, updatePerson, updatePersonAddress, } from './people';
|
|
22
22
|
export { attemptRecovery, CircuitBreaker, classifyError, createErrorReport, DEFAULT_RETRY_CONFIG, executeBulkOperation, retryWithExponentialBackoff, TIMEOUT_CONFIG, withTimeout, } from './error-scenarios';
|
|
23
23
|
export { buildQueryParams, calculateAge, createPersonWithContact, createWorkflowCardWithNote, exportAllPeopleData, extractFileUrl, formatDate, formatPersonName, getCompletePersonProfile, getFileExtension, getFilename, getListsWithCategories, getOrganizationInfo, getPeopleByHousehold, getPersonWorkflowCardsWithNotes, getPrimaryContact, isFileUpload, isFileUrl, isValidEmail, isValidPhone, processFileValue, searchPeople, validatePersonData, } from './helpers';
|