@aderis-erp/cli 1.0.15 → 1.0.17

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 (163) hide show
  1. package/README.md +30 -8
  2. package/dist/commands/access-groups.d.ts +0 -1
  3. package/dist/commands/access-groups.js +5 -61
  4. package/dist/commands/account-transaction-allocations.d.ts +0 -1
  5. package/dist/commands/account-transaction-allocations.js +5 -61
  6. package/dist/commands/account-transactions.d.ts +0 -1
  7. package/dist/commands/account-transactions.js +9 -63
  8. package/dist/commands/accounts.d.ts +0 -1
  9. package/dist/commands/accounts.js +5 -61
  10. package/dist/commands/agent-platform.d.ts +0 -1
  11. package/dist/commands/agent-platform.js +5 -61
  12. package/dist/commands/analytics.d.ts +0 -1
  13. package/dist/commands/analytics.js +5 -61
  14. package/dist/commands/api-keys.d.ts +39 -0
  15. package/dist/commands/api-keys.js +1117 -0
  16. package/dist/commands/audit-log.d.ts +0 -1
  17. package/dist/commands/audit-log.js +5 -61
  18. package/dist/commands/auth/login.d.ts +9 -0
  19. package/dist/commands/auth/login.js +73 -20
  20. package/dist/commands/auth/logout.d.ts +4 -0
  21. package/dist/commands/auth/logout.js +4 -0
  22. package/dist/commands/auth/status.js +26 -17
  23. package/dist/commands/auth/tenant.js +3 -6
  24. package/dist/commands/auth.d.ts +0 -1
  25. package/dist/commands/auth.js +5 -34
  26. package/dist/commands/bank-accounts.d.ts +0 -1
  27. package/dist/commands/bank-accounts.js +5 -61
  28. package/dist/commands/bank-integrations.d.ts +0 -1
  29. package/dist/commands/bank-integrations.js +5 -61
  30. package/dist/commands/business-partners.d.ts +1 -1
  31. package/dist/commands/business-partners.js +14 -63
  32. package/dist/commands/chat.d.ts +0 -1
  33. package/dist/commands/chat.js +5 -61
  34. package/dist/commands/config/add.d.ts +3 -0
  35. package/dist/commands/config/add.js +34 -16
  36. package/dist/commands/config/current.js +3 -0
  37. package/dist/commands/config/list.js +3 -0
  38. package/dist/commands/credit-card-accounts.d.ts +0 -1
  39. package/dist/commands/credit-card-accounts.js +5 -61
  40. package/dist/commands/delivery-notes.d.ts +0 -1
  41. package/dist/commands/delivery-notes.js +7 -63
  42. package/dist/commands/dunning-configs.d.ts +0 -1
  43. package/dist/commands/dunning-configs.js +5 -61
  44. package/dist/commands/dunning-reminders.d.ts +0 -1
  45. package/dist/commands/dunning-reminders.js +5 -61
  46. package/dist/commands/dunning-runs.d.ts +0 -1
  47. package/dist/commands/dunning-runs.js +5 -61
  48. package/dist/commands/email-transactions.d.ts +0 -1
  49. package/dist/commands/email-transactions.js +5 -61
  50. package/dist/commands/employees.d.ts +0 -1
  51. package/dist/commands/employees.js +5 -61
  52. package/dist/commands/entity-configurations.d.ts +0 -1
  53. package/dist/commands/entity-configurations.js +5 -61
  54. package/dist/commands/event-processor-mappings.d.ts +0 -1
  55. package/dist/commands/event-processor-mappings.js +5 -61
  56. package/dist/commands/files.d.ts +0 -1
  57. package/dist/commands/files.js +5 -61
  58. package/dist/commands/health.d.ts +0 -1
  59. package/dist/commands/health.js +5 -61
  60. package/dist/commands/inbox.d.ts +0 -1
  61. package/dist/commands/inbox.js +5 -61
  62. package/dist/commands/integrations.d.ts +0 -1
  63. package/dist/commands/integrations.js +5 -61
  64. package/dist/commands/item-categories.d.ts +0 -1
  65. package/dist/commands/item-categories.js +5 -61
  66. package/dist/commands/items.d.ts +0 -1
  67. package/dist/commands/items.js +5 -61
  68. package/dist/commands/journal-lines.d.ts +0 -1
  69. package/dist/commands/journal-lines.js +5 -61
  70. package/dist/commands/journals.d.ts +1 -1
  71. package/dist/commands/journals.js +9 -62
  72. package/dist/commands/ledgers.d.ts +0 -1
  73. package/dist/commands/ledgers.js +5 -61
  74. package/dist/commands/nango-integrations.d.ts +39 -0
  75. package/dist/commands/nango-integrations.js +1115 -0
  76. package/dist/commands/office-locations.d.ts +0 -1
  77. package/dist/commands/office-locations.js +5 -61
  78. package/dist/commands/payment-methods.d.ts +0 -1
  79. package/dist/commands/payment-methods.js +5 -61
  80. package/dist/commands/payment-runs.d.ts +0 -1
  81. package/dist/commands/payment-runs.js +5 -61
  82. package/dist/commands/payment-terms.d.ts +0 -1
  83. package/dist/commands/payment-terms.js +5 -61
  84. package/dist/commands/payments.d.ts +0 -1
  85. package/dist/commands/payments.js +5 -61
  86. package/dist/commands/price-lists.d.ts +0 -1
  87. package/dist/commands/price-lists.js +5 -61
  88. package/dist/commands/processor-mappings.d.ts +0 -1
  89. package/dist/commands/processor-mappings.js +5 -61
  90. package/dist/commands/processors.d.ts +0 -1
  91. package/dist/commands/processors.js +5 -61
  92. package/dist/commands/purchase-order-types.d.ts +0 -1
  93. package/dist/commands/purchase-order-types.js +5 -61
  94. package/dist/commands/purchase-orders.d.ts +0 -1
  95. package/dist/commands/purchase-orders.js +7 -63
  96. package/dist/commands/reports.d.ts +0 -1
  97. package/dist/commands/reports.js +6 -62
  98. package/dist/commands/salary-types.d.ts +0 -1
  99. package/dist/commands/salary-types.js +5 -61
  100. package/dist/commands/sales-invoices.d.ts +0 -1
  101. package/dist/commands/sales-invoices.js +12 -65
  102. package/dist/commands/sales-orders.d.ts +0 -1
  103. package/dist/commands/sales-orders.js +7 -63
  104. package/dist/commands/saved-views.d.ts +0 -1
  105. package/dist/commands/saved-views.js +5 -61
  106. package/dist/commands/scheduled-processor-mappings.d.ts +0 -1
  107. package/dist/commands/scheduled-processor-mappings.js +5 -61
  108. package/dist/commands/schedules.d.ts +0 -1
  109. package/dist/commands/schedules.js +5 -61
  110. package/dist/commands/switch.js +4 -4
  111. package/dist/commands/tag-configurations.d.ts +0 -1
  112. package/dist/commands/tag-configurations.js +9 -64
  113. package/dist/commands/tags.d.ts +0 -1
  114. package/dist/commands/tags.js +5 -61
  115. package/dist/commands/tax-codes.d.ts +0 -1
  116. package/dist/commands/tax-codes.js +5 -61
  117. package/dist/commands/tax-matrix.d.ts +0 -1
  118. package/dist/commands/tax-matrix.js +5 -61
  119. package/dist/commands/tenants.d.ts +0 -1
  120. package/dist/commands/tenants.js +7 -62
  121. package/dist/commands/text-templates.d.ts +0 -1
  122. package/dist/commands/text-templates.js +5 -61
  123. package/dist/commands/tools.d.ts +0 -1
  124. package/dist/commands/tools.js +5 -61
  125. package/dist/commands/vat-declarations.d.ts +1 -1
  126. package/dist/commands/vat-declarations.js +11 -62
  127. package/dist/commands/vendor-invoices.d.ts +0 -1
  128. package/dist/commands/vendor-invoices.js +10 -65
  129. package/dist/commands/webhooks.d.ts +0 -1
  130. package/dist/commands/webhooks.js +5 -61
  131. package/dist/commands/workflows.d.ts +0 -1
  132. package/dist/commands/workflows.js +5 -61
  133. package/dist/lib/api-client.js +3 -6
  134. package/dist/lib/auth/constants.d.ts +11 -0
  135. package/dist/lib/auth/constants.js +11 -0
  136. package/dist/lib/auth/errors.d.ts +17 -0
  137. package/dist/lib/auth/errors.js +22 -0
  138. package/dist/lib/auth/flow.d.ts +27 -0
  139. package/dist/lib/auth/flow.js +163 -0
  140. package/dist/lib/auth/guards.d.ts +5 -0
  141. package/dist/lib/auth/guards.js +14 -0
  142. package/dist/lib/auth/http.d.ts +16 -0
  143. package/dist/lib/auth/http.js +49 -0
  144. package/dist/lib/auth/index.d.ts +49 -0
  145. package/dist/lib/auth/index.js +217 -0
  146. package/dist/lib/auth/keycloak-provider.d.ts +75 -0
  147. package/dist/lib/auth/keycloak-provider.js +215 -0
  148. package/dist/lib/auth/pages.d.ts +25 -0
  149. package/dist/lib/auth/pages.js +123 -0
  150. package/dist/lib/auth/pkce.d.ts +11 -0
  151. package/dist/lib/auth/pkce.js +17 -0
  152. package/dist/lib/auth/tokens.d.ts +54 -0
  153. package/dist/lib/auth/tokens.js +104 -0
  154. package/dist/lib/auth/types.d.ts +57 -0
  155. package/dist/lib/auth/types.js +1 -0
  156. package/dist/lib/auth/validated-url.d.ts +6 -0
  157. package/dist/lib/auth/validated-url.js +53 -0
  158. package/dist/lib/config.d.ts +13 -3
  159. package/dist/lib/config.js +24 -19
  160. package/oclif.manifest.json +440 -71
  161. package/package.json +2 -1
  162. package/dist/lib/auth.d.ts +0 -43
  163. package/dist/lib/auth.js +0 -610
@@ -6,25 +6,25 @@ const HAS_IMPORT = true;
6
6
  const HAS_EXPORT = true;
7
7
  const HAS_CAMT053 = false;
8
8
  const NESTED_IMPORTS = ["line-items", "notes", "files", "payments"];
9
- const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "submit", "approve", "decline", "book", "rebuild-journal", "cancel", "confirm-cancellation", "change-vendor", "recalculate-payment-status", "validate", "create-line-items", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "list-payments", "create-payments", "get-payments", "update-payments", "delete-payments", "payments-approve", "payments-cancel"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
9
+ const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "payment-derivation", "submit", "approve", "decline", "book", "rebuild-journal", "cancel", "change-vendor", "recalculate-payment-status", "validate", "create-line-items", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "list-payments", "create-payments", "get-payments", "update-payments", "delete-payments", "payments-approve", "payments-reopen", "payments-cancel"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
10
10
  const ENDPOINTS = {
11
11
  'list': { method: 'GET', path: '/v1/vendor-invoices', summary: 'Get all vendor invoices with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
12
12
  'create': { method: 'POST', path: '/v1/vendor-invoices', summary: 'Create a new vendor invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'payments'] },
13
13
  'get': { method: 'GET', path: '/v1/vendor-invoices/{id}', summary: 'Get a vendor invoice by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
14
14
  'update': { method: 'PATCH', path: '/v1/vendor-invoices/{id}', summary: 'Update a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
15
15
  'delete': { method: 'DELETE', path: '/v1/vendor-invoices/{id}', summary: 'Delete a vendor invoice', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
16
+ 'payment-derivation': { method: 'GET', path: '/v1/vendor-invoices/{id}/payment-derivation', summary: 'Explain how a vendor invoice\'s paid amount is derived', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
16
17
  'submit': { method: 'POST', path: '/v1/vendor-invoices/{id}/submit', summary: 'Submit a vendor invoice for approval (transitions DRAFT to PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
17
18
  'approve': { method: 'POST', path: '/v1/vendor-invoices/{id}/approve', summary: 'Approve a vendor invoice (transitions DRAFT/PENDING_APPROVAL to APPROVED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
18
19
  'decline': { method: 'POST', path: '/v1/vendor-invoices/{id}/decline', summary: 'Decline a vendor invoice (transitions PENDING_APPROVAL to DECLINED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
19
20
  'book': { method: 'POST', path: '/v1/vendor-invoices/{id}/book', summary: 'Manually book a vendor invoice (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
20
21
  'rebuild-journal': { method: 'POST', path: '/v1/vendor-invoices/{id}/rebuild-journal', summary: 'Rebuild a vendor invoice booking journal (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
21
22
  'cancel': { method: 'POST', path: '/v1/vendor-invoices/{id}/cancel', summary: 'Cancel a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
22
- 'confirm-cancellation': { method: 'POST', path: '/v1/vendor-invoices/{id}/confirm-cancellation', summary: 'Confirm bank cancellation for a vendor invoice (PENDING_CANCELLATION to CANCELLED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
23
23
  'change-vendor': { method: 'POST', path: '/v1/vendor-invoices/{id}/change-vendor', summary: 'Change the vendor on a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
24
24
  'recalculate-payment-status': { method: 'POST', path: '/v1/vendor-invoices/{id}/recalculate-payment-status', summary: 'Recalculate a vendor invoice\'s open amount and payment status', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
25
25
  'validate': { method: 'POST', path: '/v1/vendor-invoices/{id}/validate', summary: 'Validate a vendor invoice update without persisting changes', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
26
- 'create-line-items': { method: 'POST', path: '/v1/vendor-invoices/{invoiceId}/line-items', summary: 'Add a line item to a vendor invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
27
- 'update-line-items': { method: 'PATCH', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Update a line item on a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
26
+ 'create-line-items': { method: 'POST', path: '/v1/vendor-invoices/{invoiceId}/line-items', summary: 'Add a line item to a vendor invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems'] },
27
+ 'update-line-items': { method: 'PATCH', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Update a line item on a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems'] },
28
28
  'delete-line-items': { method: 'DELETE', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Remove a line item from a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
29
29
  'list-notes': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes', summary: 'Get all notes for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
30
30
  'create-notes': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes', summary: 'Create a note for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
@@ -43,6 +43,7 @@ const ENDPOINTS = {
43
43
  'update-payments': { method: 'PATCH', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}', summary: 'Update a payment for a vendor invoice', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
44
44
  'delete-payments': { method: 'DELETE', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}', summary: 'Delete a payment from a vendor invoice', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
45
45
  'payments-approve': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/approve', summary: 'Approve a payment entry (OPEN → APPROVED)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
46
+ 'payments-reopen': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/reopen', summary: 'Reopen an approved payment entry (APPROVED → OPEN)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
46
47
  'payments-cancel': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/cancel', summary: 'Cancel a sent payment entry (SENT -> ERROR)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
47
48
  };
48
49
  export default class VendorInvoices extends Command {
@@ -78,7 +79,7 @@ export default class VendorInvoices extends Command {
78
79
  'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
79
80
  filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
80
81
  query: Flags.string({ description: 'Additional query params as JSON' }),
81
- fields: Flags.string({ description: 'Comma-separated list of fields to show (e.g. --fields=id,name,status)' }),
82
+ fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
82
83
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
83
84
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
84
85
  };
@@ -150,6 +151,8 @@ export default class VendorInvoices extends Command {
150
151
  queryParams.search = flags.search;
151
152
  if (flags['sort-by'])
152
153
  queryParams.sortBy = normalizeSortBy(flags['sort-by']);
154
+ if (flags.fields)
155
+ queryParams.fields = flags.fields;
153
156
  if (flags.query) {
154
157
  try {
155
158
  Object.assign(queryParams, JSON.parse(flags.query));
@@ -352,11 +355,6 @@ export default class VendorInvoices extends Command {
352
355
  output = obj[arrayKeys[0]];
353
356
  }
354
357
  }
355
- // Filter fields if --fields flag is provided
356
- if (flags.fields) {
357
- const fieldList = flags.fields.split(',').map(f => f.trim());
358
- output = this.filterFields(output, fieldList);
359
- }
360
358
  // Sanitize output if requested
361
359
  if (flags.sanitize) {
362
360
  output = this.sanitizeOutput(output);
@@ -611,6 +609,8 @@ export default class VendorInvoices extends Command {
611
609
  baseParams.sortBy = normalizeSortBy(flags['sort-by']);
612
610
  if (flags.include)
613
611
  baseParams.include = flags.include;
612
+ if (flags.fields)
613
+ baseParams.fields = flags.fields;
614
614
  if (flags.query) {
615
615
  try {
616
616
  Object.assign(baseParams, JSON.parse(flags.query));
@@ -665,12 +665,7 @@ export default class VendorInvoices extends Command {
665
665
  }
666
666
  }
667
667
  process.stderr.write(' done\n');
668
- // Filter fields if specified
669
668
  let output = allItems;
670
- if (flags.fields) {
671
- const fieldList = flags.fields.split(',').map(f => f.trim());
672
- output = this.filterFields(allItems, fieldList);
673
- }
674
669
  // Sanitize output if requested
675
670
  if (flags.sanitize) {
676
671
  output = this.sanitizeOutput(output);
@@ -999,56 +994,6 @@ export default class VendorInvoices extends Command {
999
994
  return `\$${op}`;
1000
995
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
1001
996
  }
1002
- filterFields(data, fields) {
1003
- const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
1004
- const pick = (obj, keys) => {
1005
- // Group keys by their first segment so sibling sub-paths
1006
- // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
1007
- // and don't clobber each other on merge.
1008
- const directKeys = new Set();
1009
- const nestedKeys = new Map();
1010
- for (const key of keys) {
1011
- if (key.split('.').some(isUnsafeKey))
1012
- continue;
1013
- if (key.includes('.')) {
1014
- const [first, ...rest] = key.split('.');
1015
- if (isUnsafeKey(first))
1016
- continue;
1017
- const list = nestedKeys.get(first);
1018
- if (list) {
1019
- list.push(rest.join('.'));
1020
- }
1021
- else {
1022
- nestedKeys.set(first, [rest.join('.')]);
1023
- }
1024
- }
1025
- else {
1026
- directKeys.add(key);
1027
- }
1028
- }
1029
- const result = {};
1030
- for (const key of directKeys) {
1031
- if (key in obj)
1032
- result[key] = obj[key];
1033
- }
1034
- for (const [first, restKeys] of nestedKeys) {
1035
- const val = obj[first];
1036
- if (!val || typeof val !== 'object')
1037
- continue;
1038
- if (Array.isArray(val)) {
1039
- result[first] = val.map(item => pick(item, restKeys));
1040
- }
1041
- else {
1042
- result[first] = pick(val, restKeys);
1043
- }
1044
- }
1045
- return result;
1046
- };
1047
- if (Array.isArray(data)) {
1048
- return data.map(item => pick(item, fields));
1049
- }
1050
- return pick(data, fields);
1051
- }
1052
997
  sanitizeOutput(data) {
1053
998
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1054
999
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -31,7 +31,6 @@ export default class Webhooks extends Command {
31
31
  private runCamt053Import;
32
32
  private runNestedImport;
33
33
  private parseFilterValue;
34
- private filterFields;
35
34
  private sanitizeOutput;
36
35
  private getColumns;
37
36
  private getValue;
@@ -45,7 +45,7 @@ export default class Webhooks extends Command {
45
45
  'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
46
46
  filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
47
47
  query: Flags.string({ description: 'Additional query params as JSON' }),
48
- fields: Flags.string({ description: 'Comma-separated list of fields to show (e.g. --fields=id,name,status)' }),
48
+ fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
49
49
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
50
50
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
51
51
  };
@@ -117,6 +117,8 @@ export default class Webhooks extends Command {
117
117
  queryParams.search = flags.search;
118
118
  if (flags['sort-by'])
119
119
  queryParams.sortBy = normalizeSortBy(flags['sort-by']);
120
+ if (flags.fields)
121
+ queryParams.fields = flags.fields;
120
122
  if (flags.query) {
121
123
  try {
122
124
  Object.assign(queryParams, JSON.parse(flags.query));
@@ -319,11 +321,6 @@ export default class Webhooks extends Command {
319
321
  output = obj[arrayKeys[0]];
320
322
  }
321
323
  }
322
- // Filter fields if --fields flag is provided
323
- if (flags.fields) {
324
- const fieldList = flags.fields.split(',').map(f => f.trim());
325
- output = this.filterFields(output, fieldList);
326
- }
327
324
  // Sanitize output if requested
328
325
  if (flags.sanitize) {
329
326
  output = this.sanitizeOutput(output);
@@ -578,6 +575,8 @@ export default class Webhooks extends Command {
578
575
  baseParams.sortBy = normalizeSortBy(flags['sort-by']);
579
576
  if (flags.include)
580
577
  baseParams.include = flags.include;
578
+ if (flags.fields)
579
+ baseParams.fields = flags.fields;
581
580
  if (flags.query) {
582
581
  try {
583
582
  Object.assign(baseParams, JSON.parse(flags.query));
@@ -632,12 +631,7 @@ export default class Webhooks extends Command {
632
631
  }
633
632
  }
634
633
  process.stderr.write(' done\n');
635
- // Filter fields if specified
636
634
  let output = allItems;
637
- if (flags.fields) {
638
- const fieldList = flags.fields.split(',').map(f => f.trim());
639
- output = this.filterFields(allItems, fieldList);
640
- }
641
635
  // Sanitize output if requested
642
636
  if (flags.sanitize) {
643
637
  output = this.sanitizeOutput(output);
@@ -966,56 +960,6 @@ export default class Webhooks extends Command {
966
960
  return `\$${op}`;
967
961
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
968
962
  }
969
- filterFields(data, fields) {
970
- const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
971
- const pick = (obj, keys) => {
972
- // Group keys by their first segment so sibling sub-paths
973
- // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
974
- // and don't clobber each other on merge.
975
- const directKeys = new Set();
976
- const nestedKeys = new Map();
977
- for (const key of keys) {
978
- if (key.split('.').some(isUnsafeKey))
979
- continue;
980
- if (key.includes('.')) {
981
- const [first, ...rest] = key.split('.');
982
- if (isUnsafeKey(first))
983
- continue;
984
- const list = nestedKeys.get(first);
985
- if (list) {
986
- list.push(rest.join('.'));
987
- }
988
- else {
989
- nestedKeys.set(first, [rest.join('.')]);
990
- }
991
- }
992
- else {
993
- directKeys.add(key);
994
- }
995
- }
996
- const result = {};
997
- for (const key of directKeys) {
998
- if (key in obj)
999
- result[key] = obj[key];
1000
- }
1001
- for (const [first, restKeys] of nestedKeys) {
1002
- const val = obj[first];
1003
- if (!val || typeof val !== 'object')
1004
- continue;
1005
- if (Array.isArray(val)) {
1006
- result[first] = val.map(item => pick(item, restKeys));
1007
- }
1008
- else {
1009
- result[first] = pick(val, restKeys);
1010
- }
1011
- }
1012
- return result;
1013
- };
1014
- if (Array.isArray(data)) {
1015
- return data.map(item => pick(item, fields));
1016
- }
1017
- return pick(data, fields);
1018
- }
1019
963
  sanitizeOutput(data) {
1020
964
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1021
965
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -31,7 +31,6 @@ export default class Workflows extends Command {
31
31
  private runCamt053Import;
32
32
  private runNestedImport;
33
33
  private parseFilterValue;
34
- private filterFields;
35
34
  private sanitizeOutput;
36
35
  private getColumns;
37
36
  private getValue;
@@ -37,7 +37,7 @@ export default class Workflows extends Command {
37
37
  'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
38
38
  filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
39
39
  query: Flags.string({ description: 'Additional query params as JSON' }),
40
- fields: Flags.string({ description: 'Comma-separated list of fields to show (e.g. --fields=id,name,status)' }),
40
+ fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
41
41
  sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
42
42
  'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
43
43
  };
@@ -109,6 +109,8 @@ export default class Workflows extends Command {
109
109
  queryParams.search = flags.search;
110
110
  if (flags['sort-by'])
111
111
  queryParams.sortBy = normalizeSortBy(flags['sort-by']);
112
+ if (flags.fields)
113
+ queryParams.fields = flags.fields;
112
114
  if (flags.query) {
113
115
  try {
114
116
  Object.assign(queryParams, JSON.parse(flags.query));
@@ -311,11 +313,6 @@ export default class Workflows extends Command {
311
313
  output = obj[arrayKeys[0]];
312
314
  }
313
315
  }
314
- // Filter fields if --fields flag is provided
315
- if (flags.fields) {
316
- const fieldList = flags.fields.split(',').map(f => f.trim());
317
- output = this.filterFields(output, fieldList);
318
- }
319
316
  // Sanitize output if requested
320
317
  if (flags.sanitize) {
321
318
  output = this.sanitizeOutput(output);
@@ -570,6 +567,8 @@ export default class Workflows extends Command {
570
567
  baseParams.sortBy = normalizeSortBy(flags['sort-by']);
571
568
  if (flags.include)
572
569
  baseParams.include = flags.include;
570
+ if (flags.fields)
571
+ baseParams.fields = flags.fields;
573
572
  if (flags.query) {
574
573
  try {
575
574
  Object.assign(baseParams, JSON.parse(flags.query));
@@ -624,12 +623,7 @@ export default class Workflows extends Command {
624
623
  }
625
624
  }
626
625
  process.stderr.write(' done\n');
627
- // Filter fields if specified
628
626
  let output = allItems;
629
- if (flags.fields) {
630
- const fieldList = flags.fields.split(',').map(f => f.trim());
631
- output = this.filterFields(allItems, fieldList);
632
- }
633
627
  // Sanitize output if requested
634
628
  if (flags.sanitize) {
635
629
  output = this.sanitizeOutput(output);
@@ -958,56 +952,6 @@ export default class Workflows extends Command {
958
952
  return `\$${op}`;
959
953
  return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
960
954
  }
961
- filterFields(data, fields) {
962
- const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
963
- const pick = (obj, keys) => {
964
- // Group keys by their first segment so sibling sub-paths
965
- // (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
966
- // and don't clobber each other on merge.
967
- const directKeys = new Set();
968
- const nestedKeys = new Map();
969
- for (const key of keys) {
970
- if (key.split('.').some(isUnsafeKey))
971
- continue;
972
- if (key.includes('.')) {
973
- const [first, ...rest] = key.split('.');
974
- if (isUnsafeKey(first))
975
- continue;
976
- const list = nestedKeys.get(first);
977
- if (list) {
978
- list.push(rest.join('.'));
979
- }
980
- else {
981
- nestedKeys.set(first, [rest.join('.')]);
982
- }
983
- }
984
- else {
985
- directKeys.add(key);
986
- }
987
- }
988
- const result = {};
989
- for (const key of directKeys) {
990
- if (key in obj)
991
- result[key] = obj[key];
992
- }
993
- for (const [first, restKeys] of nestedKeys) {
994
- const val = obj[first];
995
- if (!val || typeof val !== 'object')
996
- continue;
997
- if (Array.isArray(val)) {
998
- result[first] = val.map(item => pick(item, restKeys));
999
- }
1000
- else {
1001
- result[first] = pick(val, restKeys);
1002
- }
1003
- }
1004
- return result;
1005
- };
1006
- if (Array.isArray(data)) {
1007
- return data.map(item => pick(item, fields));
1008
- }
1009
- return pick(data, fields);
1010
- }
1011
955
  sanitizeOutput(data) {
1012
956
  const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
1013
957
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -1,6 +1,6 @@
1
1
  import axios from 'axios';
2
- import { getAuthHeaders } from './auth.js';
3
- import { getAuthType, getBaseUrls, getCurrentContext } from './config.js';
2
+ import { getAuthHeaders } from './auth/index.js';
3
+ import { getBaseUrls, getCurrentContext, unauthorizedMessage } from './config.js';
4
4
  /**
5
5
  * Normalize a --sort-by value: comma-separated fields, each optionally suffixed
6
6
  * with `:ASC` or `:DESC`. Fields without an explicit direction default to DESC
@@ -60,10 +60,7 @@ export async function createApiClient(contextName, options = false) {
60
60
  // Handle 401 with a clear message, pointing at the right remedy for the
61
61
  // context's auth type — re-login only helps OAuth contexts.
62
62
  if (error.response.status === 401) {
63
- const message = getAuthType(context) === 'api_key'
64
- ? `API key for context "${context.name}" was rejected. Check the key with: aderis config set-api-key --context ${context.name}`
65
- : 'Session expired. Please run: aderis auth login';
66
- throw new ApiError(message, 401, error.response.data);
63
+ throw new ApiError(unauthorizedMessage(context), 401, error.response.data);
67
64
  }
68
65
  const message = extractErrorMessage(error.response.data);
69
66
  throw new ApiError(message, error.response.status, error.response.data);
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Loopback redirect (RFC 8252 §7.3). The Keycloak client registers
3
+ * `http://localhost:*`; one shared port keeps every context on a registered redirect.
4
+ */
5
+ export declare const CALLBACK_PORT = 8085;
6
+ export declare const REDIRECT_URI = "http://localhost:8085/oauth/callback";
7
+ /** Headers for `application/x-www-form-urlencoded` token endpoint requests (RFC 6749 §4.1.3). */
8
+ export declare const FORM_POST_HEADERS: {
9
+ readonly Accept: "application/json";
10
+ readonly 'Content-Type': "application/x-www-form-urlencoded";
11
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Loopback redirect (RFC 8252 §7.3). The Keycloak client registers
3
+ * `http://localhost:*`; one shared port keeps every context on a registered redirect.
4
+ */
5
+ export const CALLBACK_PORT = 8085;
6
+ export const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}/oauth/callback`;
7
+ /** Headers for `application/x-www-form-urlencoded` token endpoint requests (RFC 6749 §4.1.3). */
8
+ export const FORM_POST_HEADERS = {
9
+ Accept: 'application/json',
10
+ 'Content-Type': 'application/x-www-form-urlencoded',
11
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * An authentication-protocol failure reported to the user, distinct from
3
+ * transport errors (axios) and from `ApiError`. Never wraps the axios error:
4
+ * its request config carries the form body (refresh token, PKCE verifier,
5
+ * client secret).
6
+ */
7
+ export declare class AuthError extends Error {
8
+ /** HTTP status of the response that caused this error, when there was one. */
9
+ readonly status?: number;
10
+ constructor(message: string, options?: {
11
+ status?: number;
12
+ });
13
+ }
14
+ /** The token that ended a tenant switch is not for the requested tenant; shown as "Tenant Switch Failed" (HTTP 400). */
15
+ export declare class TenantMismatchError extends AuthError {
16
+ constructor(message: string);
17
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * An authentication-protocol failure reported to the user, distinct from
3
+ * transport errors (axios) and from `ApiError`. Never wraps the axios error:
4
+ * its request config carries the form body (refresh token, PKCE verifier,
5
+ * client secret).
6
+ */
7
+ export class AuthError extends Error {
8
+ /** HTTP status of the response that caused this error, when there was one. */
9
+ status;
10
+ constructor(message, options = {}) {
11
+ super(message);
12
+ this.name = 'AuthError';
13
+ this.status = options.status;
14
+ }
15
+ }
16
+ /** The token that ended a tenant switch is not for the requested tenant; shown as "Tenant Switch Failed" (HTTP 400). */
17
+ export class TenantMismatchError extends AuthError {
18
+ constructor(message) {
19
+ super(message);
20
+ this.name = 'TenantMismatchError';
21
+ }
22
+ }
@@ -0,0 +1,27 @@
1
+ import { type Context, type TokenData } from '../config.js';
2
+ import { type PageMessage } from './pages.js';
3
+ import { type AuthProvider, type AuthRound } from './types.js';
4
+ export interface BrowserFlowOptions {
5
+ /** Line logger for the "Opening browser…" hints. Defaults to `console.log`. */
6
+ log?: (line: string) => void;
7
+ /** Opens the authorize URL. Defaults to the system browser; tests inject a stub. */
8
+ openBrowser?: (url: string) => Promise<unknown>;
9
+ /** Page shown after the last round succeeds. */
10
+ success: {
11
+ message: PageMessage | string;
12
+ title: string;
13
+ };
14
+ }
15
+ /** The injectable parts of a browser flow. */
16
+ export type FlowHooks = Pick<BrowserFlowOptions, 'log' | 'openBrowser'>;
17
+ /**
18
+ * Runs one or more authorization rounds through a single local callback server
19
+ * and a single browser tab. Per round: the browser lands on `/oauth/callback`,
20
+ * the code is exchanged via the provider, the tokens are saved to the context
21
+ * and the round's `afterExchange` hook runs (tokens it returns are saved and
22
+ * become the round's result). A following round is entered via
23
+ * a meta-refresh into its authorize URL; the last round answers the success
24
+ * page and resolves with its tokens. A callback whose `state` is not the
25
+ * expected round's is rejected.
26
+ */
27
+ export declare function runBrowserFlow(context: Context, provider: AuthProvider, rounds: AuthRound[], options: BrowserFlowOptions): Promise<TokenData>;