factuplan 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.
package/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # Factuplan
2
+
3
+ SDK oficial de [Factuplan](https://tufactura.com) para JavaScript y TypeScript. Emite facturas electronicas en Ecuador (SRI) desde tu aplicacion.
4
+
5
+ ## Instalacion
6
+
7
+ ```bash
8
+ npm install factuplan
9
+ ```
10
+
11
+ ## Inicio rapido
12
+
13
+ ```typescript
14
+ import { Factuplan } from 'factuplan';
15
+
16
+ const factuplan = new Factuplan('tu-api-key');
17
+
18
+ // Crear una factura
19
+ const invoice = await factuplan.invoices.create({
20
+ emissionPointId: 'ep_...',
21
+ customer: {
22
+ identificationType: 'RUC',
23
+ identification: '1790016919001',
24
+ legalName: 'Empresa ABC S.A.',
25
+ email: 'contabilidad@empresa.com',
26
+ },
27
+ items: [
28
+ {
29
+ code: 'SERV-001',
30
+ description: 'Servicio de consultoria',
31
+ quantity: 1,
32
+ unitPrice: 100.00,
33
+ taxType: 'IVA_RATE',
34
+ },
35
+ ],
36
+ });
37
+
38
+ console.log(invoice.accessKey); // Clave de acceso SRI
39
+ ```
40
+
41
+ ## Recursos
42
+
43
+ ### Clientes
44
+
45
+ ```typescript
46
+ // Crear cliente
47
+ const customer = await factuplan.customers.create({
48
+ identificationType: 'RUC',
49
+ identification: '1790016919001',
50
+ legalName: 'Empresa ABC S.A.',
51
+ email: 'info@empresa.com',
52
+ });
53
+
54
+ // Listar clientes (paginado)
55
+ const { data, meta } = await factuplan.customers.list({ page: 1, limit: 20 });
56
+
57
+ // Obtener cliente por ID
58
+ const customer = await factuplan.customers.get('customer-id');
59
+
60
+ // Actualizar cliente
61
+ await factuplan.customers.update('customer-id', { phone: '0991234567' });
62
+
63
+ // Eliminar cliente
64
+ await factuplan.customers.delete('customer-id');
65
+ ```
66
+
67
+ ### Productos
68
+
69
+ ```typescript
70
+ // Crear producto
71
+ const product = await factuplan.products.create({
72
+ code: 'PROD-001',
73
+ name: 'Laptop Dell XPS',
74
+ unitPrice: 1200.00,
75
+ taxType: 'IVA_RATE',
76
+ type: 'PRODUCT',
77
+ });
78
+
79
+ // Listar productos (paginado)
80
+ const { data, meta } = await factuplan.products.list({ page: 1, limit: 20 });
81
+
82
+ // Obtener producto por ID
83
+ const product = await factuplan.products.get('product-id');
84
+
85
+ // Actualizar producto
86
+ await factuplan.products.update('product-id', { unitPrice: 1150.00 });
87
+
88
+ // Eliminar producto
89
+ await factuplan.products.delete('product-id');
90
+ ```
91
+
92
+ ### Facturas
93
+
94
+ ```typescript
95
+ // Crear factura
96
+ const invoice = await factuplan.invoices.create({
97
+ emissionPointId: 'ep_...',
98
+ customer: {
99
+ identificationType: 'CEDULA',
100
+ identification: '1712345678',
101
+ legalName: 'Juan Perez',
102
+ email: 'juan@email.com',
103
+ saveToContacts: true,
104
+ },
105
+ items: [
106
+ {
107
+ code: 'SERV-001',
108
+ description: 'Desarrollo web',
109
+ quantity: 10,
110
+ unitPrice: 50.00,
111
+ discount: 25.00,
112
+ taxType: 'IVA_RATE',
113
+ },
114
+ ],
115
+ paymentMethod: 'TRANSFER',
116
+ additionalInfo: {
117
+ 'Orden de compra': 'OC-2024-001',
118
+ },
119
+ });
120
+
121
+ // Consultar estado
122
+ const status = await factuplan.invoices.getStatus('invoice-id');
123
+ console.log(status.status); // 'AUTHORIZED', 'PENDING', etc.
124
+
125
+ // Descargar XML autorizado
126
+ const { url } = await factuplan.invoices.downloadXml('invoice-id');
127
+
128
+ // Descargar PDF (RIDE)
129
+ const { url } = await factuplan.invoices.downloadPdf('invoice-id');
130
+ ```
131
+
132
+ ### Uso y certificado
133
+
134
+ ```typescript
135
+ // Consultar uso de la API
136
+ const usage = await factuplan.usage();
137
+ console.log(`${usage.used}/${usage.quota} facturas este mes`);
138
+
139
+ // Estado del certificado de firma
140
+ const cert = await factuplan.certificateStatus();
141
+ console.log(`Expira en ${cert.daysUntilExpiry} dias`);
142
+ ```
143
+
144
+ ## Configuracion
145
+
146
+ ```typescript
147
+ const factuplan = new Factuplan('tu-api-key', {
148
+ baseUrl: 'https://api.factuplan.com/api/v1', // default
149
+ timeout: 30000, // 30s default
150
+ });
151
+ ```
152
+
153
+ ## Manejo de errores
154
+
155
+ ```typescript
156
+ import { Factuplan, FactuplanError, AuthenticationError, RateLimitError } from 'factuplan';
157
+
158
+ try {
159
+ await factuplan.invoices.create({ /* ... */ });
160
+ } catch (error) {
161
+ if (error instanceof AuthenticationError) {
162
+ // API key invalida (401)
163
+ } else if (error instanceof RateLimitError) {
164
+ // Limite de peticiones excedido (429)
165
+ } else if (error instanceof FactuplanError) {
166
+ console.error(error.message); // Mensaje de error
167
+ console.error(error.statusCode); // Codigo HTTP
168
+ console.error(error.code); // Codigo de error interno
169
+ console.error(error.details); // Detalles adicionales
170
+ }
171
+ }
172
+ ```
173
+
174
+ ## Tipos de identificacion
175
+
176
+ | Tipo | Descripcion |
177
+ |------|-------------|
178
+ | `RUC` | Registro Unico de Contribuyentes (13 digitos) |
179
+ | `CEDULA` | Cedula de identidad (10 digitos) |
180
+ | `PASSPORT` | Pasaporte |
181
+ | `FINAL_CONSUMER` | Consumidor final (9999999999999) |
182
+
183
+ ## Tipos de impuesto
184
+
185
+ | Tipo | Descripcion |
186
+ |------|-------------|
187
+ | `IVA_RATE` | IVA 15% (tarifa vigente) |
188
+ | `IVA_0` | IVA 0% |
189
+ | `NOT_TAXABLE` | No objeto de impuesto |
190
+ | `EXEMPT` | Exento de IVA |
191
+
192
+ ## Requisitos
193
+
194
+ - Node.js 18+ (usa `fetch` nativo)
195
+ - API key de [Factuplan](https://tufactura.com)
196
+
197
+ ## Licencia
198
+
199
+ MIT
@@ -0,0 +1,218 @@
1
+ interface PaginatedResponse<T> {
2
+ data: T[];
3
+ meta: {
4
+ total: number;
5
+ page: number;
6
+ limit: number;
7
+ totalPages: number;
8
+ };
9
+ }
10
+ interface Customer {
11
+ id: string;
12
+ identificationType: 'RUC' | 'CEDULA' | 'PASSPORT' | 'FINAL_CONSUMER';
13
+ identification: string;
14
+ legalName: string;
15
+ email?: string;
16
+ additionalEmail?: string;
17
+ phone?: string;
18
+ address?: string;
19
+ createdAt: string;
20
+ updatedAt: string;
21
+ }
22
+ interface CreateCustomerInput {
23
+ identificationType: 'RUC' | 'CEDULA' | 'PASSPORT' | 'FINAL_CONSUMER';
24
+ identification: string;
25
+ legalName: string;
26
+ email?: string;
27
+ additionalEmail?: string;
28
+ phone?: string;
29
+ address?: string;
30
+ }
31
+ interface UpdateCustomerInput {
32
+ legalName?: string;
33
+ email?: string;
34
+ additionalEmail?: string;
35
+ phone?: string;
36
+ address?: string;
37
+ }
38
+ interface CustomerListParams {
39
+ page?: number;
40
+ limit?: number;
41
+ search?: string;
42
+ }
43
+ interface Product {
44
+ id: string;
45
+ code: string;
46
+ auxiliaryCode?: string;
47
+ type: 'PRODUCT' | 'SERVICE';
48
+ name: string;
49
+ description?: string;
50
+ unitPrice: number | string;
51
+ taxType: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
52
+ iceCode?: string;
53
+ iceRate?: number | string;
54
+ irbpnr: boolean;
55
+ unitOfMeasure?: string;
56
+ isActive: boolean;
57
+ createdAt: string;
58
+ updatedAt: string;
59
+ }
60
+ interface CreateProductInput {
61
+ code: string;
62
+ auxiliaryCode?: string;
63
+ type?: 'PRODUCT' | 'SERVICE';
64
+ name: string;
65
+ description?: string;
66
+ unitPrice: number;
67
+ taxType?: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
68
+ iceCode?: string;
69
+ iceRate?: number;
70
+ irbpnr?: boolean;
71
+ unitOfMeasure?: string;
72
+ }
73
+ interface UpdateProductInput {
74
+ name?: string;
75
+ description?: string;
76
+ unitPrice?: number;
77
+ taxType?: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
78
+ type?: 'PRODUCT' | 'SERVICE';
79
+ unitOfMeasure?: string;
80
+ isActive?: boolean;
81
+ }
82
+ interface ProductListParams {
83
+ page?: number;
84
+ limit?: number;
85
+ search?: string;
86
+ }
87
+ interface InvoiceCustomer {
88
+ identificationType: 'RUC' | 'CEDULA' | 'PASSPORT' | 'FINAL_CONSUMER';
89
+ identification: string;
90
+ legalName: string;
91
+ email?: string;
92
+ address?: string;
93
+ phone?: string;
94
+ saveToContacts?: boolean;
95
+ }
96
+ interface InvoiceItem {
97
+ code: string;
98
+ description: string;
99
+ quantity: number;
100
+ unitPrice: number;
101
+ discount?: number;
102
+ taxType?: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
103
+ /** Tax rate override for IVA_RATE. Valid: 0, 5, 8, 12, 14, 15. Default: 15 */
104
+ tax?: number;
105
+ }
106
+ interface CreateInvoiceInput {
107
+ /** UUID of the emission point. Optional if using establishment + emissionPoint codes, or if the taxpayer has a single active emission point. */
108
+ emissionPointId?: string;
109
+ /** SRI establishment code (e.g. "001"). Must be used together with emissionPoint. */
110
+ establishment?: string;
111
+ /** SRI emission point code (e.g. "001"). Must be used together with establishment. */
112
+ emissionPoint?: string;
113
+ customer: InvoiceCustomer;
114
+ items: InvoiceItem[];
115
+ paymentMethod?: string;
116
+ additionalInfo?: Record<string, string>;
117
+ }
118
+ interface Invoice {
119
+ id: string;
120
+ accessKey: string;
121
+ sequential: string;
122
+ status: string;
123
+ total: number | string;
124
+ }
125
+ interface InvoiceStatus {
126
+ id: string;
127
+ status: string;
128
+ accessKey?: string;
129
+ authorizationNumber?: string;
130
+ authorizationDate?: string;
131
+ }
132
+ interface DownloadUrlResponse {
133
+ url: string;
134
+ s3Key?: string;
135
+ }
136
+ interface UsageResponse {
137
+ apiKeyId: string;
138
+ month: string;
139
+ quota: number;
140
+ used: number;
141
+ remaining: number;
142
+ costs: Record<string, unknown>;
143
+ }
144
+ interface CertificateStatus {
145
+ hasCertificate: boolean;
146
+ isExpired?: boolean;
147
+ daysUntilExpiry?: number | null;
148
+ ruc?: string;
149
+ legalName?: string;
150
+ expiresAt?: string;
151
+ }
152
+ interface FactuplanOptions {
153
+ /** Base URL of the API (default: https://api.factuplan.com/api/v1) */
154
+ baseUrl?: string;
155
+ /** Request timeout in milliseconds (default: 30000) */
156
+ timeout?: number;
157
+ }
158
+
159
+ type Requester$2 = (method: string, path: string, body?: unknown, params?: Record<string, string | number | undefined>) => Promise<unknown>;
160
+ declare class CustomersResource {
161
+ private request;
162
+ constructor(request: Requester$2);
163
+ create(input: CreateCustomerInput): Promise<Customer>;
164
+ list(params?: CustomerListParams): Promise<PaginatedResponse<Customer>>;
165
+ get(id: string): Promise<Customer>;
166
+ update(id: string, input: UpdateCustomerInput): Promise<Customer>;
167
+ delete(id: string): Promise<void>;
168
+ }
169
+
170
+ type Requester$1 = (method: string, path: string, body?: unknown, params?: Record<string, string | number | undefined>) => Promise<unknown>;
171
+ declare class ProductsResource {
172
+ private request;
173
+ constructor(request: Requester$1);
174
+ create(input: CreateProductInput): Promise<Product>;
175
+ list(params?: ProductListParams): Promise<PaginatedResponse<Product>>;
176
+ get(id: string): Promise<Product>;
177
+ update(id: string, input: UpdateProductInput): Promise<Product>;
178
+ delete(id: string): Promise<void>;
179
+ }
180
+
181
+ type Requester = (method: string, path: string, body?: unknown) => Promise<unknown>;
182
+ declare class InvoicesResource {
183
+ private request;
184
+ constructor(request: Requester);
185
+ create(input: CreateInvoiceInput): Promise<Invoice>;
186
+ get(id: string): Promise<Record<string, unknown>>;
187
+ getStatus(id: string): Promise<InvoiceStatus>;
188
+ downloadXml(id: string): Promise<DownloadUrlResponse>;
189
+ downloadPdf(id: string): Promise<DownloadUrlResponse>;
190
+ }
191
+
192
+ declare class Factuplan {
193
+ private readonly apiKey;
194
+ private readonly baseUrl;
195
+ private readonly timeout;
196
+ readonly customers: CustomersResource;
197
+ readonly products: ProductsResource;
198
+ readonly invoices: InvoicesResource;
199
+ constructor(apiKey: string, options?: FactuplanOptions);
200
+ usage(): Promise<UsageResponse>;
201
+ certificateStatus(): Promise<CertificateStatus>;
202
+ private request;
203
+ }
204
+
205
+ declare class FactuplanError extends Error {
206
+ readonly statusCode: number;
207
+ readonly code: string;
208
+ readonly details?: Record<string, unknown>;
209
+ constructor(message: string, statusCode: number, code: string, details?: Record<string, unknown>);
210
+ }
211
+ declare class AuthenticationError extends FactuplanError {
212
+ constructor(message?: string);
213
+ }
214
+ declare class RateLimitError extends FactuplanError {
215
+ constructor(message?: string);
216
+ }
217
+
218
+ export { AuthenticationError, type CertificateStatus, type CreateCustomerInput, type CreateInvoiceInput, type CreateProductInput, type Customer, type CustomerListParams, type DownloadUrlResponse, Factuplan, FactuplanError, type FactuplanOptions, type Invoice, type InvoiceCustomer, type InvoiceItem, type InvoiceStatus, type PaginatedResponse, type Product, type ProductListParams, RateLimitError, type UpdateCustomerInput, type UpdateProductInput, type UsageResponse };
@@ -0,0 +1,218 @@
1
+ interface PaginatedResponse<T> {
2
+ data: T[];
3
+ meta: {
4
+ total: number;
5
+ page: number;
6
+ limit: number;
7
+ totalPages: number;
8
+ };
9
+ }
10
+ interface Customer {
11
+ id: string;
12
+ identificationType: 'RUC' | 'CEDULA' | 'PASSPORT' | 'FINAL_CONSUMER';
13
+ identification: string;
14
+ legalName: string;
15
+ email?: string;
16
+ additionalEmail?: string;
17
+ phone?: string;
18
+ address?: string;
19
+ createdAt: string;
20
+ updatedAt: string;
21
+ }
22
+ interface CreateCustomerInput {
23
+ identificationType: 'RUC' | 'CEDULA' | 'PASSPORT' | 'FINAL_CONSUMER';
24
+ identification: string;
25
+ legalName: string;
26
+ email?: string;
27
+ additionalEmail?: string;
28
+ phone?: string;
29
+ address?: string;
30
+ }
31
+ interface UpdateCustomerInput {
32
+ legalName?: string;
33
+ email?: string;
34
+ additionalEmail?: string;
35
+ phone?: string;
36
+ address?: string;
37
+ }
38
+ interface CustomerListParams {
39
+ page?: number;
40
+ limit?: number;
41
+ search?: string;
42
+ }
43
+ interface Product {
44
+ id: string;
45
+ code: string;
46
+ auxiliaryCode?: string;
47
+ type: 'PRODUCT' | 'SERVICE';
48
+ name: string;
49
+ description?: string;
50
+ unitPrice: number | string;
51
+ taxType: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
52
+ iceCode?: string;
53
+ iceRate?: number | string;
54
+ irbpnr: boolean;
55
+ unitOfMeasure?: string;
56
+ isActive: boolean;
57
+ createdAt: string;
58
+ updatedAt: string;
59
+ }
60
+ interface CreateProductInput {
61
+ code: string;
62
+ auxiliaryCode?: string;
63
+ type?: 'PRODUCT' | 'SERVICE';
64
+ name: string;
65
+ description?: string;
66
+ unitPrice: number;
67
+ taxType?: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
68
+ iceCode?: string;
69
+ iceRate?: number;
70
+ irbpnr?: boolean;
71
+ unitOfMeasure?: string;
72
+ }
73
+ interface UpdateProductInput {
74
+ name?: string;
75
+ description?: string;
76
+ unitPrice?: number;
77
+ taxType?: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
78
+ type?: 'PRODUCT' | 'SERVICE';
79
+ unitOfMeasure?: string;
80
+ isActive?: boolean;
81
+ }
82
+ interface ProductListParams {
83
+ page?: number;
84
+ limit?: number;
85
+ search?: string;
86
+ }
87
+ interface InvoiceCustomer {
88
+ identificationType: 'RUC' | 'CEDULA' | 'PASSPORT' | 'FINAL_CONSUMER';
89
+ identification: string;
90
+ legalName: string;
91
+ email?: string;
92
+ address?: string;
93
+ phone?: string;
94
+ saveToContacts?: boolean;
95
+ }
96
+ interface InvoiceItem {
97
+ code: string;
98
+ description: string;
99
+ quantity: number;
100
+ unitPrice: number;
101
+ discount?: number;
102
+ taxType?: 'IVA_0' | 'IVA_RATE' | 'NOT_TAXABLE' | 'EXEMPT';
103
+ /** Tax rate override for IVA_RATE. Valid: 0, 5, 8, 12, 14, 15. Default: 15 */
104
+ tax?: number;
105
+ }
106
+ interface CreateInvoiceInput {
107
+ /** UUID of the emission point. Optional if using establishment + emissionPoint codes, or if the taxpayer has a single active emission point. */
108
+ emissionPointId?: string;
109
+ /** SRI establishment code (e.g. "001"). Must be used together with emissionPoint. */
110
+ establishment?: string;
111
+ /** SRI emission point code (e.g. "001"). Must be used together with establishment. */
112
+ emissionPoint?: string;
113
+ customer: InvoiceCustomer;
114
+ items: InvoiceItem[];
115
+ paymentMethod?: string;
116
+ additionalInfo?: Record<string, string>;
117
+ }
118
+ interface Invoice {
119
+ id: string;
120
+ accessKey: string;
121
+ sequential: string;
122
+ status: string;
123
+ total: number | string;
124
+ }
125
+ interface InvoiceStatus {
126
+ id: string;
127
+ status: string;
128
+ accessKey?: string;
129
+ authorizationNumber?: string;
130
+ authorizationDate?: string;
131
+ }
132
+ interface DownloadUrlResponse {
133
+ url: string;
134
+ s3Key?: string;
135
+ }
136
+ interface UsageResponse {
137
+ apiKeyId: string;
138
+ month: string;
139
+ quota: number;
140
+ used: number;
141
+ remaining: number;
142
+ costs: Record<string, unknown>;
143
+ }
144
+ interface CertificateStatus {
145
+ hasCertificate: boolean;
146
+ isExpired?: boolean;
147
+ daysUntilExpiry?: number | null;
148
+ ruc?: string;
149
+ legalName?: string;
150
+ expiresAt?: string;
151
+ }
152
+ interface FactuplanOptions {
153
+ /** Base URL of the API (default: https://api.factuplan.com/api/v1) */
154
+ baseUrl?: string;
155
+ /** Request timeout in milliseconds (default: 30000) */
156
+ timeout?: number;
157
+ }
158
+
159
+ type Requester$2 = (method: string, path: string, body?: unknown, params?: Record<string, string | number | undefined>) => Promise<unknown>;
160
+ declare class CustomersResource {
161
+ private request;
162
+ constructor(request: Requester$2);
163
+ create(input: CreateCustomerInput): Promise<Customer>;
164
+ list(params?: CustomerListParams): Promise<PaginatedResponse<Customer>>;
165
+ get(id: string): Promise<Customer>;
166
+ update(id: string, input: UpdateCustomerInput): Promise<Customer>;
167
+ delete(id: string): Promise<void>;
168
+ }
169
+
170
+ type Requester$1 = (method: string, path: string, body?: unknown, params?: Record<string, string | number | undefined>) => Promise<unknown>;
171
+ declare class ProductsResource {
172
+ private request;
173
+ constructor(request: Requester$1);
174
+ create(input: CreateProductInput): Promise<Product>;
175
+ list(params?: ProductListParams): Promise<PaginatedResponse<Product>>;
176
+ get(id: string): Promise<Product>;
177
+ update(id: string, input: UpdateProductInput): Promise<Product>;
178
+ delete(id: string): Promise<void>;
179
+ }
180
+
181
+ type Requester = (method: string, path: string, body?: unknown) => Promise<unknown>;
182
+ declare class InvoicesResource {
183
+ private request;
184
+ constructor(request: Requester);
185
+ create(input: CreateInvoiceInput): Promise<Invoice>;
186
+ get(id: string): Promise<Record<string, unknown>>;
187
+ getStatus(id: string): Promise<InvoiceStatus>;
188
+ downloadXml(id: string): Promise<DownloadUrlResponse>;
189
+ downloadPdf(id: string): Promise<DownloadUrlResponse>;
190
+ }
191
+
192
+ declare class Factuplan {
193
+ private readonly apiKey;
194
+ private readonly baseUrl;
195
+ private readonly timeout;
196
+ readonly customers: CustomersResource;
197
+ readonly products: ProductsResource;
198
+ readonly invoices: InvoicesResource;
199
+ constructor(apiKey: string, options?: FactuplanOptions);
200
+ usage(): Promise<UsageResponse>;
201
+ certificateStatus(): Promise<CertificateStatus>;
202
+ private request;
203
+ }
204
+
205
+ declare class FactuplanError extends Error {
206
+ readonly statusCode: number;
207
+ readonly code: string;
208
+ readonly details?: Record<string, unknown>;
209
+ constructor(message: string, statusCode: number, code: string, details?: Record<string, unknown>);
210
+ }
211
+ declare class AuthenticationError extends FactuplanError {
212
+ constructor(message?: string);
213
+ }
214
+ declare class RateLimitError extends FactuplanError {
215
+ constructor(message?: string);
216
+ }
217
+
218
+ export { AuthenticationError, type CertificateStatus, type CreateCustomerInput, type CreateInvoiceInput, type CreateProductInput, type Customer, type CustomerListParams, type DownloadUrlResponse, Factuplan, FactuplanError, type FactuplanOptions, type Invoice, type InvoiceCustomer, type InvoiceItem, type InvoiceStatus, type PaginatedResponse, type Product, type ProductListParams, RateLimitError, type UpdateCustomerInput, type UpdateProductInput, type UsageResponse };
package/dist/index.js ADDED
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AuthenticationError: () => AuthenticationError,
24
+ Factuplan: () => Factuplan,
25
+ FactuplanError: () => FactuplanError,
26
+ RateLimitError: () => RateLimitError
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+
30
+ // src/errors.ts
31
+ var FactuplanError = class extends Error {
32
+ constructor(message, statusCode, code, details) {
33
+ super(message);
34
+ this.name = "FactuplanError";
35
+ this.statusCode = statusCode;
36
+ this.code = code;
37
+ this.details = details;
38
+ }
39
+ };
40
+ var AuthenticationError = class extends FactuplanError {
41
+ constructor(message = "Invalid API key") {
42
+ super(message, 401, "AUTH_ERROR");
43
+ this.name = "AuthenticationError";
44
+ }
45
+ };
46
+ var RateLimitError = class extends FactuplanError {
47
+ constructor(message = "Rate limit exceeded") {
48
+ super(message, 429, "RATE_LIMIT");
49
+ this.name = "RateLimitError";
50
+ }
51
+ };
52
+
53
+ // src/resources/customers.ts
54
+ var CustomersResource = class {
55
+ constructor(request) {
56
+ this.request = request;
57
+ }
58
+ async create(input) {
59
+ return this.request("POST", "/developer/customers", input);
60
+ }
61
+ async list(params) {
62
+ return this.request("GET", "/developer/customers", void 0, params);
63
+ }
64
+ async get(id) {
65
+ return this.request("GET", `/developer/customers/${id}`);
66
+ }
67
+ async update(id, input) {
68
+ return this.request("PATCH", `/developer/customers/${id}`, input);
69
+ }
70
+ async delete(id) {
71
+ await this.request("DELETE", `/developer/customers/${id}`);
72
+ }
73
+ };
74
+
75
+ // src/resources/products.ts
76
+ var ProductsResource = class {
77
+ constructor(request) {
78
+ this.request = request;
79
+ }
80
+ async create(input) {
81
+ return this.request("POST", "/developer/products", input);
82
+ }
83
+ async list(params) {
84
+ return this.request("GET", "/developer/products", void 0, params);
85
+ }
86
+ async get(id) {
87
+ return this.request("GET", `/developer/products/${id}`);
88
+ }
89
+ async update(id, input) {
90
+ return this.request("PATCH", `/developer/products/${id}`, input);
91
+ }
92
+ async delete(id) {
93
+ await this.request("DELETE", `/developer/products/${id}`);
94
+ }
95
+ };
96
+
97
+ // src/resources/invoices.ts
98
+ var InvoicesResource = class {
99
+ constructor(request) {
100
+ this.request = request;
101
+ }
102
+ async create(input) {
103
+ return this.request("POST", "/developer/invoices", input);
104
+ }
105
+ async get(id) {
106
+ return this.request("GET", `/developer/receipts/${id}`);
107
+ }
108
+ async getStatus(id) {
109
+ return this.request("GET", `/developer/receipts/${id}/status`);
110
+ }
111
+ async downloadXml(id) {
112
+ return this.request("GET", `/developer/receipts/${id}/xml`);
113
+ }
114
+ async downloadPdf(id) {
115
+ return this.request("GET", `/developer/receipts/${id}/pdf`);
116
+ }
117
+ };
118
+
119
+ // src/client.ts
120
+ var DEFAULT_BASE_URL = "https://api.factuplan.com/api/v1";
121
+ var DEFAULT_TIMEOUT = 3e4;
122
+ var Factuplan = class {
123
+ constructor(apiKey, options) {
124
+ if (!apiKey) throw new Error("API key is required");
125
+ this.apiKey = apiKey;
126
+ this.baseUrl = (options?.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
127
+ this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
128
+ const requester = this.request.bind(this);
129
+ this.customers = new CustomersResource(requester);
130
+ this.products = new ProductsResource(requester);
131
+ this.invoices = new InvoicesResource(requester);
132
+ }
133
+ async usage() {
134
+ return this.request("GET", "/developer/usage");
135
+ }
136
+ async certificateStatus() {
137
+ return this.request("GET", "/developer/certificate/status");
138
+ }
139
+ async request(method, path, body, params) {
140
+ let url = `${this.baseUrl}${path}`;
141
+ if (params) {
142
+ const searchParams = new URLSearchParams();
143
+ for (const [key, value] of Object.entries(params)) {
144
+ if (value !== void 0 && value !== null) {
145
+ searchParams.set(key, String(value));
146
+ }
147
+ }
148
+ const qs = searchParams.toString();
149
+ if (qs) url += `?${qs}`;
150
+ }
151
+ const headers = {
152
+ "x-api-key": this.apiKey,
153
+ "Accept": "application/json"
154
+ };
155
+ if (body !== void 0) {
156
+ headers["Content-Type"] = "application/json";
157
+ }
158
+ const response = await fetch(url, {
159
+ method,
160
+ headers,
161
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
162
+ signal: AbortSignal.timeout(this.timeout)
163
+ });
164
+ if (response.status === 204) return void 0;
165
+ const json = await response.json().catch(() => null);
166
+ if (!response.ok) {
167
+ const err = json?.error ?? json;
168
+ const message = err?.message ?? `Request failed with status ${response.status}`;
169
+ const code = err?.code ?? "UNKNOWN";
170
+ const details = err?.details;
171
+ if (response.status === 401) throw new AuthenticationError(message);
172
+ if (response.status === 429) throw new RateLimitError(message);
173
+ throw new FactuplanError(message, response.status, code, details);
174
+ }
175
+ if (json && typeof json === "object" && "data" in json && "meta" in json) {
176
+ const meta = json.meta;
177
+ if (Array.isArray(json.data) && typeof meta?.total !== "undefined") {
178
+ return { data: json.data, meta };
179
+ }
180
+ return json.data;
181
+ }
182
+ return json;
183
+ }
184
+ };
185
+ // Annotate the CommonJS export names for ESM import in node:
186
+ 0 && (module.exports = {
187
+ AuthenticationError,
188
+ Factuplan,
189
+ FactuplanError,
190
+ RateLimitError
191
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,161 @@
1
+ // src/errors.ts
2
+ var FactuplanError = class extends Error {
3
+ constructor(message, statusCode, code, details) {
4
+ super(message);
5
+ this.name = "FactuplanError";
6
+ this.statusCode = statusCode;
7
+ this.code = code;
8
+ this.details = details;
9
+ }
10
+ };
11
+ var AuthenticationError = class extends FactuplanError {
12
+ constructor(message = "Invalid API key") {
13
+ super(message, 401, "AUTH_ERROR");
14
+ this.name = "AuthenticationError";
15
+ }
16
+ };
17
+ var RateLimitError = class extends FactuplanError {
18
+ constructor(message = "Rate limit exceeded") {
19
+ super(message, 429, "RATE_LIMIT");
20
+ this.name = "RateLimitError";
21
+ }
22
+ };
23
+
24
+ // src/resources/customers.ts
25
+ var CustomersResource = class {
26
+ constructor(request) {
27
+ this.request = request;
28
+ }
29
+ async create(input) {
30
+ return this.request("POST", "/developer/customers", input);
31
+ }
32
+ async list(params) {
33
+ return this.request("GET", "/developer/customers", void 0, params);
34
+ }
35
+ async get(id) {
36
+ return this.request("GET", `/developer/customers/${id}`);
37
+ }
38
+ async update(id, input) {
39
+ return this.request("PATCH", `/developer/customers/${id}`, input);
40
+ }
41
+ async delete(id) {
42
+ await this.request("DELETE", `/developer/customers/${id}`);
43
+ }
44
+ };
45
+
46
+ // src/resources/products.ts
47
+ var ProductsResource = class {
48
+ constructor(request) {
49
+ this.request = request;
50
+ }
51
+ async create(input) {
52
+ return this.request("POST", "/developer/products", input);
53
+ }
54
+ async list(params) {
55
+ return this.request("GET", "/developer/products", void 0, params);
56
+ }
57
+ async get(id) {
58
+ return this.request("GET", `/developer/products/${id}`);
59
+ }
60
+ async update(id, input) {
61
+ return this.request("PATCH", `/developer/products/${id}`, input);
62
+ }
63
+ async delete(id) {
64
+ await this.request("DELETE", `/developer/products/${id}`);
65
+ }
66
+ };
67
+
68
+ // src/resources/invoices.ts
69
+ var InvoicesResource = class {
70
+ constructor(request) {
71
+ this.request = request;
72
+ }
73
+ async create(input) {
74
+ return this.request("POST", "/developer/invoices", input);
75
+ }
76
+ async get(id) {
77
+ return this.request("GET", `/developer/receipts/${id}`);
78
+ }
79
+ async getStatus(id) {
80
+ return this.request("GET", `/developer/receipts/${id}/status`);
81
+ }
82
+ async downloadXml(id) {
83
+ return this.request("GET", `/developer/receipts/${id}/xml`);
84
+ }
85
+ async downloadPdf(id) {
86
+ return this.request("GET", `/developer/receipts/${id}/pdf`);
87
+ }
88
+ };
89
+
90
+ // src/client.ts
91
+ var DEFAULT_BASE_URL = "https://api.factuplan.com/api/v1";
92
+ var DEFAULT_TIMEOUT = 3e4;
93
+ var Factuplan = class {
94
+ constructor(apiKey, options) {
95
+ if (!apiKey) throw new Error("API key is required");
96
+ this.apiKey = apiKey;
97
+ this.baseUrl = (options?.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
98
+ this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
99
+ const requester = this.request.bind(this);
100
+ this.customers = new CustomersResource(requester);
101
+ this.products = new ProductsResource(requester);
102
+ this.invoices = new InvoicesResource(requester);
103
+ }
104
+ async usage() {
105
+ return this.request("GET", "/developer/usage");
106
+ }
107
+ async certificateStatus() {
108
+ return this.request("GET", "/developer/certificate/status");
109
+ }
110
+ async request(method, path, body, params) {
111
+ let url = `${this.baseUrl}${path}`;
112
+ if (params) {
113
+ const searchParams = new URLSearchParams();
114
+ for (const [key, value] of Object.entries(params)) {
115
+ if (value !== void 0 && value !== null) {
116
+ searchParams.set(key, String(value));
117
+ }
118
+ }
119
+ const qs = searchParams.toString();
120
+ if (qs) url += `?${qs}`;
121
+ }
122
+ const headers = {
123
+ "x-api-key": this.apiKey,
124
+ "Accept": "application/json"
125
+ };
126
+ if (body !== void 0) {
127
+ headers["Content-Type"] = "application/json";
128
+ }
129
+ const response = await fetch(url, {
130
+ method,
131
+ headers,
132
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
133
+ signal: AbortSignal.timeout(this.timeout)
134
+ });
135
+ if (response.status === 204) return void 0;
136
+ const json = await response.json().catch(() => null);
137
+ if (!response.ok) {
138
+ const err = json?.error ?? json;
139
+ const message = err?.message ?? `Request failed with status ${response.status}`;
140
+ const code = err?.code ?? "UNKNOWN";
141
+ const details = err?.details;
142
+ if (response.status === 401) throw new AuthenticationError(message);
143
+ if (response.status === 429) throw new RateLimitError(message);
144
+ throw new FactuplanError(message, response.status, code, details);
145
+ }
146
+ if (json && typeof json === "object" && "data" in json && "meta" in json) {
147
+ const meta = json.meta;
148
+ if (Array.isArray(json.data) && typeof meta?.total !== "undefined") {
149
+ return { data: json.data, meta };
150
+ }
151
+ return json.data;
152
+ }
153
+ return json;
154
+ }
155
+ };
156
+ export {
157
+ AuthenticationError,
158
+ Factuplan,
159
+ FactuplanError,
160
+ RateLimitError
161
+ };
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "factuplan",
3
+ "version": "0.1.0",
4
+ "description": "Official Factuplan SDK for JavaScript & TypeScript — Create electronic invoices for Ecuador (SRI)",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "devDependencies": {
19
+ "tsup": "^8.0.0",
20
+ "typescript": "^5.5.0"
21
+ },
22
+ "keywords": [
23
+ "factuplan",
24
+ "ecuador",
25
+ "sri",
26
+ "invoice",
27
+ "factura",
28
+ "electronic-billing"
29
+ ],
30
+ "license": "MIT",
31
+ "scripts": {
32
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
33
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch"
34
+ }
35
+ }