first commit

This commit is contained in:
Raul Lugo
2024-12-25 20:30:57 +01:00
commit 6ef22531bb
165 changed files with 53426 additions and 0 deletions

78
src/client.ts Normal file
View File

@@ -0,0 +1,78 @@
import { ValidationError } from "./types/errors.ts";
import { DEFAULT_CONFIG, ENDPOINTS } from "./constants.ts";
import { handleResponse, toBlob } from "./utils.ts";
import { ClientConfig } from "./types/config.ts";
import { FileContent, ValidationResult } from "./types/validation.ts";
export class CaballoClient {
private readonly config: ClientConfig;
constructor(config: ClientConfig) {
this.config = {
...DEFAULT_CONFIG,
...config,
};
if (!this.config.baseUrl) {
throw new Error("baseUrl is required");
}
}
async validateXml(content: FileContent): Promise<ValidationResult> {
try {
const blob = toBlob(content);
if (blob.size === 0) {
throw new ValidationError("Empty file content");
}
const formData = new FormData();
formData.append("file", blob);
const response = await fetch(
`${this.config.baseUrl}${ENDPOINTS.validateXml}`,
{
method: "POST",
body: formData,
signal: AbortSignal.timeout(this.config.timeout!),
},
);
return handleResponse(response);
} catch (error) {
if (error instanceof TypeError) {
throw new ValidationError("Network error or invalid URL");
}
throw error;
}
}
async validatePdf(content: FileContent): Promise<ValidationResult> {
try {
const blob = toBlob(content);
if (blob.size === 0) {
throw new ValidationError("Empty file content");
}
const formData = new FormData();
formData.append("file", blob);
const response = await fetch(
`${this.config.baseUrl}${ENDPOINTS.validatePdf}`,
{
method: "POST",
body: formData,
signal: AbortSignal.timeout(this.config.timeout!),
},
);
return handleResponse(response);
} catch (error) {
if (error instanceof TypeError) {
throw new ValidationError("Network error or invalid URL");
}
throw error;
}
}
}

11
src/constants.ts Normal file
View File

@@ -0,0 +1,11 @@
import { ClientConfig } from "./types/config.ts";
export const DEFAULT_CONFIG: Partial<ClientConfig> = {
timeout: 30000,
retries: 3,
};
export const ENDPOINTS = {
validateXml: "/api/v1/e-invoice/validate/xml",
validatePdf: "/api/v1/e-invoice/validate/pdf",
} as const;

4
src/mod.ts Normal file
View File

@@ -0,0 +1,4 @@
export * from "./client.ts";
export * from "./types/config.ts";
export * from "./types/errors.ts";
export * from "./types/validation.ts";

5
src/types/config.ts Normal file
View File

@@ -0,0 +1,5 @@
export interface ClientConfig {
baseUrl: string;
timeout?: number;
retries?: number;
}

12
src/types/errors.ts Normal file
View File

@@ -0,0 +1,12 @@
export class ValidationError extends Error {
constructor(message: string, public statusCode?: number) {
super(message);
this.name = "ValidationError";
}
}
export interface ApiError {
status: string;
message: string;
details?: string;
}

8
src/types/validation.ts Normal file
View File

@@ -0,0 +1,8 @@
export interface ValidationResult {
valid: boolean;
conformanceLevel?: string;
structureErrors?: string[];
pdfErrors?: string[];
}
export type FileContent = Uint8Array | Blob | File;

24
src/utils.ts Normal file
View File

@@ -0,0 +1,24 @@
import { ApiError, ValidationError } from "./types/errors.ts";
import { FileContent, ValidationResult } from "./types/validation.ts";
export function toBlob(content: FileContent): Blob {
if (content instanceof Blob) return content;
return new Blob([content]);
}
export async function handleResponse(
response: Response,
): Promise<ValidationResult> {
if (!response.ok) {
const errorData = await response.json().catch(() => ({
message: response.statusText,
status: `${response.status}`,
})) as ApiError;
throw new ValidationError(
errorData.message || "Unknown error",
response.status,
);
}
const data = await response.json();
return data as ValidationResult;
}