@aloma.io/integration-sdk 3.8.62 → 3.8.64

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.
@@ -38,15 +38,18 @@ export class OpenAPIToConnector {
38
38
  if (!this.options.nestedPaths) {
39
39
  return this.generateMethodName({ method: httpMethod, path: urlPath, operationId });
40
40
  }
41
- const VERSION_RE = /^v\d+$/;
41
+ const VERSION_RE = /^v\d+$|^\d{4}-\d{2}$/;
42
42
  const STRIP = new Set(['objects', 'items']);
43
+ // Convert hyphenated segment to camelCase identifier
44
+ const toCamel = (s) => s.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
43
45
  // Build namespace parts from the URL path
44
46
  const parts = urlPath
45
47
  .replace(/\{[^}]+\}/g, '')
46
48
  .split('/')
47
49
  .filter(Boolean)
48
50
  .filter((p) => !VERSION_RE.test(p))
49
- .filter((p) => !STRIP.has(p));
51
+ .filter((p) => !STRIP.has(p))
52
+ .map(toCamel);
50
53
  // Extract the leaf action from operationId suffix
51
54
  let suffix = operationId.includes('_')
52
55
  ? operationId.split('_').pop()
@@ -59,10 +62,13 @@ export class OpenAPIToConnector {
59
62
  .split('/')
60
63
  .filter(Boolean)
61
64
  .filter((p) => !VERSION_RE.test(p))
62
- .filter((p) => !STRIP.has(p));
63
- // Use the last meaningful segment from the URL-like suffix, or fall back to HTTP method
65
+ .filter((p) => !STRIP.has(p))
66
+ .map(toCamel);
64
67
  suffix = suffixParts.length > 0 ? suffixParts[suffixParts.length - 1] : httpMethod.toLowerCase();
65
68
  }
69
+ else {
70
+ suffix = toCamel(suffix);
71
+ }
66
72
  // Dedup: if suffix equals last path segment, don't repeat it
67
73
  const last = parts[parts.length - 1];
68
74
  if (suffix === last)
@@ -152,7 +158,9 @@ export class OpenAPIToConnector {
152
158
  // Extract the last part after underscore if it exists
153
159
  const parts = cleaned.split('_');
154
160
  if (parts.length > 1) {
155
- const lastPart = parts[parts.length - 1];
161
+ let lastPart = parts[parts.length - 1];
162
+ // Convert hyphens to camelCase before testing
163
+ lastPart = lastPart.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
156
164
  // If the last part looks like a method name (camelCase), use it
157
165
  if (lastPart && /^[a-z][a-zA-Z0-9]*$/.test(lastPart)) {
158
166
  cleaned = lastPart;
@@ -192,7 +200,7 @@ export class OpenAPIToConnector {
192
200
  .filter((p) => p.toLowerCase() !== baseName.toLowerCase());
193
201
  // Use the last path segment before the method name as a distinguishing prefix
194
202
  if (pathParts.length > 0) {
195
- const prefix = pathParts[pathParts.length - 1];
203
+ const prefix = pathParts[pathParts.length - 1].replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
196
204
  const capitalizedBase = baseName.charAt(0).toUpperCase() + baseName.slice(1);
197
205
  return `${prefix}${capitalizedBase}`;
198
206
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aloma.io/integration-sdk",
3
- "version": "3.8.62",
3
+ "version": "3.8.64",
4
4
  "description": "",
5
5
  "author": "aloma.io",
6
6
  "license": "Apache-2.0",
@@ -62,16 +62,21 @@ export class OpenAPIToConnector {
62
62
  return this.generateMethodName({method: httpMethod, path: urlPath, operationId});
63
63
  }
64
64
 
65
- const VERSION_RE = /^v\d+$/;
65
+ const VERSION_RE = /^v\d+$|^\d{4}-\d{2}$/;
66
66
  const STRIP = new Set(['objects', 'items']);
67
67
 
68
+ // Convert hyphenated segment to camelCase identifier
69
+ const toCamel = (s: string): string =>
70
+ s.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
71
+
68
72
  // Build namespace parts from the URL path
69
73
  const parts = urlPath
70
74
  .replace(/\{[^}]+\}/g, '')
71
75
  .split('/')
72
76
  .filter(Boolean)
73
77
  .filter((p) => !VERSION_RE.test(p))
74
- .filter((p) => !STRIP.has(p));
78
+ .filter((p) => !STRIP.has(p))
79
+ .map(toCamel);
75
80
 
76
81
  // Extract the leaf action from operationId suffix
77
82
  let suffix = operationId.includes('_')
@@ -86,9 +91,11 @@ export class OpenAPIToConnector {
86
91
  .split('/')
87
92
  .filter(Boolean)
88
93
  .filter((p) => !VERSION_RE.test(p))
89
- .filter((p) => !STRIP.has(p));
90
- // Use the last meaningful segment from the URL-like suffix, or fall back to HTTP method
94
+ .filter((p) => !STRIP.has(p))
95
+ .map(toCamel);
91
96
  suffix = suffixParts.length > 0 ? suffixParts[suffixParts.length - 1] : httpMethod.toLowerCase();
97
+ } else {
98
+ suffix = toCamel(suffix);
92
99
  }
93
100
 
94
101
  // Dedup: if suffix equals last path segment, don't repeat it
@@ -190,7 +197,9 @@ export class OpenAPIToConnector {
190
197
  // Extract the last part after underscore if it exists
191
198
  const parts = cleaned.split('_');
192
199
  if (parts.length > 1) {
193
- const lastPart = parts[parts.length - 1];
200
+ let lastPart = parts[parts.length - 1];
201
+ // Convert hyphens to camelCase before testing
202
+ lastPart = lastPart.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
194
203
  // If the last part looks like a method name (camelCase), use it
195
204
  if (lastPart && /^[a-z][a-zA-Z0-9]*$/.test(lastPart)) {
196
205
  cleaned = lastPart;
@@ -237,7 +246,7 @@ export class OpenAPIToConnector {
237
246
 
238
247
  // Use the last path segment before the method name as a distinguishing prefix
239
248
  if (pathParts.length > 0) {
240
- const prefix = pathParts[pathParts.length - 1];
249
+ const prefix = pathParts[pathParts.length - 1].replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
241
250
  const capitalizedBase = baseName.charAt(0).toUpperCase() + baseName.slice(1);
242
251
  return `${prefix}${capitalizedBase}`;
243
252
  }
@@ -0,0 +1,69 @@
1
+ import assert from 'assert';
2
+
3
+ const { OpenAPIToConnector } = await import('../build/openapi-to-connector.mjs');
4
+
5
+ let passed = 0;
6
+ let failed = 0;
7
+
8
+ function test(name, fn) {
9
+ try { fn(); console.log(`✓ PASS: ${name}`); passed++; }
10
+ catch(e) { console.log(`✗ FAIL: ${name} — ${e.message}`); failed++; }
11
+ }
12
+
13
+ const minimalSpec = { openapi: '3.0.0', info: { title: 'Test', version: '1.0' }, paths: {} };
14
+ const gen = new OpenAPIToConnector(minimalSpec, 'test', { nestedPaths: true });
15
+
16
+ // Bug 4: hyphenated path segment must not appear in dotted method path
17
+ test('hyphenated path segment sanitised in nested method path', () => {
18
+ const path = gen.deriveMethodPath('PUT',
19
+ '/crm/lists/2025-09/{listId}/memberships/add-and-remove',
20
+ 'put-/crm/lists/2025-09/{listId}/memberships/add-and-remove_addAndRemove');
21
+ const segments = path.split('.');
22
+ segments.forEach(seg => {
23
+ assert(!seg.includes('-'), `Method path segment contains hyphen: "${seg}" in "${path}"`);
24
+ });
25
+ });
26
+
27
+ // Bug 4: path with object-type-id (hyphenated) must produce clean segments
28
+ test('object-type-id hyphenated path sanitised to camelCase', () => {
29
+ const path = gen.deriveMethodPath('GET',
30
+ '/crm/lists/2025-09/object-type-id/{objectTypeId}/name/{listName}',
31
+ 'get-/crm/lists/2025-09/object-type-id/{objectTypeId}/name/{listName}_getByName');
32
+ const segments = path.split('.');
33
+ segments.forEach(seg => {
34
+ assert(!seg.includes('-'), `Method path segment contains hyphen: "${seg}" in "${path}"`);
35
+ });
36
+ });
37
+
38
+ // Bug 5: operationId suffix that IS a raw URL path must not duplicate the full path
39
+ test('raw URL suffix does not produce duplicated path in method name', () => {
40
+ const path = gen.deriveMethodPath('GET',
41
+ '/crm/lists/2025-09',
42
+ 'get-/crm/lists/2025-09_/crm/lists/2025-09');
43
+ // Should be short and clean, not a duplicated path
44
+ assert(path.length < 40, `Path too long (duplication): "${path}" (${path.length} chars)`);
45
+ assert(!path.includes('_'), `Path contains underscores (non-nested fallback used): "${path}"`);
46
+ });
47
+
48
+ // Bug 5: same duplication test for the batch/read endpoint
49
+ test('batch/read raw URL suffix does not produce duplicated path', () => {
50
+ const path = gen.deriveMethodPath('POST',
51
+ '/crm/lists/2025-09/records/memberships/batch/read',
52
+ 'post-/crm/lists/2025-09/records/memberships/batch/read_/crm/lists/2025-09/records/memberships/batch/read');
53
+ assert(path.length < 60, `Path too long (duplication): "${path}" (${path.length} chars)`);
54
+ assert(!path.includes('_'), `Path contains underscores (non-nested fallback used): "${path}"`);
55
+ });
56
+
57
+ // Bug 4: generateMethodName fallback (no operationId) must sanitise hyphens
58
+ test('generateMethodName fallback sanitises hyphens to camelCase', () => {
59
+ const name = gen.generateMethodName({
60
+ method: 'PUT',
61
+ path: '/crm/lists/2025-09/folders/move-list',
62
+ operationId: undefined
63
+ });
64
+ assert(!name.includes('-'), `Fallback name contains hyphen: "${name}"`);
65
+ assert(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name), `Not a valid identifier: "${name}"`);
66
+ });
67
+
68
+ console.log(`\n${passed} passing / ${failed} failing`);
69
+ if (failed > 0) process.exit(1);