@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.
- package/README.md +30 -8
- package/dist/commands/access-groups.d.ts +0 -1
- package/dist/commands/access-groups.js +5 -61
- package/dist/commands/account-transaction-allocations.d.ts +0 -1
- package/dist/commands/account-transaction-allocations.js +5 -61
- package/dist/commands/account-transactions.d.ts +0 -1
- package/dist/commands/account-transactions.js +9 -63
- package/dist/commands/accounts.d.ts +0 -1
- package/dist/commands/accounts.js +5 -61
- package/dist/commands/agent-platform.d.ts +0 -1
- package/dist/commands/agent-platform.js +5 -61
- package/dist/commands/analytics.d.ts +0 -1
- package/dist/commands/analytics.js +5 -61
- package/dist/commands/api-keys.d.ts +39 -0
- package/dist/commands/api-keys.js +1117 -0
- package/dist/commands/audit-log.d.ts +0 -1
- package/dist/commands/audit-log.js +5 -61
- package/dist/commands/auth/login.d.ts +9 -0
- package/dist/commands/auth/login.js +73 -20
- package/dist/commands/auth/logout.d.ts +4 -0
- package/dist/commands/auth/logout.js +4 -0
- package/dist/commands/auth/status.js +26 -17
- package/dist/commands/auth/tenant.js +3 -6
- package/dist/commands/auth.d.ts +0 -1
- package/dist/commands/auth.js +5 -34
- package/dist/commands/bank-accounts.d.ts +0 -1
- package/dist/commands/bank-accounts.js +5 -61
- package/dist/commands/bank-integrations.d.ts +0 -1
- package/dist/commands/bank-integrations.js +5 -61
- package/dist/commands/business-partners.d.ts +1 -1
- package/dist/commands/business-partners.js +14 -63
- package/dist/commands/chat.d.ts +0 -1
- package/dist/commands/chat.js +5 -61
- package/dist/commands/config/add.d.ts +3 -0
- package/dist/commands/config/add.js +34 -16
- package/dist/commands/config/current.js +3 -0
- package/dist/commands/config/list.js +3 -0
- package/dist/commands/credit-card-accounts.d.ts +0 -1
- package/dist/commands/credit-card-accounts.js +5 -61
- package/dist/commands/delivery-notes.d.ts +0 -1
- package/dist/commands/delivery-notes.js +7 -63
- package/dist/commands/dunning-configs.d.ts +0 -1
- package/dist/commands/dunning-configs.js +5 -61
- package/dist/commands/dunning-reminders.d.ts +0 -1
- package/dist/commands/dunning-reminders.js +5 -61
- package/dist/commands/dunning-runs.d.ts +0 -1
- package/dist/commands/dunning-runs.js +5 -61
- package/dist/commands/email-transactions.d.ts +0 -1
- package/dist/commands/email-transactions.js +5 -61
- package/dist/commands/employees.d.ts +0 -1
- package/dist/commands/employees.js +5 -61
- package/dist/commands/entity-configurations.d.ts +0 -1
- package/dist/commands/entity-configurations.js +5 -61
- package/dist/commands/event-processor-mappings.d.ts +0 -1
- package/dist/commands/event-processor-mappings.js +5 -61
- package/dist/commands/files.d.ts +0 -1
- package/dist/commands/files.js +5 -61
- package/dist/commands/health.d.ts +0 -1
- package/dist/commands/health.js +5 -61
- package/dist/commands/inbox.d.ts +0 -1
- package/dist/commands/inbox.js +5 -61
- package/dist/commands/integrations.d.ts +0 -1
- package/dist/commands/integrations.js +5 -61
- package/dist/commands/item-categories.d.ts +0 -1
- package/dist/commands/item-categories.js +5 -61
- package/dist/commands/items.d.ts +0 -1
- package/dist/commands/items.js +5 -61
- package/dist/commands/journal-lines.d.ts +0 -1
- package/dist/commands/journal-lines.js +5 -61
- package/dist/commands/journals.d.ts +1 -1
- package/dist/commands/journals.js +9 -62
- package/dist/commands/ledgers.d.ts +0 -1
- package/dist/commands/ledgers.js +5 -61
- package/dist/commands/nango-integrations.d.ts +39 -0
- package/dist/commands/nango-integrations.js +1115 -0
- package/dist/commands/office-locations.d.ts +0 -1
- package/dist/commands/office-locations.js +5 -61
- package/dist/commands/payment-methods.d.ts +0 -1
- package/dist/commands/payment-methods.js +5 -61
- package/dist/commands/payment-runs.d.ts +0 -1
- package/dist/commands/payment-runs.js +5 -61
- package/dist/commands/payment-terms.d.ts +0 -1
- package/dist/commands/payment-terms.js +5 -61
- package/dist/commands/payments.d.ts +0 -1
- package/dist/commands/payments.js +5 -61
- package/dist/commands/price-lists.d.ts +0 -1
- package/dist/commands/price-lists.js +5 -61
- package/dist/commands/processor-mappings.d.ts +0 -1
- package/dist/commands/processor-mappings.js +5 -61
- package/dist/commands/processors.d.ts +0 -1
- package/dist/commands/processors.js +5 -61
- package/dist/commands/purchase-order-types.d.ts +0 -1
- package/dist/commands/purchase-order-types.js +5 -61
- package/dist/commands/purchase-orders.d.ts +0 -1
- package/dist/commands/purchase-orders.js +7 -63
- package/dist/commands/reports.d.ts +0 -1
- package/dist/commands/reports.js +6 -62
- package/dist/commands/salary-types.d.ts +0 -1
- package/dist/commands/salary-types.js +5 -61
- package/dist/commands/sales-invoices.d.ts +0 -1
- package/dist/commands/sales-invoices.js +12 -65
- package/dist/commands/sales-orders.d.ts +0 -1
- package/dist/commands/sales-orders.js +7 -63
- package/dist/commands/saved-views.d.ts +0 -1
- package/dist/commands/saved-views.js +5 -61
- package/dist/commands/scheduled-processor-mappings.d.ts +0 -1
- package/dist/commands/scheduled-processor-mappings.js +5 -61
- package/dist/commands/schedules.d.ts +0 -1
- package/dist/commands/schedules.js +5 -61
- package/dist/commands/switch.js +4 -4
- package/dist/commands/tag-configurations.d.ts +0 -1
- package/dist/commands/tag-configurations.js +9 -64
- package/dist/commands/tags.d.ts +0 -1
- package/dist/commands/tags.js +5 -61
- package/dist/commands/tax-codes.d.ts +0 -1
- package/dist/commands/tax-codes.js +5 -61
- package/dist/commands/tax-matrix.d.ts +0 -1
- package/dist/commands/tax-matrix.js +5 -61
- package/dist/commands/tenants.d.ts +0 -1
- package/dist/commands/tenants.js +7 -62
- package/dist/commands/text-templates.d.ts +0 -1
- package/dist/commands/text-templates.js +5 -61
- package/dist/commands/tools.d.ts +0 -1
- package/dist/commands/tools.js +5 -61
- package/dist/commands/vat-declarations.d.ts +1 -1
- package/dist/commands/vat-declarations.js +11 -62
- package/dist/commands/vendor-invoices.d.ts +0 -1
- package/dist/commands/vendor-invoices.js +10 -65
- package/dist/commands/webhooks.d.ts +0 -1
- package/dist/commands/webhooks.js +5 -61
- package/dist/commands/workflows.d.ts +0 -1
- package/dist/commands/workflows.js +5 -61
- package/dist/lib/api-client.js +3 -6
- package/dist/lib/auth/constants.d.ts +11 -0
- package/dist/lib/auth/constants.js +11 -0
- package/dist/lib/auth/errors.d.ts +17 -0
- package/dist/lib/auth/errors.js +22 -0
- package/dist/lib/auth/flow.d.ts +27 -0
- package/dist/lib/auth/flow.js +163 -0
- package/dist/lib/auth/guards.d.ts +5 -0
- package/dist/lib/auth/guards.js +14 -0
- package/dist/lib/auth/http.d.ts +16 -0
- package/dist/lib/auth/http.js +49 -0
- package/dist/lib/auth/index.d.ts +49 -0
- package/dist/lib/auth/index.js +217 -0
- package/dist/lib/auth/keycloak-provider.d.ts +75 -0
- package/dist/lib/auth/keycloak-provider.js +215 -0
- package/dist/lib/auth/pages.d.ts +25 -0
- package/dist/lib/auth/pages.js +123 -0
- package/dist/lib/auth/pkce.d.ts +11 -0
- package/dist/lib/auth/pkce.js +17 -0
- package/dist/lib/auth/tokens.d.ts +54 -0
- package/dist/lib/auth/tokens.js +104 -0
- package/dist/lib/auth/types.d.ts +57 -0
- package/dist/lib/auth/types.js +1 -0
- package/dist/lib/auth/validated-url.d.ts +6 -0
- package/dist/lib/auth/validated-url.js +53 -0
- package/dist/lib/config.d.ts +13 -3
- package/dist/lib/config.js +24 -19
- package/oclif.manifest.json +440 -71
- package/package.json +2 -1
- package/dist/lib/auth.d.ts +0 -43
- 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", "
|
|
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
|
|
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;
|
|
@@ -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
|
|
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;
|
|
@@ -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
|
|
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;
|
package/dist/lib/api-client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import { getAuthHeaders } from './auth.js';
|
|
3
|
-
import {
|
|
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
|
-
|
|
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>;
|