feat(sat): add FIEL management and encryption services (Phase 2)
- Add sat-crypto.service.ts with AES-256-GCM encryption for secure credential storage using JWT_SECRET as key derivation source - Add fiel.service.ts with complete FIEL lifecycle management: - Upload and validate FIEL credentials (.cer/.key files) - Verify certificate is FIEL (not CSD) and not expired - Store encrypted credentials in database - Retrieve and decrypt credentials for SAT sync operations - Install @nodecfdi/credentials for FIEL/CSD handling Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@horux/shared": "workspace:*",
|
"@horux/shared": "workspace:*",
|
||||||
|
"@nodecfdi/credentials": "^3.2.0",
|
||||||
"@prisma/client": "^5.22.0",
|
"@prisma/client": "^5.22.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
"express": "^4.21.0",
|
"express": "^4.21.0",
|
||||||
"helmet": "^8.0.0",
|
"helmet": "^8.0.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"node-forge": "^1.3.3",
|
||||||
"zod": "^3.23.0"
|
"zod": "^3.23.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -31,6 +33,7 @@
|
|||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/jsonwebtoken": "^9.0.7",
|
"@types/jsonwebtoken": "^9.0.7",
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
|
"@types/node-forge": "^1.3.14",
|
||||||
"prisma": "^5.22.0",
|
"prisma": "^5.22.0",
|
||||||
"tsx": "^4.19.0",
|
"tsx": "^4.19.0",
|
||||||
"typescript": "^5.3.0"
|
"typescript": "^5.3.0"
|
||||||
|
|||||||
232
apps/api/src/services/fiel.service.ts
Normal file
232
apps/api/src/services/fiel.service.ts
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
import { Credential } from '@nodecfdi/credentials/node';
|
||||||
|
import { prisma } from '../config/database.js';
|
||||||
|
import { encrypt, decrypt } from './sat/sat-crypto.service.js';
|
||||||
|
import type { FielStatus } from '@horux/shared';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sube y valida credenciales FIEL
|
||||||
|
*/
|
||||||
|
export async function uploadFiel(
|
||||||
|
tenantId: string,
|
||||||
|
cerBase64: string,
|
||||||
|
keyBase64: string,
|
||||||
|
password: string
|
||||||
|
): Promise<{ success: boolean; message: string; status?: FielStatus }> {
|
||||||
|
try {
|
||||||
|
// Decodificar archivos de Base64
|
||||||
|
const cerData = Buffer.from(cerBase64, 'base64');
|
||||||
|
const keyData = Buffer.from(keyBase64, 'base64');
|
||||||
|
|
||||||
|
// Validar que los archivos sean válidos y coincidan
|
||||||
|
let credential: Credential;
|
||||||
|
try {
|
||||||
|
credential = Credential.create(
|
||||||
|
cerData.toString('binary'),
|
||||||
|
keyData.toString('binary'),
|
||||||
|
password
|
||||||
|
);
|
||||||
|
} catch (error: any) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: 'Los archivos de la FIEL no son válidos o la contraseña es incorrecta',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar que sea una FIEL (no CSD)
|
||||||
|
if (!credential.isFiel()) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: 'El certificado proporcionado no es una FIEL (e.firma). Parece ser un CSD.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtener información del certificado
|
||||||
|
const certificate = credential.certificate();
|
||||||
|
const rfc = certificate.rfc();
|
||||||
|
const serialNumber = certificate.serialNumber().bytes();
|
||||||
|
const validFrom = certificate.validFromDateTime();
|
||||||
|
const validUntil = certificate.validToDateTime();
|
||||||
|
|
||||||
|
// Verificar que no esté vencida
|
||||||
|
if (new Date() > validUntil) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: 'La FIEL está vencida desde ' + validUntil.toLocaleDateString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encriptar credenciales
|
||||||
|
const { encrypted: encryptedCer, iv, tag } = encrypt(cerData);
|
||||||
|
const { encrypted: encryptedKey } = encrypt(keyData);
|
||||||
|
const { encrypted: encryptedPassword } = encrypt(Buffer.from(password, 'utf-8'));
|
||||||
|
|
||||||
|
// Guardar o actualizar en BD
|
||||||
|
await prisma.fielCredential.upsert({
|
||||||
|
where: { tenantId },
|
||||||
|
create: {
|
||||||
|
tenantId,
|
||||||
|
rfc,
|
||||||
|
cerData: encryptedCer,
|
||||||
|
keyData: encryptedKey,
|
||||||
|
keyPasswordEncrypted: encryptedPassword,
|
||||||
|
encryptionIv: iv,
|
||||||
|
encryptionTag: tag,
|
||||||
|
serialNumber,
|
||||||
|
validFrom,
|
||||||
|
validUntil,
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
rfc,
|
||||||
|
cerData: encryptedCer,
|
||||||
|
keyData: encryptedKey,
|
||||||
|
keyPasswordEncrypted: encryptedPassword,
|
||||||
|
encryptionIv: iv,
|
||||||
|
encryptionTag: tag,
|
||||||
|
serialNumber,
|
||||||
|
validFrom,
|
||||||
|
validUntil,
|
||||||
|
isActive: true,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const daysUntilExpiration = Math.ceil(
|
||||||
|
(validUntil.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'FIEL configurada correctamente',
|
||||||
|
status: {
|
||||||
|
configured: true,
|
||||||
|
rfc,
|
||||||
|
serialNumber,
|
||||||
|
validFrom: validFrom.toISOString(),
|
||||||
|
validUntil: validUntil.toISOString(),
|
||||||
|
isExpired: false,
|
||||||
|
daysUntilExpiration,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('[FIEL Upload Error]', error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: error.message || 'Error al procesar la FIEL',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtiene el estado de la FIEL de un tenant
|
||||||
|
*/
|
||||||
|
export async function getFielStatus(tenantId: string): Promise<FielStatus> {
|
||||||
|
const fiel = await prisma.fielCredential.findUnique({
|
||||||
|
where: { tenantId },
|
||||||
|
select: {
|
||||||
|
rfc: true,
|
||||||
|
serialNumber: true,
|
||||||
|
validFrom: true,
|
||||||
|
validUntil: true,
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!fiel || !fiel.isActive) {
|
||||||
|
return { configured: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const isExpired = now > fiel.validUntil;
|
||||||
|
const daysUntilExpiration = Math.ceil(
|
||||||
|
(fiel.validUntil.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
configured: true,
|
||||||
|
rfc: fiel.rfc,
|
||||||
|
serialNumber: fiel.serialNumber || undefined,
|
||||||
|
validFrom: fiel.validFrom.toISOString(),
|
||||||
|
validUntil: fiel.validUntil.toISOString(),
|
||||||
|
isExpired,
|
||||||
|
daysUntilExpiration: isExpired ? 0 : daysUntilExpiration,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Elimina la FIEL de un tenant
|
||||||
|
*/
|
||||||
|
export async function deleteFiel(tenantId: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await prisma.fielCredential.delete({
|
||||||
|
where: { tenantId },
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtiene las credenciales desencriptadas para usar en sincronización
|
||||||
|
* Solo debe usarse internamente por el servicio de SAT
|
||||||
|
*/
|
||||||
|
export async function getDecryptedFiel(tenantId: string): Promise<{
|
||||||
|
credential: Credential;
|
||||||
|
rfc: string;
|
||||||
|
} | null> {
|
||||||
|
const fiel = await prisma.fielCredential.findUnique({
|
||||||
|
where: { tenantId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!fiel || !fiel.isActive) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar que no esté vencida
|
||||||
|
if (new Date() > fiel.validUntil) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Desencriptar
|
||||||
|
const cerData = decrypt(
|
||||||
|
Buffer.from(fiel.cerData),
|
||||||
|
Buffer.from(fiel.encryptionIv),
|
||||||
|
Buffer.from(fiel.encryptionTag)
|
||||||
|
);
|
||||||
|
const keyData = decrypt(
|
||||||
|
Buffer.from(fiel.keyData),
|
||||||
|
Buffer.from(fiel.encryptionIv),
|
||||||
|
Buffer.from(fiel.encryptionTag)
|
||||||
|
);
|
||||||
|
const password = decrypt(
|
||||||
|
Buffer.from(fiel.keyPasswordEncrypted),
|
||||||
|
Buffer.from(fiel.encryptionIv),
|
||||||
|
Buffer.from(fiel.encryptionTag)
|
||||||
|
).toString('utf-8');
|
||||||
|
|
||||||
|
// Crear credencial
|
||||||
|
const credential = Credential.create(
|
||||||
|
cerData.toString('binary'),
|
||||||
|
keyData.toString('binary'),
|
||||||
|
password
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
credential,
|
||||||
|
rfc: fiel.rfc,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[FIEL Decrypt Error]', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica si un tenant tiene FIEL configurada y válida
|
||||||
|
*/
|
||||||
|
export async function hasFielConfigured(tenantId: string): Promise<boolean> {
|
||||||
|
const status = await getFielStatus(tenantId);
|
||||||
|
return status.configured && !status.isExpired;
|
||||||
|
}
|
||||||
122
apps/api/src/services/sat/sat-crypto.service.ts
Normal file
122
apps/api/src/services/sat/sat-crypto.service.ts
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import { createCipheriv, createDecipheriv, randomBytes, createHash } from 'crypto';
|
||||||
|
import { env } from '../../config/env.js';
|
||||||
|
|
||||||
|
const ALGORITHM = 'aes-256-gcm';
|
||||||
|
const IV_LENGTH = 16;
|
||||||
|
const TAG_LENGTH = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deriva una clave de 256 bits del JWT_SECRET
|
||||||
|
*/
|
||||||
|
function deriveKey(): Buffer {
|
||||||
|
return createHash('sha256').update(env.JWT_SECRET).digest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encripta datos usando AES-256-GCM
|
||||||
|
*/
|
||||||
|
export function encrypt(data: Buffer): { encrypted: Buffer; iv: Buffer; tag: Buffer } {
|
||||||
|
const iv = randomBytes(IV_LENGTH);
|
||||||
|
const key = deriveKey();
|
||||||
|
const cipher = createCipheriv(ALGORITHM, key, iv);
|
||||||
|
|
||||||
|
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
||||||
|
const tag = cipher.getAuthTag();
|
||||||
|
|
||||||
|
return { encrypted, iv, tag };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Desencripta datos usando AES-256-GCM
|
||||||
|
*/
|
||||||
|
export function decrypt(encrypted: Buffer, iv: Buffer, tag: Buffer): Buffer {
|
||||||
|
const key = deriveKey();
|
||||||
|
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
||||||
|
decipher.setAuthTag(tag);
|
||||||
|
|
||||||
|
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encripta un string y retorna los componentes
|
||||||
|
*/
|
||||||
|
export function encryptString(text: string): { encrypted: Buffer; iv: Buffer; tag: Buffer } {
|
||||||
|
return encrypt(Buffer.from(text, 'utf-8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Desencripta a string
|
||||||
|
*/
|
||||||
|
export function decryptToString(encrypted: Buffer, iv: Buffer, tag: Buffer): string {
|
||||||
|
return decrypt(encrypted, iv, tag).toString('utf-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encripta credenciales FIEL (cer, key, password)
|
||||||
|
*/
|
||||||
|
export function encryptFielCredentials(
|
||||||
|
cerData: Buffer,
|
||||||
|
keyData: Buffer,
|
||||||
|
password: string
|
||||||
|
): {
|
||||||
|
encryptedCer: Buffer;
|
||||||
|
encryptedKey: Buffer;
|
||||||
|
encryptedPassword: Buffer;
|
||||||
|
iv: Buffer;
|
||||||
|
tag: Buffer;
|
||||||
|
} {
|
||||||
|
// Usamos el mismo IV y tag para simplificar, concatenando los datos
|
||||||
|
const combined = Buffer.concat([
|
||||||
|
Buffer.from(cerData.length.toString().padStart(10, '0')),
|
||||||
|
cerData,
|
||||||
|
Buffer.from(keyData.length.toString().padStart(10, '0')),
|
||||||
|
keyData,
|
||||||
|
Buffer.from(password, 'utf-8'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { encrypted, iv, tag } = encrypt(combined);
|
||||||
|
|
||||||
|
// Extraemos las partes encriptadas
|
||||||
|
const cerLength = cerData.length;
|
||||||
|
const keyLength = keyData.length;
|
||||||
|
const passwordLength = Buffer.from(password, 'utf-8').length;
|
||||||
|
|
||||||
|
return {
|
||||||
|
encryptedCer: encrypted.subarray(0, 10 + cerLength),
|
||||||
|
encryptedKey: encrypted.subarray(10 + cerLength, 20 + cerLength + keyLength),
|
||||||
|
encryptedPassword: encrypted.subarray(20 + cerLength + keyLength),
|
||||||
|
iv,
|
||||||
|
tag,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Desencripta credenciales FIEL
|
||||||
|
*/
|
||||||
|
export function decryptFielCredentials(
|
||||||
|
encryptedCer: Buffer,
|
||||||
|
encryptedKey: Buffer,
|
||||||
|
encryptedPassword: Buffer,
|
||||||
|
iv: Buffer,
|
||||||
|
tag: Buffer
|
||||||
|
): {
|
||||||
|
cerData: Buffer;
|
||||||
|
keyData: Buffer;
|
||||||
|
password: string;
|
||||||
|
} {
|
||||||
|
const combined = Buffer.concat([encryptedCer, encryptedKey, encryptedPassword]);
|
||||||
|
const decrypted = decrypt(combined, iv, tag);
|
||||||
|
|
||||||
|
// Parseamos las partes
|
||||||
|
const cerLengthStr = decrypted.subarray(0, 10).toString();
|
||||||
|
const cerLength = parseInt(cerLengthStr, 10);
|
||||||
|
const cerData = decrypted.subarray(10, 10 + cerLength);
|
||||||
|
|
||||||
|
const keyLengthStr = decrypted.subarray(10 + cerLength, 20 + cerLength).toString();
|
||||||
|
const keyLength = parseInt(keyLengthStr, 10);
|
||||||
|
const keyData = decrypted.subarray(20 + cerLength, 20 + cerLength + keyLength);
|
||||||
|
|
||||||
|
const password = decrypted.subarray(20 + cerLength + keyLength).toString('utf-8');
|
||||||
|
|
||||||
|
return { cerData, keyData, password };
|
||||||
|
}
|
||||||
62
pnpm-lock.yaml
generated
62
pnpm-lock.yaml
generated
@@ -20,6 +20,9 @@ importers:
|
|||||||
'@horux/shared':
|
'@horux/shared':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/shared
|
version: link:../../packages/shared
|
||||||
|
'@nodecfdi/credentials':
|
||||||
|
specifier: ^3.2.0
|
||||||
|
version: 3.2.0(luxon@3.7.2)
|
||||||
'@prisma/client':
|
'@prisma/client':
|
||||||
specifier: ^5.22.0
|
specifier: ^5.22.0
|
||||||
version: 5.22.0(prisma@5.22.0)
|
version: 5.22.0(prisma@5.22.0)
|
||||||
@@ -44,6 +47,9 @@ importers:
|
|||||||
jsonwebtoken:
|
jsonwebtoken:
|
||||||
specifier: ^9.0.2
|
specifier: ^9.0.2
|
||||||
version: 9.0.3
|
version: 9.0.3
|
||||||
|
node-forge:
|
||||||
|
specifier: ^1.3.3
|
||||||
|
version: 1.3.3
|
||||||
zod:
|
zod:
|
||||||
specifier: ^3.23.0
|
specifier: ^3.23.0
|
||||||
version: 3.25.76
|
version: 3.25.76
|
||||||
@@ -63,6 +69,9 @@ importers:
|
|||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^22.0.0
|
specifier: ^22.0.0
|
||||||
version: 22.19.7
|
version: 22.19.7
|
||||||
|
'@types/node-forge':
|
||||||
|
specifier: ^1.3.14
|
||||||
|
version: 1.3.14
|
||||||
prisma:
|
prisma:
|
||||||
specifier: ^5.22.0
|
specifier: ^5.22.0
|
||||||
version: 5.22.0
|
version: 5.22.0
|
||||||
@@ -439,6 +448,20 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
|
'@nodecfdi/base-converter@1.0.7':
|
||||||
|
resolution: {integrity: sha512-YoWtdhCPB86W+2TpXrZ1yXzehNC2sEFCB0vw4XtnHKdtw6pKxKyDT2qQf4TqICROp0IZNNKunFDw3EhcoR41Tw==}
|
||||||
|
engines: {node: '>=18 <=22 || ^16'}
|
||||||
|
|
||||||
|
'@nodecfdi/credentials@3.2.0':
|
||||||
|
resolution: {integrity: sha512-knZE8kIrIib27M/tcUQRgvnObMd7oR9EKZTSdBSHXW/5Pw6UB23v0ruUAJSFY0789J3OLfKaIVRXBG2I+q9ZTA==}
|
||||||
|
engines: {node: '>=18 <=22 || ^16'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/luxon': 3.4.2
|
||||||
|
luxon: ^3.5.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/luxon':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -989,6 +1012,9 @@ packages:
|
|||||||
'@types/ms@2.1.0':
|
'@types/ms@2.1.0':
|
||||||
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
|
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
|
||||||
|
|
||||||
|
'@types/node-forge@1.3.14':
|
||||||
|
resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==}
|
||||||
|
|
||||||
'@types/node@14.18.63':
|
'@types/node@14.18.63':
|
||||||
resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==}
|
resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==}
|
||||||
|
|
||||||
@@ -1018,6 +1044,10 @@ packages:
|
|||||||
'@types/serve-static@2.2.0':
|
'@types/serve-static@2.2.0':
|
||||||
resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==}
|
resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==}
|
||||||
|
|
||||||
|
'@vilic/node-forge@1.3.2-5':
|
||||||
|
resolution: {integrity: sha512-8GVr3S/nmLKL7QI7RYhVIcz3PuT/fxfkQLuh/F1CaT+/3QgI14RqiJkcKIni7h9u4ySbQGiGvm4XbNxRBJin4g==}
|
||||||
|
engines: {node: '>= 6.13.0'}
|
||||||
|
|
||||||
accepts@1.3.8:
|
accepts@1.3.8:
|
||||||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@@ -1663,6 +1693,10 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
|
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
|
||||||
|
|
||||||
|
luxon@3.7.2:
|
||||||
|
resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
math-intrinsics@1.1.0:
|
math-intrinsics@1.1.0:
|
||||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1749,6 +1783,10 @@ packages:
|
|||||||
sass:
|
sass:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
node-forge@1.3.3:
|
||||||
|
resolution: {integrity: sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==}
|
||||||
|
engines: {node: '>= 6.13.0'}
|
||||||
|
|
||||||
node-releases@2.0.27:
|
node-releases@2.0.27:
|
||||||
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
|
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
|
||||||
|
|
||||||
@@ -2140,6 +2178,9 @@ packages:
|
|||||||
ts-interface-checker@0.1.13:
|
ts-interface-checker@0.1.13:
|
||||||
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
||||||
|
|
||||||
|
ts-mixer@6.0.4:
|
||||||
|
resolution: {integrity: sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==}
|
||||||
|
|
||||||
tslib@2.8.1:
|
tslib@2.8.1:
|
||||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
@@ -2444,6 +2485,15 @@ snapshots:
|
|||||||
'@next/swc-win32-x64-msvc@14.2.33':
|
'@next/swc-win32-x64-msvc@14.2.33':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@nodecfdi/base-converter@1.0.7': {}
|
||||||
|
|
||||||
|
'@nodecfdi/credentials@3.2.0(luxon@3.7.2)':
|
||||||
|
dependencies:
|
||||||
|
'@nodecfdi/base-converter': 1.0.7
|
||||||
|
'@vilic/node-forge': 1.3.2-5
|
||||||
|
luxon: 3.7.2
|
||||||
|
ts-mixer: 6.0.4
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nodelib/fs.stat': 2.0.5
|
'@nodelib/fs.stat': 2.0.5
|
||||||
@@ -2988,6 +3038,10 @@ snapshots:
|
|||||||
|
|
||||||
'@types/ms@2.1.0': {}
|
'@types/ms@2.1.0': {}
|
||||||
|
|
||||||
|
'@types/node-forge@1.3.14':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.19.7
|
||||||
|
|
||||||
'@types/node@14.18.63': {}
|
'@types/node@14.18.63': {}
|
||||||
|
|
||||||
'@types/node@22.19.7':
|
'@types/node@22.19.7':
|
||||||
@@ -3018,6 +3072,8 @@ snapshots:
|
|||||||
'@types/http-errors': 2.0.5
|
'@types/http-errors': 2.0.5
|
||||||
'@types/node': 22.19.7
|
'@types/node': 22.19.7
|
||||||
|
|
||||||
|
'@vilic/node-forge@1.3.2-5': {}
|
||||||
|
|
||||||
accepts@1.3.8:
|
accepts@1.3.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
@@ -3717,6 +3773,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
|
|
||||||
|
luxon@3.7.2: {}
|
||||||
|
|
||||||
math-intrinsics@1.1.0: {}
|
math-intrinsics@1.1.0: {}
|
||||||
|
|
||||||
media-typer@0.3.0: {}
|
media-typer@0.3.0: {}
|
||||||
@@ -3793,6 +3851,8 @@ snapshots:
|
|||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
|
|
||||||
|
node-forge@1.3.3: {}
|
||||||
|
|
||||||
node-releases@2.0.27: {}
|
node-releases@2.0.27: {}
|
||||||
|
|
||||||
normalize-path@3.0.0: {}
|
normalize-path@3.0.0: {}
|
||||||
@@ -4207,6 +4267,8 @@ snapshots:
|
|||||||
|
|
||||||
ts-interface-checker@0.1.13: {}
|
ts-interface-checker@0.1.13: {}
|
||||||
|
|
||||||
|
ts-mixer@6.0.4: {}
|
||||||
|
|
||||||
tslib@2.8.1: {}
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
tsx@4.21.0:
|
tsx@4.21.0:
|
||||||
|
|||||||
Reference in New Issue
Block a user