diff --git a/.gitignore b/.gitignore index 695d4b8..afc002c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,8 @@ node_modules passModels/ certificates/ *.code-workspace -.vscode/ +.vscode/* +!.vscode/settings.json *.js lib/ examples/build diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..c2dd81d --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "bracketSpacing": true, + "trailingComma": "all", + "tabWidth": 4, + "useTabs": true +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..2e8a291 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.tabSize": 4, + "editor.formatOnSave": true, + "editor.insertSpaces": false, + "editor.smoothScrolling": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" +} diff --git a/examples/README.md b/examples/README.md index 9c6d87a..e18c4bb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,7 +2,7 @@ This is examples folder. These examples are used to test new features and as sample showcases. -Each example is linked to webserver.js, which *requires* express.js to run. +Each example is linked to webserver.js, which _requires_ express.js to run. Express.js has been inserted as "example package" dipendency. ```sh @@ -18,6 +18,7 @@ To make them work, you'll have to edit both certificates and model path. Visit [http://localhost:8080/gen/examplePass](http://localhost:8080/gen/examplePass) to get the pass. Replace "examplePass" with the pass name in models folder. Please note that `field.js` example will force you to download `exampleBooking.pass`, no matter what. -___ + +--- Every contribution is really appreciated. ❤️ Thank you! diff --git a/examples/abstractModel.ts b/examples/abstractModel.ts index 0ce9ee2..5eb3c7a 100644 --- a/examples/abstractModel.ts +++ b/examples/abstractModel.ts @@ -1,5 +1,9 @@ import genRoute, { app } from "./webserver"; -import { createPass, createAbstractModel, AbstractModel } from "passkit-generator"; +import { + createPass, + createAbstractModel, + AbstractModel, +} from "passkit-generator"; let abstractModel: AbstractModel; @@ -11,148 +15,182 @@ let abstractModel: AbstractModel; signerCert: "../certificates/signerCert.pem", signerKey: { keyFile: "../certificates/signerKey.pem", - passphrase: "123456" - } + passphrase: "123456", + }, }, // overrides: request.body || request.params || request.query, }); })(); genRoute.all(async function manageRequest(request, response) { - const passName = request.params.modelName + "_" + (new Date()).toISOString().split('T')[0].replace(/-/ig, ""); + const passName = + request.params.modelName + + "_" + + new Date().toISOString().split("T")[0].replace(/-/gi, ""); try { const pass = await createPass(abstractModel); pass.transitType = "PKTransitTypeAir"; - pass.headerFields.push({ - "key": "header1", - "label": "Data", - "value": "25 mag", - "textAlignment": "PKTextAlignmentCenter" - }, { - "key": "header2", - "label": "Volo", - "value": "EZY997", - "textAlignment": "PKTextAlignmentCenter" - }); + pass.headerFields.push( + { + key: "header1", + label: "Data", + value: "25 mag", + textAlignment: "PKTextAlignmentCenter", + }, + { + key: "header2", + label: "Volo", + value: "EZY997", + textAlignment: "PKTextAlignmentCenter", + }, + ); - pass.primaryFields.push({ - key: "IATA-source", - value: "NAP", - label: "Napoli", - textAlignment: "PKTextAlignmentLeft" - }, { - key: "IATA-destination", - value: "VCE", - label: "Venezia Marco Polo", - textAlignment: "PKTextAlignmentRight" - }); + pass.primaryFields.push( + { + key: "IATA-source", + value: "NAP", + label: "Napoli", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "IATA-destination", + value: "VCE", + label: "Venezia Marco Polo", + textAlignment: "PKTextAlignmentRight", + }, + ); - pass.secondaryFields.push({ - "key": "secondary1", - "label": "Imbarco chiuso", - "value": "18:40", - "textAlignment": "PKTextAlignmentCenter", - }, { - "key": "sec2", - "label": "Partenze", - "value": "19:10", - "textAlignment": "PKTextAlignmentCenter" - }, { - "key": "sec3", - "label": "SB", - "value": "Sì", - "textAlignment": "PKTextAlignmentCenter" - }, { - "key": "sec4", - "label": "Imbarco", - "value": "Anteriore", - "textAlignment": "PKTextAlignmentCenter" - }); + pass.secondaryFields.push( + { + key: "secondary1", + label: "Imbarco chiuso", + value: "18:40", + textAlignment: "PKTextAlignmentCenter", + }, + { + key: "sec2", + label: "Partenze", + value: "19:10", + textAlignment: "PKTextAlignmentCenter", + }, + { + key: "sec3", + label: "SB", + value: "Sì", + textAlignment: "PKTextAlignmentCenter", + }, + { + key: "sec4", + label: "Imbarco", + value: "Anteriore", + textAlignment: "PKTextAlignmentCenter", + }, + ); - pass.auxiliaryFields.push({ - "key": "aux1", - "label": "Passeggero", - "value": "MR. WHO KNOWS", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "aux2", - "label": "Posto", - "value": "1A*", - "textAlignment": "PKTextAlignmentCenter" - }); + pass.auxiliaryFields.push( + { + key: "aux1", + label: "Passeggero", + value: "MR. WHO KNOWS", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "aux2", + label: "Posto", + value: "1A*", + textAlignment: "PKTextAlignmentCenter", + }, + ); - pass.backFields.push({ - "key": "document number", - "label": "Numero documento:", - "value": "- -", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "You're checked in, what next", - "label": "Hai effettuato il check-in, Quali sono le prospettive", - "value": "", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "Check In", - "label": "1. check-in✓", - "value": "", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "checkIn", - "label": "", - "value": "Le uscite d'imbarco chiudono 30 minuti prima della partenza, quindi sii puntuale. In questo aeroporto puoi utilizzare la corsia Fast Track ai varchi di sicurezza.", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "2. Bags", - "label": "2. Bagaglio", - "value": "", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "Require special assistance", - "label": "Assistenza speciale", - "value": "Se hai richiesto assistenza speciale, presentati a un membro del personale nell'area di Consegna bagagli almeno 90 minuti prima del volo.", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "3. Departures", - "label": "3. Partenze", - "value": "", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "photoId", - "label": "Un documento d’identità corredato di fotografia", - "value": "è obbligatorio su TUTTI i voli. Per un viaggio internazionale è necessario un passaporto valido o, dove consentita, una carta d’identità.", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "yourSeat", - "label": "Il tuo posto:", - "value": "verifica il tuo numero di posto nella parte superiore. Durante l’imbarco utilizza le scale anteriori e posteriori: per le file 1-10 imbarcati dalla parte anteriore; per le file 11-31 imbarcati dalla parte posteriore. Colloca le borse di dimensioni ridotte sotto il sedile davanti a te.", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "Pack safely", - "label": "Bagaglio sicuro", - "value": "Fai clic http://easyjet.com/it/articoli-pericolosi per maggiori informazioni sulle merci pericolose oppure visita il sito CAA http://www.caa.co.uk/default.aspx?catid=2200", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "Thank you for travelling easyJet", - "label": "Grazie per aver viaggiato con easyJet", - "value": "", - "textAlignment": "PKTextAlignmentLeft" - }); + pass.backFields.push( + { + key: "document number", + label: "Numero documento:", + value: "- -", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "You're checked in, what next", + label: "Hai effettuato il check-in, Quali sono le prospettive", + value: "", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "Check In", + label: "1. check-in✓", + value: "", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "checkIn", + label: "", + value: + "Le uscite d'imbarco chiudono 30 minuti prima della partenza, quindi sii puntuale. In questo aeroporto puoi utilizzare la corsia Fast Track ai varchi di sicurezza.", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "2. Bags", + label: "2. Bagaglio", + value: "", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "Require special assistance", + label: "Assistenza speciale", + value: + "Se hai richiesto assistenza speciale, presentati a un membro del personale nell'area di Consegna bagagli almeno 90 minuti prima del volo.", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "3. Departures", + label: "3. Partenze", + value: "", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "photoId", + label: "Un documento d’identità corredato di fotografia", + value: + "è obbligatorio su TUTTI i voli. Per un viaggio internazionale è necessario un passaporto valido o, dove consentita, una carta d’identità.", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "yourSeat", + label: "Il tuo posto:", + value: + "verifica il tuo numero di posto nella parte superiore. Durante l’imbarco utilizza le scale anteriori e posteriori: per le file 1-10 imbarcati dalla parte anteriore; per le file 11-31 imbarcati dalla parte posteriore. Colloca le borse di dimensioni ridotte sotto il sedile davanti a te.", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "Pack safely", + label: "Bagaglio sicuro", + value: + "Fai clic http://easyjet.com/it/articoli-pericolosi per maggiori informazioni sulle merci pericolose oppure visita il sito CAA http://www.caa.co.uk/default.aspx?catid=2200", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "Thank you for travelling easyJet", + label: "Grazie per aver viaggiato con easyJet", + value: "", + textAlignment: "PKTextAlignmentLeft", + }, + ); const stream = pass.generate(); response.set({ "Content-type": "application/vnd.apple.pkpass", - "Content-disposition": `attachment; filename=${passName}.pkpass` + "Content-disposition": `attachment; filename=${passName}.pkpass`, }); stream.pipe(response); - } catch(err) { + } catch (err) { console.log(err); response.set({ - "Content-type": "text/html" + "Content-type": "text/html", }); response.send(err.message); diff --git a/examples/additionalBuffers.ts b/examples/additionalBuffers.ts index 8d6c750..1f30f38 100644 --- a/examples/additionalBuffers.ts +++ b/examples/additionalBuffers.ts @@ -11,12 +11,14 @@ import fetch from "node-fetch"; import { createPass } from "passkit-generator"; app.all(async function manageRequest(request, response) { - let passName = request.params.modelName + "_" + (new Date()).toISOString().split('T')[0].replace(/-/ig, ""); + let passName = + request.params.modelName + + "_" + + new Date().toISOString().split("T")[0].replace(/-/gi, ""); - const avatar = await ( - fetch("https://s.gravatar.com/avatar/83cd11399b7ea79977bc302f3931ee52?size=32&default=retro") - .then(res => res.buffer()) - ); + const avatar = await fetch( + "https://s.gravatar.com/avatar/83cd11399b7ea79977bc302f3931ee52?size=32&default=retro", + ).then((res) => res.buffer()); const passConfig = { model: `./models/${request.params.modelName}`, @@ -25,8 +27,8 @@ app.all(async function manageRequest(request, response) { signerCert: "../certificates/signerCert.pem", signerKey: { keyFile: "../certificates/signerKey.pem", - passphrase: "123456" - } + passphrase: "123456", + }, }, overrides: request.body || request.params || request.query, }; @@ -44,7 +46,7 @@ app.all(async function manageRequest(request, response) { response.set({ "Content-type": "application/vnd.apple.pkpass", - "Content-disposition": `attachment; filename=${passName}.pkpass` + "Content-disposition": `attachment; filename=${passName}.pkpass`, }); stream.pipe(response); diff --git a/examples/barcode.ts b/examples/barcode.ts index 1d3f59f..17302bb 100644 --- a/examples/barcode.ts +++ b/examples/barcode.ts @@ -12,7 +12,10 @@ import app from "./webserver"; import { createPass } from "passkit-generator"; app.all(async function manageRequest(request, response) { - const passName = request.params.modelName + "_" + (new Date()).toISOString().split('T')[0].replace(/-/ig, ""); + const passName = + request.params.modelName + + "_" + + new Date().toISOString().split("T")[0].replace(/-/gi, ""); try { const pass = await createPass({ @@ -22,8 +25,8 @@ app.all(async function manageRequest(request, response) { signerCert: "../certificates/signerCert.pem", signerKey: { keyFile: "../certificates/signerKey.pem", - passphrase: "123456" - } + passphrase: "123456", + }, }, overrides: request.body || request.params || request.query, }); @@ -37,17 +40,21 @@ app.all(async function manageRequest(request, response) { // After this, pass.props["barcodes"] will have support for just two of three // of the passed format (the valid ones); - pass.barcodes({ - message: "Thank you for using this package <3", - format: "PKBarcodeFormatCode128" - }, { - message: "Thank you for using this package <3", - format: "PKBarcodeFormatPDF417" - }, { - message: "Thank you for using this package <3", - // @ts-expect-error - format: "PKBarcodeFormatMock44617" - }); + pass.barcodes( + { + message: "Thank you for using this package <3", + format: "PKBarcodeFormatCode128", + }, + { + message: "Thank you for using this package <3", + format: "PKBarcodeFormatPDF417", + }, + { + message: "Thank you for using this package <3", + // @ts-expect-error + format: "PKBarcodeFormatMock44617", + }, + ); } // You can change the format chosen for barcode prop support by calling .barcode() @@ -57,12 +64,15 @@ app.all(async function manageRequest(request, response) { pass.barcode("PKBarcodeFormatPDF417"); console.log("Barcode property is now:", pass.props["barcode"]); - console.log("Barcodes support is autocompleted:", pass.props["barcodes"]); + console.log( + "Barcodes support is autocompleted:", + pass.props["barcodes"], + ); const stream = pass.generate(); response.set({ "Content-type": "application/vnd.apple.pkpass", - "Content-disposition": `attachment; filename=${passName}.pkpass` + "Content-disposition": `attachment; filename=${passName}.pkpass`, }); stream.pipe(response); diff --git a/examples/expiration.ts b/examples/expiration.ts index ca650a7..76ac077 100644 --- a/examples/expiration.ts +++ b/examples/expiration.ts @@ -13,11 +13,16 @@ import { createPass } from "passkit-generator"; app.all(async function manageRequest(request, response) { if (!request.query.fn) { - response.send("Generate a voided pass.
Generate a pass with expiration date"); + response.send( + "Generate a voided pass.
Generate a pass with expiration date", + ); return; } - let passName = request.params.modelName + "_" + (new Date()).toISOString().split('T')[0].replace(/-/ig, ""); + let passName = + request.params.modelName + + "_" + + new Date().toISOString().split("T")[0].replace(/-/gi, ""); try { let pass = await createPass({ @@ -27,8 +32,8 @@ app.all(async function manageRequest(request, response) { signerCert: "../certificates/signerCert.pem", signerKey: { keyFile: "../certificates/signerKey.pem", - passphrase: "123456" - } + passphrase: "123456", + }, }, overrides: request.body || request.params || request.query, }); @@ -47,7 +52,7 @@ app.all(async function manageRequest(request, response) { const stream = pass.generate(); response.set({ "Content-type": "application/vnd.apple.pkpass", - "Content-disposition": `attachment; filename=${passName}.pkpass` + "Content-disposition": `attachment; filename=${passName}.pkpass`, }); stream.pipe(response); diff --git a/examples/fields.ts b/examples/fields.ts index 241b552..98fce6e 100644 --- a/examples/fields.ts +++ b/examples/fields.ts @@ -13,7 +13,10 @@ import app from "./webserver"; import { createPass } from "passkit-generator"; app.all(async function manageRequest(request, response) { - let passName = "exampleBooking" + "_" + (new Date()).toISOString().split('T')[0].replace(/-/ig, ""); + let passName = + "exampleBooking" + + "_" + + new Date().toISOString().split("T")[0].replace(/-/gi, ""); try { let pass = await createPass({ model: `./models/exampleBooking`, @@ -22,133 +25,164 @@ app.all(async function manageRequest(request, response) { signerCert: "../certificates/signerCert.pem", signerKey: { keyFile: "../certificates/signerKey.pem", - passphrase: "123456" - } + passphrase: "123456", + }, }, overrides: request.body || request.params || request.query, }); pass.transitType = "PKTransitTypeAir"; - pass.headerFields.push({ - "key": "header1", - "label": "Data", - "value": "25 mag", - "textAlignment": "PKTextAlignmentCenter" - }, { - "key": "header2", - "label": "Volo", - "value": "EZY997", - "textAlignment": "PKTextAlignmentCenter" - }); + pass.headerFields.push( + { + key: "header1", + label: "Data", + value: "25 mag", + textAlignment: "PKTextAlignmentCenter", + }, + { + key: "header2", + label: "Volo", + value: "EZY997", + textAlignment: "PKTextAlignmentCenter", + }, + ); - pass.primaryFields.push({ - key: "IATA-source", - value: "NAP", - label: "Napoli", - textAlignment: "PKTextAlignmentLeft" - }, { - key: "IATA-destination", - value: "VCE", - label: "Venezia Marco Polo", - textAlignment: "PKTextAlignmentRight" - }); + pass.primaryFields.push( + { + key: "IATA-source", + value: "NAP", + label: "Napoli", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "IATA-destination", + value: "VCE", + label: "Venezia Marco Polo", + textAlignment: "PKTextAlignmentRight", + }, + ); - pass.secondaryFields.push({ - "key": "secondary1", - "label": "Imbarco chiuso", - "value": "18:40", - "textAlignment": "PKTextAlignmentCenter", - }, { - "key": "sec2", - "label": "Partenze", - "value": "19:10", - "textAlignment": "PKTextAlignmentCenter" - }, { - "key": "sec3", - "label": "SB", - "value": "Sì", - "textAlignment": "PKTextAlignmentCenter" - }, { - "key": "sec4", - "label": "Imbarco", - "value": "Anteriore", - "textAlignment": "PKTextAlignmentCenter" - }); + pass.secondaryFields.push( + { + key: "secondary1", + label: "Imbarco chiuso", + value: "18:40", + textAlignment: "PKTextAlignmentCenter", + }, + { + key: "sec2", + label: "Partenze", + value: "19:10", + textAlignment: "PKTextAlignmentCenter", + }, + { + key: "sec3", + label: "SB", + value: "Sì", + textAlignment: "PKTextAlignmentCenter", + }, + { + key: "sec4", + label: "Imbarco", + value: "Anteriore", + textAlignment: "PKTextAlignmentCenter", + }, + ); - pass.auxiliaryFields.push({ - "key": "aux1", - "label": "Passeggero", - "value": "MR. WHO KNOWS", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "aux2", - "label": "Posto", - "value": "1A*", - "textAlignment": "PKTextAlignmentCenter" - }); + pass.auxiliaryFields.push( + { + key: "aux1", + label: "Passeggero", + value: "MR. WHO KNOWS", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "aux2", + label: "Posto", + value: "1A*", + textAlignment: "PKTextAlignmentCenter", + }, + ); - pass.backFields.push({ - "key": "document number", - "label": "Numero documento:", - "value": "- -", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "You're checked in, what next", - "label": "Hai effettuato il check-in, Quali sono le prospettive", - "value": "", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "Check In", - "label": "1. check-in✓", - "value": "", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "checkIn", - "label": "", - "value": "Le uscite d'imbarco chiudono 30 minuti prima della partenza, quindi sii puntuale. In questo aeroporto puoi utilizzare la corsia Fast Track ai varchi di sicurezza.", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "2. Bags", - "label": "2. Bagaglio", - "value": "", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "Require special assistance", - "label": "Assistenza speciale", - "value": "Se hai richiesto assistenza speciale, presentati a un membro del personale nell'area di Consegna bagagli almeno 90 minuti prima del volo.", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "3. Departures", - "label": "3. Partenze", - "value": "", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "photoId", - "label": "Un documento d’identità corredato di fotografia", - "value": "è obbligatorio su TUTTI i voli. Per un viaggio internazionale è necessario un passaporto valido o, dove consentita, una carta d’identità.", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "yourSeat", - "label": "Il tuo posto:", - "value": "verifica il tuo numero di posto nella parte superiore. Durante l’imbarco utilizza le scale anteriori e posteriori: per le file 1-10 imbarcati dalla parte anteriore; per le file 11-31 imbarcati dalla parte posteriore. Colloca le borse di dimensioni ridotte sotto il sedile davanti a te.", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "Pack safely", - "label": "Bagaglio sicuro", - "value": "Fai clic http://easyjet.com/it/articoli-pericolosi per maggiori informazioni sulle merci pericolose oppure visita il sito CAA http://www.caa.co.uk/default.aspx?catid=2200", - "textAlignment": "PKTextAlignmentLeft" - }, { - "key": "Thank you for travelling easyJet", - "label": "Grazie per aver viaggiato con easyJet", - "value": "", - "textAlignment": "PKTextAlignmentLeft" - }); + pass.backFields.push( + { + key: "document number", + label: "Numero documento:", + value: "- -", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "You're checked in, what next", + label: "Hai effettuato il check-in, Quali sono le prospettive", + value: "", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "Check In", + label: "1. check-in✓", + value: "", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "checkIn", + label: "", + value: + "Le uscite d'imbarco chiudono 30 minuti prima della partenza, quindi sii puntuale. In questo aeroporto puoi utilizzare la corsia Fast Track ai varchi di sicurezza.", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "2. Bags", + label: "2. Bagaglio", + value: "", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "Require special assistance", + label: "Assistenza speciale", + value: + "Se hai richiesto assistenza speciale, presentati a un membro del personale nell'area di Consegna bagagli almeno 90 minuti prima del volo.", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "3. Departures", + label: "3. Partenze", + value: "", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "photoId", + label: "Un documento d’identità corredato di fotografia", + value: + "è obbligatorio su TUTTI i voli. Per un viaggio internazionale è necessario un passaporto valido o, dove consentita, una carta d’identità.", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "yourSeat", + label: "Il tuo posto:", + value: + "verifica il tuo numero di posto nella parte superiore. Durante l’imbarco utilizza le scale anteriori e posteriori: per le file 1-10 imbarcati dalla parte anteriore; per le file 11-31 imbarcati dalla parte posteriore. Colloca le borse di dimensioni ridotte sotto il sedile davanti a te.", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "Pack safely", + label: "Bagaglio sicuro", + value: + "Fai clic http://easyjet.com/it/articoli-pericolosi per maggiori informazioni sulle merci pericolose oppure visita il sito CAA http://www.caa.co.uk/default.aspx?catid=2200", + textAlignment: "PKTextAlignmentLeft", + }, + { + key: "Thank you for travelling easyJet", + label: "Grazie per aver viaggiato con easyJet", + value: "", + textAlignment: "PKTextAlignmentLeft", + }, + ); const stream = pass.generate(); response.set({ "Content-type": "application/vnd.apple.pkpass", - "Content-disposition": `attachment; filename=${passName}.pkpass` + "Content-disposition": `attachment; filename=${passName}.pkpass`, }); stream.pipe(response); @@ -156,7 +190,7 @@ app.all(async function manageRequest(request, response) { console.log(err); response.set({ - "Content-type": "text/html" + "Content-type": "text/html", }); response.send(err.message); diff --git a/examples/localization.ts b/examples/localization.ts index 55cf44e..0723bfb 100644 --- a/examples/localization.ts +++ b/examples/localization.ts @@ -8,7 +8,10 @@ import app from "./webserver"; import { createPass } from "passkit-generator"; app.all(async function manageRequest(request, response) { - const passName = request.params.modelName + "_" + (new Date()).toISOString().split('T')[0].replace(/-/ig, ""); + const passName = + request.params.modelName + + "_" + + new Date().toISOString().split("T")[0].replace(/-/gi, ""); try { const pass = await createPass({ @@ -18,10 +21,10 @@ app.all(async function manageRequest(request, response) { signerCert: "../certificates/signerCert.pem", signerKey: { keyFile: "../certificates/signerKey.pem", - passphrase: "123456" - } + passphrase: "123456", + }, }, - overrides: request.body || request.params || request.query + overrides: request.body || request.params || request.query, }); // For each language you include, an .lproj folder in pass bundle @@ -38,30 +41,33 @@ app.all(async function manageRequest(request, response) { // Italian, already has an .lproj which gets included pass.localize("it", { - "EVENT": "Evento", - "LOCATION": "Dove" + EVENT: "Evento", + LOCATION: "Dove", }); // German, doesn't, so is created pass.localize("de", { - "EVENT": "Ereignis", - "LOCATION": "Ort" + EVENT: "Ereignis", + LOCATION: "Ort", }); // This language does not exist but is still added as .lproj folder pass.localize("zu", {}); // @ts-ignore - ignoring for logging purposes. Do not replicate - console.log("Added languages", Object.keys(pass.l10nTranslations).join(", ")) + console.log( + "Added languages", + Object.keys(pass.l10nTranslations).join(", "), + ); const stream = pass.generate(); response.set({ "Content-type": "application/vnd.apple.pkpass", - "Content-disposition": `attachment; filename=${passName}.pkpass` + "Content-disposition": `attachment; filename=${passName}.pkpass`, }); stream.pipe(response); - } catch(err) { + } catch (err) { console.log(err); response.set({ diff --git a/examples/models/examplePass.pass/pass.json b/examples/models/examplePass.pass/pass.json index 673f4fb..6b2f743 100644 --- a/examples/models/examplePass.pass/pass.json +++ b/examples/models/examplePass.pass/pass.json @@ -1,49 +1,51 @@ { - "formatVersion": 1, - "passTypeIdentifier": "pass.com.example.myapp", - "serialNumber": "nmyuxofgna", - "teamIdentifier": "F53WB8AE67", - "webServiceURL": "https://192.168.1.254:80/", - "authenticationToken": "vxwxd7J8AlNNFPS8k0a0FfUFtq0ewzFdc", - "relevantDate": "2011-12-08T13:00-08:00", - "locations": [ - { - "longitude": -122.3748889, - "latitude": 37.6189722 - }, - { - "longitude": -122.03118, - "latitude": 37.33182 - } - ], - "barcodes": [{ - "message": "123456789", - "format": "PKBarcodeFormatQR", - "messageEncoding": "iso-8859-1" - }], - "barcode": { - "message": "123456789", - "format": "PKBarcodeFormatQR", - "messageEncoding": "iso-8859-1" - }, - "organizationName": "Apple Inc.", - "description": "Apple Event Ticket", - "foregroundColor": "rgb(255, 255, 255)", - "backgroundColor": "rgb(60, 65, 76)", - "eventTicket": { - "primaryFields": [ - { - "key": "event", - "label": "EVENT", - "value": "The Beat Goes On" - } - ], - "secondaryFields": [ - { - "key": "loc", - "label": "LOCATION", - "value": "Moscone West" - } - ] - } + "formatVersion": 1, + "passTypeIdentifier": "pass.com.example.myapp", + "serialNumber": "nmyuxofgna", + "teamIdentifier": "F53WB8AE67", + "webServiceURL": "https://192.168.1.254:80/", + "authenticationToken": "vxwxd7J8AlNNFPS8k0a0FfUFtq0ewzFdc", + "relevantDate": "2011-12-08T13:00-08:00", + "locations": [ + { + "longitude": -122.3748889, + "latitude": 37.6189722 + }, + { + "longitude": -122.03118, + "latitude": 37.33182 + } + ], + "barcodes": [ + { + "message": "123456789", + "format": "PKBarcodeFormatQR", + "messageEncoding": "iso-8859-1" + } + ], + "barcode": { + "message": "123456789", + "format": "PKBarcodeFormatQR", + "messageEncoding": "iso-8859-1" + }, + "organizationName": "Apple Inc.", + "description": "Apple Event Ticket", + "foregroundColor": "rgb(255, 255, 255)", + "backgroundColor": "rgb(60, 65, 76)", + "eventTicket": { + "primaryFields": [ + { + "key": "event", + "label": "EVENT", + "value": "The Beat Goes On" + } + ], + "secondaryFields": [ + { + "key": "loc", + "label": "LOCATION", + "value": "Moscone West" + } + ] + } } diff --git a/examples/package-lock.json b/examples/package-lock.json index 8332f33..160d7f7 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -1,560 +1,560 @@ { - "name": "examples", - "version": "0.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@hapi/hoek": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.1.1.tgz", - "integrity": "sha512-CAEbWH7OIur6jEOzaai83jq3FmKmv4PmX1JYfs9IrYcGEVI/lyL1EXJGCj7eFVJ0bg5QR8LMxBlEtA+xKiLpFw==" - }, - "@hapi/topo": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz", - "integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@sideway/address": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.0.tgz", - "integrity": "sha512-wAH/JYRXeIFQRsxerIuLjgUu2Xszam+O5xKeatJ4oudShOOirfmsQ1D6LL54XOU2tizpCYku+s1wmU0SYdpoSA==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" - }, - "@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, - "@types/body-parser": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", - "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.32", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", - "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/express": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.0.tgz", - "integrity": "sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw==", - "dev": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.16.7", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.7.tgz", - "integrity": "sha512-847KvL8Q1y3TtFLRTXcVakErLJQgdpFSaq+k043xefz9raEf0C7HalpSY7OW5PyjCnY8P7bPW5t/Co9qqp+USg==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/range-parser": "*" - } - }, - "@types/mime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", - "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==", - "dev": true - }, - "@types/node": { - "version": "12.6.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.8.tgz", - "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==", - "dev": true - }, - "@types/node-fetch": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.0.tgz", - "integrity": "sha512-TLFRywthBgL68auWj+ziWu+vnmmcHCDFC/sqCOQf1xTz4hRq8cu79z8CtHU9lncExGBsB8fXA4TiLDLt6xvMzw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/range-parser": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", - "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", - "dev": true - }, - "@types/serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", - "dev": true, - "requires": { - "@types/express-serve-static-core": "*", - "@types/mime": "*" - } - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" - }, - "joi": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.3.0.tgz", - "integrity": "sha512-Qh5gdU6niuYbUIUV5ejbsMiiFmBdw8Kcp8Buj2JntszCkCfxJ9Cz76OtHxOZMPXrt5810iDIXs+n1nNVoquHgg==", - "requires": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.0", - "@sideway/formula": "^3.0.0", - "@sideway/pinpoint": "^2.0.0" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "requires": { - "mime-db": "1.40.0" - } - }, - "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - }, - "node-forge": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.2.tgz", - "integrity": "sha512-naKSScof4Wn+aoHU6HBsifh92Zeicm1GDQKd1vp3Y/kOi8ub0DozCa9KpvYNCXslFHYRmLNiqRopGdTGwNLpNw==" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "passkit-generator": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/passkit-generator/-/passkit-generator-2.0.5.tgz", - "integrity": "sha512-qcCc82IFfzRChQPDrm/KMhuoaO5hcnnz2M2XuI4nTad3h+RcRn/gZ0H2Zqmfta3CNutr0SHKuYzUxstyTttwGA==", - "requires": { - "debug": "^4.1.1", - "joi": "^17.2.1", - "moment": "^2.27.0", - "node-forge": "^0.9.2", - "yazl": "^2.5.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typescript": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", - "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "yazl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", - "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", - "requires": { - "buffer-crc32": "~0.2.3" - } - } - } + "name": "examples", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@hapi/hoek": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.1.1.tgz", + "integrity": "sha512-CAEbWH7OIur6jEOzaai83jq3FmKmv4PmX1JYfs9IrYcGEVI/lyL1EXJGCj7eFVJ0bg5QR8LMxBlEtA+xKiLpFw==" + }, + "@hapi/topo": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz", + "integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/address": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.0.tgz", + "integrity": "sha512-wAH/JYRXeIFQRsxerIuLjgUu2Xszam+O5xKeatJ4oudShOOirfmsQ1D6LL54XOU2tizpCYku+s1wmU0SYdpoSA==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", + "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, + "@types/body-parser": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", + "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.32", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", + "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/express": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.0.tgz", + "integrity": "sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.16.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.7.tgz", + "integrity": "sha512-847KvL8Q1y3TtFLRTXcVakErLJQgdpFSaq+k043xefz9raEf0C7HalpSY7OW5PyjCnY8P7bPW5t/Co9qqp+USg==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/range-parser": "*" + } + }, + "@types/mime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", + "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==", + "dev": true + }, + "@types/node": { + "version": "12.6.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.8.tgz", + "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==", + "dev": true + }, + "@types/node-fetch": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.0.tgz", + "integrity": "sha512-TLFRywthBgL68auWj+ziWu+vnmmcHCDFC/sqCOQf1xTz4hRq8cu79z8CtHU9lncExGBsB8fXA4TiLDLt6xvMzw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, + "@types/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + }, + "joi": { + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.3.0.tgz", + "integrity": "sha512-Qh5gdU6niuYbUIUV5ejbsMiiFmBdw8Kcp8Buj2JntszCkCfxJ9Cz76OtHxOZMPXrt5810iDIXs+n1nNVoquHgg==", + "requires": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.0", + "@sideway/formula": "^3.0.0", + "@sideway/pinpoint": "^2.0.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "node-forge": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.2.tgz", + "integrity": "sha512-naKSScof4Wn+aoHU6HBsifh92Zeicm1GDQKd1vp3Y/kOi8ub0DozCa9KpvYNCXslFHYRmLNiqRopGdTGwNLpNw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "passkit-generator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/passkit-generator/-/passkit-generator-2.0.5.tgz", + "integrity": "sha512-qcCc82IFfzRChQPDrm/KMhuoaO5hcnnz2M2XuI4nTad3h+RcRn/gZ0H2Zqmfta3CNutr0SHKuYzUxstyTttwGA==", + "requires": { + "debug": "^4.1.1", + "joi": "^17.2.1", + "moment": "^2.27.0", + "node-forge": "^0.9.2", + "yazl": "^2.5.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typescript": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "requires": { + "buffer-crc32": "~0.2.3" + } + } + } } diff --git a/examples/tsconfig.json b/examples/tsconfig.json index c6c7a01..d32b4a5 100644 --- a/examples/tsconfig.json +++ b/examples/tsconfig.json @@ -3,7 +3,5 @@ "compilerOptions": { "outDir": "build" }, - "exclude": [ - "node_modules" - ] + "exclude": ["node_modules"] } diff --git a/examples/webserver.ts b/examples/webserver.ts index 01141b9..5a4788c 100644 --- a/examples/webserver.ts +++ b/examples/webserver.ts @@ -9,7 +9,7 @@ export const app = express(); app.use(express.json()); -app.listen(8080, "0.0.0.0", function(request, response) { +app.listen(8080, "0.0.0.0", function (request, response) { console.log("Webserver started."); }); @@ -17,10 +17,11 @@ app.all("/", function (request, response) { response.redirect("/gen/"); }); -app.route("/gen") - .all((req, res) => { - res.set("Content-Type", "text/html"); - res.send("Cannot generate a pass. Specify a modelName in the url to continue.
Usage: /gen/modelName") - }); +app.route("/gen").all((req, res) => { + res.set("Content-Type", "text/html"); + res.send( + "Cannot generate a pass. Specify a modelName in the url to continue.
Usage: /gen/modelName", + ); +}); export default app.route("/gen/:modelName"); diff --git a/package-lock.json b/package-lock.json index 79c14db..7ad48ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -209,6 +209,12 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", diff --git a/package.json b/package.json index 4fa667d..96026e0 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@types/node-forge": "^0.9.7", "@types/yazl": "^2.4.2", "jasmine": "^3.6.4", + "prettier": "^2.2.1", "rimraf": "^3.0.2", "typescript": "^4.1.3" }, diff --git a/spec/factory.ts b/spec/factory.ts index 070d853..b41a07f 100644 --- a/spec/factory.ts +++ b/spec/factory.ts @@ -6,7 +6,7 @@ import * as path from "path"; describe("createPass", () => { it("should throw if first argument is not provided", async () => { await expectAsync(createPass(undefined)).toBeRejectedWithError( - formatMessage("CP_NO_OPTS") + formatMessage("CP_NO_OPTS"), ); }); @@ -27,71 +27,144 @@ describe("createPass", () => { if (certificatesPath) { it("should return a pass instance", async () => { - await expectAsync(createPass({ - model: path.resolve(__dirname, "../examples/models/exampleBooking.pass"), - certificates: { - signerCert: path.resolve(__dirname, certificatesPath, "./signerCert.pem"), - signerKey: { - keyFile: path.resolve(__dirname, certificatesPath, "./signerKey.pem"), - passphrase: "password1234" + await expectAsync( + createPass({ + model: path.resolve( + __dirname, + "../examples/models/exampleBooking.pass", + ), + certificates: { + signerCert: path.resolve( + __dirname, + certificatesPath, + "./signerCert.pem", + ), + signerKey: { + keyFile: path.resolve( + __dirname, + certificatesPath, + "./signerKey.pem", + ), + passphrase: "password1234", + }, + wwdr: path.resolve( + __dirname, + certificatesPath, + "./WWDR.pem", + ), }, - wwdr: path.resolve(__dirname, certificatesPath, "./WWDR.pem"), - } - })).toBeResolved(); + }), + ).toBeResolved(); }); describe("Model validation", () => { it("Should reject with non valid model", async () => { - await expectAsync(createPass({ - // @ts-expect-error - model: 0, - certificates: { - signerCert: path.resolve(__dirname, certificatesPath, "./signerCert.pem"), - signerKey: { - keyFile: path.resolve(__dirname, certificatesPath, "./signerKey.pem"), - passphrase: "password1234" + await expectAsync( + createPass({ + // @ts-expect-error + model: 0, + certificates: { + signerCert: path.resolve( + __dirname, + certificatesPath, + "./signerCert.pem", + ), + signerKey: { + keyFile: path.resolve( + __dirname, + certificatesPath, + "./signerKey.pem", + ), + passphrase: "password1234", + }, + wwdr: path.resolve( + __dirname, + certificatesPath, + "./WWDR.pem", + ), }, - wwdr: path.resolve(__dirname, certificatesPath, "./WWDR.pem"), - } - })).toBeRejected(); + }), + ).toBeRejected(); - await expectAsync(createPass({ - model: undefined, - certificates: { - signerCert: path.resolve(__dirname, certificatesPath, "./signerCert.pem"), - signerKey: { - keyFile: path.resolve(__dirname, certificatesPath, "./signerKey.pem"), - passphrase: "password1234" + await expectAsync( + createPass({ + model: undefined, + certificates: { + signerCert: path.resolve( + __dirname, + certificatesPath, + "./signerCert.pem", + ), + signerKey: { + keyFile: path.resolve( + __dirname, + certificatesPath, + "./signerKey.pem", + ), + passphrase: "password1234", + }, + wwdr: path.resolve( + __dirname, + certificatesPath, + "./WWDR.pem", + ), }, - wwdr: path.resolve(__dirname, certificatesPath, "./WWDR.pem"), - } - })).toBeRejected(); + }), + ).toBeRejected(); - await expectAsync(createPass({ - model: null, - certificates: { - signerCert: path.resolve(__dirname, certificatesPath, "./signerCert.pem"), - signerKey: { - keyFile: path.resolve(__dirname, certificatesPath, "./signerKey.pem"), - passphrase: "password1234" + await expectAsync( + createPass({ + model: null, + certificates: { + signerCert: path.resolve( + __dirname, + certificatesPath, + "./signerCert.pem", + ), + signerKey: { + keyFile: path.resolve( + __dirname, + certificatesPath, + "./signerKey.pem", + ), + passphrase: "password1234", + }, + wwdr: path.resolve( + __dirname, + certificatesPath, + "./WWDR.pem", + ), }, - wwdr: path.resolve(__dirname, certificatesPath, "./WWDR.pem"), - } - })).toBeRejected(); + }), + ).toBeRejected(); - await expectAsync(createPass({ - model: {}, - certificates: { - signerCert: path.resolve(__dirname, certificatesPath, "./signerCert.pem"), - signerKey: { - keyFile: path.resolve(__dirname, certificatesPath, "./signerKey.pem"), - passphrase: "password1234" + await expectAsync( + createPass({ + model: {}, + certificates: { + signerCert: path.resolve( + __dirname, + certificatesPath, + "./signerCert.pem", + ), + signerKey: { + keyFile: path.resolve( + __dirname, + certificatesPath, + "./signerKey.pem", + ), + passphrase: "password1234", + }, + wwdr: path.resolve( + __dirname, + certificatesPath, + "./WWDR.pem", + ), }, - wwdr: path.resolve(__dirname, certificatesPath, "./WWDR.pem"), - } - })).toBeRejected(); + }), + ).toBeRejected(); }); }); } - } catch (err) { } + } catch (err) {} }); diff --git a/spec/index.ts b/spec/index.ts index a69b319..3c952b7 100644 --- a/spec/index.ts +++ b/spec/index.ts @@ -9,16 +9,25 @@ describe("Passkit-generator", function () { let pass: Pass; beforeEach(async () => { pass = await createPass({ - model: path.resolve(__dirname, "../examples/models/examplePass.pass"), + model: path.resolve( + __dirname, + "../examples/models/examplePass.pass", + ), certificates: { wwdr: path.resolve(__dirname, "../certificates/WWDR.pem"), - signerCert: path.resolve(__dirname, "../certificates/signerCert.pem"), + signerCert: path.resolve( + __dirname, + "../certificates/signerCert.pem", + ), signerKey: { - keyFile: path.resolve(__dirname, "../certificates/signerKey.pem"), - passphrase: "123456" - } + keyFile: path.resolve( + __dirname, + "../certificates/signerKey.pem", + ), + passphrase: "123456", + }, }, - overrides: {} + overrides: {}, }); }); @@ -48,7 +57,7 @@ describe("Passkit-generator", function () { it("Will apply changes if a second object argument with translations is passed", () => { pass.localize("it", { - "Test": "Prova" + Test: "Prova", }); expect(typeof pass["l10nTranslations"]["it"]).toBe("object"); @@ -99,16 +108,21 @@ describe("Passkit-generator", function () { describe("locations()", () => { it("Won't apply changes if invalid location objects are passed", () => { const props = pass.props["locations"] || []; - const oldAmountOfLocations = props && props.length || 0; + const oldAmountOfLocations = (props && props.length) || 0; - pass.locations({ - // @ts-expect-error - "ibrupofene": "no", - "longitude": 0.00000000 - }, ...props); + pass.locations( + { + // @ts-expect-error + ibrupofene: "no", + longitude: 0.0, + }, + ...props, + ); if (oldAmountOfLocations) { - expect(pass.props["locations"].length).toBe(oldAmountOfLocations); + expect(pass.props["locations"].length).toBe( + oldAmountOfLocations, + ); } else { expect(pass.props["locations"]).toBe(undefined); } @@ -116,33 +130,42 @@ describe("Passkit-generator", function () { it("Will filter out invalid location objects", () => { const props = pass.props["locations"] || []; - const oldAmountOfLocations = props && props.length || 0; + const oldAmountOfLocations = (props && props.length) || 0; - pass.locations({ - // @ts-expect-error - "ibrupofene": "no", - "longitude": 0.00000000 - }, { - "longitude": 4.42634523, - "latitude": 5.344233323352 - }, ...(pass.props["locations"] || [])); + pass.locations( + { + // @ts-expect-error + ibrupofene: "no", + longitude: 0.0, + }, + { + longitude: 4.42634523, + latitude: 5.344233323352, + }, + ...(pass.props["locations"] || []), + ); - expect(pass.props["locations"].length).toBe((oldAmountOfLocations || 0) + 1); + expect(pass.props["locations"].length).toBe( + (oldAmountOfLocations || 0) + 1, + ); }); }); describe("Beacons()", () => { it("Won't apply changes if invalid beacon objects are passed", () => { const props = pass.props["beacons"] || []; - const oldAmountOfBeacons = props && props.length || 0; + const oldAmountOfBeacons = (props && props.length) || 0; - pass.beacons({ - // @ts-expect-error - "ibrupofene": "no", - "major": 55, - "minor": 0, - "proximityUUID": "2707c5f4-deb9-48ff-b760-671bc885b6a7" - }, ...props); + pass.beacons( + { + // @ts-expect-error + ibrupofene: "no", + major: 55, + minor: 0, + proximityUUID: "2707c5f4-deb9-48ff-b760-671bc885b6a7", + }, + ...props, + ); if (oldAmountOfBeacons) { expect(pass.props["beacons"].length).toBe(oldAmountOfBeacons); @@ -153,20 +176,23 @@ describe("Passkit-generator", function () { it("Will filter out invalid beacons objects", () => { const props = pass.props["beacons"] || []; - const oldAmountOfBeacons = props && props.length || 0; - - pass.beacons({ - "major": 55, - "minor": 0, - "proximityUUID": "59da0f96-3fb5-43aa-9028-2bc796c3d0c5" - }, { - "major": 55, - "minor": 0, - "proximityUUID": "fdcbbf48-a4ae-4ffb-9200-f8a373c5c18e", - // @ts-expect-error - "animal": "Monkey" - }, ...props); + const oldAmountOfBeacons = (props && props.length) || 0; + pass.beacons( + { + major: 55, + minor: 0, + proximityUUID: "59da0f96-3fb5-43aa-9028-2bc796c3d0c5", + }, + { + major: 55, + minor: 0, + proximityUUID: "fdcbbf48-a4ae-4ffb-9200-f8a373c5c18e", + // @ts-expect-error + animal: "Monkey", + }, + ...props, + ); expect(pass.props["beacons"].length).toBe(oldAmountOfBeacons + 1); }); @@ -175,7 +201,7 @@ describe("Passkit-generator", function () { describe("barcodes()", () => { it("Won't apply changes if no data is passed", () => { const props = pass.props["barcodes"] || []; - const oldAmountOfBarcodes = props && props.length || 0; + const oldAmountOfBarcodes = (props && props.length) || 0; pass.barcodes(); expect(pass.props["barcodes"].length).toBe(oldAmountOfBarcodes); @@ -183,7 +209,7 @@ describe("Passkit-generator", function () { it("Will ignore boolean parameter", () => { const props = pass.props["barcodes"] || []; - const oldAmountOfBarcodes = props && props.length || 0; + const oldAmountOfBarcodes = (props && props.length) || 0; // @ts-expect-error pass.barcode(true); @@ -192,7 +218,7 @@ describe("Passkit-generator", function () { it("Will ignore numeric parameter", () => { const props = pass.props["barcodes"] || []; - const oldAmountOfBarcodes = props && props.length || 0; + const oldAmountOfBarcodes = (props && props.length) || 0; // @ts-expect-error pass.barcodes(42); @@ -208,7 +234,7 @@ describe("Passkit-generator", function () { pass.barcodes({ message: "28363516282", format: "PKBarcodeFormatPDF417", - messageEncoding: "utf8" + messageEncoding: "utf8", }); expect(pass.props["barcodes"].length).toBe(1); @@ -220,12 +246,14 @@ describe("Passkit-generator", function () { format: "PKBarcodeFormatPDF417", }); - expect(pass.props["barcodes"][0].messageEncoding).toBe("iso-8859-1"); + expect(pass.props["barcodes"][0].messageEncoding).toBe( + "iso-8859-1", + ); }); it("Will ignore objects without message property", () => { const props = pass.props["barcodes"] || []; - const oldAmountOfBarcodes = props && props.length || 0; + const oldAmountOfBarcodes = (props && props.length) || 0; // @ts-expect-error pass.barcodes({ @@ -237,12 +265,19 @@ describe("Passkit-generator", function () { it("Will ignore non-Barcodes schema compliant objects", () => { // @ts-expect-error - pass.barcodes(5, 10, 15, { - message: "28363516282", - format: "PKBarcodeFormatPDF417" - }, 7, 1); + pass.barcodes( + 5, + 10, + 15, + { + message: "28363516282", + format: "PKBarcodeFormatPDF417", + }, + 7, + 1, + ); - expect(pass.props["barcodes"].length).toBe(1) + expect(pass.props["barcodes"].length).toBe(1); }); it("Will reset barcodes content if parameter is null", () => { @@ -255,18 +290,18 @@ describe("Passkit-generator", function () { it("Will ignore non string or null arguments", function () { const oldBarcode = pass.props["barcode"] || undefined; - pass - .barcodes("Message-22645272183") + pass.barcodes("Message-22645272183") // @ts-expect-error - .barcode(55) + .barcode(55); // unchanged expect(pass.props["barcode"]).toEqual(oldBarcode); }); it("Will reset backward value on null", () => { - pass.barcodes("Message-22645272183") - .barcode("PKBarcodeFormatAztec"); + pass.barcodes("Message-22645272183").barcode( + "PKBarcodeFormatAztec", + ); expect(pass.props["barcode"].format).toBe("PKBarcodeFormatAztec"); @@ -277,8 +312,7 @@ describe("Passkit-generator", function () { it("Won't apply changes if unknown format is passed", () => { const oldBarcode = pass.props["barcode"] || undefined; - pass - .barcodes("Message-22645272183") + pass.barcodes("Message-22645272183") // @ts-expect-error .barcode("PKBingoBongoFormat"); diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json index 58037b5..99dd35e 100644 --- a/spec/support/jasmine.json +++ b/spec/support/jasmine.json @@ -1,8 +1,6 @@ { "spec_dir": "spec", - "spec_files": [ - "**/*.js" - ], + "spec_files": ["**/*.js"], "stopSpecOnExpectationFailure": false, "random": true } diff --git a/spec/utils.ts b/spec/utils.ts index 5d3266c..3343d08 100644 --- a/spec/utils.ts +++ b/spec/utils.ts @@ -9,7 +9,7 @@ describe("splitBufferBundle", () => { "de.lproj/background@2x.png": zeroBuffer, "it.lproj/thumbnail@2x.png": zeroBuffer, "thumbnail@2x.png": zeroBuffer, - "background.png": zeroBuffer + "background.png": zeroBuffer, }; const result = splitBufferBundle(payload); @@ -25,11 +25,11 @@ describe("splitBufferBundle", () => { }, "it.lproj": { "thumbnail@2x.png": zeroBuffer, - } + }, }); expect(result[1]).toEqual({ "thumbnail@2x.png": zeroBuffer, - "background.png": zeroBuffer + "background.png": zeroBuffer, }); }); diff --git a/src/abstract.ts b/src/abstract.ts index 4e36163..3235d55 100644 --- a/src/abstract.ts +++ b/src/abstract.ts @@ -1,68 +1,75 @@ -import { Certificates, FinalCertificates, PartitionedBundle, OverridesSupportedOptions, FactoryOptions } from "./schema"; -import { getModelContents, readCertificatesFromOptions } from "./parser"; -import formatMessage from "./messages"; - -const abmCertificates = Symbol("certificates"); -const abmModel = Symbol("model"); -const abmOverrides = Symbol("overrides"); - -export interface AbstractFactoryOptions extends Omit { - certificates?: Certificates; -} - -interface AbstractModelOptions { - bundle: PartitionedBundle; - certificates: FinalCertificates; - overrides?: OverridesSupportedOptions; -} - -/** - * Creates an abstract model to keep data - * in memory for future passes creation - * @param options - */ - -export async function createAbstractModel(options: AbstractFactoryOptions) { - if (!(options && Object.keys(options).length)) { - throw new Error(formatMessage("CP_NO_OPTS")); - } - - try { - const [bundle, certificates] = await Promise.all([ - getModelContents(options.model), - readCertificatesFromOptions(options.certificates) - ]); - - return new AbstractModel({ - bundle, - certificates, - overrides: options.overrides - }); - } catch (err) { - throw new Error(formatMessage("CP_INIT_ERROR", "abstract model", err)); - } -} - -export class AbstractModel { - private [abmCertificates]: FinalCertificates; - private [abmModel]: PartitionedBundle; - private [abmOverrides]: OverridesSupportedOptions; - - constructor(options: AbstractModelOptions) { - this[abmModel] = options.bundle; - this[abmCertificates] = options.certificates; - this[abmOverrides] = options.overrides - } - - get certificates(): FinalCertificates { - return this[abmCertificates]; - } - - get bundle(): PartitionedBundle { - return this[abmModel]; - } - - get overrides(): OverridesSupportedOptions { - return this[abmOverrides]; - } -} +import { + Certificates, + FinalCertificates, + PartitionedBundle, + OverridesSupportedOptions, + FactoryOptions, +} from "./schema"; +import { getModelContents, readCertificatesFromOptions } from "./parser"; +import formatMessage from "./messages"; + +const abmCertificates = Symbol("certificates"); +const abmModel = Symbol("model"); +const abmOverrides = Symbol("overrides"); + +export interface AbstractFactoryOptions + extends Omit { + certificates?: Certificates; +} + +interface AbstractModelOptions { + bundle: PartitionedBundle; + certificates: FinalCertificates; + overrides?: OverridesSupportedOptions; +} + +/** + * Creates an abstract model to keep data + * in memory for future passes creation + * @param options + */ + +export async function createAbstractModel(options: AbstractFactoryOptions) { + if (!(options && Object.keys(options).length)) { + throw new Error(formatMessage("CP_NO_OPTS")); + } + + try { + const [bundle, certificates] = await Promise.all([ + getModelContents(options.model), + readCertificatesFromOptions(options.certificates), + ]); + + return new AbstractModel({ + bundle, + certificates, + overrides: options.overrides, + }); + } catch (err) { + throw new Error(formatMessage("CP_INIT_ERROR", "abstract model", err)); + } +} + +export class AbstractModel { + private [abmCertificates]: FinalCertificates; + private [abmModel]: PartitionedBundle; + private [abmOverrides]: OverridesSupportedOptions; + + constructor(options: AbstractModelOptions) { + this[abmModel] = options.bundle; + this[abmCertificates] = options.certificates; + this[abmOverrides] = options.overrides; + } + + get certificates(): FinalCertificates { + return this[abmCertificates]; + } + + get bundle(): PartitionedBundle { + return this[abmModel]; + } + + get overrides(): OverridesSupportedOptions { + return this[abmOverrides]; + } +} diff --git a/src/factory.ts b/src/factory.ts index fa9a5fc..52e185e 100644 --- a/src/factory.ts +++ b/src/factory.ts @@ -1,5 +1,11 @@ import { Pass } from "./pass"; -import { FactoryOptions, BundleUnit, FinalCertificates, PartitionedBundle, OverridesSupportedOptions } from "./schema"; +import { + FactoryOptions, + BundleUnit, + FinalCertificates, + PartitionedBundle, + OverridesSupportedOptions, +} from "./schema"; import formatMessage from "./messages"; import { getModelContents, readCertificatesFromOptions } from "./parser"; import { splitBufferBundle } from "./utils"; @@ -16,9 +22,14 @@ import { AbstractModel, AbstractFactoryOptions } from "./abstract"; export async function createPass( options: FactoryOptions | InstanceType, additionalBuffers?: BundleUnit, - abstractMissingData?: Omit + abstractMissingData?: Omit, ): Promise { - if (!(options && (options instanceof AbstractModel || Object.keys(options).length))) { + if ( + !( + options && + (options instanceof AbstractModel || Object.keys(options).length) + ) + ) { throw new Error(formatMessage("CP_NO_OPTS")); } @@ -27,35 +38,62 @@ export async function createPass( let certificates: FinalCertificates; let overrides: OverridesSupportedOptions = { ...(options.overrides || {}), - ...(abstractMissingData && abstractMissingData.overrides || {}) + ...((abstractMissingData && abstractMissingData.overrides) || + {}), }; - if (!(options.certificates && options.certificates.signerCert && options.certificates.signerKey) && abstractMissingData.certificates) { + if ( + !( + options.certificates && + options.certificates.signerCert && + options.certificates.signerKey + ) && + abstractMissingData.certificates + ) { certificates = Object.assign( options.certificates, - await readCertificatesFromOptions(abstractMissingData.certificates) + await readCertificatesFromOptions( + abstractMissingData.certificates, + ), ); } else { certificates = options.certificates; } - return createPassInstance(options.bundle, certificates, overrides, additionalBuffers); + return createPassInstance( + options.bundle, + certificates, + overrides, + additionalBuffers, + ); } else { const [bundle, certificates] = await Promise.all([ getModelContents(options.model), - readCertificatesFromOptions(options.certificates) + readCertificatesFromOptions(options.certificates), ]); - return createPassInstance(bundle, certificates, options.overrides, additionalBuffers); + return createPassInstance( + bundle, + certificates, + options.overrides, + additionalBuffers, + ); } } catch (err) { throw new Error(formatMessage("CP_INIT_ERROR", "pass", err)); } } -function createPassInstance(model: PartitionedBundle, certificates: FinalCertificates, overrides: OverridesSupportedOptions, additionalBuffers?: BundleUnit) { +function createPassInstance( + model: PartitionedBundle, + certificates: FinalCertificates, + overrides: OverridesSupportedOptions, + additionalBuffers?: BundleUnit, +) { if (additionalBuffers) { - const [additionalL10n, additionalBundle] = splitBufferBundle(additionalBuffers); + const [additionalL10n, additionalBundle] = splitBufferBundle( + additionalBuffers, + ); Object.assign(model["l10nBundle"], additionalL10n); Object.assign(model["bundle"], additionalBundle); } @@ -63,6 +101,6 @@ function createPassInstance(model: PartitionedBundle, certificates: FinalCertifi return new Pass({ model, certificates, - overrides + overrides, }); } diff --git a/src/fieldsArray.ts b/src/fieldsArray.ts index 0e79aa6..b2543bf 100644 --- a/src/fieldsArray.ts +++ b/src/fieldsArray.ts @@ -24,20 +24,28 @@ export default class FieldsArray extends Array { */ push(...fieldsData: schema.Field[]): number { - const validFields = fieldsData.reduce((acc: schema.Field[], current: schema.Field) => { - if (!(typeof current === "object") || !schema.isValid(current, "field")) { + const validFields = fieldsData.reduce( + (acc: schema.Field[], current: schema.Field) => { + if ( + !(typeof current === "object") || + !schema.isValid(current, "field") + ) { + return acc; + } + + if (this[poolSymbol].has(current.key)) { + fieldsDebug( + `Field with key "${current.key}" discarded: fields must be unique in pass scope.`, + ); + } else { + this[poolSymbol].add(current.key); + acc.push(current); + } + return acc; - } - - if (this[poolSymbol].has(current.key)) { - fieldsDebug(`Field with key "${current.key}" discarded: fields must be unique in pass scope.`); - } else { - this[poolSymbol].add(current.key); - acc.push(current); - } - - return acc; - }, []); + }, + [], + ); return Array.prototype.push.call(this, ...validFields); } @@ -58,9 +66,13 @@ export default class FieldsArray extends Array { * also uniqueKeys set */ - splice(start: number, deleteCount: number, ...items: schema.Field[]): schema.Field[] { + splice( + start: number, + deleteCount: number, + ...items: schema.Field[] + ): schema.Field[] { const removeList = this.slice(start, deleteCount + start); - removeList.forEach(item => this[poolSymbol].delete(item.key)); + removeList.forEach((item) => this[poolSymbol].delete(item.key)); return Array.prototype.splice.call(this, start, deleteCount, items); } diff --git a/src/index.ts b/src/index.ts index 29d0dd1..7df70e5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,5 +3,5 @@ import { AbstractModel as AbstractModelClass } from "./abstract"; export { createPass } from "./factory"; export { createAbstractModel } from "./abstract"; -export type Pass = InstanceType -export type AbstractModel = InstanceType +export type Pass = InstanceType; +export type AbstractModel = InstanceType; diff --git a/src/messages.ts b/src/messages.ts index 94f9cf0..7d0d227 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -1,31 +1,51 @@ const errors = { - CP_INIT_ERROR: "Something went really bad in the %s initialization! Look at the log below this message. It should contain all the infos about the problem: \n%s", - CP_NO_OPTS: "Cannot initialize the pass or abstract model creation: no options were passed.", - CP_NO_CERTS: "Cannot initialize the pass creation: no valid certificates were passed.", - PASSFILE_VALIDATION_FAILED: "Validation of pass type failed. Pass file is not a valid buffer or (more probably) does not respect the schema.\nRefer to https://apple.co/2Nvshvn to build a correct pass.", - REQUIR_VALID_FAILED: "The options passed to Pass constructor does not meet the requirements.\nRefer to the documentation to compile them correctly.", - MODEL_UNINITIALIZED: "Provided model ( %s ) matched but unitialized or may not contain icon or a valid pass.json.\nRefer to https://apple.co/2IhJr0Q, https://apple.co/2Nvshvn and documentation to fill the model correctly.", - MODEL_NOT_VALID: "A model must be provided in form of path (string) or object { 'fileName': Buffer } in order to continue.", + CP_INIT_ERROR: + "Something went really bad in the %s initialization! Look at the log below this message. It should contain all the infos about the problem: \n%s", + CP_NO_OPTS: + "Cannot initialize the pass or abstract model creation: no options were passed.", + CP_NO_CERTS: + "Cannot initialize the pass creation: no valid certificates were passed.", + PASSFILE_VALIDATION_FAILED: + "Validation of pass type failed. Pass file is not a valid buffer or (more probably) does not respect the schema.\nRefer to https://apple.co/2Nvshvn to build a correct pass.", + REQUIR_VALID_FAILED: + "The options passed to Pass constructor does not meet the requirements.\nRefer to the documentation to compile them correctly.", + MODEL_UNINITIALIZED: + "Provided model ( %s ) matched but unitialized or may not contain icon or a valid pass.json.\nRefer to https://apple.co/2IhJr0Q, https://apple.co/2Nvshvn and documentation to fill the model correctly.", + MODEL_NOT_VALID: + "A model must be provided in form of path (string) or object { 'fileName': Buffer } in order to continue.", MODELF_NOT_FOUND: "Model %s not found. Provide a valid one to continue.", MODELF_FILE_NOT_FOUND: "File %s not found.", - INVALID_CERTS: "Invalid certificate(s) loaded: %s. Please provide valid WWDR certificates and developer signer certificate and key (with passphrase).\nRefer to docs to obtain them.", + INVALID_CERTS: + "Invalid certificate(s) loaded: %s. Please provide valid WWDR certificates and developer signer certificate and key (with passphrase).\nRefer to docs to obtain them.", INVALID_CERT_PATH: "Invalid certificate loaded. %s does not exist.", - TRSTYPE_REQUIRED: "Cannot proceed with pass creation. transitType field is required for boardingPasses.", - OVV_KEYS_BADFORMAT: "Cannot proceed with pass creation due to bad keys format in overrides.", - NO_PASS_TYPE: "Cannot proceed with pass creation. Model definition (pass.json) has no valid type in it.\nRefer to https://apple.co/2wzyL5J to choose a valid pass type." + TRSTYPE_REQUIRED: + "Cannot proceed with pass creation. transitType field is required for boardingPasses.", + OVV_KEYS_BADFORMAT: + "Cannot proceed with pass creation due to bad keys format in overrides.", + NO_PASS_TYPE: + "Cannot proceed with pass creation. Model definition (pass.json) has no valid type in it.\nRefer to https://apple.co/2wzyL5J to choose a valid pass type.", }; const debugMessages = { - TRSTYPE_NOT_VALID: "Transit type changing rejected as not compliant with Apple Specifications. Transit type would become \"%s\" but should be in [PKTransitTypeAir, PKTransitTypeBoat, PKTransitTypeBus, PKTransitTypeGeneric, PKTransitTypeTrain]", - BRC_NOT_SUPPORTED: "Format not found among barcodes. Cannot set backward compatibility.", - BRC_FORMATTYPE_UNMATCH: "Format must be a string or null. Cannot set backward compatibility.", - BRC_AUTC_MISSING_DATA: "Unable to autogenerate barcodes. Data is not a string.", - BRC_BW_FORMAT_UNSUPPORTED: "This format is not supported (by Apple) for backward support. Please choose another one.", - BRC_NO_POOL: "Cannot set barcode: no barcodes found. Please set barcodes first. Barcode is for retrocompatibility only.", + TRSTYPE_NOT_VALID: + 'Transit type changing rejected as not compliant with Apple Specifications. Transit type would become "%s" but should be in [PKTransitTypeAir, PKTransitTypeBoat, PKTransitTypeBus, PKTransitTypeGeneric, PKTransitTypeTrain]', + BRC_NOT_SUPPORTED: + "Format not found among barcodes. Cannot set backward compatibility.", + BRC_FORMATTYPE_UNMATCH: + "Format must be a string or null. Cannot set backward compatibility.", + BRC_AUTC_MISSING_DATA: + "Unable to autogenerate barcodes. Data is not a string.", + BRC_BW_FORMAT_UNSUPPORTED: + "This format is not supported (by Apple) for backward support. Please choose another one.", + BRC_NO_POOL: + "Cannot set barcode: no barcodes found. Please set barcodes first. Barcode is for retrocompatibility only.", DATE_FORMAT_UNMATCH: "%s was not set due to incorrect date format.", - NFC_INVALID: "Unable to set NFC properties: data not compliant with schema.", - PRS_INVALID: "Unable to parse Personalization.json. File is not a valid JSON. Error: %s", - PRS_REMOVED: "Personalization has been removed as it requires an NFC-enabled pass to work." + NFC_INVALID: + "Unable to set NFC properties: data not compliant with schema.", + PRS_INVALID: + "Unable to parse Personalization.json. File is not a valid JSON. Error: %s", + PRS_REMOVED: + "Personalization has been removed as it requires an NFC-enabled pass to work.", }; type AllMessages = keyof (typeof debugMessages & typeof errors); diff --git a/src/parser.ts b/src/parser.ts index 49bad8f..08c69ab 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -1,8 +1,21 @@ import * as path from "path"; import forge from "node-forge"; import formatMessage from "./messages"; -import { FactoryOptions, PartitionedBundle, BundleUnit, Certificates, FinalCertificates, isValid } from "./schema"; -import { removeHidden, splitBufferBundle, getAllFilesWithName, hasFilesWithName, deletePersonalization } from "./utils"; +import { + FactoryOptions, + PartitionedBundle, + BundleUnit, + Certificates, + FinalCertificates, + isValid, +} from "./schema"; +import { + removeHidden, + splitBufferBundle, + getAllFilesWithName, + hasFilesWithName, + deletePersonalization, +} from "./utils"; import fs from "fs"; import debug from "debug"; @@ -27,10 +40,9 @@ export async function getModelContents(model: FactoryOptions["model"]) { } const modelFiles = Object.keys(modelContents.bundle); - const isModelInitialized = ( + const isModelInitialized = modelFiles.includes("pass.json") && - hasFilesWithName("icon", modelFiles, "startsWith") - ); + hasFilesWithName("icon", modelFiles, "startsWith"); if (!isModelInitialized) { throw new Error(formatMessage("MODEL_UNINITIALIZED", "parse result")); @@ -46,19 +58,34 @@ export async function getModelContents(model: FactoryOptions["model"]) { return modelContents; } - const logoFullNames = getAllFilesWithName("personalizationLogo", modelFiles, "startsWith"); - if (!(logoFullNames.length && modelContents.bundle[personalizationJsonFile].length)) { + const logoFullNames = getAllFilesWithName( + "personalizationLogo", + modelFiles, + "startsWith", + ); + if ( + !( + logoFullNames.length && + modelContents.bundle[personalizationJsonFile].length + ) + ) { deletePersonalization(modelContents.bundle, logoFullNames); return modelContents; } try { - const parsedPersonalization = JSON.parse(modelContents.bundle[personalizationJsonFile].toString("utf8")); - const isPersonalizationValid = isValid(parsedPersonalization, "personalizationDict"); + const parsedPersonalization = JSON.parse( + modelContents.bundle[personalizationJsonFile].toString("utf8"), + ); + const isPersonalizationValid = isValid( + parsedPersonalization, + "personalizationDict", + ); if (!isPersonalizationValid) { - [...logoFullNames, personalizationJsonFile] - .forEach(file => delete modelContents.bundle[file]); + [...logoFullNames, personalizationJsonFile].forEach( + (file) => delete modelContents.bundle[file], + ); return modelContents; } @@ -76,55 +103,70 @@ export async function getModelContents(model: FactoryOptions["model"]) { * @param model */ -export async function getModelFolderContents(model: string): Promise { +export async function getModelFolderContents( + model: string, +): Promise { try { - const modelPath = `${model}${!path.extname(model) && ".pass" || ""}`; + const modelPath = `${model}${(!path.extname(model) && ".pass") || ""}`; const modelFilesList = await readDir(modelPath); // No dot-starting files, manifest and signature - const filteredFiles = removeHidden(modelFilesList) - .filter(f => !/(manifest|signature)/i.test(f) && /.+$/.test(path.parse(f).ext)); - - const isModelInitialized = ( - filteredFiles.length && - hasFilesWithName("icon", filteredFiles, "startsWith") + const filteredFiles = removeHidden(modelFilesList).filter( + (f) => + !/(manifest|signature)/i.test(f) && + /.+$/.test(path.parse(f).ext), ); + const isModelInitialized = + filteredFiles.length && + hasFilesWithName("icon", filteredFiles, "startsWith"); + // Icon is required to proceed if (!isModelInitialized) { - throw new Error(formatMessage( - "MODEL_UNINITIALIZED", - path.parse(model).name - )); + throw new Error( + formatMessage("MODEL_UNINITIALIZED", path.parse(model).name), + ); } // Splitting files from localization folders - const rawBundleFiles = filteredFiles.filter(entry => !entry.includes(".lproj")); - const l10nFolders = filteredFiles.filter(entry => entry.includes(".lproj")); - - const rawBundleBuffers = await Promise.all( - rawBundleFiles.map(file => readFile(path.resolve(modelPath, file))) + const rawBundleFiles = filteredFiles.filter( + (entry) => !entry.includes(".lproj"), + ); + const l10nFolders = filteredFiles.filter((entry) => + entry.includes(".lproj"), ); - const bundle: BundleUnit = Object.assign({}, - ...rawBundleFiles.map((fileName, index) => ({ [fileName]: rawBundleBuffers[index] })) + const rawBundleBuffers = await Promise.all( + rawBundleFiles.map((file) => + readFile(path.resolve(modelPath, file)), + ), + ); + + const bundle: BundleUnit = Object.assign( + {}, + ...rawBundleFiles.map((fileName, index) => ({ + [fileName]: rawBundleBuffers[index], + })), ); // Reading concurrently localizations folder // and their files and their buffers const L10N_FilesListByFolder: Array = await Promise.all( - l10nFolders.map(async folderPath => { + l10nFolders.map(async (folderPath) => { // Reading current folder const currentLangPath = path.join(modelPath, folderPath); const files = await readDir(currentLangPath); // Transforming files path to a model-relative path - const validFiles = removeHidden(files) - .map(file => path.join(currentLangPath, file)); + const validFiles = removeHidden(files).map((file) => + path.join(currentLangPath, file), + ); // Getting all the buffers from file paths const buffers = await Promise.all( - validFiles.map(file => readFile(file).catch(() => Buffer.alloc(0))) + validFiles.map((file) => + readFile(file).catch(() => Buffer.alloc(0)), + ), ); // Assigning each file path to its buffer @@ -140,34 +182,37 @@ export async function getModelFolderContents(model: string): Promise ({ [l10nFolders[index]]: folder })) + ...L10N_FilesListByFolder.map((folder, index) => ({ + [l10nFolders[index]]: folder, + })), ); return { bundle, - l10nBundle + l10nBundle, }; } catch (err) { if (err?.code === "ENOENT") { if (err.syscall === "open") { // file opening failed - throw new Error(formatMessage("MODELF_NOT_FOUND", err.path)) + throw new Error(formatMessage("MODELF_NOT_FOUND", err.path)); } else if (err.syscall === "scandir") { // directory reading failed const pathContents = (err.path as string).split(/(\/|\\\?)/); - throw new Error(formatMessage( - "MODELF_FILE_NOT_FOUND", - pathContents[pathContents.length - 1] - )) + throw new Error( + formatMessage( + "MODELF_FILE_NOT_FOUND", + pathContents[pathContents.length - 1], + ), + ); } } @@ -182,27 +227,28 @@ export async function getModelFolderContents(model: string): Promise((acc, current) => { - // Checking if current file is one of the autogenerated ones or if its - // content is not available + const rawBundle = removeHidden(Object.keys(model)).reduce( + (acc, current) => { + // Checking if current file is one of the autogenerated ones or if its + // content is not available - if (/(manifest|signature)/.test(current) || !model[current]) { - return acc; - } + if (/(manifest|signature)/.test(current) || !model[current]) { + return acc; + } - return { ...acc, [current]: model[current] }; - }, {}); + return { ...acc, [current]: model[current] }; + }, + {}, + ); const bundleKeys = Object.keys(rawBundle); - const isModelInitialized = ( - bundleKeys.length && - hasFilesWithName("icon", bundleKeys, "startsWith") - ); + const isModelInitialized = + bundleKeys.length && hasFilesWithName("icon", bundleKeys, "startsWith"); // Icon is required to proceed if (!isModelInitialized) { - throw new Error(formatMessage("MODEL_UNINITIALIZED", "Buffers")) + throw new Error(formatMessage("MODEL_UNINITIALIZED", "Buffers")); } // separing localization folders from bundle files @@ -210,7 +256,7 @@ export function getModelBufferContents(model: BundleUnit): PartitionedBundle { return { bundle, - l10nBundle + l10nBundle, }; } @@ -224,8 +270,16 @@ type flatCertificates = Omit & { signerKey: string; }; -export async function readCertificatesFromOptions(options: Certificates): Promise { - if (!(options && Object.keys(options).length && isValid(options, "certificatesSchema"))) { +export async function readCertificatesFromOptions( + options: Certificates, +): Promise { + if ( + !( + options && + Object.keys(options).length && + isValid(options, "certificatesSchema") + ) + ) { throw new Error(formatMessage("CP_NO_CERTS")); } @@ -239,30 +293,31 @@ export async function readCertificatesFromOptions(options: Certificates): Promis // if the signerKey is an object, we want to get // all the real contents and don't care of passphrase - const flattenedDocs = Object.assign({}, options, { signerKey }) as flatCertificates; + const flattenedDocs = Object.assign({}, options, { + signerKey, + }) as flatCertificates; // We read the contents - const rawContentsPromises = Object.keys(flattenedDocs) - .map(key => { - const content = flattenedDocs[key]; + const rawContentsPromises = Object.keys(flattenedDocs).map((key) => { + const content = flattenedDocs[key]; - if (!!path.parse(content).ext) { - // The content is a path to the document - return readFile(path.resolve(content), { encoding: "utf8" }); - } else { - // Content is the real document content - return Promise.resolve(content); - } - }); + if (!!path.parse(content).ext) { + // The content is a path to the document + return readFile(path.resolve(content), { encoding: "utf8" }); + } else { + // Content is the real document content + return Promise.resolve(content); + } + }); try { const parsedContents = await Promise.all(rawContentsPromises); const pemParsedContents = parsedContents.map((file, index) => { const certName = Object.keys(options)[index]; - const passphrase = ( - typeof options.signerKey === "object" && - options.signerKey?.passphrase - ) || undefined; + const passphrase = + (typeof options.signerKey === "object" && + options.signerKey?.passphrase) || + undefined; const pem = parsePEM(certName, file, passphrase); @@ -279,7 +334,9 @@ export async function readCertificatesFromOptions(options: Certificates): Promis throw err; } - throw new Error(formatMessage("INVALID_CERT_PATH", path.parse(err.path).base)); + throw new Error( + formatMessage("INVALID_CERT_PATH", path.parse(err.path).base), + ); } } diff --git a/src/pass.ts b/src/pass.ts index 03ca452..15dd629 100644 --- a/src/pass.ts +++ b/src/pass.ts @@ -7,7 +7,13 @@ import { ZipFile } from "yazl"; import * as schema from "./schema"; import formatMessage from "./messages"; import FieldsArray from "./fieldsArray"; -import { generateStringFile, dateToW3CString, isValidRGB, deletePersonalization, getAllFilesWithName } from "./utils"; +import { + generateStringFile, + dateToW3CString, + isValidRGB, + deletePersonalization, + getAllFilesWithName, +} from "./utils"; const barcodeDebug = debug("passkit:barcode"); const genericDebug = debug("passkit:generic"); @@ -20,7 +26,7 @@ const propsSchemaMap = new Map([ ["barcode", "barcode"], ["beacons", "beaconsDict"], ["locations", "locationsDict"], - ["nfc", "nfcDict"] + ["nfc", "nfcDict"], ]); export class Pass { @@ -42,7 +48,9 @@ export class Pass { private Certificates: schema.FinalCertificates; private [transitType]: string = ""; - private l10nTranslations: { [languageCode: string]: { [placeholder: string]: string } } = {}; + private l10nTranslations: { + [languageCode: string]: { [placeholder: string]: string }; + } = {}; constructor(options: schema.PassInstance) { if (!schema.isValid(options, "instance")) { @@ -54,77 +62,101 @@ export class Pass { this.bundle = { ...options.model.bundle }; try { - this.passCore = JSON.parse(this.bundle["pass.json"].toString("utf8")); + this.passCore = JSON.parse( + this.bundle["pass.json"].toString("utf8"), + ); } catch (err) { throw new Error(formatMessage("PASSFILE_VALIDATION_FAILED")); } // Parsing the options and extracting only the valid ones. - const validOverrides = schema.getValidated(options.overrides || {}, "supportedOptions") as schema.OverridesSupportedOptions; + const validOverrides = schema.getValidated( + options.overrides || {}, + "supportedOptions", + ) as schema.OverridesSupportedOptions; if (validOverrides === null) { - throw new Error(formatMessage("OVV_KEYS_BADFORMAT")) + throw new Error(formatMessage("OVV_KEYS_BADFORMAT")); } - this.type = Object.keys(this.passCore) - .find(key => /(boardingPass|eventTicket|coupon|generic|storeCard)/.test(key)) as keyof schema.ValidPassType; + this.type = Object.keys(this.passCore).find((key) => + /(boardingPass|eventTicket|coupon|generic|storeCard)/.test(key), + ) as keyof schema.ValidPassType; if (!this.type) { throw new Error(formatMessage("NO_PASS_TYPE")); } // Parsing and validating pass.json keys - const passCoreKeys = Object.keys(this.passCore) as (keyof schema.ValidPass)[]; - const validatedPassKeys = passCoreKeys.reduce((acc, current) => { - if (this.type === current) { - // We want to exclude type keys (eventTicket, - // boardingPass, ecc.) and their content - return acc; - } + const passCoreKeys = Object.keys( + this.passCore, + ) as (keyof schema.ValidPass)[]; + const validatedPassKeys = passCoreKeys.reduce( + (acc, current) => { + if (this.type === current) { + // We want to exclude type keys (eventTicket, + // boardingPass, ecc.) and their content + return acc; + } - if (!propsSchemaMap.has(current)) { - // If the property is unknown (we don't care if - // it is valid or not for Wallet), we return - // directly the content - return { ...acc, [current]: this.passCore[current] }; - } + if (!propsSchemaMap.has(current)) { + // If the property is unknown (we don't care if + // it is valid or not for Wallet), we return + // directly the content + return { ...acc, [current]: this.passCore[current] }; + } - const currentSchema = propsSchemaMap.get(current)!; + const currentSchema = propsSchemaMap.get(current)!; - if (Array.isArray(this.passCore[current])) { - const valid = getValidInArray( - currentSchema, - this.passCore[current] as schema.ArrayPassSchema[] - ); - return { ...acc, [current]: valid }; - } else { - return { - ...acc, - [current]: schema.isValid( - this.passCore[current], - currentSchema - ) && this.passCore[current] || undefined - }; - } - }, {}); + if (Array.isArray(this.passCore[current])) { + const valid = getValidInArray( + currentSchema, + this.passCore[current] as schema.ArrayPassSchema[], + ); + return { ...acc, [current]: valid }; + } else { + return { + ...acc, + [current]: + (schema.isValid( + this.passCore[current], + currentSchema, + ) && + this.passCore[current]) || + undefined, + }; + } + }, + {}, + ); this[passProps] = { ...(validatedPassKeys || {}), - ...(validOverrides || {}) + ...(validOverrides || {}), }; - if (this.type === "boardingPass" && this.passCore[this.type]["transitType"]) { + if ( + this.type === "boardingPass" && + this.passCore[this.type]["transitType"] + ) { // We might want to generate a boarding pass without setting manually // in the code the transit type but right in the model; this[transitType] = this.passCore[this.type]["transitType"]; } - this._fields = ["primaryFields", "secondaryFields", "auxiliaryFields", "backFields", "headerFields"]; - this._fields.forEach(fieldName => { + this._fields = [ + "primaryFields", + "secondaryFields", + "auxiliaryFields", + "backFields", + "headerFields", + ]; + this._fields.forEach((fieldName) => { this[fieldName] = new FieldsArray( this.fieldsKeys, - ...(this.passCore[this.type][fieldName] || []) - .filter(field => schema.isValid(field, "field")) + ...(this.passCore[this.type][fieldName] || []).filter((field) => + schema.isValid(field, "field"), + ), ); }); } @@ -146,13 +178,19 @@ export class Pass { */ const currentBundleFiles = Object.keys(this.bundle); - if (!this[passProps].nfc && currentBundleFiles.includes("personalization.json")) { + if ( + !this[passProps].nfc && + currentBundleFiles.includes("personalization.json") + ) { genericDebug(formatMessage("PRS_REMOVED")); - deletePersonalization(this.bundle, getAllFilesWithName( - "personalizationLogo", - currentBundleFiles, - "startsWith" - )); + deletePersonalization( + this.bundle, + getAllFilesWithName( + "personalizationLogo", + currentBundleFiles, + "startsWith", + ), + ); } const finalBundle = { ...this.bundle } as schema.BundleUnit; @@ -161,7 +199,7 @@ export class Pass { * Iterating through languages and generating pass.string file */ - Object.keys(this.l10nTranslations).forEach(lang => { + Object.keys(this.l10nTranslations).forEach((lang) => { const strings = generateStringFile(this.l10nTranslations[lang]); const langInBundles = `${lang}.lproj`; @@ -176,13 +214,21 @@ export class Pass { this.l10nBundles[langInBundles] = {}; } - this.l10nBundles[langInBundles]["pass.strings"] = Buffer.concat([ - this.l10nBundles[langInBundles]["pass.strings"] || Buffer.alloc(0), - strings + this.l10nBundles[langInBundles][ + "pass.strings" + ] = Buffer.concat([ + this.l10nBundles[langInBundles]["pass.strings"] || + Buffer.alloc(0), + strings, ]); } - if (!(this.l10nBundles[langInBundles] && Object.keys(this.l10nBundles[langInBundles]).length)) { + if ( + !( + this.l10nBundles[langInBundles] && + Object.keys(this.l10nBundles[langInBundles]).length + ) + ) { return; } @@ -194,33 +240,48 @@ export class Pass { * composition. */ - Object.assign(finalBundle, ...Object.keys(this.l10nBundles[langInBundles]) - .map(fileName => { - const fullPath = path.join(langInBundles, fileName).replace(/\\/, "/"); - return { [fullPath]: this.l10nBundles[langInBundles][fileName] }; - }) + Object.assign( + finalBundle, + ...Object.keys(this.l10nBundles[langInBundles]).map( + (fileName) => { + const fullPath = path + .join(langInBundles, fileName) + .replace(/\\/, "/"); + return { + [fullPath]: this.l10nBundles[langInBundles][ + fileName + ], + }; + }, + ), ); }); /* - * Parsing the buffers, pushing them into the archive - * and returning the compiled manifest - */ + * Parsing the buffers, pushing them into the archive + * and returning the compiled manifest + */ const archive = new ZipFile(); - const manifest = Object.keys(finalBundle).reduce((acc, current) => { - let hashFlow = forge.md.sha1.create(); + const manifest = Object.keys(finalBundle).reduce( + (acc, current) => { + let hashFlow = forge.md.sha1.create(); - hashFlow.update(finalBundle[current].toString("binary")); - archive.addBuffer(finalBundle[current], current); - acc[current] = hashFlow.digest().toHex(); + hashFlow.update(finalBundle[current].toString("binary")); + archive.addBuffer(finalBundle[current], current); + acc[current] = hashFlow.digest().toHex(); - return acc; - }, {}); + return acc; + }, + {}, + ); const signatureBuffer = this._sign(manifest); archive.addBuffer(signatureBuffer, "signature"); - archive.addBuffer(Buffer.from(JSON.stringify(manifest)), "manifest.json"); + archive.addBuffer( + Buffer.from(JSON.stringify(manifest)), + "manifest.json", + ); const passStream = new Stream.PassThrough(); archive.outputStream.pipe(passStream); @@ -242,8 +303,15 @@ export class Pass { * @see https://apple.co/2KOv0OW - Passes support localization */ - localize(lang: string, translations?: { [placeholder: string]: string }): this { - if (lang && typeof lang === "string" && (typeof translations === "object" || translations === undefined)) { + localize( + lang: string, + translations?: { [placeholder: string]: string }, + ): this { + if ( + lang && + typeof lang === "string" && + (typeof translations === "object" || translations === undefined) + ) { this.l10nTranslations[lang] = translations || {}; } @@ -292,7 +360,7 @@ export class Pass { */ beacons(resetFlag: null): this; - beacons(...data: schema.Beacon[]): this + beacons(...data: schema.Beacon[]): this; beacons(...data: (schema.Beacon | null)[]): this { if (data[0] === null) { delete this[passProps]["beacons"]; @@ -322,7 +390,10 @@ export class Pass { return this; } - const valid = processRelevancySet("locations", data as schema.Location[]); + const valid = processRelevancySet( + "locations", + data as schema.Location[], + ); if (valid.length) { this[passProps]["locations"] = valid; @@ -390,19 +461,28 @@ export class Pass { * Validation assign default value to missing parameters (if any). */ - const validBarcodes = data.reduce((acc, current) => { - if (!(current && current instanceof Object)) { - return acc; - } + const validBarcodes = data.reduce( + (acc, current) => { + if (!(current && current instanceof Object)) { + return acc; + } - const validated = schema.getValidated(current, "barcode"); + const validated = schema.getValidated(current, "barcode"); - if (!(validated && validated instanceof Object && Object.keys(validated).length)) { - return acc; - } + if ( + !( + validated && + validated instanceof Object && + Object.keys(validated).length + ) + ) { + return acc; + } - return [...acc, validated] as schema.Barcode[]; - }, []); + return [...acc, validated] as schema.Barcode[]; + }, + [], + ); if (validBarcodes.length) { this[passProps]["barcodes"] = validBarcodes; @@ -446,7 +526,9 @@ export class Pass { } // Checking which object among barcodes has the same format of the specified one. - const index = barcodes.findIndex(b => b.format.toLowerCase().includes(chosenFormat.toLowerCase())); + const index = barcodes.findIndex((b) => + b.format.toLowerCase().includes(chosenFormat.toLowerCase()), + ); if (index === -1) { barcodeDebug(formatMessage("BRC_NOT_SUPPORTED")); @@ -472,7 +554,14 @@ export class Pass { return this; } - if (!(data && typeof data === "object" && !Array.isArray(data) && schema.isValid(data, "nfcDict"))) { + if ( + !( + data && + typeof data === "object" && + !Array.isArray(data) && + schema.isValid(data, "nfcDict") + ) + ) { genericDebug(formatMessage("NFC_INVALID")); return this; } @@ -505,7 +594,10 @@ export class Pass { private _sign(manifest: schema.Manifest): Buffer { const signature = forge.pkcs7.createSignedData(); - signature.content = forge.util.createBuffer(JSON.stringify(manifest), "utf8"); + signature.content = forge.util.createBuffer( + JSON.stringify(manifest), + "utf8", + ); signature.addCertificate(this.Certificates.wwdr); signature.addCertificate(this.Certificates.signerCert); @@ -523,14 +615,18 @@ export class Pass { key: this.Certificates.signerKey, certificate: this.Certificates.signerCert, digestAlgorithm: forge.pki.oids.sha1, - authenticatedAttributes: [{ - type: forge.pki.oids.contentType, - value: forge.pki.oids.data - }, { - type: forge.pki.oids.messageDigest, - }, { - type: forge.pki.oids.signingTime, - }] + authenticatedAttributes: [ + { + type: forge.pki.oids.contentType, + value: forge.pki.oids.data, + }, + { + type: forge.pki.oids.messageDigest, + }, + { + type: forge.pki.oids.signingTime, + }, + ], }); /** @@ -554,7 +650,10 @@ export class Pass { * of beautiful things. ¯\_(ツ)_/¯ */ - return Buffer.from(forge.asn1.toDer(signature.toAsn1()).getBytes(), "binary"); + return Buffer.from( + forge.asn1.toDer(signature.toAsn1()).getBytes(), + "binary", + ); } /** @@ -566,7 +665,9 @@ export class Pass { */ private _patch(passCoreBuffer: Buffer): Buffer { - const passFile = JSON.parse(passCoreBuffer.toString()) as schema.ValidPass; + const passFile = JSON.parse( + passCoreBuffer.toString(), + ) as schema.ValidPass; if (Object.keys(this[passProps]).length) { /* @@ -575,14 +676,22 @@ export class Pass { * and then delete it from the passFile. */ - const passColors = ["backgroundColor", "foregroundColor", "labelColor"] as Array; - passColors.filter(v => this[passProps][v] && !isValidRGB(this[passProps][v])) - .forEach(v => delete this[passProps][v]); + const passColors = [ + "backgroundColor", + "foregroundColor", + "labelColor", + ] as Array; + passColors + .filter( + (v) => + this[passProps][v] && !isValidRGB(this[passProps][v]), + ) + .forEach((v) => delete this[passProps][v]); Object.assign(passFile, this[passProps]); } - this._fields.forEach(field => { + this._fields.forEach((field) => { passFile[this.type][field] = this[field]; }); @@ -627,8 +736,14 @@ function barcodesFromUncompleteData(message: string): schema.Barcode[] { "PKBarcodeFormatQR", "PKBarcodeFormatPDF417", "PKBarcodeFormatAztec", - "PKBarcodeFormatCode128" - ].map(format => schema.getValidated({ format, message }, "barcode") as schema.Barcode); + "PKBarcodeFormatCode128", + ].map( + (format) => + schema.getValidated( + { format, message }, + "barcode", + ) as schema.Barcode, + ); } function processRelevancySet(key: string, data: T[]): T[] { @@ -636,7 +751,10 @@ function processRelevancySet(key: string, data: T[]): T[] { } function getValidInArray(schemaName: schema.Schema, contents: T[]): T[] { - return contents.filter(current => Object.keys(current).length && schema.isValid(current, schemaName)); + return contents.filter( + (current) => + Object.keys(current).length && schema.isValid(current, schemaName), + ); } function processDate(key: string, date: Date): string | null { diff --git a/src/schema.ts b/src/schema.ts index 51399ed..413c0ec 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -10,10 +10,12 @@ export interface Manifest { export interface Certificates { wwdr?: string; signerCert?: string; - signerKey?: { - keyFile: string; - passphrase?: string; - } | string; + signerKey?: + | { + keyFile: string; + passphrase?: string; + } + | string; } export interface FactoryOptions { @@ -29,7 +31,7 @@ export interface BundleUnit { export interface PartitionedBundle { bundle: BundleUnit; l10nBundle: { - [key: string]: BundleUnit + [key: string]: BundleUnit; }; } @@ -49,17 +51,24 @@ export interface PassInstance { // * JOI Schemas + Related Interfaces * // // ************************************ // -const certificatesSchema = Joi.object().keys({ - wwdr: Joi.alternatives(Joi.binary(), Joi.string()).required(), - signerCert: Joi.alternatives(Joi.binary(), Joi.string()).required(), - signerKey: Joi.alternatives().try( - Joi.object().keys({ - keyFile: Joi.alternatives(Joi.binary(), Joi.string()).required(), - passphrase: Joi.string().required(), - }), - Joi.alternatives(Joi.binary(), Joi.string()) - ).required() -}).required(); +const certificatesSchema = Joi.object() + .keys({ + wwdr: Joi.alternatives(Joi.binary(), Joi.string()).required(), + signerCert: Joi.alternatives(Joi.binary(), Joi.string()).required(), + signerKey: Joi.alternatives() + .try( + Joi.object().keys({ + keyFile: Joi.alternatives( + Joi.binary(), + Joi.string(), + ).required(), + passphrase: Joi.string().required(), + }), + Joi.alternatives(Joi.binary(), Joi.string()), + ) + .required(), + }) + .required(); const instance = Joi.object().keys({ model: Joi.alternatives(Joi.object(), Joi.string()).required(), @@ -88,28 +97,31 @@ export interface OverridesSupportedOptions { maxDistance?: number; } -const supportedOptions = Joi.object().keys({ - serialNumber: Joi.string(), - description: Joi.string(), - organizationName: Joi.string(), - passTypeIdentifier: Joi.string(), - teamIdentifier: Joi.string(), - appLaunchURL: Joi.string(), - associatedStoreIdentifiers: Joi.array().items(Joi.number()), - userInfo: Joi.alternatives(Joi.object().unknown(), Joi.array()), - // parsing url as set of words and nums followed by dots, optional port and any possible path after - webServiceURL: Joi.string().regex(/https?:\/\/(?:[a-z0-9]+\.?)+(?::\d{2,})?(?:\/[\S]+)*/), - authenticationToken: Joi.string().min(16), - sharingProhibited: Joi.boolean(), - backgroundColor: Joi.string().min(10).max(16), - foregroundColor: Joi.string().min(10).max(16), - labelColor: Joi.string().min(10).max(16), - groupingIdentifier: Joi.string(), - suppressStripShine: Joi.boolean(), - logoText: Joi.string(), - maxDistance: Joi.number().positive(), -}).with("webServiceURL", "authenticationToken"); - +const supportedOptions = Joi.object() + .keys({ + serialNumber: Joi.string(), + description: Joi.string(), + organizationName: Joi.string(), + passTypeIdentifier: Joi.string(), + teamIdentifier: Joi.string(), + appLaunchURL: Joi.string(), + associatedStoreIdentifiers: Joi.array().items(Joi.number()), + userInfo: Joi.alternatives(Joi.object().unknown(), Joi.array()), + // parsing url as set of words and nums followed by dots, optional port and any possible path after + webServiceURL: Joi.string().regex( + /https?:\/\/(?:[a-z0-9]+\.?)+(?::\d{2,})?(?:\/[\S]+)*/, + ), + authenticationToken: Joi.string().min(16), + sharingProhibited: Joi.boolean(), + backgroundColor: Joi.string().min(10).max(16), + foregroundColor: Joi.string().min(10).max(16), + labelColor: Joi.string().min(10).max(16), + groupingIdentifier: Joi.string(), + suppressStripShine: Joi.boolean(), + logoText: Joi.string(), + maxDistance: Joi.number().positive(), + }) + .with("webServiceURL", "authenticationToken"); /* For a correct usage of semantics, please refer to https://apple.co/2I66Phk */ @@ -130,7 +142,7 @@ interface PersonNameComponent { const personNameComponents = Joi.object().keys({ givenName: Joi.string().required(), - familyName: Joi.string().required() + familyName: Joi.string().required(), }); interface Seat { @@ -148,12 +160,12 @@ const seat = Joi.object().keys({ seatNumber: Joi.string(), seatIdentifier: Joi.string(), seatType: Joi.string(), - seatDescription: Joi.string() + seatDescription: Joi.string(), }); const location = Joi.object().keys({ latitude: Joi.number().required(), - longitude: Joi.number().required() + longitude: Joi.number().required(), }); interface Semantics { @@ -201,7 +213,15 @@ interface Semantics { venueEntrance?: string; venuePhoneNumber?: string; venueRoom?: string; - eventType?: "PKEventTypeGeneric" | "PKEventTypeLivePerformance" | "PKEventTypeMovie" | "PKEventTypeSports" | "PKEventTypeConference" | "PKEventTypeConvention" | "PKEventTypeWorkshop" | "PKEventTypeSocialGathering"; + eventType?: + | "PKEventTypeGeneric" + | "PKEventTypeLivePerformance" + | "PKEventTypeMovie" + | "PKEventTypeSports" + | "PKEventTypeConference" + | "PKEventTypeConvention" + | "PKEventTypeWorkshop" + | "PKEventTypeSocialGathering"; eventStartDate?: string; eventEndDate?: string; artistIDs?: string; @@ -270,7 +290,9 @@ const semantics = Joi.object().keys({ venueEntrance: Joi.string(), venuePhoneNumber: Joi.string(), venueRoom: Joi.string(), - eventType: Joi.string().regex(/(PKEventTypeGeneric|PKEventTypeLivePerformance|PKEventTypeMovie|PKEventTypeSports|PKEventTypeConference|PKEventTypeConvention|PKEventTypeWorkshop|PKEventTypeSocialGathering)/), + eventType: Joi.string().regex( + /(PKEventTypeGeneric|PKEventTypeLivePerformance|PKEventTypeMovie|PKEventTypeSports|PKEventTypeConference|PKEventTypeConvention|PKEventTypeWorkshop|PKEventTypeSocialGathering)/, + ), eventStartDate: Joi.string(), eventEndDate: Joi.string(), artistIDs: Joi.string(), @@ -287,7 +309,7 @@ const semantics = Joi.object().keys({ awayTeamAbbreviation: Joi.string(), sportName: Joi.string(), // Store Card Passes - balance: currencyAmount + balance: currencyAmount, }); export interface ValidPassType { @@ -310,11 +332,16 @@ interface PassInterfacesProps { voided?: boolean; } -type AllPassProps = PassInterfacesProps & ValidPassType & OverridesSupportedOptions; +type AllPassProps = PassInterfacesProps & + ValidPassType & + OverridesSupportedOptions; export type ValidPass = { [K in keyof AllPassProps]: AllPassProps[K]; }; -export type PassColors = Pick; +export type PassColors = Pick< + OverridesSupportedOptions, + "backgroundColor" | "foregroundColor" | "labelColor" +>; export interface Barcode { altText?: string; @@ -323,13 +350,22 @@ export interface Barcode { message: string; } -export type BarcodeFormat = "PKBarcodeFormatQR" | "PKBarcodeFormatPDF417" | "PKBarcodeFormatAztec" | "PKBarcodeFormatCode128"; +export type BarcodeFormat = + | "PKBarcodeFormatQR" + | "PKBarcodeFormatPDF417" + | "PKBarcodeFormatAztec" + | "PKBarcodeFormatCode128"; const barcode = Joi.object().keys({ altText: Joi.string(), messageEncoding: Joi.string().default("iso-8859-1"), - format: Joi.string().required().regex(/(PKBarcodeFormatQR|PKBarcodeFormatPDF417|PKBarcodeFormatAztec|PKBarcodeFormatCode128)/, "barcodeType"), - message: Joi.string().required() + format: Joi.string() + .required() + .regex( + /(PKBarcodeFormatQR|PKBarcodeFormatPDF417|PKBarcodeFormatAztec|PKBarcodeFormatCode128)/, + "barcodeType", + ), + message: Joi.string().required(), }); export interface Field { @@ -350,30 +386,53 @@ export interface Field { } const field = Joi.object().keys({ - attributedValue: Joi.alternatives(Joi.string().allow(""), Joi.number(), Joi.date().iso()), + attributedValue: Joi.alternatives( + Joi.string().allow(""), + Joi.number(), + Joi.date().iso(), + ), changeMessage: Joi.string(), - dataDetectorType: Joi.array().items(Joi.string().regex(/(PKDataDetectorTypePhoneNumber|PKDataDetectorTypeLink|PKDataDetectorTypeAddress|PKDataDetectorTypeCalendarEvent)/, "dataDetectorType")), + dataDetectorType: Joi.array().items( + Joi.string().regex( + /(PKDataDetectorTypePhoneNumber|PKDataDetectorTypeLink|PKDataDetectorTypeAddress|PKDataDetectorTypeCalendarEvent)/, + "dataDetectorType", + ), + ), label: Joi.string().allow(""), - textAlignment: Joi.string().regex(/(PKTextAlignmentLeft|PKTextAlignmentCenter|PKTextAlignmentRight|PKTextAlignmentNatural)/, "graphic-alignment"), + textAlignment: Joi.string().regex( + /(PKTextAlignmentLeft|PKTextAlignmentCenter|PKTextAlignmentRight|PKTextAlignmentNatural)/, + "graphic-alignment", + ), key: Joi.string().required(), - value: Joi.alternatives(Joi.string().allow(""), Joi.number(), Joi.date().iso()).required(), + value: Joi.alternatives( + Joi.string().allow(""), + Joi.number(), + Joi.date().iso(), + ).required(), semantics, // date fields formatters, all optionals - dateStyle: Joi.string().regex(/(PKDateStyleNone|PKDateStyleShort|PKDateStyleMedium|PKDateStyleLong|PKDateStyleFull)/, "date style"), + dateStyle: Joi.string().regex( + /(PKDateStyleNone|PKDateStyleShort|PKDateStyleMedium|PKDateStyleLong|PKDateStyleFull)/, + "date style", + ), ignoreTimeZone: Joi.boolean(), isRelative: Joi.boolean(), - timeStyle: Joi.string().regex(/(PKDateStyleNone|PKDateStyleShort|PKDateStyleMedium|PKDateStyleLong|PKDateStyleFull)/, "date style"), + timeStyle: Joi.string().regex( + /(PKDateStyleNone|PKDateStyleShort|PKDateStyleMedium|PKDateStyleLong|PKDateStyleFull)/, + "date style", + ), // number fields formatters, all optionals - currencyCode: Joi.string() - .when("value", { - is: Joi.number(), - otherwise: Joi.string().forbidden() - }), + currencyCode: Joi.string().when("value", { + is: Joi.number(), + otherwise: Joi.string().forbidden(), + }), numberStyle: Joi.string() - .regex(/(PKNumberStyleDecimal|PKNumberStylePercent|PKNumberStyleScientific|PKNumberStyleSpellOut)/) + .regex( + /(PKNumberStyleDecimal|PKNumberStylePercent|PKNumberStyleScientific|PKNumberStyleSpellOut)/, + ) .when("value", { is: Joi.number(), - otherwise: Joi.string().forbidden() + otherwise: Joi.string().forbidden(), }), }); @@ -385,10 +444,14 @@ export interface Beacon { } const beaconsDict = Joi.object().keys({ - major: Joi.number().integer().positive().max(65535).greater(Joi.ref("minor")), + major: Joi.number() + .integer() + .positive() + .max(65535) + .greater(Joi.ref("minor")), minor: Joi.number().integer().min(0).max(65535), proximityUUID: Joi.string().required(), - relevantText: Joi.string() + relevantText: Joi.string(), }); export interface Location { @@ -402,7 +465,7 @@ const locationsDict = Joi.object().keys({ altitude: Joi.number(), latitude: Joi.number().required(), longitude: Joi.number().required(), - relevantText: Joi.string() + relevantText: Joi.string(), }); export interface PassFields { @@ -414,18 +477,29 @@ export interface PassFields { } const passDict = Joi.object().keys({ - auxiliaryFields: Joi.array().items(Joi.object().keys({ - row: Joi.number().max(1).min(0) - }).concat(field)), + auxiliaryFields: Joi.array().items( + Joi.object() + .keys({ + row: Joi.number().max(1).min(0), + }) + .concat(field), + ), backFields: Joi.array().items(field), headerFields: Joi.array().items(field), primaryFields: Joi.array().items(field), - secondaryFields: Joi.array().items(field) + secondaryFields: Joi.array().items(field), }); -export type TransitType = "PKTransitTypeAir" | "PKTransitTypeBoat" | "PKTransitTypeBus" | "PKTransitTypeGeneric" | "PKTransitTypeTrain"; +export type TransitType = + | "PKTransitTypeAir" + | "PKTransitTypeBoat" + | "PKTransitTypeBus" + | "PKTransitTypeGeneric" + | "PKTransitTypeTrain"; -const transitType = Joi.string().regex(/(PKTransitTypeAir|PKTransitTypeBoat|PKTransitTypeBus|PKTransitTypeGeneric|PKTransitTypeTrain)/); +const transitType = Joi.string().regex( + /(PKTransitTypeAir|PKTransitTypeBoat|PKTransitTypeBus|PKTransitTypeGeneric|PKTransitTypeTrain)/, +); export interface NFC { message: string; @@ -434,7 +508,7 @@ export interface NFC { const nfcDict = Joi.object().keys({ message: Joi.string().required().max(64), - encryptionPublicKey: Joi.string() + encryptionPublicKey: Joi.string(), }); // ************************************* // @@ -447,11 +521,20 @@ export interface Personalization { termsAndConditions?: string; } -type PRSField = "PKPassPersonalizationFieldName" | "PKPassPersonalizationFieldPostalCode" | "PKPassPersonalizationFieldEmailAddress" | "PKPassPersonalizationFieldPhoneNumber"; +type PRSField = + | "PKPassPersonalizationFieldName" + | "PKPassPersonalizationFieldPostalCode" + | "PKPassPersonalizationFieldEmailAddress" + | "PKPassPersonalizationFieldPhoneNumber"; const personalizationDict = Joi.object().keys({ requiredPersonalizationFields: Joi.array() - .items("PKPassPersonalizationFieldName", "PKPassPersonalizationFieldPostalCode", "PKPassPersonalizationFieldEmailAddress", "PKPassPersonalizationFieldPhoneNumber") + .items( + "PKPassPersonalizationFieldName", + "PKPassPersonalizationFieldPostalCode", + "PKPassPersonalizationFieldEmailAddress", + "PKPassPersonalizationFieldPhoneNumber", + ) .required(), description: Joi.string().required(), termsAndConditions: Joi.string(), @@ -470,7 +553,7 @@ const schemas = { transitType, nfcDict, supportedOptions, - personalizationDict + personalizationDict, }; export type Schema = keyof typeof schemas; @@ -491,14 +574,18 @@ export function isValid(opts: any, schemaName: Schema): boolean { const resolvedSchema = resolveSchemaName(schemaName); if (!resolvedSchema) { - schemaDebug(`validation failed due to missing or mispelled schema name`); + schemaDebug( + `validation failed due to missing or mispelled schema name`, + ); return false; } const validation = resolvedSchema.validate(opts); if (validation.error) { - schemaDebug(`validation failed due to error: ${validation.error.message}`); + schemaDebug( + `validation failed due to error: ${validation.error.message}`, + ); } return !validation.error; @@ -511,18 +598,25 @@ export function isValid(opts: any, schemaName: Schema): boolean { * @returns {object} the filtered value or empty object */ -export function getValidated(opts: any, schemaName: Schema): T | null { +export function getValidated( + opts: any, + schemaName: Schema, +): T | null { const resolvedSchema = resolveSchemaName(schemaName); if (!resolvedSchema) { - schemaDebug(`validation failed due to missing or mispelled schema name`); + schemaDebug( + `validation failed due to missing or mispelled schema name`, + ); return null; } const validation = resolvedSchema.validate(opts, { stripUnknown: true }); if (validation.error) { - schemaDebug(`Validation failed in getValidated due to error: ${validation.error.message}`); + schemaDebug( + `Validation failed in getValidated due to error: ${validation.error.message}`, + ); return null; } diff --git a/src/utils.ts b/src/utils.ts index 0fb9fdb..19912e9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -16,13 +16,15 @@ export function isValidRGB(value?: string): boolean { return false; } - const rgb = value.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/); + const rgb = value.match( + /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/, + ); if (!rgb) { return false; } - return rgb.slice(1, 4).every(v => Math.abs(Number(v)) <= 255); + return rgb.slice(1, 4).every((v) => Math.abs(Number(v)) <= 255); } /** @@ -57,7 +59,7 @@ export function dateToW3CString(date: Date) { */ export function removeHidden(from: Array): Array { - return from.filter(e => e.charAt(0) !== "."); + return from.filter((e) => e.charAt(0) !== "."); } /** @@ -77,8 +79,9 @@ export function generateStringFile(lang: { [index: string]: string }): Buffer { // Pass.strings format is the following one for each row: // "key" = "value"; - const strings = Object.keys(lang) - .map(key => `"${key}" = "${lang[key].replace(/"/g, '\"')}";`); + const strings = Object.keys(lang).map( + (key) => `"${key}" = "${lang[key].replace(/"/g, '"')}";`, + ); return Buffer.from(strings.join(EOL), "utf8"); } @@ -89,47 +92,73 @@ export function generateStringFile(lang: { [index: string]: string }): Buffer { * @param origin */ -type PartitionedBundleElements = [PartitionedBundle["l10nBundle"], PartitionedBundle["bundle"]]; +type PartitionedBundleElements = [ + PartitionedBundle["l10nBundle"], + PartitionedBundle["bundle"], +]; -export function splitBufferBundle(origin: BundleUnit): PartitionedBundleElements { +export function splitBufferBundle( + origin: BundleUnit, +): PartitionedBundleElements { const initialValue: PartitionedBundleElements = [{}, {}]; if (!origin) { return initialValue; } - return Object.entries(origin).reduce(([l10n, bundle], [key, buffer]) => { - if (!key.includes(".lproj")) { - return [ - l10n, - { - ...bundle, - [key]: buffer - } - ]; - } + return Object.entries(origin).reduce( + ([l10n, bundle], [key, buffer]) => { + if (!key.includes(".lproj")) { + return [ + l10n, + { + ...bundle, + [key]: buffer, + }, + ]; + } - const pathComponents = key.split(sep); - const lang = pathComponents[0]; - const file = pathComponents.slice(1).join("/"); + const pathComponents = key.split(sep); + const lang = pathComponents[0]; + const file = pathComponents.slice(1).join("/"); - (l10n[lang] || (l10n[lang] = {}))[file] = buffer; + (l10n[lang] || (l10n[lang] = {}))[file] = buffer; - return [l10n, bundle]; - }, initialValue); + return [l10n, bundle]; + }, + initialValue, + ); } type StringSearchMode = "includes" | "startsWith" | "endsWith"; -export function getAllFilesWithName(name: string, source: string[], mode: StringSearchMode = "includes", forceLowerCase: boolean = false): string[] { - return source.filter(file => (forceLowerCase && file.toLowerCase() || file)[mode](name)); +export function getAllFilesWithName( + name: string, + source: string[], + mode: StringSearchMode = "includes", + forceLowerCase: boolean = false, +): string[] { + return source.filter((file) => + ((forceLowerCase && file.toLowerCase()) || file)[mode](name), + ); } -export function hasFilesWithName(name: string, source: string[], mode: StringSearchMode = "includes", forceLowerCase: boolean = false): boolean { - return source.some(file => (forceLowerCase && file.toLowerCase() || file)[mode](name)); +export function hasFilesWithName( + name: string, + source: string[], + mode: StringSearchMode = "includes", + forceLowerCase: boolean = false, +): boolean { + return source.some((file) => + ((forceLowerCase && file.toLowerCase()) || file)[mode](name), + ); } -export function deletePersonalization(source: BundleUnit, logosNames: string[] = []): void { - [...logosNames, "personalization.json"] - .forEach(file => delete source[file]); +export function deletePersonalization( + source: BundleUnit, + logosNames: string[] = [], +): void { + [...logosNames, "personalization.json"].forEach( + (file) => delete source[file], + ); }