@facturacr/atv-sdk 0.0.26-beta → 1.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/.github/workflows/ci.yml +2 -2
- package/.github/workflows/publish.yml +25 -0
- package/.github/workflows/release-please.yml +17 -0
- package/CHANGELOG.md +37 -0
- package/__tests__/stubs/commonExpectedXml.xml +13 -13
- package/__tests__/stubs/createDocument.data.ts +3 -3
- package/__tests__/tests/ATV/__snapshots__/index.test.ts.snap +1 -1
- package/__tests__/tests/ATV/index.test.ts +5 -5
- package/dist/src/ATV/core/CreateDocFactory.d.ts +2 -0
- package/dist/src/ATV/core/CreateDocFactory.js +1 -1
- package/dist/src/ATV/core/CreateDocFactory.js.map +1 -1
- package/dist/src/ATV/core/Person.d.ts +1 -1
- package/dist/src/ATV/core/ReceptorConsecutive.d.ts +13 -0
- package/dist/src/ATV/core/ReceptorConsecutive.js +21 -0
- package/dist/src/ATV/core/ReceptorConsecutive.js.map +1 -0
- package/dist/src/ATV/core/documentNames.types.d.ts +1 -1
- package/dist/src/ATV/core/types.d.ts +20 -0
- package/dist/src/ATV/core/types.js +10 -0
- package/dist/src/ATV/core/types.js.map +1 -0
- package/dist/src/ATV/index.d.ts +8 -0
- package/dist/src/ATV/index.js +5 -0
- package/dist/src/ATV/index.js.map +1 -1
- package/dist/src/ATV/mappers/billDocToAtv.d.ts +2 -0
- package/dist/src/ATV/mappers/billDocToAtv.js +23 -2
- package/dist/src/ATV/mappers/billDocToAtv.js.map +1 -1
- package/dist/src/ATV/useCases/createDocument/index.js.map +1 -1
- package/dist/src/ATV/useCases/createDocument/types.d.ts +16 -13
- package/dist/src/ATV/useCases/createReceptorMessage/index.d.ts +31 -0
- package/dist/src/ATV/useCases/createReceptorMessage/index.js +115 -0
- package/dist/src/ATV/useCases/createReceptorMessage/index.js.map +1 -0
- package/dist/src/confirmXML.js +1 -1
- package/dist/src/confirmXML.js.map +1 -1
- package/dist/src/electronicBill.js.map +1 -1
- package/dist/src/helpers/comprobantes.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +3 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/genJSON/confirmXML.js +3 -3
- package/dist/src/lib/genJSON/confirmXML.js.map +1 -1
- package/dist/src/lib/genJSON/index.js.map +1 -1
- package/dist/src/lib/genXML/index.d.ts +2 -1
- package/dist/src/lib/genXML/index.js +3 -3
- package/dist/src/lib/genXML/index.js.map +1 -1
- package/dist/src/types/facturaInterfaces.d.ts +19 -2
- package/examples/README.md +10 -5
- package/examples/atvAccept.ts +76 -0
- package/examples/createAndSend.ts +6 -3
- package/package.json +1 -1
- package/src/ATV/core/CreateDocFactory.ts +3 -1
- package/src/ATV/core/Person.ts +1 -1
- package/src/ATV/core/ReceptorConsecutive.ts +27 -0
- package/src/ATV/core/documentNames.types.ts +2 -1
- package/src/ATV/core/types.ts +22 -0
- package/src/ATV/index.ts +6 -0
- package/src/ATV/mappers/billDocToAtv.ts +23 -1
- package/src/ATV/useCases/createDocument/index.ts +0 -2
- package/src/ATV/useCases/createDocument/types.ts +16 -13
- package/src/ATV/useCases/createReceptorMessage/index.ts +121 -0
- package/src/confirmXML.ts +2 -2
- package/src/electronicBill.ts +2 -1
- package/src/helpers/comprobantes.ts +1 -1
- package/src/index.ts +1 -1
- package/src/lib/genJSON/confirmXML.ts +5 -5
- package/src/lib/genJSON/index.ts +1 -1
- package/src/lib/genXML/index.ts +2 -1
- package/src/types/facturaInterfaces.ts +21 -2
- package/examples/confirmXML.ts +0 -76
|
@@ -5,22 +5,25 @@ import { Method } from 'axios'
|
|
|
5
5
|
|
|
6
6
|
export type DocumentTypes = 'FE'
|
|
7
7
|
|
|
8
|
+
type CommandData = {
|
|
9
|
+
clave: string;
|
|
10
|
+
fecha: string;
|
|
11
|
+
emisor: {
|
|
12
|
+
tipoIdentificacion: string;
|
|
13
|
+
numeroIdentificacion: string;
|
|
14
|
+
};
|
|
15
|
+
receptor: {
|
|
16
|
+
tipoIdentificacion: string;
|
|
17
|
+
numeroIdentificacion: string;
|
|
18
|
+
};
|
|
19
|
+
comprobanteXml: string;
|
|
20
|
+
consecutivoReceptor?: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
8
23
|
export type Command = {
|
|
9
24
|
url: string;
|
|
10
25
|
method: Method;
|
|
11
|
-
data:
|
|
12
|
-
clave: string;
|
|
13
|
-
fecha: string;
|
|
14
|
-
emisor: {
|
|
15
|
-
tipoIdentificacion: string;
|
|
16
|
-
numeroIdentificacion: string;
|
|
17
|
-
};
|
|
18
|
-
receptor: {
|
|
19
|
-
tipoIdentificacion: string;
|
|
20
|
-
numeroIdentificacion: string;
|
|
21
|
-
};
|
|
22
|
-
comprobanteXml: string;
|
|
23
|
-
};
|
|
26
|
+
data: CommandData;
|
|
24
27
|
headers: {
|
|
25
28
|
Authorization: string;
|
|
26
29
|
'Content-Type': string;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { AceptationStates, ReceptorMessageProps } from "@src/ATV/core/types";
|
|
2
|
+
import { mapReceptorMessageToAtvFormat } from "@src/ATV/mappers/billDocToAtv";
|
|
3
|
+
import { genXML, parseElectronicBillXML } from "@src/lib/genXML";
|
|
4
|
+
import { Command } from "../createDocument/types";
|
|
5
|
+
import { ATV } from "@src/ATV";
|
|
6
|
+
import { ReceptorConsecutive } from "@src/ATV/core/ReceptorConsecutive";
|
|
7
|
+
|
|
8
|
+
export type CreateReceptorMessageCommandInput = {
|
|
9
|
+
receivedDocument: string;
|
|
10
|
+
aceptationState: AceptationStates;
|
|
11
|
+
aceptationDetailMessage: string;
|
|
12
|
+
branch: string;
|
|
13
|
+
terminal: string;
|
|
14
|
+
token: string;
|
|
15
|
+
consecutive: string;
|
|
16
|
+
signatureOptions: {
|
|
17
|
+
buffer: string;
|
|
18
|
+
password: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const options: { [key: string]: { serviceUrl: string}} = {
|
|
23
|
+
prod: {
|
|
24
|
+
serviceUrl: 'https://api.comprobanteselectronicos.go.cr/v1/recepcion'
|
|
25
|
+
},
|
|
26
|
+
stg: {
|
|
27
|
+
serviceUrl: 'https://api-sandbox.comprobanteselectronicos.go.cr/recepcion/v1/recepcion'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class CreateReceptorMessageCommand {
|
|
32
|
+
private readonly serviceUrl: string
|
|
33
|
+
|
|
34
|
+
constructor(scope: ATV) {
|
|
35
|
+
this.serviceUrl = options[scope.mode].serviceUrl
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public async execute(input: CreateReceptorMessageCommandInput) {
|
|
39
|
+
const receptorMessageProps = this.processDocument(input)
|
|
40
|
+
const atvDocument = mapReceptorMessageToAtvFormat(receptorMessageProps)
|
|
41
|
+
const xml = await genXML('MensajeReceptor', atvDocument, input.signatureOptions)
|
|
42
|
+
const command = await this.createDocumentCommand(receptorMessageProps, xml, input.token)
|
|
43
|
+
return {
|
|
44
|
+
command,
|
|
45
|
+
extraData: {
|
|
46
|
+
xml,
|
|
47
|
+
document: atvDocument
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private async createDocumentCommand(receptorMessageProps: ReceptorMessageProps, xml: string, token: string): Promise<Command> {
|
|
53
|
+
return {
|
|
54
|
+
url: this.serviceUrl,
|
|
55
|
+
method: 'post',
|
|
56
|
+
data: {
|
|
57
|
+
clave: receptorMessageProps.clave,
|
|
58
|
+
fecha: receptorMessageProps.documentIssueDate.toISOString(),
|
|
59
|
+
emisor: {
|
|
60
|
+
tipoIdentificacion: receptorMessageProps.emitterIdentifierType,
|
|
61
|
+
numeroIdentificacion: receptorMessageProps.emitterIdentifier,
|
|
62
|
+
},
|
|
63
|
+
receptor: {
|
|
64
|
+
tipoIdentificacion: receptorMessageProps.receptorIdentifierType,
|
|
65
|
+
numeroIdentificacion: receptorMessageProps.receptorIdentifier,
|
|
66
|
+
},
|
|
67
|
+
comprobanteXml: this.encodeXML(xml),
|
|
68
|
+
consecutivoReceptor: receptorMessageProps.receptorConcecutive
|
|
69
|
+
},
|
|
70
|
+
headers: {
|
|
71
|
+
Authorization: 'bearer ' + token,
|
|
72
|
+
'Content-Type': 'application/json'
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private encodeXML(xmlStr: string): string {
|
|
78
|
+
const buffer = Buffer.from(xmlStr)
|
|
79
|
+
return buffer.toString('base64')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private acceptationStateToDocumentType(aceptationState: AceptationStates) {
|
|
83
|
+
switch (aceptationState) {
|
|
84
|
+
case AceptationStates.ACCEPTED:
|
|
85
|
+
return '05'
|
|
86
|
+
case AceptationStates.PARTIALLY_ACCEPTED:
|
|
87
|
+
return '06'
|
|
88
|
+
default:
|
|
89
|
+
return '07'
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private processDocument(input: CreateReceptorMessageCommandInput): ReceptorMessageProps {
|
|
94
|
+
try {
|
|
95
|
+
const electronillBillRaw = parseElectronicBillXML(input.receivedDocument)
|
|
96
|
+
const receptorConsecutive = ReceptorConsecutive.create({
|
|
97
|
+
branch: input.branch,
|
|
98
|
+
terminal: input.terminal,
|
|
99
|
+
documentType: this.acceptationStateToDocumentType(input.aceptationState),
|
|
100
|
+
consecutive: input.consecutive,
|
|
101
|
+
})
|
|
102
|
+
return {
|
|
103
|
+
clave: electronillBillRaw.Clave,
|
|
104
|
+
emitterIdentifier: electronillBillRaw.Emisor.Identificacion.Numero,
|
|
105
|
+
emitterIdentifierType: electronillBillRaw.Emisor.Identificacion.Tipo,
|
|
106
|
+
receptorIdentifier: electronillBillRaw.Receptor.Identificacion.Numero,
|
|
107
|
+
receptorIdentifierType: electronillBillRaw.Receptor.Identificacion.Tipo,
|
|
108
|
+
documentIssueDate: new Date(electronillBillRaw.FechaEmision),
|
|
109
|
+
activityCode: electronillBillRaw.CodigoActividad,
|
|
110
|
+
taxCondition: electronillBillRaw.CondicionVenta,
|
|
111
|
+
totalTaxes: electronillBillRaw.ResumenFactura.TotalImpuesto,
|
|
112
|
+
totalSale: electronillBillRaw.ResumenFactura.TotalVenta,
|
|
113
|
+
aceptationState: input.aceptationState,
|
|
114
|
+
aceptationDetailMessage: input.aceptationDetailMessage,
|
|
115
|
+
receptorConcecutive: receptorConsecutive.value
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
throw new Error('Error parsing the document')
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
package/src/confirmXML.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { consecutivoStr } from '@src/lib/genClave'
|
|
2
|
-
import {
|
|
2
|
+
import { parseElectronicBillXML, genXML } from '@src/lib/genXML/index'
|
|
3
3
|
import { send } from '@src/services/send/index'
|
|
4
4
|
import { getFinalMessage, getMsjObj } from '@src/lib/genJSON/confirmXML'
|
|
5
5
|
|
|
@@ -21,7 +21,7 @@ export default async (opts: {
|
|
|
21
21
|
}): Promise<any> => {
|
|
22
22
|
const { token, pemOpt, tipoDocKey } = opts
|
|
23
23
|
const consecutivo = getConsecutivoStr(opts)
|
|
24
|
-
const fullInvoice =
|
|
24
|
+
const fullInvoice = parseElectronicBillXML(opts.xmlStr)
|
|
25
25
|
const msjObj = getMsjObj({ fullInvoice, consecutivo, tipoDocKey })
|
|
26
26
|
const xmlBase64 = await genXML(tipoDocKey, msjObj, {
|
|
27
27
|
buffer: pemOpt.buffer,
|
package/src/electronicBill.ts
CHANGED
|
@@ -2,9 +2,10 @@ import { ClientPayload, FinalMessagePerson } from '@src/types/globalInterfaces'
|
|
|
2
2
|
import { genClaveObj, genString, parseOptions } from '@src/lib/genClave/index'
|
|
3
3
|
import genJSON from '@src/lib/genJSON/index'
|
|
4
4
|
import { send } from '@src/services/send/index'
|
|
5
|
+
import { Persona } from './types/facturaInterfaces'
|
|
5
6
|
|
|
6
7
|
const DEFAULT_VALUES = {
|
|
7
|
-
tipoIdentificacion: '01'
|
|
8
|
+
tipoIdentificacion: '01' as Persona['Identificacion']['Tipo']
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
const encodeXML = (xmlStr: string): string => {
|
|
@@ -6,7 +6,7 @@ const DEFAULT_VALUES = {
|
|
|
6
6
|
message: 'Default msj',
|
|
7
7
|
detailsMessage: 'Default details msj',
|
|
8
8
|
taxes: 100,
|
|
9
|
-
tipoIdentificacion: '01'
|
|
9
|
+
tipoIdentificacion: '01' as Persona['Identificacion']['Tipo']
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function getSimpleSender(frontEndRequest: ClientPayload): FinalMessagePerson {
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export { parseElectronicBillXML as xmlToJson } from './lib/genXML'
|
|
2
2
|
export { Document, InvoiceDocumentContainer, DetalleServicio, Resumen, Persona } from './types/facturaInterfaces'
|
|
3
3
|
export { CreateDocumentInput } from './ATV/core/CreateDocFactory'
|
|
4
4
|
export { ATV } from './ATV'
|
|
@@ -31,17 +31,17 @@ export function getMsjObj(opts: {
|
|
|
31
31
|
export function getFinalMessage(fullInvoice: any, consecutivoReceptor: string, xmlBase64: any): any {
|
|
32
32
|
const date = new Date()
|
|
33
33
|
return {
|
|
34
|
-
clave: fullInvoice.Clave,
|
|
35
|
-
fecha: date.toISOString(),
|
|
36
|
-
emisor: {
|
|
34
|
+
clave: fullInvoice.Clave, // in command
|
|
35
|
+
fecha: date.toISOString(), // in command
|
|
36
|
+
emisor: { // in command
|
|
37
37
|
tipoIdentificacion: fullInvoice.Receptor.Identificacion.Tipo,
|
|
38
38
|
numeroIdentificacion: fullInvoice.Receptor.Identificacion.Numero
|
|
39
39
|
},
|
|
40
|
-
receptor: {
|
|
40
|
+
receptor: { // in command
|
|
41
41
|
tipoIdentificacion: fullInvoice.Emisor.Identificacion.Tipo,
|
|
42
42
|
numeroIdentificacion: fullInvoice.Emisor.Identificacion.Numero
|
|
43
43
|
},
|
|
44
44
|
consecutivoReceptor,
|
|
45
|
-
comprobanteXml: xmlBase64
|
|
45
|
+
comprobanteXml: xmlBase64 // in command
|
|
46
46
|
}
|
|
47
47
|
}
|
package/src/lib/genJSON/index.ts
CHANGED
package/src/lib/genXML/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { j2xParser, parse } from 'fast-xml-parser'
|
|
2
2
|
import { declaration, defaultOptions, xmlExtructures } from '@src/lib/genXML/xmlConfig'
|
|
3
3
|
import sigXML from '@src/lib/genXML/sigXML/index'
|
|
4
|
+
import { XMLRawDocument } from '@src/types/facturaInterfaces'
|
|
4
5
|
|
|
5
6
|
export const objToXML = (xmlStructure: string, obj: object): string => {
|
|
6
7
|
const parser = new j2xParser(defaultOptions) // eslint-disable-line new-cap
|
|
@@ -21,7 +22,7 @@ export async function genXML(xmlStructure: string, obj: object, options?: {
|
|
|
21
22
|
return signedXML
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
export const
|
|
25
|
+
export const parseElectronicBillXML = (xml: string): XMLRawDocument => {
|
|
25
26
|
try {
|
|
26
27
|
const json = parse(xml, {
|
|
27
28
|
ignoreAttributes: false,
|
|
@@ -51,7 +51,7 @@ export interface Resumen {
|
|
|
51
51
|
export interface Persona {
|
|
52
52
|
Nombre: string;
|
|
53
53
|
Identificacion: {
|
|
54
|
-
Tipo?:
|
|
54
|
+
Tipo?: '01' | '02';
|
|
55
55
|
Numero: string;
|
|
56
56
|
};
|
|
57
57
|
NombreComercial?: string;
|
|
@@ -104,8 +104,27 @@ export interface Document {
|
|
|
104
104
|
InformacionReferencia?: InformacionReferencia;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
export interface XMLRawDocument extends Document {
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
interface MensajeReceptor {
|
|
112
|
+
Clave: string;
|
|
113
|
+
NumeroCedulaEmisor: string;
|
|
114
|
+
FechaEmisionDoc: string;
|
|
115
|
+
Mensaje: string;
|
|
116
|
+
DetalleMensaje: string;
|
|
117
|
+
MontoTotalImpuesto: number;
|
|
118
|
+
CodigoActividad: string;
|
|
119
|
+
CondicionImpuesto: string;
|
|
120
|
+
MontoTotalDeGastoAplicable: number;
|
|
121
|
+
TotalFactura: number;
|
|
122
|
+
NumeroCedulaReceptor: string;
|
|
123
|
+
NumeroConsecutivoReceptor: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
107
126
|
export interface InvoiceDocumentContainer {
|
|
108
|
-
[key: string]: Document;
|
|
127
|
+
[key: string]: Document | MensajeReceptor;
|
|
109
128
|
}
|
|
110
129
|
|
|
111
130
|
export type ConfirmationMessageRaw = {
|
package/examples/confirmXML.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import confirmXML from '@src/confirmXML'
|
|
2
|
-
import { sendToCustomURL } from '@src/services/send/index'
|
|
3
|
-
import getToken from '@src/services/getToken'
|
|
4
|
-
import fs from 'fs'
|
|
5
|
-
|
|
6
|
-
const USERNAME_TEST = process.env.USERNAME_TEST
|
|
7
|
-
const PASSWORD_TEST = process.env.PASSWORD_TEST
|
|
8
|
-
|
|
9
|
-
const SOURCE_P12_URI = process.env.SOURCE_P12_URI
|
|
10
|
-
const SOURCE_P12_PASSPORT = process.env.SOURCE_P12_PASSPORT
|
|
11
|
-
const XML_TO_CONFIRM = process.env.XML_TO_CONFIRM
|
|
12
|
-
|
|
13
|
-
function decodeBase64(encodedStr: string): string {
|
|
14
|
-
const buff = Buffer.from(encodedStr, 'base64')
|
|
15
|
-
return buff.toString('ascii')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function getConfimation(token: string, data: any, ms: number): Promise<any> {
|
|
19
|
-
return new Promise((resolve, reject): any => {
|
|
20
|
-
setTimeout(() => {
|
|
21
|
-
const location = data.headers.location
|
|
22
|
-
console.log('location', location)
|
|
23
|
-
sendToCustomURL(token, location)
|
|
24
|
-
.then(data => resolve(data))
|
|
25
|
-
.catch(err => reject(err))
|
|
26
|
-
}, ms)
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async function main(): Promise<void> {
|
|
31
|
-
try {
|
|
32
|
-
const pem = fs.readFileSync(SOURCE_P12_URI, 'binary')
|
|
33
|
-
const xml = fs.readFileSync(XML_TO_CONFIRM, 'utf-8')
|
|
34
|
-
const token = await getToken({
|
|
35
|
-
client_id: 'api-stag', // eslint-disable-line
|
|
36
|
-
client_secret: '', // eslint-disable-line
|
|
37
|
-
grant_type: 'password', // eslint-disable-line
|
|
38
|
-
username: USERNAME_TEST,
|
|
39
|
-
password: PASSWORD_TEST
|
|
40
|
-
})
|
|
41
|
-
const consecutivo = {
|
|
42
|
-
consecutivo: '0000000013'
|
|
43
|
-
}
|
|
44
|
-
const data = await confirmXML({
|
|
45
|
-
token: token.data.access_token,
|
|
46
|
-
consecutivo,
|
|
47
|
-
xmlStr: xml,
|
|
48
|
-
tipoDocKey: 'CCE',
|
|
49
|
-
pemOpt: {
|
|
50
|
-
buffer: pem,
|
|
51
|
-
password: SOURCE_P12_PASSPORT
|
|
52
|
-
}
|
|
53
|
-
})
|
|
54
|
-
if (data) {
|
|
55
|
-
const secondResponse = await getConfimation(token.data.access_token, data, 10000)
|
|
56
|
-
.catch(err => {
|
|
57
|
-
const response = err.response || {}
|
|
58
|
-
console.log('response', response)
|
|
59
|
-
})
|
|
60
|
-
const XMLResponse = secondResponse.data['respuesta-xml']
|
|
61
|
-
if (!XMLResponse) {
|
|
62
|
-
const state = secondResponse.data['ind-estado']
|
|
63
|
-
console.log('state', state)
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
const text = decodeBase64(XMLResponse)
|
|
67
|
-
console.log('secondResponse', text)
|
|
68
|
-
} else {
|
|
69
|
-
console.log('no data', data)
|
|
70
|
-
}
|
|
71
|
-
} catch (error) {
|
|
72
|
-
console.warn(error.code)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
main()
|