@hobokengrace/n8n-nodes-pco 0.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 (124) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +79 -0
  3. package/dist/credentials/PlanningCenterPatApi.credentials.d.ts +9 -0
  4. package/dist/credentials/PlanningCenterPatApi.credentials.js +84 -0
  5. package/dist/credentials/PlanningCenterPatApi.credentials.js.map +1 -0
  6. package/dist/index.d.ts +12 -0
  7. package/dist/index.js +28 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/nodes/assets/api.svg +4 -0
  10. package/dist/nodes/assets/calendar.svg +4 -0
  11. package/dist/nodes/assets/check-ins.svg +4 -0
  12. package/dist/nodes/assets/current.svg +4 -0
  13. package/dist/nodes/assets/giving.svg +4 -0
  14. package/dist/nodes/assets/groups.svg +4 -0
  15. package/dist/nodes/assets/pco.svg +4 -0
  16. package/dist/nodes/assets/people.svg +4 -0
  17. package/dist/nodes/assets/publishing.svg +4 -0
  18. package/dist/nodes/assets/registrations.svg +4 -0
  19. package/dist/nodes/assets/services.svg +4 -0
  20. package/dist/nodes/assets/webhooks.svg +4 -0
  21. package/dist/nodes/generated/api/PlanningCenterApi.node.d.ts +16 -0
  22. package/dist/nodes/generated/api/PlanningCenterApi.node.js +745 -0
  23. package/dist/nodes/generated/api/PlanningCenterApi.node.js.map +1 -0
  24. package/dist/nodes/generated/api/api.svg +4 -0
  25. package/dist/nodes/generated/api/pco.svg +4 -0
  26. package/dist/nodes/generated/calendar/PlanningCenterCalendar.node.d.ts +305 -0
  27. package/dist/nodes/generated/calendar/PlanningCenterCalendar.node.js +33813 -0
  28. package/dist/nodes/generated/calendar/PlanningCenterCalendar.node.js.map +1 -0
  29. package/dist/nodes/generated/calendar/calendar.svg +4 -0
  30. package/dist/nodes/generated/calendar/pco.svg +4 -0
  31. package/dist/nodes/generated/check-ins/PlanningCenterCheckIns.node.d.ts +308 -0
  32. package/dist/nodes/generated/check-ins/PlanningCenterCheckIns.node.js +29142 -0
  33. package/dist/nodes/generated/check-ins/PlanningCenterCheckIns.node.js.map +1 -0
  34. package/dist/nodes/generated/check-ins/check-ins.svg +4 -0
  35. package/dist/nodes/generated/check-ins/pco.svg +4 -0
  36. package/dist/nodes/generated/current/PlanningCenterCurrent.node.d.ts +13 -0
  37. package/dist/nodes/generated/current/PlanningCenterCurrent.node.js +564 -0
  38. package/dist/nodes/generated/current/PlanningCenterCurrent.node.js.map +1 -0
  39. package/dist/nodes/generated/current/current.svg +4 -0
  40. package/dist/nodes/generated/current/pco.svg +4 -0
  41. package/dist/nodes/generated/giving/PlanningCenterGiving.node.d.ts +308 -0
  42. package/dist/nodes/generated/giving/PlanningCenterGiving.node.js +25566 -0
  43. package/dist/nodes/generated/giving/PlanningCenterGiving.node.js.map +1 -0
  44. package/dist/nodes/generated/giving/giving.svg +4 -0
  45. package/dist/nodes/generated/giving/pco.svg +4 -0
  46. package/dist/nodes/generated/groups/PlanningCenterGroups.node.d.ts +189 -0
  47. package/dist/nodes/generated/groups/PlanningCenterGroups.node.js +15344 -0
  48. package/dist/nodes/generated/groups/PlanningCenterGroups.node.js.map +1 -0
  49. package/dist/nodes/generated/groups/groups.svg +4 -0
  50. package/dist/nodes/generated/groups/pco.svg +4 -0
  51. package/dist/nodes/generated/people/PlanningCenterPeople.node.d.ts +979 -0
  52. package/dist/nodes/generated/people/PlanningCenterPeople.node.js +102040 -0
  53. package/dist/nodes/generated/people/PlanningCenterPeople.node.js.map +1 -0
  54. package/dist/nodes/generated/people/pco.svg +4 -0
  55. package/dist/nodes/generated/people/people.svg +4 -0
  56. package/dist/nodes/generated/publishing/PlanningCenterPublishing.node.d.ts +130 -0
  57. package/dist/nodes/generated/publishing/PlanningCenterPublishing.node.js +10671 -0
  58. package/dist/nodes/generated/publishing/PlanningCenterPublishing.node.js.map +1 -0
  59. package/dist/nodes/generated/publishing/pco.svg +4 -0
  60. package/dist/nodes/generated/publishing/publishing.svg +4 -0
  61. package/dist/nodes/generated/registrations/PlanningCenterRegistrations.node.d.ts +55 -0
  62. package/dist/nodes/generated/registrations/PlanningCenterRegistrations.node.js +3640 -0
  63. package/dist/nodes/generated/registrations/PlanningCenterRegistrations.node.js.map +1 -0
  64. package/dist/nodes/generated/registrations/pco.svg +4 -0
  65. package/dist/nodes/generated/registrations/registrations.svg +4 -0
  66. package/dist/nodes/generated/services/PlanningCenterServices.node.d.ts +1032 -0
  67. package/dist/nodes/generated/services/PlanningCenterServices.node.js +82196 -0
  68. package/dist/nodes/generated/services/PlanningCenterServices.node.js.map +1 -0
  69. package/dist/nodes/generated/services/pco.svg +4 -0
  70. package/dist/nodes/generated/services/services.svg +4 -0
  71. package/dist/nodes/generated/webhooks/PlanningCenterWebhooks.node.d.ts +25 -0
  72. package/dist/nodes/generated/webhooks/PlanningCenterWebhooks.node.js +1346 -0
  73. package/dist/nodes/generated/webhooks/PlanningCenterWebhooks.node.js.map +1 -0
  74. package/dist/nodes/generated/webhooks/pco.svg +4 -0
  75. package/dist/nodes/generated/webhooks/webhooks.svg +4 -0
  76. package/dist/src/generator/checkGenerated.d.ts +1 -0
  77. package/dist/src/generator/checkGenerated.js +34 -0
  78. package/dist/src/generator/checkGenerated.js.map +1 -0
  79. package/dist/src/generator/config.d.ts +13 -0
  80. package/dist/src/generator/config.js +114 -0
  81. package/dist/src/generator/config.js.map +1 -0
  82. package/dist/src/generator/generate.d.ts +1 -0
  83. package/dist/src/generator/generate.js +26 -0
  84. package/dist/src/generator/generate.js.map +1 -0
  85. package/dist/src/generator/labels.d.ts +3 -0
  86. package/dist/src/generator/labels.js +23 -0
  87. package/dist/src/generator/labels.js.map +1 -0
  88. package/dist/src/generator/model.d.ts +81 -0
  89. package/dist/src/generator/model.js +3 -0
  90. package/dist/src/generator/model.js.map +1 -0
  91. package/dist/src/generator/openapi.d.ts +3 -0
  92. package/dist/src/generator/openapi.js +667 -0
  93. package/dist/src/generator/openapi.js.map +1 -0
  94. package/dist/src/generator/refreshSnapshots.d.ts +1 -0
  95. package/dist/src/generator/refreshSnapshots.js +28 -0
  96. package/dist/src/generator/refreshSnapshots.js.map +1 -0
  97. package/dist/src/generator/render.d.ts +3 -0
  98. package/dist/src/generator/render.js +580 -0
  99. package/dist/src/generator/render.js.map +1 -0
  100. package/dist/src/runtime/execute.d.ts +2 -0
  101. package/dist/src/runtime/execute.js +25 -0
  102. package/dist/src/runtime/execute.js.map +1 -0
  103. package/dist/src/runtime/jsonApi.d.ts +3 -0
  104. package/dist/src/runtime/jsonApi.js +43 -0
  105. package/dist/src/runtime/jsonApi.js.map +1 -0
  106. package/dist/src/runtime/pagination.d.ts +8 -0
  107. package/dist/src/runtime/pagination.js +68 -0
  108. package/dist/src/runtime/pagination.js.map +1 -0
  109. package/dist/src/runtime/request.d.ts +22 -0
  110. package/dist/src/runtime/request.js +93 -0
  111. package/dist/src/runtime/request.js.map +1 -0
  112. package/dist/src/runtime/resourceLocator.d.ts +1 -0
  113. package/dist/src/runtime/resourceLocator.js +15 -0
  114. package/dist/src/runtime/resourceLocator.js.map +1 -0
  115. package/dist/tests/generator.test.d.ts +1 -0
  116. package/dist/tests/generator.test.js +622 -0
  117. package/dist/tests/generator.test.js.map +1 -0
  118. package/dist/tests/metadata.test.d.ts +1 -0
  119. package/dist/tests/metadata.test.js +124 -0
  120. package/dist/tests/metadata.test.js.map +1 -0
  121. package/dist/tests/runtime.test.d.ts +1 -0
  122. package/dist/tests/runtime.test.js +181 -0
  123. package/dist/tests/runtime.test.js.map +1 -0
  124. package/package.json +80 -0
@@ -0,0 +1,667 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildProductGeneration = buildProductGeneration;
7
+ const swagger_parser_1 = __importDefault(require("@apidevtools/swagger-parser"));
8
+ const inflection_1 = require("inflection");
9
+ const promises_1 = require("node:fs/promises");
10
+ const config_1 = require("./config");
11
+ const labels_1 = require("./labels");
12
+ const HTTP_METHODS = new Set(['get', 'post', 'put', 'patch', 'delete']);
13
+ const GENERIC_TAGS = new Set(['default', 'api', 'openapi']);
14
+ const RANGE_OPERATORS = new Set(['gt', 'gte', 'lt', 'lte']);
15
+ const USEFUL_STRING_FILTERS = new Set([
16
+ 'address',
17
+ 'email',
18
+ 'label',
19
+ 'name',
20
+ 'search_name',
21
+ 'search_name_or_email',
22
+ 'search_name_or_email_or_phone_number',
23
+ 'search_phone_number',
24
+ 'subject',
25
+ 'submitter_name',
26
+ 'title',
27
+ ]);
28
+ const LOOKUP_RESULT_LIMIT = 25;
29
+ const LOOKUP_LABEL_FIELDS = [
30
+ 'name',
31
+ 'full_name',
32
+ 'display_name',
33
+ 'search_name',
34
+ 'path_name',
35
+ 'first_name last_name',
36
+ 'given_name last_name',
37
+ 'nickname last_name',
38
+ 'title',
39
+ 'subject',
40
+ 'label',
41
+ ];
42
+ const LOOKUP_SEARCH_FILTER_PRIORITY = [
43
+ 'search_name',
44
+ 'name',
45
+ 'search_name_or_email',
46
+ 'search_name_or_email_or_phone_number',
47
+ 'title',
48
+ 'subject',
49
+ 'label',
50
+ ];
51
+ function camelCase(value) {
52
+ const words = (0, labels_1.titleCase)(value).split(' ').filter(Boolean);
53
+ if (words.length === 0)
54
+ return 'value';
55
+ return words
56
+ .map((word, index) => (index === 0 ? word.charAt(0).toLowerCase() + word.slice(1) : word))
57
+ .join('')
58
+ .replace(/[^A-Za-z0-9]/g, '');
59
+ }
60
+ function pathSegments(path) {
61
+ return path.split('/').filter(Boolean);
62
+ }
63
+ function routeSegments(path) {
64
+ const segments = pathSegments(path);
65
+ return segments[1] === 'v2' && !isPathParameter(segments[0]) ? segments.slice(2) : segments;
66
+ }
67
+ function isPathParameter(segment) {
68
+ return segment.startsWith('{') && segment.endsWith('}');
69
+ }
70
+ function operationAction(method, path, responseIsList) {
71
+ if (method === 'get') {
72
+ if (responseIsList !== undefined)
73
+ return responseIsList ? 'List' : 'Get';
74
+ const lastSegment = pathSegments(path).at(-1) ?? '';
75
+ return isPathParameter(lastSegment) ? 'Get' : 'List';
76
+ }
77
+ if (method === 'post')
78
+ return 'Create';
79
+ if (method === 'put' || method === 'patch')
80
+ return 'Update';
81
+ if (method === 'delete')
82
+ return 'Delete';
83
+ return (0, labels_1.titleCase)(method);
84
+ }
85
+ function operationTargetSegment(path) {
86
+ const segments = pathSegments(path);
87
+ for (let index = segments.length - 1; index >= 0; index--) {
88
+ if (!isPathParameter(segments[index]))
89
+ return segments[index];
90
+ }
91
+ return 'item';
92
+ }
93
+ function operationTargetSegmentIndex(path) {
94
+ const segments = pathSegments(path);
95
+ for (let index = segments.length - 1; index >= 0; index--) {
96
+ if (!isPathParameter(segments[index]))
97
+ return index;
98
+ }
99
+ return -1;
100
+ }
101
+ function singularizeLastWord(segment) {
102
+ const parts = segment.split(/([_\-.\s]+)/);
103
+ let lastWordIndex = -1;
104
+ for (let index = parts.length - 1; index >= 0; index--) {
105
+ if (/[A-Za-z]/.test(parts[index])) {
106
+ lastWordIndex = index;
107
+ break;
108
+ }
109
+ }
110
+ if (lastWordIndex === -1)
111
+ return segment;
112
+ parts[lastWordIndex] = (0, inflection_1.singularize)(parts[lastWordIndex]);
113
+ return parts.join('');
114
+ }
115
+ function operationTarget(path, action) {
116
+ const segment = operationTargetSegment(path);
117
+ return (0, labels_1.displayLabel)(action === 'List' ? segment : singularizeLastWord(segment));
118
+ }
119
+ function relationshipContext(path, depth = 1) {
120
+ const segments = routeSegments(path);
121
+ const targetIndex = operationTargetSegmentIndex(`/${segments.join('/')}`);
122
+ if (targetIndex <= 0)
123
+ return undefined;
124
+ const contexts = [];
125
+ for (let index = targetIndex - 1; index >= 0; index--) {
126
+ if (!isPathParameter(segments[index]))
127
+ contexts.unshift((0, labels_1.displayLabel)(singularizeLastWord(segments[index])));
128
+ }
129
+ return contexts.slice(-depth).join(' ') || undefined;
130
+ }
131
+ function fallbackOperationLabel(method, path, deprecated, contextDepth = 1, responseIsList) {
132
+ const action = operationAction(method, path, responseIsList);
133
+ const context = relationshipContext(path, contextDepth);
134
+ const label = `${action} ${operationTarget(path, action)}${context ? ` (via ${context})` : ''}`;
135
+ return deprecated ? `${label} (Deprecated)` : label;
136
+ }
137
+ function operationLabel(operation, method, path) {
138
+ const source = operation.operationId ?? operation.summary;
139
+ const label = source
140
+ ? (0, labels_1.displayLabel)(source)
141
+ : fallbackOperationLabel(method, path, Boolean(operation.deprecated), 1, responseIsList(operation));
142
+ return operation.deprecated && source ? `${label} (Deprecated)` : label;
143
+ }
144
+ function operationSortPriority(operation) {
145
+ if (operation.isList)
146
+ return 0;
147
+ if (operation.method === 'GET')
148
+ return 1;
149
+ if (operation.method === 'POST')
150
+ return 2;
151
+ if (operation.method === 'PUT' || operation.method === 'PATCH')
152
+ return 3;
153
+ if (operation.method === 'DELETE')
154
+ return 4;
155
+ return 5;
156
+ }
157
+ function duplicateFallbackLabelGroups(operations, fallbackOperationIds) {
158
+ const operationsByLabel = new Map();
159
+ for (const operation of operations) {
160
+ if (!fallbackOperationIds.has(operation.id))
161
+ continue;
162
+ operationsByLabel.set(operation.operation, [...(operationsByLabel.get(operation.operation) ?? []), operation]);
163
+ }
164
+ return [...operationsByLabel.values()].filter((group) => group.length > 1);
165
+ }
166
+ function disambiguateFallbackOperationLabels(operations, fallbackOperationIds) {
167
+ for (let contextDepth = 2; contextDepth <= 10; contextDepth++) {
168
+ const duplicateGroups = duplicateFallbackLabelGroups(operations, fallbackOperationIds);
169
+ if (!duplicateGroups.length)
170
+ return;
171
+ let changed = false;
172
+ for (const group of duplicateGroups) {
173
+ for (const operation of group) {
174
+ const nextLabel = fallbackOperationLabel(operation.method.toLowerCase(), operation.path, operation.deprecated, contextDepth, operation.isList);
175
+ if (nextLabel === operation.operation)
176
+ continue;
177
+ operation.operation = nextLabel;
178
+ changed = true;
179
+ }
180
+ }
181
+ if (!changed)
182
+ return;
183
+ }
184
+ }
185
+ function resourceLabel(operation, path) {
186
+ const tag = Array.isArray(operation.tags)
187
+ ? operation.tags.find((candidate) => !GENERIC_TAGS.has(String(candidate).toLowerCase()))
188
+ : undefined;
189
+ if (tag)
190
+ return (0, labels_1.displayLabel)(tag);
191
+ const segment = path.split('/').find((part) => part && !part.startsWith('{') && part !== 'v2');
192
+ return (0, labels_1.displayLabel)(segment ?? 'General');
193
+ }
194
+ function schemaType(schema) {
195
+ if (schema?.type === 'integer' || schema?.type === 'number')
196
+ return 'number';
197
+ if (schema?.type === 'boolean')
198
+ return 'boolean';
199
+ return 'string';
200
+ }
201
+ function jsonApiResponseSchema(operation) {
202
+ return operation.responses?.['200']?.content?.['application/vnd.api+json']?.schema
203
+ ?? operation.responses?.['200']?.content?.['application/json']?.schema;
204
+ }
205
+ function responseDataSchema(operation) {
206
+ return jsonApiResponseSchema(operation)?.properties?.data;
207
+ }
208
+ function responseIsList(operation) {
209
+ const dataSchema = responseDataSchema(operation);
210
+ if (!dataSchema)
211
+ return undefined;
212
+ return dataSchema.type === 'array';
213
+ }
214
+ function responseResourceSchema(operation) {
215
+ const dataSchema = responseDataSchema(operation);
216
+ if (!dataSchema)
217
+ return undefined;
218
+ return dataSchema.type === 'array' ? dataSchema.items : dataSchema;
219
+ }
220
+ function responseResourceType(operation) {
221
+ const type = responseResourceSchema(operation)?.properties?.type;
222
+ const value = Array.isArray(type?.enum) ? type.enum[0] : undefined;
223
+ return typeof value === 'string' ? value : undefined;
224
+ }
225
+ function lookupTargetKey(value) {
226
+ return (0, labels_1.displayLabel)(singularizeLastWord(value)).toLowerCase();
227
+ }
228
+ function lookupTargetFromResponse(operation, path) {
229
+ return lookupTargetKey(responseResourceType(operation) ?? operationTargetSegment(path));
230
+ }
231
+ function directRelationships(operation) {
232
+ const relationships = responseResourceSchema(operation)?.properties?.relationships?.properties;
233
+ return new Set(Object.keys(relationships ?? {}));
234
+ }
235
+ function pathRelationshipConstraints(path) {
236
+ const constraints = new Set();
237
+ for (const match of path.matchAll(/\{([^}]+)_id\}/g)) {
238
+ constraints.add(match[1]);
239
+ }
240
+ return constraints;
241
+ }
242
+ function operationQueryContext(path, operation) {
243
+ return {
244
+ directRelationships: directRelationships(operation),
245
+ pathRelationshipConstraints: pathRelationshipConstraints(path),
246
+ };
247
+ }
248
+ function parseWhereParameter(name) {
249
+ if (!name.startsWith('where['))
250
+ return undefined;
251
+ const segments = [...name.matchAll(/\[([^\]]+)\]/g)].map((match) => match[1]);
252
+ if (!segments.length)
253
+ return undefined;
254
+ const lastSegment = segments.at(-1) ?? '';
255
+ const operator = RANGE_OPERATORS.has(lastSegment) ? lastSegment : undefined;
256
+ const baseSegments = operator ? segments.slice(0, -1) : segments;
257
+ const field = baseSegments.at(-1);
258
+ if (!field)
259
+ return undefined;
260
+ return { field, operator, segments: baseSegments };
261
+ }
262
+ function whereParameterName(segments, operator) {
263
+ return `where${[...segments, ...(operator ? [operator] : [])].map((segment) => `[${segment}]`).join('')}`;
264
+ }
265
+ function lookupTargetFromWhereParameter(sourceName, operation) {
266
+ const parsed = parseWhereParameter(sourceName);
267
+ if (!parsed || parsed.operator || parsed.field !== 'id')
268
+ return undefined;
269
+ if (parsed.segments.length === 1)
270
+ return operation.lookupTarget;
271
+ if (parsed.segments.length === 2)
272
+ return lookupTargetKey(parsed.segments[0]);
273
+ return undefined;
274
+ }
275
+ function queryOptionDisplayName(sourceName) {
276
+ const parsed = parseWhereParameter(sourceName);
277
+ if (parsed)
278
+ return (0, labels_1.displayLabel)(parsed.segments.join(' '));
279
+ return (0, labels_1.displayLabel)(sourceName);
280
+ }
281
+ function queryOptionName(sourceName, suffix = '') {
282
+ return `${camelCase(sourceName)}${suffix}`;
283
+ }
284
+ function queryOptionGroup(sourceName) {
285
+ if (sourceName.startsWith('where['))
286
+ return 'filter';
287
+ if (sourceName === 'order')
288
+ return 'order';
289
+ if (sourceName === 'include')
290
+ return 'include';
291
+ return undefined;
292
+ }
293
+ function operatorLabel(operator) {
294
+ if (operator === 'eq')
295
+ return 'Equals';
296
+ if (operator === 'gt')
297
+ return 'Greater Than';
298
+ if (operator === 'gte')
299
+ return 'Greater Than Or Equal';
300
+ if (operator === 'lt')
301
+ return 'Less Than';
302
+ if (operator === 'lte')
303
+ return 'Less Than Or Equal';
304
+ return (0, labels_1.titleCase)(operator);
305
+ }
306
+ function schemaEnumValues(schema) {
307
+ const values = schema?.enum ?? schema?.items?.enum;
308
+ if (!Array.isArray(values))
309
+ return [];
310
+ return values.filter((value) => ['string', 'number', 'boolean'].includes(typeof value));
311
+ }
312
+ function uniqueValueOptions(options) {
313
+ const seen = new Set();
314
+ return options.filter((option) => {
315
+ const key = String(option.value);
316
+ if (seen.has(key))
317
+ return false;
318
+ seen.add(key);
319
+ return true;
320
+ });
321
+ }
322
+ function queryValueOptions(parameter) {
323
+ const values = schemaEnumValues(parameter.schema);
324
+ if (!values.length)
325
+ return undefined;
326
+ if (parameter.name === 'order') {
327
+ return uniqueValueOptions(values.flatMap((value) => {
328
+ const sourceValue = String(value);
329
+ const orderField = sourceValue.startsWith('-') ? sourceValue.slice(1) : sourceValue;
330
+ return [
331
+ { name: `${(0, labels_1.displayLabel)(orderField)} Ascending`, value: orderField },
332
+ { name: `${(0, labels_1.displayLabel)(orderField)} Descending`, value: `-${orderField}` },
333
+ ];
334
+ }));
335
+ }
336
+ return values.map((value) => ({ name: (0, labels_1.displayLabel)(String(value)), value }));
337
+ }
338
+ function isDateFilter(schema) {
339
+ return schema?.format === 'date' || schema?.format === 'date-time';
340
+ }
341
+ function isUsefulStringFilter(field, schema) {
342
+ return schemaType(schema) === 'string' && USEFUL_STRING_FILTERS.has(field);
343
+ }
344
+ function shouldRenderWhereParameter(parameter, context) {
345
+ const parsed = parseWhereParameter(parameter.name);
346
+ if (!parsed)
347
+ return false;
348
+ const { field, segments } = parsed;
349
+ if (segments.length === 1) {
350
+ return field === 'id' || isDateFilter(parameter.schema) || isUsefulStringFilter(field, parameter.schema);
351
+ }
352
+ if (segments.length === 2) {
353
+ const relationship = segments[0];
354
+ if (context.pathRelationshipConstraints.has(relationship))
355
+ return false;
356
+ return context.directRelationships.has(relationship) && field === 'id';
357
+ }
358
+ return false;
359
+ }
360
+ function shouldRenderQueryParameter(parameter, context) {
361
+ const name = parameter.name;
362
+ if (!name)
363
+ return false;
364
+ if (name === 'per_page' || name === 'offset')
365
+ return false;
366
+ if (name.startsWith('fields['))
367
+ return false;
368
+ if (name.startsWith('where['))
369
+ return shouldRenderWhereParameter(parameter, context);
370
+ return true;
371
+ }
372
+ function buildQueryOptions(parameters) {
373
+ const dateRangeGroups = new Map();
374
+ const fieldsBySourceName = new Map(parameters.map((parameter) => [parameter.sourceName, parameter]));
375
+ const usedSourceNames = new Set();
376
+ for (const parameter of parameters) {
377
+ const parsed = parseWhereParameter(parameter.sourceName);
378
+ if (!parsed?.operator)
379
+ continue;
380
+ const baseSourceName = whereParameterName(parsed.segments);
381
+ dateRangeGroups.set(baseSourceName, [
382
+ ...(dateRangeGroups.get(baseSourceName) ?? []),
383
+ parameter,
384
+ ]);
385
+ }
386
+ const options = [];
387
+ for (const [baseSourceName, rangeParameters] of dateRangeGroups) {
388
+ const baseParameter = fieldsBySourceName.get(baseSourceName);
389
+ const allParameters = [baseParameter, ...rangeParameters].filter((parameter) => Boolean(parameter));
390
+ const sourceNames = new Set(allParameters.map((parameter) => parameter.sourceName));
391
+ for (const sourceName of sourceNames)
392
+ usedSourceNames.add(sourceName);
393
+ options.push({
394
+ name: queryOptionName(baseSourceName, 'Filter'),
395
+ displayName: queryOptionDisplayName(baseSourceName),
396
+ group: 'filter',
397
+ type: allParameters[0]?.type ?? 'string',
398
+ kind: 'operator',
399
+ operators: allParameters.map((parameter) => {
400
+ const parsed = parseWhereParameter(parameter.sourceName);
401
+ const operator = parsed?.operator ?? 'eq';
402
+ return {
403
+ name: operatorLabel(operator),
404
+ value: operator,
405
+ sourceName: parameter.sourceName,
406
+ };
407
+ }),
408
+ });
409
+ }
410
+ for (const parameter of parameters) {
411
+ if (usedSourceNames.has(parameter.sourceName))
412
+ continue;
413
+ const group = queryOptionGroup(parameter.sourceName);
414
+ if (!group)
415
+ continue;
416
+ options.push({
417
+ name: queryOptionName(parameter.sourceName),
418
+ displayName: queryOptionDisplayName(parameter.sourceName),
419
+ group,
420
+ type: parameter.type,
421
+ kind: 'single',
422
+ sourceName: parameter.sourceName,
423
+ valueOptions: parameter.valueOptions,
424
+ });
425
+ }
426
+ return options;
427
+ }
428
+ function parameterField(parameter) {
429
+ return {
430
+ name: camelCase(parameter.name),
431
+ sourceName: parameter.name,
432
+ displayName: (0, labels_1.displayLabel)(parameter.name),
433
+ required: Boolean(parameter.required),
434
+ type: schemaType(parameter.schema),
435
+ valueOptions: parameter.in === 'query' ? queryValueOptions(parameter) : undefined,
436
+ };
437
+ }
438
+ function lookupTargetFromIdName(sourceName) {
439
+ if (!sourceName.endsWith('_id') || sourceName.endsWith('_ids'))
440
+ return undefined;
441
+ return lookupTargetKey(sourceName.slice(0, -3));
442
+ }
443
+ function lookupSearchFilter(sourceNames) {
444
+ const sourceNameSet = new Set(sourceNames);
445
+ return LOOKUP_SEARCH_FILTER_PRIORITY
446
+ .map((field) => `where[${field}]`)
447
+ .find((sourceName) => sourceNameSet.has(sourceName));
448
+ }
449
+ function lookupSplitNameSearch(sourceNames) {
450
+ const sourceNameSet = new Set(sourceNames);
451
+ const firstNameFilter = 'where[first_name]';
452
+ const lastNameFilter = 'where[last_name]';
453
+ if (!sourceNameSet.has(firstNameFilter) || !sourceNameSet.has(lastNameFilter))
454
+ return undefined;
455
+ return { firstNameFilter, lastNameFilter };
456
+ }
457
+ function buildLookupCatalog(operations) {
458
+ const catalog = new Map();
459
+ for (const operation of operations) {
460
+ if (!operation.isList)
461
+ continue;
462
+ const searchFilter = lookupSearchFilter(operation.lookupQueryParameterNames);
463
+ const source = {
464
+ operation,
465
+ pathParameters: operation.pathParameters,
466
+ target: operation.lookupTarget,
467
+ searchFilter,
468
+ splitNameSearch: searchFilter ? undefined : lookupSplitNameSearch(operation.lookupQueryParameterNames),
469
+ };
470
+ catalog.set(source.target, [...(catalog.get(source.target) ?? []), source]);
471
+ }
472
+ for (const sources of catalog.values()) {
473
+ sources.sort((a, b) => b.pathParameters.length - a.pathParameters.length
474
+ || a.operation.path.localeCompare(b.operation.path)
475
+ || a.operation.id.localeCompare(b.operation.id));
476
+ }
477
+ return catalog;
478
+ }
479
+ function compatibleLookupSource(catalog, target, operation) {
480
+ if (!target)
481
+ return undefined;
482
+ const operationPathNames = new Set(operation.pathParameters.map((parameter) => parameter.sourceName));
483
+ return catalog.get(target)?.find((source) => source.pathParameters.every((parameter) => operationPathNames.has(parameter.sourceName)));
484
+ }
485
+ function lookupForSource(operation, fieldName, source) {
486
+ const operationFields = new Map(operation.pathParameters.map((parameter) => [parameter.sourceName, parameter.name]));
487
+ return {
488
+ methodName: camelCase(`search ${operation.id} ${fieldName}`),
489
+ sourcePath: source.operation.path,
490
+ parentBindings: source.pathParameters.map((parameter) => ({
491
+ sourceName: parameter.sourceName,
492
+ fieldName: `${operation.id}_${operationFields.get(parameter.sourceName) ?? parameter.name}`,
493
+ })),
494
+ searchFilter: source.searchFilter,
495
+ splitNameSearch: source.splitNameSearch,
496
+ labelFields: LOOKUP_LABEL_FIELDS,
497
+ resultLimit: LOOKUP_RESULT_LIMIT,
498
+ };
499
+ }
500
+ function addLookupMetadata(operations) {
501
+ const catalog = buildLookupCatalog(operations);
502
+ for (const operation of operations) {
503
+ for (const field of operation.pathParameters) {
504
+ const source = compatibleLookupSource(catalog, lookupTargetFromIdName(field.sourceName), operation);
505
+ if (source)
506
+ field.lookup = lookupForSource(operation, field.name, source);
507
+ }
508
+ for (const option of operation.queryOptions) {
509
+ if (option.kind !== 'single' || !option.sourceName)
510
+ continue;
511
+ const source = compatibleLookupSource(catalog, lookupTargetFromWhereParameter(option.sourceName, operation), operation);
512
+ if (source)
513
+ option.lookup = lookupForSource(operation, option.name, source);
514
+ }
515
+ for (const field of operation.attributeFields) {
516
+ const source = compatibleLookupSource(catalog, lookupTargetFromIdName(field.sourceName), operation);
517
+ if (source)
518
+ field.lookup = lookupForSource(operation, field.name, source);
519
+ }
520
+ for (const field of operation.relationshipFields) {
521
+ if (field.multiple)
522
+ continue;
523
+ const source = compatibleLookupSource(catalog, lookupTargetKey(field.relationshipType), operation);
524
+ if (source)
525
+ field.lookup = lookupForSource(operation, field.name, source);
526
+ }
527
+ }
528
+ }
529
+ function collectParameters(path, pathItem, operation, where) {
530
+ const parameters = [...(pathItem.parameters ?? []), ...(operation.parameters ?? [])];
531
+ const seen = new Set();
532
+ const context = operationQueryContext(path, operation);
533
+ return parameters
534
+ .filter((parameter) => {
535
+ if (parameter?.in !== where || !parameter.name || seen.has(parameter.name))
536
+ return false;
537
+ seen.add(parameter.name);
538
+ return true;
539
+ })
540
+ .filter((parameter) => where !== 'query' || shouldRenderQueryParameter(parameter, context))
541
+ .map((parameter) => parameterField(parameter));
542
+ }
543
+ function collectParameterSourceNames(pathItem, operation, where) {
544
+ const parameters = [...(pathItem.parameters ?? []), ...(operation.parameters ?? [])];
545
+ const seen = new Set();
546
+ return parameters.flatMap((parameter) => {
547
+ if (parameter?.in !== where || !parameter.name || seen.has(parameter.name))
548
+ return [];
549
+ seen.add(parameter.name);
550
+ return [parameter.name];
551
+ });
552
+ }
553
+ function requestBodySchema(operation) {
554
+ return operation.requestBody?.content?.['application/vnd.api+json']?.schema
555
+ ?? operation.requestBody?.content?.['application/json']?.schema;
556
+ }
557
+ function collectAttributeFields(operation) {
558
+ const attributes = requestBodySchema(operation)?.properties?.data?.properties?.attributes;
559
+ const properties = attributes?.properties;
560
+ if (!properties || typeof properties !== 'object')
561
+ return [];
562
+ const required = new Set(attributes.required ?? []);
563
+ return Object.entries(properties)
564
+ .filter(([, schema]) => !schema?.readOnly && !schema?.writeOnly)
565
+ .map(([name, schema]) => ({
566
+ name: camelCase(name),
567
+ sourceName: name,
568
+ displayName: (0, labels_1.displayLabel)(name),
569
+ required: required.has(name),
570
+ type: schemaType(schema),
571
+ }));
572
+ }
573
+ function relationshipDataSchema(schema) {
574
+ return schema?.properties?.data;
575
+ }
576
+ function relationshipLooksSimple(dataSchema) {
577
+ const itemSchema = dataSchema?.type === 'array' ? dataSchema.items : dataSchema;
578
+ const properties = itemSchema?.properties;
579
+ return Boolean(properties?.type && properties?.id);
580
+ }
581
+ function collectRelationshipFields(operation) {
582
+ const relationships = requestBodySchema(operation)?.properties?.data?.properties?.relationships?.properties;
583
+ if (!relationships || typeof relationships !== 'object')
584
+ return [];
585
+ return Object.entries(relationships).flatMap(([name, schema]) => {
586
+ const dataSchema = relationshipDataSchema(schema);
587
+ if (!relationshipLooksSimple(dataSchema))
588
+ return [];
589
+ const itemSchema = dataSchema.type === 'array' ? dataSchema.items : dataSchema;
590
+ const relationshipType = itemSchema?.properties?.type?.enum?.[0] ?? name;
591
+ const multiple = dataSchema.type === 'array';
592
+ return [
593
+ {
594
+ name: `${camelCase(name)}Ids`,
595
+ displayName: (0, labels_1.normalizeIdentifierDisplayLabel)(`${(0, labels_1.titleCase)(name)} ID${multiple ? 's' : ''}`),
596
+ relationshipName: name,
597
+ relationshipType,
598
+ multiple,
599
+ },
600
+ ];
601
+ });
602
+ }
603
+ function isListOperation(method, path, operation) {
604
+ return operationAction(method, path, responseIsList(operation)) === 'List';
605
+ }
606
+ function requestPath(config, path) {
607
+ const prefix = `/${config.product}/v2`;
608
+ if (path.startsWith(prefix))
609
+ return path;
610
+ if (!path)
611
+ return prefix;
612
+ return `${prefix}${path.startsWith('/') ? path : `/${path}`}`;
613
+ }
614
+ async function buildProductGeneration(config) {
615
+ const file = (0, config_1.snapshotPath)(config);
616
+ const spec = JSON.parse(await (0, promises_1.readFile)(file, 'utf8'));
617
+ const api = (await swagger_parser_1.default.dereference(spec));
618
+ const operations = [];
619
+ const exclusions = [];
620
+ const fallbackOperationIds = new Set();
621
+ for (const [path, pathItem] of Object.entries(api.paths ?? {})) {
622
+ for (const [method, operation] of Object.entries(pathItem)) {
623
+ if (!HTTP_METHODS.has(method))
624
+ continue;
625
+ if (!operation || typeof operation !== 'object') {
626
+ exclusions.push(`${method.toUpperCase()} ${path}: operation is not an object`);
627
+ continue;
628
+ }
629
+ const id = operation.operationId ? camelCase(operation.operationId) : camelCase(`${method} ${path}`);
630
+ if (!operation.operationId && !operation.summary)
631
+ fallbackOperationIds.add(id);
632
+ const queryParameters = collectParameters(path, pathItem, operation, 'query');
633
+ const lookupQueryParameterNames = collectParameterSourceNames(pathItem, operation, 'query');
634
+ operations.push({
635
+ id,
636
+ resource: resourceLabel(operation, path),
637
+ operation: operationLabel(operation, method, path),
638
+ description: operation.description ?? operation.summary ?? `${method.toUpperCase()} ${path}`,
639
+ method: method.toUpperCase(),
640
+ path: requestPath(config, path),
641
+ deprecated: Boolean(operation.deprecated),
642
+ isList: isListOperation(method, path, operation),
643
+ lookupTarget: lookupTargetFromResponse(operation, path),
644
+ lookupQueryParameterNames,
645
+ pathParameters: collectParameters(path, pathItem, operation, 'path'),
646
+ queryParameters,
647
+ queryOptions: buildQueryOptions(queryParameters),
648
+ attributeFields: collectAttributeFields(operation),
649
+ relationshipFields: collectRelationshipFields(operation),
650
+ });
651
+ }
652
+ }
653
+ disambiguateFallbackOperationLabels(operations, fallbackOperationIds);
654
+ addLookupMetadata(operations);
655
+ const resources = new Set(operations.map((operation) => operation.resource));
656
+ operations.sort((a, b) => `${a.resource}:${operationSortPriority(a)}:${a.operation}:${a.id}`.localeCompare(`${b.resource}:${operationSortPriority(b)}:${b.operation}:${b.id}`));
657
+ return {
658
+ product: config.product,
659
+ displayName: config.displayName,
660
+ className: config.className,
661
+ operationCount: operations.length,
662
+ resourceCount: resources.size,
663
+ operations,
664
+ exclusions,
665
+ };
666
+ }
667
+ //# sourceMappingURL=openapi.js.map