passkit-generator
Advanced tools
Comparing version 2.0.6 to 2.0.7
@@ -1,12 +0,12 @@ | ||
import { Certificates, FinalCertificates, PartitionedBundle, OverridesSupportedOptions, FactoryOptions } from "./schema"; | ||
import * as Schemas from "./schemas"; | ||
declare const abmCertificates: unique symbol; | ||
declare const abmModel: unique symbol; | ||
declare const abmOverrides: unique symbol; | ||
export interface AbstractFactoryOptions extends Omit<FactoryOptions, "certificates"> { | ||
certificates?: Certificates; | ||
export interface AbstractFactoryOptions extends Omit<Schemas.FactoryOptions, "certificates"> { | ||
certificates?: Schemas.Certificates; | ||
} | ||
interface AbstractModelOptions { | ||
bundle: PartitionedBundle; | ||
certificates: FinalCertificates; | ||
overrides?: OverridesSupportedOptions; | ||
bundle: Schemas.PartitionedBundle; | ||
certificates: Schemas.CertificatesSchema; | ||
overrides?: Schemas.OverridesSupportedOptions; | ||
} | ||
@@ -24,6 +24,6 @@ /** | ||
constructor(options: AbstractModelOptions); | ||
get certificates(): FinalCertificates; | ||
get bundle(): PartitionedBundle; | ||
get overrides(): OverridesSupportedOptions; | ||
get certificates(): Schemas.CertificatesSchema; | ||
get bundle(): Schemas.PartitionedBundle; | ||
get overrides(): Schemas.OverridesSupportedOptions; | ||
} | ||
export {}; |
@@ -6,3 +6,3 @@ "use strict"; | ||
const parser_1 = require("./parser"); | ||
const messages_1 = tslib_1.__importDefault(require("./messages")); | ||
const messages_1 = tslib_1.__importStar(require("./messages")); | ||
const abmCertificates = Symbol("certificates"); | ||
@@ -18,3 +18,3 @@ const abmModel = Symbol("model"); | ||
if (!(options && Object.keys(options).length)) { | ||
throw new Error(messages_1.default("CP_NO_OPTS")); | ||
throw new Error(messages_1.default(messages_1.ERROR.CP_NO_OPTS)); | ||
} | ||
@@ -33,3 +33,3 @@ try { | ||
catch (err) { | ||
throw new Error(messages_1.default("CP_INIT_ERROR", "abstract model", err)); | ||
throw new Error(messages_1.default(messages_1.ERROR.CP_INIT, "abstract model", err)); | ||
} | ||
@@ -36,0 +36,0 @@ } |
import { Pass } from "./pass"; | ||
import { FactoryOptions, BundleUnit } from "./schema"; | ||
import * as Schemas from "./schemas"; | ||
import { AbstractModel, AbstractFactoryOptions } from "./abstract"; | ||
@@ -11,2 +11,2 @@ /** | ||
*/ | ||
export declare function createPass(options: FactoryOptions | InstanceType<typeof AbstractModel>, additionalBuffers?: BundleUnit, abstractMissingData?: Omit<AbstractFactoryOptions, "model">): Promise<Pass>; | ||
export declare function createPass(options: Schemas.FactoryOptions | InstanceType<typeof AbstractModel>, additionalBuffers?: Schemas.BundleUnit, abstractMissingData?: Omit<AbstractFactoryOptions, "model">): Promise<Pass>; |
@@ -6,3 +6,3 @@ "use strict"; | ||
const pass_1 = require("./pass"); | ||
const messages_1 = tslib_1.__importDefault(require("./messages")); | ||
const messages_1 = tslib_1.__importStar(require("./messages")); | ||
const parser_1 = require("./parser"); | ||
@@ -21,3 +21,3 @@ const utils_1 = require("./utils"); | ||
(options instanceof abstract_1.AbstractModel || Object.keys(options).length))) { | ||
throw new Error(messages_1.default("CP_NO_OPTS")); | ||
throw new Error(messages_1.default(messages_1.ERROR.CP_NO_OPTS)); | ||
} | ||
@@ -52,3 +52,3 @@ try { | ||
catch (err) { | ||
throw new Error(messages_1.default("CP_INIT_ERROR", "pass", err)); | ||
throw new Error(messages_1.default(messages_1.ERROR.CP_INIT, "pass", err)); | ||
} | ||
@@ -55,0 +55,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import * as schema from "./schema"; | ||
import * as Schemas from "./schemas"; | ||
/** | ||
@@ -14,3 +14,3 @@ * Class to represent lower-level keys pass fields | ||
*/ | ||
push(...fieldsData: schema.Field[]): number; | ||
push(...fieldsData: Schemas.Field[]): number; | ||
/** | ||
@@ -20,3 +20,3 @@ * Like `Array.prototype.pop`, but will alter | ||
*/ | ||
pop(): schema.Field; | ||
pop(): Schemas.Field; | ||
/** | ||
@@ -26,5 +26,5 @@ * Like `Array.prototype.splice` but will alter | ||
*/ | ||
splice(start: number, deleteCount: number, ...items: schema.Field[]): schema.Field[]; | ||
splice(start: number, deleteCount: number, ...items: Schemas.Field[]): Schemas.Field[]; | ||
get length(): number; | ||
} | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const tslib_1 = require("tslib"); | ||
const schema = tslib_1.__importStar(require("./schema")); | ||
const Schemas = tslib_1.__importStar(require("./schemas")); | ||
const debug_1 = tslib_1.__importDefault(require("debug")); | ||
@@ -24,3 +24,3 @@ const fieldsDebug = debug_1.default("passkit:fields"); | ||
if (!(typeof current === "object") || | ||
!schema.isValid(current, "field")) { | ||
!Schemas.isValid(current, Schemas.Field)) { | ||
return acc; | ||
@@ -27,0 +27,0 @@ } |
@@ -1,30 +0,30 @@ | ||
declare const errors: { | ||
CP_INIT_ERROR: string; | ||
CP_NO_OPTS: string; | ||
CP_NO_CERTS: string; | ||
PASSFILE_VALIDATION_FAILED: string; | ||
REQUIR_VALID_FAILED: string; | ||
MODEL_UNINITIALIZED: string; | ||
MODEL_NOT_VALID: string; | ||
MODELF_NOT_FOUND: string; | ||
MODELF_FILE_NOT_FOUND: string; | ||
INVALID_CERTS: string; | ||
INVALID_CERT_PATH: string; | ||
TRSTYPE_REQUIRED: string; | ||
OVV_KEYS_BADFORMAT: string; | ||
NO_PASS_TYPE: string; | ||
export declare const ERROR: { | ||
readonly CP_INIT: "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"; | ||
readonly CP_NO_OPTS: "Cannot initialize the pass or abstract model creation: no options were passed."; | ||
readonly CP_NO_CERTS: "Cannot initialize the pass creation: no valid certificates were passed."; | ||
readonly 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."; | ||
readonly REQUIR_VALID_FAILED: "The options passed to Pass constructor does not meet the requirements.\nRefer to the documentation to compile them correctly."; | ||
readonly 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."; | ||
readonly MODEL_NOT_VALID: "A model must be provided in form of path (string) or object { 'fileName': Buffer } in order to continue."; | ||
readonly MODELF_NOT_FOUND: "Model %s not found. Provide a valid one to continue."; | ||
readonly MODELF_FILE_NOT_FOUND: "File %s not found."; | ||
readonly 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."; | ||
readonly INVALID_CERT_PATH: "Invalid certificate loaded. %s does not exist."; | ||
readonly TRSTYPE_REQUIRED: "Cannot proceed with pass creation. transitType field is required for boardingPasses."; | ||
readonly OVV_KEYS_BADFORMAT: "Cannot proceed with pass creation due to bad keys format in overrides."; | ||
readonly 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."; | ||
}; | ||
declare const debugMessages: { | ||
TRSTYPE_NOT_VALID: string; | ||
BRC_NOT_SUPPORTED: string; | ||
BRC_FORMATTYPE_UNMATCH: string; | ||
BRC_AUTC_MISSING_DATA: string; | ||
BRC_BW_FORMAT_UNSUPPORTED: string; | ||
BRC_NO_POOL: string; | ||
DATE_FORMAT_UNMATCH: string; | ||
NFC_INVALID: string; | ||
PRS_INVALID: string; | ||
PRS_REMOVED: string; | ||
export declare const DEBUG: { | ||
readonly 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]"; | ||
readonly BRC_NOT_SUPPORTED: "Format not found among barcodes. Cannot set backward compatibility."; | ||
readonly BRC_FORMATTYPE_UNMATCH: "Format must be a string or null. Cannot set backward compatibility."; | ||
readonly BRC_AUTC_MISSING_DATA: "Unable to autogenerate barcodes. Data is not a string."; | ||
readonly BRC_BW_FORMAT_UNSUPPORTED: "This format is not supported (by Apple) for backward support. Please choose another one."; | ||
readonly BRC_NO_POOL: "Cannot set barcode: no barcodes found. Please set barcodes first. Barcode is for retrocompatibility only."; | ||
readonly DATE_FORMAT_UNMATCH: "%s was not set due to incorrect date format."; | ||
readonly NFC_INVALID: "Unable to set NFC properties: data not compliant with schema."; | ||
readonly PRS_INVALID: "Unable to parse Personalization.json. File is not a valid JSON. Error: %s"; | ||
readonly PRS_REMOVED: "Personalization has been removed as it requires an NFC-enabled pass to work."; | ||
}; | ||
declare type AllMessages = keyof (typeof debugMessages & typeof errors); | ||
declare type ERROR_OR_DEBUG_MESSAGE = typeof ERROR[keyof typeof ERROR] | typeof DEBUG[keyof typeof DEBUG]; | ||
/** | ||
@@ -35,3 +35,3 @@ * Creates a message with replaced values | ||
*/ | ||
export default function format(messageName: AllMessages, ...values: any[]): string; | ||
export default function format(messageName: ERROR_OR_DEBUG_MESSAGE, ...values: any[]): string; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
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", | ||
exports.DEBUG = exports.ERROR = void 0; | ||
exports.ERROR = { | ||
CP_INIT: "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.", | ||
@@ -19,3 +20,3 @@ CP_NO_CERTS: "Cannot initialize the pass creation: no valid certificates were passed.", | ||
}; | ||
const debugMessages = { | ||
exports.DEBUG = { | ||
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]', | ||
@@ -40,3 +41,3 @@ BRC_NOT_SUPPORTED: "Format not found among barcodes. Cannot set backward compatibility.", | ||
let replaceValues = values.reverse(); | ||
return resolveMessageName(messageName).replace(/%s/g, () => { | ||
return messageName.replace(/%s/g, () => { | ||
let next = replaceValues.pop(); | ||
@@ -47,12 +48,2 @@ return next !== undefined ? next : "<passedValueIsUndefined>"; | ||
exports.default = format; | ||
/** | ||
* Looks among errors and debugMessages for the specified message name | ||
* @param {string} name | ||
*/ | ||
function resolveMessageName(name) { | ||
if (!errors[name] && !debugMessages[name]) { | ||
return `<ErrorName "${name}" is not linked to any error messages>`; | ||
} | ||
return errors[name] || debugMessages[name]; | ||
} | ||
//# sourceMappingURL=messages.js.map |
@@ -1,2 +0,2 @@ | ||
import { FactoryOptions, PartitionedBundle, BundleUnit, Certificates, FinalCertificates } from "./schema"; | ||
import * as Schemas from "./schemas"; | ||
/** | ||
@@ -7,3 +7,3 @@ * Performs checks on the passed model to | ||
*/ | ||
export declare function getModelContents(model: FactoryOptions["model"]): Promise<PartitionedBundle>; | ||
export declare function getModelContents(model: Schemas.FactoryOptions["model"]): Promise<Schemas.PartitionedBundle>; | ||
/** | ||
@@ -14,3 +14,3 @@ * Reads and model contents and creates a splitted | ||
*/ | ||
export declare function getModelFolderContents(model: string): Promise<PartitionedBundle>; | ||
export declare function getModelFolderContents(model: string): Promise<Schemas.PartitionedBundle>; | ||
/** | ||
@@ -21,3 +21,3 @@ * Analyzes the passed buffer model and splits it to | ||
*/ | ||
export declare function getModelBufferContents(model: BundleUnit): PartitionedBundle; | ||
export declare function readCertificatesFromOptions(options: Certificates): Promise<FinalCertificates>; | ||
export declare function getModelBufferContents(model: Schemas.BundleUnit): Schemas.PartitionedBundle; | ||
export declare function readCertificatesFromOptions(options: Schemas.Certificates): Promise<Schemas.CertificatesSchema>; |
@@ -7,4 +7,4 @@ "use strict"; | ||
const node_forge_1 = tslib_1.__importDefault(require("node-forge")); | ||
const messages_1 = tslib_1.__importDefault(require("./messages")); | ||
const schema_1 = require("./schema"); | ||
const messages_1 = tslib_1.__importStar(require("./messages")); | ||
const Schemas = tslib_1.__importStar(require("./schemas")); | ||
const utils_1 = require("./utils"); | ||
@@ -29,3 +29,3 @@ const fs_1 = tslib_1.__importDefault(require("fs")); | ||
else { | ||
throw new Error(messages_1.default("MODEL_NOT_VALID")); | ||
throw new Error(messages_1.default(messages_1.ERROR.MODEL_NOT_VALID)); | ||
} | ||
@@ -36,3 +36,3 @@ const modelFiles = Object.keys(modelContents.bundle); | ||
if (!isModelInitialized) { | ||
throw new Error(messages_1.default("MODEL_UNINITIALIZED", "parse result")); | ||
throw new Error(messages_1.default(messages_1.ERROR.MODEL_UNINITIALIZED, "parse result")); | ||
} | ||
@@ -54,3 +54,3 @@ // ======================= // | ||
const parsedPersonalization = JSON.parse(modelContents.bundle[personalizationJsonFile].toString("utf8")); | ||
const isPersonalizationValid = schema_1.isValid(parsedPersonalization, "personalizationDict"); | ||
const isPersonalizationValid = Schemas.isValid(parsedPersonalization, Schemas.Personalization); | ||
if (!isPersonalizationValid) { | ||
@@ -62,3 +62,3 @@ [...logoFullNames, personalizationJsonFile].forEach((file) => delete modelContents.bundle[file]); | ||
catch (err) { | ||
prsDebug(messages_1.default("PRS_INVALID", err)); | ||
prsDebug(messages_1.default(messages_1.DEBUG.PRS_INVALID, err)); | ||
utils_1.deletePersonalization(modelContents.bundle, logoFullNames); | ||
@@ -85,3 +85,3 @@ } | ||
if (!isModelInitialized) { | ||
throw new Error(messages_1.default("MODEL_UNINITIALIZED", path.parse(model).name)); | ||
throw new Error(messages_1.default(messages_1.ERROR.MODEL_UNINITIALIZED, path.parse(model).name)); | ||
} | ||
@@ -131,3 +131,3 @@ // Splitting files from localization folders | ||
// file opening failed | ||
throw new Error(messages_1.default("MODELF_NOT_FOUND", err.path)); | ||
throw new Error(messages_1.default(messages_1.ERROR.MODELF_NOT_FOUND, err.path)); | ||
} | ||
@@ -137,3 +137,3 @@ else if (err.syscall === "scandir") { | ||
const pathContents = err.path.split(/(\/|\\\?)/); | ||
throw new Error(messages_1.default("MODELF_FILE_NOT_FOUND", pathContents[pathContents.length - 1])); | ||
throw new Error(messages_1.default(messages_1.ERROR.MODELF_FILE_NOT_FOUND, pathContents[pathContents.length - 1])); | ||
} | ||
@@ -163,3 +163,3 @@ } | ||
if (!isModelInitialized) { | ||
throw new Error(messages_1.default("MODEL_UNINITIALIZED", "Buffers")); | ||
throw new Error(messages_1.default(messages_1.ERROR.MODEL_UNINITIALIZED, "Buffers")); | ||
} | ||
@@ -178,4 +178,4 @@ // separing localization folders from bundle files | ||
Object.keys(options).length && | ||
schema_1.isValid(options, "certificatesSchema"))) { | ||
throw new Error(messages_1.default("CP_NO_CERTS")); | ||
Schemas.isValid(options, Schemas.CertificatesSchema))) { | ||
throw new Error(messages_1.default(messages_1.ERROR.CP_NO_CERTS)); | ||
} | ||
@@ -211,7 +211,8 @@ let signerKey; | ||
const certName = Object.keys(options)[index]; | ||
const passphrase = (typeof options.signerKey === "object" && ((_a = options.signerKey) === null || _a === void 0 ? void 0 : _a.passphrase)) || | ||
const passphrase = (typeof options.signerKey === "object" && | ||
((_a = options.signerKey) === null || _a === void 0 ? void 0 : _a.passphrase)) || | ||
undefined; | ||
const pem = parsePEM(certName, file, passphrase); | ||
if (!pem) { | ||
throw new Error(messages_1.default("INVALID_CERTS", certName)); | ||
throw new Error(messages_1.default(messages_1.ERROR.INVALID_CERTS, certName)); | ||
} | ||
@@ -226,3 +227,3 @@ return { [certName]: pem }; | ||
} | ||
throw new Error(messages_1.default("INVALID_CERT_PATH", path.parse(err.path).base)); | ||
throw new Error(messages_1.default(messages_1.ERROR.INVALID_CERT_PATH, path.parse(err.path).base)); | ||
} | ||
@@ -229,0 +230,0 @@ } |
/// <reference types="node" /> | ||
import { Stream } from "stream"; | ||
import * as schema from "./schema"; | ||
import * as Schemas from "./schemas"; | ||
import FieldsArray from "./fieldsArray"; | ||
@@ -15,11 +15,11 @@ declare const transitType: unique symbol; | ||
private passCore; | ||
headerFields: FieldsArray | undefined; | ||
primaryFields: FieldsArray | undefined; | ||
secondaryFields: FieldsArray | undefined; | ||
auxiliaryFields: FieldsArray | undefined; | ||
backFields: FieldsArray | undefined; | ||
headerFields: FieldsArray; | ||
primaryFields: FieldsArray; | ||
secondaryFields: FieldsArray; | ||
auxiliaryFields: FieldsArray; | ||
backFields: FieldsArray; | ||
private Certificates; | ||
private [transitType]; | ||
private l10nTranslations; | ||
constructor(options: schema.PassInstance); | ||
constructor(options: Schemas.PassInstance); | ||
/** | ||
@@ -68,3 +68,3 @@ * Generates the pass Stream | ||
beacons(resetFlag: null): this; | ||
beacons(...data: schema.Beacon[]): this; | ||
beacons(...data: Schemas.Beacon[]): this; | ||
/** | ||
@@ -76,3 +76,3 @@ * Sets current pass' relevancy through locations | ||
locations(resetFlag: null): this; | ||
locations(...data: schema.Location[]): this; | ||
locations(...data: Schemas.Location[]): this; | ||
/** | ||
@@ -96,3 +96,3 @@ * Sets current pass' relevancy through a date | ||
barcodes(message: string): this; | ||
barcodes(...data: schema.Barcode[]): this; | ||
barcodes(...data: Schemas.Barcode[]): this; | ||
/** | ||
@@ -107,3 +107,3 @@ * Given an index <= the amount of already set "barcodes", | ||
*/ | ||
barcode(chosenFormat: schema.BarcodeFormat | null): this; | ||
barcode(chosenFormat: Schemas.BarcodeFormat | null): this; | ||
/** | ||
@@ -117,3 +117,3 @@ * Sets nfc fields in properties | ||
*/ | ||
nfc(data: schema.NFC | null): this; | ||
nfc(data: Schemas.NFC | null): this; | ||
/** | ||
@@ -126,12 +126,4 @@ * Allows to get the current inserted props; | ||
*/ | ||
get props(): Readonly<schema.ValidPass>; | ||
get props(): Readonly<Schemas.ValidPass>; | ||
/** | ||
* Generates the PKCS #7 cryptografic signature for the manifest file. | ||
* | ||
* @method _sign | ||
* @params {Object} manifest - Manifest content. | ||
* @returns {Buffer} | ||
*/ | ||
private _sign; | ||
/** | ||
* Edits the buffer of pass.json based on the passed options. | ||
@@ -138,0 +130,0 @@ * |
192
lib/pass.js
@@ -11,6 +11,7 @@ "use strict"; | ||
const yazl_1 = require("yazl"); | ||
const schema = tslib_1.__importStar(require("./schema")); | ||
const messages_1 = tslib_1.__importDefault(require("./messages")); | ||
const Schemas = tslib_1.__importStar(require("./schemas")); | ||
const messages_1 = tslib_1.__importStar(require("./messages")); | ||
const fieldsArray_1 = tslib_1.__importDefault(require("./fieldsArray")); | ||
const utils_1 = require("./utils"); | ||
const Signature = tslib_1.__importStar(require("./signature")); | ||
const barcodeDebug = debug_1.default("passkit:barcode"); | ||
@@ -21,10 +22,17 @@ const genericDebug = debug_1.default("passkit:generic"); | ||
const propsSchemaMap = new Map([ | ||
["barcodes", "barcode"], | ||
["barcode", "barcode"], | ||
["beacons", "beaconsDict"], | ||
["locations", "locationsDict"], | ||
["nfc", "nfcDict"], | ||
["barcodes", Schemas.Barcode], | ||
["barcode", Schemas.Barcode], | ||
["beacons", Schemas.Beacon], | ||
["locations", Schemas.Location], | ||
["nfc", Schemas.NFC], | ||
]); | ||
class Pass { | ||
constructor(options) { | ||
this._fields = [ | ||
"primaryFields", | ||
"secondaryFields", | ||
"auxiliaryFields", | ||
"backFields", | ||
"headerFields", | ||
]; | ||
this[_a] = {}; | ||
@@ -34,4 +42,4 @@ this.fieldsKeys = new Set(); | ||
this.l10nTranslations = {}; | ||
if (!schema.isValid(options, "instance")) { | ||
throw new Error(messages_1.default("REQUIR_VALID_FAILED")); | ||
if (!Schemas.isValid(options, Schemas.PassInstance)) { | ||
throw new Error(messages_1.default(messages_1.ERROR.REQUIR_VALID_FAILED)); | ||
} | ||
@@ -45,12 +53,12 @@ this.Certificates = options.certificates; | ||
catch (err) { | ||
throw new Error(messages_1.default("PASSFILE_VALIDATION_FAILED")); | ||
throw new Error(messages_1.default(messages_1.ERROR.PASSFILE_VALIDATION_FAILED)); | ||
} | ||
// Parsing the options and extracting only the valid ones. | ||
const validOverrides = schema.getValidated(options.overrides || {}, "supportedOptions"); | ||
const validOverrides = Schemas.getValidated(options.overrides || {}, Schemas.OverridesSupportedOptions); | ||
if (validOverrides === null) { | ||
throw new Error(messages_1.default("OVV_KEYS_BADFORMAT")); | ||
throw new Error(messages_1.default(messages_1.ERROR.OVV_KEYS_BADFORMAT)); | ||
} | ||
this.type = Object.keys(this.passCore).find((key) => /(boardingPass|eventTicket|coupon|generic|storeCard)/.test(key)); | ||
if (!this.type) { | ||
throw new Error(messages_1.default("NO_PASS_TYPE")); | ||
throw new Error(messages_1.default(messages_1.ERROR.NO_PASS_TYPE)); | ||
} | ||
@@ -74,3 +82,6 @@ // Parsing and validating pass.json keys | ||
const valid = getValidInArray(currentSchema, this.passCore[current]); | ||
return { ...acc, [current]: valid }; | ||
return { | ||
...acc, | ||
[current]: valid, | ||
}; | ||
} | ||
@@ -80,3 +91,3 @@ else { | ||
...acc, | ||
[current]: (schema.isValid(this.passCore[current], currentSchema) && | ||
[current]: (Schemas.isValid(this.passCore[current], currentSchema) && | ||
this.passCore[current]) || | ||
@@ -97,11 +108,4 @@ undefined, | ||
} | ||
this._fields = [ | ||
"primaryFields", | ||
"secondaryFields", | ||
"auxiliaryFields", | ||
"backFields", | ||
"headerFields", | ||
]; | ||
this._fields.forEach((fieldName) => { | ||
this[fieldName] = new fieldsArray_1.default(this.fieldsKeys, ...(this.passCore[this.type][fieldName] || []).filter((field) => schema.isValid(field, "field"))); | ||
this[fieldName] = new fieldsArray_1.default(this.fieldsKeys, ...(this.passCore[this.type][fieldName] || []).filter((field) => Schemas.isValid(field, Schemas.Field))); | ||
}); | ||
@@ -116,2 +120,4 @@ } | ||
generate() { | ||
var _c; | ||
var _d; | ||
// Editing Pass.json | ||
@@ -126,3 +132,3 @@ this.bundle["pass.json"] = this._patch(this.bundle["pass.json"]); | ||
currentBundleFiles.includes("personalization.json")) { | ||
genericDebug(messages_1.default("PRS_REMOVED")); | ||
genericDebug(messages_1.default(messages_1.DEBUG.PRS_REMOVED)); | ||
utils_1.deletePersonalization(this.bundle, utils_1.getAllFilesWithName("personalizationLogo", currentBundleFiles, "startsWith")); | ||
@@ -134,5 +140,6 @@ } | ||
*/ | ||
Object.keys(this.l10nTranslations).forEach((lang) => { | ||
const translationsLanguageCodes = Object.keys(this.l10nTranslations); | ||
for (let langs = translationsLanguageCodes.length, lang; (lang = translationsLanguageCodes[--langs]);) { | ||
const strings = utils_1.generateStringFile(this.l10nTranslations[lang]); | ||
const langInBundles = `${lang}.lproj`; | ||
const languageBundleDirname = `${lang}.lproj`; | ||
if (strings.length) { | ||
@@ -144,14 +151,11 @@ /** | ||
*/ | ||
if (!this.l10nBundles[langInBundles]) { | ||
this.l10nBundles[langInBundles] = {}; | ||
} | ||
this.l10nBundles[langInBundles]["pass.strings"] = Buffer.concat([ | ||
this.l10nBundles[langInBundles]["pass.strings"] || | ||
Buffer.alloc(0), | ||
const languageBundleUnit = ((_c = (_d = this.l10nBundles)[languageBundleDirname]) !== null && _c !== void 0 ? _c : (_d[languageBundleDirname] = {})); | ||
languageBundleUnit["pass.strings"] = Buffer.concat([ | ||
languageBundleUnit["pass.strings"] || Buffer.alloc(0), | ||
strings, | ||
]); | ||
} | ||
if (!(this.l10nBundles[langInBundles] && | ||
Object.keys(this.l10nBundles[langInBundles]).length)) { | ||
return; | ||
if (!this.l10nBundles[languageBundleDirname] || | ||
!Object.keys(this.l10nBundles[languageBundleDirname]).length) { | ||
continue; | ||
} | ||
@@ -165,11 +169,13 @@ /** | ||
*/ | ||
Object.assign(finalBundle, ...Object.keys(this.l10nBundles[langInBundles]).map((fileName) => { | ||
const bundleRelativeL10NPaths = Object.entries(this.l10nBundles[languageBundleDirname]).reduce((acc, [fileName, fileContent]) => { | ||
const fullPath = path_1.default | ||
.join(langInBundles, fileName) | ||
.join(languageBundleDirname, fileName) | ||
.replace(/\\/, "/"); | ||
return { | ||
[fullPath]: this.l10nBundles[langInBundles][fileName], | ||
...acc, | ||
[fullPath]: fileContent, | ||
}; | ||
})); | ||
}); | ||
}, {}); | ||
Object.assign(finalBundle, bundleRelativeL10NPaths); | ||
} | ||
/* | ||
@@ -180,10 +186,12 @@ * Parsing the buffers, pushing them into the archive | ||
const archive = new yazl_1.ZipFile(); | ||
const manifest = Object.keys(finalBundle).reduce((acc, current) => { | ||
const manifest = Object.entries(finalBundle).reduce((acc, [fileName, buffer]) => { | ||
let hashFlow = node_forge_1.default.md.sha1.create(); | ||
hashFlow.update(finalBundle[current].toString("binary")); | ||
archive.addBuffer(finalBundle[current], current); | ||
acc[current] = hashFlow.digest().toHex(); | ||
return acc; | ||
hashFlow.update(buffer.toString("binary")); | ||
archive.addBuffer(buffer, fileName); | ||
return { | ||
...acc, | ||
[fileName]: hashFlow.digest().toHex(), | ||
}; | ||
}, {}); | ||
const signatureBuffer = this._sign(manifest); | ||
const signatureBuffer = Signature.create(manifest, this.Certificates); | ||
archive.addBuffer(signatureBuffer, "signature"); | ||
@@ -249,3 +257,3 @@ archive.addBuffer(Buffer.from(JSON.stringify(manifest)), "manifest.json"); | ||
} | ||
const valid = processRelevancySet("beacons", data); | ||
const valid = getValidInArray(Schemas.Beacon, data); | ||
if (valid.length) { | ||
@@ -261,3 +269,3 @@ this[passProps]["beacons"] = valid; | ||
} | ||
const valid = processRelevancySet("locations", data); | ||
const valid = getValidInArray(Schemas.Location, data); | ||
if (valid.length) { | ||
@@ -292,3 +300,3 @@ this[passProps]["locations"] = valid; | ||
if (!autogen.length) { | ||
barcodeDebug(messages_1.default("BRC_AUTC_MISSING_DATA")); | ||
barcodeDebug(messages_1.default(messages_1.DEBUG.BRC_AUTC_MISSING_DATA)); | ||
return this; | ||
@@ -309,3 +317,3 @@ } | ||
} | ||
const validated = schema.getValidated(current, "barcode"); | ||
const validated = Schemas.getValidated(current, Schemas.Barcode); | ||
if (!(validated && | ||
@@ -340,11 +348,11 @@ validated instanceof Object && | ||
if (typeof chosenFormat !== "string") { | ||
barcodeDebug(messages_1.default("BRC_FORMATTYPE_UNMATCH")); | ||
barcodeDebug(messages_1.default(messages_1.DEBUG.BRC_FORMATTYPE_UNMATCH)); | ||
return this; | ||
} | ||
if (chosenFormat === "PKBarcodeFormatCode128") { | ||
barcodeDebug(messages_1.default("BRC_BW_FORMAT_UNSUPPORTED")); | ||
barcodeDebug(messages_1.default(messages_1.DEBUG.BRC_BW_FORMAT_UNSUPPORTED)); | ||
return this; | ||
} | ||
if (!(barcodes && barcodes.length)) { | ||
barcodeDebug(messages_1.default("BRC_NO_POOL")); | ||
barcodeDebug(messages_1.default(messages_1.DEBUG.BRC_NO_POOL)); | ||
return this; | ||
@@ -355,3 +363,3 @@ } | ||
if (index === -1) { | ||
barcodeDebug(messages_1.default("BRC_NOT_SUPPORTED")); | ||
barcodeDebug(messages_1.default(messages_1.DEBUG.BRC_NOT_SUPPORTED)); | ||
return this; | ||
@@ -378,4 +386,4 @@ } | ||
!Array.isArray(data) && | ||
schema.isValid(data, "nfcDict"))) { | ||
genericDebug(messages_1.default("NFC_INVALID")); | ||
Schemas.isValid(data, Schemas.NFC))) { | ||
genericDebug(messages_1.default(messages_1.DEBUG.NFC_INVALID)); | ||
return this; | ||
@@ -397,59 +405,2 @@ } | ||
/** | ||
* Generates the PKCS #7 cryptografic signature for the manifest file. | ||
* | ||
* @method _sign | ||
* @params {Object} manifest - Manifest content. | ||
* @returns {Buffer} | ||
*/ | ||
_sign(manifest) { | ||
const signature = node_forge_1.default.pkcs7.createSignedData(); | ||
signature.content = node_forge_1.default.util.createBuffer(JSON.stringify(manifest), "utf8"); | ||
signature.addCertificate(this.Certificates.wwdr); | ||
signature.addCertificate(this.Certificates.signerCert); | ||
/** | ||
* authenticatedAttributes belong to PKCS#9 standard. | ||
* It requires at least 2 values: | ||
* • content-type (which is a PKCS#7 oid) and | ||
* • message-digest oid. | ||
* | ||
* Wallet requires a signingTime. | ||
*/ | ||
signature.addSigner({ | ||
key: this.Certificates.signerKey, | ||
certificate: this.Certificates.signerCert, | ||
digestAlgorithm: node_forge_1.default.pki.oids.sha1, | ||
authenticatedAttributes: [ | ||
{ | ||
type: node_forge_1.default.pki.oids.contentType, | ||
value: node_forge_1.default.pki.oids.data, | ||
}, | ||
{ | ||
type: node_forge_1.default.pki.oids.messageDigest, | ||
}, | ||
{ | ||
type: node_forge_1.default.pki.oids.signingTime, | ||
}, | ||
], | ||
}); | ||
/** | ||
* We are creating a detached signature because we don't need the signed content. | ||
* Detached signature is a property of PKCS#7 cryptography standard. | ||
*/ | ||
signature.sign({ detached: true }); | ||
/** | ||
* Signature here is an ASN.1 valid structure (DER-compliant). | ||
* Generating a non-detached signature, would have pushed inside signature.contentInfo | ||
* (which has type 16, or "SEQUENCE", and is an array) a Context-Specific element, with the | ||
* signed content as value. | ||
* | ||
* In fact the previous approach was to generating a detached signature and the pull away the generated | ||
* content. | ||
* | ||
* That's what happens when you copy a fu****g line without understanding what it does. | ||
* Well, nevermind, it was funny to study BER, DER, CER, ASN.1 and PKCS#7. You can learn a lot | ||
* of beautiful things. ¯\_(ツ)_/¯ | ||
*/ | ||
return Buffer.from(node_forge_1.default.asn1.toDer(signature.toAsn1()).getBytes(), "binary"); | ||
} | ||
/** | ||
* Edits the buffer of pass.json based on the passed options. | ||
@@ -483,3 +434,3 @@ * | ||
if (this.type === "boardingPass" && !this[transitType]) { | ||
throw new Error(messages_1.default("TRSTYPE_REQUIRED")); | ||
throw new Error(messages_1.default(messages_1.ERROR.TRSTYPE_REQUIRED)); | ||
} | ||
@@ -490,4 +441,4 @@ passFile[this.type]["transitType"] = this[transitType]; | ||
set transitType(value) { | ||
if (!schema.isValid(value, "transitType")) { | ||
genericDebug(messages_1.default("TRSTYPE_NOT_VALID", value)); | ||
if (!Schemas.isValid(value, Schemas.TransitType)) { | ||
genericDebug(messages_1.default(messages_1.DEBUG.TRSTYPE_NOT_VALID, value)); | ||
this[transitType] = this[transitType] || ""; | ||
@@ -520,9 +471,6 @@ return; | ||
"PKBarcodeFormatCode128", | ||
].map((format) => schema.getValidated({ format, message }, "barcode")); | ||
].map((format) => Schemas.getValidated({ format, message }, Schemas.Barcode)); | ||
} | ||
function processRelevancySet(key, data) { | ||
return getValidInArray(`${key}Dict`, data); | ||
} | ||
function getValidInArray(schemaName, contents) { | ||
return contents.filter((current) => Object.keys(current).length && schema.isValid(current, schemaName)); | ||
return contents.filter((current) => Object.keys(current).length && Schemas.isValid(current, schemaName)); | ||
} | ||
@@ -535,3 +483,3 @@ function processDate(key, date) { | ||
if (!dateParse) { | ||
genericDebug(messages_1.default("DATE_FORMAT_UNMATCH", key)); | ||
genericDebug(messages_1.default(messages_1.DEBUG.DATE_FORMAT_UNMATCH, key)); | ||
return null; | ||
@@ -538,0 +486,0 @@ } |
/// <reference types="node" /> | ||
import { PartitionedBundle, BundleUnit } from "./schema"; | ||
import type * as Schemas from "./schemas"; | ||
/** | ||
@@ -45,10 +45,10 @@ * Checks if an rgb value is compliant with CSS-like syntax | ||
declare type PartitionedBundleElements = [ | ||
PartitionedBundle["l10nBundle"], | ||
PartitionedBundle["bundle"] | ||
Schemas.PartitionedBundle["l10nBundle"], | ||
Schemas.PartitionedBundle["bundle"] | ||
]; | ||
export declare function splitBufferBundle(origin: BundleUnit): PartitionedBundleElements; | ||
export declare function splitBufferBundle(origin: Schemas.BundleUnit): PartitionedBundleElements; | ||
declare type StringSearchMode = "includes" | "startsWith" | "endsWith"; | ||
export declare function getAllFilesWithName(name: string, source: string[], mode?: StringSearchMode, forceLowerCase?: boolean): string[]; | ||
export declare function hasFilesWithName(name: string, source: string[], mode?: StringSearchMode, forceLowerCase?: boolean): boolean; | ||
export declare function deletePersonalization(source: BundleUnit, logosNames?: string[]): void; | ||
export declare function deletePersonalization(source: Schemas.BundleUnit, logosNames?: string[]): void; | ||
export {}; |
{ | ||
"name": "passkit-generator", | ||
"version": "2.0.6", | ||
"version": "2.0.7", | ||
"description": "The easiest way to generate custom Apple Wallet passes in Node.js", | ||
@@ -29,5 +29,5 @@ "main": "lib/index.js", | ||
"debug": "^4.3.1", | ||
"joi": "^17.3.0", | ||
"joi": "^17.4.0", | ||
"node-forge": "^0.10.0", | ||
"tslib": "^2.1.0", | ||
"tslib": "^2.3.0", | ||
"yazl": "^2.5.1" | ||
@@ -40,10 +40,10 @@ }, | ||
"@types/debug": "^4.1.5", | ||
"@types/jasmine": "^3.6.3", | ||
"@types/node": "^14.14.25", | ||
"@types/node-forge": "^0.9.7", | ||
"@types/jasmine": "^3.7.7", | ||
"@types/node": "^14.17.3", | ||
"@types/node-forge": "^0.10.0", | ||
"@types/yazl": "^2.4.2", | ||
"jasmine": "^3.6.4", | ||
"prettier": "^2.2.1", | ||
"jasmine": "^3.7.0", | ||
"prettier": "^2.3.1", | ||
"rimraf": "^3.0.2", | ||
"typescript": "^4.1.3" | ||
"typescript": "^4.3.4" | ||
}, | ||
@@ -50,0 +50,0 @@ "files": [ |
108
README.md
@@ -29,2 +29,3 @@ <div align="center"> | ||
### Install | ||
```sh | ||
@@ -34,3 +35,3 @@ $ npm install passkit-generator --save | ||
___ | ||
--- | ||
@@ -41,3 +42,3 @@ ### API Documentation | ||
___ | ||
--- | ||
@@ -48,3 +49,3 @@ ### Looking for the previous major version? | ||
___ | ||
--- | ||
@@ -55,3 +56,3 @@ ### Coming from the previous major version? | ||
___ | ||
--- | ||
@@ -71,3 +72,3 @@ ## Get Started | ||
___ | ||
--- | ||
@@ -80,4 +81,9 @@ > Using the .pass extension is a best practice, showing that the directory is a pass package. | ||
___ | ||
--- | ||
Model creation can be performed both manually or with the auxiliary of a web tool I developed, [Passkit Visual Designer](https://pkvd.app), which will let you design your model through a neat user interface. | ||
It will output a .zip file that you can decompress and use it as both file model and buffer model. | ||
Since `.pass` extension is required, **it will be up to you to unzip the generated model in a .pass folder**. | ||
```bash | ||
@@ -88,5 +94,5 @@ $ cd yourProjectDir; | ||
Follow the [Apple Developer documentation](https://apple.co/2wuJLC1) (_Package Structure_) to build a correct pass model. The **icon is required** in order to make the pass work. *Manifest.json* and *signature* will be automatically ignored from the model and generated in runtime. | ||
Follow the [Apple Developer documentation](https://apple.co/2wuJLC1) (_Package Structure_) to build a correct pass model. The **icon is required** in order to make the pass work. _Manifest.json_ and _signature_ will be automatically ignored from the model and generated in runtime. | ||
You can also create `.lproj` folders (e.g. *en.lproj* or *it.lproj*) containing localized media. To include a folder or translate texts inside the pass, please refer to [Localizing Passes](./API.md#method_localize) in the API documentation. | ||
You can also create `.lproj` folders (e.g. _en.lproj_ or _it.lproj_) containing localized media. To include a folder or translate texts inside the pass, please refer to [Localizing Passes](./API.md#method_localize) in the API documentation. | ||
@@ -101,10 +107,11 @@ To include a file that belongs to an `.lproj` folder in buffers, you'll just have to name a key like `en.lproj/thumbnail.png`. | ||
{ | ||
"formatVersion": 1, | ||
"passTypeIdentifier": "pass.<bundle id>", | ||
"teamIdentifier": "<here your team identifier>", | ||
"organizationName": "<your organization name>", | ||
"description": "A localizable description of your pass. To do so, put here a placeholder.", | ||
"boardingPass": {} | ||
"formatVersion": 1, | ||
"passTypeIdentifier": "pass.<bundle id>", | ||
"teamIdentifier": "<here your team identifier>", | ||
"organizationName": "<your organization name>", | ||
"description": "A localizable description of your pass. To do so, put here a placeholder.", | ||
"boardingPass": {} | ||
} | ||
``` | ||
<a name="certificates"></a> | ||
@@ -119,5 +126,5 @@ | ||
* Apple WWDR (_Worldwide Developer Relationship_) certificate | ||
* Signer certificate | ||
* Signer key | ||
- Apple WWDR (_Worldwide Developer Relationship_) certificate | ||
- Signer certificate | ||
- Signer key | ||
@@ -130,5 +137,5 @@ While WWDR can be obtained from [Apple PKI Portal](https://www.apple.com/certificateauthority/), to get the `signer key` and the `certificate`, you'll have to get first a `Certificate Signing Request` (`.certSigningRequest` file) and upload it to Apple Developers Portal, at [Pass Types Identifiers](https://developer.apple.com/account/ios/identifier/passTypeId) (open it, it's worth it 😜). | ||
> **If you don't have access to macOS** (or you are a terminal enthusiast), **follow [these steps](./non-macOS-steps.md) instead.** | ||
<hr> | ||
1. Create a new pass type identifier and provide it with a Name and a reverse-domain bundle id (starting with "pass."). You will put this identifier as value for `passTypeIdentifier` in `pass.json` file. | ||
@@ -142,16 +149,19 @@ 2. Confirm and register the new identifier. | ||
```sh | ||
# Creating and changing dir | ||
# Creating and changing dir | ||
$ mkdir "certs" && cd $_ | ||
# Extracting key and cert from pkcs12 | ||
# Extracting key and cert from pkcs12 | ||
$ openssl pkcs12 -in <cert-name>.p12 -clcerts -nokeys -out signerCert.pem -passin pass:<your-password> | ||
$ openssl pkcs12 -in <cert-name>.p12 -nocerts -out signerKey.pem -passin pass:<your-password> -passout pass:<secret-passphrase> | ||
``` | ||
7. Execute step 5 also for the WWDR certificate (`.cer`) you downloaded from Apple PKI portal (default name: *AppleWWDRCA.cer*) but instead exporting it as PKCS#12 (`.p12` - you'll also be unable to do that), export it as PEM (`.pem`) file. | ||
___ | ||
7. Execute step 5 also for the WWDR certificate (`.cer`) you downloaded from Apple PKI portal (default name: _AppleWWDRCA.cer_) but instead exporting it as PKCS#12 (`.p12` - you'll also be unable to do that), export it as PEM (`.pem`) file. | ||
--- | ||
<a name="usage_example"></a> | ||
## Usage example | ||
## Usage Examples | ||
#### Folder Model | ||
```typescript | ||
@@ -186,3 +196,3 @@ /** | ||
// Generate the stream, which gets returned through a Promise | ||
// Generate the stream .pkpass file stream | ||
const stream: Stream = examplePass.generate(); | ||
@@ -196,5 +206,51 @@ | ||
#### Buffer Model | ||
```typescript | ||
/** | ||
* Use `const { createPass } = require("passkit-generator");` | ||
* for usage in pure Node.js. Please note that `Pass` is only exported | ||
* as Typescript type. | ||
*/ | ||
import { createPass, Pass } from "passkit-generator"; | ||
try { | ||
const examplePass = await createPass({ | ||
model: { | ||
"thumbnail": Buffer.from([ ... ]), | ||
"icon": Buffer.from([ ... ]), | ||
"pass.json": Buffer.from([ ... ]), | ||
"it.lproj/pass.strings": Buffer.from([ ... ]) | ||
}, | ||
certificates: { | ||
wwdr: "./certs/wwdr.pem", | ||
signerCert: "./certs/signercert.pem", | ||
signerKey: { | ||
keyFile: "./certs/signerkey.pem", | ||
passphrase: "123456" | ||
} | ||
}, | ||
overrides: { | ||
// keys to be added or overridden | ||
serialNumber: "AAGH44625236dddaffbda" | ||
} | ||
}); | ||
// Adding some settings to be written inside pass.json | ||
examplePass.localize("en", { ... }); | ||
examplePass.barcode("36478105430"); // Random value | ||
// Generate the stream .pkpass file stream | ||
const stream: Stream = examplePass.generate(); | ||
doSomethingWithTheStream(stream); | ||
} catch (err) { | ||
doSomethingWithTheError(err); | ||
} | ||
``` | ||
For more complex usage examples, please refer to [examples](https://github.com/alexandercerutti/passkit-generator/tree/master/examples) folder. | ||
___ | ||
--- | ||
@@ -212,3 +268,3 @@ ## Other | ||
___ | ||
--- | ||
@@ -215,0 +271,0 @@ ## Contributors |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
39
2055
287
0
100714
Updatedjoi@^17.4.0
Updatedtslib@^2.3.0