From abc669594023c1c95be16dda33a6799c4a5cb932 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 18 Sep 2021 18:32:14 +0200 Subject: [PATCH 001/199] Added Bundle class implementation along with tests --- spec/Bundle.ts | 58 +++++++++++++++++++++++++++++ src/Bundle.ts | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 2 + 3 files changed, 159 insertions(+) create mode 100644 spec/Bundle.ts create mode 100644 src/Bundle.ts diff --git a/spec/Bundle.ts b/spec/Bundle.ts new file mode 100644 index 0000000..f77d645 --- /dev/null +++ b/spec/Bundle.ts @@ -0,0 +1,58 @@ +import { Stream } from "stream"; +import { default as Bundle, filesSymbol } from "../lib/Bundle"; + +describe("Bundle", () => { + let bundle: InstanceType; + + beforeEach(() => { + bundle = new Bundle("application/vnd.apple.pkpass"); + }); + + it("should throw an error if no mime-type is specified in the constructor", () => { + // @ts-expect-error + expect(() => new Bundle()).toThrowError( + Error, + "Cannot build Bundle. MimeType is missing", + ); + }); + + it("should expose the mime-type as public property", () => { + expect(bundle.mimeType).toBe("application/vnd.apple.pkpass"); + }); + + it("should allow to add buffers", () => { + const buffer = Buffer.alloc(0); + bundle.addBuffer("pass.json", buffer); + + expect(bundle[filesSymbol]).toEqual({ "pass.json": buffer }); + }); + + it("should throw error if freezed", async () => { + addEmptyFilesToBundle(bundle); + + await bundle.getAsBuffer(); + + expect(() => + bundle.addBuffer("icon.png", Buffer.alloc(0)), + ).toThrowError(Error, "Cannot add file. Bundle is closed."); + }); + + it("should return a stream with 'getAsStream'", () => { + addEmptyFilesToBundle(bundle); + + expect(bundle.getAsStream()).toBeInstanceOf(Stream); + }); + + it("should return a buffer with 'getAsBuffer'", async () => { + addEmptyFilesToBundle(bundle); + + expect(await bundle.getAsBuffer()).toBeInstanceOf(Buffer); + }); +}); + +function addEmptyFilesToBundle(bundle: Bundle) { + const buffer = Buffer.alloc(0); + bundle.addBuffer("pass.json", buffer); + bundle.addBuffer("icon@2x.png", buffer); + bundle.addBuffer("icon@3x.png", buffer); +} diff --git a/src/Bundle.ts b/src/Bundle.ts new file mode 100644 index 0000000..966fcf8 --- /dev/null +++ b/src/Bundle.ts @@ -0,0 +1,99 @@ +import { Stream } from "stream"; +import { ZipFile } from "yazl"; + +export const filesSymbol = Symbol("bundleFiles"); +const bundleStateSymbol = Symbol("state"); +const archiveSymbol = Symbol("zip"); + +enum BundleState { + CLOSED = 0, + OPEN = 1, +} + +namespace Mime { + export type type = string; + export type subtype = string; +} + +/** + * Defines a container ready to be distributed. + * If no mimeType is passed to the constructor, + * it will throw an error. + */ + +export default class Bundle { + private [bundleStateSymbol]: BundleState = BundleState.OPEN; + private [filesSymbol]: { [key: string]: Buffer } = {}; + private [archiveSymbol] = new ZipFile(); + + constructor(public mimeType: `${Mime.type}/${Mime.subtype}`) { + if (!mimeType) { + throw new Error("Cannot build Bundle. MimeType is missing"); + } + } + + /** + * Freezes / Closes the bundle so no more files + * can be added any further. + */ + + private freeze() { + if (this[bundleStateSymbol] === BundleState.CLOSED) { + return; + } + + this[bundleStateSymbol] = BundleState.CLOSED; + this[archiveSymbol].end(); + } + + /** + * Allows files to be added to the bundle. + * If the bundle is closed, it will throw an error. + * + * @param fileName + * @param buffer + */ + + public addBuffer(fileName: string, buffer: Buffer) { + if (this[bundleStateSymbol] === BundleState.CLOSED) { + throw new Error("Cannot add file. Bundle is closed."); + } + + this[filesSymbol][fileName] = buffer; + this[archiveSymbol].addBuffer(buffer, fileName); + } + + /** + * Closes the bundle and returns it as a Buffer. + * Once closed, the bundle does not allow files + * to be added any further. + * + * @returns Promise + */ + + public getAsBuffer(): Promise { + const stream = this.getAsStream(); + const chunks = []; + + return new Promise((resolve) => { + stream.on("data", (data: Buffer) => { + chunks.push(data); + }); + + stream.on("end", () => resolve(Buffer.from(chunks))); + }); + } + + /** + * Closes the bundle and returns it as a stream. + * Once closed, the bundle does not allow files + * to be added any further. + * + * @returns + */ + + public getAsStream(): Stream { + this.freeze(); + return this[archiveSymbol].outputStream; + } +} diff --git a/src/index.ts b/src/index.ts index 55dfbb3..7d46253 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,3 +3,5 @@ export type { AbstractModel } from "./abstract"; export { createPass } from "./factory"; export { createAbstractModel } from "./abstract"; + +export { default as Bundle } from "./Bundle"; From 8d1a6277fb9aeb7c36124df0eb7e486ec68fec9b Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 18 Sep 2021 18:32:41 +0200 Subject: [PATCH 002/199] (auto) Converted format of package-lock.json of examples sub-package --- examples/package-lock.json | 821 ++++++++++++++++++++++++++++++++++++- 1 file changed, 817 insertions(+), 4 deletions(-) diff --git a/examples/package-lock.json b/examples/package-lock.json index 1ae64e9..bf5ffc4 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -1,9 +1,758 @@ { "name": "examples", "version": "0.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "version": "0.0.0", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "express": "^4.17.1", + "node-fetch": "^2.6.1", + "tslib": "^2.1.0" + }, + "devDependencies": { + "@types/express": "^4.17.0", + "@types/node-fetch": "^2.5.0", + "typescript": "^4.1.3" + }, + "peerDependencies": { + "passkit-generator": "latest" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz", + "integrity": "sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==", + "peer": true + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "peer": true, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.2.tgz", + "integrity": "sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA==", + "peer": true, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", + "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==", + "peer": true + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "peer": true + }, + "node_modules/@types/body-parser": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", + "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.32", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", + "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.0.tgz", + "integrity": "sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.16.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.7.tgz", + "integrity": "sha512-847KvL8Q1y3TtFLRTXcVakErLJQgdpFSaq+k043xefz9raEf0C7HalpSY7OW5PyjCnY8P7bPW5t/Co9qqp+USg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/mime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", + "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "12.6.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.8.tgz", + "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==", + "dev": true + }, + "node_modules/@types/node-fetch": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.0.tgz", + "integrity": "sha512-TLFRywthBgL68auWj+ziWu+vnmmcHCDFC/sqCOQf1xTz4hRq8cu79z8CtHU9lncExGBsB8fXA4TiLDLt6xvMzw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, + "node_modules/@types/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/joi": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.4.2.tgz", + "integrity": "sha512-Lm56PP+n0+Z2A2rfRvsfWVDXGEWjXxatPopkQ8qQ5mxCEhwHG+Ettgg5o98FFaxilOxozoa14cFhrE/hOzh/Nw==", + "peer": true, + "dependencies": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.0", + "@sideway/formula": "^3.0.0", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dependencies": { + "mime-db": "1.40.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "peer": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/passkit-generator": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/passkit-generator/-/passkit-generator-2.0.8.tgz", + "integrity": "sha512-blirusjaav6oK9fzNhX20Ff3uLLDMF4fGsOgLYpUFHaoVYjKbOATuX9bUuQ0FTguBwJ5BNTeCtWhNAoIEttaNg==", + "peer": true, + "dependencies": { + "debug": "^4.3.1", + "joi": "^17.4.0", + "node-forge": "^0.10.0", + "tslib": "^2.3.0", + "yazl": "^2.5.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/passkit-generator/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/passkit-generator/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "peer": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "peer": true, + "dependencies": { + "buffer-crc32": "~0.2.3" + } + } + }, "dependencies": { + "@hapi/hoek": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz", + "integrity": "sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==", + "peer": true + }, + "@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "peer": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/address": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.2.tgz", + "integrity": "sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA==", + "peer": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", + "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==", + "peer": true + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "peer": true + }, "@types/body-parser": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", @@ -112,6 +861,12 @@ "type-is": "~1.6.17" } }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "peer": true + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -269,6 +1024,19 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" }, + "joi": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.4.2.tgz", + "integrity": "sha512-Lm56PP+n0+Z2A2rfRvsfWVDXGEWjXxatPopkQ8qQ5mxCEhwHG+Ettgg5o98FFaxilOxozoa14cFhrE/hOzh/Nw==", + "peer": true, + "requires": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.0", + "@sideway/formula": "^3.0.0", + "@sideway/pinpoint": "^2.0.0" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -317,6 +1085,12 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "peer": true + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -330,6 +1104,36 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "passkit-generator": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/passkit-generator/-/passkit-generator-2.0.8.tgz", + "integrity": "sha512-blirusjaav6oK9fzNhX20Ff3uLLDMF4fGsOgLYpUFHaoVYjKbOATuX9bUuQ0FTguBwJ5BNTeCtWhNAoIEttaNg==", + "peer": true, + "requires": { + "debug": "^4.3.1", + "joi": "^17.4.0", + "node-forge": "^0.10.0", + "tslib": "^2.3.0", + "yazl": "^2.5.1" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "peer": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "peer": true + } + } + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -429,9 +1233,9 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, "type-is": { "version": "1.6.18", @@ -462,6 +1266,15 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "peer": true, + "requires": { + "buffer-crc32": "~0.2.3" + } } } } From cab8e08c3ecbc142584828bbe5f7c79dc8b62ee7 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 18 Sep 2021 18:56:09 +0200 Subject: [PATCH 003/199] Added first integration of PKPass file --- src/PKPass.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/PKPass.ts diff --git a/src/PKPass.ts b/src/PKPass.ts new file mode 100644 index 0000000..4d01130 --- /dev/null +++ b/src/PKPass.ts @@ -0,0 +1,23 @@ +import { Certificates } from "../lib/schemas"; +import { default as Bundle, filesSymbol } from "./Bundle"; + +interface NamedBuffers { + [key: string]: Buffer; +} + +export class PKPass extends Bundle { + constructor(buffers: NamedBuffers, certificates: Certificates) { + super("application/vnd.apple.pkpass"); + + const buffersEntries = Object.entries(buffers); + + for ( + let i = buffersEntries.length, buffer: [string, Buffer]; + (buffer = buffersEntries[--i]); + + ) { + const [fileName, contentBuffer] = buffer; + this.addBuffer(fileName, contentBuffer); + } + } +} From a97023262374652141895d00fc38282df17c6896 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 18 Sep 2021 21:45:46 +0200 Subject: [PATCH 004/199] Added fields and pool creation to PKPass --- src/PKPass.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index 4d01130..1f9f820 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -1,11 +1,21 @@ -import { Certificates } from "../lib/schemas"; +import FieldsArray from "../src/fieldsArray"; +import { Certificates } from "../src/schemas"; import { default as Bundle, filesSymbol } from "./Bundle"; +const fieldKeysPoolSymbol = Symbol("fieldKeysPoolSymbol"); + interface NamedBuffers { [key: string]: Buffer; } export class PKPass extends Bundle { + private [fieldKeysPoolSymbol] = new Set(); + public primaryFields /*****/ = new FieldsArray(this[fieldKeysPoolSymbol]); + public secondaryFields /***/ = new FieldsArray(this[fieldKeysPoolSymbol]); + public auxiliaryFields /***/ = new FieldsArray(this[fieldKeysPoolSymbol]); + public headerFields /******/ = new FieldsArray(this[fieldKeysPoolSymbol]); + public backFields /********/ = new FieldsArray(this[fieldKeysPoolSymbol]); + constructor(buffers: NamedBuffers, certificates: Certificates) { super("application/vnd.apple.pkpass"); From a38c235cf8e2bd5537866ef51a5b43a3d102c21b Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 18 Sep 2021 22:07:13 +0200 Subject: [PATCH 005/199] Added first untested and incomplete implementation of PKPass.from --- src/PKPass.ts | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/PKPass.ts b/src/PKPass.ts index 1f9f820..8e91c3d 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -1,6 +1,7 @@ import FieldsArray from "../src/fieldsArray"; import { Certificates } from "../src/schemas"; import { default as Bundle, filesSymbol } from "./Bundle"; +import { getModelFolderContents } from "./parser"; const fieldKeysPoolSymbol = Symbol("fieldKeysPoolSymbol"); @@ -16,6 +17,47 @@ export class PKPass extends Bundle { public headerFields /******/ = new FieldsArray(this[fieldKeysPoolSymbol]); public backFields /********/ = new FieldsArray(this[fieldKeysPoolSymbol]); + /** + * Either create a pass from another one + * or a disk path. + * + * @param source + * @returns + */ + + static async from(source: PKPass | string): Promise { + let certificates: Certificates = undefined; + let buffers: NamedBuffers = undefined; + + if (source instanceof PKPass) { + /** Cloning is happening here */ + certificates = source.certificates; + buffers = {}; + + const buffersEntries = Object.entries(source[filesSymbol]); + + /** Cloning all the buffers to prevent unwanted edits */ + for (let i = 0; i < buffersEntries.length; i++) { + const [fileName, contentBuffer] = buffersEntries[i]; + + buffers[fileName] = Buffer.from(contentBuffer); + } + } else { + /** Disk model reading is happening here */ + + /** + * @TODO Rename bundles in something else. + * @TODO determine how to use localized files + */ + + const { bundle, l10nBundle } = await getModelFolderContents(source); + + buffers = bundle; + } + + return new PKPass(buffers, certificates); + } + constructor(buffers: NamedBuffers, certificates: Certificates) { super("application/vnd.apple.pkpass"); From 47d8f28e67287766b2ff7fac8e560b763d4f5e97 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 18 Sep 2021 23:16:50 +0200 Subject: [PATCH 006/199] Added experimental autoFreezable method in bundle (probably I'll regret it) --- src/Bundle.ts | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Bundle.ts b/src/Bundle.ts index 966fcf8..6580e0b 100644 --- a/src/Bundle.ts +++ b/src/Bundle.ts @@ -32,12 +32,35 @@ export default class Bundle { } } + /** + * @EXPERIMENTAL + * + * @param mimeType + */ + + static autoFreezable(mimeType: `${Mime.type}/${Mime.subtype}`): Bundle { + const bundle = new Bundle(mimeType); + + /** + * @TODO + * Might not be the best idea I might have had. + * I have to test this further more to check if + * actually we can leverage of this event loop feature + * to freeze it automatically once we processed every + * promise for bundling; + */ + + setTimeout(bundle.freeze, 0); + + return bundle; + } + /** * Freezes / Closes the bundle so no more files * can be added any further. */ - private freeze() { + protected freeze() { if (this[bundleStateSymbol] === BundleState.CLOSED) { return; } From 7c7a8680dee536a89fcb1916288e5f127cd249ca Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 18 Sep 2021 23:17:47 +0200 Subject: [PATCH 007/199] Added signatures for PKPass methods --- src/PKPass.ts | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) diff --git a/src/PKPass.ts b/src/PKPass.ts index 8e91c3d..c5cfb36 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -9,7 +9,15 @@ interface NamedBuffers { [key: string]: Buffer; } +type TransitTypes = `PKTransitType${ + | "Air" + | "Boat" + | "Bus" + | "Generic" + | "Train"}`; + export class PKPass extends Bundle { + private certificates: Certificates; private [fieldKeysPoolSymbol] = new Set(); public primaryFields /*****/ = new FieldsArray(this[fieldKeysPoolSymbol]); public secondaryFields /***/ = new FieldsArray(this[fieldKeysPoolSymbol]); @@ -72,4 +80,251 @@ export class PKPass extends Bundle { this.addBuffer(fileName, contentBuffer); } } + + /** + * Allows getting an image of the props + * that are composing your pass instance. + */ + + get props(): Readonly { + /** + * @TODO implement + */ + + return undefined; + } + + /** + * Allows setting a transitType property + * for a boardingPass + * + * @param value + */ + + set transitType(value: TransitTypes) { + /** + * @TODO implement + * @TODO validate against schema + * @TODO save into props + */ + } + + /** + * Allows getting the current transitType + * from pass props + */ + + get transitType(): TransitTypes { + /** + * @TODO implement + * @TODO read from props + */ + + return undefined; + } + + // **************************** // + // *** ASSETS SETUP METHODS *** // + // **************************** // + + /** + * Allows adding a new asset inside the pass / bundle; + * + * @param pathName + * @param buffer + */ + + public addBuffer(pathName: string, buffer: Buffer): void { + /** + * @TODO implement + * @TODO exclude pass.json, manifest, signature files + */ + + super.addBuffer(pathName, buffer); + } + + // ************************* // + // *** EXPORTING METHODS *** // + // ************************* // + + /** + * Exports the pass as a zip buffer. When this method + * is invoked, the bundle will get frozen and, thus, + * no files will be allowed to be added any further. + * + * @returns + */ + + public async getAsBuffer(): Promise { + /** + * @TODO compile this pass into something usable + * @TODO like _patch on old version + * @TODO share implementation with getAsStream + */ + + return super.getAsBuffer(); + } + + /** + * Exports the pass as a zip stream. When this method + * is invoked, the bundle will get frozen and, thus, + * no files will be allowed to be added any further. + * + * @returns + */ + + public getAsStream(): Stream { + /** + * @TODO compile this pass into something usable + * @TODO like _patch on old version + * @TODO share implementation with getAsBuffer + */ + + return super.getAsStream(); + } + + // ************************** // + // *** DATA SETUP METHODS *** // + // ************************** // + + /** + * Allows to specify a language to be added to the + * final bundle, along with some optionals / additional + * translations. + * + * If the language already exists in the origin source, + * translations will be added to the existing ones. + * + * @param lang + * @param translations + */ + + localize(lang: string, translations?: any): this { + /** + * @TODO change translations format + * @TODO specify a way to get current ones deleted + * @TODO Default languages from source + * @TODO print warning if lang is already in selection? + */ + + return this; + } + + /** + * Allows to specify an expiration date for the pass. + * + * @param date + * @returns + */ + + setExpiration(date: Date | null): this { + /** + * @TODO implement + */ + + return this; + } + + /** + * Allows to set the Pass directly as voided. + * Useful for updates. + * + * @TODO REMOVE, can be passed in overrides. It doesn't require any validation. + * It is just a boolean + */ + + void(): this { + /** + * @TODO implement + */ + + return this; + } + + /** + * Allows setting some beacons the OS should + * react to and show this pass. + * + * @param beacons + * @returns + */ + + setBeacons(...beacons: Schemas.Beacon[]): this { + /** + * @TODO implement + * @TODO specify a way to get current ones deleted + */ + + return this; + } + + /** + * Allows setting some locations the OS should + * react to and show this pass. + * + * @param locations + * @returns + */ + + setLocations(...locations: Schemas.Location[]): this { + /** + * @TODO implement + * @TODO specify a way to get current ones deleted + */ + + return this; + } + + /** + * Allows setting a relevant date in which the OS + * should show this pass. + * + * @param date + */ + + setRelevantDate(date: Date): this { + /** + * @TODO implement + */ + + return this; + } + + /** + * Allows to specify some barcodes formats. + * As per the current specifications, only the first + * will be shown to the user, without any possibility + * to change it. + * + * @param barcodes + * @returns + */ + + setBarcodes(...barcodes: Schemas.Barcode[]): this { + /** + * @TODO implement + * @TODO implement data completion + * @TODO specify a way to get current ones deleted + */ + + return this; + } + + /** + * Allows to specify details to make this, an + * NFC-capable pass. + * + * @see https://developer.apple.com/documentation/walletpasses/pass/nfc + * @param data + * @returns + */ + + setNFCCapability(data: Schemas.NFC): this { + /** + * @TODO implement + * @TODO specify a way to get current one deleted + */ + + return this; + } } From ef0c28b744da326d9becb5adad14ecf7ed9d81c6 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 18 Sep 2021 23:18:12 +0200 Subject: [PATCH 008/199] Added untested implementation of PKPass.pack method --- src/PKPass.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/PKPass.ts b/src/PKPass.ts index c5cfb36..cf0e2fd 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -66,6 +66,35 @@ export class PKPass extends Bundle { return new PKPass(buffers, certificates); } + /** + * + * @param passes + */ + + static async pack(...passes: PKPass[]): Promise { + if (!passes.every((pass) => pass instanceof PKPass)) { + throw new Error( + "Cannot pack passes. Only PKPass instances allowed", + ); + } + + const bundle = Bundle.autoFreezable("application/vnd.apple.pkpasses"); + + const buffers = await Promise.all( + passes.map((pass) => pass.getAsBuffer()), + ); + + for (let i = 0; i < buffers.length; i++) { + bundle.addBuffer(`packed-pass-${i + 1}.pkpass`, buffers[i]); + } + + return bundle; + } + + // **************** // + // *** INSTANCE *** // + // **************** // + constructor(buffers: NamedBuffers, certificates: Certificates) { super("application/vnd.apple.pkpass"); From 1d3e1f9af586243e02622ebf1d659ff6bcf3b608 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 18 Sep 2021 23:18:25 +0200 Subject: [PATCH 009/199] Fixed paths and added imports --- src/PKPass.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index cf0e2fd..de5bbf4 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -1,7 +1,9 @@ -import FieldsArray from "../src/fieldsArray"; -import { Certificates } from "../src/schemas"; +import FieldsArray from "./fieldsArray"; +import { Certificates } from "./schemas"; import { default as Bundle, filesSymbol } from "./Bundle"; import { getModelFolderContents } from "./parser"; +import * as Schemas from "./schemas"; +import { Stream } from "stream"; const fieldKeysPoolSymbol = Symbol("fieldKeysPoolSymbol"); From 3ee5913c010a25e44e28a8a36e4820650ef0ba78 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 18 Sep 2021 23:18:39 +0200 Subject: [PATCH 010/199] Added another TODO to constructor --- src/PKPass.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/PKPass.ts b/src/PKPass.ts index de5bbf4..2505dea 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -100,6 +100,10 @@ export class PKPass extends Bundle { constructor(buffers: NamedBuffers, certificates: Certificates) { super("application/vnd.apple.pkpass"); + /** + * @TODO Validate options against Joi Schema + */ + const buffersEntries = Object.entries(buffers); for ( From 658bb287a8436aa043910f534a3caadcc62f3b5d Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 18 Sep 2021 23:22:06 +0200 Subject: [PATCH 011/199] Added comment for PKPass.pack --- src/PKPass.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/PKPass.ts b/src/PKPass.ts index 2505dea..b83799f 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -69,7 +69,13 @@ export class PKPass extends Bundle { } /** + * Creates a Bundle made of PKPass to be distributed + * as a `.pkpasses` zip file. Returns a Bundle instance + * so it can be outputted both as stream or as a buffer. * + * Throws if not all the files are instance of PKPass. + * + * @TODO test autofreezing * @param passes */ From 37b7b8698f9f4f6b8be69027f7c44259b32ca156 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 18 Sep 2021 23:23:03 +0200 Subject: [PATCH 012/199] Moved Bundle.autoFreezable creation in the attempt of creating the bundle after processing promises and leveraging event loop --- src/PKPass.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index b83799f..99df1fc 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -86,12 +86,12 @@ export class PKPass extends Bundle { ); } - const bundle = Bundle.autoFreezable("application/vnd.apple.pkpasses"); - const buffers = await Promise.all( passes.map((pass) => pass.getAsBuffer()), ); + const bundle = Bundle.autoFreezable("application/vnd.apple.pkpasses"); + for (let i = 0; i < buffers.length; i++) { bundle.addBuffer(`packed-pass-${i + 1}.pkpass`, buffers[i]); } From bdbbf235d267344ff26d175ba8c3a43445c62e82 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 19 Sep 2021 18:55:43 +0200 Subject: [PATCH 013/199] Added propsSymbol and prop getter implementation --- src/PKPass.ts | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index 99df1fc..839f317 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -6,6 +6,7 @@ import * as Schemas from "./schemas"; import { Stream } from "stream"; const fieldKeysPoolSymbol = Symbol("fieldKeysPoolSymbol"); +const propsSymbol = Symbol("props"); interface NamedBuffers { [key: string]: Buffer; @@ -21,6 +22,7 @@ type TransitTypes = `PKTransitType${ export class PKPass extends Bundle { private certificates: Certificates; private [fieldKeysPoolSymbol] = new Set(); + private [propsSymbol]: Schemas.ValidPass = {}; public primaryFields /*****/ = new FieldsArray(this[fieldKeysPoolSymbol]); public secondaryFields /***/ = new FieldsArray(this[fieldKeysPoolSymbol]); public auxiliaryFields /***/ = new FieldsArray(this[fieldKeysPoolSymbol]); @@ -128,11 +130,7 @@ export class PKPass extends Bundle { */ get props(): Readonly { - /** - * @TODO implement - */ - - return undefined; + return freezeRecusive(this[propsSymbol]); } /** @@ -369,3 +367,28 @@ export class PKPass extends Bundle { return this; } } + +function freezeRecusive(object: 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] = freezeRecusive(value[j]); + } + } else { + objectCopy[key] = freezeRecusive(value); + } + } else { + objectCopy[key] = value; + } + } + + return Object.freeze(objectCopy); +} From a73b367a6297e9744bbb4ef092b903b6675fb7e9 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 19 Sep 2021 19:29:30 +0200 Subject: [PATCH 014/199] Setted PKPass to be exported as default --- src/PKPass.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index 839f317..64eef2d 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -19,7 +19,7 @@ type TransitTypes = `PKTransitType${ | "Generic" | "Train"}`; -export class PKPass extends Bundle { +export default class PKPass extends Bundle { private certificates: Certificates; private [fieldKeysPoolSymbol] = new Set(); private [propsSymbol]: Schemas.ValidPass = {}; From 8a814e46f069ca71d7d9c6e18377de06bd4e9db7 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 19 Sep 2021 19:52:08 +0200 Subject: [PATCH 015/199] Added implementation for setBeacons along with some tests --- spec/PKPass.ts | 48 ++++++++++++++++++++++++++++++++++++++++++++ src/PKPass.ts | 27 ++++++++++++++++++++----- src/schemas/index.ts | 11 ++++++++++ 3 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 spec/PKPass.ts diff --git a/spec/PKPass.ts b/spec/PKPass.ts new file mode 100644 index 0000000..6027e9b --- /dev/null +++ b/spec/PKPass.ts @@ -0,0 +1,48 @@ +import { default as PKPass } from "../lib/PKPass"; + +describe("PKPass", () => { + describe("setBeacons", () => { + it("should reset instance.props['beacons'] if 'null' is passed as value", () => { + const pass = new PKPass({}, {}); + + pass.setBeacons({ + proximityUUID: "0000000000-00000000", + major: 4, + minor: 3, + relevantText: "This is not the Kevin you are looking for.", + }); + + expect(pass.props["beacons"].length).toBe(1); + + pass.setBeacons(null); + + expect(pass.props["beacons"]).toBeUndefined(); + }); + + it("should filter out invalid beacons objects", () => { + const pass = new PKPass({}, {}); + + /** This is invalid, major should be greater than minor */ + pass.setBeacons( + { + proximityUUID: "0000000000-00000000", + major: 2, + minor: 3, + relevantText: "This is not the Kevin you are looking for.", + }, + // @ts-expect-error + { + major: 2, + minor: 3, + }, + { + proximityUUID: "0000000000-00000", + major: 2, + minor: 1, + }, + ); + + expect(pass.props["beacons"].length).toBe(1); + }); + }); +}); diff --git a/src/PKPass.ts b/src/PKPass.ts index 64eef2d..3694af8 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -284,15 +284,32 @@ export default class PKPass extends Bundle { * Allows setting some beacons the OS should * react to and show this pass. * + * Pass `null` to remove them at all. + * + * @example + * ```ts + * PKPassInstance.setBeacons(null) + * PKPassInstance.setBeacons({ + * proximityUUID: "00000-000000-0000-00000000000", + * }); + * ``` + * * @param beacons * @returns */ - setBeacons(...beacons: Schemas.Beacon[]): this { - /** - * @TODO implement - * @TODO specify a way to get current ones deleted - */ + setBeacons(beacons: null): this; + setBeacons(...beacons: Schemas.Beacon[]): this; + setBeacons(...beacons: (Schemas.Beacon | null)[]): this { + if (beacons[0] === null) { + delete this[propsSymbol]["beacons"]; + return; + } + + this[propsSymbol]["beacons"] = Schemas.filterValid( + beacons, + Schemas.Beacon, + ); return this; } diff --git a/src/schemas/index.ts b/src/schemas/index.ts index 20c3df9..88fca9e 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -245,3 +245,14 @@ export function getValidated( return validation.value; } + +export function filterValid( + source: T[], + schema: AvailableSchemas, +): T[] { + if (!source) { + return []; + } + + return source.filter((current) => isValid(current, schema)); +} From 59efe84bc8d578901cc41b8d723b4e8af55427b3 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 19 Sep 2021 20:09:32 +0200 Subject: [PATCH 016/199] Added implementation of setLocations along with tests --- spec/PKPass.ts | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/PKPass.ts | 28 +++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/spec/PKPass.ts b/spec/PKPass.ts index 6027e9b..f0ed4ff 100644 --- a/spec/PKPass.ts +++ b/spec/PKPass.ts @@ -45,4 +45,54 @@ describe("PKPass", () => { expect(pass.props["beacons"].length).toBe(1); }); }); + + describe("setLocations", () => { + it("should reset instance.props['locations'] if 'null' is passed as value", () => { + const pass = new PKPass({}, {}); + + pass.setLocations({ + longitude: 0.25456342344, + latitude: 0.26665773234, + }); + + expect(pass.props["locations"].length).toBe(1); + + pass.setLocations(null); + + expect(pass.props["locations"]).toBeUndefined(); + }); + + it("should filter out invalid beacons objects", () => { + const pass = new PKPass({}, {}); + + pass.setLocations( + { + // @ts-expect-error + longitude: "unknown", + // @ts-expect-error + latitude: "unknown", + }, + { + altitude: "say hello from here", + longitude: 0.25456342344, + }, + { + longitude: 0.25456342344, + latitude: 0.26665773234, + altitude: 12552.31233321, + relevantText: + /** Hi mom, see how do I fly! */ + "Ciao mamma, guarda come volooo!", + }, + ); + + expect(pass.props["locations"].length).toBe(1); + expect(pass.props["locations"][0].longitude).toBe(0.25456342344); + expect(pass.props["locations"][0].latitude).toBe(0.26665773234); + expect(pass.props["locations"][0].altitude).toBe(12552.31233321); + expect(pass.props["locations"][0].relevantText).toBe( + "Ciao mamma, guarda come volooo!", + ); + }); + }); }); diff --git a/src/PKPass.ts b/src/PKPass.ts index 3694af8..f1d7b69 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -318,15 +318,33 @@ export default class PKPass extends Bundle { * Allows setting some locations the OS should * react to and show this pass. * + * Pass `null` to remove them at all. + * + * @example + * ```ts + * PKPassInstance.setLocations(null) + * PKPassInstance.setLocations({ + * latitude: 0.5333245342 + * longitude: 0.2135332252 + * }); + * ``` + * * @param locations * @returns */ - setLocations(...locations: Schemas.Location[]): this { - /** - * @TODO implement - * @TODO specify a way to get current ones deleted - */ + setLocations(locations: null): this; + setLocations(...locations: Schemas.Location[]): this; + setLocations(...locations: (Schemas.Location | null)[]): this { + if (locations[0] === null) { + delete this[propsSymbol]["locations"]; + return; + } + + this[propsSymbol]["locations"] = Schemas.filterValid( + locations, + Schemas.Location, + ); return this; } From a3195e7d219e8d4c9db9826905e22e986d523a06 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 19 Sep 2021 20:17:09 +0200 Subject: [PATCH 017/199] Added forgotten return this --- src/PKPass.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index f1d7b69..276ac56 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -303,7 +303,7 @@ export default class PKPass extends Bundle { setBeacons(...beacons: (Schemas.Beacon | null)[]): this { if (beacons[0] === null) { delete this[propsSymbol]["beacons"]; - return; + return this; } this[propsSymbol]["beacons"] = Schemas.filterValid( @@ -338,7 +338,7 @@ export default class PKPass extends Bundle { setLocations(...locations: (Schemas.Location | null)[]): this { if (locations[0] === null) { delete this[propsSymbol]["locations"]; - return; + return this; } this[propsSymbol]["locations"] = Schemas.filterValid( From 389aa96532d3a1b99a95e9406b479df2609671e2 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 19 Sep 2021 22:45:37 +0200 Subject: [PATCH 018/199] Added setNFCCapability implementation along with tests --- spec/PKPass.ts | 32 ++++++++++++++++++++++++++++++++ src/PKPass.ts | 15 ++++++++++----- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/spec/PKPass.ts b/spec/PKPass.ts index f0ed4ff..797b62d 100644 --- a/spec/PKPass.ts +++ b/spec/PKPass.ts @@ -95,4 +95,36 @@ describe("PKPass", () => { ); }); }); + + describe("setNFCCapability", () => { + it("should reset instance.props['nfc'] if 'null' is passed as value", () => { + const pass = new PKPass({}, {}); + + pass.setNFCCapability({ + encryptionPublicKey: "mimmo", + message: "No message for you here", + }); + + expect(pass.props["nfc"]).toEqual({ + encryptionPublicKey: "mimmo", + message: "No message for you here", + }); + + pass.setNFCCapability(null); + + expect(pass.props["nfc"]).toBeUndefined(); + }); + + it("should not accept invalid objects", () => { + const pass = new PKPass({}, {}); + + pass.setNFCCapability({ + // @ts-expect-error + requiresAuth: false, + encryptionPublicKey: "Nope", + }); + + expect(pass.props["nfc"]).toBeUndefined(); + }); + }); }); diff --git a/src/PKPass.ts b/src/PKPass.ts index 276ac56..f1147d4 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -388,16 +388,21 @@ export default class PKPass extends Bundle { * Allows to specify details to make this, an * NFC-capable pass. * + * Pass `null` as parameter to remove it at all. + * * @see https://developer.apple.com/documentation/walletpasses/pass/nfc * @param data * @returns */ - setNFCCapability(data: Schemas.NFC): this { - /** - * @TODO implement - * @TODO specify a way to get current one deleted - */ + setNFCCapability(nfc: Schemas.NFC | null): this { + if (nfc === null) { + delete this[propsSymbol]["nfc"]; + return this; + } + + this[propsSymbol]["nfc"] = + Schemas.getValidated(nfc, Schemas.NFC) ?? undefined; return this; } From 208faee4601f6dab84b13faf56429ef5e953c687 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Tue, 21 Sep 2021 00:02:03 +0200 Subject: [PATCH 019/199] Added setExpirationDate implementation along with tests --- spec/PKPass.ts | 37 +++++++++++++++++++++++++++++++++++++ src/PKPass.ts | 15 +++++++++++---- src/pass.ts | 16 +--------------- src/processDate.ts | 17 +++++++++++++++++ 4 files changed, 66 insertions(+), 19 deletions(-) create mode 100644 src/processDate.ts diff --git a/spec/PKPass.ts b/spec/PKPass.ts index 797b62d..86e8588 100644 --- a/spec/PKPass.ts +++ b/spec/PKPass.ts @@ -126,5 +126,42 @@ describe("PKPass", () => { expect(pass.props["nfc"]).toBeUndefined(); }); + + describe("setExpirationDate", () => { + it("Won't apply changes without a valid argument", () => { + const pass = new PKPass({}, {}); + + // @ts-expect-error + pass.setExpirationDate(); + expect(pass.props["expirationDate"]).toBe(undefined); + + // @ts-expect-error + pass.setExpirationDate(42); + expect(pass.props["expirationDate"]).toBe(undefined); + }); + + it("expects a Date object as the only argument", () => { + const pass = new PKPass({}, {}); + + pass.setExpirationDate(new Date(2020, 6, 1, 0, 0, 0, 0)); + // Month starts from 0 in Date Object when used this way, therefore + // we expect one month more + expect(pass.props["expirationDate"]).toBe( + "2020-07-01T00:00:00Z", + ); + }); + + it("An invalid date, will not apply changes", () => { + const pass = new PKPass({}, {}); + + // @ts-expect-error + pass.setExpirationDate("32/18/228317"); + expect(pass.props["expirationDate"]).toBe(undefined); + + // @ts-expect-error + pass.setExpirationDate("32/18/228317"); + expect(pass.props["expirationDate"]).toBe(undefined); + }); + }); }); }); diff --git a/src/PKPass.ts b/src/PKPass.ts index f1147d4..3581186 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -256,10 +256,17 @@ export default class PKPass extends Bundle { * @returns */ - setExpiration(date: Date | null): this { - /** - * @TODO implement - */ + setExpirationDate(date: Date | null): this { + if (date === null) { + delete this[propsSymbol]["expirationDate"]; + return this; + } + + const parsedDate = processDate("expirationDate", date); + + if (parsedDate) { + this[propsSymbol]["expirationDate"] = parsedDate; + } return this; } diff --git a/src/pass.ts b/src/pass.ts index 43409cd..553631f 100644 --- a/src/pass.ts +++ b/src/pass.ts @@ -16,6 +16,7 @@ import { getAllFilesWithName, } from "./utils"; import * as Signature from "./signature"; +import { processDate } from "./processDate"; const barcodeDebug = debug("passkit:barcode"); const genericDebug = debug("passkit:generic"); @@ -687,18 +688,3 @@ function getValidInArray( Object.keys(current).length && Schemas.isValid(current, schemaName), ); } - -function processDate(key: string, date: Date): string | null { - if (!(date instanceof Date)) { - return null; - } - - const dateParse = dateToW3CString(date); - - if (!dateParse) { - genericDebug(formatMessage(DEBUG.DATE_FORMAT_UNMATCH, key)); - return null; - } - - return dateParse; -} diff --git a/src/processDate.ts b/src/processDate.ts new file mode 100644 index 0000000..6000436 --- /dev/null +++ b/src/processDate.ts @@ -0,0 +1,17 @@ +import { dateToW3CString } from "./utils"; +import formatMessage, { ERROR, DEBUG } from "./messages"; + +export function processDate(key: string, date: Date): string | null { + if (!(date instanceof Date)) { + return null; + } + + const dateParse = dateToW3CString(date); + + if (!dateParse) { + console.warn(formatMessage(DEBUG.DATE_FORMAT_UNMATCH, key)); + return null; + } + + return dateParse; +} From 59d9e693a310e66c1c16b5420a5a3549cc0ff6b8 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Tue, 21 Sep 2021 00:20:21 +0200 Subject: [PATCH 020/199] Added docs references in some comments --- src/PKPass.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/PKPass.ts b/src/PKPass.ts index 3581186..35ac097 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -234,6 +234,7 @@ export default class PKPass extends Bundle { * If the language already exists in the origin source, * translations will be added to the existing ones. * + * @see https://developer.apple.com/documentation/walletpasses/creating_the_source_for_a_pass#3736718 * @param lang * @param translations */ @@ -301,6 +302,7 @@ export default class PKPass extends Bundle { * }); * ``` * + * @see https://developer.apple.com/documentation/walletpasses/pass/beacons * @param beacons * @returns */ @@ -336,6 +338,7 @@ export default class PKPass extends Bundle { * }); * ``` * + * @see https://developer.apple.com/documentation/walletpasses/pass/locations * @param locations * @returns */ @@ -377,6 +380,7 @@ export default class PKPass extends Bundle { * will be shown to the user, without any possibility * to change it. * + * @see https://developer.apple.com/documentation/walletpasses/pass/barcodes * @param barcodes * @returns */ From d3eb66b2c8146e5d2233de9ae2d45edced8dd05d Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Tue, 21 Sep 2021 20:26:03 +0200 Subject: [PATCH 021/199] Added setRelevantDate implementation along tests and added and fixed tests of setExpirationDate --- spec/PKPass.ts | 113 +++++++++++++++++++++++++++++++++++++------------ src/PKPass.ts | 13 ++++-- 2 files changed, 95 insertions(+), 31 deletions(-) diff --git a/spec/PKPass.ts b/spec/PKPass.ts index 86e8588..1135466 100644 --- a/spec/PKPass.ts +++ b/spec/PKPass.ts @@ -126,42 +126,99 @@ describe("PKPass", () => { expect(pass.props["nfc"]).toBeUndefined(); }); + }); - describe("setExpirationDate", () => { - it("Won't apply changes without a valid argument", () => { - const pass = new PKPass({}, {}); + describe("setExpirationDate", () => { + it("should reset instance.props['expirationDate'] if 'null' is passed as value", () => { + const pass = new PKPass({}, {}); - // @ts-expect-error - pass.setExpirationDate(); - expect(pass.props["expirationDate"]).toBe(undefined); + pass.setExpirationDate(new Date(2020, 6, 1, 0, 0, 0, 0)); + // Month starts from 0 in Date Object when used this way, therefore + // we expect one month more + expect(pass.props["expirationDate"]).toBe("2020-07-01T00:00:00Z"); - // @ts-expect-error - pass.setExpirationDate(42); - expect(pass.props["expirationDate"]).toBe(undefined); - }); + pass.setExpirationDate(null); - it("expects a Date object as the only argument", () => { - const pass = new PKPass({}, {}); + expect(pass.props["expirationDate"]).toBeUndefined(); + }); - pass.setExpirationDate(new Date(2020, 6, 1, 0, 0, 0, 0)); - // Month starts from 0 in Date Object when used this way, therefore - // we expect one month more - expect(pass.props["expirationDate"]).toBe( - "2020-07-01T00:00:00Z", - ); - }); + it("Won't apply changes without a valid argument", () => { + const pass = new PKPass({}, {}); - it("An invalid date, will not apply changes", () => { - const pass = new PKPass({}, {}); + // @ts-expect-error + pass.setExpirationDate(); + expect(pass.props["expirationDate"]).toBe(undefined); - // @ts-expect-error - pass.setExpirationDate("32/18/228317"); - expect(pass.props["expirationDate"]).toBe(undefined); + // @ts-expect-error + pass.setExpirationDate(42); + expect(pass.props["expirationDate"]).toBe(undefined); + }); - // @ts-expect-error - pass.setExpirationDate("32/18/228317"); - expect(pass.props["expirationDate"]).toBe(undefined); - }); + it("expects a Date object as the only argument", () => { + const pass = new PKPass({}, {}); + + pass.setExpirationDate(new Date(2020, 6, 1, 0, 0, 0, 0)); + // Month starts from 0 in Date Object when used this way, therefore + // we expect one month more + expect(pass.props["expirationDate"]).toBe("2020-07-01T00:00:00Z"); + }); + + it("An invalid date, will not apply changes", () => { + const pass = new PKPass({}, {}); + + // @ts-expect-error + pass.setExpirationDate("32/18/228317"); + expect(pass.props["expirationDate"]).toBe(undefined); + + // @ts-expect-error + pass.setExpirationDate("32/18/228317"); + expect(pass.props["expirationDate"]).toBe(undefined); + }); + }); + + describe("setRelevantDate", () => { + it("should reset instance.props['relevantDate'] if 'null' is passed as value", () => { + const pass = new PKPass({}, {}); + + pass.setRelevantDate(new Date(2020, 6, 1, 0, 0, 0, 0)); + // Month starts from 0 in Date Object when used this way, therefore + // we expect one month more + expect(pass.props["relevantDate"]).toBe("2020-07-01T00:00:00Z"); + + pass.setRelevantDate(null); + + expect(pass.props["relevantDate"]).toBeUndefined(); + }); + + it("Won't apply changes without a valid argument", () => { + const pass = new PKPass({}, {}); + + // @ts-expect-error + pass.setRelevantDate(); + expect(pass.props["relevantDate"]).toBe(undefined); + + // @ts-expect-error + pass.setRelevantDate(42); + expect(pass.props["relevantDate"]).toBe(undefined); + }); + + it("expects a Date object as the only argument", () => { + const pass = new PKPass({}, {}); + + pass.setRelevantDate(new Date("10-04-2021")); + expect(pass.props["relevantDate"]).toBe("2021-10-04T00:00:00Z"); + }); + + it("An invalid date, will not apply changes", () => { + const pass = new PKPass({}, {}); + + // @ts-expect-error + pass.setRelevantDate("32/18/228317"); + expect(pass.props["relevantDate"]).toBe(undefined); + + // @ts-expect-error + pass.setRelevantDate("32/18/228317"); + expect(pass.props["relevantDate"]).toBe(undefined); }); }); }); diff --git a/src/PKPass.ts b/src/PKPass.ts index 35ac097..6214fc5 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -367,9 +367,16 @@ export default class PKPass extends Bundle { */ setRelevantDate(date: Date): this { - /** - * @TODO implement - */ + if (date === null) { + delete this[propsSymbol]["relevantDate"]; + return this; + } + + const parsedDate = processDate("relevantDate", date); + + if (parsedDate) { + this[propsSymbol]["relevantDate"] = parsedDate; + } return this; } From c49aad4098028a929d2843fb49a2a9a02eca83b7 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Tue, 21 Sep 2021 21:34:28 +0200 Subject: [PATCH 022/199] Added setBarcodes implementation along with tests --- spec/PKPass.ts | 117 +++++++++++++++++++++++++++++++++++++++++++++++++ src/PKPass.ts | 51 ++++++++++++++++++--- 2 files changed, 162 insertions(+), 6 deletions(-) diff --git a/spec/PKPass.ts b/spec/PKPass.ts index 1135466..5c1783b 100644 --- a/spec/PKPass.ts +++ b/spec/PKPass.ts @@ -221,4 +221,121 @@ describe("PKPass", () => { expect(pass.props["relevantDate"]).toBe(undefined); }); }); + + describe("setBarcodes", () => { + it("shouldn't apply changes if no data is passed", () => { + const pass = new PKPass({}, {}); + + const props = pass.props["barcodes"] || []; + const oldAmountOfBarcodes = props?.length ?? 0; + + pass.setBarcodes(); + expect(pass.props["barcodes"]?.length || 0).toBe( + oldAmountOfBarcodes, + ); + }); + + it("should throw error if a boolean parameter is received", () => { + const pass = new PKPass({}, {}); + + // @ts-expect-error + expect(() => pass.setBarcodes(true)).toThrowError( + TypeError, + "Expected Schema.Barcode in setBarcodes but no one is valid.", + ); + }); + + it("should ignore if a number parameter is received", () => { + const pass = new PKPass({}, {}); + + // @ts-expect-error + expect(() => pass.setBarcodes(42)).toThrowError( + TypeError, + "Expected Schema.Barcode in setBarcodes but no one is valid.", + ); + }); + + it("should autogenerate all the barcodes objects if a string is provided as message", () => { + const pass = new PKPass({}, {}); + + pass.setBarcodes("28363516282"); + expect(pass.props["barcodes"].length).toBe(4); + }); + + it("should save changes if object conforming to Schemas.Barcode are provided", () => { + const pass = new PKPass({}, {}); + + pass.setBarcodes({ + message: "28363516282", + format: "PKBarcodeFormatPDF417", + messageEncoding: "utf8", + }); + + expect(pass.props["barcodes"].length).toBe(1); + }); + + it("should add 'messageEncoding' if missing in valid Schema.Barcode parameters", () => { + const pass = new PKPass({}, {}); + + pass.setBarcodes({ + message: "28363516282", + format: "PKBarcodeFormatPDF417", + }); + + expect(pass.props["barcodes"][0].messageEncoding).toBe( + "iso-8859-1", + ); + }); + + it("should ignore objects without 'message' property in Schema.Barcode", () => { + const pass = new PKPass({}, {}); + + pass.setBarcodes( + { + format: "PKBarcodeFormatCode128", + message: "No one can validate meeee", + }, + // @ts-expect-error + { + format: "PKBarcodeFormatPDF417", + }, + ); + + expect(pass.props["barcodes"].length).toBe(1); + }); + + it("should ignore objects and values that not comply with Schema.Barcodes", () => { + const pass = new PKPass({}, {}); + + pass.setBarcodes( + // @ts-expect-error + 5, + 10, + 15, + { + message: "28363516282", + format: "PKBarcodeFormatPDF417", + }, + 7, + 1, + ); + + expect(pass.props["barcodes"].length).toBe(1); + }); + + it("should reset barcodes content if parameter is null", () => { + const pass = new PKPass({}, {}); + + pass.setBarcodes({ + message: "28363516282", + format: "PKBarcodeFormatPDF417", + messageEncoding: "utf8", + }); + + expect(pass.props["barcodes"].length).toBe(1); + + pass.setBarcodes(null); + expect(pass.props["barcodes"]).toBe(undefined); + }); + }); }); diff --git a/src/PKPass.ts b/src/PKPass.ts index 6214fc5..ba1b6a2 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -392,12 +392,51 @@ export default class PKPass extends Bundle { * @returns */ - setBarcodes(...barcodes: Schemas.Barcode[]): this { - /** - * @TODO implement - * @TODO implement data completion - * @TODO specify a way to get current ones deleted - */ + setBarcodes(barcodes: null): this; + setBarcodes(message: string): this; + setBarcodes(...barcodes: Schemas.Barcode[]): this; + setBarcodes(...barcodes: (Schemas.Barcode | string | null)[]): this { + if (!barcodes.length) { + return this; + } + + if (barcodes[0] === null) { + delete this[propsSymbol]["barcodes"]; + return this; + } + + let finalBarcodes: Schemas.Barcode[]; + + if (typeof barcodes[0] === "string") { + /** A string has been received instead of objects. We can only auto-fill them all with the same data. */ + + const supportedFormats: Array = [ + "PKBarcodeFormatQR", + "PKBarcodeFormatPDF417", + "PKBarcodeFormatAztec", + "PKBarcodeFormatCode128", + ]; + + finalBarcodes = supportedFormats.map((format) => + Schemas.getValidated( + { format, message: barcodes[0] } as Schemas.Barcode, + Schemas.Barcode, + ), + ); + } else { + finalBarcodes = Schemas.filterValid( + barcodes as Schemas.Barcode[], + Schemas.Barcode, + ); + + if (!finalBarcodes.length) { + throw new TypeError( + "Expected Schema.Barcode in setBarcodes but no one is valid.", + ); + } + } + + this[propsSymbol]["barcodes"] = finalBarcodes; return this; } From 461caaaed7a82aca4241963aef6dd88d941a9f89 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Tue, 21 Sep 2021 22:24:37 +0200 Subject: [PATCH 023/199] Changed filterValid to use getValidated instead of isValid and to reduce the valid values --- src/schemas/index.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/schemas/index.ts b/src/schemas/index.ts index 88fca9e..ec80339 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -254,5 +254,13 @@ export function filterValid( return []; } - return source.filter((current) => isValid(current, schema)); + return source.reduce((acc, current) => { + const validation = getValidated(current, schema); + + if (!validation) { + return acc; + } + + return [...acc, validation]; + }, []); } From 9d76856d676d56218e6fe87603fb0d30b041d6f8 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Tue, 21 Sep 2021 22:28:57 +0200 Subject: [PATCH 024/199] Added implementation of transitType along with tests to be modified yet and tested when we'll have a good content saving --- spec/PKPass.ts | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/PKPass.ts | 26 ++++++++++++++++--------- 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/spec/PKPass.ts b/spec/PKPass.ts index 5c1783b..0069842 100644 --- a/spec/PKPass.ts +++ b/spec/PKPass.ts @@ -338,4 +338,55 @@ describe("PKPass", () => { expect(pass.props["barcodes"]).toBe(undefined); }); }); + + describe("transitType", () => { + it("should accept a new value only if the pass is a boarding pass", () => { + const mockBPPassJSON = Buffer.from( + JSON.stringify({ + boardingPass: {}, + }), + ); + + const mockCPPassJSON = Buffer.from( + JSON.stringify({ + coupon: {}, + }), + ); + + const passBP = new PKPass( + { + "pass.json": mockBPPassJSON, + }, + {}, + ); + + const passCP = new PKPass( + { + "pass.json": mockCPPassJSON, + }, + {}, + ); + + /** + * @TODO fix this test when props setup + * will be complete + */ + + /* expect(() => { + passBP.transitType = "PKTransitTypeAir"; + }).toThrowError( + TypeError, + "Cannot set transitType on a pass with type different from 'boardingPass'.", + ); */ + // expect(passBP.transitType).toBe("PKTransitTypeAir"); + + /* expect( + () => (passCP.transitType = "PKTransitTypeAir"), + ).toThrowError( + TypeError, + "Cannot set transitType on a pass with type different from 'boardingPass'.", + ); + expect(passCP.transitType).toBeUndefined(); */ + }); + }); }); diff --git a/src/PKPass.ts b/src/PKPass.ts index ba1b6a2..1f8d822 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -141,11 +141,24 @@ export default class PKPass extends Bundle { */ set transitType(value: TransitTypes) { + if (!this[propsSymbol].boardingPass) { + throw new TypeError( + "Cannot set transitType on a pass with type different from 'boardingPass'.", + ); + } + /** - * @TODO implement - * @TODO validate against schema - * @TODO save into props + * @TODO Make getValidated more explicit in case of error. + * @TODO maybe make an automated error. */ + + if (!Schemas.getValidated(value, Schemas.TransitType)) { + throw new TypeError( + `Cannot set transitType to '${value}': invalid type. Expected one of PKTransitTypeAir, PKTransitTypeBoat, PKTransitTypeBus, PKTransitTypeGeneric, PKTransitTypeTrain.`, + ); + } + + this[propsSymbol]["boardingPass"].transitType = value; } /** @@ -154,12 +167,7 @@ export default class PKPass extends Bundle { */ get transitType(): TransitTypes { - /** - * @TODO implement - * @TODO read from props - */ - - return undefined; + return this[propsSymbol]["boardingPass"]?.transitType; } // **************************** // From 84823794d82a3a268b7d900147e9e60d80d2727a Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Wed, 22 Sep 2021 22:58:54 +0200 Subject: [PATCH 025/199] Added partial implementation of addBuffer --- src/PKPass.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/PKPass.ts b/src/PKPass.ts index 1f8d822..946aceb 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -187,6 +187,24 @@ export default class PKPass extends Bundle { * @TODO exclude pass.json, manifest, signature files */ + if (/manifest|signature/.test(pathName)) { + return; + } + + if (/pass\.json/.test(pathName)) { + if (this[filesSymbol]["pass.json"]) { + /** + * Ignoring any further addition. In a + * future we might consider merging instead + */ + return; + } + + /** + * @TODO parse pass.json + */ + } + super.addBuffer(pathName, buffer); } From d4a4c267fba8eb89c2f2eda7007df7f19f9ceb83 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Fri, 24 Sep 2021 23:09:09 +0200 Subject: [PATCH 026/199] Added pass importing method --- src/PKPass.ts | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/PKPass.ts b/src/PKPass.ts index 946aceb..147c685 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -7,6 +7,7 @@ import { Stream } from "stream"; const fieldKeysPoolSymbol = Symbol("fieldKeysPoolSymbol"); const propsSymbol = Symbol("props"); +const importMetadataSymbol = Symbol("import.pass.metadata"); interface NamedBuffers { [key: string]: Buffer; @@ -108,6 +109,26 @@ export default class PKPass extends Bundle { constructor(buffers: NamedBuffers, certificates: Certificates) { super("application/vnd.apple.pkpass"); + for (let [key, value] of Object.entries(buffers)) { + const isManifestOrSignature = /manifest|signature/.test(key); + const isPassJson = /pass\.json/.test(key); + + if (!isManifestOrSignature) { + if (isPassJson) { + this[importMetadataSymbol](readPassMetadata(value)); + + /** + * Adding an empty buffer just for reference + * that we received a valid pass.json file. + * It will be reconciliated in export phase. + */ + + this.addBuffer("pass.json", Buffer.alloc(0)); + } else { + this.addBuffer(key, value); + } + } + } /** * @TODO Validate options against Joi Schema */ @@ -200,6 +221,8 @@ export default class PKPass extends Bundle { return; } + this[importMetadataSymbol](readPassMetadata(buffer)); + /** * @TODO parse pass.json */ @@ -208,6 +231,32 @@ export default class PKPass extends Bundle { super.addBuffer(pathName, buffer); } + private [importMetadataSymbol](data: Schemas.ValidPass) { + const possibleTypes = [ + "boardingPass", + "coupon", + "eventTicket", + "storeCard", + "generic", + ] as string[]; /** @TODO fix this type */ + + const type = possibleTypes.find((type) => Boolean(data[type])); + + if (!type) { + /** + * @TODO improve message + */ + + throw new Error("Cannot find a valid type in this pass.json"); + } + + this.headerFields.push(...data[type]?.headerFields); + this.primaryFields.push(...data[type]?.primaryFields); + this.secondaryFields.push(...data[type]?.secondaryFields); + this.auxiliaryFields.push(...data[type]?.auxiliaryFields); + this.backFields.push(...data[type]?.backFields); + } + // ************************* // // *** EXPORTING METHODS *** // // ************************* // @@ -515,3 +564,30 @@ function freezeRecusive(object: Object) { return Object.freeze(objectCopy); } + +function readPassMetadata(buffer: Buffer) { + try { + const contentAsJSON = JSON.parse( + buffer.toString("utf8"), + ) as Schemas.ValidPass; + + const validation = Schemas.getValidated( + contentAsJSON, + Schemas.ValidPass, + ); + + /** + * @TODO validation.error? + */ + + if (!validation) { + throw new Error( + "Cannot validate pass.json file. Not conformant to", + ); + } + + return validation; + } catch (err) { + console.error(err); + } +} From e326a25b4fcef9246a34773fad6b8d28e42e7abe Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 25 Sep 2021 17:30:08 +0200 Subject: [PATCH 027/199] Added parsing of pass.strings files when using addBuffer --- src/PKPass.ts | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index 147c685..a4d2dd4 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -8,6 +8,7 @@ import { Stream } from "stream"; const fieldKeysPoolSymbol = Symbol("fieldKeysPoolSymbol"); const propsSymbol = Symbol("props"); const importMetadataSymbol = Symbol("import.pass.metadata"); +const localizationSymbol = Symbol("pass.l10n"); interface NamedBuffers { [key: string]: Buffer; @@ -20,10 +21,17 @@ type TransitTypes = `PKTransitType${ | "Generic" | "Train"}`; +const LOCALIZED_FILE_REGEX_BASE = "(?[a-zA-Z-]{2,}).lproj/"; + export default class PKPass extends Bundle { private certificates: Certificates; private [fieldKeysPoolSymbol] = new Set(); private [propsSymbol]: Schemas.ValidPass = {}; + private [localizationSymbol]: { + [lang: string]: { + [placeholder: string]: string; + }; + } = {}; public primaryFields /*****/ = new FieldsArray(this[fieldKeysPoolSymbol]); public secondaryFields /***/ = new FieldsArray(this[fieldKeysPoolSymbol]); public auxiliaryFields /***/ = new FieldsArray(this[fieldKeysPoolSymbol]); @@ -228,7 +236,30 @@ export default class PKPass extends Bundle { */ } - super.addBuffer(pathName, buffer); + /** + * If a new pass.strings file is added, we want to + * prevent if from being merged and, instead, save + * its translations for later + */ + + const translationsFileRegexp = new RegExp( + `${LOCALIZED_FILE_REGEX_BASE}pass.strings`, + ); + + let match: RegExpMatchArray; + + if ((match = pathName.match(translationsFileRegexp))) { + const [, lang] = match; + + Object.assign( + (this[localizationSymbol][lang] ??= {}), + parseStringsFile(buffer), + ); + + return; + } + + return super.addBuffer(pathName, buffer); } private [importMetadataSymbol](data: Schemas.ValidPass) { @@ -591,3 +622,62 @@ function readPassMetadata(buffer: Buffer) { console.error(err); } } + +function parseStringsFile(buffer: Buffer) { + const fileAsString = buffer.toString("utf8"); + const translationRowRegex = /"(?.+)"\s+=\s+"(?.+)";\n?/; + const commentRowRegex = /\/\*\s*(.+)\s*\*\//; + + /** + * Regole di parsing: + * 1) Uso un solo ciclo + * 2) Accumulo il cursore finché non trovo "\n" oppure il contenuto non è finito + * 3) Quando questo succede, parso il blocco. Ho due blocchi possibili: commento e riga + * + */ + + let translations: [placeholder: string, value: string][] = []; + let comments: string[] = []; + + let blockStartPoint = 0; + let blockEndPoint = 0; + + do { + if ( + /** New Line, new life */ + /\n/.test(fileAsString[blockEndPoint]) || + /** EOF */ + blockEndPoint === fileAsString.length + ) { + let match: RegExpMatchArray; + + const section = fileAsString.substring( + blockStartPoint, + blockEndPoint + 1, + ); + + if ((match = section.match(translationRowRegex))) { + const { + groups: { key, value }, + } = match; + + translations.push([key, value]); + } else if ((match = section.match(commentRowRegex))) { + const [, content] = match; + + comments.push(content.trimEnd()); + } + + /** Skipping \n and going to the next block. */ + blockEndPoint += 2; + blockStartPoint = blockEndPoint - 1; + } else { + blockEndPoint += 1; + } + } while (blockEndPoint <= fileAsString.length); + + return { + translations, + comments, + }; +} From 2c3ca1076c0bc38ac52e0cffba293e9dcaaf6691 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 25 Sep 2021 17:31:31 +0200 Subject: [PATCH 028/199] Unified file import strategy flow in constructor with the one in addBuffer --- src/PKPass.ts | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index a4d2dd4..ded2398 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -118,24 +118,7 @@ export default class PKPass extends Bundle { super("application/vnd.apple.pkpass"); for (let [key, value] of Object.entries(buffers)) { - const isManifestOrSignature = /manifest|signature/.test(key); - const isPassJson = /pass\.json/.test(key); - - if (!isManifestOrSignature) { - if (isPassJson) { - this[importMetadataSymbol](readPassMetadata(value)); - - /** - * Adding an empty buffer just for reference - * that we received a valid pass.json file. - * It will be reconciliated in export phase. - */ - - this.addBuffer("pass.json", Buffer.alloc(0)); - } else { - this.addBuffer(key, value); - } - } + this.addBuffer(key, value); } /** * @TODO Validate options against Joi Schema @@ -232,8 +215,12 @@ export default class PKPass extends Bundle { this[importMetadataSymbol](readPassMetadata(buffer)); /** - * @TODO parse pass.json + * Adding an empty buffer just for reference + * that we received a valid pass.json file. + * It will be reconciliated in export phase. */ + + return super.addBuffer(pathName, Buffer.alloc(0)); } /** From 3ad4c420419ce72598486bd7bca8c468d5a2f318 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 25 Sep 2021 17:42:49 +0200 Subject: [PATCH 029/199] Removed duplicated addBuffer call --- src/PKPass.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index ded2398..fc73e34 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -117,9 +117,6 @@ export default class PKPass extends Bundle { constructor(buffers: NamedBuffers, certificates: Certificates) { super("application/vnd.apple.pkpass"); - for (let [key, value] of Object.entries(buffers)) { - this.addBuffer(key, value); - } /** * @TODO Validate options against Joi Schema */ From fb0fac0cab8ef30c6e43783553937b059070353a Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 25 Sep 2021 18:12:41 +0200 Subject: [PATCH 030/199] Moved fields to be have only a getter and a place in props --- src/PKPass.ts | 68 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index fc73e34..75aef5e 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -26,17 +26,19 @@ const LOCALIZED_FILE_REGEX_BASE = "(?[a-zA-Z-]{2,}).lproj/"; export default class PKPass extends Bundle { private certificates: Certificates; private [fieldKeysPoolSymbol] = new Set(); - private [propsSymbol]: Schemas.ValidPass = {}; + private [propsSymbol]: Schemas.ValidPass = { + primaryFields /*****/: new FieldsArray(this[fieldKeysPoolSymbol]), + secondaryFields /***/: new FieldsArray(this[fieldKeysPoolSymbol]), + auxiliaryFields /***/: new FieldsArray(this[fieldKeysPoolSymbol]), + headerFields /******/: new FieldsArray(this[fieldKeysPoolSymbol]), + backFields /********/: new FieldsArray(this[fieldKeysPoolSymbol]), + }; private [localizationSymbol]: { [lang: string]: { [placeholder: string]: string; }; } = {}; - public primaryFields /*****/ = new FieldsArray(this[fieldKeysPoolSymbol]); - public secondaryFields /***/ = new FieldsArray(this[fieldKeysPoolSymbol]); - public auxiliaryFields /***/ = new FieldsArray(this[fieldKeysPoolSymbol]); - public headerFields /******/ = new FieldsArray(this[fieldKeysPoolSymbol]); - public backFields /********/ = new FieldsArray(this[fieldKeysPoolSymbol]); + public type: string = undefined; /** @TODO change type */ /** * Either create a pass from another one @@ -179,6 +181,46 @@ export default class PKPass extends Bundle { return this[propsSymbol]["boardingPass"]?.transitType; } + /** + * Allows accessing to primaryFields object + */ + + get primaryFields(): Schemas.Field[] { + return this[propsSymbol][this.type].primaryFields; + } + + /** + * Allows accessing to secondaryFields object + */ + + get secondaryFields(): Schemas.Field[] { + return this[propsSymbol][this.type].secondaryFields; + } + + /** + * Allows accessing to auxiliaryFields object + */ + + get auxiliaryFields(): Schemas.Field[] { + return this[propsSymbol][this.type].auxiliaryFields; + } + + /** + * Allows accessing to headerFields object + */ + + get headerFields(): Schemas.Field[] { + return this[propsSymbol][this.type].headerFields; + } + + /** + * Allows accessing to backFields object + */ + + get backFields(): Schemas.Field[] { + return this[propsSymbol][this.type].backFields; + } + // **************************** // // *** ASSETS SETUP METHODS *** // // **************************** // @@ -255,9 +297,9 @@ export default class PKPass extends Bundle { "generic", ] as string[]; /** @TODO fix this type */ - const type = possibleTypes.find((type) => Boolean(data[type])); + this.type = possibleTypes.find((type) => Boolean(data[type])); - if (!type) { + if (!this.type) { /** * @TODO improve message */ @@ -265,11 +307,11 @@ export default class PKPass extends Bundle { throw new Error("Cannot find a valid type in this pass.json"); } - this.headerFields.push(...data[type]?.headerFields); - this.primaryFields.push(...data[type]?.primaryFields); - this.secondaryFields.push(...data[type]?.secondaryFields); - this.auxiliaryFields.push(...data[type]?.auxiliaryFields); - this.backFields.push(...data[type]?.backFields); + this.headerFields.push(...data[this.type]?.headerFields); + this.primaryFields.push(...data[this.type]?.primaryFields); + this.secondaryFields.push(...data[this.type]?.secondaryFields); + this.auxiliaryFields.push(...data[this.type]?.auxiliaryFields); + this.backFields.push(...data[this.type]?.backFields); } // ************************* // From 690e2d46896dd25ee11a45124f880d6705a38422 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 25 Sep 2021 20:01:12 +0200 Subject: [PATCH 031/199] Changed again fields as they should not be directly under props, but under its type --- src/PKPass.ts | 60 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index 75aef5e..87ef037 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -26,13 +26,7 @@ const LOCALIZED_FILE_REGEX_BASE = "(?[a-zA-Z-]{2,}).lproj/"; export default class PKPass extends Bundle { private certificates: Certificates; private [fieldKeysPoolSymbol] = new Set(); - private [propsSymbol]: Schemas.ValidPass = { - primaryFields /*****/: new FieldsArray(this[fieldKeysPoolSymbol]), - secondaryFields /***/: new FieldsArray(this[fieldKeysPoolSymbol]), - auxiliaryFields /***/: new FieldsArray(this[fieldKeysPoolSymbol]), - headerFields /******/: new FieldsArray(this[fieldKeysPoolSymbol]), - backFields /********/: new FieldsArray(this[fieldKeysPoolSymbol]), - }; + private [propsSymbol]: Schemas.ValidPass = {}; private [localizationSymbol]: { [lang: string]: { [placeholder: string]: string; @@ -146,7 +140,8 @@ export default class PKPass extends Bundle { /** * Allows setting a transitType property - * for a boardingPass + * for a boardingPass. Throws an error if + * the current type is not a boardingPass. * * @param value */ @@ -182,7 +177,11 @@ export default class PKPass extends Bundle { } /** - * Allows accessing to primaryFields object + * Allows accessing to primaryFields object. + * + * It will (automatically) throw an error if + * no valid pass.json has been parsed yet or, + * anyway, if it has not a valid type. */ get primaryFields(): Schemas.Field[] { @@ -191,6 +190,10 @@ export default class PKPass extends Bundle { /** * Allows accessing to secondaryFields object + * + * It will (automatically) throw an error if + * no valid pass.json has been parsed yet or, + * anyway, if it has not a valid type. */ get secondaryFields(): Schemas.Field[] { @@ -199,6 +202,10 @@ export default class PKPass extends Bundle { /** * Allows accessing to auxiliaryFields object + * + * It will (automatically) throw an error if + * no valid pass.json has been parsed yet or, + * anyway, if it has not a valid type. */ get auxiliaryFields(): Schemas.Field[] { @@ -207,6 +214,10 @@ export default class PKPass extends Bundle { /** * Allows accessing to headerFields object + * + * It will (automatically) throw an error if + * no valid pass.json has been parsed yet or, + * anyway, if it has not a valid type. */ get headerFields(): Schemas.Field[] { @@ -215,6 +226,10 @@ export default class PKPass extends Bundle { /** * Allows accessing to backFields object + * + * It will (automatically) throw an error if + * no valid pass.json has been parsed yet or, + * anyway, if it has not a valid type. */ get backFields(): Schemas.Field[] { @@ -307,11 +322,28 @@ export default class PKPass extends Bundle { throw new Error("Cannot find a valid type in this pass.json"); } - this.headerFields.push(...data[this.type]?.headerFields); - this.primaryFields.push(...data[this.type]?.primaryFields); - this.secondaryFields.push(...data[this.type]?.secondaryFields); - this.auxiliaryFields.push(...data[this.type]?.auxiliaryFields); - this.backFields.push(...data[this.type]?.backFields); + this[propsSymbol][this.type] = { + primaryFields /*****/: new FieldsArray( + this[fieldKeysPoolSymbol], + ...data[this.type]?.primaryFields, + ), + secondaryFields /***/: new FieldsArray( + this[fieldKeysPoolSymbol], + ...data[this.type]?.secondaryFields, + ), + auxiliaryFields /***/: new FieldsArray( + this[fieldKeysPoolSymbol], + ...data[this.type]?.auxiliaryFields, + ), + headerFields /******/: new FieldsArray( + this[fieldKeysPoolSymbol], + ...data[this.type]?.headerFields, + ), + backFields /********/: new FieldsArray( + this[fieldKeysPoolSymbol], + ...data[this.type]?.backFields, + ), + }; } // ************************* // From c0dfdf1d3d97fce2ea936fc2fdfa5ba61afa8ef7 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 25 Sep 2021 20:18:41 +0200 Subject: [PATCH 032/199] Removed forgotten design comment --- src/PKPass.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index 87ef037..c9bcfd0 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -686,14 +686,6 @@ function parseStringsFile(buffer: Buffer) { const translationRowRegex = /"(?.+)"\s+=\s+"(?.+)";\n?/; const commentRowRegex = /\/\*\s*(.+)\s*\*\//; - /** - * Regole di parsing: - * 1) Uso un solo ciclo - * 2) Accumulo il cursore finché non trovo "\n" oppure il contenuto non è finito - * 3) Quando questo succede, parso il blocco. Ho due blocchi possibili: commento e riga - * - */ - let translations: [placeholder: string, value: string][] = []; let comments: string[] = []; From 7a23cba582dd630a429bdf267fc1457d442d854e Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 25 Sep 2021 20:21:22 +0200 Subject: [PATCH 033/199] Added accessors on getters and setters --- src/PKPass.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index c9bcfd0..b775645 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -134,7 +134,7 @@ export default class PKPass extends Bundle { * that are composing your pass instance. */ - get props(): Readonly { + public get props(): Readonly { return freezeRecusive(this[propsSymbol]); } @@ -146,7 +146,7 @@ export default class PKPass extends Bundle { * @param value */ - set transitType(value: TransitTypes) { + public set transitType(value: TransitTypes) { if (!this[propsSymbol].boardingPass) { throw new TypeError( "Cannot set transitType on a pass with type different from 'boardingPass'.", @@ -172,7 +172,7 @@ export default class PKPass extends Bundle { * from pass props */ - get transitType(): TransitTypes { + public get transitType(): TransitTypes { return this[propsSymbol]["boardingPass"]?.transitType; } @@ -184,7 +184,7 @@ export default class PKPass extends Bundle { * anyway, if it has not a valid type. */ - get primaryFields(): Schemas.Field[] { + public get primaryFields(): Schemas.Field[] { return this[propsSymbol][this.type].primaryFields; } @@ -196,7 +196,7 @@ export default class PKPass extends Bundle { * anyway, if it has not a valid type. */ - get secondaryFields(): Schemas.Field[] { + public get secondaryFields(): Schemas.Field[] { return this[propsSymbol][this.type].secondaryFields; } @@ -208,7 +208,7 @@ export default class PKPass extends Bundle { * anyway, if it has not a valid type. */ - get auxiliaryFields(): Schemas.Field[] { + public get auxiliaryFields(): Schemas.Field[] { return this[propsSymbol][this.type].auxiliaryFields; } @@ -220,7 +220,7 @@ export default class PKPass extends Bundle { * anyway, if it has not a valid type. */ - get headerFields(): Schemas.Field[] { + public get headerFields(): Schemas.Field[] { return this[propsSymbol][this.type].headerFields; } @@ -232,7 +232,7 @@ export default class PKPass extends Bundle { * anyway, if it has not a valid type. */ - get backFields(): Schemas.Field[] { + public get backFields(): Schemas.Field[] { return this[propsSymbol][this.type].backFields; } From 344c33c54ca100af4bebfc9f123b0032ede813ed Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 25 Sep 2021 21:41:06 +0200 Subject: [PATCH 034/199] Added localize implementation along with its tests --- spec/PKPass.ts | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- src/PKPass.ts | 39 ++++++++++++++++++++++++++------------- 2 files changed, 75 insertions(+), 14 deletions(-) diff --git a/spec/PKPass.ts b/spec/PKPass.ts index 0069842..fc813fa 100644 --- a/spec/PKPass.ts +++ b/spec/PKPass.ts @@ -1,4 +1,8 @@ -import { default as PKPass } from "../lib/PKPass"; +import { + default as PKPass, + localizationSymbol, + propsSymbol, +} from "../lib/PKPass"; describe("PKPass", () => { describe("setBeacons", () => { @@ -389,4 +393,48 @@ describe("PKPass", () => { expect(passCP.transitType).toBeUndefined(); */ }); }); + + describe("localize", () => { + const pass = new PKPass({}, {}, {}); + + it("should create a new language record inside class props", () => { + pass.localize("en"); + + expect(pass[localizationSymbol]["en"]).toEqual({}); + }); + + it("should save some translations to be exported later", () => { + pass.localize("it", { + say_hi: "ciao", + say_gb: "arrivederci", + }); + + expect(pass[localizationSymbol]["it"]).toEqual({ + say_hi: "ciao", + say_gb: "arrivederci", + }); + }); + + it("should accept later translations and merge them with existing ones", () => { + pass.localize("it", { + say_good_morning: "buongiorno", + say_good_evening: "buonasera", + }); + + expect(pass[localizationSymbol]["it"]).toEqual({ + say_hi: "ciao", + say_gb: "arrivederci", + say_good_morning: "buongiorno", + say_good_evening: "buonasera", + }); + }); + + it("should delete a language and its all translations when null is passed as parameter", () => { + pass.localize("it", null); + pass.localize("en", null); + + expect(pass[localizationSymbol]["it"]).toBeUndefined(); + expect(pass[localizationSymbol]["en"]).toBeUndefined(); + }); + }); }); diff --git a/src/PKPass.ts b/src/PKPass.ts index b775645..6bbb56e 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -5,10 +5,12 @@ import { getModelFolderContents } from "./parser"; import * as Schemas from "./schemas"; import { Stream } from "stream"; +/** Exporting for tests specs */ +export const propsSymbol = Symbol("props"); +export const localizationSymbol = Symbol("pass.l10n"); + const fieldKeysPoolSymbol = Symbol("fieldKeysPoolSymbol"); -const propsSymbol = Symbol("props"); const importMetadataSymbol = Symbol("import.pass.metadata"); -const localizationSymbol = Symbol("pass.l10n"); interface NamedBuffers { [key: string]: Buffer; @@ -363,6 +365,7 @@ export default class PKPass extends Bundle { * @TODO compile this pass into something usable * @TODO like _patch on old version * @TODO share implementation with getAsStream + * @TODO Build back translations */ return super.getAsBuffer(); @@ -381,6 +384,7 @@ export default class PKPass extends Bundle { * @TODO compile this pass into something usable * @TODO like _patch on old version * @TODO share implementation with getAsBuffer + * @TODO Build back translations */ return super.getAsStream(); @@ -392,24 +396,33 @@ export default class PKPass extends Bundle { /** * Allows to specify a language to be added to the - * final bundle, along with some optionals / additional - * translations. + * final bundle, along with some optionals translations. * - * If the language already exists in the origin source, - * translations will be added to the existing ones. + * If the language already exists, translations will be + * merged with the existing ones. + * + * Setting `translations` to `null`, fully deletes a language + * and its translations. * * @see https://developer.apple.com/documentation/walletpasses/creating_the_source_for_a_pass#3736718 * @param lang * @param translations */ - localize(lang: string, translations?: any): this { - /** - * @TODO change translations format - * @TODO specify a way to get current ones deleted - * @TODO Default languages from source - * @TODO print warning if lang is already in selection? - */ + localize( + lang: string, + translations?: { [key: string]: string } | null, + ): this { + if (translations === null) { + delete this[localizationSymbol][lang]; + return this; + } + + this[localizationSymbol][lang] ??= {}; + + if (typeof translations === "object" && !Array.isArray(translations)) { + Object.assign(this[localizationSymbol][lang], translations); + } return this; } From 662b43729565047a1b8b206b1a743d796e84fad3 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 25 Sep 2021 21:53:00 +0200 Subject: [PATCH 035/199] Changed from buffers clone strategy --- src/PKPass.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index 6bbb56e..273edce 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -59,7 +59,8 @@ export default class PKPass extends Bundle { for (let i = 0; i < buffersEntries.length; i++) { const [fileName, contentBuffer] = buffersEntries[i]; - buffers[fileName] = Buffer.from(contentBuffer); + buffers[fileName] = Buffer.alloc(contentBuffer.length); + contentBuffer.copy(buffers[fileName]); } } else { /** Disk model reading is happening here */ From 3cbb1b46b893bba5812a06f4470161281033969e Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 25 Sep 2021 23:27:25 +0200 Subject: [PATCH 036/199] Edited getModelFolderContents to return just one object of buffers (to be tested yet) --- src/parser.ts | 166 ++++++++++++++++++++++++++------------------------ 1 file changed, 85 insertions(+), 81 deletions(-) diff --git a/src/parser.ts b/src/parser.ts index 05d2ee2..fb60c34 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -9,11 +9,10 @@ import { hasFilesWithName, deletePersonalization, } from "./utils"; -import fs from "fs"; +import { promises as fs } from "fs"; import debug from "debug"; const prsDebug = debug("Personalization"); -const { readdir: readDir, readFile } = fs.promises; /** * Performs checks on the passed model to @@ -93,26 +92,28 @@ export async function getModelContents(model: Schemas.FactoryOptions["model"]) { } /** - * Reads and model contents and creates a splitted - * bundles-object. + * Reads the model folder contents + * * @param model + * @returns A promise of an object containing all + * filePaths and the relative buffer */ export async function getModelFolderContents( model: string, -): Promise { +): Promise<{ [filePath: string]: Buffer }> { try { const modelPath = `${model}${(!path.extname(model) && ".pass") || ""}`; - const modelFilesList = await readDir(modelPath); + const modelFilesList = await fs.readdir(modelPath); // No dot-starting files, manifest and signature - const filteredFiles = removeHidden(modelFilesList).filter( + const filteredModelRecords = removeHidden(modelFilesList).filter( (f) => !/(manifest|signature)/i.test(f) && /.+$/.test(path.parse(f).ext), ); - const isModelInitialized = + /* const isModelInitialized = filteredFiles.length && hasFilesWithName("icon", filteredFiles, "startsWith"); @@ -124,85 +125,32 @@ export async function getModelFolderContents( path.parse(model).name, ), ); - } + } */ - // Splitting files from localization folders - const rawBundleFiles = filteredFiles.filter( - (entry) => !entry.includes(".lproj"), - ); - const l10nFolders = filteredFiles.filter((entry) => - entry.includes(".lproj"), - ); - - const rawBundleBuffers = await Promise.all( - rawBundleFiles.map((file) => - readFile(path.resolve(modelPath, file)), - ), - ); - - const bundle: Schemas.BundleUnit = Object.assign( - {}, - ...rawBundleFiles.map((fileName, index) => ({ - [fileName]: rawBundleBuffers[index], - })), - ); - - // Reading concurrently localizations folder - // and their files and their buffers - const L10N_FilesListByFolder: Array = + const modelRecords = ( await Promise.all( - l10nFolders.map(async (folderPath) => { - // Reading current folder - const currentLangPath = path.join(modelPath, folderPath); + /** + * Obtaining flattened array of buffer records + * containing file name and the buffer itself. + * + * This goes also to read every nested l10n + * subfolder. + */ - const files = await readDir(currentLangPath); - // Transforming files path to a model-relative path - const validFiles = removeHidden(files).map((file) => - path.join(currentLangPath, file), + filteredModelRecords.map((fileOrDirectoryPath) => { + const fullPath = path.resolve( + modelPath, + fileOrDirectoryPath, ); - // Getting all the buffers from file paths - const buffers = await Promise.all( - validFiles.map((file) => - readFile(file).catch(() => Buffer.alloc(0)), - ), - ); - - // Assigning each file path to its buffer - // and discarding the empty ones - - return validFiles.reduce( - (acc, file, index) => { - if (!buffers[index].length) { - return acc; - } - - const fileComponents = file.split(path.sep); - const fileName = - fileComponents[fileComponents.length - 1]; - - return { - ...acc, - [fileName]: buffers[index], - }; - }, - {}, - ); + return readFileOrDirectory(fullPath); }), - ); + ) + ) + .flat(1) + .reduce((acc, current) => ({ ...acc, ...current }), {}); - const l10nBundle: Schemas.PartitionedBundle["l10nBundle"] = - Object.assign( - {}, - ...L10N_FilesListByFolder.map((folder, index) => ({ - [l10nFolders[index]]: folder, - })), - ); - - return { - bundle, - l10nBundle, - }; + return modelRecords; } catch (err) { if (err?.code === "ENOENT") { if (err.syscall === "open") { @@ -226,6 +174,62 @@ export async function getModelFolderContents( } } +/** + * Reads sequentially + * @param filePath + * @returns + */ + +async function readFileOrDirectory(filePath: string) { + if ((await fs.lstat(filePath)).isDirectory()) { + return Promise.all(await readDirectory(filePath)); + } else { + return fs + .readFile(filePath) + .then((content) => getObjectFromModelFile(filePath, content, 1)); + } +} + +/** + * Returns an object containing the parsed fileName + * from a path along with its content. + * + * @param filePath + * @param content + * @param depthFromEnd - used to preserve localization lproj content + * @returns + */ + +function getObjectFromModelFile( + filePath: string, + content: Buffer, + depthFromEnd: number, +) { + const fileComponents = filePath.split(path.sep); + const fileName = fileComponents + .slice(fileComponents.length - depthFromEnd) + .join(path.sep); + + return { [fileName]: content }; +} + +/** + * Reads a directory and returns all the files in it + * as an Array + * + * @param filePath + * @returns + */ + +async function readDirectory(filePath: string) { + const dirContent = await fs.readdir(filePath).then(removeHidden); + + return dirContent.map(async (fileName) => { + const content = await fs.readFile(path.resolve(filePath, fileName)); + return getObjectFromModelFile(filePath, content, 1); + }); +} + /** * Analyzes the passed buffer model and splits it to * return buffers and localization files buffers. @@ -310,7 +314,7 @@ export async function readCertificatesFromOptions( if (!!path.parse(content).ext) { // The content is a path to the document - return readFile(path.resolve(content), { encoding: "utf8" }); + return fs.readFile(path.resolve(content), { encoding: "utf8" }); } else { // Content is the real document content return Promise.resolve(content); From b1f4739db905f47acb4064cc46d41d801d1255c3 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sat, 25 Sep 2021 23:41:33 +0200 Subject: [PATCH 037/199] Improved PKPass.from --- src/PKPass.ts | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index 273edce..f9619bc 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -1,5 +1,4 @@ import FieldsArray from "./fieldsArray"; -import { Certificates } from "./schemas"; import { default as Bundle, filesSymbol } from "./Bundle"; import { getModelFolderContents } from "./parser"; import * as Schemas from "./schemas"; @@ -16,6 +15,14 @@ interface NamedBuffers { [key: string]: Buffer; } +namespace PKPass { + export interface Template { + model: string; + certificates: Schemas.Certificates; + overrides?: Schemas.OverridesSupportedOptions; + } +} + type TransitTypes = `PKTransitType${ | "Air" | "Boat" @@ -26,7 +33,7 @@ type TransitTypes = `PKTransitType${ const LOCALIZED_FILE_REGEX_BASE = "(?[a-zA-Z-]{2,}).lproj/"; export default class PKPass extends Bundle { - private certificates: Certificates; + private certificates: Schemas.Certificates; private [fieldKeysPoolSymbol] = new Set(); private [propsSymbol]: Schemas.ValidPass = {}; private [localizationSymbol]: { @@ -44,10 +51,16 @@ export default class PKPass extends Bundle { * @returns */ - static async from(source: PKPass | string): Promise { - let certificates: Certificates = undefined; + static async from(source: PKPass | PKPass.Template): Promise { + let certificates: Schemas.Certificates = undefined; let buffers: NamedBuffers = undefined; + if (!source) { + throw new TypeError( + `Cannot create PKPass from source: source is '${source}'`, + ); + } + if (source instanceof PKPass) { /** Cloning is happening here */ certificates = source.certificates; @@ -63,6 +76,12 @@ export default class PKPass extends Bundle { contentBuffer.copy(buffers[fileName]); } } else { + if (!source.model || typeof source.model !== "string") { + throw new TypeError( + "Cannot create PKPass from source: unknown model but expected a string.", + ); + } + /** Disk model reading is happening here */ /** @@ -70,12 +89,10 @@ export default class PKPass extends Bundle { * @TODO determine how to use localized files */ - const { bundle, l10nBundle } = await getModelFolderContents(source); - - buffers = bundle; + buffers = await getModelFolderContents(source.model); } - return new PKPass(buffers, certificates); + return new PKPass(buffers, certificates, {}); } /** From 039b6362620d006a55b11f4708b0794c9b118a28 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 26 Sep 2021 14:53:15 +0200 Subject: [PATCH 038/199] Added manifest creation, signature creation, pass.strings creation and pass-close methods --- src/PKPass.ts | 69 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index f9619bc..60d2450 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -366,6 +366,53 @@ export default class PKPass extends Bundle { }; } + private [createManifestSymbol]() { + return Object.entries(this[filesSymbol]).reduce( + (acc, [fileName, buffer]) => { + const hashFlow = forge.md.sha1.create(); + + hashFlow.update(buffer.toString("binary")); + + return { + ...acc, + [fileName]: hashFlow.digest().toHex(), + }; + }, + {}, + ); + } + + private [closePassSymbol]() { + const passJson = Buffer.from(JSON.stringify(this[propsSymbol])); + super.addBuffer("pass.json", passJson); + + const localizationEntries = Object.entries(this[localizationSymbol]); + + for ( + let i = localizationEntries.length, + entry: [string, { [key: string]: string }]; + (entry = localizationEntries[--i]); + + ) { + const [lang, translations] = entry; + + super.addBuffer( + `${lang}.lproj/pass.strings`, + createStringFile(translations), + ); + } + + /** + * @TODO pack out fields from FieldsArray + */ + + const manifest = this[createManifestSymbol](); + super.addBuffer("manifest.json", Buffer.from(JSON.stringify(manifest))); + + const signatureBuffer = Signature.create(manifest, this.certificates); + super.addBuffer("signature", signatureBuffer); + } + // ************************* // // *** EXPORTING METHODS *** // // ************************* // @@ -383,9 +430,11 @@ export default class PKPass extends Bundle { * @TODO compile this pass into something usable * @TODO like _patch on old version * @TODO share implementation with getAsStream - * @TODO Build back translations + * @TODO warning if no icon files */ + this[closePassSymbol](); + return super.getAsBuffer(); } @@ -402,9 +451,11 @@ export default class PKPass extends Bundle { * @TODO compile this pass into something usable * @TODO like _patch on old version * @TODO share implementation with getAsBuffer - * @TODO Build back translations + * @TODO warning if no icon files */ + this[closePassSymbol](); + return super.getAsStream(); } @@ -762,3 +813,17 @@ function parseStringsFile(buffer: Buffer) { comments, }; } + +function createStringFile(translations: { [key: string]: string }): Buffer { + const stringContents = []; + + const translationsEntries = Object.entries(translations); + + for (let i = 0; i < translationsEntries.length; i++) { + const [key, value] = translationsEntries[i]; + + stringContents.push(`"${key}" = "${value}";`); + } + + return Buffer.from(stringContents.join("\n")); +} From 58bb4bdd9ae7dae2b099627276d0ac52c1c0410c Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 26 Sep 2021 16:07:04 +0200 Subject: [PATCH 039/199] Made string file to be added only if stringsFile has a length --- src/PKPass.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index 60d2450..a27827c 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -396,10 +396,11 @@ export default class PKPass extends Bundle { ) { const [lang, translations] = entry; - super.addBuffer( - `${lang}.lproj/pass.strings`, - createStringFile(translations), - ); + const stringsFile = createStringFile(translations); + + if (stringsFile.length) { + super.addBuffer(`${lang}.lproj/pass.strings`, stringsFile); + } } /** From 4fe4f5e487b582dfa67d6700882cbc85d84eefd1 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 26 Sep 2021 16:17:50 +0200 Subject: [PATCH 040/199] Added forgotten imports and symbols, and replaced \n as separator for stringFile with EOL --- src/PKPass.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index a27827c..5588edc 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -3,6 +3,10 @@ import { default as Bundle, filesSymbol } from "./Bundle"; import { getModelFolderContents } from "./parser"; import * as Schemas from "./schemas"; import { Stream } from "stream"; +import { processDate } from "./processDate"; +import forge from "node-forge"; +import * as Signature from "./signature"; +import { EOL } from "os"; /** Exporting for tests specs */ export const propsSymbol = Symbol("props"); @@ -10,6 +14,8 @@ export const localizationSymbol = Symbol("pass.l10n"); const fieldKeysPoolSymbol = Symbol("fieldKeysPoolSymbol"); const importMetadataSymbol = Symbol("import.pass.metadata"); +const createManifestSymbol = Symbol("pass.manifest"); +const closePassSymbol = Symbol("pass.close"); interface NamedBuffers { [key: string]: Buffer; @@ -826,5 +832,5 @@ function createStringFile(translations: { [key: string]: string }): Buffer { stringContents.push(`"${key}" = "${value}";`); } - return Buffer.from(stringContents.join("\n")); + return Buffer.from(stringContents.join(EOL)); } From d41a592a691f596e145e98b0463335216ef55f15 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 26 Sep 2021 16:39:00 +0200 Subject: [PATCH 041/199] Added color parsing in closePassSymbol method --- src/PKPass.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/PKPass.ts b/src/PKPass.ts index 5588edc..72389c3 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -7,6 +7,7 @@ import { processDate } from "./processDate"; import forge from "node-forge"; import * as Signature from "./signature"; import { EOL } from "os"; +import { isValidRGB } from "./utils"; /** Exporting for tests specs */ export const propsSymbol = Symbol("props"); @@ -389,6 +390,30 @@ export default class PKPass extends Bundle { } private [closePassSymbol]() { + /** + * Filtering colors props that have an + * invalid RGB value + */ + + const passColors = [ + "backgroundColor", + "foregroundColor", + "labelColor", + ] as Array; + + for (let i = 0; i < passColors.length; i++) { + const colorProperty = passColors[i]; + const colorInProps = this[propsSymbol][colorProperty]; + + if (colorInProps && !isValidRGB(colorInProps)) { + console.warn( + `'${colorProperty}' property has been removed from pass.json as it has not a valid RGB-strin value.`, + ); + + delete this[propsSymbol][colorProperty]; + } + } + const passJson = Buffer.from(JSON.stringify(this[propsSymbol])); super.addBuffer("pass.json", passJson); From 1b3647da8fe1083c7eaaaf6882f1897960403524 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 26 Sep 2021 18:17:24 +0200 Subject: [PATCH 042/199] Added Bundle.prototype.isFrozen getter and used it to close pass if it is not yet frozen --- src/Bundle.ts | 13 +++++++++++-- src/PKPass.ts | 8 ++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Bundle.ts b/src/Bundle.ts index 6580e0b..6727046 100644 --- a/src/Bundle.ts +++ b/src/Bundle.ts @@ -61,7 +61,7 @@ export default class Bundle { */ protected freeze() { - if (this[bundleStateSymbol] === BundleState.CLOSED) { + if (this.isFrozen) { return; } @@ -69,6 +69,15 @@ export default class Bundle { this[archiveSymbol].end(); } + /** + * Tells if this bundle still allows files to be added + * @returns + */ + + public get isFrozen() { + return this[bundleStateSymbol] === BundleState.CLOSED; + } + /** * Allows files to be added to the bundle. * If the bundle is closed, it will throw an error. @@ -78,7 +87,7 @@ export default class Bundle { */ public addBuffer(fileName: string, buffer: Buffer) { - if (this[bundleStateSymbol] === BundleState.CLOSED) { + if (this.isFrozen) { throw new Error("Cannot add file. Bundle is closed."); } diff --git a/src/PKPass.ts b/src/PKPass.ts index 72389c3..ec9e26e 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -465,7 +465,9 @@ export default class PKPass extends Bundle { * @TODO warning if no icon files */ - this[closePassSymbol](); + if (!this.isFrozen) { + this[closePassSymbol](); + } return super.getAsBuffer(); } @@ -486,7 +488,9 @@ export default class PKPass extends Bundle { * @TODO warning if no icon files */ - this[closePassSymbol](); + if (!this.isFrozen) { + this[closePassSymbol](); + } return super.getAsStream(); } From 6f7a597cbe62d441a590a613d8484dba5205ed84 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 26 Sep 2021 18:21:25 +0200 Subject: [PATCH 043/199] Added Bundle.prototype.files getter to prevent exposing filesSymbol --- spec/Bundle.ts | 4 ++-- src/Bundle.ts | 10 +++++++++- src/PKPass.ts | 8 ++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/spec/Bundle.ts b/spec/Bundle.ts index f77d645..2873b1d 100644 --- a/spec/Bundle.ts +++ b/spec/Bundle.ts @@ -1,5 +1,5 @@ import { Stream } from "stream"; -import { default as Bundle, filesSymbol } from "../lib/Bundle"; +import { default as Bundle } from "../lib/Bundle"; describe("Bundle", () => { let bundle: InstanceType; @@ -24,7 +24,7 @@ describe("Bundle", () => { const buffer = Buffer.alloc(0); bundle.addBuffer("pass.json", buffer); - expect(bundle[filesSymbol]).toEqual({ "pass.json": buffer }); + expect(bundle.files).toEqual({ "pass.json": buffer }); }); it("should throw error if freezed", async () => { diff --git a/src/Bundle.ts b/src/Bundle.ts index 6727046..935c877 100644 --- a/src/Bundle.ts +++ b/src/Bundle.ts @@ -1,7 +1,7 @@ import { Stream } from "stream"; import { ZipFile } from "yazl"; -export const filesSymbol = Symbol("bundleFiles"); +const filesSymbol = Symbol("bundleFiles"); const bundleStateSymbol = Symbol("state"); const archiveSymbol = Symbol("zip"); @@ -78,6 +78,14 @@ export default class Bundle { return this[bundleStateSymbol] === BundleState.CLOSED; } + /** + * Returns the list of files added to the bundle + */ + + public get files() { + return this[filesSymbol]; + } + /** * Allows files to be added to the bundle. * If the bundle is closed, it will throw an error. diff --git a/src/PKPass.ts b/src/PKPass.ts index ec9e26e..f18c70f 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -1,5 +1,5 @@ import FieldsArray from "./fieldsArray"; -import { default as Bundle, filesSymbol } from "./Bundle"; +import { default as Bundle } from "./Bundle"; import { getModelFolderContents } from "./parser"; import * as Schemas from "./schemas"; import { Stream } from "stream"; @@ -73,7 +73,7 @@ export default class PKPass extends Bundle { certificates = source.certificates; buffers = {}; - const buffersEntries = Object.entries(source[filesSymbol]); + const buffersEntries = Object.entries(source.files); /** Cloning all the buffers to prevent unwanted edits */ for (let i = 0; i < buffersEntries.length; i++) { @@ -285,7 +285,7 @@ export default class PKPass extends Bundle { } if (/pass\.json/.test(pathName)) { - if (this[filesSymbol]["pass.json"]) { + if (this.files["pass.json"]) { /** * Ignoring any further addition. In a * future we might consider merging instead @@ -374,7 +374,7 @@ export default class PKPass extends Bundle { } private [createManifestSymbol]() { - return Object.entries(this[filesSymbol]).reduce( + return Object.entries(this.files).reduce( (acc, [fileName, buffer]) => { const hashFlow = forge.md.sha1.create(); From aaea9c1304245bee36496cf33763b39a2b2858bd Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 26 Sep 2021 23:32:21 +0200 Subject: [PATCH 044/199] Revert "Added Bundle.prototype.files getter to prevent exposing filesSymbol" This reverts commit 6f7a597cbe62d441a590a613d8484dba5205ed84. --- spec/Bundle.ts | 4 ++-- src/Bundle.ts | 10 +--------- src/PKPass.ts | 8 ++++---- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/spec/Bundle.ts b/spec/Bundle.ts index 2873b1d..f77d645 100644 --- a/spec/Bundle.ts +++ b/spec/Bundle.ts @@ -1,5 +1,5 @@ import { Stream } from "stream"; -import { default as Bundle } from "../lib/Bundle"; +import { default as Bundle, filesSymbol } from "../lib/Bundle"; describe("Bundle", () => { let bundle: InstanceType; @@ -24,7 +24,7 @@ describe("Bundle", () => { const buffer = Buffer.alloc(0); bundle.addBuffer("pass.json", buffer); - expect(bundle.files).toEqual({ "pass.json": buffer }); + expect(bundle[filesSymbol]).toEqual({ "pass.json": buffer }); }); it("should throw error if freezed", async () => { diff --git a/src/Bundle.ts b/src/Bundle.ts index 935c877..6727046 100644 --- a/src/Bundle.ts +++ b/src/Bundle.ts @@ -1,7 +1,7 @@ import { Stream } from "stream"; import { ZipFile } from "yazl"; -const filesSymbol = Symbol("bundleFiles"); +export const filesSymbol = Symbol("bundleFiles"); const bundleStateSymbol = Symbol("state"); const archiveSymbol = Symbol("zip"); @@ -78,14 +78,6 @@ export default class Bundle { return this[bundleStateSymbol] === BundleState.CLOSED; } - /** - * Returns the list of files added to the bundle - */ - - public get files() { - return this[filesSymbol]; - } - /** * Allows files to be added to the bundle. * If the bundle is closed, it will throw an error. diff --git a/src/PKPass.ts b/src/PKPass.ts index f18c70f..ec9e26e 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -1,5 +1,5 @@ import FieldsArray from "./fieldsArray"; -import { default as Bundle } from "./Bundle"; +import { default as Bundle, filesSymbol } from "./Bundle"; import { getModelFolderContents } from "./parser"; import * as Schemas from "./schemas"; import { Stream } from "stream"; @@ -73,7 +73,7 @@ export default class PKPass extends Bundle { certificates = source.certificates; buffers = {}; - const buffersEntries = Object.entries(source.files); + const buffersEntries = Object.entries(source[filesSymbol]); /** Cloning all the buffers to prevent unwanted edits */ for (let i = 0; i < buffersEntries.length; i++) { @@ -285,7 +285,7 @@ export default class PKPass extends Bundle { } if (/pass\.json/.test(pathName)) { - if (this.files["pass.json"]) { + if (this[filesSymbol]["pass.json"]) { /** * Ignoring any further addition. In a * future we might consider merging instead @@ -374,7 +374,7 @@ export default class PKPass extends Bundle { } private [createManifestSymbol]() { - return Object.entries(this.files).reduce( + return Object.entries(this[filesSymbol]).reduce( (acc, [fileName, buffer]) => { const hashFlow = forge.md.sha1.create(); From cabb0daab4f011b9551cffd297f022b331aec266 Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Sun, 26 Sep 2021 23:42:17 +0200 Subject: [PATCH 045/199] Replaced BundleState with freeze state of Bundle.prototype[filesSymbol] --- src/Bundle.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/Bundle.ts b/src/Bundle.ts index 6727046..964a161 100644 --- a/src/Bundle.ts +++ b/src/Bundle.ts @@ -2,14 +2,8 @@ import { Stream } from "stream"; import { ZipFile } from "yazl"; export const filesSymbol = Symbol("bundleFiles"); -const bundleStateSymbol = Symbol("state"); const archiveSymbol = Symbol("zip"); -enum BundleState { - CLOSED = 0, - OPEN = 1, -} - namespace Mime { export type type = string; export type subtype = string; @@ -22,7 +16,6 @@ namespace Mime { */ export default class Bundle { - private [bundleStateSymbol]: BundleState = BundleState.OPEN; private [filesSymbol]: { [key: string]: Buffer } = {}; private [archiveSymbol] = new ZipFile(); @@ -56,7 +49,7 @@ export default class Bundle { } /** - * Freezes / Closes the bundle so no more files + * Freezes the bundle so no more files * can be added any further. */ @@ -65,7 +58,7 @@ export default class Bundle { return; } - this[bundleStateSymbol] = BundleState.CLOSED; + Object.freeze(this[filesSymbol]); this[archiveSymbol].end(); } @@ -75,7 +68,7 @@ export default class Bundle { */ public get isFrozen() { - return this[bundleStateSymbol] === BundleState.CLOSED; + return Object.isFrozen(this[filesSymbol]); } /** From ac1c994b75cc101274b5998316c4e8bb8ef9a24d Mon Sep 17 00:00:00 2001 From: Alexander Cerutti Date: Mon, 27 Sep 2021 00:32:58 +0200 Subject: [PATCH 046/199] Moved Template type to Schemas --- src/PKPass.ts | 12 ++---------- src/schemas/index.ts | 12 ++++++------ 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/PKPass.ts b/src/PKPass.ts index ec9e26e..f3bd977 100644 --- a/src/PKPass.ts +++ b/src/PKPass.ts @@ -22,14 +22,6 @@ interface NamedBuffers { [key: string]: Buffer; } -namespace PKPass { - export interface Template { - model: string; - certificates: Schemas.Certificates; - overrides?: Schemas.OverridesSupportedOptions; - } -} - type TransitTypes = `PKTransitType${ | "Air" | "Boat" @@ -58,8 +50,8 @@ export default class PKPass extends Bundle { * @returns */ - static async from(source: PKPass | PKPass.Template): Promise { - let certificates: Schemas.Certificates = undefined; + static async from(source: PKPass | Schemas.Template): Promise { + let certificates: Schemas.CertificatesSchema = undefined; let buffers: NamedBuffers = undefined; if (!source) { diff --git a/src/schemas/index.ts b/src/schemas/index.ts index ec80339..ec480c9 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -78,15 +78,15 @@ export const CertificatesSchema = Joi.object() }) .required(); -export interface PassInstance { - model: PartitionedBundle; +export interface Template { + model: string; certificates: CertificatesSchema; overrides?: OverridesSupportedOptions; } -export const PassInstance = Joi.object().keys({ - model: Joi.alternatives(Joi.object(), Joi.string()).required(), - certificates: Joi.object(), +export const Template = Joi.object