@rachelallyson/planning-center-people-ts 2.14.1 → 3.0.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 +76 -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 +236 -93
  15. package/dist/index.d.ts +9 -7
  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 +30 -28
  32. package/dist/modules/lists.js +55 -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 +50 -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 +47 -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
@@ -2,16 +2,9 @@
2
2
  * v2.0.0 Fields Module
3
3
  */
4
4
  import { BaseModule } from '@rachelallyson/planning-center-base-ts';
5
- import type { PcoHttpClient } from '@rachelallyson/planning-center-base-ts';
6
- import type { PaginationHelper } from '@rachelallyson/planning-center-base-ts';
7
- import type { PcoEventEmitter } from '@rachelallyson/planning-center-base-ts';
8
- import type { FieldDefinitionResource, FieldDefinitionAttributes, FieldDatumResource, FieldOptionResource, FieldOptionAttributes, TabResource, TabAttributes } from '../types';
9
- export interface FieldDefinitionCache {
10
- byId: Map<string, FieldDefinitionResource>;
11
- bySlug: Map<string, FieldDefinitionResource>;
12
- byName: Map<string, FieldDefinitionResource>;
13
- lastUpdated: number;
14
- }
5
+ import type { FieldDefinitionResource, FieldDefinitionAttributes, FieldDatumAttributes, FlattenedFieldDatumResource, FieldOptionAttributes, TabAttributes, Meta, TopLevelLinks } from '../types';
6
+ import type { FieldDataOptions, FieldDefinitionListOptions } from '../types/api-options';
7
+ import type { FlattenedResource } from '@rachelallyson/planning-center-base-ts';
15
8
  export interface FieldSetOptions {
16
9
  /** Field definition ID */
17
10
  fieldId?: string;
@@ -25,33 +18,40 @@ export interface FieldSetOptions {
25
18
  handleFileUploads?: boolean;
26
19
  }
27
20
  export declare class FieldsModule extends BaseModule {
28
- private fieldDefinitionCache;
29
- private cacheTtl;
30
- constructor(httpClient: PcoHttpClient, paginationHelper: PaginationHelper, eventEmitter: PcoEventEmitter);
31
21
  /**
32
- * Get all field definitions with caching
22
+ * Get all field definitions
23
+ * @param include - Optional array of relationships to include (defaults to ['tab'])
24
+ * Valid values: 'tab', 'field_options'
25
+ * @param options - Optional additional options
26
+ * - includeDeleted: If true, includes deleted field definitions (default: false)
27
+ * - where: Optional object for filtering field definitions
28
+ * Valid keys: config, data_type, deleted_at, name, sequence, slug, tab_id
29
+ * Example: { tab_id: '123', data_type: 'string' }
30
+ * - order: Optional field name to order by (prefix with '-' to reverse order)
31
+ * Valid values: config, data_type, deleted_at, name, sequence, slug, tab_id
32
+ * Example: 'sequence' or '-name' for descending
33
33
  */
34
- getAllFieldDefinitions(useCache?: boolean): Promise<FieldDefinitionResource[]>;
34
+ getAllFieldDefinitions(options?: FieldDefinitionListOptions): Promise<import("@rachelallyson/planning-center-base-ts").PaginationResult<FieldDefinitionResource, import("@rachelallyson/planning-center-base-ts").ResourceObject<string, any, any>, Record<string, never>>>;
35
35
  /**
36
36
  * Get a single field definition by ID
37
37
  */
38
- getFieldDefinition(id: string): Promise<FieldDefinitionResource>;
38
+ getFieldDefinition(id: string): Promise<FlattenedResource<"FieldDefinition", FieldDefinitionAttributes, import("../types").FieldDefinitionRelationships>>;
39
39
  /**
40
40
  * Get field definition by slug
41
41
  */
42
- getFieldDefinitionBySlug(slug: string): Promise<FieldDefinitionResource | null>;
42
+ getFieldDefinitionBySlug(slug: string): Promise<FlattenedResource<"FieldDefinition", FieldDefinitionAttributes, import("../types").FieldDefinitionRelationships, Record<string, never>> | null>;
43
43
  /**
44
44
  * Get field definition by name
45
45
  */
46
- getFieldDefinitionByName(name: string): Promise<FieldDefinitionResource | null>;
46
+ getFieldDefinitionByName(name: string): Promise<FlattenedResource<"FieldDefinition", FieldDefinitionAttributes, import("../types").FieldDefinitionRelationships, Record<string, never>> | null>;
47
47
  /**
48
48
  * Create a field definition
49
49
  */
50
- createFieldDefinition(tabId: string, data: FieldDefinitionAttributes): Promise<FieldDefinitionResource>;
50
+ createFieldDefinition(tabId: string, data: Partial<FieldDefinitionAttributes>): Promise<FlattenedResource<"FieldDefinition", FieldDefinitionAttributes, import("../types").FieldDefinitionRelationships>>;
51
51
  /**
52
52
  * Update a field definition
53
53
  */
54
- updateFieldDefinition(id: string, data: Partial<FieldDefinitionAttributes>): Promise<FieldDefinitionResource>;
54
+ updateFieldDefinition(id: string, data: Partial<FieldDefinitionAttributes>): Promise<FlattenedResource<"FieldDefinition", FieldDefinitionAttributes, import("../types").FieldDefinitionRelationships>>;
55
55
  /**
56
56
  * Delete a field definition
57
57
  */
@@ -60,44 +60,44 @@ export declare class FieldsModule extends BaseModule {
60
60
  * Get field options for a field definition
61
61
  */
62
62
  getFieldOptions(fieldDefinitionId: string): Promise<{
63
- data: FieldOptionResource[];
64
- meta?: any;
65
- links?: any;
63
+ data: FlattenedResource<"FieldOption", FieldOptionAttributes, import("../types").FieldOptionRelationships, Record<string, never>>[];
64
+ meta?: Meta;
65
+ links?: TopLevelLinks;
66
66
  }>;
67
67
  /**
68
68
  * Create a field option
69
69
  */
70
- createFieldOption(fieldDefinitionId: string, data: FieldOptionAttributes): Promise<FieldOptionResource>;
70
+ createFieldOption(fieldDefinitionId: string, data: FieldOptionAttributes): Promise<FlattenedResource<"FieldOption", FieldOptionAttributes, import("../types").FieldOptionRelationships>>;
71
71
  /**
72
72
  * Get person's field data
73
73
  */
74
- getPersonFieldData(personId: string): Promise<{
75
- data: FieldDatumResource[];
76
- meta?: any;
77
- links?: any;
74
+ getPersonFieldData(personId: string, options?: FieldDataOptions): Promise<{
75
+ data: FlattenedFieldDatumResource[];
76
+ meta?: Meta;
77
+ links?: TopLevelLinks;
78
78
  }>;
79
79
  /**
80
80
  * Set a person's field value with automatic field lookup
81
81
  */
82
- setPersonField(personId: string, options: FieldSetOptions): Promise<FieldDatumResource>;
82
+ setPersonField(personId: string, options: FieldSetOptions): Promise<FlattenedResource<"FieldDatum", FieldDatumAttributes, import("../types").FieldDatumRelationships>>;
83
83
  /**
84
84
  * Set a person's field value by field definition ID
85
85
  */
86
- setPersonFieldById(personId: string, fieldId: string, value: string): Promise<FieldDatumResource>;
86
+ setPersonFieldById(personId: string, fieldId: string, value: string): Promise<FlattenedResource<"FieldDatum", FieldDatumAttributes, import("../types").FieldDatumRelationships>>;
87
87
  /**
88
88
  * Set a person's field value by field slug
89
89
  */
90
- setPersonFieldBySlug(personId: string, fieldSlug: string, value: string): Promise<FieldDatumResource>;
90
+ setPersonFieldBySlug(personId: string, fieldSlug: string, value: string): Promise<FlattenedResource<"FieldDatum", FieldDatumAttributes, import("../types").FieldDatumRelationships>>;
91
91
  /**
92
92
  * Set a person's field value by field name
93
93
  */
94
- setPersonFieldByName(personId: string, fieldName: string, value: string): Promise<FieldDatumResource>;
94
+ setPersonFieldByName(personId: string, fieldName: string, value: string): Promise<FlattenedResource<"FieldDatum", FieldDatumAttributes, import("../types").FieldDatumRelationships>>;
95
95
  /**
96
96
  * Create field data for a person
97
97
  */
98
98
  createPersonFieldData(personId: string, fieldDefinitionId: string, value: string, options?: {
99
99
  handleFileUploads?: boolean;
100
- }): Promise<FieldDatumResource>;
100
+ }): Promise<FlattenedResource<"FieldDatum", FieldDatumAttributes, import("../types").FieldDatumRelationships>>;
101
101
  /**
102
102
  * Delete person's field data
103
103
  */
@@ -106,22 +106,22 @@ export declare class FieldsModule extends BaseModule {
106
106
  * Get all tabs
107
107
  */
108
108
  getTabs(): Promise<{
109
- data: TabResource[];
110
- meta?: any;
111
- links?: any;
109
+ data: FlattenedResource<"Tab", TabAttributes, import("../types").TabRelationships, Record<string, never>>[];
110
+ meta?: Meta;
111
+ links?: TopLevelLinks;
112
112
  }>;
113
113
  /**
114
114
  * Get a single tab by ID
115
115
  */
116
- getTabById(id: string, include?: string[]): Promise<TabResource>;
116
+ getTabById(id: string, include?: string[]): Promise<FlattenedResource<"Tab", TabAttributes, import("../types").TabRelationships>>;
117
117
  /**
118
118
  * Create a tab
119
119
  */
120
- createTab(data: TabAttributes): Promise<TabResource>;
120
+ createTab(data: TabAttributes): Promise<FlattenedResource<"Tab", TabAttributes, import("../types").TabRelationships>>;
121
121
  /**
122
122
  * Update a tab
123
123
  */
124
- updateTab(id: string, data: Partial<TabAttributes>): Promise<TabResource>;
124
+ updateTab(id: string, data: Partial<TabAttributes>): Promise<FlattenedResource<"Tab", TabAttributes, import("../types").TabRelationships>>;
125
125
  /**
126
126
  * Delete a tab
127
127
  */
@@ -134,22 +134,6 @@ export declare class FieldsModule extends BaseModule {
134
134
  * Create field data for file uploads
135
135
  */
136
136
  private createPersonFileFieldData;
137
- /**
138
- * Check if cache is valid
139
- */
140
- private isCacheValid;
141
- /**
142
- * Ensure cache is loaded
143
- */
144
- private ensureCacheLoaded;
145
- /**
146
- * Update field definition cache
147
- */
148
- private updateFieldDefinitionCache;
149
- /**
150
- * Invalidate cache
151
- */
152
- private invalidateCache;
153
137
  /**
154
138
  * Check if a value is a file URL
155
139
  */
@@ -6,104 +6,93 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.FieldsModule = void 0;
7
7
  const planning_center_base_ts_1 = require("@rachelallyson/planning-center-base-ts");
8
8
  class FieldsModule extends planning_center_base_ts_1.BaseModule {
9
- constructor(httpClient, paginationHelper, eventEmitter) {
10
- super(httpClient, paginationHelper, eventEmitter);
11
- this.fieldDefinitionCache = null;
12
- this.cacheTtl = 300000; // 5 minutes
13
- }
14
9
  /**
15
- * Get all field definitions with caching
10
+ * Get all field definitions
11
+ * @param include - Optional array of relationships to include (defaults to ['tab'])
12
+ * Valid values: 'tab', 'field_options'
13
+ * @param options - Optional additional options
14
+ * - includeDeleted: If true, includes deleted field definitions (default: false)
15
+ * - where: Optional object for filtering field definitions
16
+ * Valid keys: config, data_type, deleted_at, name, sequence, slug, tab_id
17
+ * Example: { tab_id: '123', data_type: 'string' }
18
+ * - order: Optional field name to order by (prefix with '-' to reverse order)
19
+ * Valid values: config, data_type, deleted_at, name, sequence, slug, tab_id
20
+ * Example: 'sequence' or '-name' for descending
16
21
  */
17
- async getAllFieldDefinitions(useCache = true) {
18
- if (useCache && this.isCacheValid()) {
19
- return Array.from(this.fieldDefinitionCache.byId.values());
20
- }
21
- const result = await this.getAllPages('/field_definitions', {
22
- include: ['tab'],
23
- });
24
- // Update cache
25
- this.updateFieldDefinitionCache(result.data);
26
- return result.data;
22
+ async getAllFieldDefinitions(options) {
23
+ this.debugLog('fields.getAllFieldDefinitions', { options });
24
+ return this.getAllPages('/field_definitions', options);
27
25
  }
28
26
  /**
29
27
  * Get a single field definition by ID
30
28
  */
31
29
  async getFieldDefinition(id) {
30
+ this.debugLog('fields.getFieldDefinition', { id });
32
31
  return this.getSingle(`/field_definitions/${id}`);
33
32
  }
34
33
  /**
35
34
  * Get field definition by slug
36
35
  */
37
36
  async getFieldDefinitionBySlug(slug) {
38
- await this.ensureCacheLoaded();
39
- return this.fieldDefinitionCache?.bySlug.get(slug) || null;
37
+ this.debugLog('fields.getFieldDefinitionBySlug', { slug });
38
+ const allFieldDefinitions = await this.getAllFieldDefinitions();
39
+ return allFieldDefinitions.data.find((fd) => fd.slug === slug) || null;
40
40
  }
41
41
  /**
42
42
  * Get field definition by name
43
43
  */
44
44
  async getFieldDefinitionByName(name) {
45
- await this.ensureCacheLoaded();
46
- return this.fieldDefinitionCache?.byName.get(name) || null;
45
+ this.debugLog('fields.getFieldDefinitionByName', { name });
46
+ const allFieldDefinitions = await this.getAllFieldDefinitions();
47
+ return allFieldDefinitions.data.find((fd) => fd.name === name) || null;
47
48
  }
48
49
  /**
49
50
  * Create a field definition
50
51
  */
51
52
  async createFieldDefinition(tabId, data) {
52
- const fieldDef = await this.createResource(`/tabs/${tabId}/field_definitions`, data);
53
- // Invalidate cache
54
- this.invalidateCache();
55
- return fieldDef;
53
+ this.debugLog('fields.createFieldDefinition', { tabId, data });
54
+ return this.createResource(`/tabs/${tabId}/field_definitions`, data);
56
55
  }
57
56
  /**
58
57
  * Update a field definition
59
58
  */
60
59
  async updateFieldDefinition(id, data) {
61
- const fieldDef = await this.updateResource(`/field_definitions/${id}`, data);
62
- // Update cache
63
- if (this.fieldDefinitionCache && fieldDef.attributes) {
64
- this.fieldDefinitionCache.byId.set(id, fieldDef);
65
- this.fieldDefinitionCache.bySlug.set(fieldDef.attributes.slug, fieldDef);
66
- this.fieldDefinitionCache.byName.set(fieldDef.attributes.name, fieldDef);
67
- }
68
- return fieldDef;
60
+ this.debugLog('fields.updateFieldDefinition', { id, data });
61
+ return this.updateResource(`/field_definitions/${id}`, data);
69
62
  }
70
63
  /**
71
64
  * Delete a field definition
72
65
  */
73
66
  async deleteFieldDefinition(id) {
74
- await this.deleteResource(`/field_definitions/${id}`);
75
- // Remove from cache
76
- if (this.fieldDefinitionCache) {
77
- const fieldDef = this.fieldDefinitionCache.byId.get(id);
78
- if (fieldDef && fieldDef.attributes) {
79
- this.fieldDefinitionCache.byId.delete(id);
80
- this.fieldDefinitionCache.bySlug.delete(fieldDef.attributes.slug);
81
- this.fieldDefinitionCache.byName.delete(fieldDef.attributes.name);
82
- }
83
- }
67
+ this.debugLog('fields.deleteFieldDefinition', { id });
68
+ return this.deleteResource(`/field_definitions/${id}`);
84
69
  }
85
70
  /**
86
71
  * Get field options for a field definition
87
72
  */
88
73
  async getFieldOptions(fieldDefinitionId) {
74
+ this.debugLog('fields.getFieldOptions', { fieldDefinitionId });
89
75
  return this.getList(`/field_definitions/${fieldDefinitionId}/field_options`);
90
76
  }
91
77
  /**
92
78
  * Create a field option
93
79
  */
94
80
  async createFieldOption(fieldDefinitionId, data) {
81
+ this.debugLog('fields.createFieldOption', { fieldDefinitionId, data });
95
82
  return this.createResource(`/field_definitions/${fieldDefinitionId}/field_options`, data);
96
83
  }
97
84
  /**
98
85
  * Get person's field data
99
86
  */
100
- async getPersonFieldData(personId) {
101
- return this.getList(`/people/${personId}/field_data`);
87
+ async getPersonFieldData(personId, options) {
88
+ this.debugLog('fields.getPersonFieldData', { personId, options });
89
+ return this.getList(`/people/${personId}/field_data`, options);
102
90
  }
103
91
  /**
104
92
  * Set a person's field value with automatic field lookup
105
93
  */
106
94
  async setPersonField(personId, options) {
95
+ this.debugLog('fields.setPersonField', { personId, options });
107
96
  const fieldDef = await this.resolveFieldDefinition(options);
108
97
  if (!fieldDef) {
109
98
  throw new Error(`Field definition not found for: ${options.fieldId || options.fieldSlug || options.fieldName}`);
@@ -116,12 +105,14 @@ class FieldsModule extends planning_center_base_ts_1.BaseModule {
116
105
  * Set a person's field value by field definition ID
117
106
  */
118
107
  async setPersonFieldById(personId, fieldId, value) {
108
+ this.debugLog('fields.setPersonFieldById', { personId, fieldId });
119
109
  return this.createPersonFieldData(personId, fieldId, value);
120
110
  }
121
111
  /**
122
112
  * Set a person's field value by field slug
123
113
  */
124
114
  async setPersonFieldBySlug(personId, fieldSlug, value) {
115
+ this.debugLog('fields.setPersonFieldBySlug', { personId, fieldSlug, value });
125
116
  const fieldDef = await this.getFieldDefinitionBySlug(fieldSlug);
126
117
  if (!fieldDef) {
127
118
  throw new Error(`Field definition not found for slug: ${fieldSlug}`);
@@ -132,6 +123,7 @@ class FieldsModule extends planning_center_base_ts_1.BaseModule {
132
123
  * Set a person's field value by field name
133
124
  */
134
125
  async setPersonFieldByName(personId, fieldName, value) {
126
+ this.debugLog('fields.setPersonFieldByName', { personId, fieldName, value });
135
127
  const fieldDef = await this.getFieldDefinitionByName(fieldName);
136
128
  if (!fieldDef) {
137
129
  throw new Error(`Field definition not found for name: ${fieldName}`);
@@ -142,31 +134,38 @@ class FieldsModule extends planning_center_base_ts_1.BaseModule {
142
134
  * Create field data for a person
143
135
  */
144
136
  async createPersonFieldData(personId, fieldDefinitionId, value, options = {}) {
137
+ this.debugLog('fields.createPersonFieldData', { personId, fieldDefinitionId, options });
145
138
  const { handleFileUploads = true } = options;
146
139
  // Get field definition to determine type
147
140
  const fieldDef = await this.getFieldDefinition(fieldDefinitionId);
148
141
  // Check if this is a file field and handle accordingly
149
- if (fieldDef.attributes && fieldDef.attributes.data_type === 'file' && handleFileUploads) {
142
+ if (fieldDef && (fieldDef).data_type === 'file' && handleFileUploads) {
150
143
  return this.createPersonFileFieldData(personId, fieldDefinitionId, value);
151
144
  }
152
145
  // For text fields, clean the value if it's a file URL
153
146
  const cleanValue = this.isFileUrl(value) ? this.extractFileUrl(value) : value;
154
147
  // Check if field data already exists for this person and field
155
- try {
156
- const existingFieldData = await this.getPersonFieldData(personId);
157
- const existingDatum = existingFieldData.data.find(datum => {
158
- const fieldDefData = datum.relationships?.field_definition?.data;
159
- return Array.isArray(fieldDefData)
160
- ? fieldDefData.some(fd => fd.id === fieldDefinitionId)
161
- : fieldDefData?.id === fieldDefinitionId;
162
- });
163
- if (existingDatum) {
164
- // Update existing field data
165
- return this.updateResource(`/people/${personId}/field_data/${existingDatum.id}`, { value: cleanValue });
148
+ // Per test construction standards, we should fail explicitly if something is wrong
149
+ const existingFieldData = await this.getPersonFieldData(personId, { include: ['field_definition'] });
150
+ const existingDatum = existingFieldData.data.find(datum => {
151
+ // Check the relationship field_definition first
152
+ // When include: ['field_definition'] is used, this should be a full resource object
153
+ // When not included, it might be a ResourceIdentifier with just {type, id}
154
+ const fieldDefData = datum.field_definition;
155
+ if (fieldDefData && typeof fieldDefData === 'object' && 'id' in fieldDefData) {
156
+ // Compare as strings to handle number/string ID mismatches
157
+ return String(fieldDefData.id) === String(fieldDefinitionId);
166
158
  }
167
- }
168
- catch (error) {
169
- // If we can't get existing field data, continue with creation
159
+ // Fallback: check if field_definition_id is available as an attribute
160
+ // (some API responses might include this even though it's not in the type definition)
161
+ if ('field_definition_id' in datum && datum.field_definition_id !== undefined) {
162
+ return String(datum.field_definition_id) === String(fieldDefinitionId);
163
+ }
164
+ return false;
165
+ });
166
+ if (existingDatum) {
167
+ // Update existing field data
168
+ return this.updateResource(`/people/${personId}/field_data/${existingDatum.id}`, { value: cleanValue });
170
169
  }
171
170
  return this.createResource(`/people/${personId}/field_data`, {
172
171
  field_definition_id: fieldDefinitionId,
@@ -177,40 +176,42 @@ class FieldsModule extends planning_center_base_ts_1.BaseModule {
177
176
  * Delete person's field data
178
177
  */
179
178
  async deletePersonFieldData(personId, fieldDataId) {
179
+ this.debugLog('fields.deletePersonFieldData', { personId, fieldDataId });
180
180
  return this.deleteResource(`/people/${personId}/field_data/${fieldDataId}`);
181
181
  }
182
182
  /**
183
183
  * Get all tabs
184
184
  */
185
185
  async getTabs() {
186
+ this.debugLog('fields.getTabs');
186
187
  return this.getList('/tabs');
187
188
  }
188
189
  /**
189
190
  * Get a single tab by ID
190
191
  */
191
192
  async getTabById(id, include) {
192
- const params = {};
193
- if (include) {
194
- params.include = include.join(',');
195
- }
196
- return this.getSingle(`/tabs/${id}`, params);
193
+ this.debugLog('fields.getTabById', { id, include });
194
+ return this.getSingle(`/tabs/${id}`, include);
197
195
  }
198
196
  /**
199
197
  * Create a tab
200
198
  */
201
199
  async createTab(data) {
200
+ this.debugLog('fields.createTab', { data });
202
201
  return this.createResource('/tabs', data);
203
202
  }
204
203
  /**
205
204
  * Update a tab
206
205
  */
207
206
  async updateTab(id, data) {
207
+ this.debugLog('fields.updateTab', { id, data });
208
208
  return this.updateResource(`/tabs/${id}`, data);
209
209
  }
210
210
  /**
211
211
  * Delete a tab
212
212
  */
213
213
  async deleteTab(id) {
214
+ this.debugLog('fields.deleteTab', { id });
214
215
  return this.deleteResource(`/tabs/${id}`);
215
216
  }
216
217
  /**
@@ -289,47 +290,6 @@ class FieldsModule extends planning_center_base_ts_1.BaseModule {
289
290
  throw error;
290
291
  }
291
292
  }
292
- /**
293
- * Check if cache is valid
294
- */
295
- isCacheValid() {
296
- if (!this.fieldDefinitionCache) {
297
- return false;
298
- }
299
- return Date.now() - this.fieldDefinitionCache.lastUpdated < this.cacheTtl;
300
- }
301
- /**
302
- * Ensure cache is loaded
303
- */
304
- async ensureCacheLoaded() {
305
- if (!this.isCacheValid()) {
306
- await this.getAllFieldDefinitions(false);
307
- }
308
- }
309
- /**
310
- * Update field definition cache
311
- */
312
- updateFieldDefinitionCache(fieldDefinitions) {
313
- this.fieldDefinitionCache = {
314
- byId: new Map(),
315
- bySlug: new Map(),
316
- byName: new Map(),
317
- lastUpdated: Date.now(),
318
- };
319
- for (const fieldDef of fieldDefinitions) {
320
- if (fieldDef.attributes) {
321
- this.fieldDefinitionCache.byId.set(fieldDef.id, fieldDef);
322
- this.fieldDefinitionCache.bySlug.set(fieldDef.attributes.slug, fieldDef);
323
- this.fieldDefinitionCache.byName.set(fieldDef.attributes.name, fieldDef);
324
- }
325
- }
326
- }
327
- /**
328
- * Invalidate cache
329
- */
330
- invalidateCache() {
331
- this.fieldDefinitionCache = null;
332
- }
333
293
  /**
334
294
  * Check if a value is a file URL
335
295
  */
@@ -2,82 +2,93 @@
2
2
  * v2.0.0 Forms Module
3
3
  */
4
4
  import { BaseModule } from '@rachelallyson/planning-center-base-ts';
5
- import type { PcoHttpClient } from '@rachelallyson/planning-center-base-ts';
6
- import type { PaginationHelper } from '@rachelallyson/planning-center-base-ts';
7
- import type { PcoEventEmitter } from '@rachelallyson/planning-center-base-ts';
8
- import type { FormResource, FormsList, FormCategoryResource, FormFieldResource, FormFieldOptionResource, FormSubmissionResource, FormSubmissionValueResource } from '../types';
5
+ import type { FormResource, FormAttributes } from '../types';
6
+ import type { FormListOptions, FormPageOptions } from '../types/api-options';
9
7
  /**
10
8
  * Forms module for managing form-related operations
11
9
  * Most operations are read-only based on API documentation
12
10
  */
13
11
  export declare class FormsModule extends BaseModule {
14
- constructor(httpClient: PcoHttpClient, paginationHelper: PaginationHelper, eventEmitter: PcoEventEmitter);
15
12
  /**
16
- * Get all forms
13
+ * Get all forms across all pages
17
14
  */
18
- getAll(params?: {
19
- where?: Record<string, any>;
20
- include?: string[];
21
- per_page?: number;
22
- page?: number;
23
- }): Promise<FormsList>;
15
+ getAll(params?: FormListOptions): Promise<import("@rachelallyson/planning-center-base-ts").PaginationResult<FormResource, import("@rachelallyson/planning-center-base-ts").ResourceObject<string, any, any>, Record<string, never>>>;
16
+ /**
17
+ * Get a single page of forms with optional filtering and pagination control
18
+ * Use this when you need a specific page or want to limit the number of results
19
+ * @param params - List parameters including where, include, perPage, page, and order
20
+ * @returns A single page of results with meta and links for pagination
21
+ */
22
+ getPage(params?: FormPageOptions): Promise<{
23
+ data: import("@rachelallyson/planning-center-base-ts").FlattenedResource<"Form", FormAttributes, import("../types").FormRelationships, Record<string, never>>[];
24
+ meta?: import("@rachelallyson/planning-center-base-ts").Meta;
25
+ links?: import("@rachelallyson/planning-center-base-ts").TopLevelLinks;
26
+ }>;
24
27
  /**
25
28
  * Get a specific form by ID
26
29
  */
27
- getById(id: string, include?: string[]): Promise<FormResource>;
30
+ getById(id: string, include?: string[]): Promise<import("@rachelallyson/planning-center-base-ts").FlattenedResource<"Form", FormAttributes, import("../types").FormRelationships>>;
28
31
  /**
29
32
  * Get form category for a specific form
30
33
  */
31
- getFormCategory(formId: string): Promise<FormCategoryResource>;
34
+ getFormCategory(formId: string): Promise<import("@rachelallyson/planning-center-base-ts").FlattenedResource<"FormCategory", import("../types").FormCategoryAttributes, import("../types").FormCategoryRelationships>>;
32
35
  /**
33
36
  * Get form fields for a specific form
34
37
  */
35
38
  getFormFields(formId: string, params?: {
36
- where?: Record<string, any>;
39
+ where?: Record<string, string | number | boolean | undefined>;
37
40
  include?: string[];
38
- per_page?: number;
41
+ perPage?: number;
39
42
  page?: number;
40
43
  }): Promise<{
41
- data: FormFieldResource[];
44
+ data: import("@rachelallyson/planning-center-base-ts").FlattenedResource<"FormField", import("../types").FormFieldAttributes, import("../types").FormFieldRelationships, Record<string, never>>[];
45
+ meta?: import("@rachelallyson/planning-center-base-ts").Meta;
46
+ links?: import("@rachelallyson/planning-center-base-ts").TopLevelLinks;
42
47
  }>;
43
48
  /**
44
49
  * Get form field options for a specific form field
45
50
  * Note: This requires the formId to get field options
46
51
  */
47
52
  getFormFieldOptions(formId: string, formFieldId: string, params?: {
48
- where?: Record<string, any>;
53
+ where?: Record<string, string | number | boolean | undefined>;
49
54
  include?: string[];
50
55
  per_page?: number;
51
56
  page?: number;
52
57
  }): Promise<{
53
- data: FormFieldOptionResource[];
58
+ data: import("@rachelallyson/planning-center-base-ts").FlattenedResource<"FormFieldOption", import("../types").FormFieldOptionAttributes, import("../types").FormFieldOptionRelationships, Record<string, never>>[];
59
+ meta?: import("@rachelallyson/planning-center-base-ts").Meta;
60
+ links?: import("@rachelallyson/planning-center-base-ts").TopLevelLinks;
54
61
  }>;
55
62
  /**
56
63
  * Get form submissions for a specific form
57
64
  */
58
65
  getFormSubmissions(formId: string, params?: {
59
- where?: Record<string, any>;
66
+ where?: Record<string, string | number | boolean | undefined>;
60
67
  include?: string[];
61
68
  per_page?: number;
62
69
  page?: number;
63
70
  }): Promise<{
64
- data: FormSubmissionResource[];
71
+ data: import("@rachelallyson/planning-center-base-ts").FlattenedResource<"FormSubmission", import("../types").FormSubmissionAttributes, import("../types").FormSubmissionRelationships, Record<string, never>>[];
72
+ meta?: import("@rachelallyson/planning-center-base-ts").Meta;
73
+ links?: import("@rachelallyson/planning-center-base-ts").TopLevelLinks;
65
74
  }>;
66
75
  /**
67
76
  * Get a specific form submission by ID
68
77
  * Note: This requires the formId to get the submission
69
78
  */
70
- getFormSubmissionById(formId: string, formSubmissionId: string, include?: string[]): Promise<FormSubmissionResource>;
79
+ getFormSubmissionById(formId: string, formSubmissionId: string, include?: string[]): Promise<import("@rachelallyson/planning-center-base-ts").FlattenedResource<"FormSubmission", import("../types").FormSubmissionAttributes, import("../types").FormSubmissionRelationships>>;
71
80
  /**
72
81
  * Get form submission values for a specific form submission
73
82
  * Note: This requires the formId to get submission values
74
83
  */
75
84
  getFormSubmissionValues(formId: string, formSubmissionId: string, params?: {
76
- where?: Record<string, any>;
85
+ where?: Record<string, string | number | boolean | undefined>;
77
86
  include?: string[];
78
87
  per_page?: number;
79
88
  page?: number;
80
89
  }): Promise<{
81
- data: FormSubmissionValueResource[];
90
+ data: import("@rachelallyson/planning-center-base-ts").FlattenedResource<"FormSubmissionValue", import("../types").FormSubmissionValueAttributes, import("../types").FormSubmissionValueRelationships, Record<string, never>>[];
91
+ meta?: import("@rachelallyson/planning-center-base-ts").Meta;
92
+ links?: import("@rachelallyson/planning-center-base-ts").TopLevelLinks;
82
93
  }>;
83
94
  }