noxctl 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 (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +326 -0
  3. package/dist/auth.d.ts +29 -0
  4. package/dist/auth.d.ts.map +1 -0
  5. package/dist/auth.js +267 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +1012 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/credentials-store.d.ts +4 -0
  12. package/dist/credentials-store.d.ts.map +1 -0
  13. package/dist/credentials-store.js +223 -0
  14. package/dist/credentials-store.js.map +1 -0
  15. package/dist/formatter.d.ts +62 -0
  16. package/dist/formatter.d.ts.map +1 -0
  17. package/dist/formatter.js +200 -0
  18. package/dist/formatter.js.map +1 -0
  19. package/dist/fortnox-client.d.ts +27 -0
  20. package/dist/fortnox-client.d.ts.map +1 -0
  21. package/dist/fortnox-client.js +178 -0
  22. package/dist/fortnox-client.js.map +1 -0
  23. package/dist/identifiers.d.ts +4 -0
  24. package/dist/identifiers.d.ts.map +1 -0
  25. package/dist/identifiers.js +16 -0
  26. package/dist/identifiers.js.map +1 -0
  27. package/dist/index.d.ts +4 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +43 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/operations/accounts.d.ts +18 -0
  32. package/dist/operations/accounts.d.ts.map +1 -0
  33. package/dist/operations/accounts.js +28 -0
  34. package/dist/operations/accounts.js.map +1 -0
  35. package/dist/operations/articles.d.ts +20 -0
  36. package/dist/operations/articles.d.ts.map +1 -0
  37. package/dist/operations/articles.js +36 -0
  38. package/dist/operations/articles.js.map +1 -0
  39. package/dist/operations/company.d.ts +2 -0
  40. package/dist/operations/company.d.ts.map +1 -0
  41. package/dist/operations/company.js +6 -0
  42. package/dist/operations/company.js.map +1 -0
  43. package/dist/operations/customers.d.ts +20 -0
  44. package/dist/operations/customers.d.ts.map +1 -0
  45. package/dist/operations/customers.js +37 -0
  46. package/dist/operations/customers.js.map +1 -0
  47. package/dist/operations/financial-reports.d.ts +37 -0
  48. package/dist/operations/financial-reports.d.ts.map +1 -0
  49. package/dist/operations/financial-reports.js +193 -0
  50. package/dist/operations/financial-reports.js.map +1 -0
  51. package/dist/operations/invoices.d.ts +32 -0
  52. package/dist/operations/invoices.d.ts.map +1 -0
  53. package/dist/operations/invoices.js +74 -0
  54. package/dist/operations/invoices.js.map +1 -0
  55. package/dist/operations/supplier-invoices.d.ts +23 -0
  56. package/dist/operations/supplier-invoices.d.ts.map +1 -0
  57. package/dist/operations/supplier-invoices.js +36 -0
  58. package/dist/operations/supplier-invoices.js.map +1 -0
  59. package/dist/operations/suppliers.d.ts +20 -0
  60. package/dist/operations/suppliers.d.ts.map +1 -0
  61. package/dist/operations/suppliers.js +36 -0
  62. package/dist/operations/suppliers.js.map +1 -0
  63. package/dist/operations/tax.d.ts +26 -0
  64. package/dist/operations/tax.d.ts.map +1 -0
  65. package/dist/operations/tax.js +47 -0
  66. package/dist/operations/tax.js.map +1 -0
  67. package/dist/operations/vouchers.d.ts +22 -0
  68. package/dist/operations/vouchers.d.ts.map +1 -0
  69. package/dist/operations/vouchers.js +41 -0
  70. package/dist/operations/vouchers.js.map +1 -0
  71. package/dist/tool-output.d.ts +16 -0
  72. package/dist/tool-output.d.ts.map +1 -0
  73. package/dist/tool-output.js +48 -0
  74. package/dist/tool-output.js.map +1 -0
  75. package/dist/tools/articles.d.ts +3 -0
  76. package/dist/tools/articles.d.ts.map +1 -0
  77. package/dist/tools/articles.js +75 -0
  78. package/dist/tools/articles.js.map +1 -0
  79. package/dist/tools/bookkeeping.d.ts +3 -0
  80. package/dist/tools/bookkeeping.d.ts.map +1 -0
  81. package/dist/tools/bookkeeping.js +82 -0
  82. package/dist/tools/bookkeeping.js.map +1 -0
  83. package/dist/tools/company.d.ts +3 -0
  84. package/dist/tools/company.d.ts.map +1 -0
  85. package/dist/tools/company.js +13 -0
  86. package/dist/tools/company.js.map +1 -0
  87. package/dist/tools/customers.d.ts +3 -0
  88. package/dist/tools/customers.d.ts.map +1 -0
  89. package/dist/tools/customers.js +75 -0
  90. package/dist/tools/customers.js.map +1 -0
  91. package/dist/tools/financial-reports.d.ts +3 -0
  92. package/dist/tools/financial-reports.d.ts.map +1 -0
  93. package/dist/tools/financial-reports.js +35 -0
  94. package/dist/tools/financial-reports.js.map +1 -0
  95. package/dist/tools/invoices.d.ts +3 -0
  96. package/dist/tools/invoices.d.ts.map +1 -0
  97. package/dist/tools/invoices.js +160 -0
  98. package/dist/tools/invoices.js.map +1 -0
  99. package/dist/tools/status.d.ts +3 -0
  100. package/dist/tools/status.d.ts.map +1 -0
  101. package/dist/tools/status.js +109 -0
  102. package/dist/tools/status.js.map +1 -0
  103. package/dist/tools/supplier-invoices.d.ts +3 -0
  104. package/dist/tools/supplier-invoices.d.ts.map +1 -0
  105. package/dist/tools/supplier-invoices.js +88 -0
  106. package/dist/tools/supplier-invoices.js.map +1 -0
  107. package/dist/tools/suppliers.d.ts +3 -0
  108. package/dist/tools/suppliers.d.ts.map +1 -0
  109. package/dist/tools/suppliers.js +77 -0
  110. package/dist/tools/suppliers.js.map +1 -0
  111. package/dist/tools/tax.d.ts +3 -0
  112. package/dist/tools/tax.d.ts.map +1 -0
  113. package/dist/tools/tax.js +18 -0
  114. package/dist/tools/tax.js.map +1 -0
  115. package/dist/views.d.ts +19 -0
  116. package/dist/views.d.ts.map +1 -0
  117. package/dist/views.js +165 -0
  118. package/dist/views.js.map +1 -0
  119. package/package.json +60 -0
@@ -0,0 +1,178 @@
1
+ import { getValidToken } from './auth.js';
2
+ const BASE_URL = 'https://api.fortnox.se/3';
3
+ // Rate limiter: max 25 requests per 5 seconds
4
+ const RATE_WINDOW_MS = 5000;
5
+ const RATE_LIMIT = 25;
6
+ const requestTimestamps = [];
7
+ async function waitForRateLimit() {
8
+ const now = Date.now();
9
+ // Remove timestamps older than the window
10
+ while (requestTimestamps.length > 0 && requestTimestamps[0] < now - RATE_WINDOW_MS) {
11
+ requestTimestamps.shift();
12
+ }
13
+ if (requestTimestamps.length >= RATE_LIMIT) {
14
+ const oldestInWindow = requestTimestamps[0];
15
+ const waitMs = oldestInWindow + RATE_WINDOW_MS - now + 50; // 50ms buffer
16
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
17
+ }
18
+ requestTimestamps.push(Date.now());
19
+ }
20
+ export class FortnoxApiError extends Error {
21
+ statusCode;
22
+ fortnoxMessage;
23
+ details;
24
+ hint;
25
+ constructor(statusCode, fortnoxMessage, details, endpoint) {
26
+ const hint = getErrorHint(statusCode, fortnoxMessage, endpoint);
27
+ const parts = [`Fortnox API error (${statusCode}): ${fortnoxMessage}`];
28
+ if (hint)
29
+ parts.push(`Hint: ${hint}`);
30
+ super(parts.join('\n'));
31
+ this.statusCode = statusCode;
32
+ this.fortnoxMessage = fortnoxMessage;
33
+ this.details = details;
34
+ this.name = 'FortnoxApiError';
35
+ this.hint = hint;
36
+ }
37
+ }
38
+ function getErrorHint(statusCode, message, endpoint) {
39
+ switch (statusCode) {
40
+ case 401:
41
+ return 'Authentication failed. Try `noxctl init` to re-authenticate.';
42
+ case 403:
43
+ if (endpoint) {
44
+ const scope = endpointToScope(endpoint);
45
+ if (scope) {
46
+ return `Missing "${scope}" scope. Enable it in your Fortnox app at developer.fortnox.se, then re-run \`noxctl init\`.`;
47
+ }
48
+ }
49
+ return 'Forbidden. Check that your Fortnox app has the required scopes enabled at developer.fortnox.se.';
50
+ case 404:
51
+ return 'Resource not found. Verify the ID/number exists in Fortnox.';
52
+ case 429:
53
+ return 'Rate limited by Fortnox. The request will be retried automatically.';
54
+ default:
55
+ if (statusCode >= 500) {
56
+ return 'Fortnox server error. Try again in a moment.';
57
+ }
58
+ return undefined;
59
+ }
60
+ }
61
+ function endpointToScope(endpoint) {
62
+ const path = endpoint.split('?')[0].toLowerCase();
63
+ const mapping = {
64
+ articles: 'article',
65
+ customers: 'customer',
66
+ invoices: 'invoice',
67
+ suppliers: 'supplier',
68
+ supplierinvoices: 'supplierinvoice',
69
+ vouchers: 'bookkeeping',
70
+ accounts: 'bookkeeping',
71
+ companyinformation: 'companyinformation',
72
+ settings: 'settings',
73
+ };
74
+ for (const [prefix, scope] of Object.entries(mapping)) {
75
+ if (path.startsWith(prefix))
76
+ return scope;
77
+ }
78
+ return undefined;
79
+ }
80
+ async function retryWithBackoff(fn, retryable, maxRetries = 3) {
81
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
82
+ try {
83
+ return await fn();
84
+ }
85
+ catch (err) {
86
+ if (attempt === maxRetries)
87
+ throw err;
88
+ if (!retryable)
89
+ throw err;
90
+ // Only retry on transient errors (429, 500, 502, 503, 504)
91
+ if (err instanceof FortnoxApiError) {
92
+ const retryable = [429, 500, 502, 503, 504];
93
+ if (!retryable.includes(err.statusCode))
94
+ throw err;
95
+ }
96
+ else if (err instanceof Error &&
97
+ !err.message.includes('ECONNRESET') &&
98
+ !err.message.includes('ETIMEDOUT')) {
99
+ throw err;
100
+ }
101
+ const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
102
+ await new Promise((resolve) => setTimeout(resolve, delay));
103
+ }
104
+ }
105
+ throw new Error('Unreachable');
106
+ }
107
+ export async function fortnoxRequest(endpoint, options = {}) {
108
+ const method = (options.method || 'GET').toUpperCase();
109
+ const retryable = method === 'GET' || method === 'HEAD' || method === 'OPTIONS';
110
+ return retryWithBackoff(async () => {
111
+ await waitForRateLimit();
112
+ const token = await getValidToken();
113
+ const url = new URL(`${BASE_URL}/${endpoint}`);
114
+ if (options.params) {
115
+ for (const [key, value] of Object.entries(options.params)) {
116
+ if (value !== undefined) {
117
+ url.searchParams.set(key, String(value));
118
+ }
119
+ }
120
+ }
121
+ const headers = {
122
+ Authorization: `Bearer ${token}`,
123
+ 'Content-Type': 'application/json',
124
+ Accept: 'application/json',
125
+ };
126
+ const fetchOptions = {
127
+ method,
128
+ headers,
129
+ };
130
+ if (options.body) {
131
+ fetchOptions.body = JSON.stringify(options.body);
132
+ }
133
+ const response = await fetch(url.toString(), fetchOptions);
134
+ if (!response.ok) {
135
+ let errorMessage = `HTTP ${response.status}`;
136
+ let details;
137
+ try {
138
+ const errorBody = (await response.json());
139
+ if (errorBody?.ErrorInformation) {
140
+ errorMessage = errorBody.ErrorInformation.message || errorMessage;
141
+ details = `Error code: ${errorBody.ErrorInformation.code}`;
142
+ }
143
+ }
144
+ catch {
145
+ // ignore parse errors
146
+ }
147
+ throw new FortnoxApiError(response.status, errorMessage, details, endpoint);
148
+ }
149
+ // Some endpoints return empty responses (e.g., DELETE)
150
+ const text = await response.text();
151
+ if (!text)
152
+ return undefined;
153
+ return JSON.parse(text);
154
+ }, retryable);
155
+ }
156
+ /**
157
+ * Fetch all pages of a paginated Fortnox list endpoint.
158
+ * `dataKey` is the envelope key (e.g. "Invoices", "Customers").
159
+ */
160
+ export async function fetchAllPages(endpoint, dataKey, params = {}) {
161
+ const all = [];
162
+ let page = 1;
163
+ let totalPages = 1;
164
+ let totalResources = 0;
165
+ do {
166
+ const data = await fortnoxRequest(endpoint, {
167
+ params: { ...params, page, limit: 100 },
168
+ });
169
+ const items = data[dataKey] ?? [];
170
+ all.push(...items);
171
+ const meta = data.MetaInformation;
172
+ totalPages = meta?.['@TotalPages'] ?? 1;
173
+ totalResources = meta?.['@TotalResources'] ?? all.length;
174
+ page++;
175
+ } while (page <= totalPages);
176
+ return { items: all, totalResources };
177
+ }
178
+ //# sourceMappingURL=fortnox-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fortnox-client.js","sourceRoot":"","sources":["../src/fortnox-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function customerSegment(customerNumber: string): string;
2
+ export declare function documentSegment(documentNumber: string): string;
3
+ export declare function voucherSeriesSegment(series: string): string;
4
+ //# sourceMappingURL=identifiers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifiers.d.ts","sourceRoot":"","sources":["../src/identifiers.ts"],"names":[],"mappings":"AAOA,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE3D"}
@@ -0,0 +1,16 @@
1
+ function validateSegment(name, value, pattern) {
2
+ if (!pattern.test(value)) {
3
+ throw new Error(`Invalid ${name}: ${value}`);
4
+ }
5
+ return encodeURIComponent(value);
6
+ }
7
+ export function customerSegment(customerNumber) {
8
+ return validateSegment('customer number', customerNumber, /^[A-Za-z0-9][A-Za-z0-9_-]{0,49}$/);
9
+ }
10
+ export function documentSegment(documentNumber) {
11
+ return validateSegment('document number', documentNumber, /^\d+$/);
12
+ }
13
+ export function voucherSeriesSegment(series) {
14
+ return validateSegment('voucher series', series, /^[A-Za-z0-9][A-Za-z0-9_-]{0,9}$/);
15
+ }
16
+ //# sourceMappingURL=identifiers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifiers.js","sourceRoot":"","sources":["../src/identifiers.ts"],"names":[],"mappings":"AAAA,SAAS,eAAe,CAAC,IAAY,EAAE,KAAa,EAAE,OAAe;IACnE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,cAAsB;IACpD,OAAO,eAAe,CAAC,iBAAiB,EAAE,cAAc,EAAE,kCAAkC,CAAC,CAAC;AAChG,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,cAAsB;IACpD,OAAO,eAAe,CAAC,iBAAiB,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,OAAO,eAAe,CAAC,gBAAgB,EAAE,MAAM,EAAE,iCAAiC,CAAC,CAAC;AACtF,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function createServer(): McpServer;
3
+ export declare function startMcpServer(): Promise<void>;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAapE,wBAAgB,YAAY,IAAI,SAAS,CAkBxC;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAIpD"}
package/dist/index.js ADDED
@@ -0,0 +1,43 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { registerCustomerTools } from './tools/customers.js';
4
+ import { registerInvoiceTools } from './tools/invoices.js';
5
+ import { registerBookkeepingTools } from './tools/bookkeeping.js';
6
+ import { registerTaxTools } from './tools/tax.js';
7
+ import { registerCompanyTools } from './tools/company.js';
8
+ import { registerArticleTools } from './tools/articles.js';
9
+ import { registerSupplierTools } from './tools/suppliers.js';
10
+ import { registerSupplierInvoiceTools } from './tools/supplier-invoices.js';
11
+ import { registerFinancialReportTools } from './tools/financial-reports.js';
12
+ import { registerStatusTools } from './tools/status.js';
13
+ export function createServer() {
14
+ const server = new McpServer({
15
+ name: 'fortnox-mcp',
16
+ version: '0.1.0',
17
+ });
18
+ registerCustomerTools(server);
19
+ registerInvoiceTools(server);
20
+ registerBookkeepingTools(server);
21
+ registerTaxTools(server);
22
+ registerCompanyTools(server);
23
+ registerArticleTools(server);
24
+ registerSupplierTools(server);
25
+ registerSupplierInvoiceTools(server);
26
+ registerFinancialReportTools(server);
27
+ registerStatusTools(server);
28
+ return server;
29
+ }
30
+ export async function startMcpServer() {
31
+ const server = createServer();
32
+ const transport = new StdioServerTransport();
33
+ await server.connect(transport);
34
+ }
35
+ // Auto-start when run directly (backward compat: `node dist/index.js`)
36
+ const isDirectRun = import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith('/index.js');
37
+ if (isDirectRun) {
38
+ startMcpServer().catch((err) => {
39
+ console.error('Fatal error:', err);
40
+ process.exit(1);
41
+ });
42
+ }
43
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAC9B,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACjC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAC9B,4BAA4B,CAAC,MAAM,CAAC,CAAC;IACrC,4BAA4B,CAAC,MAAM,CAAC,CAAC;IACrC,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAE5B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,uEAAuE;AACvE,MAAM,WAAW,GACf,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;AAE5F,IAAI,WAAW,EAAE,CAAC;IAChB,cAAc,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QAC7B,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,18 @@
1
+ interface AccountsResponse {
2
+ Accounts: Record<string, unknown>[];
3
+ MetaInformation?: {
4
+ '@TotalResources': number;
5
+ '@TotalPages': number;
6
+ '@CurrentPage': number;
7
+ };
8
+ }
9
+ export interface ListAccountsParams {
10
+ financialYear?: number;
11
+ search?: string;
12
+ page?: number;
13
+ limit?: number;
14
+ all?: boolean;
15
+ }
16
+ export declare function listAccounts(params?: ListAccountsParams): Promise<AccountsResponse>;
17
+ export {};
18
+ //# sourceMappingURL=accounts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../src/operations/accounts.ts"],"names":[],"mappings":"AAEA,UAAU,gBAAgB;IACxB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACpC,eAAe,CAAC,EAAE;QAAE,iBAAiB,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;CAChG;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAsB,YAAY,CAAC,MAAM,GAAE,kBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAmC7F"}
@@ -0,0 +1,28 @@
1
+ import { fortnoxRequest, fetchAllPages } from '../fortnox-client.js';
2
+ export async function listAccounts(params = {}) {
3
+ const queryParams = {
4
+ financialyear: params.financialYear,
5
+ };
6
+ let data;
7
+ if (params.all) {
8
+ const { items, totalResources } = await fetchAllPages('accounts', 'Accounts', queryParams);
9
+ data = {
10
+ Accounts: items,
11
+ MetaInformation: { '@TotalResources': totalResources, '@TotalPages': 1, '@CurrentPage': 1 },
12
+ };
13
+ }
14
+ else {
15
+ data = await fortnoxRequest('accounts', {
16
+ params: { ...queryParams, page: params.page || 1, limit: params.limit || 100 },
17
+ });
18
+ }
19
+ if (params.search) {
20
+ const term = params.search.toLowerCase();
21
+ data.Accounts = data.Accounts.filter((a) => String(a.Number || '').includes(term) ||
22
+ String(a.Description || '')
23
+ .toLowerCase()
24
+ .includes(term));
25
+ }
26
+ return data;
27
+ }
28
+ //# sourceMappingURL=accounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/operations/accounts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAerE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,SAA6B,EAAE;IAChE,MAAM,WAAW,GAAgD;QAC/D,aAAa,EAAE,MAAM,CAAC,aAAa;KACpC,CAAC;IAEF,IAAI,IAAsB,CAAC;IAE3B,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;QACf,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,MAAM,aAAa,CACnD,UAAU,EACV,UAAU,EACV,WAAW,CACZ,CAAC;QACF,IAAI,GAAG;YACL,QAAQ,EAAE,KAAK;YACf,eAAe,EAAE,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE;SAC5F,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,MAAM,cAAc,CAAmB,UAAU,EAAE;YACxD,MAAM,EAAE,EAAE,GAAG,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,GAAG,EAAE;SAC/E,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACzC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAClC,CAAC,CAAC,EAAE,EAAE,CACJ,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;YACrC,MAAM,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC;iBACxB,WAAW,EAAE;iBACb,QAAQ,CAAC,IAAI,CAAC,CACpB,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,20 @@
1
+ interface ArticlesResponse {
2
+ Articles: Record<string, unknown>[];
3
+ MetaInformation?: {
4
+ '@TotalResources': number;
5
+ '@TotalPages': number;
6
+ '@CurrentPage': number;
7
+ };
8
+ }
9
+ export interface ListArticlesParams {
10
+ search?: string;
11
+ page?: number;
12
+ limit?: number;
13
+ all?: boolean;
14
+ }
15
+ export declare function listArticles(params?: ListArticlesParams): Promise<ArticlesResponse>;
16
+ export declare function getArticle(articleNumber: string): Promise<Record<string, unknown>>;
17
+ export declare function createArticle(params: Record<string, unknown>): Promise<Record<string, unknown>>;
18
+ export declare function updateArticle(articleNumber: string, fields: Record<string, unknown>): Promise<Record<string, unknown>>;
19
+ export {};
20
+ //# sourceMappingURL=articles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"articles.d.ts","sourceRoot":"","sources":["../../src/operations/articles.ts"],"names":[],"mappings":"AAMA,UAAU,gBAAgB;IACxB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACpC,eAAe,CAAC,EAAE;QAAE,iBAAiB,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;CAChG;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAsB,YAAY,CAAC,MAAM,GAAE,kBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAoB7F;AAED,wBAAsB,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAKxF;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAMlC;AAED,wBAAsB,aAAa,CACjC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAUlC"}
@@ -0,0 +1,36 @@
1
+ import { fortnoxRequest, fetchAllPages } from '../fortnox-client.js';
2
+ export async function listArticles(params = {}) {
3
+ const queryParams = {
4
+ ...(params.search ? { description: params.search } : {}),
5
+ };
6
+ if (params.all) {
7
+ const { items, totalResources } = await fetchAllPages('articles', 'Articles', queryParams);
8
+ return {
9
+ Articles: items,
10
+ MetaInformation: { '@TotalResources': totalResources, '@TotalPages': 1, '@CurrentPage': 1 },
11
+ };
12
+ }
13
+ return fortnoxRequest('articles', {
14
+ params: { ...queryParams, page: params.page || 1, limit: params.limit || 100 },
15
+ });
16
+ }
17
+ export async function getArticle(articleNumber) {
18
+ const data = await fortnoxRequest(`articles/${encodeURIComponent(articleNumber)}`);
19
+ return data.Article;
20
+ }
21
+ export async function createArticle(params) {
22
+ const data = await fortnoxRequest('articles', {
23
+ method: 'POST',
24
+ body: { Article: params },
25
+ });
26
+ return data.Article;
27
+ }
28
+ export async function updateArticle(articleNumber, fields) {
29
+ const { ArticleNumber: _, ...body } = fields;
30
+ const data = await fortnoxRequest(`articles/${encodeURIComponent(articleNumber)}`, {
31
+ method: 'PUT',
32
+ body: { Article: body },
33
+ });
34
+ return data.Article;
35
+ }
36
+ //# sourceMappingURL=articles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"articles.js","sourceRoot":"","sources":["../../src/operations/articles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAkBrE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,SAA6B,EAAE;IAChE,MAAM,WAAW,GAAgD;QAC/D,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzD,CAAC;IAEF,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;QACf,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,MAAM,aAAa,CACnD,UAAU,EACV,UAAU,EACV,WAAW,CACZ,CAAC;QACF,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,eAAe,EAAE,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE;SAC5F,CAAC;IACJ,CAAC;IAED,OAAO,cAAc,CAAmB,UAAU,EAAE;QAClD,MAAM,EAAE,EAAE,GAAG,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,GAAG,EAAE;KAC/E,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,aAAqB;IACpD,MAAM,IAAI,GAAG,MAAM,cAAc,CAC/B,YAAY,kBAAkB,CAAC,aAAa,CAAC,EAAE,CAChD,CAAC;IACF,OAAO,IAAI,CAAC,OAAO,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA+B;IAE/B,MAAM,IAAI,GAAG,MAAM,cAAc,CAAkB,UAAU,EAAE;QAC7D,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;KAC1B,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,OAAO,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,aAAqB,EACrB,MAA+B;IAE/B,MAAM,EAAE,aAAa,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IAC7C,MAAM,IAAI,GAAG,MAAM,cAAc,CAC/B,YAAY,kBAAkB,CAAC,aAAa,CAAC,EAAE,EAC/C;QACE,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;KACxB,CACF,CAAC;IACF,OAAO,IAAI,CAAC,OAAO,CAAC;AACtB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function getCompanyInfo(): Promise<Record<string, unknown>>;
2
+ //# sourceMappingURL=company.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"company.d.ts","sourceRoot":"","sources":["../../src/operations/company.ts"],"names":[],"mappings":"AAMA,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAGvE"}
@@ -0,0 +1,6 @@
1
+ import { fortnoxRequest } from '../fortnox-client.js';
2
+ export async function getCompanyInfo() {
3
+ const data = await fortnoxRequest('companyinformation');
4
+ return data.CompanyInformation;
5
+ }
6
+ //# sourceMappingURL=company.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"company.js","sourceRoot":"","sources":["../../src/operations/company.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAMtD,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,IAAI,GAAG,MAAM,cAAc,CAAsB,oBAAoB,CAAC,CAAC;IAC7E,OAAO,IAAI,CAAC,kBAAkB,CAAC;AACjC,CAAC"}
@@ -0,0 +1,20 @@
1
+ interface CustomersResponse {
2
+ Customers: Record<string, unknown>[];
3
+ MetaInformation?: {
4
+ '@TotalResources': number;
5
+ '@TotalPages': number;
6
+ '@CurrentPage': number;
7
+ };
8
+ }
9
+ export interface ListCustomersParams {
10
+ search?: string;
11
+ page?: number;
12
+ limit?: number;
13
+ all?: boolean;
14
+ }
15
+ export declare function listCustomers(params?: ListCustomersParams): Promise<CustomersResponse>;
16
+ export declare function getCustomer(customerNumber: string): Promise<Record<string, unknown>>;
17
+ export declare function createCustomer(params: Record<string, unknown>): Promise<Record<string, unknown>>;
18
+ export declare function updateCustomer(customerNumber: string, fields: Record<string, unknown>): Promise<Record<string, unknown>>;
19
+ export {};
20
+ //# sourceMappingURL=customers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customers.d.ts","sourceRoot":"","sources":["../../src/operations/customers.ts"],"names":[],"mappings":"AAOA,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACrC,eAAe,CAAC,EAAE;QAAE,iBAAiB,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;CAChG;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAsB,aAAa,CAAC,MAAM,GAAE,mBAAwB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAoBhG;AAED,wBAAsB,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAK1F;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAMlC;AAED,wBAAsB,cAAc,CAClC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAUlC"}
@@ -0,0 +1,37 @@
1
+ import { fortnoxRequest, fetchAllPages } from '../fortnox-client.js';
2
+ import { customerSegment } from '../identifiers.js';
3
+ export async function listCustomers(params = {}) {
4
+ const queryParams = {
5
+ ...(params.search ? { name: params.search } : {}),
6
+ };
7
+ if (params.all) {
8
+ const { items, totalResources } = await fetchAllPages('customers', 'Customers', queryParams);
9
+ return {
10
+ Customers: items,
11
+ MetaInformation: { '@TotalResources': totalResources, '@TotalPages': 1, '@CurrentPage': 1 },
12
+ };
13
+ }
14
+ return fortnoxRequest('customers', {
15
+ params: { ...queryParams, page: params.page || 1, limit: params.limit || 100 },
16
+ });
17
+ }
18
+ export async function getCustomer(customerNumber) {
19
+ const data = await fortnoxRequest(`customers/${customerSegment(customerNumber)}`);
20
+ return data.Customer;
21
+ }
22
+ export async function createCustomer(params) {
23
+ const data = await fortnoxRequest('customers', {
24
+ method: 'POST',
25
+ body: { Customer: params },
26
+ });
27
+ return data.Customer;
28
+ }
29
+ export async function updateCustomer(customerNumber, fields) {
30
+ const { customerNumber: _, ...body } = fields;
31
+ const data = await fortnoxRequest(`customers/${customerSegment(customerNumber)}`, {
32
+ method: 'PUT',
33
+ body: { Customer: body },
34
+ });
35
+ return data.Customer;
36
+ }
37
+ //# sourceMappingURL=customers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customers.js","sourceRoot":"","sources":["../../src/operations/customers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAkBpD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAA8B,EAAE;IAClE,MAAM,WAAW,GAAgD;QAC/D,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClD,CAAC;IAEF,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;QACf,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,MAAM,aAAa,CACnD,WAAW,EACX,WAAW,EACX,WAAW,CACZ,CAAC;QACF,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,eAAe,EAAE,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE;SAC5F,CAAC;IACJ,CAAC;IAED,OAAO,cAAc,CAAoB,WAAW,EAAE;QACpD,MAAM,EAAE,EAAE,GAAG,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,GAAG,EAAE;KAC/E,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,cAAsB;IACtD,MAAM,IAAI,GAAG,MAAM,cAAc,CAC/B,aAAa,eAAe,CAAC,cAAc,CAAC,EAAE,CAC/C,CAAC;IACF,OAAO,IAAI,CAAC,QAAQ,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAA+B;IAE/B,MAAM,IAAI,GAAG,MAAM,cAAc,CAAmB,WAAW,EAAE;QAC/D,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;KAC3B,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,QAAQ,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,cAAsB,EACtB,MAA+B;IAE/B,MAAM,EAAE,cAAc,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IAC9C,MAAM,IAAI,GAAG,MAAM,cAAc,CAC/B,aAAa,eAAe,CAAC,cAAc,CAAC,EAAE,EAC9C;QACE,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;KACzB,CACF,CAAC;IACF,OAAO,IAAI,CAAC,QAAQ,CAAC;AACvB,CAAC"}
@@ -0,0 +1,37 @@
1
+ export interface FinancialReportParams {
2
+ financialYear?: number;
3
+ fromDate?: string;
4
+ toDate?: string;
5
+ }
6
+ export interface ReportLine {
7
+ account: number;
8
+ description: string;
9
+ balance: number;
10
+ }
11
+ export interface ReportSection {
12
+ label: string;
13
+ lines: ReportLine[];
14
+ total: number;
15
+ }
16
+ export interface IncomeStatement {
17
+ type: 'income-statement';
18
+ financialYear?: number;
19
+ period?: {
20
+ from: string;
21
+ to: string;
22
+ };
23
+ sections: ReportSection[];
24
+ netResult: number;
25
+ }
26
+ export interface BalanceSheet {
27
+ type: 'balance-sheet';
28
+ financialYear?: number;
29
+ asOfDate?: string;
30
+ assets: ReportSection[];
31
+ totalAssets: number;
32
+ liabilitiesAndEquity: ReportSection[];
33
+ totalLiabilitiesAndEquity: number;
34
+ }
35
+ export declare function getIncomeStatement(params?: FinancialReportParams): Promise<IncomeStatement>;
36
+ export declare function getBalanceSheet(params?: FinancialReportParams): Promise<BalanceSheet>;
37
+ //# sourceMappingURL=financial-reports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"financial-reports.d.ts","sourceRoot":"","sources":["../../src/operations/financial-reports.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,qBAAqB;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,kBAAkB,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,eAAe,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,aAAa,EAAE,CAAC;IACtC,yBAAyB,EAAE,MAAM,CAAC;CACnC;AAkMD,wBAAsB,kBAAkB,CACtC,MAAM,GAAE,qBAA0B,GACjC,OAAO,CAAC,eAAe,CAAC,CAqB1B;AAED,wBAAsB,eAAe,CAAC,MAAM,GAAE,qBAA0B,GAAG,OAAO,CAAC,YAAY,CAAC,CAwB/F"}