UNPKG

@walletpass/pass-js

Version:

Apple Wallet Pass generating and pushing updates from Node.js

141 lines 5.25 kB
// SPDX-License-Identifier: AGPL-3.0-or-later // Copyright (C) 2017-2026 Konstantin Vyatkin <tino@vtkn.io> import { PASS_STYLES, TRANSIT, STRUCTURE_FIELDS } from '../constants.js'; import { FieldsMap } from './fieldsMap.js'; import { NFCField } from './nfc-fields.js'; export class PassStructure { fields = {}; constructor(fields = {}) { for (const style of PASS_STYLES) { if (!(style in fields)) continue; this.style = style; if ('boardingPass' in fields && fields.boardingPass) { this.transitType = fields.boardingPass.transitType; } else if ('storeCard' in this.fields && 'nfc' in fields) { this.fields.nfc = new NFCField(fields.nfc); } const structure = fields[style]; if (!structure) continue; for (const prop of STRUCTURE_FIELDS) { if (!(prop in structure)) continue; const currentProperty = structure[prop]; const target = this[prop]; if (Array.isArray(currentProperty)) for (const field of currentProperty) target.add(field); else if (currentProperty instanceof FieldsMap) for (const [key, data] of currentProperty) target.add({ key, ...data }); } } } // Returns the structure container for the current pass style, creating it // if it doesn't exist. Throws if no style is set. structure() { const { style } = this; if (!style) throw new ReferenceError(`Pass style is undefined, set the pass style before accessing pass structure fields`); const s = this.fields[style]; if (s) return s; const fresh = {}; this.fields[style] = fresh; return fresh; } fieldMap(name) { const s = this.structure(); if (!(s[name] instanceof FieldsMap)) s[name] = new FieldsMap(); return s[name]; } /** Pass type, e.g. boardingPass, coupon, etc. */ get style() { for (const style of PASS_STYLES) { if (style in this.fields) return style; } return undefined; } set style(v) { for (const style of PASS_STYLES) if (style !== v) delete this.fields[style]; // NFC is a storeCard-only field; drop any carry-over when switching away. if (v !== 'storeCard') delete this.fields.nfc; if (!v) return; if (!PASS_STYLES.has(v)) throw new TypeError(`Invalid Pass type "${v}"`); if (!(v in this.fields)) this.fields[v] = {}; if ('storeCard' in this.fields && !this.fields.nfc) this.fields.nfc = new NFCField(); } get transitType() { if (this.style !== 'boardingPass') throw new ReferenceError(`transitType field only allowed in Boarding Passes, current pass is ${this.style}`); if ('boardingPass' in this.fields && this.fields.boardingPass) return this.fields.boardingPass.transitType; return undefined; } set transitType(v) { const { style } = this; if (!style) { if (!v) return; this.style = 'boardingPass'; } if (!('boardingPass' in this.fields)) throw new ReferenceError(`transitType field is only allowed at boarding passes`); if (!v) { if (this.fields.boardingPass) delete this.fields.boardingPass .transitType; } else { if (Object.values(TRANSIT).includes(v)) { if (this.fields.boardingPass) this.fields.boardingPass.transitType = v; else this.fields.boardingPass = { transitType: v }; } else throw new TypeError(`Unknown transit type "${v}"`); } } get nfc() { if (!('storeCard' in this.fields)) throw new ReferenceError(`NFC fields only available for storeCard passes, current is ${this.style}`); return this.fields.nfc; } get headerFields() { return this.fieldMap('headerFields'); } get auxiliaryFields() { return this.fieldMap('auxiliaryFields'); } get backFields() { return this.fieldMap('backFields'); } get primaryFields() { return this.fieldMap('primaryFields'); } get secondaryFields() { return this.fieldMap('secondaryFields'); } // iOS 18 event-ticket dashboard fields. Only valid on eventTicket // passes — mirrors the style gating used by `transitType` (boardingPass) // and `nfc` (storeCard). get additionalInfoFields() { if (this.style !== 'eventTicket') throw new ReferenceError(`additionalInfoFields only allowed on eventTicket passes, current style is ${this.style}`); return this.fieldMap('additionalInfoFields'); } } //# sourceMappingURL=pass-structure.js.map