Merge pull request #225 from alexandercerutti/feature/iOS18-changes

Changes for v3.2.0
This commit is contained in:
Alexander Cerutti
2024-10-29 23:02:18 +01:00
committed by GitHub
63 changed files with 17368 additions and 41346 deletions

2
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,2 @@
# Prettier rewrite
0827730d410d0491bd271afa4381b52176166c0f

View File

@@ -20,12 +20,15 @@ jobs:
WWDR: ${{ secrets.WWDR }} WWDR: ${{ secrets.WWDR }}
SIGNER_KEY_PASSPHRASE: ${{ secrets.SIGNER_KEY_PASSPHRASE }} SIGNER_KEY_PASSPHRASE: ${{ secrets.SIGNER_KEY_PASSPHRASE }}
steps: steps:
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: "14.x" node-version: "20.x"
check-latest: true check-latest: true
- run: | - run: |
npm install pnpm install
npm run build pnpm build
npm run test pnpm test

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
18

View File

@@ -5,5 +5,5 @@
"editor.smoothScrolling": true, "editor.smoothScrolling": true,
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"jest.jestCommandLine": "node --no-warnings ./node_modules/.bin/jest -c jest.config.cjs --runInBand --silent" "jest.jestCommandLine": "NODE_OPTIONS=\"--warnings\" pnpm jest -c jest.config.cjs --runInBand --silent"
} }

File diff suppressed because it is too large Load Diff

View File

@@ -2,15 +2,15 @@
"name": "functions", "name": "functions",
"description": "Cloud Functions for Firebase", "description": "Cloud Functions for Firebase",
"scripts": { "scripts": {
"serve": "npm run build && npx firebase emulators:start --only functions", "serve": "pnpm build && pnpm firebase emulators:start --only functions",
"shell": "npm run build && npx firebase functions:shell", "shell": "pnpm build && pnpm firebase functions:shell",
"deploy": "npx firebase deploy --only functions", "deploy": "pnpm firebase deploy --only functions",
"logs": "npx firebase functions:log", "logs": "pnpm firebase functions:log",
"service:link-pg": "cd ../../.. && npm run build && npm link", "service:link-pg": "cd ../../.. && pnpm build && pnpm link",
"predev:install": "npm run clear:deps", "predev:install": "pnpm clear:deps",
"dev:install": "npm run service:link-pg && npm link passkit-generator", "dev:install": "pnpm service:link-pg && pnpm link passkit-generator",
"clear:deps": "rm -rf node_modules", "clear:deps": "rm -rf node_modules",
"build": "rm -rf lib && npx tsc" "build": "rm -rf lib && pnpm tsc"
}, },
"engines": { "engines": {
"node": "16" "node": "16"
@@ -18,16 +18,16 @@
"type": "module", "type": "module",
"main": "lib/index.js", "main": "lib/index.js",
"dependencies": { "dependencies": {
"firebase-admin": "^11.10.1", "firebase-admin": "^11.11.1",
"firebase-functions": "^4.4.1", "firebase-functions": "^4.9.0",
"tslib": "^2.6.1" "tslib": "^2.7.0"
}, },
"peerDependencies": { "peerDependencies": {
"passkit-generator": "latest" "passkit-generator": "latest"
}, },
"devDependencies": { "devDependencies": {
"firebase-functions-test": "^0.2.3", "firebase-functions-test": "^0.2.3",
"firebase-tools": "^12.4.6" "firebase-tools": "^12.9.1"
}, },
"private": true "private": true
} }

4616
examples/firebase/functions/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@ import os from "node:os";
// Please note this is experimental in NodeJS as // Please note this is experimental in NodeJS as
// it is marked as Stage 3 in TC39 // it is marked as Stage 3 in TC39
// Should probably not be used in production // Should probably not be used in production
import startData from "./startData.json" assert { "type": "json" }; import startData from "./startData.json" assert { type: "json" };
const PKPass = passkit.PKPass; const PKPass = passkit.PKPass;

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,83 @@
{
"formatVersion": 1,
"serialNumber": "<serial number>",
"passTypeIdentifier": "pass.com.passkitgenerator",
"teamIdentifier": "F53WB8AE67",
"description": "Apple Event",
"organizationName": "Apple Inc.",
"foregroundColor": "#ffffff",
"backgroundColor": "#1595d7",
"labelColor": "#ffffff",
"voided": false,
"sharingProhibited": false,
"beacons": [
{
"proximityUUID": "B7FA1C44-B5B2-436D-AD33-C26651C498BB",
"relevantText": "Prepare to check in."
}
],
"locations": [{ "latitude": 37.3349, "longitude": -122.00902 }],
"barcode": {
"message": "<nfc message>",
"format": "PKBarcodeFormatQR",
"messageEncoding": "iso-8859-1",
"altText": "Special Apple Event"
},
"nfc": {
"message": "<nfc message>",
"encryptionPublicKey": "MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADrCFAKUbtUh7oAnm5drhZKj5+CHO+B5Q0VdF4QTkd17I="
},
"eventTicket": {
"headerFields": [
{ "key": "eventDateTime", "label": "DATE", "value": "Sep 09, 2024" }
],
"primaryFields": [
{ "key": "eventGuest", "label": "Guest", "value": "<guest name>" }
],
"secondaryFields": [
{
"key": "eventVenue",
"label": "Venue",
"value": "Steve Jobs Theater"
}
],
"backFields": [
{
"key": "eventBackInformation",
"label": "Event Information",
"value": "A government-issued photo ID will be required at check-in.\n\nPasses are unique, non-transferable, and can only be used once.\n\nYour confirmation email contains details regarding arrival and event location.\n\nPhoto & Video Release:\nPhotographs, audio and/or video taken at the event by Apple, or others on behalf of Apple, may include your voice, image or likeness. You agree that Apple may use such photographs, audio and/or video, including those that may contain your voice, image or likeness, for any purpose on a worldwide basis in perpetuity, without any compensation to you, and you release Apple from all liability related thereto."
},
{
"key": "eventBackLocation",
"label": "EVENT LOCATION",
"value": "Steve Jobs Theater\n10600 N. Tantau Avenue\nCupertino, CA 95014"
}
]
},
"suppressStripShine": false,
"passLastModDate": "2024-08-21T00:00:00.000Z",
"semantics": {
"eventType": "PKEventTypeGeneric",
"eventName": "Special Apple Event",
"venueName": "Steve Jobs Theater",
"entranceDescription": "Apple Park Visitor Center",
"attendeeName": "<attendee name>",
"eventStartDate": "2024-10-04T17:00:00+00:00",
"eventEndDate": "2024-10-05T19:00:00+00:00",
"venueLocation": { "latitude": 37.3349, "longitude": -122.00902 },
"playlistIDs": []
},
"relevantDates": [
{
"startDate": "2024-10-03T17:00:00+00:00",
"endDate": "2024-10-03T23:00:00+00:00"
},
{
"startDate": "2024-10-04T17:00:00+00:00",
"endDate": "2024-10-04T19:00:00+00:00"
}
],
"directionsInformationURL": "https://apple.co/mapsapvc",
"contactVenueEmail": "rsvp_events@apple.com",
"preferredStyleSchemes": ["posterEventTicket", "eventTicket"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,64 @@
{
"formatVersion": 1,
"passTypeIdentifier": "pass.com.passkitgenerator",
"teamIdentifier": "F53WB8AE67",
"groupingIdentifier": "ticket-demo",
"description": "Description",
"organizationName": "Something",
"backgroundColor": "rgb(255, 255, 255)",
"foregroundColor": "rgb(0, 0, 0)",
"labelColor": "rgb(0, 0, 0)",
"logoText": "iOS18 EventTicket Demo",
"preferredStyleSchemes": ["posterEventTicket", "eventTicket"],
"eventTicket": {
"headerFields": [
{
"key": "event_date",
"label": "event-date",
"value": "26.09.2024"
}
]
},
"semantics": {
"venueParkingLotsOpenDate": "2024-10-04T09:00:00+00:00",
"venueGatesOpenDate": "2024-10-06T20:00:00+00:00",
"eventType": "PKEventTypeLivePerformance",
"eventName": "Secret meeting place",
"admissionLevel": "VIP Access",
"venueRegionName": "Undisclosed location",
"performerNames": ["Lady Gaga"],
"venueBoxOfficeOpenDate": "2024-10-06T20:15:00+00:00",
"venueCloseDate": "2024-10-06T23:59:59+00:00",
"venueDoorsOpenDate": "2024-10-06T20:00:00+00:00",
"venueFanZoneOpenDate": "2024-10-06T19:30:00+00:00",
"updatedEventStartDate": "2024-10-06T21:30:00+00:00",
"updatedEventEndDate": "2024-10-07T01:30:00+00:00",
"admissionLevelAbbreviation": "VIP A.",
"venueEntranceDoor": "15A",
"venueEntrancePortal": "7B",
"albumIDs": ["1440818588"],
"additionalTicketAttributes": "3,4,5",
"entranceDescription": "Event at The Stadium",
"venueLocation": {
"latitude": 51.555557,
"longitude": 0.238041
}
},
"parkingInformationURL": "https://www.southbayjazzfestival.com/parking",
"directionsInformationURL": "https://apple.co/mapsapvc",
"contactVenueEmail": "rsvp_events@apple.com",
"relevantDates": [
{
"startDate": "2024-10-03T17:00:00+01:00",
"endDate": "2024-10-03T23:00:00+01:00"
},
{
"startDate": "2024-10-06T00:00:00+00:00",
"endDate": "2024-10-06T19:00:00+01:00"
}
],
"nfc": {
"message": "message",
"encryptionPublicKey": "MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADwKMBv29ByaSLiGF0FctuyB+Hs2oZ1kDIYhTVllPexNE="
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,96 @@
{
"formatVersion": 1,
"passTypeIdentifier": "pass.com.passkitgenerator",
"teamIdentifier": "F53WB8AE67",
"groupingIdentifier": "ticket-demo",
"description": "Description",
"organizationName": "A some kind of event happening tomorrow",
"backgroundColor": "#ffffff",
"foregroundColor": "#000000",
"labelColor": "#FF0000",
"logoText": "Demo",
"preferredStyleSchemes": ["posterEventTicket", "eventTicket"],
"eventTicket": {
"headerFields": [
{
"key": "event_date",
"label": "event-date",
"value": "26.09.2024"
}
],
"primaryFields": [
{ "key": "event_name", "label": "event-name", "value": "Dune" }
],
"additionalInfoFields": [
{
"key": "additionalInfo-1",
"label": "Additional Info 1",
"value": "The text to show"
},
{
"key": "additionalInfo-2",
"label": "Additional Info 2",
"value": "The text to show 2"
},
{
"key": "lineItem3",
"label": "Emergency Contact",
"value": "+1 8716 12736131",
"dataDetectorTypes": ["PKDataDetectorTypePhoneNumber"]
},
{
"key": "lineItem4",
"label": "Test link",
"value": "https://apple.com",
"dataDetectorTypes": ["PKDataDetectorTypeLink"],
"attributedValue": "<a href=\"https://apple.com\">Used literally on iPhone, used correctly on Watch</a>"
}
]
},
"semantics": {
"venueParkingLotsOpenDate": "2024-10-09T04:00:00+00:00",
"venueGatesOpenDate": "2024-10-09T06:00:00+00:00",
"eventLiveMessage": "This event is going to start soon! Try to relax your anus (cit.)",
"eventType": "PKEventTypeLivePerformance",
"eventName": "South Bay Jazz Festival",
"entranceDescription": "Event at The Stadium",
"venueLocation": {
"latitude": 51.555557,
"longitude": 0.238041
},
"venueName": "The Stadium",
"performerNames": ["Lady Gaga"],
"eventStartDate": "2024-10-08T22:00:00+00:00",
"eventEndDate": "2024-10-09T23:59:59+00:00",
"tailgatingAllowed": true,
"seats": [
{
"seatNumber": "5",
"seatRow": "3",
"seatSection": "100",
"seatSectionColor": "#FFD700"
}
],
"artistIDs": ["984117861"]
},
"directionsInformationURL": "https://www.displaysomeinfoexample.com",
"contactVenueWebsite": "https://www.venueexample.com",
"relevantDates": [
{
"startDate": "2024-10-09T17:00:00+01:00",
"endDate": "2024-10-09T23:59:59+01:00"
},
{
"startDate": "2024-10-10T17:00:00+00:00",
"endDate": "2024-10-10T19:00:00+00:00"
},
{
"startDate": "2024-10-11T17:00:00+00:00",
"endDate": "2024-10-11T19:00:00+00:00"
}
],
"nfc": {
"message": "message",
"encryptionPublicKey": "MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADwKMBv29ByaSLiGF0FctuyB+Hs2oZ1kDIYhTVllPexNE="
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -6,24 +6,24 @@
"author": "Alexander P. Cerutti <cerutti.alexander@gmail.com>", "author": "Alexander P. Cerutti <cerutti.alexander@gmail.com>",
"license": "ISC", "license": "ISC",
"scripts": { "scripts": {
"service:link-pg": "cd ../.. && npm run build && npm link", "service:link-pg": "cd ../.. && pnpm build && pnpm link",
"preinstall": "npm run clear:deps", "preinstall": "pnpm clear:deps",
"postinstall": "npm run service:link-pg && npm link passkit-generator", "postinstall": "pnpm service:link-pg && pnpm link passkit-generator",
"clear:deps": "rm -rf node_modules", "clear:deps": "rm -rf node_modules",
"example": "npx ts-node src/index.ts", "example": "pnpm ts-node src/index.ts",
"example:debug": "node -r ts-node/register --inspect-brk src/index.ts" "example:debug": "node -r ts-node/register --inspect-brk src/index.ts"
}, },
"peerDependencies": { "peerDependencies": {
"passkit-generator": "latest" "passkit-generator": "latest"
}, },
"dependencies": { "dependencies": {
"express": "^4.17.3", "express": "^4.21.1",
"node-fetch": "^3.2.3", "node-fetch": "^3.2.3",
"tslib": "^2.3.1" "tslib": "^2.7.0"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "4.17.13", "@types/express": "4.17.13",
"ts-node": "^10.7.0", "ts-node": "^10.9.2",
"typescript": "^4.6.3" "typescript": "^5.6.3"
} }
} }

764
examples/self-hosted/pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,764 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
express:
specifier: ^4.21.1
version: 4.21.1
node-fetch:
specifier: ^3.2.3
version: 3.2.3
passkit-generator:
specifier: latest
version: 3.1.11
tslib:
specifier: ^2.7.0
version: 2.7.0
devDependencies:
'@types/express':
specifier: 4.17.13
version: 4.17.13
ts-node:
specifier: ^10.9.2
version: 10.9.2(@types/node@17.0.15)(typescript@5.6.3)
typescript:
specifier: ^5.6.3
version: 5.6.3
packages:
/@cspotcode/source-map-support@0.8.1:
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
dependencies:
'@jridgewell/trace-mapping': 0.3.9
dev: true
/@hapi/hoek@9.2.1:
resolution: {integrity: sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw==}
dev: false
/@hapi/topo@5.1.0:
resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
dependencies:
'@hapi/hoek': 9.2.1
dev: false
/@jridgewell/resolve-uri@3.1.2:
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
engines: {node: '>=6.0.0'}
dev: true
/@jridgewell/sourcemap-codec@1.5.0:
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
dev: true
/@jridgewell/trace-mapping@0.3.9:
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
dev: true
/@sideway/address@4.1.3:
resolution: {integrity: sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ==}
dependencies:
'@hapi/hoek': 9.2.1
dev: false
/@sideway/formula@3.0.0:
resolution: {integrity: sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==}
dev: false
/@sideway/pinpoint@2.0.0:
resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
dev: false
/@tsconfig/node10@1.0.8:
resolution: {integrity: sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==}
dev: true
/@tsconfig/node12@1.0.9:
resolution: {integrity: sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==}
dev: true
/@tsconfig/node14@1.0.1:
resolution: {integrity: sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==}
dev: true
/@tsconfig/node16@1.0.2:
resolution: {integrity: sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==}
dev: true
/@types/body-parser@1.19.2:
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
dependencies:
'@types/connect': 3.4.35
'@types/node': 17.0.15
dev: true
/@types/connect@3.4.35:
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
dependencies:
'@types/node': 17.0.15
dev: true
/@types/express-serve-static-core@4.17.28:
resolution: {integrity: sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==}
dependencies:
'@types/node': 17.0.15
'@types/qs': 6.9.7
'@types/range-parser': 1.2.4
dev: true
/@types/express@4.17.13:
resolution: {integrity: sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==}
dependencies:
'@types/body-parser': 1.19.2
'@types/express-serve-static-core': 4.17.28
'@types/qs': 6.9.7
'@types/serve-static': 1.13.10
dev: true
/@types/mime@1.3.2:
resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==}
dev: true
/@types/node@17.0.15:
resolution: {integrity: sha512-zWt4SDDv1S9WRBNxLFxFRHxdD9tvH8f5/kg5/IaLFdnSNXsDY4eL3Q3XXN+VxUnWIhyVFDwcsmAprvwXoM/ClA==}
dev: true
/@types/qs@6.9.7:
resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==}
dev: true
/@types/range-parser@1.2.4:
resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==}
dev: true
/@types/serve-static@1.13.10:
resolution: {integrity: sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==}
dependencies:
'@types/mime': 1.3.2
'@types/node': 17.0.15
dev: true
/accepts@1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
dependencies:
mime-types: 2.1.34
negotiator: 0.6.3
dev: false
/acorn-walk@8.2.0:
resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==}
engines: {node: '>=0.4.0'}
dev: true
/acorn@8.7.0:
resolution: {integrity: sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==}
engines: {node: '>=0.4.0'}
hasBin: true
dev: true
/arg@4.1.3:
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
dev: true
/array-flatten@1.1.1:
resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
dev: false
/body-parser@1.20.3:
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
dependencies:
bytes: 3.1.2
content-type: 1.0.5
debug: 2.6.9
depd: 2.0.0
destroy: 1.2.0
http-errors: 2.0.0
iconv-lite: 0.4.24
on-finished: 2.4.1
qs: 6.13.0
raw-body: 2.5.2
type-is: 1.6.18
unpipe: 1.0.0
transitivePeerDependencies:
- supports-color
dev: false
/bytes@3.1.2:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
dev: false
/call-bind@1.0.7:
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
engines: {node: '>= 0.4'}
dependencies:
es-define-property: 1.0.0
es-errors: 1.3.0
function-bind: 1.1.2
get-intrinsic: 1.2.4
set-function-length: 1.2.2
dev: false
/content-disposition@0.5.4:
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
engines: {node: '>= 0.6'}
dependencies:
safe-buffer: 5.2.1
dev: false
/content-type@1.0.4:
resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==}
engines: {node: '>= 0.6'}
dev: false
/content-type@1.0.5:
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
engines: {node: '>= 0.6'}
dev: false
/cookie-signature@1.0.6:
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
dev: false
/cookie@0.7.1:
resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==}
engines: {node: '>= 0.6'}
dev: false
/create-require@1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
dev: true
/data-uri-to-buffer@4.0.0:
resolution: {integrity: sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==}
engines: {node: '>= 12'}
dev: false
/debug@2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.0.0
dev: false
/define-data-property@1.1.4:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
dependencies:
es-define-property: 1.0.0
es-errors: 1.3.0
gopd: 1.0.1
dev: false
/depd@2.0.0:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
dev: false
/destroy@1.2.0:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
dev: false
/diff@4.0.2:
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
engines: {node: '>=0.3.1'}
dev: true
/do-not-zip@1.0.0:
resolution: {integrity: sha512-Pgd81ET43bhAGaN2Hq1zluSX1FmD7kl7KcV9ER/lawiLsRUB9pRA5y8r6us29Xk6BrINZETO8TjhYwtwafWUww==}
dev: false
/ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
dev: false
/encodeurl@1.0.2:
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
engines: {node: '>= 0.8'}
dev: false
/encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
dev: false
/es-define-property@1.0.0:
resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
engines: {node: '>= 0.4'}
dependencies:
get-intrinsic: 1.2.4
dev: false
/es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
dev: false
/escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
dev: false
/etag@1.8.1:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
dev: false
/express@4.21.1:
resolution: {integrity: sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==}
engines: {node: '>= 0.10.0'}
dependencies:
accepts: 1.3.8
array-flatten: 1.1.1
body-parser: 1.20.3
content-disposition: 0.5.4
content-type: 1.0.4
cookie: 0.7.1
cookie-signature: 1.0.6
debug: 2.6.9
depd: 2.0.0
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
finalhandler: 1.3.1
fresh: 0.5.2
http-errors: 2.0.0
merge-descriptors: 1.0.3
methods: 1.1.2
on-finished: 2.4.1
parseurl: 1.3.3
path-to-regexp: 0.1.10
proxy-addr: 2.0.7
qs: 6.13.0
range-parser: 1.2.1
safe-buffer: 5.2.1
send: 0.19.0
serve-static: 1.16.2
setprototypeof: 1.2.0
statuses: 2.0.1
type-is: 1.6.18
utils-merge: 1.0.1
vary: 1.1.2
transitivePeerDependencies:
- supports-color
dev: false
/fetch-blob@3.1.4:
resolution: {integrity: sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==}
engines: {node: ^12.20 || >= 14.13}
dependencies:
node-domexception: 1.0.0
web-streams-polyfill: 3.2.0
dev: false
/finalhandler@1.3.1:
resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==}
engines: {node: '>= 0.8'}
dependencies:
debug: 2.6.9
encodeurl: 2.0.0
escape-html: 1.0.3
on-finished: 2.4.1
parseurl: 1.3.3
statuses: 2.0.1
unpipe: 1.0.0
transitivePeerDependencies:
- supports-color
dev: false
/formdata-polyfill@4.0.10:
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
engines: {node: '>=12.20.0'}
dependencies:
fetch-blob: 3.1.4
dev: false
/forwarded@0.2.0:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
engines: {node: '>= 0.6'}
dev: false
/fresh@0.5.2:
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
engines: {node: '>= 0.6'}
dev: false
/function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
dev: false
/get-intrinsic@1.2.4:
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
engines: {node: '>= 0.4'}
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
has-proto: 1.0.3
has-symbols: 1.0.3
hasown: 2.0.2
dev: false
/gopd@1.0.1:
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
dependencies:
get-intrinsic: 1.2.4
dev: false
/has-property-descriptors@1.0.2:
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
dependencies:
es-define-property: 1.0.0
dev: false
/has-proto@1.0.3:
resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
engines: {node: '>= 0.4'}
dev: false
/has-symbols@1.0.3:
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
engines: {node: '>= 0.4'}
dev: false
/hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
dependencies:
function-bind: 1.1.2
dev: false
/http-errors@2.0.0:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
dependencies:
depd: 2.0.0
inherits: 2.0.4
setprototypeof: 1.2.0
statuses: 2.0.1
toidentifier: 1.0.1
dev: false
/iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
dependencies:
safer-buffer: 2.1.2
dev: false
/inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
dev: false
/ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
dev: false
/joi@17.4.2:
resolution: {integrity: sha512-Lm56PP+n0+Z2A2rfRvsfWVDXGEWjXxatPopkQ8qQ5mxCEhwHG+Ettgg5o98FFaxilOxozoa14cFhrE/hOzh/Nw==}
dependencies:
'@hapi/hoek': 9.2.1
'@hapi/topo': 5.1.0
'@sideway/address': 4.1.3
'@sideway/formula': 3.0.0
'@sideway/pinpoint': 2.0.0
dev: false
/make-error@1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
dev: true
/media-typer@0.3.0:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
dev: false
/merge-descriptors@1.0.3:
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
dev: false
/methods@1.1.2:
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
engines: {node: '>= 0.6'}
dev: false
/mime-db@1.51.0:
resolution: {integrity: sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==}
engines: {node: '>= 0.6'}
dev: false
/mime-types@2.1.34:
resolution: {integrity: sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==}
engines: {node: '>= 0.6'}
dependencies:
mime-db: 1.51.0
dev: false
/mime@1.6.0:
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
engines: {node: '>=4'}
hasBin: true
dev: false
/ms@2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
dev: false
/ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
dev: false
/negotiator@0.6.3:
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
engines: {node: '>= 0.6'}
dev: false
/node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
dev: false
/node-fetch@3.2.3:
resolution: {integrity: sha512-AXP18u4pidSZ1xYXRDPY/8jdv3RAozIt/WLNR/MBGZAz+xjtlr90RvCnsvHQRiXyWliZF/CpytExp32UU67/SA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
data-uri-to-buffer: 4.0.0
fetch-blob: 3.1.4
formdata-polyfill: 4.0.10
dev: false
/node-forge@1.3.1:
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
engines: {node: '>= 6.13.0'}
dev: false
/object-inspect@1.13.2:
resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==}
engines: {node: '>= 0.4'}
dev: false
/on-finished@2.4.1:
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
engines: {node: '>= 0.8'}
dependencies:
ee-first: 1.1.1
dev: false
/parseurl@1.3.3:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'}
dev: false
/passkit-generator@3.1.11:
resolution: {integrity: sha512-R6xSkkbzojouvJOPt/F1sRBgSawrIetd4XVHaIhYxRxX7pNHbbdpwwQMwPIuKRgmTzUAi4hFiFlN4CTCd0NAZg==}
engines: {node: '>=14.18.1'}
dependencies:
do-not-zip: 1.0.0
joi: 17.4.2
node-forge: 1.3.1
tslib: 2.7.0
dev: false
/path-to-regexp@0.1.10:
resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==}
dev: false
/proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
dependencies:
forwarded: 0.2.0
ipaddr.js: 1.9.1
dev: false
/qs@6.13.0:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
engines: {node: '>=0.6'}
dependencies:
side-channel: 1.0.6
dev: false
/range-parser@1.2.1:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
dev: false
/raw-body@2.5.2:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
engines: {node: '>= 0.8'}
dependencies:
bytes: 3.1.2
http-errors: 2.0.0
iconv-lite: 0.4.24
unpipe: 1.0.0
dev: false
/safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
dev: false
/safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
dev: false
/send@0.19.0:
resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
engines: {node: '>= 0.8.0'}
dependencies:
debug: 2.6.9
depd: 2.0.0
destroy: 1.2.0
encodeurl: 1.0.2
escape-html: 1.0.3
etag: 1.8.1
fresh: 0.5.2
http-errors: 2.0.0
mime: 1.6.0
ms: 2.1.3
on-finished: 2.4.1
range-parser: 1.2.1
statuses: 2.0.1
transitivePeerDependencies:
- supports-color
dev: false
/serve-static@1.16.2:
resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
engines: {node: '>= 0.8.0'}
dependencies:
encodeurl: 2.0.0
escape-html: 1.0.3
parseurl: 1.3.3
send: 0.19.0
transitivePeerDependencies:
- supports-color
dev: false
/set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}
dependencies:
define-data-property: 1.1.4
es-errors: 1.3.0
function-bind: 1.1.2
get-intrinsic: 1.2.4
gopd: 1.0.1
has-property-descriptors: 1.0.2
dev: false
/setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
dev: false
/side-channel@1.0.6:
resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.7
es-errors: 1.3.0
get-intrinsic: 1.2.4
object-inspect: 1.13.2
dev: false
/statuses@2.0.1:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'}
dev: false
/toidentifier@1.0.1:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
dev: false
/ts-node@10.9.2(@types/node@17.0.15)(typescript@5.6.3):
resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
hasBin: true
peerDependencies:
'@swc/core': '>=1.2.50'
'@swc/wasm': '>=1.2.50'
'@types/node': '*'
typescript: '>=2.7'
peerDependenciesMeta:
'@swc/core':
optional: true
'@swc/wasm':
optional: true
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.8
'@tsconfig/node12': 1.0.9
'@tsconfig/node14': 1.0.1
'@tsconfig/node16': 1.0.2
'@types/node': 17.0.15
acorn: 8.7.0
acorn-walk: 8.2.0
arg: 4.1.3
create-require: 1.1.1
diff: 4.0.2
make-error: 1.3.6
typescript: 5.6.3
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
dev: true
/tslib@2.7.0:
resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
dev: false
/type-is@1.6.18:
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
engines: {node: '>= 0.6'}
dependencies:
media-typer: 0.3.0
mime-types: 2.1.34
dev: false
/typescript@5.6.3:
resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==}
engines: {node: '>=14.17'}
hasBin: true
dev: true
/unpipe@1.0.0:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
dev: false
/utils-merge@1.0.1:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}
dev: false
/v8-compile-cache-lib@3.0.1:
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
dev: true
/vary@1.1.2:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
dev: false
/web-streams-polyfill@3.2.0:
resolution: {integrity: sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==}
engines: {node: '>= 8'}
dev: false
/yn@3.1.1:
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
engines: {node: '>=6'}
dev: true

View File

@@ -7,8 +7,8 @@
import { app } from "./webserver"; import { app } from "./webserver";
import { getCertificates } from "./shared"; import { getCertificates } from "./shared";
import path from "path"; import path from "node:path";
import { promises as fs } from "fs"; import { promises as fs } from "node:fs";
import { PKPass } from "passkit-generator"; import { PKPass } from "passkit-generator";
import * as Utils from "passkit-generator/lib/utils"; import * as Utils from "passkit-generator/lib/utils";

View File

@@ -31,8 +31,8 @@
import { app } from "./webserver"; import { app } from "./webserver";
import { getCertificates } from "./shared"; import { getCertificates } from "./shared";
import { promises as fs } from "fs"; import { promises as fs } from "node:fs";
import path from "path"; import path from "node:path";
import { PKPass } from "passkit-generator"; import { PKPass } from "passkit-generator";
// *************************** // // *************************** //

View File

@@ -11,7 +11,7 @@
import { app } from "./webserver"; import { app } from "./webserver";
import { getCertificates } from "./shared"; import { getCertificates } from "./shared";
import path from "path"; import path from "node:path";
import { PKPass } from "passkit-generator"; import { PKPass } from "passkit-generator";
app.route("/fields/:modelName").get(async (request, response) => { app.route("/fields/:modelName").get(async (request, response) => {

View File

@@ -6,7 +6,7 @@
import { app } from "./webserver"; import { app } from "./webserver";
import { getCertificates } from "./shared"; import { getCertificates } from "./shared";
import path from "path"; import path from "node:path";
import { PKPass } from "passkit-generator"; import { PKPass } from "passkit-generator";
app.route("/localize/:modelName").get(async (request, response) => { app.route("/localize/:modelName").get(async (request, response) => {

View File

@@ -5,8 +5,8 @@
import { app } from "./webserver"; import { app } from "./webserver";
import { getCertificates } from "./shared"; import { getCertificates } from "./shared";
import path from "path"; import path from "node:path";
import { promises as fs } from "fs"; import { promises as fs } from "node:fs";
import { PKPass } from "passkit-generator"; import { PKPass } from "passkit-generator";
function getRandomColorPart() { function getRandomColorPart() {

View File

@@ -11,7 +11,7 @@
import { app } from "./webserver"; import { app } from "./webserver";
import { getCertificates } from "./shared"; import { getCertificates } from "./shared";
import { PKPass } from "passkit-generator"; import { PKPass } from "passkit-generator";
import path from "path"; import path from "node:path";
app.route("/barcodes/:modelName").get(async (request, response) => { app.route("/barcodes/:modelName").get(async (request, response) => {
const passName = const passName =

View File

@@ -9,7 +9,7 @@
import { app } from "./webserver"; import { app } from "./webserver";
import { getCertificates } from "./shared"; import { getCertificates } from "./shared";
import path from "path"; import path from "node:path";
import { PKPass } from "passkit-generator"; import { PKPass } from "passkit-generator";
app.route("/expirationDate/:modelName").get(async (request, response) => { app.route("/expirationDate/:modelName").get(async (request, response) => {

View File

@@ -1,5 +1,5 @@
import { promises as fs } from "fs"; import fs from "node:fs/promises";
import path from "path"; import path from "node:path";
interface Cache { interface Cache {
certificates: certificates:

File diff suppressed because it is too large Load Diff

View File

@@ -7,21 +7,21 @@
"license": "ISC", "license": "ISC",
"main": "src/index.js", "main": "src/index.js",
"scripts": { "scripts": {
"service:link-pg": "cd ../.. && npm run build && npm link", "service:link-pg": "cd ../.. && pnpm build && pnpm link",
"preinstall": "npm run clear:deps", "preinstall": "pnpm clear:deps",
"postinstall": "npm run service:link-pg && npm link passkit-generator", "postinstall": "pnpm service:link-pg && pnpm link passkit-generator",
"clear:deps": "rm -rf node_modules", "clear:deps": "rm -rf node_modules",
"example": "npx serverless@3 offline --host 0.0.0.0; :'specifying host due to WSL limits'" "example": "pnpm serverless@3 offline --host 0.0.0.0; :'specifying host due to WSL limits'"
}, },
"dependencies": { "dependencies": {
"aws-sdk": "^2.1092.0", "aws-sdk": "^2.1691.0",
"tslib": "^2.3.1" "tslib": "^2.7.0"
}, },
"devDependencies": { "devDependencies": {
"@types/aws-lambda": "^8.10.93", "@types/aws-lambda": "^8.10.145",
"serverless-offline": "^8.5.0", "serverless-offline": "^8.8.1",
"serverless-plugin-typescript": "^2.1.1", "serverless-plugin-typescript": "^2.1.5",
"serverless-s3-local": "^0.6.22", "serverless-s3-local": "^0.8.5",
"typescript": "^4.6.3" "typescript": "^5.6.3"
} }
} }

8401
examples/serverless/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
import { ALBEvent, ALBResult } from "aws-lambda"; import { ALBEvent, ALBResult } from "aws-lambda";
import AWS from "aws-sdk"; import AWS from "aws-sdk";
import { promises as fs } from "fs"; import fs from "node:fs/promises";
import path from "path"; import path from "node:path";
import { Buffer } from "buffer"; import { Buffer } from "node:buffer";
import config from "../config.json"; import config from "../config.json";
import { PKPass } from "passkit-generator"; import { PKPass } from "passkit-generator";

6282
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
{ {
"name": "passkit-generator", "name": "passkit-generator",
"version": "3.1.11", "version": "3.2.0-alpha.2",
"description": "The easiest way to generate custom Apple Wallet passes in Node.js", "description": "The easiest way to generate custom Apple Wallet passes in Node.js",
"main": "lib/index.js", "main": "lib/index.js",
"scripts": { "scripts": {
"build": "npm run build:src", "build": "pnpm build:src",
"build:all": "npm run build:src && npm run build:examples", "build:all": "pnpm build:src && pnpm build:examples",
"build:src": "rimraf lib && npx tsc -p tsconfig.dist.json", "build:src": "pnpm rimraf lib && pnpm tsc -p tsconfig.dist.json",
"prepublishOnly": "npm run build && npm run test", "prepublishOnly": "pnpm build && pnpm test",
"test": "NODE_OPTIONS=\"--experimental-vm-modules --no-warnings\" jest -c jest.config.cjs --silent" "test": "NODE_OPTIONS=\"--experimental-vm-modules --no-warnings\" pnpm jest -c jest.config.cjs --silent"
}, },
"author": "Alexander Patrick Cerutti", "author": "Alexander Patrick Cerutti",
"license": "MIT", "license": "MIT",
@@ -23,23 +23,23 @@
"dependencies": { "dependencies": {
"do-not-zip": "^1.0.0", "do-not-zip": "^1.0.0",
"joi": "17.4.2", "joi": "17.4.2",
"node-forge": "^1.3.0", "node-forge": "^1.3.1",
"tslib": "^2.3.1" "tslib": "^2.7.0"
}, },
"engines": { "engines": {
"node": ">=14.18.1" "node": ">=14.18.1"
}, },
"devDependencies": { "devDependencies": {
"@types/do-not-zip": "^1.0.0", "@types/do-not-zip": "^1.0.2",
"@types/node": "^16.11.26", "@types/node": "^16.11.26",
"@types/node-forge": "^1.0.1", "@types/node-forge": "^1.3.11",
"jest": "^29.5.0", "jest": "^29.7.0",
"jest-environment-node": "^29.5.0", "jest-environment-node": "^29.7.0",
"prettier": "^2.5.1", "prettier": "^3.3.3",
"rimraf": "^3.0.2", "rimraf": "^4.4.1",
"typescript": "^5.0.4" "typescript": "^5.6.3"
}, },
"files": [ "files": [
"lib/**/*.+(js|d.ts)!(*.map)" "lib/**/*.+(js|d.ts)"
] ]
} }

2397
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -8,19 +8,72 @@ const {
} = require("@jest/globals"); } = require("@jest/globals");
const fs = require("node:fs"); const fs = require("node:fs");
const path = require("node:path"); const path = require("node:path");
const forge = require("node-forge");
const { default: PKPass } = require("../lib/PKPass"); const { default: PKPass } = require("../lib/PKPass");
/**
* @returns {[cert: Buffer, key: Buffer]}
*/
function generateCertificateAndPrivateKey() {
const keys = forge.pki.rsa.generateKeyPair(2048);
const cert = forge.pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = "01";
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(
cert.validity.notBefore.getFullYear() + 1,
);
const attrs = [
{
name: "commonName",
value: "example.org",
},
{
name: "countryName",
value: "TS",
},
{
shortName: "ST",
value: "Test",
},
{
name: "localityName",
value: "Test",
},
{
name: "organizationName",
value: "Test",
},
{
shortName: "OU",
value: "Test",
},
];
cert.setIssuer(attrs);
cert.setSubject(attrs);
cert.sign(keys.privateKey);
return [
Buffer.from(forge.pki.certificateToPem(cert)),
Buffer.from(forge.pki.privateKeyToPem(keys.privateKey)),
];
}
const [signerCertBuffer, privateKeyBuffer] = generateCertificateAndPrivateKey();
/** /**
* SIGNER_CERT, SIGNER_KEY, WWDR and SIGNER_KEY_PASSPHRASE are also set * SIGNER_CERT, SIGNER_KEY, WWDR and SIGNER_KEY_PASSPHRASE are also set
* as secrets in Github for run tests on Github Actions * as secrets in Github for run tests on Github Actions
*/ */
const SIGNER_CERT = const SIGNER_CERT = process.env.SIGNER_CERT || signerCertBuffer;
process.env.SIGNER_CERT || const SIGNER_KEY = process.env.SIGNER_KEY || privateKeyBuffer;
fs.readFileSync(path.resolve(__dirname, "../certificates/signerCert.pem"));
const SIGNER_KEY =
process.env.SIGNER_KEY ||
fs.readFileSync(path.resolve(__dirname, "../certificates/signerKey.pem"));
const WWDR = const WWDR =
process.env.WWDR || process.env.WWDR ||
fs.readFileSync(path.resolve(__dirname, "../certificates/WWDR.pem")); fs.readFileSync(path.resolve(__dirname, "../certificates/WWDR.pem"));
@@ -1148,4 +1201,99 @@ describe("PKPass", () => {
); );
}); });
}); });
describe("eventTicket new layout", () => {
it("should contain preferredStyleSchemes if coming from an imported pass json", () => {
const passjson = modelFiles["pass.json"];
const changedPassJson = Buffer.from(
JSON.stringify(
Object.assign({}, JSON.parse(passjson.toString("utf-8")), {
preferredStyleSchemes: [
"posterEventTicket",
"eventTicket",
],
eventTicket: {},
}),
),
"utf-8",
);
pkpass = new PKPass(
Object.assign({}, modelFiles, { "pass.json": changedPassJson }),
{
signerCert: SIGNER_CERT,
signerKey: SIGNER_KEY,
wwdr: WWDR,
signerKeyPassphrase: SIGNER_KEY_PASSPHRASE,
},
);
expect(pkpass.preferredStyleSchemes).toEqual([
"posterEventTicket",
"eventTicket",
]);
const passjsonGenerated = getGeneratedPassJson(pkpass);
expect(passjsonGenerated.preferredStyleSchemes).not.toBeUndefined();
expect(passjsonGenerated.preferredStyleSchemes).toEqual([
"posterEventTicket",
"eventTicket",
]);
});
it("should contain preferredStyleSchemes if coming from the setter (legacy order)", () => {
pkpass.type = "eventTicket";
pkpass.preferredStyleSchemes = ["eventTicket", "posterEventTicket"];
expect(pkpass.preferredStyleSchemes).toEqual([
"eventTicket",
"posterEventTicket",
]);
const passjsonGenerated = getGeneratedPassJson(pkpass);
expect(passjsonGenerated.preferredStyleSchemes).not.toBeUndefined();
expect(passjsonGenerated.preferredStyleSchemes).toEqual([
"eventTicket",
"posterEventTicket",
]);
});
it("should contain preferredStyleSchemes if coming from the setter (new order)", () => {
pkpass.type = "eventTicket";
pkpass.preferredStyleSchemes = ["posterEventTicket", "eventTicket"];
expect(pkpass.preferredStyleSchemes).toEqual([
"posterEventTicket",
"eventTicket",
]);
const passjsonGenerated = getGeneratedPassJson(pkpass);
expect(passjsonGenerated.preferredStyleSchemes).not.toBeUndefined();
expect(passjsonGenerated.preferredStyleSchemes).toEqual([
"posterEventTicket",
"eventTicket",
]);
});
});
it("preferredStyleSchemes setter should throw if pass is not an eventTicket", () => {
pkpass.type = "boardingPass";
expect(() => {
pkpass.preferredStyleSchemes = ["posterEventTicket", "eventTicket"];
}).toThrowError();
});
it("preferredStyleSchemes getter should throw if pass is not an eventTicket", () => {
pkpass.type = "boardingPass";
expect(() => {
pkpass.preferredStyleSchemes;
}).toThrowError();
});
}); });

View File

@@ -1,6 +1,7 @@
import { Readable, Stream } from "stream"; import { Readable, Stream } from "node:stream";
import * as Messages from "./messages"; import * as Messages from "./messages";
import * as zip from "do-not-zip"; import { toArray as zipToArray } from "do-not-zip";
import { Buffer } from "node:buffer";
export const filesSymbol = Symbol("bundleFiles"); export const filesSymbol = Symbol("bundleFiles");
export const freezeSymbol = Symbol("bundleFreeze"); export const freezeSymbol = Symbol("bundleFreeze");
@@ -128,7 +129,7 @@ export default class Bundle {
public getAsBuffer(): Buffer { public getAsBuffer(): Buffer {
this[freezeSymbol](); this[freezeSymbol]();
return zip.toBuffer(createZipFilesMap(this[filesSymbol])); return Buffer.from(zipToArray(createZipFilesMap(this[filesSymbol])));
} }
/** /**
@@ -142,7 +143,7 @@ export default class Bundle {
public getAsStream(): Stream { public getAsStream(): Stream {
this[freezeSymbol](); this[freezeSymbol]();
return Readable.from( return Readable.from(
zip.toBuffer(createZipFilesMap(this[filesSymbol])), Buffer.from(zipToArray(createZipFilesMap(this[filesSymbol]))),
); );
} }

View File

@@ -1,6 +1,6 @@
import { Stream } from "stream"; import { Stream } from "node:stream";
import { Buffer } from "buffer"; import { Buffer } from "node:buffer";
import path from "path"; import path from "node:path";
import FieldsArray from "./FieldsArray"; import FieldsArray from "./FieldsArray";
import Bundle, { filesSymbol } from "./Bundle"; import Bundle, { filesSymbol } from "./Bundle";
import getModelFolderContents from "./getModelFolderContents"; import getModelFolderContents from "./getModelFolderContents";
@@ -216,6 +216,49 @@ export default class PKPass extends Bundle {
return Utils.cloneRecursive(this[propsSymbol]); return Utils.cloneRecursive(this[propsSymbol]);
} }
/**
* Allows accessing to iOS 18 new Event Ticket
* property `preferredStyleSchemes`.
*
* @throws if current type is not "eventTicket".
*/
public get preferredStyleSchemes(): Schemas.PreferredStyleSchemes {
if (this.type !== "eventTicket") {
throw new TypeError(
Messages.PREFERRED_STYLE_SCHEMES.UNEXPECTED_PASS_TYPE_GET,
);
}
return this[propsSymbol].preferredStyleSchemes;
}
/**
* Allows setting a preferredStyleSchemes property
* for a eventTicket.
*
* @throws if current type is not "eventTicket".
* @param value
*/
public set preferredStyleSchemes(value: Schemas.PreferredStyleSchemes) {
Utils.assertUnfrozen(this);
if (this.type !== "eventTicket") {
throw new TypeError(
Messages.PREFERRED_STYLE_SCHEMES.UNEXPECTED_PASS_TYPE_SET,
);
}
Schemas.assertValidity(
Schemas.PreferredStyleSchemes,
value,
Messages.PREFERRED_STYLE_SCHEMES.INVALID,
);
this[propsSymbol].preferredStyleSchemes = value;
}
/** /**
* Allows setting a transitType property * Allows setting a transitType property
* for a boardingPass. * for a boardingPass.
@@ -316,6 +359,19 @@ export default class PKPass extends Bundle {
return this[propsSymbol][this.type].backFields; return this[propsSymbol][this.type].backFields;
} }
/**
* Allows accessing to new iOS 18
* event ticket additional fields
*
* @throws (automatically) if no valid pass.json
* has been parsed yet or, anyway, if current
* type is not "eventTicket".
*/
public get additionalInfoFields(): Schemas.Field[] {
return this[propsSymbol]["eventTicket"].additionalInfoFields;
}
/** /**
* Allows setting a pass type. * Allows setting a pass type.
* *
@@ -345,6 +401,7 @@ export default class PKPass extends Bundle {
*/ */
this[propsSymbol][this.type] = undefined; this[propsSymbol][this.type] = undefined;
this[propsSymbol].preferredStyleSchemes = undefined;
} }
const sharedKeysPool = new Set<string>(); const sharedKeysPool = new Set<string>();
@@ -376,6 +433,11 @@ export default class PKPass extends Bundle {
sharedKeysPool, sharedKeysPool,
Schemas.Field, Schemas.Field,
), ),
additionalInfoFields: new FieldsArray(
this,
sharedKeysPool,
Schemas.Field,
),
transitType: undefined, transitType: undefined,
}; };
} }
@@ -545,6 +607,7 @@ export default class PKPass extends Bundle {
auxiliaryFields = [], auxiliaryFields = [],
backFields = [], backFields = [],
transitType, transitType,
additionalInfoFields = [],
} = data[type] || {}; } = data[type] || {};
this.headerFields.push(...headerFields); this.headerFields.push(...headerFields);
@@ -556,6 +619,10 @@ export default class PKPass extends Bundle {
if (this.type === "boardingPass") { if (this.type === "boardingPass") {
this.transitType = transitType; this.transitType = transitType;
} }
if (this.type === "eventTicket") {
this.additionalInfoFields.push(...additionalInfoFields);
}
} }
} }

View File

@@ -1,6 +1,6 @@
import forge from "node-forge"; import forge from "node-forge";
import type * as Schemas from "./schemas"; import type * as Schemas from "./schemas";
import { Buffer } from "buffer"; import { Buffer } from "node:buffer";
/** /**
* Creates an hash for a buffer. Used by manifest * Creates an hash for a buffer. Used by manifest

View File

@@ -1,5 +1,5 @@
import { EOL } from "os"; import { EOL } from "node:os";
import { Buffer } from "buffer"; import { Buffer } from "node:buffer";
// ************************************ // // ************************************ //
// *** UTILS FOR PASS.STRINGS FILES *** // // *** UTILS FOR PASS.STRINGS FILES *** //

View File

@@ -1,8 +1,8 @@
import * as path from "path"; import * as path from "node:path";
import * as Utils from "./utils"; import * as Utils from "./utils";
import * as Messages from "./messages"; import * as Messages from "./messages";
import { promises as fs } from "fs"; import { promises as fs } from "node:fs";
import type { Buffer } from "buffer"; import type { Buffer } from "node:buffer";
/** /**
* Reads the model folder contents * Reads the model folder contents

View File

@@ -15,6 +15,15 @@ export const TRANSIT_TYPE = {
"Cannot set transitType because not compliant with Apple specifications. Refer to https://apple.co/3DHuAG4 for more - %s", "Cannot set transitType because not compliant with Apple specifications. Refer to https://apple.co/3DHuAG4 for more - %s",
} as const; } as const;
export const PREFERRED_STYLE_SCHEMES = {
UNEXPECTED_PASS_TYPE_SET:
"Cannot set preferredStyleSchemes on a pass with type different from eventTicket.",
UNEXPECTED_PASS_TYPE_GET:
"Cannot get preferredStyleSchemes on a pass with type different from eventTicket.",
INVALID:
"Cannot set preferredStyleSchemes because not compliant with Apple specifications - %s",
} as const;
export const PASS_TYPE = { export const PASS_TYPE = {
INVALID: INVALID:
"Cannot set type because not compliant with Apple specifications. Refer to https://apple.co/3aFpSfg for a list of valid props - %s", "Cannot set type because not compliant with Apple specifications. Refer to https://apple.co/3aFpSfg for a list of valid props - %s",

View File

@@ -1,4 +1,4 @@
import { Buffer } from "buffer"; import { Buffer } from "node:buffer";
import Joi from "joi"; import Joi from "joi";
export interface CertificatesSchema { export interface CertificatesSchema {

View File

@@ -42,7 +42,7 @@ export interface Field {
dateStyle?: PKDateStyleType; dateStyle?: PKDateStyleType;
ignoresTimeZone?: boolean; ignoresTimeZone?: boolean;
isRelative?: boolean; isRelative?: boolean;
timeStyle?: string; timeStyle?: PKDateStyleType;
currencyCode?: string; currencyCode?: string;
numberStyle?: PKNumberStyleType; numberStyle?: PKNumberStyleType;
} }

View File

@@ -19,6 +19,7 @@ export interface PassFields {
primaryFields: Field[]; primaryFields: Field[];
secondaryFields: Field[]; secondaryFields: Field[];
transitType?: TransitType; transitType?: TransitType;
additionalInfoFields?: Field[];
} }
export const PassFields = Joi.object<PassFields>().keys({ export const PassFields = Joi.object<PassFields>().keys({
@@ -28,4 +29,5 @@ export const PassFields = Joi.object<PassFields>().keys({
primaryFields: Joi.array().items(Field), primaryFields: Joi.array().items(Field),
secondaryFields: Joi.array().items(Field), secondaryFields: Joi.array().items(Field),
transitType: TransitType, transitType: TransitType,
additionalInfoFields: Joi.array().items(Field),
}); });

View File

@@ -1,4 +1,5 @@
import Joi from "joi"; import Joi from "joi";
import { RGB_HEX_COLOR_REGEX } from "./regexps";
/** /**
* For a better description of every single field, * For a better description of every single field,
@@ -39,6 +40,24 @@ declare namespace SemanticTagType {
seatIdentifier?: string; seatIdentifier?: string;
seatType?: string; seatType?: string;
seatDescription?: string; seatDescription?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
seatAisle?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
seatLevel?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
seatSectionColor?: string;
} }
interface WifiNetwork { interface WifiNetwork {
@@ -63,16 +82,34 @@ const PersonNameComponent =
phoneticRepresentation: Joi.string(), phoneticRepresentation: Joi.string(),
}); });
const seat = Joi.object<SemanticTagType.Seat>().keys({ const SeatSemantics = Joi.object<SemanticTagType.Seat>().keys({
seatSection: Joi.string(), seatSection: Joi.string(),
seatRow: Joi.string(), seatRow: Joi.string(),
seatNumber: Joi.string(), seatNumber: Joi.string(),
seatIdentifier: Joi.string(), seatIdentifier: Joi.string(),
seatType: Joi.string(), seatType: Joi.string(),
seatDescription: Joi.string(), seatDescription: Joi.string(),
/**
* Newly-introduced in iOS 18
* Used in poster event tickets
*/
seatAisle: Joi.string(),
/**
* Newly-introduced in iOS 18
* Used in poster event tickets
*/
seatLevel: Joi.string(),
/**
* For newly-introduced event tickets
* in iOS 18
*/
seatSectionColor: Joi.string().regex(RGB_HEX_COLOR_REGEX),
}); });
const location = Joi.object<SemanticTagType.Location>().keys({ const LocationSemantics = Joi.object<SemanticTagType.Location>().keys({
latitude: Joi.number().required(), latitude: Joi.number().required(),
longitude: Joi.number().required(), longitude: Joi.number().required(),
}); });
@@ -88,12 +125,51 @@ const WifiNetwork = Joi.object<SemanticTagType.WifiNetwork>().keys({
*/ */
export interface Semantics { export interface Semantics {
/**
* For newly-introduced event tickets
* in iOS 18
*/
admissionLevel?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
admissionLevelAbbreviation?: string;
airlineCode?: string; airlineCode?: string;
artistIDs?: string[]; artistIDs?: string[];
/**
* For newly-introduced event tickets
* in iOS 18
*/
albumIDs?: string[];
/**
* For newly-introduced event tickets
* in iOS 18
*/
airplay?: {
airPlayDeviceGroupToken: string;
}[];
/**
* For newly-introduced event tickets
* in iOS 18
*/
attendeeName?: string;
awayTeamAbbreviation?: string; awayTeamAbbreviation?: string;
awayTeamLocation?: string; awayTeamLocation?: string;
awayTeamName?: string; awayTeamName?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
additionalTicketAttributes?: string;
balance?: SemanticTagType.CurrencyAmount; balance?: SemanticTagType.CurrencyAmount;
boardingGroup?: string; boardingGroup?: string;
boardingSequenceNumber?: string; boardingSequenceNumber?: string;
@@ -122,7 +198,23 @@ export interface Semantics {
destinationTerminal?: string; destinationTerminal?: string;
duration?: number; duration?: number;
/**
* For newly-introduced event tickets
* in iOS 18
*/
entranceDescription?: string;
eventEndDate?: string; eventEndDate?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*
* This seem to exists but it is not
* known yet what it does...
*/
eventLiveMessage?: string;
eventName?: string; eventName?: string;
eventStartDate?: string; eventStartDate?: string;
eventType?: eventType?:
@@ -157,11 +249,23 @@ export interface Semantics {
performerNames?: string[]; performerNames?: string[];
priorityStatus?: string; priorityStatus?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
playlistIDs?: string[];
seats?: SemanticTagType.Seat[]; seats?: SemanticTagType.Seat[];
securityScreening?: string; securityScreening?: string;
silenceRequested?: boolean; silenceRequested?: boolean;
sportName?: string; sportName?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
tailgatingAllowed?: boolean;
totalPrice?: SemanticTagType.CurrencyAmount; totalPrice?: SemanticTagType.CurrencyAmount;
transitProvider?: string; transitProvider?: string;
transitStatus?: string; transitStatus?: string;
@@ -170,22 +274,120 @@ export interface Semantics {
vehicleName?: string; vehicleName?: string;
vehicleNumber?: string; vehicleNumber?: string;
vehicleType?: string; vehicleType?: string;
venueEntrance?: string; venueEntrance?: string;
venueLocation?: SemanticTagType.Location; venueLocation?: SemanticTagType.Location;
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueGatesOpenDate?: string;
venueName?: string; venueName?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueParkingLotsOpenDate?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueBoxOfficeOpenDate?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueDoorsOpenDate?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueFanZoneOpenDate?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueCloseDate?: string;
venuePhoneNumber?: string; venuePhoneNumber?: string;
venueRoom?: string; venueRoom?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueRegionName?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueEntranceGate?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueEntranceDoor?: string;
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueEntrancePortal?: string;
wifiAccess?: SemanticTagType.WifiNetwork[]; wifiAccess?: SemanticTagType.WifiNetwork[];
} }
export const Semantics = Joi.object<Semantics>().keys({ export const Semantics = Joi.object<Semantics>().keys({
/**
* For newly-introduced event tickets
* in iOS 18
*/
admissionLevel: Joi.string(),
/**
* For newly-introduced event tickets
* in iOS 18
*/
admissionLevelAbbreviation: Joi.string(),
airlineCode: Joi.string(), airlineCode: Joi.string(),
artistIDs: Joi.array().items(Joi.string()), artistIDs: Joi.array().items(Joi.string()),
/**
* For newly-introduced event tickets
* in iOS 18
*/
albumIDs: Joi.array().items(Joi.string()),
/**
* For newly-introduced event tickets
* in iOS 18
*/
airplay: Joi.array().items({
airplayDeviceGroupToken: Joi.string(),
}),
/**
* For newly-introduced event tickets
* in iOS 18
*/
attendeeName: Joi.string(),
awayTeamAbbreviation: Joi.string(), awayTeamAbbreviation: Joi.string(),
awayTeamLocation: Joi.string(), awayTeamLocation: Joi.string(),
awayTeamName: Joi.string(), awayTeamName: Joi.string(),
additionalTicketAttributes: Joi.string(),
balance: CurrencyAmount, balance: CurrencyAmount,
boardingGroup: Joi.string(), boardingGroup: Joi.string(),
boardingSequenceNumber: Joi.string(), boardingSequenceNumber: Joi.string(),
@@ -199,7 +401,7 @@ export const Semantics = Joi.object<Semantics>().keys({
departureAirportCode: Joi.string(), departureAirportCode: Joi.string(),
departureAirportName: Joi.string(), departureAirportName: Joi.string(),
departureGate: Joi.string(), departureGate: Joi.string(),
departureLocation: location, departureLocation: LocationSemantics,
departureLocationDescription: Joi.string(), departureLocationDescription: Joi.string(),
departurePlatform: Joi.string(), departurePlatform: Joi.string(),
departureStationName: Joi.string(), departureStationName: Joi.string(),
@@ -207,15 +409,31 @@ export const Semantics = Joi.object<Semantics>().keys({
destinationAirportCode: Joi.string(), destinationAirportCode: Joi.string(),
destinationAirportName: Joi.string(), destinationAirportName: Joi.string(),
destinationGate: Joi.string(), destinationGate: Joi.string(),
destinationLocation: location, destinationLocation: LocationSemantics,
destinationLocationDescription: Joi.string(), destinationLocationDescription: Joi.string(),
destinationPlatform: Joi.string(), destinationPlatform: Joi.string(),
destinationStationName: Joi.string(), destinationStationName: Joi.string(),
destinationTerminal: Joi.string(), destinationTerminal: Joi.string(),
duration: Joi.number(), duration: Joi.number(),
/**
* For newly-introduced event tickets
* in iOS 18
*/
entranceDescription: Joi.string(),
eventEndDate: Joi.string(), eventEndDate: Joi.string(),
eventName: Joi.string(), eventName: Joi.string(),
/**
* For newly-introduced event tickets
* in iOS 18
*
* This seem to exists but it is not
* known yet what it does...
*/
eventLiveMessage: Joi.string(),
eventStartDate: Joi.string(), eventStartDate: Joi.string(),
eventType: Joi.string().regex( eventType: Joi.string().regex(
/(PKEventTypeGeneric|PKEventTypeLivePerformance|PKEventTypeMovie|PKEventTypeSports|PKEventTypeConference|PKEventTypeConvention|PKEventTypeWorkshop|PKEventTypeSocialGathering)/, /(PKEventTypeGeneric|PKEventTypeLivePerformance|PKEventTypeMovie|PKEventTypeSports|PKEventTypeConference|PKEventTypeConvention|PKEventTypeWorkshop|PKEventTypeSocialGathering)/,
@@ -243,11 +461,15 @@ export const Semantics = Joi.object<Semantics>().keys({
performerNames: Joi.array().items(Joi.string()), performerNames: Joi.array().items(Joi.string()),
priorityStatus: Joi.string(), priorityStatus: Joi.string(),
seats: Joi.array().items(seat), playlistIDs: Joi.array().items(Joi.string()),
seats: Joi.array().items(SeatSemantics),
securityScreening: Joi.string(), securityScreening: Joi.string(),
silenceRequested: Joi.boolean(), silenceRequested: Joi.boolean(),
sportName: Joi.string(), sportName: Joi.string(),
tailgatingAllowed: Joi.boolean(),
totalPrice: CurrencyAmount, totalPrice: CurrencyAmount,
transitProvider: Joi.string(), transitProvider: Joi.string(),
transitStatus: Joi.string(), transitStatus: Joi.string(),
@@ -256,11 +478,74 @@ export const Semantics = Joi.object<Semantics>().keys({
vehicleName: Joi.string(), vehicleName: Joi.string(),
vehicleNumber: Joi.string(), vehicleNumber: Joi.string(),
vehicleType: Joi.string(), vehicleType: Joi.string(),
venueEntrance: Joi.string(), venueEntrance: Joi.string(),
venueLocation: location,
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueGatesOpenDate: Joi.string(),
venueLocation: LocationSemantics,
venueName: Joi.string(), venueName: Joi.string(),
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueParkingLotsOpenDate: Joi.string(),
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueBoxOfficeOpenDate: Joi.string(),
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueDoorsOpenDate: Joi.string(),
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueFanZoneOpenDate: Joi.string(),
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueCloseDate: Joi.string(),
venuePhoneNumber: Joi.string(), venuePhoneNumber: Joi.string(),
venueRoom: Joi.string(), venueRoom: Joi.string(),
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueRegionName: Joi.string(),
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueEntranceGate: Joi.string(),
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueEntranceDoor: Joi.string(),
/**
* For newly-introduced event tickets
* in iOS 18
*/
venueEntrancePortal: Joi.string(),
wifiAccess: Joi.array().items(WifiNetwork), wifiAccess: Joi.array().items(WifiNetwork),
}); });

View File

@@ -9,7 +9,7 @@ export * from "./Personalize";
export * from "./Certificates"; export * from "./Certificates";
import Joi from "joi"; import Joi from "joi";
import { Buffer } from "buffer"; import type { Buffer } from "node:buffer";
import { Barcode } from "./Barcode"; import { Barcode } from "./Barcode";
import { Location } from "./Location"; import { Location } from "./Location";
@@ -20,9 +20,33 @@ import { Semantics } from "./Semantics";
import { CertificatesSchema } from "./Certificates"; import { CertificatesSchema } from "./Certificates";
import * as Messages from "../messages"; import * as Messages from "../messages";
import { RGB_HEX_COLOR_REGEX, URL_REGEX } from "./regexps";
const RGB_COLOR_REGEX = export type PreferredStyleSchemes = ("posterEventTicket" | "eventTicket")[];
/rgb\(\s*(?:[01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\s*,\s*(?:[01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\s*,\s*(?:[01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\s*\)/;
export const PreferredStyleSchemes = Joi.array().items(
"posterEventTicket",
"eventTicket",
) satisfies Joi.Schema<PreferredStyleSchemes>;
/**
* For newly-introduced event tickets
* in iOS 18
*/
interface RelevantDate {
startDate: string;
endDate: string;
}
/**
* Minimum supported version: iOS 18
*/
const RelevantDate = Joi.object<RelevantDate>().keys({
startDate: Joi.string().required(),
endDate: Joi.string().required(),
});
export interface FileBuffers { export interface FileBuffers {
[key: string]: Buffer; [key: string]: Buffer;
@@ -57,6 +81,9 @@ export interface PassProps {
beacons?: Beacon[]; beacons?: Beacon[];
barcodes?: Barcode[]; barcodes?: Barcode[];
relevantDate?: string; relevantDate?: string;
relevantDates?: RelevantDate[];
expirationDate?: string; expirationDate?: string;
locations?: Location[]; locations?: Location[];
@@ -65,6 +92,144 @@ export interface PassProps {
coupon?: PassFields; coupon?: PassFields;
generic?: PassFields; generic?: PassFields;
storeCard?: PassFields; storeCard?: PassFields;
/**
* New field for iOS 18
* Event Ticket
*/
preferredStyleSchemes?: PreferredStyleSchemes;
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
bagPolicyURL?: string;
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
orderFoodURL?: string;
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
parkingInformationURL?: string;
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
directionsInformationURL?: string;
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
contactVenueEmail?: string;
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
contactVenuePhoneNumber?: string;
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
contactVenueWebsite?: string;
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
purchaseParkingURL?: string;
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
merchandiseURL?: string;
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
transitInformationURL?: string;
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
accessibilityURL?: string;
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
addOnURL?: string;
/**
* New field for iOS 18 Event Ticket.
* Will add a button among options near "share"
*/
transferURL?: string;
/**
* New field for iOS 18 Event Ticket.
* Will add a button among options near "share"
*/
sellURL?: string;
} }
/** /**
@@ -77,8 +242,10 @@ type PassMethodsProps =
| "beacons" | "beacons"
| "barcodes" | "barcodes"
| "relevantDate" | "relevantDate"
| "relevantDates"
| "expirationDate" | "expirationDate"
| "locations"; | "locations"
| "preferredStyleSchemes";
export type PassTypesProps = export type PassTypesProps =
| "boardingPass" | "boardingPass"
@@ -104,8 +271,10 @@ export const PassPropsFromMethods = Joi.object<PassPropsFromMethods>({
beacons: Joi.array().items(Beacon), beacons: Joi.array().items(Beacon),
barcodes: Joi.array().items(Barcode), barcodes: Joi.array().items(Barcode),
relevantDate: Joi.string().isoDate(), relevantDate: Joi.string().isoDate(),
relevantDates: Joi.array().items(RelevantDate),
expirationDate: Joi.string().isoDate(), expirationDate: Joi.string().isoDate(),
locations: Joi.array().items(Location), locations: Joi.array().items(Location),
preferredStyleSchemes: PreferredStyleSchemes,
}); });
export const PassKindsProps = Joi.object<PassKindsProps>({ export const PassKindsProps = Joi.object<PassKindsProps>({
@@ -136,15 +305,144 @@ export const OverridablePassProps = Joi.object<OverridablePassProps>({
suppressStripShine: Joi.boolean(), suppressStripShine: Joi.boolean(),
maxDistance: Joi.number().positive(), maxDistance: Joi.number().positive(),
authenticationToken: Joi.string().min(16), authenticationToken: Joi.string().min(16),
labelColor: Joi.string().regex(RGB_COLOR_REGEX), labelColor: Joi.string().regex(RGB_HEX_COLOR_REGEX),
backgroundColor: Joi.string().regex(RGB_COLOR_REGEX), backgroundColor: Joi.string().regex(RGB_HEX_COLOR_REGEX),
foregroundColor: Joi.string().regex(RGB_COLOR_REGEX), foregroundColor: Joi.string().regex(RGB_HEX_COLOR_REGEX),
associatedStoreIdentifiers: Joi.array().items(Joi.number()), associatedStoreIdentifiers: Joi.array().items(Joi.number()),
userInfo: Joi.alternatives(Joi.object().unknown(), Joi.array()), userInfo: Joi.alternatives(Joi.object().unknown(), Joi.array()),
// parsing url as set of words and nums followed by dots, optional port and any possible path after webServiceURL: Joi.string().regex(URL_REGEX),
webServiceURL: Joi.string().regex(
/https?:\/\/(?:[a-z0-9]+\.?)+(?::\d{2,})?(?:\/[\S]+)*/, /**
), * New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
bagPolicyURL: Joi.string().regex(URL_REGEX),
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
orderFoodURL: Joi.string().regex(URL_REGEX),
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
parkingInformationURL: Joi.string().regex(URL_REGEX),
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
directionsInformationURL: Joi.string(),
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
contactVenueEmail: Joi.string(),
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
contactVenuePhoneNumber: Joi.string(),
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
contactVenueWebsite: Joi.string(),
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
purchaseParkingURL: Joi.string(),
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
merchandiseURL: Joi.string(),
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
transitInformationURL: Joi.string(),
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
accessibilityURL: Joi.string(),
/**
* New field for iOS 18 Event Ticket.
* @domain event guide
*
* To show buttons in the event guide,
* at least two among those marked with
* "@domain event guide" must be used.
*/
addOnURL: Joi.string(),
/**
* New field for iOS 18 Event Ticket.
* Will add a button among options near "share"
*/
transferURL: Joi.string(),
/**
* New field for iOS 18 Event Ticket.
* Will add a button among options near "share"
*/
sellURL: Joi.string(),
}).with("webServiceURL", "authenticationToken"); }).with("webServiceURL", "authenticationToken");
export const PassProps = Joi.object< export const PassProps = Joi.object<
@@ -175,7 +473,7 @@ export const Template = Joi.object<Template>({
*/ */
export function assertValidity<T>( export function assertValidity<T>(
schema: Joi.ObjectSchema<T> | Joi.StringSchema, schema: Joi.ObjectSchema<T> | Joi.StringSchema | Joi.Schema<T>,
data: T, data: T,
customErrorMessage?: string, customErrorMessage?: string,
): void { ): void {

3
src/schemas/regexps.ts Normal file
View File

@@ -0,0 +1,3 @@
export const RGB_HEX_COLOR_REGEX =
/(?:\#[a-fA-F0-9]{3,6}|rgb\(\s*(?:[01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\s*,\s*(?:[01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\s*,\s*(?:[01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\s*\))/;
export const URL_REGEX = /https?:\/\/(?:[a-z0-9]+\.?)+(?::\d{2,})?(?:\/[\S]+)*/;

View File

@@ -1,10 +1,10 @@
{ {
"extends": "./tsconfig.json", "extends": "./tsconfig.json",
"compilerOptions": { "compilerOptions": {
"outDir": "lib", "outDir": "lib",
"declaration": true, "declaration": true,
"sourceMap": true, "inlineSourceMap": true,
"skipLibCheck": true "skipLibCheck": true
}, },
"include": ["src/**/*"] "include": ["src/**/*"]
} }

View File

@@ -1,11 +1,11 @@
{ {
"compilerOptions": { "compilerOptions": {
"module": "commonjs", "module": "commonjs",
"target": "es2018", "target": "es2018",
"esModuleInterop": true, "esModuleInterop": true,
"newLine": "LF", "newLine": "LF",
"importHelpers": true, "importHelpers": true,
"useUnknownInCatchVariables": true "useUnknownInCatchVariables": true
}, },
"exclude": ["node_modules/"] "exclude": ["node_modules/"]
} }