@uipath/uipath-typescript 1.4.2 → 1.5.1

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 (52) hide show
  1. package/README.md +7 -1
  2. package/dist/agent-memory/index.d.ts +4 -1
  3. package/dist/agents/index.cjs +341 -6
  4. package/dist/agents/index.d.ts +717 -16
  5. package/dist/agents/index.mjs +342 -7
  6. package/dist/assets/index.cjs +132 -15
  7. package/dist/assets/index.d.ts +12 -1
  8. package/dist/assets/index.mjs +132 -15
  9. package/dist/attachments/index.cjs +120 -12
  10. package/dist/attachments/index.mjs +120 -12
  11. package/dist/buckets/index.cjs +136 -15
  12. package/dist/buckets/index.d.ts +12 -1
  13. package/dist/buckets/index.mjs +136 -15
  14. package/dist/cases/index.cjs +1203 -938
  15. package/dist/cases/index.d.ts +325 -45
  16. package/dist/cases/index.mjs +1203 -938
  17. package/dist/conversational-agent/index.cjs +48 -10
  18. package/dist/conversational-agent/index.d.ts +117 -6
  19. package/dist/conversational-agent/index.mjs +48 -10
  20. package/dist/core/index.cjs +1 -1
  21. package/dist/core/index.mjs +1 -1
  22. package/dist/entities/index.cjs +448 -9
  23. package/dist/entities/index.d.ts +441 -1
  24. package/dist/entities/index.mjs +447 -10
  25. package/dist/feedback/index.cjs +25 -9
  26. package/dist/feedback/index.mjs +25 -9
  27. package/dist/index.cjs +1281 -330
  28. package/dist/index.d.ts +1988 -143
  29. package/dist/index.mjs +1282 -331
  30. package/dist/index.umd.js +1230 -279
  31. package/dist/jobs/index.cjs +141 -19
  32. package/dist/jobs/index.d.ts +22 -6
  33. package/dist/jobs/index.mjs +141 -19
  34. package/dist/maestro-processes/index.cjs +553 -354
  35. package/dist/maestro-processes/index.d.ts +376 -47
  36. package/dist/maestro-processes/index.mjs +553 -354
  37. package/dist/notifications/index.cjs +2012 -0
  38. package/dist/notifications/index.d.ts +615 -0
  39. package/dist/notifications/index.mjs +2010 -0
  40. package/dist/processes/index.cjs +118 -18
  41. package/dist/processes/index.d.ts +18 -2
  42. package/dist/processes/index.mjs +118 -18
  43. package/dist/queues/index.cjs +131 -14
  44. package/dist/queues/index.d.ts +12 -1
  45. package/dist/queues/index.mjs +131 -14
  46. package/dist/tasks/index.cjs +125 -13
  47. package/dist/tasks/index.d.ts +4 -1
  48. package/dist/tasks/index.mjs +125 -13
  49. package/dist/traces/index.cjs +220 -6
  50. package/dist/traces/index.d.ts +360 -25
  51. package/dist/traces/index.mjs +221 -7
  52. package/package.json +14 -4
@@ -954,7 +954,16 @@ const BUCKET_TOKEN_PARAMS = {
954
954
  * Returns the original value if parsing fails.
955
955
  */
956
956
  /**
957
- * Transforms data by mapping fields according to the provided field mapping
957
+ * Transforms data by renaming each key in `data` exactly once, using the
958
+ * mapping (`sourceField → targetField`). Keys not present in the mapping
959
+ * pass through unchanged. The original (pre-rename) key is dropped — the
960
+ * result contains only the renamed key.
961
+ *
962
+ * Each rename is independent. If the mapping happens to contain chained
963
+ * entries (`a → b` and `b → c`), they do NOT compose: a field named `a`
964
+ * in `data` becomes `b` (not `c`), because the renames are applied based
965
+ * on the original data's keys, not the running result.
966
+ *
958
967
  * @param data The source data to transform
959
968
  * @param fieldMapping Object mapping source field names to target field names
960
969
  * @returns Transformed data with mapped field names
@@ -977,21 +986,28 @@ const BUCKET_TOKEN_PARAMS = {
977
986
  * // { userId: '123', name: 'john' },
978
987
  * // { userId: '456', name: 'jane' }
979
988
  * // ]
989
+ *
990
+ * // No chaining — `a → b` does not become `a → c` even if the map has `b → c`.
991
+ * transformData({ a: 1 }, { a: 'b', b: 'c' });
992
+ * // result = { b: 1 }
980
993
  * ```
981
994
  */
982
995
  function transformData(data, fieldMapping) {
996
+ // Pass null/undefined through unchanged — callers (e.g. AttachmentService.getById)
997
+ // may invoke this on optional fields that an OData `select` excluded.
998
+ if (data == null) {
999
+ return data;
1000
+ }
983
1001
  // Handle array of objects
984
1002
  if (Array.isArray(data)) {
985
1003
  return data.map(item => transformData(item, fieldMapping));
986
1004
  }
987
- // Handle single object
988
- const result = { ...data };
989
- for (const [sourceField, targetField] of Object.entries(fieldMapping)) {
990
- if (sourceField in result) {
991
- const value = result[sourceField];
992
- delete result[sourceField];
993
- result[targetField] = value;
994
- }
1005
+ // Walk the ORIGINAL data's keys, look up each in the mapping. One rename
1006
+ // per data key — no mutation of an in-progress result, so chains can't form.
1007
+ const result = {};
1008
+ for (const [key, value] of Object.entries(data)) {
1009
+ const renamedKey = fieldMapping[key] ?? key;
1010
+ result[renamedKey] = value;
995
1011
  }
996
1012
  return result;
997
1013
  }
@@ -1101,6 +1117,97 @@ function addPrefixToKeys(obj, prefix, keys) {
1101
1117
  }
1102
1118
  return result;
1103
1119
  }
1120
+ /**
1121
+ * Creates a new map with the keys and values reversed
1122
+ * @param map The original map to reverse
1123
+ * @returns A new map with keys and values swapped
1124
+ *
1125
+ * @example
1126
+ * ```typescript
1127
+ * const original = { key1: 'value1', key2: 'value2' };
1128
+ * const reversed = reverseMap(original);
1129
+ * // reversed = { value1: 'key1', value2: 'key2' }
1130
+ * ```
1131
+ */
1132
+ function reverseMap(map) {
1133
+ return Object.entries(map).reduce((acc, [key, value]) => {
1134
+ acc[value] = key;
1135
+ return acc;
1136
+ }, {});
1137
+ }
1138
+ /**
1139
+ * OData query-string keys whose values may contain field identifiers that
1140
+ * need rewriting from SDK names → API names.
1141
+ */
1142
+ const ODATA_FIELD_PARAM_KEYS = ['filter', 'orderby', 'select', 'expand'];
1143
+ /**
1144
+ * Matches one token at a time in an OData expression:
1145
+ * 1. A single-quoted string literal, allowing the `''` escape sequence —
1146
+ * consumed atomically so identifiers inside the literal can't match.
1147
+ * 2. An OData identifier (`[A-Za-z_][A-Za-z0-9_]*`).
1148
+ * Anything else (whitespace, operators, parens, commas) is left alone by
1149
+ * `String.prototype.replace`, which only substitutes matched substrings.
1150
+ */
1151
+ const ODATA_TOKEN_RE = /'(?:[^']|'')*'|[A-Za-z_][A-Za-z0-9_]*/g;
1152
+ /**
1153
+ * Rewrites SDK field identifiers to API field identifiers inside an OData
1154
+ * expression string (`$filter`, `$orderby`, `$select`, `$expand`).
1155
+ *
1156
+ * Field maps (e.g. `JobMap`) rename API fields → SDK fields on responses, so
1157
+ * SDK consumers see the renamed names. Without this rewrite, the same name
1158
+ * in a `filter` string would be forwarded verbatim and the API (which still
1159
+ * uses the original name) would reject it.
1160
+ *
1161
+ * Quoted string literals (with the OData `''` escape) are preserved exactly:
1162
+ * the token regex consumes them whole, so identifiers inside literals never
1163
+ * match. Identifier tokens are looked up in the reversed field map.
1164
+ *
1165
+ * @example
1166
+ * ```typescript
1167
+ * const requestMap = { processName: 'releaseName' };
1168
+ * rewriteODataIdentifiers("processName eq 'processName'", requestMap);
1169
+ * // "releaseName eq 'processName'" — identifier rewritten, literal preserved
1170
+ * ```
1171
+ */
1172
+ function rewriteODataIdentifiers(expression, requestMap) {
1173
+ if (!expression)
1174
+ return expression;
1175
+ return expression.replace(ODATA_TOKEN_RE, (match) => match.startsWith("'") ? match : (requestMap[match] ?? match));
1176
+ }
1177
+ /**
1178
+ * Symmetric counterpart of {@link transformRequest} for OData query options:
1179
+ * rewrites SDK field identifiers inside the recognized OData string params
1180
+ * (`filter`, `orderby`, `select`, `expand`) to their API names using the
1181
+ * reversed form of a response field map. Returns a shallow copy with the
1182
+ * relevant values rewritten; other keys pass through unchanged.
1183
+ *
1184
+ * Use at the OData edge so SDK consumers can refer to renamed fields by
1185
+ * their SDK name throughout — for reading the response and for filtering /
1186
+ * sorting / projecting / expanding.
1187
+ *
1188
+ * @param options The OData query options as authored with SDK field names
1189
+ * @param responseMap The response field map (API → SDK); reversed internally
1190
+ *
1191
+ * @example
1192
+ * ```typescript
1193
+ * // JobMap renames releaseName → processName on responses.
1194
+ * transformOptions({ filter: "processName eq 'X'" }, JobMap);
1195
+ * // { filter: "releaseName eq 'X'" }
1196
+ * ```
1197
+ */
1198
+ function transformOptions(options, responseMap) {
1199
+ const requestMap = reverseMap(responseMap);
1200
+ if (Object.keys(requestMap).length === 0)
1201
+ return options;
1202
+ const result = { ...options };
1203
+ for (const key of ODATA_FIELD_PARAM_KEYS) {
1204
+ const value = result[key];
1205
+ if (typeof value === 'string') {
1206
+ result[key] = rewriteODataIdentifiers(value, requestMap);
1207
+ }
1208
+ }
1209
+ return result;
1210
+ }
1104
1211
 
1105
1212
  /**
1106
1213
  * Constants used throughout the pagination system
@@ -1864,9 +1971,12 @@ class FolderScopedService extends BaseService {
1864
1971
  * @param name - Resource name to search for
1865
1972
  * @param options - Folder scoping (`folderId` / `folderKey` / `folderPath`) + OData query options (`expand`, `select`)
1866
1973
  * @param transform - Maps a raw OData item to the typed response (e.g. PascalCase → camelCase via field map)
1974
+ * @param responseFieldMap - Optional response field map (API → SDK), reversed internally by
1975
+ * `transformOptions` to rewrite SDK field names back to API names in user-supplied
1976
+ * `expand` / `select` (symmetric counterpart to `transform`)
1867
1977
  * @throws ValidationError when inputs are malformed; NotFoundError when no match
1868
1978
  */
1869
- async getByNameLookup(resourceType, endpoint, name, options, transform) {
1979
+ async getByNameLookup(resourceType, endpoint, name, options, transform, responseFieldMap) {
1870
1980
  const validatedName = validateName(resourceType, name);
1871
1981
  const { folderId, folderKey, folderPath, ...queryOptions } = options;
1872
1982
  const headers = resolveFolderHeaders({
@@ -1876,8 +1986,11 @@ class FolderScopedService extends BaseService {
1876
1986
  resourceType: `${resourceType}.getByName`,
1877
1987
  fallbackFolderKey: this.config.folderKey,
1878
1988
  });
1989
+ const apiFieldOptions = responseFieldMap
1990
+ ? transformOptions(queryOptions, responseFieldMap)
1991
+ : queryOptions;
1879
1992
  const apiOptions = {
1880
- ...addPrefixToKeys(queryOptions, ODATA_PREFIX, Object.keys(queryOptions)),
1993
+ ...addPrefixToKeys(apiFieldOptions, ODATA_PREFIX, Object.keys(apiFieldOptions)),
1881
1994
  '$filter': `Name eq '${validatedName.replace(SINGLE_QUOTE_RE, "''")}'`,
1882
1995
  '$top': '1',
1883
1996
  };
@@ -2023,6 +2136,10 @@ class AssetService extends FolderScopedService {
2023
2136
  async getAll(options) {
2024
2137
  // Transformation function for assets
2025
2138
  const transformAssetResponse = (asset) => transformData(pascalToCamelCaseKeys(asset), AssetMap);
2139
+ // Rewrite renamed SDK field names → API names inside OData strings
2140
+ // before delegating, mirroring the transformRequest pattern used for
2141
+ // request bodies.
2142
+ const apiOptions = options ? transformOptions(options, AssetMap) : options;
2026
2143
  return PaginationHelpers.getAll({
2027
2144
  serviceAccess: this.createPaginationServiceAccess(),
2028
2145
  getEndpoint: (folderId) => folderId ? ASSET_ENDPOINTS.GET_BY_FOLDER : ASSET_ENDPOINTS.GET_ALL,
@@ -2038,7 +2155,7 @@ class AssetService extends FolderScopedService {
2038
2155
  countParam: ODATA_OFFSET_PARAMS.COUNT_PARAM
2039
2156
  }
2040
2157
  }
2041
- }, options);
2158
+ }, apiOptions);
2042
2159
  }
2043
2160
  /**
2044
2161
  * Gets a single asset by ID
@@ -2060,8 +2177,8 @@ class AssetService extends FolderScopedService {
2060
2177
  */
2061
2178
  async getById(id, folderId, options = {}) {
2062
2179
  const headers = createHeaders({ [FOLDER_ID]: folderId });
2063
- const keysToPrefix = Object.keys(options);
2064
- const apiOptions = addPrefixToKeys(options, ODATA_PREFIX, keysToPrefix);
2180
+ const apiFieldOptions = transformOptions(options, AssetMap);
2181
+ const apiOptions = addPrefixToKeys(apiFieldOptions, ODATA_PREFIX, Object.keys(apiFieldOptions));
2065
2182
  const response = await this.get(ASSET_ENDPOINTS.GET_BY_ID(id), {
2066
2183
  headers,
2067
2184
  params: apiOptions
@@ -2096,7 +2213,7 @@ class AssetService extends FolderScopedService {
2096
2213
  * ```
2097
2214
  */
2098
2215
  async getByName(name, options = {}) {
2099
- return this.getByNameLookup('Asset', ASSET_ENDPOINTS.GET_BY_FOLDER, name, options, (raw) => transformData(pascalToCamelCaseKeys(raw), AssetMap));
2216
+ return this.getByNameLookup('Asset', ASSET_ENDPOINTS.GET_BY_FOLDER, name, options, (raw) => transformData(pascalToCamelCaseKeys(raw), AssetMap), AssetMap);
2100
2217
  }
2101
2218
  /**
2102
2219
  * Updates the value of an existing asset by ID.
@@ -277,7 +277,16 @@ class NetworkError extends UiPathError {
277
277
  * Returns the original value if parsing fails.
278
278
  */
279
279
  /**
280
- * Transforms data by mapping fields according to the provided field mapping
280
+ * Transforms data by renaming each key in `data` exactly once, using the
281
+ * mapping (`sourceField → targetField`). Keys not present in the mapping
282
+ * pass through unchanged. The original (pre-rename) key is dropped — the
283
+ * result contains only the renamed key.
284
+ *
285
+ * Each rename is independent. If the mapping happens to contain chained
286
+ * entries (`a → b` and `b → c`), they do NOT compose: a field named `a`
287
+ * in `data` becomes `b` (not `c`), because the renames are applied based
288
+ * on the original data's keys, not the running result.
289
+ *
281
290
  * @param data The source data to transform
282
291
  * @param fieldMapping Object mapping source field names to target field names
283
292
  * @returns Transformed data with mapped field names
@@ -300,21 +309,28 @@ class NetworkError extends UiPathError {
300
309
  * // { userId: '123', name: 'john' },
301
310
  * // { userId: '456', name: 'jane' }
302
311
  * // ]
312
+ *
313
+ * // No chaining — `a → b` does not become `a → c` even if the map has `b → c`.
314
+ * transformData({ a: 1 }, { a: 'b', b: 'c' });
315
+ * // result = { b: 1 }
303
316
  * ```
304
317
  */
305
318
  function transformData(data, fieldMapping) {
319
+ // Pass null/undefined through unchanged — callers (e.g. AttachmentService.getById)
320
+ // may invoke this on optional fields that an OData `select` excluded.
321
+ if (data == null) {
322
+ return data;
323
+ }
306
324
  // Handle array of objects
307
325
  if (Array.isArray(data)) {
308
326
  return data.map(item => transformData(item, fieldMapping));
309
327
  }
310
- // Handle single object
311
- const result = { ...data };
312
- for (const [sourceField, targetField] of Object.entries(fieldMapping)) {
313
- if (sourceField in result) {
314
- const value = result[sourceField];
315
- delete result[sourceField];
316
- result[targetField] = value;
317
- }
328
+ // Walk the ORIGINAL data's keys, look up each in the mapping. One rename
329
+ // per data key — no mutation of an in-progress result, so chains can't form.
330
+ const result = {};
331
+ for (const [key, value] of Object.entries(data)) {
332
+ const renamedKey = fieldMapping[key] ?? key;
333
+ result[renamedKey] = value;
318
334
  }
319
335
  return result;
320
336
  }
@@ -424,6 +440,97 @@ function addPrefixToKeys(obj, prefix, keys) {
424
440
  }
425
441
  return result;
426
442
  }
443
+ /**
444
+ * Creates a new map with the keys and values reversed
445
+ * @param map The original map to reverse
446
+ * @returns A new map with keys and values swapped
447
+ *
448
+ * @example
449
+ * ```typescript
450
+ * const original = { key1: 'value1', key2: 'value2' };
451
+ * const reversed = reverseMap(original);
452
+ * // reversed = { value1: 'key1', value2: 'key2' }
453
+ * ```
454
+ */
455
+ function reverseMap(map) {
456
+ return Object.entries(map).reduce((acc, [key, value]) => {
457
+ acc[value] = key;
458
+ return acc;
459
+ }, {});
460
+ }
461
+ /**
462
+ * OData query-string keys whose values may contain field identifiers that
463
+ * need rewriting from SDK names → API names.
464
+ */
465
+ const ODATA_FIELD_PARAM_KEYS = ['filter', 'orderby', 'select', 'expand'];
466
+ /**
467
+ * Matches one token at a time in an OData expression:
468
+ * 1. A single-quoted string literal, allowing the `''` escape sequence —
469
+ * consumed atomically so identifiers inside the literal can't match.
470
+ * 2. An OData identifier (`[A-Za-z_][A-Za-z0-9_]*`).
471
+ * Anything else (whitespace, operators, parens, commas) is left alone by
472
+ * `String.prototype.replace`, which only substitutes matched substrings.
473
+ */
474
+ const ODATA_TOKEN_RE = /'(?:[^']|'')*'|[A-Za-z_][A-Za-z0-9_]*/g;
475
+ /**
476
+ * Rewrites SDK field identifiers to API field identifiers inside an OData
477
+ * expression string (`$filter`, `$orderby`, `$select`, `$expand`).
478
+ *
479
+ * Field maps (e.g. `JobMap`) rename API fields → SDK fields on responses, so
480
+ * SDK consumers see the renamed names. Without this rewrite, the same name
481
+ * in a `filter` string would be forwarded verbatim and the API (which still
482
+ * uses the original name) would reject it.
483
+ *
484
+ * Quoted string literals (with the OData `''` escape) are preserved exactly:
485
+ * the token regex consumes them whole, so identifiers inside literals never
486
+ * match. Identifier tokens are looked up in the reversed field map.
487
+ *
488
+ * @example
489
+ * ```typescript
490
+ * const requestMap = { processName: 'releaseName' };
491
+ * rewriteODataIdentifiers("processName eq 'processName'", requestMap);
492
+ * // "releaseName eq 'processName'" — identifier rewritten, literal preserved
493
+ * ```
494
+ */
495
+ function rewriteODataIdentifiers(expression, requestMap) {
496
+ if (!expression)
497
+ return expression;
498
+ return expression.replace(ODATA_TOKEN_RE, (match) => match.startsWith("'") ? match : (requestMap[match] ?? match));
499
+ }
500
+ /**
501
+ * Symmetric counterpart of {@link transformRequest} for OData query options:
502
+ * rewrites SDK field identifiers inside the recognized OData string params
503
+ * (`filter`, `orderby`, `select`, `expand`) to their API names using the
504
+ * reversed form of a response field map. Returns a shallow copy with the
505
+ * relevant values rewritten; other keys pass through unchanged.
506
+ *
507
+ * Use at the OData edge so SDK consumers can refer to renamed fields by
508
+ * their SDK name throughout — for reading the response and for filtering /
509
+ * sorting / projecting / expanding.
510
+ *
511
+ * @param options The OData query options as authored with SDK field names
512
+ * @param responseMap The response field map (API → SDK); reversed internally
513
+ *
514
+ * @example
515
+ * ```typescript
516
+ * // JobMap renames releaseName → processName on responses.
517
+ * transformOptions({ filter: "processName eq 'X'" }, JobMap);
518
+ * // { filter: "releaseName eq 'X'" }
519
+ * ```
520
+ */
521
+ function transformOptions(options, responseMap) {
522
+ const requestMap = reverseMap(responseMap);
523
+ if (Object.keys(requestMap).length === 0)
524
+ return options;
525
+ const result = { ...options };
526
+ for (const key of ODATA_FIELD_PARAM_KEYS) {
527
+ const value = result[key];
528
+ if (typeof value === 'string') {
529
+ result[key] = rewriteODataIdentifiers(value, requestMap);
530
+ }
531
+ }
532
+ return result;
533
+ }
427
534
 
428
535
  /**
429
536
  * Base path constants for different services
@@ -1777,9 +1884,10 @@ class AttachmentService extends BaseService {
1777
1884
  if (!id) {
1778
1885
  throw new ValidationError({ message: 'id is required for getById' });
1779
1886
  }
1780
- // Prefix all keys in options with $ for OData
1781
- const keysToPrefix = Object.keys(options);
1782
- const apiOptions = addPrefixToKeys(options, ODATA_PREFIX, keysToPrefix);
1887
+ // Response applies both maps (BucketMap on blobFileAccess, AttachmentsMap on top-level);
1888
+ // merge so SDK names from either are rewritten in one pass.
1889
+ const apiFieldOptions = transformOptions(options, { ...AttachmentsMap, ...BucketMap });
1890
+ const apiOptions = addPrefixToKeys(apiFieldOptions, ODATA_PREFIX, Object.keys(apiFieldOptions));
1783
1891
  const response = await this.get(ORCHESTRATOR_ATTACHMENT_ENDPOINTS.GET_BY_ID(id), {
1784
1892
  params: apiOptions,
1785
1893
  });
@@ -275,7 +275,16 @@ class NetworkError extends UiPathError {
275
275
  * Returns the original value if parsing fails.
276
276
  */
277
277
  /**
278
- * Transforms data by mapping fields according to the provided field mapping
278
+ * Transforms data by renaming each key in `data` exactly once, using the
279
+ * mapping (`sourceField → targetField`). Keys not present in the mapping
280
+ * pass through unchanged. The original (pre-rename) key is dropped — the
281
+ * result contains only the renamed key.
282
+ *
283
+ * Each rename is independent. If the mapping happens to contain chained
284
+ * entries (`a → b` and `b → c`), they do NOT compose: a field named `a`
285
+ * in `data` becomes `b` (not `c`), because the renames are applied based
286
+ * on the original data's keys, not the running result.
287
+ *
279
288
  * @param data The source data to transform
280
289
  * @param fieldMapping Object mapping source field names to target field names
281
290
  * @returns Transformed data with mapped field names
@@ -298,21 +307,28 @@ class NetworkError extends UiPathError {
298
307
  * // { userId: '123', name: 'john' },
299
308
  * // { userId: '456', name: 'jane' }
300
309
  * // ]
310
+ *
311
+ * // No chaining — `a → b` does not become `a → c` even if the map has `b → c`.
312
+ * transformData({ a: 1 }, { a: 'b', b: 'c' });
313
+ * // result = { b: 1 }
301
314
  * ```
302
315
  */
303
316
  function transformData(data, fieldMapping) {
317
+ // Pass null/undefined through unchanged — callers (e.g. AttachmentService.getById)
318
+ // may invoke this on optional fields that an OData `select` excluded.
319
+ if (data == null) {
320
+ return data;
321
+ }
304
322
  // Handle array of objects
305
323
  if (Array.isArray(data)) {
306
324
  return data.map(item => transformData(item, fieldMapping));
307
325
  }
308
- // Handle single object
309
- const result = { ...data };
310
- for (const [sourceField, targetField] of Object.entries(fieldMapping)) {
311
- if (sourceField in result) {
312
- const value = result[sourceField];
313
- delete result[sourceField];
314
- result[targetField] = value;
315
- }
326
+ // Walk the ORIGINAL data's keys, look up each in the mapping. One rename
327
+ // per data key — no mutation of an in-progress result, so chains can't form.
328
+ const result = {};
329
+ for (const [key, value] of Object.entries(data)) {
330
+ const renamedKey = fieldMapping[key] ?? key;
331
+ result[renamedKey] = value;
316
332
  }
317
333
  return result;
318
334
  }
@@ -422,6 +438,97 @@ function addPrefixToKeys(obj, prefix, keys) {
422
438
  }
423
439
  return result;
424
440
  }
441
+ /**
442
+ * Creates a new map with the keys and values reversed
443
+ * @param map The original map to reverse
444
+ * @returns A new map with keys and values swapped
445
+ *
446
+ * @example
447
+ * ```typescript
448
+ * const original = { key1: 'value1', key2: 'value2' };
449
+ * const reversed = reverseMap(original);
450
+ * // reversed = { value1: 'key1', value2: 'key2' }
451
+ * ```
452
+ */
453
+ function reverseMap(map) {
454
+ return Object.entries(map).reduce((acc, [key, value]) => {
455
+ acc[value] = key;
456
+ return acc;
457
+ }, {});
458
+ }
459
+ /**
460
+ * OData query-string keys whose values may contain field identifiers that
461
+ * need rewriting from SDK names → API names.
462
+ */
463
+ const ODATA_FIELD_PARAM_KEYS = ['filter', 'orderby', 'select', 'expand'];
464
+ /**
465
+ * Matches one token at a time in an OData expression:
466
+ * 1. A single-quoted string literal, allowing the `''` escape sequence —
467
+ * consumed atomically so identifiers inside the literal can't match.
468
+ * 2. An OData identifier (`[A-Za-z_][A-Za-z0-9_]*`).
469
+ * Anything else (whitespace, operators, parens, commas) is left alone by
470
+ * `String.prototype.replace`, which only substitutes matched substrings.
471
+ */
472
+ const ODATA_TOKEN_RE = /'(?:[^']|'')*'|[A-Za-z_][A-Za-z0-9_]*/g;
473
+ /**
474
+ * Rewrites SDK field identifiers to API field identifiers inside an OData
475
+ * expression string (`$filter`, `$orderby`, `$select`, `$expand`).
476
+ *
477
+ * Field maps (e.g. `JobMap`) rename API fields → SDK fields on responses, so
478
+ * SDK consumers see the renamed names. Without this rewrite, the same name
479
+ * in a `filter` string would be forwarded verbatim and the API (which still
480
+ * uses the original name) would reject it.
481
+ *
482
+ * Quoted string literals (with the OData `''` escape) are preserved exactly:
483
+ * the token regex consumes them whole, so identifiers inside literals never
484
+ * match. Identifier tokens are looked up in the reversed field map.
485
+ *
486
+ * @example
487
+ * ```typescript
488
+ * const requestMap = { processName: 'releaseName' };
489
+ * rewriteODataIdentifiers("processName eq 'processName'", requestMap);
490
+ * // "releaseName eq 'processName'" — identifier rewritten, literal preserved
491
+ * ```
492
+ */
493
+ function rewriteODataIdentifiers(expression, requestMap) {
494
+ if (!expression)
495
+ return expression;
496
+ return expression.replace(ODATA_TOKEN_RE, (match) => match.startsWith("'") ? match : (requestMap[match] ?? match));
497
+ }
498
+ /**
499
+ * Symmetric counterpart of {@link transformRequest} for OData query options:
500
+ * rewrites SDK field identifiers inside the recognized OData string params
501
+ * (`filter`, `orderby`, `select`, `expand`) to their API names using the
502
+ * reversed form of a response field map. Returns a shallow copy with the
503
+ * relevant values rewritten; other keys pass through unchanged.
504
+ *
505
+ * Use at the OData edge so SDK consumers can refer to renamed fields by
506
+ * their SDK name throughout — for reading the response and for filtering /
507
+ * sorting / projecting / expanding.
508
+ *
509
+ * @param options The OData query options as authored with SDK field names
510
+ * @param responseMap The response field map (API → SDK); reversed internally
511
+ *
512
+ * @example
513
+ * ```typescript
514
+ * // JobMap renames releaseName → processName on responses.
515
+ * transformOptions({ filter: "processName eq 'X'" }, JobMap);
516
+ * // { filter: "releaseName eq 'X'" }
517
+ * ```
518
+ */
519
+ function transformOptions(options, responseMap) {
520
+ const requestMap = reverseMap(responseMap);
521
+ if (Object.keys(requestMap).length === 0)
522
+ return options;
523
+ const result = { ...options };
524
+ for (const key of ODATA_FIELD_PARAM_KEYS) {
525
+ const value = result[key];
526
+ if (typeof value === 'string') {
527
+ result[key] = rewriteODataIdentifiers(value, requestMap);
528
+ }
529
+ }
530
+ return result;
531
+ }
425
532
 
426
533
  /**
427
534
  * Base path constants for different services
@@ -1775,9 +1882,10 @@ class AttachmentService extends BaseService {
1775
1882
  if (!id) {
1776
1883
  throw new ValidationError({ message: 'id is required for getById' });
1777
1884
  }
1778
- // Prefix all keys in options with $ for OData
1779
- const keysToPrefix = Object.keys(options);
1780
- const apiOptions = addPrefixToKeys(options, ODATA_PREFIX, keysToPrefix);
1885
+ // Response applies both maps (BucketMap on blobFileAccess, AttachmentsMap on top-level);
1886
+ // merge so SDK names from either are rewritten in one pass.
1887
+ const apiFieldOptions = transformOptions(options, { ...AttachmentsMap, ...BucketMap });
1888
+ const apiOptions = addPrefixToKeys(apiFieldOptions, ODATA_PREFIX, Object.keys(apiFieldOptions));
1781
1889
  const response = await this.get(ORCHESTRATOR_ATTACHMENT_ENDPOINTS.GET_BY_ID(id), {
1782
1890
  params: apiOptions,
1783
1891
  });