@qontoctl/core 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +93 -0
  3. package/dist/api-types.d.ts +38 -0
  4. package/dist/api-types.d.ts.map +1 -0
  5. package/dist/api-types.js +4 -0
  6. package/dist/api-types.js.map +1 -0
  7. package/dist/auth/api-key.d.ts +16 -0
  8. package/dist/auth/api-key.d.ts.map +1 -0
  9. package/dist/auth/api-key.js +28 -0
  10. package/dist/auth/api-key.js.map +1 -0
  11. package/dist/auth/index.d.ts +2 -0
  12. package/dist/auth/index.d.ts.map +1 -0
  13. package/dist/auth/index.js +4 -0
  14. package/dist/auth/index.js.map +1 -0
  15. package/dist/config/env.d.ts +17 -0
  16. package/dist/config/env.d.ts.map +1 -0
  17. package/dist/config/env.js +52 -0
  18. package/dist/config/env.js.map +1 -0
  19. package/dist/config/index.d.ts +8 -0
  20. package/dist/config/index.d.ts.map +1 -0
  21. package/dist/config/index.js +7 -0
  22. package/dist/config/index.js.map +1 -0
  23. package/dist/config/loader.d.ts +18 -0
  24. package/dist/config/loader.d.ts.map +1 -0
  25. package/dist/config/loader.js +49 -0
  26. package/dist/config/loader.js.map +1 -0
  27. package/dist/config/resolve.d.ts +19 -0
  28. package/dist/config/resolve.d.ts.map +1 -0
  29. package/dist/config/resolve.js +84 -0
  30. package/dist/config/resolve.js.map +1 -0
  31. package/dist/config/types.d.ts +38 -0
  32. package/dist/config/types.d.ts.map +1 -0
  33. package/dist/config/types.js +4 -0
  34. package/dist/config/types.js.map +1 -0
  35. package/dist/config/validate.d.ts +22 -0
  36. package/dist/config/validate.d.ts.map +1 -0
  37. package/dist/config/validate.js +99 -0
  38. package/dist/config/validate.js.map +1 -0
  39. package/dist/constants.d.ts +7 -0
  40. package/dist/constants.d.ts.map +1 -0
  41. package/dist/constants.js +9 -0
  42. package/dist/constants.js.map +1 -0
  43. package/dist/http-client.d.ts +100 -0
  44. package/dist/http-client.d.ts.map +1 -0
  45. package/dist/http-client.js +210 -0
  46. package/dist/http-client.js.map +1 -0
  47. package/dist/index.d.ts +12 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +7 -1
  50. package/dist/index.js.map +1 -1
  51. package/dist/services/bank-accounts.d.ts +18 -0
  52. package/dist/services/bank-accounts.d.ts.map +1 -0
  53. package/dist/services/bank-accounts.js +23 -0
  54. package/dist/services/bank-accounts.js.map +1 -0
  55. package/dist/services/index.d.ts +3 -0
  56. package/dist/services/index.d.ts.map +1 -0
  57. package/dist/services/index.js +5 -0
  58. package/dist/services/index.js.map +1 -0
  59. package/dist/services/organization.d.ts +10 -0
  60. package/dist/services/organization.d.ts.map +1 -0
  61. package/dist/services/organization.js +13 -0
  62. package/dist/services/organization.js.map +1 -0
  63. package/dist/statements/index.d.ts +2 -0
  64. package/dist/statements/index.d.ts.map +1 -0
  65. package/dist/statements/index.js +4 -0
  66. package/dist/statements/index.js.map +1 -0
  67. package/dist/statements/types.d.ts +19 -0
  68. package/dist/statements/types.d.ts.map +1 -0
  69. package/dist/statements/types.js +4 -0
  70. package/dist/statements/types.js.map +1 -0
  71. package/dist/testing/index.d.ts +2 -0
  72. package/dist/testing/index.d.ts.map +1 -0
  73. package/dist/testing/index.js +4 -0
  74. package/dist/testing/index.js.map +1 -0
  75. package/dist/testing/json-response.d.ts +6 -0
  76. package/dist/testing/json-response.d.ts.map +1 -0
  77. package/dist/testing/json-response.js +14 -0
  78. package/dist/testing/json-response.js.map +1 -0
  79. package/dist/transactions/index.d.ts +3 -0
  80. package/dist/transactions/index.d.ts.map +1 -0
  81. package/dist/transactions/index.js +4 -0
  82. package/dist/transactions/index.js.map +1 -0
  83. package/dist/transactions/service.d.ts +13 -0
  84. package/dist/transactions/service.d.ts.map +1 -0
  85. package/dist/transactions/service.js +65 -0
  86. package/dist/transactions/service.js.map +1 -0
  87. package/dist/transactions/types.d.ts +70 -0
  88. package/dist/transactions/types.d.ts.map +1 -0
  89. package/dist/transactions/types.js +4 -0
  90. package/dist/transactions/types.js.map +1 -0
  91. package/dist/types/index.d.ts +3 -0
  92. package/dist/types/index.d.ts.map +1 -0
  93. package/dist/types/index.js +4 -0
  94. package/dist/types/index.js.map +1 -0
  95. package/dist/types/label.d.ts +10 -0
  96. package/dist/types/label.d.ts.map +1 -0
  97. package/dist/types/label.js +4 -0
  98. package/dist/types/label.js.map +1 -0
  99. package/dist/types/membership.d.ts +17 -0
  100. package/dist/types/membership.d.ts.map +1 -0
  101. package/dist/types/membership.js +4 -0
  102. package/dist/types/membership.js.map +1 -0
  103. package/package.json +32 -11
@@ -0,0 +1,99 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ /**
4
+ * Check whether a profile name is safe to use as a filename.
5
+ *
6
+ * Rejects names containing path separators (`/`, `\`) or parent-directory
7
+ * references (`..`) to prevent path-traversal attacks.
8
+ */
9
+ export function isValidProfileName(name) {
10
+ return !/[/\\]/.test(name) && !name.includes("..");
11
+ }
12
+ const KNOWN_TOP_LEVEL_KEYS = new Set(["api-key", "endpoint", "sandbox"]);
13
+ const KNOWN_API_KEY_KEYS = new Set(["organization-slug", "secret-key"]);
14
+ /**
15
+ * Validates a parsed YAML document against the expected config schema.
16
+ *
17
+ * - Known keys with wrong types produce errors.
18
+ * - Unknown keys produce warnings (forward compatibility).
19
+ * - Returns a partially-populated config for valid fields.
20
+ */
21
+ export function validateConfig(raw) {
22
+ const warnings = [];
23
+ const errors = [];
24
+ const config = {};
25
+ if (raw === null || raw === undefined) {
26
+ return { config, warnings, errors };
27
+ }
28
+ if (typeof raw !== "object" || Array.isArray(raw)) {
29
+ errors.push("Configuration must be a YAML mapping");
30
+ return { config, warnings, errors };
31
+ }
32
+ const doc = raw;
33
+ for (const key of Object.keys(doc)) {
34
+ if (!KNOWN_TOP_LEVEL_KEYS.has(key)) {
35
+ warnings.push(`Unknown configuration key: "${key}"`);
36
+ }
37
+ }
38
+ if ("api-key" in doc) {
39
+ const apiKeySection = doc["api-key"];
40
+ if (apiKeySection === null || apiKeySection === undefined) {
41
+ // api-key section present but empty — not an error, just no credentials from file
42
+ }
43
+ else if (typeof apiKeySection !== "object" || Array.isArray(apiKeySection)) {
44
+ errors.push('"api-key" must be a mapping');
45
+ }
46
+ else {
47
+ const apiKey = apiKeySection;
48
+ for (const key of Object.keys(apiKey)) {
49
+ if (!KNOWN_API_KEY_KEYS.has(key)) {
50
+ warnings.push(`Unknown key in "api-key": "${key}"`);
51
+ }
52
+ }
53
+ const orgSlug = apiKey["organization-slug"];
54
+ const secretKey = apiKey["secret-key"];
55
+ if (orgSlug !== undefined && typeof orgSlug !== "string") {
56
+ errors.push('"api-key.organization-slug" must be a string');
57
+ }
58
+ if (secretKey !== undefined && typeof secretKey !== "string") {
59
+ errors.push('"api-key.secret-key" must be a string');
60
+ }
61
+ if (typeof orgSlug === "string" || typeof secretKey === "string") {
62
+ config.apiKey = {
63
+ organizationSlug: typeof orgSlug === "string" ? orgSlug : "",
64
+ secretKey: typeof secretKey === "string" ? secretKey : "",
65
+ };
66
+ }
67
+ }
68
+ }
69
+ if ("endpoint" in doc) {
70
+ const endpoint = doc["endpoint"];
71
+ if (endpoint !== null && endpoint !== undefined) {
72
+ if (typeof endpoint !== "string") {
73
+ errors.push('"endpoint" must be a string');
74
+ }
75
+ else {
76
+ try {
77
+ new URL(endpoint);
78
+ config.endpoint = endpoint;
79
+ }
80
+ catch {
81
+ errors.push('"endpoint" must be a valid URL');
82
+ }
83
+ }
84
+ }
85
+ }
86
+ if ("sandbox" in doc) {
87
+ const sandbox = doc["sandbox"];
88
+ if (sandbox !== null && sandbox !== undefined) {
89
+ if (typeof sandbox !== "boolean") {
90
+ errors.push('"sandbox" must be a boolean');
91
+ }
92
+ else {
93
+ config.sandbox = sandbox;
94
+ }
95
+ }
96
+ }
97
+ return { config, warnings, errors };
98
+ }
99
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/config/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /** Production API base URL. */
2
+ export declare const API_BASE_URL = "https://thirdparty.qonto.com";
3
+ /** Sandbox API base URL. */
4
+ export declare const SANDBOX_BASE_URL = "https://thirdparty-sandbox.staging.qonto.co";
5
+ /** Configuration directory name under the user's home directory. */
6
+ export declare const CONFIG_DIR = ".qontoctl";
7
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAGA,+BAA+B;AAC/B,eAAO,MAAM,YAAY,iCAAiC,CAAC;AAE3D,4BAA4B;AAC5B,eAAO,MAAM,gBAAgB,gDAAgD,CAAC;AAE9E,oEAAoE;AACpE,eAAO,MAAM,UAAU,cAAc,CAAC"}
@@ -0,0 +1,9 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ /** Production API base URL. */
4
+ export const API_BASE_URL = "https://thirdparty.qonto.com";
5
+ /** Sandbox API base URL. */
6
+ export const SANDBOX_BASE_URL = "https://thirdparty-sandbox.staging.qonto.co";
7
+ /** Configuration directory name under the user's home directory. */
8
+ export const CONFIG_DIR = ".qontoctl";
9
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,+BAA+B;AAC/B,MAAM,CAAC,MAAM,YAAY,GAAG,8BAA8B,CAAC;AAE3D,4BAA4B;AAC5B,MAAM,CAAC,MAAM,gBAAgB,GAAG,6CAA6C,CAAC;AAE9E,oEAAoE;AACpE,MAAM,CAAC,MAAM,UAAU,GAAG,WAAW,CAAC"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Logger interface for HTTP client wire logging.
3
+ */
4
+ export interface HttpClientLogger {
5
+ verbose(message: string): void;
6
+ debug(message: string): void;
7
+ }
8
+ /**
9
+ * A single error entry from the Qonto API (JSON:API format).
10
+ */
11
+ export interface QontoApiErrorEntry {
12
+ readonly code: string;
13
+ readonly detail: string;
14
+ readonly source?: {
15
+ readonly pointer?: string;
16
+ readonly parameter?: string;
17
+ };
18
+ }
19
+ /**
20
+ * Structured error for Qonto API error responses (4xx/5xx).
21
+ */
22
+ export declare class QontoApiError extends Error {
23
+ readonly status: number;
24
+ readonly errors: readonly QontoApiErrorEntry[];
25
+ constructor(status: number, errors: readonly QontoApiErrorEntry[]);
26
+ }
27
+ /**
28
+ * Error thrown when all retry attempts are exhausted on 429 responses.
29
+ */
30
+ export declare class QontoRateLimitError extends Error {
31
+ readonly retryAfter: number | undefined;
32
+ constructor(retryAfter: number | undefined);
33
+ }
34
+ export interface HttpClientOptions {
35
+ /** Base URL for API requests. */
36
+ readonly baseUrl: string;
37
+ /** Value for the Authorization header. */
38
+ readonly authorization: string;
39
+ /** Logger for verbose/debug output. */
40
+ readonly logger?: HttpClientLogger | undefined;
41
+ /** Maximum number of retries on 429 responses. Defaults to 5. */
42
+ readonly maxRetries?: number | undefined;
43
+ }
44
+ /**
45
+ * Query parameter value: a single string or an array of strings for
46
+ * repeated-key parameters (e.g., `status[]=pending&status[]=completed`).
47
+ */
48
+ export type QueryParamValue = string | readonly string[];
49
+ /**
50
+ * Query parameters for an HTTP request.
51
+ */
52
+ export type QueryParams = Readonly<Record<string, QueryParamValue>>;
53
+ /**
54
+ * Core HTTP client for the Qonto API.
55
+ *
56
+ * Features:
57
+ * - Configurable base URL (production/sandbox)
58
+ * - User-Agent header on all requests
59
+ * - Exponential backoff on 429 responses
60
+ * - Structured error handling for 4xx/5xx
61
+ * - Wire logging (verbose/debug) via injected logger
62
+ */
63
+ export declare class HttpClient {
64
+ private readonly baseUrl;
65
+ private readonly authorization;
66
+ private readonly logger;
67
+ private readonly maxRetries;
68
+ private readonly userAgent;
69
+ constructor(options: HttpClientOptions);
70
+ /**
71
+ * Sends an HTTP request and parses the response as JSON.
72
+ *
73
+ * **Trust boundary**: The parsed JSON is cast to `T` without runtime validation.
74
+ * Callers trust the Qonto API contract for response shapes.
75
+ */
76
+ request<T>(method: string, path: string, options?: {
77
+ readonly body?: unknown;
78
+ readonly params?: QueryParams;
79
+ }): Promise<T>;
80
+ /**
81
+ * Sends an HTTP request expecting no response body (e.g. 204 No Content).
82
+ */
83
+ requestVoid(method: string, path: string, options?: {
84
+ readonly body?: unknown;
85
+ readonly params?: QueryParams;
86
+ }): Promise<void>;
87
+ get<T>(path: string, params?: QueryParams): Promise<T>;
88
+ post<T>(path: string, body?: unknown): Promise<T>;
89
+ delete(path: string): Promise<void>;
90
+ private fetchWithRetry;
91
+ private buildUrl;
92
+ private buildHeaders;
93
+ private parseRetryAfter;
94
+ private extractErrors;
95
+ private safeReadJson;
96
+ private logVerbose;
97
+ private logDebug;
98
+ protected sleep(ms: number): Promise<void>;
99
+ }
100
+ //# sourceMappingURL=http-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE;QAChB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;KAC7B,CAAC;CACH;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;aAEpB,MAAM,EAAE,MAAM;aACd,MAAM,EAAE,SAAS,kBAAkB,EAAE;gBADrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,SAAS,kBAAkB,EAAE;CAMxD;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;aAChB,UAAU,EAAE,MAAM,GAAG,SAAS;gBAA9B,UAAU,EAAE,MAAM,GAAG,SAAS;CAI3D;AAED,MAAM,WAAW,iBAAiB;IAChC,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,0CAA0C;IAC1C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAE/B,uCAAuC;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAE/C,iEAAiE;IACjE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAsCD;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;AAEzD;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;AAEpE;;;;;;;;;GASG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,OAAO,EAAE,iBAAiB;IAQtC;;;;;OAKG;IACG,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;KAC/B,GACA,OAAO,CAAC,CAAC,CAAC;IAOb;;OAEG;IACG,WAAW,CACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;KAC/B,GACA,OAAO,CAAC,IAAI,CAAC;IAIV,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;IAItD,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIjD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAI3B,cAAc;IAmD5B,OAAO,CAAC,QAAQ;IAgBhB,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,aAAa;YAYP,YAAY;IAQ1B,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,QAAQ;IAIhB,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG3C"}
@@ -0,0 +1,210 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { createRequire } from "node:module";
4
+ const require = createRequire(import.meta.url);
5
+ const packageJson = require("../package.json");
6
+ /**
7
+ * Structured error for Qonto API error responses (4xx/5xx).
8
+ */
9
+ export class QontoApiError extends Error {
10
+ status;
11
+ errors;
12
+ constructor(status, errors) {
13
+ const summary = errors.map((e) => `${e.code}: ${e.detail}`).join("; ");
14
+ super(`Qonto API error ${status}: ${summary}`);
15
+ this.status = status;
16
+ this.errors = errors;
17
+ this.name = "QontoApiError";
18
+ }
19
+ }
20
+ /**
21
+ * Error thrown when all retry attempts are exhausted on 429 responses.
22
+ */
23
+ export class QontoRateLimitError extends Error {
24
+ retryAfter;
25
+ constructor(retryAfter) {
26
+ super(`Rate limit exceeded${retryAfter !== undefined ? ` (retry after ${retryAfter}s)` : ""}`);
27
+ this.retryAfter = retryAfter;
28
+ this.name = "QontoRateLimitError";
29
+ }
30
+ }
31
+ const DEFAULT_MAX_RETRIES = 5;
32
+ const BASE_BACKOFF_MS = 1000;
33
+ /**
34
+ * Field names redacted from debug log output to avoid leaking financial data.
35
+ */
36
+ const SENSITIVE_FIELDS = new Set([
37
+ "iban",
38
+ "bic",
39
+ "balance",
40
+ "balance_cents",
41
+ "authorized_balance",
42
+ "authorized_balance_cents",
43
+ ]);
44
+ function redactSensitiveFields(value) {
45
+ if (value === null || value === undefined) {
46
+ return value;
47
+ }
48
+ if (Array.isArray(value)) {
49
+ return value.map((item) => redactSensitiveFields(item));
50
+ }
51
+ if (typeof value === "object") {
52
+ const result = {};
53
+ for (const [key, val] of Object.entries(value)) {
54
+ result[key] = SENSITIVE_FIELDS.has(key) ? "[REDACTED]" : redactSensitiveFields(val);
55
+ }
56
+ return result;
57
+ }
58
+ return value;
59
+ }
60
+ function buildUserAgent() {
61
+ return `QontoCtl/${packageJson.version} (Node.js/${process.versions.node}; ${process.platform})`;
62
+ }
63
+ /**
64
+ * Core HTTP client for the Qonto API.
65
+ *
66
+ * Features:
67
+ * - Configurable base URL (production/sandbox)
68
+ * - User-Agent header on all requests
69
+ * - Exponential backoff on 429 responses
70
+ * - Structured error handling for 4xx/5xx
71
+ * - Wire logging (verbose/debug) via injected logger
72
+ */
73
+ export class HttpClient {
74
+ baseUrl;
75
+ authorization;
76
+ logger;
77
+ maxRetries;
78
+ userAgent;
79
+ constructor(options) {
80
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
81
+ this.authorization = options.authorization;
82
+ this.logger = options.logger;
83
+ this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
84
+ this.userAgent = buildUserAgent();
85
+ }
86
+ /**
87
+ * Sends an HTTP request and parses the response as JSON.
88
+ *
89
+ * **Trust boundary**: The parsed JSON is cast to `T` without runtime validation.
90
+ * Callers trust the Qonto API contract for response shapes.
91
+ */
92
+ async request(method, path, options) {
93
+ const response = await this.fetchWithRetry(method, path, options);
94
+ const responseBody = await response.json();
95
+ this.logDebug(`Response body: ${JSON.stringify(redactSensitiveFields(responseBody))}`);
96
+ return responseBody;
97
+ }
98
+ /**
99
+ * Sends an HTTP request expecting no response body (e.g. 204 No Content).
100
+ */
101
+ async requestVoid(method, path, options) {
102
+ await this.fetchWithRetry(method, path, options);
103
+ }
104
+ async get(path, params) {
105
+ return this.request("GET", path, params !== undefined ? { params } : undefined);
106
+ }
107
+ async post(path, body) {
108
+ return this.request("POST", path, body !== undefined ? { body } : undefined);
109
+ }
110
+ async delete(path) {
111
+ return this.requestVoid("DELETE", path);
112
+ }
113
+ async fetchWithRetry(method, path, options) {
114
+ const url = this.buildUrl(path, options?.params);
115
+ const headers = this.buildHeaders(options?.body !== undefined);
116
+ const body = options?.body !== undefined ? JSON.stringify(options.body) : undefined;
117
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
118
+ this.logVerbose(`${method} ${url.toString()}${attempt > 0 ? ` (retry ${attempt})` : ""}`);
119
+ if (body !== undefined) {
120
+ this.logDebug(`Request body: ${body}`);
121
+ }
122
+ const startTime = performance.now();
123
+ const response = await fetch(url, body !== undefined ? { method, headers, body } : { method, headers });
124
+ const elapsed = performance.now() - startTime;
125
+ this.logVerbose(`${response.status} ${response.statusText} (${elapsed.toFixed(0)}ms)`);
126
+ this.logDebug(`Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
127
+ if (response.status === 429) {
128
+ const retryAfter = this.parseRetryAfter(response);
129
+ if (attempt === this.maxRetries) {
130
+ throw new QontoRateLimitError(retryAfter);
131
+ }
132
+ const delay = retryAfter !== undefined ? retryAfter * 1000 : BASE_BACKOFF_MS * Math.pow(2, attempt);
133
+ this.logVerbose(`Rate limited, waiting ${delay}ms before retry ${attempt + 1}`);
134
+ await this.sleep(delay);
135
+ continue;
136
+ }
137
+ if (!response.ok) {
138
+ const errorBody = await this.safeReadJson(response);
139
+ const errors = this.extractErrors(errorBody);
140
+ throw new QontoApiError(response.status, errors);
141
+ }
142
+ return response;
143
+ }
144
+ // Unreachable in practice: the loop always returns or throws
145
+ throw new QontoRateLimitError(undefined);
146
+ }
147
+ buildUrl(path, params) {
148
+ const url = new URL(`${this.baseUrl}${path}`);
149
+ if (params) {
150
+ for (const [key, value] of Object.entries(params)) {
151
+ if (typeof value === "string") {
152
+ url.searchParams.set(key, value);
153
+ }
154
+ else {
155
+ for (const v of value) {
156
+ url.searchParams.append(key, v);
157
+ }
158
+ }
159
+ }
160
+ }
161
+ return url;
162
+ }
163
+ buildHeaders(hasBody) {
164
+ const headers = {
165
+ Authorization: this.authorization,
166
+ "User-Agent": this.userAgent,
167
+ Accept: "application/json",
168
+ };
169
+ if (hasBody) {
170
+ headers["Content-Type"] = "application/json";
171
+ }
172
+ this.logDebug(`Request headers: ${JSON.stringify({ ...headers, Authorization: "[REDACTED]" })}`);
173
+ return headers;
174
+ }
175
+ parseRetryAfter(response) {
176
+ const header = response.headers.get("Retry-After");
177
+ if (header === null) {
178
+ return undefined;
179
+ }
180
+ const seconds = Number(header);
181
+ return Number.isFinite(seconds) && seconds > 0 ? seconds : undefined;
182
+ }
183
+ extractErrors(body) {
184
+ if (typeof body === "object" &&
185
+ body !== null &&
186
+ "errors" in body &&
187
+ Array.isArray(body.errors)) {
188
+ return body.errors;
189
+ }
190
+ return [{ code: "unknown", detail: "Unknown error" }];
191
+ }
192
+ async safeReadJson(response) {
193
+ try {
194
+ return await response.json();
195
+ }
196
+ catch {
197
+ return undefined;
198
+ }
199
+ }
200
+ logVerbose(message) {
201
+ this.logger?.verbose(message);
202
+ }
203
+ logDebug(message) {
204
+ this.logger?.debug(message);
205
+ }
206
+ sleep(ms) {
207
+ return new Promise((resolve) => setTimeout(resolve, ms));
208
+ }
209
+ }
210
+ //# sourceMappingURL=http-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,13 @@
1
- export {};
1
+ export { HttpClient, QontoApiError, QontoRateLimitError, type HttpClientLogger, type HttpClientOptions, type QueryParams, type QueryParamValue, type QontoApiErrorEntry, } from "./http-client.js";
2
+ export { resolveConfig, ConfigError, isValidProfileName, loadConfigFile, validateConfig, applyEnvOverlay, } from "./config/index.js";
3
+ export type { ApiKeyCredentials, QontoctlConfig, ConfigResult, ResolveOptions, LoadResult, ValidationResult, } from "./config/index.js";
4
+ export { AuthError, buildApiKeyAuthorization } from "./auth/index.js";
5
+ export { API_BASE_URL, CONFIG_DIR, SANDBOX_BASE_URL } from "./constants.js";
6
+ export type { Label, Membership } from "./types/index.js";
7
+ export type { Statement, StatementFile } from "./statements/index.js";
8
+ export { buildTransactionQueryParams, getTransaction } from "./transactions/index.js";
9
+ export type { Transaction, TransactionLabel, ListTransactionsParams } from "./transactions/index.js";
10
+ export type { BankAccount, Organization, PaginationMeta } from "./api-types.js";
11
+ export { getOrganization } from "./services/organization.js";
12
+ export { getBankAccount, resolveDefaultBankAccount } from "./services/bank-accounts.js";
2
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,UAAU,EACV,aAAa,EACb,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,kBAAkB,GACxB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,cAAc,EACd,UAAU,EACV,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,SAAS,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAE5E,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE1D,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAAE,2BAA2B,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEtF,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAErG,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhF,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,10 @@
1
- export {};
2
1
  // SPDX-License-Identifier: AGPL-3.0-only
3
2
  // Copyright (C) 2026 Oleksii PELYKH
3
+ export { HttpClient, QontoApiError, QontoRateLimitError, } from "./http-client.js";
4
+ export { resolveConfig, ConfigError, isValidProfileName, loadConfigFile, validateConfig, applyEnvOverlay, } from "./config/index.js";
5
+ export { AuthError, buildApiKeyAuthorization } from "./auth/index.js";
6
+ export { API_BASE_URL, CONFIG_DIR, SANDBOX_BASE_URL } from "./constants.js";
7
+ export { buildTransactionQueryParams, getTransaction } from "./transactions/index.js";
8
+ export { getOrganization } from "./services/organization.js";
9
+ export { getBankAccount, resolveDefaultBankAccount } from "./services/bank-accounts.js";
4
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,yCAAyC;AACzC,oCAAoC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EACL,UAAU,EACV,aAAa,EACb,mBAAmB,GAMpB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAW3B,OAAO,EAAE,SAAS,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAM5E,OAAO,EAAE,2BAA2B,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAMtF,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { BankAccount, Organization } from "../api-types.js";
2
+ import type { HttpClient } from "../http-client.js";
3
+ /**
4
+ * Fetch a single bank account by ID.
5
+ *
6
+ * @param client - The HTTP client to use for the request.
7
+ * @param id - The bank account UUID.
8
+ * @returns The bank account details.
9
+ */
10
+ export declare function getBankAccount(client: HttpClient, id: string): Promise<BankAccount>;
11
+ /**
12
+ * Resolve the default bank account from an organization.
13
+ *
14
+ * Returns the account marked as `main`, or falls back to the first account.
15
+ * Returns `undefined` if the organization has no bank accounts.
16
+ */
17
+ export declare function resolveDefaultBankAccount(org: Organization): BankAccount | undefined;
18
+ //# sourceMappingURL=bank-accounts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bank-accounts.d.ts","sourceRoot":"","sources":["../../src/services/bank-accounts.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAMpD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAGzF;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,YAAY,GAAG,WAAW,GAAG,SAAS,CAEpF"}
@@ -0,0 +1,23 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ /**
4
+ * Fetch a single bank account by ID.
5
+ *
6
+ * @param client - The HTTP client to use for the request.
7
+ * @param id - The bank account UUID.
8
+ * @returns The bank account details.
9
+ */
10
+ export async function getBankAccount(client, id) {
11
+ const response = await client.get(`/v2/bank_accounts/${encodeURIComponent(id)}`);
12
+ return response.bank_account;
13
+ }
14
+ /**
15
+ * Resolve the default bank account from an organization.
16
+ *
17
+ * Returns the account marked as `main`, or falls back to the first account.
18
+ * Returns `undefined` if the organization has no bank accounts.
19
+ */
20
+ export function resolveDefaultBankAccount(org) {
21
+ return org.bank_accounts.find((a) => a.main) ?? org.bank_accounts[0];
22
+ }
23
+ //# sourceMappingURL=bank-accounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bank-accounts.js","sourceRoot":"","sources":["../../src/services/bank-accounts.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AASpC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAkB,EAAE,EAAU;IACjE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAsB,qBAAqB,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACtG,OAAO,QAAQ,CAAC,YAAY,CAAC;AAC/B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAiB;IACzD,OAAO,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;AACvE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { getOrganization } from "./organization.js";
2
+ export { getBankAccount } from "./bank-accounts.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,5 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ export { getOrganization } from "./organization.js";
4
+ export { getBankAccount } from "./bank-accounts.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { Organization } from "../api-types.js";
2
+ import type { HttpClient } from "../http-client.js";
3
+ /**
4
+ * Fetch the authenticated organization details, including its bank accounts.
5
+ *
6
+ * @param client - The HTTP client to use for the request.
7
+ * @returns The organization object with nested bank accounts.
8
+ */
9
+ export declare function getOrganization(client: HttpClient): Promise<Organization>;
10
+ //# sourceMappingURL=organization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organization.d.ts","sourceRoot":"","sources":["../../src/services/organization.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAMpD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAG/E"}
@@ -0,0 +1,13 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ /**
4
+ * Fetch the authenticated organization details, including its bank accounts.
5
+ *
6
+ * @param client - The HTTP client to use for the request.
7
+ * @returns The organization object with nested bank accounts.
8
+ */
9
+ export async function getOrganization(client) {
10
+ const response = await client.get("/v2/organization");
11
+ return response.organization;
12
+ }
13
+ //# sourceMappingURL=organization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organization.js","sourceRoot":"","sources":["../../src/services/organization.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AASpC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAkB;IACtD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAuB,kBAAkB,CAAC,CAAC;IAC5E,OAAO,QAAQ,CAAC,YAAY,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export type { Statement, StatementFile } from "./types.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/statements/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,4 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ export {};
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/statements/index.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * File metadata for a bank statement.
3
+ */
4
+ export interface StatementFile {
5
+ readonly file_name: string;
6
+ readonly file_content_type: string;
7
+ readonly file_size: string;
8
+ readonly file_url: string;
9
+ }
10
+ /**
11
+ * A bank statement from the Qonto API.
12
+ */
13
+ export interface Statement {
14
+ readonly id: string;
15
+ readonly bank_account_id: string;
16
+ readonly period: string;
17
+ readonly file: StatementFile;
18
+ }
19
+ //# sourceMappingURL=types.d.ts.map