Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

passkit-generator

Package Overview
Dependencies
Maintainers
1
Versions
53
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

passkit-generator - npm Package Compare versions

Comparing version 2.0.8 to 3.0.0

lib/Bundle.d.ts

6

lib/index.d.ts

@@ -1,4 +0,2 @@

export type { Pass } from "./pass";
export type { AbstractModel } from "./abstract";
export { createPass } from "./factory";
export { createAbstractModel } from "./abstract";
export { default as PKPass } from "./PKPass";
export type { Barcode, Beacon, Field, Location, NFC, PassProps, Semantics, TransitType, Personalize, OverridablePassProps, } from "./schemas";
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createAbstractModel = exports.createPass = void 0;
var factory_1 = require("./factory");
Object.defineProperty(exports, "createPass", { enumerable: true, get: function () { return factory_1.createPass; } });
var abstract_1 = require("./abstract");
Object.defineProperty(exports, "createAbstractModel", { enumerable: true, get: function () { return abstract_1.createAbstractModel; } });
exports.PKPass = void 0;
var PKPass_1 = require("./PKPass");
Object.defineProperty(exports, "PKPass", { enumerable: true, get: function () { return __importDefault(PKPass_1).default; } });
//# sourceMappingURL=index.js.map

@@ -1,36 +0,67 @@

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.";
export declare const CERTIFICATES: {
readonly INVALID: "Invalid certificate(s) loaded. %s. Please provide valid WWDR certificates and developer signer certificate and key (with passphrase).\nRefer to docs to obtain them";
};
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.";
export declare const TRANSIT_TYPE: {
readonly UNEXPECTED_PASS_TYPE: "Cannot set transitType on a pass with type different from boardingPass.";
readonly INVALID: "Cannot set transitType because not compliant with Apple specifications. Refer to https://apple.co/3DHuAG4 for more - %s";
};
declare type ERROR_OR_DEBUG_MESSAGE = typeof ERROR[keyof typeof ERROR] | typeof DEBUG[keyof typeof DEBUG];
export declare const PASS_TYPE: {
readonly INVALID: "Cannot set type because not compliant with Apple specifications. Refer to https://apple.co/3aFpSfg for a list of valid props - %s";
};
export declare const TEMPLATE: {
readonly INVALID: "Cannot create pass from a template. %s";
};
export declare const FILTER_VALID: {
readonly INVALID: "Cannot validate property. %s";
};
export declare const FIELDS: {
readonly INVALID: "Cannot add field. %s";
readonly REPEATED_KEY: "Cannot add field with key '%s': another field already owns this key. Ignored.";
};
export declare const DATE: {
readonly INVALID: "Cannot set %s. Invalid date %s";
};
export declare const LANGUAGES: {
readonly INVALID_LANG: "Cannot set localization. Expected a string for 'lang' but received %s";
readonly NO_TRANSLATIONS: "Cannot create or use language %s. If your itention was to just add a language (.lproj) folder to the bundle, both specify some translations or use .addBuffer to add some media.";
};
export declare const BARCODES: {
readonly INVALID_POST: "";
};
export declare const PASS_SOURCE: {
readonly INVALID: "Cannot add pass.json to bundle because it is invalid. %s";
readonly UNKNOWN_TYPE: "Cannot find a valid type in pass.json. You won't be able to set fields until you won't set explicitly one.";
readonly JOIN: "The imported pass.json's properties will be joined with the current setted props. You might lose some data.";
};
export declare const PERSONALIZE: {
readonly INVALID: "Cannot add personalization.json to bundle because it is invalid. %s";
};
export declare const JSON: {
readonly INVALID: "Cannot parse JSON. Invalid file";
};
export declare const CLOSE: {
readonly MISSING_TYPE: "Cannot proceed creating the pass because type is missing.";
readonly MISSING_ICON: "At least one icon file is missing in your bundle. Your pass won't be openable by any Apple Device.";
readonly PERSONALIZATION_REMOVED: "Personalization file '%s' have been removed from the bundle as the requirements for personalization are not met.";
readonly MISSING_TRANSIT_TYPE: "Cannot proceed creating the pass because transitType is missing on your boardingPass.";
};
export declare const MODELS: {
readonly DIR_NOT_FOUND: "Cannot import model: directory %s not found.";
readonly FILE_NO_OPEN: "Cannot open model file. %s";
};
export declare const BUNDLE: {
readonly MIME_TYPE_MISSING: "Cannot build Bundle. MimeType is missing";
readonly CLOSED: "Cannot add file or set property. Bundle is closed.";
};
export declare const FROM: {
readonly MISSING_SOURCE: "Cannot create PKPass from source: source is '%s'";
};
export declare const PACK: {
readonly INVALID: "Cannot pack passes. Only PKPass instances allowed";
};
/**
* Creates a message with replaced values
* @param {string} messageName
* @param {any[]} values
* @param messageName
* @param values
*/
export default function format(messageName: ERROR_OR_DEBUG_MESSAGE, ...values: any[]): string;
export {};
export declare function format(messageName: string, ...values: any[]): string;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
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.",
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_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.",
exports.format = exports.PACK = exports.FROM = exports.BUNDLE = exports.MODELS = exports.CLOSE = exports.JSON = exports.PERSONALIZE = exports.PASS_SOURCE = exports.BARCODES = exports.LANGUAGES = exports.DATE = exports.FIELDS = exports.FILTER_VALID = exports.TEMPLATE = exports.PASS_TYPE = exports.TRANSIT_TYPE = exports.CERTIFICATES = void 0;
exports.CERTIFICATES = {
INVALID: "Invalid certificate(s) loaded. %s. Please provide valid WWDR certificates and developer signer certificate and key (with passphrase).\nRefer to docs to obtain them",
};
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]',
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.",
exports.TRANSIT_TYPE = {
UNEXPECTED_PASS_TYPE: "Cannot set transitType on a pass with type different from boardingPass.",
INVALID: "Cannot set transitType because not compliant with Apple specifications. Refer to https://apple.co/3DHuAG4 for more - %s",
};
exports.PASS_TYPE = {
INVALID: "Cannot set type because not compliant with Apple specifications. Refer to https://apple.co/3aFpSfg for a list of valid props - %s",
};
exports.TEMPLATE = {
INVALID: "Cannot create pass from a template. %s",
};
exports.FILTER_VALID = {
INVALID: "Cannot validate property. %s",
};
exports.FIELDS = {
INVALID: "Cannot add field. %s",
REPEATED_KEY: "Cannot add field with key '%s': another field already owns this key. Ignored.",
};
exports.DATE = {
INVALID: "Cannot set %s. Invalid date %s",
};
exports.LANGUAGES = {
INVALID_LANG: "Cannot set localization. Expected a string for 'lang' but received %s",
NO_TRANSLATIONS: "Cannot create or use language %s. If your itention was to just add a language (.lproj) folder to the bundle, both specify some translations or use .addBuffer to add some media.",
};
exports.BARCODES = {
INVALID_POST: "",
};
exports.PASS_SOURCE = {
INVALID: "Cannot add pass.json to bundle because it is invalid. %s",
UNKNOWN_TYPE: "Cannot find a valid type in pass.json. You won't be able to set fields until you won't set explicitly one.",
JOIN: "The imported pass.json's properties will be joined with the current setted props. You might lose some data.",
};
exports.PERSONALIZE = {
INVALID: "Cannot add personalization.json to bundle because it is invalid. %s",
};
exports.JSON = {
INVALID: "Cannot parse JSON. Invalid file",
};
exports.CLOSE = {
MISSING_TYPE: "Cannot proceed creating the pass because type is missing.",
MISSING_ICON: "At least one icon file is missing in your bundle. Your pass won't be openable by any Apple Device.",
PERSONALIZATION_REMOVED: "Personalization file '%s' have been removed from the bundle as the requirements for personalization are not met.",
MISSING_TRANSIT_TYPE: "Cannot proceed creating the pass because transitType is missing on your boardingPass.",
};
exports.MODELS = {
DIR_NOT_FOUND: "Cannot import model: directory %s not found.",
FILE_NO_OPEN: "Cannot open model file. %s",
};
exports.BUNDLE = {
MIME_TYPE_MISSING: "Cannot build Bundle. MimeType is missing",
CLOSED: "Cannot add file or set property. Bundle is closed.",
};
exports.FROM = {
MISSING_SOURCE: "Cannot create PKPass from source: source is '%s'",
};
exports.PACK = {
INVALID: "Cannot pack passes. Only PKPass instances allowed",
};
/**
* Creates a message with replaced values
* @param {string} messageName
* @param {any[]} values
* @param messageName
* @param values
*/
function format(messageName, ...values) {
// reversing because it is better popping than shifting.
let replaceValues = values.reverse();
return messageName.replace(/%s/g, () => {
let next = replaceValues.pop();
return next !== undefined ? next : "<passedValueIsUndefined>";
});
const replaceValues = values.reverse();
return messageName.replace(/%s/g, () => replaceValues.pop());
}
exports.default = format;
exports.format = format;
//# sourceMappingURL=messages.js.map
/// <reference types="node" />
export * from "./Barcodes";
export * from "./Beacons";
export * from "./Barcode";
export * from "./Beacon";
export * from "./Location";
export * from "./PassFieldContent";
export * from "./Field";
export * from "./NFC";
export * from "./SemanticTags";
export * from "./Semantics";
export * from "./PassFields";
export * from "./Personalize";
export * from "./Certificates";
import Joi from "joi";
import { Barcode } from "./Barcodes";
import { Barcode } from "./Barcode";
import { Location } from "./Location";
import { Beacon } from "./Beacons";
import { Beacon } from "./Beacon";
import { NFC } from "./NFC";
import { Field } from "./PassFieldContent";
import { PassFields, TransitType } from "./PassFields";
import { Personalization } from "./Personalize";
import { Semantics } from "./SemanticTags";
export interface Manifest {
[key: string]: string;
}
export interface Certificates {
wwdr?: string;
signerCert?: string;
signerKey?: {
keyFile: string;
passphrase?: string;
} | string;
}
export interface FactoryOptions {
model: BundleUnit | string;
certificates: Certificates;
overrides?: OverridesSupportedOptions;
}
export interface BundleUnit {
import { Semantics } from "./Semantics";
import { CertificatesSchema } from "./Certificates";
export interface FileBuffers {
[key: string]: Buffer;
}
export interface PartitionedBundle {
bundle: BundleUnit;
l10nBundle: {
[key: string]: BundleUnit;
};
}
export interface CertificatesSchema {
wwdr: string;
signerCert: string;
signerKey: string;
}
export declare const CertificatesSchema: Joi.ObjectSchema<CertificatesSchema>;
export interface PassInstance {
model: PartitionedBundle;
certificates: CertificatesSchema;
overrides?: OverridesSupportedOptions;
}
export declare const PassInstance: Joi.ObjectSchema<PassInstance>;
export interface OverridesSupportedOptions {
export interface PassProps {
formatVersion?: 1;
serialNumber?: string;

@@ -63,12 +30,7 @@ description?: string;

appLaunchURL?: string;
associatedStoreIdentifiers?: Array<number>;
voided?: boolean;
userInfo?: {
[key: string]: any;
};
webServiceURL?: string;
authenticationToken?: string;
sharingProhibited?: boolean;
backgroundColor?: string;
foregroundColor?: string;
labelColor?: string;
groupingIdentifier?: string;

@@ -79,5 +41,14 @@ suppressStripShine?: boolean;

semantics?: Semantics;
}
export declare const OverridesSupportedOptions: Joi.ObjectSchema<OverridesSupportedOptions>;
export interface ValidPassType {
webServiceURL?: string;
associatedStoreIdentifiers?: Array<number>;
authenticationToken?: string;
backgroundColor?: string;
foregroundColor?: string;
labelColor?: string;
nfc?: NFC;
beacons?: Beacon[];
barcodes?: Barcode[];
relevantDate?: string;
expirationDate?: string;
locations?: Location[];
boardingPass?: PassFields & {

@@ -91,33 +62,44 @@ transitType: TransitType;

}
interface PassInterfacesProps {
barcode?: Barcode;
barcodes?: Barcode[];
beacons?: Beacon[];
locations?: Location[];
maxDistance?: number;
relevantDate?: string;
nfc?: NFC;
expirationDate?: string;
voided?: boolean;
/**
* These are the properties passkit-generator will
* handle through its methods
*/
declare type PassMethodsProps = "nfc" | "beacons" | "barcodes" | "relevantDate" | "expirationDate" | "locations";
export declare type PassTypesProps = "boardingPass" | "eventTicket" | "coupon" | "generic" | "storeCard";
export declare type OverridablePassProps = Omit<PassProps, PassMethodsProps | PassTypesProps>;
export declare type PassPropsFromMethods = {
[K in PassMethodsProps]: PassProps[K];
};
export declare type PassKindsProps = {
[K in PassTypesProps]: PassProps[K];
};
export declare type PassColors = Pick<OverridablePassProps, "backgroundColor" | "foregroundColor" | "labelColor">;
export declare const PassPropsFromMethods: Joi.ObjectSchema<PassPropsFromMethods>;
export declare const PassKindsProps: Joi.ObjectSchema<PassKindsProps>;
export declare const PassType: Joi.StringSchema;
export declare const OverridablePassProps: Joi.ObjectSchema<OverridablePassProps>;
export declare const PassProps: Joi.ObjectSchema<OverridablePassProps & PassKindsProps & PassPropsFromMethods>;
export interface Template {
model: string;
certificates: CertificatesSchema;
}
declare type AllPassProps = PassInterfacesProps & ValidPassType & OverridesSupportedOptions;
export declare type ValidPass = {
[K in keyof AllPassProps]: AllPassProps[K];
};
export declare type PassColors = Pick<OverridesSupportedOptions, "backgroundColor" | "foregroundColor" | "labelColor">;
declare type AvailableSchemas = typeof Barcode | typeof Location | typeof Beacon | typeof NFC | typeof Field | typeof PassFields | typeof Personalization | typeof TransitType | typeof PassInstance | typeof CertificatesSchema | typeof OverridesSupportedOptions;
export declare type ArrayPassSchema = Beacon | Location | Barcode;
export declare const Template: Joi.ObjectSchema<Template>;
/**
* Checks if the passed options are compliant with the indicated schema
* @param {any} opts - options to be checks
* @param {string} schemaName - the indicated schema (will be converted)
* @returns {boolean} - result of the check
* Performs validation of a schema on an object.
* If it fails, will throw an error.
*
* @param schema
* @param data
*/
export declare function isValid(opts: any, schema: AvailableSchemas): boolean;
export declare function assertValidity<T>(schema: Joi.ObjectSchema<T> | Joi.StringSchema, data: T, customErrorMessage?: string): void;
/**
* Executes the validation in verbose mode, exposing the value or an empty object
* @param {object} opts - to be validated
* @param {*} schemaName - selected schema
* @returns {object} the filtered value or empty object
* Performs validation and throws the error if there's one.
* Otherwise returns a (possibly patched) version of the specified
* options (it depends on the schema)
*
* @param schema
* @param options
* @returns
*/
export declare function getValidated<T extends Object>(opts: T, schema: AvailableSchemas): T | null;
export declare function validate<T extends Object>(schema: Joi.ObjectSchema<T> | Joi.StringSchema, options: T): T;
export declare function filterValid<T extends Object>(schema: Joi.ObjectSchema<T>, source: T[]): T[];
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getValidated = exports.isValid = exports.OverridesSupportedOptions = exports.PassInstance = exports.CertificatesSchema = void 0;
exports.filterValid = exports.validate = exports.assertValidity = exports.Template = exports.PassProps = exports.OverridablePassProps = exports.PassType = exports.PassKindsProps = exports.PassPropsFromMethods = void 0;
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./Barcodes"), exports);
tslib_1.__exportStar(require("./Beacons"), exports);
tslib_1.__exportStar(require("./Location"), exports);
tslib_1.__exportStar(require("./PassFieldContent"), exports);
tslib_1.__exportStar(require("./NFC"), exports);
tslib_1.__exportStar(require("./SemanticTags"), exports);
tslib_1.__exportStar(require("./PassFields"), exports);
tslib_1.__exportStar(require("./Personalize"), exports);
const joi_1 = tslib_1.__importDefault(require("joi"));
const debug_1 = tslib_1.__importDefault(require("debug"));
const SemanticTags_1 = require("./SemanticTags");
const schemaDebug = debug_1.default("Schema");
exports.CertificatesSchema = joi_1.default.object()
.keys({
wwdr: joi_1.default.alternatives(joi_1.default.binary(), joi_1.default.string()).required(),
signerCert: joi_1.default.alternatives(joi_1.default.binary(), joi_1.default.string()).required(),
signerKey: joi_1.default.alternatives()
.try(joi_1.default.object().keys({
keyFile: joi_1.default.alternatives(joi_1.default.binary(), joi_1.default.string()).required(),
passphrase: joi_1.default.string().required(),
}), joi_1.default.alternatives(joi_1.default.binary(), joi_1.default.string()))
.required(),
})
.required();
exports.PassInstance = joi_1.default.object().keys({
model: joi_1.default.alternatives(joi_1.default.object(), joi_1.default.string()).required(),
certificates: joi_1.default.object(),
overrides: joi_1.default.object(),
(0, tslib_1.__exportStar)(require("./Barcode"), exports);
(0, tslib_1.__exportStar)(require("./Beacon"), exports);
(0, tslib_1.__exportStar)(require("./Location"), exports);
(0, tslib_1.__exportStar)(require("./Field"), exports);
(0, tslib_1.__exportStar)(require("./NFC"), exports);
(0, tslib_1.__exportStar)(require("./Semantics"), exports);
(0, tslib_1.__exportStar)(require("./PassFields"), exports);
(0, tslib_1.__exportStar)(require("./Personalize"), exports);
(0, tslib_1.__exportStar)(require("./Certificates"), exports);
const joi_1 = (0, tslib_1.__importDefault)(require("joi"));
const Barcode_1 = require("./Barcode");
const Location_1 = require("./Location");
const Beacon_1 = require("./Beacon");
const NFC_1 = require("./NFC");
const PassFields_1 = require("./PassFields");
const Semantics_1 = require("./Semantics");
const Messages = (0, tslib_1.__importStar)(require("../messages"));
const RGB_COLOR_REGEX = /rgb\(\s*(?:[01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\s*,\s*(?:[01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\s*,\s*(?:[01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\s*\)/;
exports.PassPropsFromMethods = joi_1.default.object({
nfc: NFC_1.NFC,
beacons: joi_1.default.array().items(Beacon_1.Beacon),
barcodes: joi_1.default.array().items(Barcode_1.Barcode),
relevantDate: joi_1.default.string().isoDate(),
expirationDate: joi_1.default.string().isoDate(),
locations: joi_1.default.array().items(Location_1.Location),
});
exports.OverridesSupportedOptions = joi_1.default.object()
.keys({
exports.PassKindsProps = joi_1.default.object({
coupon: PassFields_1.PassFields.disallow("transitType"),
generic: PassFields_1.PassFields.disallow("transitType"),
storeCard: PassFields_1.PassFields.disallow("transitType"),
eventTicket: PassFields_1.PassFields.disallow("transitType"),
boardingPass: PassFields_1.PassFields,
});
exports.PassType = joi_1.default.string().regex(/(boardingPass|coupon|eventTicket|storeCard|generic)/);
exports.OverridablePassProps = joi_1.default.object({
formatVersion: joi_1.default.number().default(1),
semantics: Semantics_1.Semantics,
voided: joi_1.default.boolean(),
logoText: joi_1.default.string(),
description: joi_1.default.string(),
serialNumber: joi_1.default.string(),
description: joi_1.default.string(),
appLaunchURL: joi_1.default.string(),
teamIdentifier: joi_1.default.string(),
organizationName: joi_1.default.string(),
passTypeIdentifier: joi_1.default.string(),
teamIdentifier: joi_1.default.string(),
appLaunchURL: joi_1.default.string(),
sharingProhibited: joi_1.default.boolean(),
groupingIdentifier: joi_1.default.string(),
suppressStripShine: joi_1.default.boolean(),
maxDistance: joi_1.default.number().positive(),
authenticationToken: joi_1.default.string().min(16),
labelColor: joi_1.default.string().regex(RGB_COLOR_REGEX),
backgroundColor: joi_1.default.string().regex(RGB_COLOR_REGEX),
foregroundColor: joi_1.default.string().regex(RGB_COLOR_REGEX),
associatedStoreIdentifiers: joi_1.default.array().items(joi_1.default.number()),

@@ -46,55 +62,65 @@ userInfo: joi_1.default.alternatives(joi_1.default.object().unknown(), joi_1.default.array()),

webServiceURL: joi_1.default.string().regex(/https?:\/\/(?:[a-z0-9]+\.?)+(?::\d{2,})?(?:\/[\S]+)*/),
authenticationToken: joi_1.default.string().min(16),
sharingProhibited: joi_1.default.boolean(),
backgroundColor: joi_1.default.string().min(10).max(16),
foregroundColor: joi_1.default.string().min(10).max(16),
labelColor: joi_1.default.string().min(10).max(16),
groupingIdentifier: joi_1.default.string(),
suppressStripShine: joi_1.default.boolean(),
logoText: joi_1.default.string(),
maxDistance: joi_1.default.number().positive(),
semantics: SemanticTags_1.Semantics,
})
.with("webServiceURL", "authenticationToken");
/* function resolveSchemaName(name: Schema) {
return schemas[name] || undefined;
}
*/
}).with("webServiceURL", "authenticationToken");
exports.PassProps = joi_1.default.object()
.concat(exports.OverridablePassProps)
.concat(exports.PassKindsProps)
.concat(exports.PassPropsFromMethods);
exports.Template = joi_1.default.object({
model: joi_1.default.string().required(),
certificates: joi_1.default.object().required(),
});
// --------- UTILITIES ---------- //
/**
* Checks if the passed options are compliant with the indicated schema
* @param {any} opts - options to be checks
* @param {string} schemaName - the indicated schema (will be converted)
* @returns {boolean} - result of the check
* Performs validation of a schema on an object.
* If it fails, will throw an error.
*
* @param schema
* @param data
*/
function isValid(opts, schema) {
if (!schema) {
schemaDebug(`validation failed due to missing or mispelled schema name`);
return false;
}
const validation = schema.validate(opts);
function assertValidity(schema, data, customErrorMessage) {
const validation = schema.validate(data);
if (validation.error) {
schemaDebug(`validation failed due to error: ${validation.error.message}`);
if (customErrorMessage) {
console.warn(validation.error);
throw new TypeError(`${validation.error.name} happened. ${Messages.format(customErrorMessage, validation.error.message)}`);
}
throw new TypeError(validation.error.message);
}
return !validation.error;
}
exports.isValid = isValid;
exports.assertValidity = assertValidity;
/**
* Executes the validation in verbose mode, exposing the value or an empty object
* @param {object} opts - to be validated
* @param {*} schemaName - selected schema
* @returns {object} the filtered value or empty object
* Performs validation and throws the error if there's one.
* Otherwise returns a (possibly patched) version of the specified
* options (it depends on the schema)
*
* @param schema
* @param options
* @returns
*/
function getValidated(opts, schema) {
if (!schema) {
schemaDebug(`validation failed due to missing schema`);
return null;
function validate(schema, options) {
const validationResult = schema.validate(options, {
stripUnknown: true,
abortEarly: true,
});
if (validationResult.error) {
throw validationResult.error;
}
const validation = schema.validate(opts, { stripUnknown: true });
if (validation.error) {
schemaDebug(`Validation failed in getValidated due to error: ${validation.error.message}`);
return null;
return validationResult.value;
}
exports.validate = validate;
function filterValid(schema, source) {
if (!source) {
return [];
}
return validation.value;
return source.reduce((acc, current) => {
try {
return [...acc, validate(schema, current)];
}
catch (err) {
console.warn(Messages.format(Messages.FILTER_VALID.INVALID, err));
return [...acc];
}
}, []);
}
exports.getValidated = getValidated;
exports.filterValid = filterValid;
//# sourceMappingURL=index.js.map
import Joi from "joi";
/**
* @see https://developer.apple.com/documentation/walletpasses/pass/locations
* @TODO Rename "Location" in "Locations". This will be done in v3.0
*/

@@ -6,0 +5,0 @@ export interface Location {

@@ -5,3 +5,3 @@ "use strict";

const tslib_1 = require("tslib");
const joi_1 = tslib_1.__importDefault(require("joi"));
const joi_1 = (0, tslib_1.__importDefault)(require("joi"));
exports.Location = joi_1.default.object().keys({

@@ -8,0 +8,0 @@ altitude: joi_1.default.number(),

@@ -5,3 +5,3 @@ "use strict";

const tslib_1 = require("tslib");
const joi_1 = tslib_1.__importDefault(require("joi"));
const joi_1 = (0, tslib_1.__importDefault)(require("joi"));
exports.NFC = joi_1.default.object().keys({

@@ -8,0 +8,0 @@ message: joi_1.default.string().required().max(64),

import Joi from "joi";
import { Field } from "./PassFieldContent";
import { Field } from "./Field";
export declare type TransitType = "PKTransitTypeAir" | "PKTransitTypeBoat" | "PKTransitTypeBus" | "PKTransitTypeGeneric" | "PKTransitTypeTrain";
export declare const TransitType: Joi.StringSchema;
export interface PassFields {

@@ -11,5 +13,4 @@ auxiliaryFields: (Field & {

secondaryFields: Field[];
transitType?: TransitType;
}
export declare const PassFields: Joi.ObjectSchema<PassFields>;
export declare type TransitType = "PKTransitTypeAir" | "PKTransitTypeBoat" | "PKTransitTypeBus" | "PKTransitTypeGeneric" | "PKTransitTypeTrain";
export declare const TransitType: Joi.StringSchema;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TransitType = exports.PassFields = void 0;
exports.PassFields = exports.TransitType = void 0;
const tslib_1 = require("tslib");
const joi_1 = tslib_1.__importDefault(require("joi"));
const PassFieldContent_1 = require("./PassFieldContent");
const joi_1 = (0, tslib_1.__importDefault)(require("joi"));
const Field_1 = require("./Field");
exports.TransitType = joi_1.default.string().regex(/(PKTransitTypeAir|PKTransitTypeBoat|PKTransitTypeBus|PKTransitTypeGeneric|PKTransitTypeTrain)/);
exports.PassFields = joi_1.default.object().keys({

@@ -12,9 +13,9 @@ auxiliaryFields: joi_1.default.array().items(joi_1.default.object()

})
.concat(PassFieldContent_1.Field)),
backFields: joi_1.default.array().items(PassFieldContent_1.Field),
headerFields: joi_1.default.array().items(PassFieldContent_1.Field),
primaryFields: joi_1.default.array().items(PassFieldContent_1.Field),
secondaryFields: joi_1.default.array().items(PassFieldContent_1.Field),
.concat(Field_1.Field)),
backFields: joi_1.default.array().items(Field_1.Field),
headerFields: joi_1.default.array().items(Field_1.Field),
primaryFields: joi_1.default.array().items(Field_1.Field),
secondaryFields: joi_1.default.array().items(Field_1.Field),
transitType: exports.TransitType,
});
exports.TransitType = joi_1.default.string().regex(/(PKTransitTypeAir|PKTransitTypeBoat|PKTransitTypeBus|PKTransitTypeGeneric|PKTransitTypeTrain)/);
//# sourceMappingURL=PassFields.js.map
import Joi from "joi";
/**
* @see https://developer.apple.com/documentation/walletpasses/personalize
* @TODO Rename "Personalization" in "Personalize". This will be done in v3.0
*/
export interface Personalization {
declare type RequiredPersonalizationFields = "PKPassPersonalizationFieldName" | "PKPassPersonalizationFieldPostalCode" | "PKPassPersonalizationFieldEmailAddress" | "PKPassPersonalizationFieldPhoneNumber";
export interface Personalize {
description: string;

@@ -11,4 +11,3 @@ requiredPersonalizationFields: RequiredPersonalizationFields[];

}
declare type RequiredPersonalizationFields = "PKPassPersonalizationFieldName" | "PKPassPersonalizationFieldPostalCode" | "PKPassPersonalizationFieldEmailAddress" | "PKPassPersonalizationFieldPhoneNumber";
export declare const Personalization: Joi.ObjectSchema<Personalization>;
export declare const Personalize: Joi.ObjectSchema<Personalize>;
export {};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Personalization = void 0;
exports.Personalize = void 0;
const tslib_1 = require("tslib");
const joi_1 = tslib_1.__importDefault(require("joi"));
exports.Personalization = joi_1.default.object().keys({
const joi_1 = (0, tslib_1.__importDefault)(require("joi"));
exports.Personalize = joi_1.default.object().keys({
description: joi_1.default.string().required(),

@@ -8,0 +8,0 @@ requiredPersonalizationFields: joi_1.default.array()

@@ -1,53 +0,22 @@

/// <reference types="node" />
import type * as Schemas from "./schemas";
import type Bundle from "./Bundle";
/**
* Checks if an rgb value is compliant with CSS-like syntax
*
* @function isValidRGB
* @params {String} value - string to analyze
* @returns {Boolean} True if valid rgb, false otherwise
* Acts as a wrapper for converting date to W3C string
* @param date
* @returns
*/
export declare function isValidRGB(value?: string): boolean;
export declare function processDate(date: Date): string | null;
/**
* Converts a date to W3C Standard format
* Removes hidden files from a list (those starting with dot)
*
* @function dateToW3Cstring
* @params date - The date to be parsed
* @returns - The parsed string if the parameter is valid,
* undefined otherwise
* @params from - list of file names
* @return
*/
export declare function dateToW3CString(date: Date): string;
/**
* Apply a filter to arg0 to remove hidden files names (starting with dot)
*
* @function removeHidden
* @params {String[]} from - list of file names
* @return {String[]}
*/
export declare function removeHidden(from: Array<string>): Array<string>;
/**
* Creates a buffer of translations in Apple .strings format
* Clones recursively an object and all of its properties
*
* @function generateStringFile
* @params {Object} lang - structure containing related to ISO 3166 alpha-2 code for the language
* @returns {Buffer} Buffer to be written in pass.strings for language in lang
* @see https://apple.co/2M9LWVu - String Resources
* @param object
* @returns
*/
export declare function generateStringFile(lang: {
[index: string]: string;
}): Buffer;
/**
* Applies a partition to split one bundle
* to two
* @param origin
*/
declare type PartitionedBundleElements = [
Schemas.PartitionedBundle["l10nBundle"],
Schemas.PartitionedBundle["bundle"]
];
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: Schemas.BundleUnit, logosNames?: string[]): void;
export {};
export declare function cloneRecursive(object: Object): {};
export declare function assertUnfrozen(instance: InstanceType<typeof Bundle>): void;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deletePersonalization = exports.hasFilesWithName = exports.getAllFilesWithName = exports.splitBufferBundle = exports.generateStringFile = exports.removeHidden = exports.dateToW3CString = exports.isValidRGB = void 0;
const os_1 = require("os");
const path_1 = require("path");
exports.assertUnfrozen = exports.cloneRecursive = exports.removeHidden = exports.processDate = void 0;
const tslib_1 = require("tslib");
const Messages = (0, tslib_1.__importStar)(require("./messages"));
/**
* Checks if an rgb value is compliant with CSS-like syntax
*
* @function isValidRGB
* @params {String} value - string to analyze
* @returns {Boolean} True if valid rgb, false otherwise
* Acts as a wrapper for converting date to W3C string
* @param date
* @returns
*/
function isValidRGB(value) {
if (!value || typeof value !== "string") {
return false;
function processDate(date) {
if (!(date instanceof Date)) {
throw "Invalid date";
}
const rgb = value.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/);
if (!rgb) {
return false;
const dateParse = dateToW3CString(date);
if (!dateParse) {
throw "Invalid date";
}
return rgb.slice(1, 4).every((v) => Math.abs(Number(v)) <= 255);
return dateParse;
}
exports.isValidRGB = isValidRGB;
exports.processDate = processDate;
/**

@@ -33,5 +31,2 @@ * Converts a date to W3C Standard format

function dateToW3CString(date) {
if (!(date instanceof Date)) {
return "";
}
// if it is NaN, it is "Invalid Date"

@@ -64,3 +59,2 @@ if (isNaN(Number(date))) {

}
exports.dateToW3CString = dateToW3CString;
function padMeTwo(original) {

@@ -70,7 +64,6 @@ return String(original).padStart(2, "0");

/**
* Apply a filter to arg0 to remove hidden files names (starting with dot)
* Removes hidden files from a list (those starting with dot)
*
* @function removeHidden
* @params {String[]} from - list of file names
* @return {String[]}
* @params from - list of file names
* @return
*/

@@ -82,54 +75,36 @@ function removeHidden(from) {

/**
* Creates a buffer of translations in Apple .strings format
* Clones recursively an object and all of its properties
*
* @function generateStringFile
* @params {Object} lang - structure containing related to ISO 3166 alpha-2 code for the language
* @returns {Buffer} Buffer to be written in pass.strings for language in lang
* @see https://apple.co/2M9LWVu - String Resources
* @param object
* @returns
*/
function generateStringFile(lang) {
if (!Object.keys(lang).length) {
return Buffer.from("", "utf8");
function cloneRecursive(object) {
const objectCopy = {};
const objectEntries = Object.entries(object);
for (let i = 0; i < objectEntries.length; i++) {
const [key, value] = objectEntries[i];
if (value && typeof value === "object") {
if (Array.isArray(value)) {
objectCopy[key] = value.slice();
for (let j = 0; j < value.length; j++) {
objectCopy[key][j] = cloneRecursive(value[j]);
}
}
else {
objectCopy[key] = cloneRecursive(value);
}
}
else {
objectCopy[key] = value;
}
}
// Pass.strings format is the following one for each row:
// "key" = "value";
const strings = Object.keys(lang).map((key) => `"${key}" = "${lang[key].replace(/"/g, '"')}";`);
return Buffer.from(strings.join(os_1.EOL), "utf8");
return objectCopy;
}
exports.generateStringFile = generateStringFile;
function splitBufferBundle(origin) {
const initialValue = [{}, {}];
if (!origin) {
return initialValue;
exports.cloneRecursive = cloneRecursive;
function assertUnfrozen(instance) {
if (instance.isFrozen) {
throw new Error(Messages.BUNDLE.CLOSED);
}
return Object.entries(origin).reduce(([l10n, bundle], [key, buffer]) => {
if (!key.includes(".lproj")) {
return [
l10n,
{
...bundle,
[key]: buffer,
},
];
}
const pathComponents = key.split(path_1.sep);
const lang = pathComponents[0];
const file = pathComponents.slice(1).join("/");
(l10n[lang] || (l10n[lang] = {}))[file] = buffer;
return [l10n, bundle];
}, initialValue);
}
exports.splitBufferBundle = splitBufferBundle;
function getAllFilesWithName(name, source, mode = "includes", forceLowerCase = false) {
return source.filter((file) => ((forceLowerCase && file.toLowerCase()) || file)[mode](name));
}
exports.getAllFilesWithName = getAllFilesWithName;
function hasFilesWithName(name, source, mode = "includes", forceLowerCase = false) {
return source.some((file) => ((forceLowerCase && file.toLowerCase()) || file)[mode](name));
}
exports.hasFilesWithName = hasFilesWithName;
function deletePersonalization(source, logosNames = []) {
[...logosNames, "personalization.json"].forEach((file) => delete source[file]);
}
exports.deletePersonalization = deletePersonalization;
exports.assertUnfrozen = assertUnfrozen;
//# sourceMappingURL=utils.js.map
{
"name": "passkit-generator",
"version": "2.0.8",
"version": "3.0.0",
"description": "The easiest way to generate custom Apple Wallet passes in Node.js",

@@ -10,3 +10,2 @@ "main": "lib/index.js",

"build:src": "rimraf lib && npx tsc -p tsconfig.dist.json",
"build:examples": "cd examples && npm run build",
"build:spec": "rimraf \"./spec/*.!(ts)\" && npx tsc -p tsconfig.spec.json",

@@ -16,2 +15,4 @@ "prepublishOnly": "npm run build",

"example": "npm run build:src && npm --prefix examples run example",
"example:install": "npm --prefix examples install",
"example:build": "npm --prefix examples run build",
"example:debug": "npm run build:src && npm --prefix examples run example:debug"

@@ -30,21 +31,19 @@ },

"dependencies": {
"debug": "^4.3.1",
"joi": "^17.4.0",
"do-not-zip": "^1.0.0",
"joi": "^17.4.2",
"node-forge": "^0.10.0",
"tslib": "^2.3.0",
"yazl": "^2.5.1"
"tslib": "^2.3.1"
},
"engines": {
"node": ">=10.0.0"
"node": ">=14.18.1"
},
"devDependencies": {
"@types/debug": "^4.1.5",
"@types/jasmine": "^3.7.7",
"@types/node": "^14.17.3",
"@types/node-forge": "^0.10.0",
"@types/yazl": "^2.4.2",
"jasmine": "^3.7.0",
"prettier": "^2.3.1",
"@types/jasmine": "^3.10.1",
"@types/node": "^16.11.4",
"@types/node-forge": "^0.10.8",
"@types/do-not-zip": "^1.0.0",
"jasmine": "^3.10.0",
"prettier": "^2.4.1",
"rimraf": "^3.0.2",
"typescript": "^4.3.4"
"typescript": "^4.4.4"
},

@@ -51,0 +50,0 @@ "files": [

@@ -7,3 +7,3 @@ <div align="center">

<br>
<p align="center">Simple Node.js interface to generate customized <a href="https://developer.apple.com/wallet/">Apple Wallet Passes</a> for iOS.</p>
<p align="center">Simple Node.js interface to generate customized <a href="https://developer.apple.com/wallet/">Apple Wallet Passes</a> for iOS and WatchOS.</p>

@@ -18,15 +18,12 @@ ![](https://img.shields.io/npm/v/passkit-generator.svg?label=passkit-generator)

### Architecture
## Architecture
This package was created with a specific architecture in mind: **application** and **model** (as preprocessed entity), to split as much as possible static objects (such as logo, background, icon, etc.) from dynamic ones (translations, barcodes, serialNumber, ...).
This library was created with a specific architecture in mind: **application** and **model** (as preprocessed entity), to split as much as possible static objects (such as logo, background, icon, etc.) from dynamic ones (translations, barcodes, serialNumber, ...), while keeping an eye on the different possible execution contexts.
Pass creation and population doesn't fully happen in runtime. Pass template (model) can be one of a set of buffers or a folder, that will contain all the objects needed (static medias) and structure to make a pass work.
Pass creation and population might not fully happen in runtime. This library allows to create a pass from scratch, specify a folder model (template) or specify a set of buffers. In the last two cases, both should contain all the objects needed (static medias) and structure to make a pass work.
Both Pass template will be read and pushed as they are in the resulting .zip file, while dynamic objects will be patched against `pass.json` or generated in runtime (`manifest.json`, `signature` and translation files).
All the static medias from both sources, will be read and pushed as they are in the resulting .zip file; dynamic object will be patched against `pass.json`, generated on runtime (`manifest.json`, `signature`) or merged if already existing (translation files).
Whenever adding files, through scratch, template or buffer, these will be read and pushed as they are in the resulting .zip file, while dynamic data will be patched (`pass.json` with props) or generated in runtime (`manifest.json`, `signature` and translation files).
> ⚠ Do not rely on branches outside "master", as might not be stable and will be removed once merged.
### Installation
### Install
```sh

@@ -40,3 +37,3 @@ $ npm install passkit-generator --save

This package comes with an [API documentation](./API.md), that makes available a series of methods to create and customize passes.
This package comes with an [API Documentation Reference](https://github.com/alexandercerutti/passkit-generator/wiki/API-Documentation-Reference), available in wiki, that makes available a series of methods to create and customize passes.

@@ -47,3 +44,3 @@ ---

Check the [v1 branch](https://github.com/alexandercerutti/passkit-generator/tree/v1.6.8). That branch is kept for reference only.
Check the [v2 tag](https://github.com/alexandercerutti/passkit-generator/tree/v2.0.8). That tag is kept for reference only.

@@ -54,21 +51,19 @@ ---

Look at the [Migration Guide](https://github.com/alexandercerutti/passkit-generator/wiki/Migrating-from-v1-to-v2).
Look at the [Migration Guide](https://github.com/alexandercerutti/passkit-generator/wiki/Migrating-from-v2-to-v3).
---
## Get Started
## Getting Started
##### Model
The first thing you'll have to do, is to start creating a model. A model contains all the basic pass data that compose the Pass identity.
These data can be files (icon, thumbnails, ...), or pieces of information to be written in `pass.json` (Pass type identifier, Team Identifier, colors, ...).
Assuming that you don't have a model yet, the first thing you'll have to do, is creating one. A model contains all the basic pass data that compose the Pass identity.
These data can be files (icon, thumbnails, ...), or pieces of information to be written in `pass.json` (Pass type identifier, Team Identifier, colors, ...) and whatever you know that likely won't be customized on runtime.
This package allows to use two kinds of models: **Folder Model** or **Buffer Model**. If starting from scratch, the preferred solution is to use the folder as model, as it will allow you to access easily all the files. Also, a buffer model is mainly designed for models that are ready to be used in your application.
When starting from zero, the best suggested solution is to use a Template (folder) to start with, as it will allow an easier access to all the files and data. Nothing will prevent you using a buffer model or creating a pass from scratch, but they are meant for an advanced usage or different contexts (e.g. running a cloud function might require a scratch model for faster startup, without storing the model in a "data bucket").
Let's suppose you have a file `model.zip` stored somewhere: you unzip it in runtime and then get the access to its files as buffers. Those buffers should be available for the rest of your application run-time and you shouldn't be in need to read them every time you are going to create a pass.
> To keep a model in memory, the method [`createAbstractModel`](https://github.com/alexandercerutti/passkit-generator/blob/master/API.md#create-an-abstract-model) has been created.
**To maintain a pass model available during the run-time, a PKPass instance can be created from whatever source, and then used as a template through `PKPass.from`**.
---
> Using the .pass extension is a best practice, showing that the directory is a pass package.

@@ -83,15 +78,10 @@ > ([Build your first pass - Apple Developer Portal](https://apple.co/2LYXWo3)).

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.
It will output a .zip file that you can decompress and use as source.
Since `.pass` extension is required, **it will be up to you to unzip the generated model in a .pass folder**.
---
```bash
$ cd yourProjectDir;
$ mkdir passModels && mkdir $_/myFirstModel.pass && cd $_;
```
You can 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. Omitting an icon resolution, might make a pass work on a device (e.g. Mac) but not on another (e.g. iPhone). _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#localizing-passes) 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.
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`.

@@ -107,3 +97,3 @@

"passTypeIdentifier": "pass.<bundle id>",
"teamIdentifier": "<here your team identifier>",
"teamIdentifier": "<your team identifier>",
"organizationName": "<your organization name>",

@@ -122,34 +112,5 @@ "description": "A localizable description of your pass. To do so, put here a placeholder.",

This is a standard procedure: you would have to do it also without using this library. We'll use OpenSSL to complete our work (or to do it entirely, if only on terminal), so be sure to have it installed.
You'll need the following three elements:
- Apple WWDR (_Worldwide Developer Relationship_) certificate
- Signer certificate
- Signer key
[Follow the **FULL GUIDE in wiki** to get all the files you need to proceed](https://github.com/alexandercerutti/passkit-generator/wiki/Generating-Certificates).
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 😜).
<br>
<hr>
> **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.
2. Confirm and register the new identifier.
3. Go back to the pass type identifiers, click on your new pass id and edit it.
4. Click "Create Certificate" button and follow the instructions until you won't download a certificate like `pass.cer`. (here you'll generate the `.certSigningRequest` file to be uploaded).
5. Open the downloaded certificate. Go in "Certificates" on left in macOS Keychain access and `right-click > Export "\<certname\>"`. Choose a password (and write it down) and you will get a PKCS#12 file (`.p12`).
6. Open terminal, place where you want to save the files and insert the following OpenSSL commands changing the contents between angular brackets. You'll have to choose a secret passphrase (and write it down) that you'll use also in the application.
```sh
# Creating and changing dir
$ mkdir "certs" && cd $_
# 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.
---

@@ -165,10 +126,9 @@

/**
* Use `const { createPass } = require("passkit-generator");`
* for usage in pure Node.js. Please note that `Pass` is only exported
* as Typescript type.
* Use `const { PKPass } = require("passkit-generator");`
* for usage in pure Node.js
*/
import { createPass, Pass } from "passkit-generator";
import { PKPass } from "passkit-generator";
try {
const examplePass = await createPass({
const pass = PKPass.from({
model: "./passModels/myFirstModel",

@@ -178,21 +138,22 @@ certificates: {

signerCert: "./certs/signercert.pem",
signerKey: {
keyFile: "./certs/signerkey.pem",
passphrase: "123456"
}
signerKey: "./certs/signerkey.pem",
signerKeyPassphrase: "123456"
},
overrides: {
// keys to be added or overridden
serialNumber: "AAGH44625236dddaffbda"
}
}, {
// 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
pass.localize("en", { ... });
pass.setBarcodes("36478105430"); // Random value
// Generate the stream .pkpass file stream
const stream: Stream = examplePass.generate();
const stream = pass.getAsStream();
doSomethingWithTheStream(stream);
doSomethingWithTheStream(stream);
// or
const buffer = pass.getAsBuffer();
doSomethingWithTheBuffer(buffer);
} catch (err) {

@@ -207,38 +168,37 @@ doSomethingWithTheError(err);

/**
* Use `const { createPass } = require("passkit-generator");`
* for usage in pure Node.js. Please note that `Pass` is only exported
* as Typescript type.
* Use `const { PKPass } = require("passkit-generator");`
* for usage in pure Node.js
*/
import { createPass, Pass } from "passkit-generator";
import { PKPass } 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"
}
const examplePass = new PKPass({
"thumbnail": Buffer.from([ ... ]),
"icon": Buffer.from([ ... ]),
"pass.json": Buffer.from([ ... ]),
"it.lproj/pass.strings": Buffer.from([ ... ])
},
{
wwdr: "./certs/wwdr.pem",
signerCert: "./certs/signercert.pem",
signerKey: "./certs/signerkey.pem",
signerKeyPassphrase: "123456",
},
{
// 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
pass.localize("en", { ... });
pass.setBarcodes("36478105430"); // Random value
// Generate the stream .pkpass file stream
const stream: Stream = examplePass.generate();
const stream = pass.getAsStream();
doSomethingWithTheStream(stream);
doSomethingWithTheStream(stream);
// or
const buffer = pass.getAsBuffer();
doSomethingWithTheBuffer(buffer);
} catch (err) {

@@ -256,3 +216,3 @@ doSomethingWithTheError(err);

If you used this package in any of your projects, feel free to open a topic in issues to tell me and include a project description or link (for companies). 😊 You'll make me feel like my time hasn't been wasted, even if it had not anyway because I learnt a lot of things by creating this.
If you used this package in any of your projects, feel free to open a topic in issues to tell me and include a project description or link (for companies). 😊 You'll make me feel like my time hasn't been wasted, even if it had not anyway because I learnt and keep learning a lot of things by creating this.

@@ -270,4 +230,4 @@ The idea to develop this package, was born during the Apple Developer Academy 17/18, in Naples, Italy, driven by the need to create an iOS app component regarding passes generation for events.

A big thanks to all the people that contributed to improve this package. Any contribution is welcome. Do you have an idea to make this improve or something to say? Open a topic in the issues and we'll discuss together! Thank you ❤
Also a big big big big thank you to all the financial contributors!
A big thanks to all the people that contributed to improve this package. Any contribution is welcome. Do you have an idea to make this improve or something to say? Open a topic in the issues and we'll discuss together! Thank you ❤️
Also a big big big big thank you to all the financial contributors, which help me maintain the development of this package ❤️!

@@ -274,0 +234,0 @@ ### Code Contributors

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc