@peektravel/app-utilities 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -2426,41 +2426,21 @@ interface Waiver {
2426
2426
  declare function parseWaiverWebhook(payload: unknown, options?: AccessOptions): Waiver;
2427
2427
 
2428
2428
  /**
2429
- * Verifies an install-status webhook token and returns its clean claims.
2430
- *
2431
- * Validates the HMAC signature (with `secret`), the token expiry, the
2432
- * `"app_registry_v2"` issuer, and the `"Joken"` audience — the same checks as
2433
- * {@link PeekAccessService.verifyPeekAuthToken} — then maps the payload to
2434
- * {@link InstallWebhookClaims}, tolerating a `null` `user` (system events).
2435
- * Throws from the `jsonwebtoken` library on any verification failure, so callers
2436
- * can branch on the error kind:
2437
- *
2438
- * - `JsonWebTokenError` — signature invalid, wrong issuer/audience, or malformed
2439
- * - `TokenExpiredError` — past `exp`
2440
- * - `NotBeforeError` — before `nbf`
2441
- *
2442
- * @param token the raw JWT delivered by the install webhook (no `Bearer` prefix)
2443
- * @param secret the app's HMAC signing secret (the `jwtSecret` you construct
2444
- * `PeekAccessService` with)
2445
- * @throws {JsonWebTokenError} signature invalid or token malformed
2446
- * @throws {TokenExpiredError} token has expired
2447
- * @throws {NotBeforeError} token not yet valid
2448
- */
2449
- declare function verifyInstallWebhook(token: string, secret: string): InstallWebhookClaims;
2450
-
2451
- /**
2452
- * The clean data model for an app-store **install lifecycle event** — the JSON
2453
- * webhook body Peek's app registry POSTs when an app is installed, uninstalled,
2454
- * or updated.
2455
- *
2456
- * This is the sibling of the signed-JWT channel behind `verifyInstallWebhook`
2457
- * (see `models/peek/auth-token.ts`). The two carry the same event; this one is
2458
- * the richer of the pair, because only it reports the account **name**,
2459
- * **platform**, and **test** flag.
2429
+ * The clean data model for an app-store **install lifecycle event** — the single
2430
+ * flat object `parseInstallWebhook` returns for the webhook Peek's app registry
2431
+ * POSTs when an app is installed, uninstalled, or updated.
2432
+ *
2433
+ * That delivery carries two things at once: a **signed `app_registry_v2` JWT**
2434
+ * (the security boundary) and a plain **JSON body** (the enrichment channel).
2435
+ * `parseInstallWebhook` verifies the JWT and merges both into this shape, so a
2436
+ * consumer sees one clean, flat, JSON-safe record instead of two differently
2437
+ * shaped payloads. The verified JWT is authoritative for the fields it carries
2438
+ * (`installId`, `accountId`, `status`, `displayVersion`, `user`); the JSON body
2439
+ * is the only source of `accountName`, `platform`, and `isTest`.
2460
2440
  *
2461
2441
  * It is also the single origin of the account id in the whole system: no
2462
- * GraphQL read and no peek-auth token returns it, so whatever a consumer
2463
- * persists from this event is all it will ever have.
2442
+ * GraphQL read returns it, so whatever a consumer persists from this event is
2443
+ * all it will ever have.
2464
2444
  */
2465
2445
 
2466
2446
  /**
@@ -2471,21 +2451,27 @@ declare const INSTALL_STATUSES: readonly ["installed", "uninstalled", "update_in
2471
2451
  /** Install lifecycle status. */
2472
2452
  type InstallStatus = (typeof INSTALL_STATUSES)[number];
2473
2453
  /**
2474
- * Who an install is — the core account data, in one object.
2454
+ * A verified install lifecycle event — what happened, to which install, and who
2455
+ * (if anyone) triggered it — as one flat, JSON-safe record.
2475
2456
  *
2476
- * Flat and JSON-safe by design: it round-trips through a datastore unchanged,
2477
- * so the same shape the parser returns is the shape a consumer persists and
2478
- * later rehydrates. There is deliberately no second representation.
2457
+ * This is the shape `parseInstallWebhook` returns. It is flat by design: it
2458
+ * round-trips through a datastore unchanged, so the same shape the parser
2459
+ * returns is the shape a consumer persists and later rehydrates. There is
2460
+ * deliberately no nested identity object and no second representation.
2461
+ *
2462
+ * Fields sourced from the **verified JWT** (`installId`, `accountId`, `status`,
2463
+ * `rawStatus`, `displayVersion`, `user`) are trustworthy; the enrichment fields
2464
+ * (`accountName`, `platform`, `isTest`) come from the unauthenticated JSON body.
2479
2465
  */
2480
- interface InstallIdentity {
2481
- /** Install ID — Peek-assigned UUID. Also the subject of every peek-auth JWT. */
2466
+ interface InstallWebhook {
2467
+ /** Install ID — Peek-assigned UUID. Also the JWT subject (`sub`). */
2482
2468
  installId: string;
2483
2469
  /**
2484
2470
  * Stable Peek account id — the same value Peek calls the **partner ID**.
2485
2471
  * This event is its only source; it cannot be re-fetched later.
2486
2472
  */
2487
2473
  accountId: string;
2488
- /** Partner display name. */
2474
+ /** Partner display name. `""` when the JSON body omits it. */
2489
2475
  accountName: string;
2490
2476
  /**
2491
2477
  * Which Peek platform owns the install, or `null` when the registry reported
@@ -2503,9 +2489,6 @@ interface InstallIdentity {
2503
2489
  platform: PeekPlatform | null;
2504
2490
  /** Whether this is a test account. Defaults to `false` when not reported. */
2505
2491
  isTest: boolean;
2506
- }
2507
- /** A parsed install lifecycle event: what happened, to which install. */
2508
- interface InstallEvent {
2509
2492
  /**
2510
2493
  * The lifecycle status, or `null` when the registry sent a status this
2511
2494
  * version of the package does not know. Handle `null` explicitly — treating
@@ -2516,59 +2499,87 @@ interface InstallEvent {
2516
2499
  rawStatus: string;
2517
2500
  /** App display version at the time of the event. */
2518
2501
  displayVersion: string;
2519
- /** The install's core account data. */
2520
- identity: InstallIdentity;
2502
+ /**
2503
+ * The user that triggered the event, or `null` for system-initiated events
2504
+ * (install lifecycle changes are often system-initiated). From the JWT.
2505
+ */
2506
+ user: PeekAuthTokenUser | null;
2521
2507
  }
2522
2508
 
2523
2509
  /**
2524
- * Public webhook surface for the **install-event** channel — the JSON body
2525
- * Peek's app registry POSTs on install, uninstall, and update.
2510
+ * Verifies an install-status webhook token and merges it with the delivered
2511
+ * JSON body into one flat {@link InstallWebhook}.
2526
2512
  *
2527
- * This is the second of the two install channels. Its sibling,
2528
- * `verifyInstallWebhook`, receives the same lifecycle event as a signed
2529
- * `app_registry_v2` JWT; that payload authenticates itself but carries only the
2530
- * account **id**. This one carries the account's name, platform, and test flag
2531
- * as well, which makes it the authoritative source of {@link InstallIdentity}.
2513
+ * The **verified token** is the authoritative source of `installId`,
2514
+ * `accountId`, `status`, `displayVersion`, and `user`. The **JSON body** — which
2515
+ * is not signed and so is trusted only for fields the token cannot carry —
2516
+ * supplies `accountName`, `platform`, and `isTest`. A missing or malformed body
2517
+ * degrades those three to `""`/`null`/`false`; it never overrides an
2518
+ * authenticated field and never throws.
2532
2519
  *
2533
- * Like the booking and waiver parsers, this is a pure transform — no auth, no
2534
- * network, no client — so it is a standalone function rather than a method on
2535
- * `PeekAccessService`. The receiver holds no gateway credentials for an install
2536
- * it has not recorded yet, and an uninstall describes one being torn down.
2537
- *
2538
- * **It never throws.** The sender is first-party and the payload contract is
2539
- * Peek's own, so defending against malformed input would be defending against a
2540
- * case that does not arise. What the parser does guard is the contract
2541
- * *growing*: an unrecognised `status` or `platform` surfaces as `null` rather
2542
- * than being coerced into a known value, so a newer registry can never be
2543
- * silently mistaken for an older one.
2544
- */
2545
-
2546
- /**
2547
- * Parses a delivered install-event webhook body into a clean
2548
- * {@link InstallEvent}.
2520
+ * Signature verification is strict: the HMAC signature (with `secret`), the
2521
+ * token expiry, the `"app_registry_v2"` issuer, and the `"Joken"` audience are
2522
+ * all checked — the same checks as {@link PeekAccessService.verifyPeekAuthToken}.
2523
+ * Any failure throws from the `jsonwebtoken` library, so callers can branch on
2524
+ * the error kind:
2549
2525
  *
2550
- * Accepts the raw request body, or a JSON string, which is parsed first.
2551
- * Missing fields map to `""`/`false`; an unknown `status` or `platform` maps to
2552
- * `null` with the wire value preserved on {@link InstallEvent.rawStatus}.
2526
+ * - `JsonWebTokenError` — signature invalid, wrong issuer/audience, or malformed
2527
+ * - `TokenExpiredError` — past `exp`
2528
+ * - `NotBeforeError` — before `nbf`
2553
2529
  *
2554
- * Handle a `null` `status` explicitly. Peek treats any 2xx as delivered and
2555
- * will not redeliver, so responding successfully to a status the package does
2556
- * not recognise silently drops a lifecycle transition:
2530
+ * An unrecognised `status` or `platform` maps to `null` (with the wire status
2531
+ * preserved on {@link InstallWebhook.rawStatus}) rather than being coerced —
2532
+ * Peek treats any 2xx as delivered and does not redeliver, so handle a `null`
2533
+ * `status` explicitly:
2557
2534
  *
2558
2535
  * ```ts
2559
- * const event = parseInstallEvent(req.body);
2536
+ * const event = parseInstallWebhook(token, req.body, secret);
2560
2537
  * switch (event.status) {
2561
- * case "installed": return provision(event.identity, event.displayVersion);
2562
- * case "uninstalled": return deprovision(event.identity);
2563
- * case "update_installed": return recordVersion(event.identity, event.displayVersion);
2538
+ * case "installed": return provision(event);
2539
+ * case "uninstalled": return deprovision(event);
2540
+ * case "update_installed": return recordVersion(event);
2564
2541
  * default: throw new Error(`unknown install status: ${event.rawStatus}`);
2565
2542
  * }
2566
2543
  * ```
2567
2544
  *
2568
- * Persist `event.identity` as a unit: it is flat and JSON-safe, and its
2569
- * `accountId` (the partner ID) has no other source in the system.
2545
+ * @param token the raw JWT delivered by the install webhook (no `Bearer` prefix)
2546
+ * @param body the delivered JSON body (raw object or a JSON string)
2547
+ * @param secret the app's HMAC signing secret (the `jwtSecret` you construct
2548
+ * `PeekAccessService` with)
2549
+ * @throws {JsonWebTokenError} signature invalid or token malformed
2550
+ * @throws {TokenExpiredError} token has expired
2551
+ * @throws {NotBeforeError} token not yet valid
2570
2552
  */
2571
- declare function parseInstallEvent(payload: unknown): InstallEvent;
2553
+ declare function parseInstallWebhook(token: string, body: unknown, secret: string): InstallWebhook;
2554
+ /**
2555
+ * Verifies an install-status webhook token and returns its clean claims.
2556
+ *
2557
+ * @deprecated Use {@link parseInstallWebhook} instead. This verifier reads only
2558
+ * the signed token, so it cannot report the account **name**, **platform**, or
2559
+ * **test** flag that the webhook's JSON body also carries — the caller was left
2560
+ * to parse that body separately and stitch the two shapes together.
2561
+ * `parseInstallWebhook(token, body, secret)` verifies the same token and returns
2562
+ * one flat {@link InstallWebhook} with every field. This function remains only
2563
+ * for backwards compatibility.
2564
+ *
2565
+ * Validates the HMAC signature (with `secret`), the token expiry, the
2566
+ * `"app_registry_v2"` issuer, and the `"Joken"` audience — the same checks as
2567
+ * {@link PeekAccessService.verifyPeekAuthToken} — then maps the payload to
2568
+ * {@link InstallWebhookClaims}, tolerating a `null` `user` (system events).
2569
+ * Throws from the `jsonwebtoken` library on any verification failure:
2570
+ *
2571
+ * - `JsonWebTokenError` — signature invalid, wrong issuer/audience, or malformed
2572
+ * - `TokenExpiredError` — past `exp`
2573
+ * - `NotBeforeError` — before `nbf`
2574
+ *
2575
+ * @param token the raw JWT delivered by the install webhook (no `Bearer` prefix)
2576
+ * @param secret the app's HMAC signing secret (the `jwtSecret` you construct
2577
+ * `PeekAccessService` with)
2578
+ * @throws {JsonWebTokenError} signature invalid or token malformed
2579
+ * @throws {TokenExpiredError} token has expired
2580
+ * @throws {NotBeforeError} token not yet valid
2581
+ */
2582
+ declare function verifyInstallWebhook(token: string, secret: string): InstallWebhookClaims;
2572
2583
 
2573
2584
  /**
2574
2585
  * Typed errors thrown by the package. Each mirrors a failure mode of the Peek
@@ -2681,4 +2692,4 @@ declare class AcmeApiError extends Error {
2681
2692
  constructor(statusCode: number, body: unknown, message?: string);
2682
2693
  }
2683
2694
 
2684
- export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, type AccessOptions, type AccountUser, AccountUserService, type AccountUserServiceOptions, AcmeAccessService, type AcmeAccessServiceConfig, type AcmeActivity, type AcmeActivityTicket, AcmeApiError, AcmeProductService, type Activity, type ActivityOverrides, type ActivityTicket, type AddAddonInput, AdminAccountRequiredError, type Agent, type AssignGuideResult, type AssignedActivity, type AssignedResource, type Availability, AvailabilityService, type AvailabilityTime, type AvailabilityTimesQuery, type Booking, type BookingAddon, type BookingAddonMoney, type BookingAddonOption, type BookingAddons, type BookingAddonsMutationResult, type BookingPaymentsOnFile, type BookingReadOptions, type BookingSearchBy, BookingService, type BookingServiceOptions, type BookingTimeRangeSearch, type CancelBookingResult, type Channel, type ClearOverridesInput, CngAccessService, type CngAccessServiceConfig, CngApiError, CngProductService, type CreateBookingGuest, type CreateBookingInput, type CreateBookingTicket, type CreateEngineInput, type CreatePromoCodeInput, type CreatedBooking, type CreatedPricingEngine, type CreatedPromoCode, type CustomQuestionAnswer, type DailyNote, DailyNoteService, type Duration, type Guest, type GuestMetadata, type Guide, type GuideAssignment, INSTALL_STATUSES, type InstallEvent, type InstallIdentity, type InstallStatus, type InstallWebhookAccount, type InstallWebhookClaims, InvalidPeekTokenError, type InvoiceLinkResult, type Logger, type MakePaymentInput, type MakePaymentResult, type Membership, type MembershipPurchaseInput, MembershipService, type NoteMode, type Payment, type PaymentSource, PeekAccessService, type PeekAccessServiceConfig, type PeekAuthTokenClaims, type PeekAuthTokenUser, PeekGraphQLError, PeekHttpError, type PeekPlatform, PiiAccessDisabledError, type Price, type PricingActivityContext, type PricingEngine, type PricingMoney, type PricingNamedRef, type PricingOverride, type PricingOverrideFilter, PricingService, type Product, type ProductMeetingLocation, ProductService, type ProductServiceOptions, type ProductTicket, type PromoCode, type PromoCodeFixedAmount, PromoCodeService, type PromoCodeServiceOptions, type PurchasedMembership, RENTAL_PRODUCT_TYPE, RateLimitError, type RefundInput, type RefundResult, ResellerService, type ResolvedOverride, type ResolvedPricingOverride, type ResolvedResourceOption, type Resource, type ResourceOptionOverride, type ResourceOptionQuantity, type ResourcePool, type ResourcePoolAccountUser, type ResourcePoolAssignment, type ResourcePoolMode, ResourcePoolService, type Review, ReviewService, type Ticket, type Timeslot, type TimeslotFilter, TimeslotService, type UpdateEngineInput, type UpdateTimeslotResult, type UpsertOverridesInput, type UpsertOverridesResult, type Waiver, noopLogger, parseBookingWebhook, parseInstallEvent, parseWaiverWebhook, verifyInstallWebhook };
2695
+ export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, type AccessOptions, type AccountUser, AccountUserService, type AccountUserServiceOptions, AcmeAccessService, type AcmeAccessServiceConfig, type AcmeActivity, type AcmeActivityTicket, AcmeApiError, AcmeProductService, type Activity, type ActivityOverrides, type ActivityTicket, type AddAddonInput, AdminAccountRequiredError, type Agent, type AssignGuideResult, type AssignedActivity, type AssignedResource, type Availability, AvailabilityService, type AvailabilityTime, type AvailabilityTimesQuery, type Booking, type BookingAddon, type BookingAddonMoney, type BookingAddonOption, type BookingAddons, type BookingAddonsMutationResult, type BookingPaymentsOnFile, type BookingReadOptions, type BookingSearchBy, BookingService, type BookingServiceOptions, type BookingTimeRangeSearch, type CancelBookingResult, type Channel, type ClearOverridesInput, CngAccessService, type CngAccessServiceConfig, CngApiError, CngProductService, type CreateBookingGuest, type CreateBookingInput, type CreateBookingTicket, type CreateEngineInput, type CreatePromoCodeInput, type CreatedBooking, type CreatedPricingEngine, type CreatedPromoCode, type CustomQuestionAnswer, type DailyNote, DailyNoteService, type Duration, type Guest, type GuestMetadata, type Guide, type GuideAssignment, INSTALL_STATUSES, type InstallStatus, type InstallWebhook, type InstallWebhookAccount, type InstallWebhookClaims, InvalidPeekTokenError, type InvoiceLinkResult, type Logger, type MakePaymentInput, type MakePaymentResult, type Membership, type MembershipPurchaseInput, MembershipService, type NoteMode, type Payment, type PaymentSource, PeekAccessService, type PeekAccessServiceConfig, type PeekAuthTokenClaims, type PeekAuthTokenUser, PeekGraphQLError, PeekHttpError, type PeekPlatform, PiiAccessDisabledError, type Price, type PricingActivityContext, type PricingEngine, type PricingMoney, type PricingNamedRef, type PricingOverride, type PricingOverrideFilter, PricingService, type Product, type ProductMeetingLocation, ProductService, type ProductServiceOptions, type ProductTicket, type PromoCode, type PromoCodeFixedAmount, PromoCodeService, type PromoCodeServiceOptions, type PurchasedMembership, RENTAL_PRODUCT_TYPE, RateLimitError, type RefundInput, type RefundResult, ResellerService, type ResolvedOverride, type ResolvedPricingOverride, type ResolvedResourceOption, type Resource, type ResourceOptionOverride, type ResourceOptionQuantity, type ResourcePool, type ResourcePoolAccountUser, type ResourcePoolAssignment, type ResourcePoolMode, ResourcePoolService, type Review, ReviewService, type Ticket, type Timeslot, type TimeslotFilter, TimeslotService, type UpdateEngineInput, type UpdateTimeslotResult, type UpsertOverridesInput, type UpsertOverridesResult, type Waiver, noopLogger, parseBookingWebhook, parseInstallWebhook, parseWaiverWebhook, verifyInstallWebhook };
package/dist/index.d.ts CHANGED
@@ -2426,41 +2426,21 @@ interface Waiver {
2426
2426
  declare function parseWaiverWebhook(payload: unknown, options?: AccessOptions): Waiver;
2427
2427
 
2428
2428
  /**
2429
- * Verifies an install-status webhook token and returns its clean claims.
2430
- *
2431
- * Validates the HMAC signature (with `secret`), the token expiry, the
2432
- * `"app_registry_v2"` issuer, and the `"Joken"` audience — the same checks as
2433
- * {@link PeekAccessService.verifyPeekAuthToken} — then maps the payload to
2434
- * {@link InstallWebhookClaims}, tolerating a `null` `user` (system events).
2435
- * Throws from the `jsonwebtoken` library on any verification failure, so callers
2436
- * can branch on the error kind:
2437
- *
2438
- * - `JsonWebTokenError` — signature invalid, wrong issuer/audience, or malformed
2439
- * - `TokenExpiredError` — past `exp`
2440
- * - `NotBeforeError` — before `nbf`
2441
- *
2442
- * @param token the raw JWT delivered by the install webhook (no `Bearer` prefix)
2443
- * @param secret the app's HMAC signing secret (the `jwtSecret` you construct
2444
- * `PeekAccessService` with)
2445
- * @throws {JsonWebTokenError} signature invalid or token malformed
2446
- * @throws {TokenExpiredError} token has expired
2447
- * @throws {NotBeforeError} token not yet valid
2448
- */
2449
- declare function verifyInstallWebhook(token: string, secret: string): InstallWebhookClaims;
2450
-
2451
- /**
2452
- * The clean data model for an app-store **install lifecycle event** — the JSON
2453
- * webhook body Peek's app registry POSTs when an app is installed, uninstalled,
2454
- * or updated.
2455
- *
2456
- * This is the sibling of the signed-JWT channel behind `verifyInstallWebhook`
2457
- * (see `models/peek/auth-token.ts`). The two carry the same event; this one is
2458
- * the richer of the pair, because only it reports the account **name**,
2459
- * **platform**, and **test** flag.
2429
+ * The clean data model for an app-store **install lifecycle event** — the single
2430
+ * flat object `parseInstallWebhook` returns for the webhook Peek's app registry
2431
+ * POSTs when an app is installed, uninstalled, or updated.
2432
+ *
2433
+ * That delivery carries two things at once: a **signed `app_registry_v2` JWT**
2434
+ * (the security boundary) and a plain **JSON body** (the enrichment channel).
2435
+ * `parseInstallWebhook` verifies the JWT and merges both into this shape, so a
2436
+ * consumer sees one clean, flat, JSON-safe record instead of two differently
2437
+ * shaped payloads. The verified JWT is authoritative for the fields it carries
2438
+ * (`installId`, `accountId`, `status`, `displayVersion`, `user`); the JSON body
2439
+ * is the only source of `accountName`, `platform`, and `isTest`.
2460
2440
  *
2461
2441
  * It is also the single origin of the account id in the whole system: no
2462
- * GraphQL read and no peek-auth token returns it, so whatever a consumer
2463
- * persists from this event is all it will ever have.
2442
+ * GraphQL read returns it, so whatever a consumer persists from this event is
2443
+ * all it will ever have.
2464
2444
  */
2465
2445
 
2466
2446
  /**
@@ -2471,21 +2451,27 @@ declare const INSTALL_STATUSES: readonly ["installed", "uninstalled", "update_in
2471
2451
  /** Install lifecycle status. */
2472
2452
  type InstallStatus = (typeof INSTALL_STATUSES)[number];
2473
2453
  /**
2474
- * Who an install is — the core account data, in one object.
2454
+ * A verified install lifecycle event — what happened, to which install, and who
2455
+ * (if anyone) triggered it — as one flat, JSON-safe record.
2475
2456
  *
2476
- * Flat and JSON-safe by design: it round-trips through a datastore unchanged,
2477
- * so the same shape the parser returns is the shape a consumer persists and
2478
- * later rehydrates. There is deliberately no second representation.
2457
+ * This is the shape `parseInstallWebhook` returns. It is flat by design: it
2458
+ * round-trips through a datastore unchanged, so the same shape the parser
2459
+ * returns is the shape a consumer persists and later rehydrates. There is
2460
+ * deliberately no nested identity object and no second representation.
2461
+ *
2462
+ * Fields sourced from the **verified JWT** (`installId`, `accountId`, `status`,
2463
+ * `rawStatus`, `displayVersion`, `user`) are trustworthy; the enrichment fields
2464
+ * (`accountName`, `platform`, `isTest`) come from the unauthenticated JSON body.
2479
2465
  */
2480
- interface InstallIdentity {
2481
- /** Install ID — Peek-assigned UUID. Also the subject of every peek-auth JWT. */
2466
+ interface InstallWebhook {
2467
+ /** Install ID — Peek-assigned UUID. Also the JWT subject (`sub`). */
2482
2468
  installId: string;
2483
2469
  /**
2484
2470
  * Stable Peek account id — the same value Peek calls the **partner ID**.
2485
2471
  * This event is its only source; it cannot be re-fetched later.
2486
2472
  */
2487
2473
  accountId: string;
2488
- /** Partner display name. */
2474
+ /** Partner display name. `""` when the JSON body omits it. */
2489
2475
  accountName: string;
2490
2476
  /**
2491
2477
  * Which Peek platform owns the install, or `null` when the registry reported
@@ -2503,9 +2489,6 @@ interface InstallIdentity {
2503
2489
  platform: PeekPlatform | null;
2504
2490
  /** Whether this is a test account. Defaults to `false` when not reported. */
2505
2491
  isTest: boolean;
2506
- }
2507
- /** A parsed install lifecycle event: what happened, to which install. */
2508
- interface InstallEvent {
2509
2492
  /**
2510
2493
  * The lifecycle status, or `null` when the registry sent a status this
2511
2494
  * version of the package does not know. Handle `null` explicitly — treating
@@ -2516,59 +2499,87 @@ interface InstallEvent {
2516
2499
  rawStatus: string;
2517
2500
  /** App display version at the time of the event. */
2518
2501
  displayVersion: string;
2519
- /** The install's core account data. */
2520
- identity: InstallIdentity;
2502
+ /**
2503
+ * The user that triggered the event, or `null` for system-initiated events
2504
+ * (install lifecycle changes are often system-initiated). From the JWT.
2505
+ */
2506
+ user: PeekAuthTokenUser | null;
2521
2507
  }
2522
2508
 
2523
2509
  /**
2524
- * Public webhook surface for the **install-event** channel — the JSON body
2525
- * Peek's app registry POSTs on install, uninstall, and update.
2510
+ * Verifies an install-status webhook token and merges it with the delivered
2511
+ * JSON body into one flat {@link InstallWebhook}.
2526
2512
  *
2527
- * This is the second of the two install channels. Its sibling,
2528
- * `verifyInstallWebhook`, receives the same lifecycle event as a signed
2529
- * `app_registry_v2` JWT; that payload authenticates itself but carries only the
2530
- * account **id**. This one carries the account's name, platform, and test flag
2531
- * as well, which makes it the authoritative source of {@link InstallIdentity}.
2513
+ * The **verified token** is the authoritative source of `installId`,
2514
+ * `accountId`, `status`, `displayVersion`, and `user`. The **JSON body** — which
2515
+ * is not signed and so is trusted only for fields the token cannot carry —
2516
+ * supplies `accountName`, `platform`, and `isTest`. A missing or malformed body
2517
+ * degrades those three to `""`/`null`/`false`; it never overrides an
2518
+ * authenticated field and never throws.
2532
2519
  *
2533
- * Like the booking and waiver parsers, this is a pure transform — no auth, no
2534
- * network, no client — so it is a standalone function rather than a method on
2535
- * `PeekAccessService`. The receiver holds no gateway credentials for an install
2536
- * it has not recorded yet, and an uninstall describes one being torn down.
2537
- *
2538
- * **It never throws.** The sender is first-party and the payload contract is
2539
- * Peek's own, so defending against malformed input would be defending against a
2540
- * case that does not arise. What the parser does guard is the contract
2541
- * *growing*: an unrecognised `status` or `platform` surfaces as `null` rather
2542
- * than being coerced into a known value, so a newer registry can never be
2543
- * silently mistaken for an older one.
2544
- */
2545
-
2546
- /**
2547
- * Parses a delivered install-event webhook body into a clean
2548
- * {@link InstallEvent}.
2520
+ * Signature verification is strict: the HMAC signature (with `secret`), the
2521
+ * token expiry, the `"app_registry_v2"` issuer, and the `"Joken"` audience are
2522
+ * all checked — the same checks as {@link PeekAccessService.verifyPeekAuthToken}.
2523
+ * Any failure throws from the `jsonwebtoken` library, so callers can branch on
2524
+ * the error kind:
2549
2525
  *
2550
- * Accepts the raw request body, or a JSON string, which is parsed first.
2551
- * Missing fields map to `""`/`false`; an unknown `status` or `platform` maps to
2552
- * `null` with the wire value preserved on {@link InstallEvent.rawStatus}.
2526
+ * - `JsonWebTokenError` — signature invalid, wrong issuer/audience, or malformed
2527
+ * - `TokenExpiredError` — past `exp`
2528
+ * - `NotBeforeError` — before `nbf`
2553
2529
  *
2554
- * Handle a `null` `status` explicitly. Peek treats any 2xx as delivered and
2555
- * will not redeliver, so responding successfully to a status the package does
2556
- * not recognise silently drops a lifecycle transition:
2530
+ * An unrecognised `status` or `platform` maps to `null` (with the wire status
2531
+ * preserved on {@link InstallWebhook.rawStatus}) rather than being coerced —
2532
+ * Peek treats any 2xx as delivered and does not redeliver, so handle a `null`
2533
+ * `status` explicitly:
2557
2534
  *
2558
2535
  * ```ts
2559
- * const event = parseInstallEvent(req.body);
2536
+ * const event = parseInstallWebhook(token, req.body, secret);
2560
2537
  * switch (event.status) {
2561
- * case "installed": return provision(event.identity, event.displayVersion);
2562
- * case "uninstalled": return deprovision(event.identity);
2563
- * case "update_installed": return recordVersion(event.identity, event.displayVersion);
2538
+ * case "installed": return provision(event);
2539
+ * case "uninstalled": return deprovision(event);
2540
+ * case "update_installed": return recordVersion(event);
2564
2541
  * default: throw new Error(`unknown install status: ${event.rawStatus}`);
2565
2542
  * }
2566
2543
  * ```
2567
2544
  *
2568
- * Persist `event.identity` as a unit: it is flat and JSON-safe, and its
2569
- * `accountId` (the partner ID) has no other source in the system.
2545
+ * @param token the raw JWT delivered by the install webhook (no `Bearer` prefix)
2546
+ * @param body the delivered JSON body (raw object or a JSON string)
2547
+ * @param secret the app's HMAC signing secret (the `jwtSecret` you construct
2548
+ * `PeekAccessService` with)
2549
+ * @throws {JsonWebTokenError} signature invalid or token malformed
2550
+ * @throws {TokenExpiredError} token has expired
2551
+ * @throws {NotBeforeError} token not yet valid
2570
2552
  */
2571
- declare function parseInstallEvent(payload: unknown): InstallEvent;
2553
+ declare function parseInstallWebhook(token: string, body: unknown, secret: string): InstallWebhook;
2554
+ /**
2555
+ * Verifies an install-status webhook token and returns its clean claims.
2556
+ *
2557
+ * @deprecated Use {@link parseInstallWebhook} instead. This verifier reads only
2558
+ * the signed token, so it cannot report the account **name**, **platform**, or
2559
+ * **test** flag that the webhook's JSON body also carries — the caller was left
2560
+ * to parse that body separately and stitch the two shapes together.
2561
+ * `parseInstallWebhook(token, body, secret)` verifies the same token and returns
2562
+ * one flat {@link InstallWebhook} with every field. This function remains only
2563
+ * for backwards compatibility.
2564
+ *
2565
+ * Validates the HMAC signature (with `secret`), the token expiry, the
2566
+ * `"app_registry_v2"` issuer, and the `"Joken"` audience — the same checks as
2567
+ * {@link PeekAccessService.verifyPeekAuthToken} — then maps the payload to
2568
+ * {@link InstallWebhookClaims}, tolerating a `null` `user` (system events).
2569
+ * Throws from the `jsonwebtoken` library on any verification failure:
2570
+ *
2571
+ * - `JsonWebTokenError` — signature invalid, wrong issuer/audience, or malformed
2572
+ * - `TokenExpiredError` — past `exp`
2573
+ * - `NotBeforeError` — before `nbf`
2574
+ *
2575
+ * @param token the raw JWT delivered by the install webhook (no `Bearer` prefix)
2576
+ * @param secret the app's HMAC signing secret (the `jwtSecret` you construct
2577
+ * `PeekAccessService` with)
2578
+ * @throws {JsonWebTokenError} signature invalid or token malformed
2579
+ * @throws {TokenExpiredError} token has expired
2580
+ * @throws {NotBeforeError} token not yet valid
2581
+ */
2582
+ declare function verifyInstallWebhook(token: string, secret: string): InstallWebhookClaims;
2572
2583
 
2573
2584
  /**
2574
2585
  * Typed errors thrown by the package. Each mirrors a failure mode of the Peek
@@ -2681,4 +2692,4 @@ declare class AcmeApiError extends Error {
2681
2692
  constructor(statusCode: number, body: unknown, message?: string);
2682
2693
  }
2683
2694
 
2684
- export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, type AccessOptions, type AccountUser, AccountUserService, type AccountUserServiceOptions, AcmeAccessService, type AcmeAccessServiceConfig, type AcmeActivity, type AcmeActivityTicket, AcmeApiError, AcmeProductService, type Activity, type ActivityOverrides, type ActivityTicket, type AddAddonInput, AdminAccountRequiredError, type Agent, type AssignGuideResult, type AssignedActivity, type AssignedResource, type Availability, AvailabilityService, type AvailabilityTime, type AvailabilityTimesQuery, type Booking, type BookingAddon, type BookingAddonMoney, type BookingAddonOption, type BookingAddons, type BookingAddonsMutationResult, type BookingPaymentsOnFile, type BookingReadOptions, type BookingSearchBy, BookingService, type BookingServiceOptions, type BookingTimeRangeSearch, type CancelBookingResult, type Channel, type ClearOverridesInput, CngAccessService, type CngAccessServiceConfig, CngApiError, CngProductService, type CreateBookingGuest, type CreateBookingInput, type CreateBookingTicket, type CreateEngineInput, type CreatePromoCodeInput, type CreatedBooking, type CreatedPricingEngine, type CreatedPromoCode, type CustomQuestionAnswer, type DailyNote, DailyNoteService, type Duration, type Guest, type GuestMetadata, type Guide, type GuideAssignment, INSTALL_STATUSES, type InstallEvent, type InstallIdentity, type InstallStatus, type InstallWebhookAccount, type InstallWebhookClaims, InvalidPeekTokenError, type InvoiceLinkResult, type Logger, type MakePaymentInput, type MakePaymentResult, type Membership, type MembershipPurchaseInput, MembershipService, type NoteMode, type Payment, type PaymentSource, PeekAccessService, type PeekAccessServiceConfig, type PeekAuthTokenClaims, type PeekAuthTokenUser, PeekGraphQLError, PeekHttpError, type PeekPlatform, PiiAccessDisabledError, type Price, type PricingActivityContext, type PricingEngine, type PricingMoney, type PricingNamedRef, type PricingOverride, type PricingOverrideFilter, PricingService, type Product, type ProductMeetingLocation, ProductService, type ProductServiceOptions, type ProductTicket, type PromoCode, type PromoCodeFixedAmount, PromoCodeService, type PromoCodeServiceOptions, type PurchasedMembership, RENTAL_PRODUCT_TYPE, RateLimitError, type RefundInput, type RefundResult, ResellerService, type ResolvedOverride, type ResolvedPricingOverride, type ResolvedResourceOption, type Resource, type ResourceOptionOverride, type ResourceOptionQuantity, type ResourcePool, type ResourcePoolAccountUser, type ResourcePoolAssignment, type ResourcePoolMode, ResourcePoolService, type Review, ReviewService, type Ticket, type Timeslot, type TimeslotFilter, TimeslotService, type UpdateEngineInput, type UpdateTimeslotResult, type UpsertOverridesInput, type UpsertOverridesResult, type Waiver, noopLogger, parseBookingWebhook, parseInstallEvent, parseWaiverWebhook, verifyInstallWebhook };
2695
+ export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, type AccessOptions, type AccountUser, AccountUserService, type AccountUserServiceOptions, AcmeAccessService, type AcmeAccessServiceConfig, type AcmeActivity, type AcmeActivityTicket, AcmeApiError, AcmeProductService, type Activity, type ActivityOverrides, type ActivityTicket, type AddAddonInput, AdminAccountRequiredError, type Agent, type AssignGuideResult, type AssignedActivity, type AssignedResource, type Availability, AvailabilityService, type AvailabilityTime, type AvailabilityTimesQuery, type Booking, type BookingAddon, type BookingAddonMoney, type BookingAddonOption, type BookingAddons, type BookingAddonsMutationResult, type BookingPaymentsOnFile, type BookingReadOptions, type BookingSearchBy, BookingService, type BookingServiceOptions, type BookingTimeRangeSearch, type CancelBookingResult, type Channel, type ClearOverridesInput, CngAccessService, type CngAccessServiceConfig, CngApiError, CngProductService, type CreateBookingGuest, type CreateBookingInput, type CreateBookingTicket, type CreateEngineInput, type CreatePromoCodeInput, type CreatedBooking, type CreatedPricingEngine, type CreatedPromoCode, type CustomQuestionAnswer, type DailyNote, DailyNoteService, type Duration, type Guest, type GuestMetadata, type Guide, type GuideAssignment, INSTALL_STATUSES, type InstallStatus, type InstallWebhook, type InstallWebhookAccount, type InstallWebhookClaims, InvalidPeekTokenError, type InvoiceLinkResult, type Logger, type MakePaymentInput, type MakePaymentResult, type Membership, type MembershipPurchaseInput, MembershipService, type NoteMode, type Payment, type PaymentSource, PeekAccessService, type PeekAccessServiceConfig, type PeekAuthTokenClaims, type PeekAuthTokenUser, PeekGraphQLError, PeekHttpError, type PeekPlatform, PiiAccessDisabledError, type Price, type PricingActivityContext, type PricingEngine, type PricingMoney, type PricingNamedRef, type PricingOverride, type PricingOverrideFilter, PricingService, type Product, type ProductMeetingLocation, ProductService, type ProductServiceOptions, type ProductTicket, type PromoCode, type PromoCodeFixedAmount, PromoCodeService, type PromoCodeServiceOptions, type PurchasedMembership, RENTAL_PRODUCT_TYPE, RateLimitError, type RefundInput, type RefundResult, ResellerService, type ResolvedOverride, type ResolvedPricingOverride, type ResolvedResourceOption, type Resource, type ResourceOptionOverride, type ResourceOptionQuantity, type ResourcePool, type ResourcePoolAccountUser, type ResourcePoolAssignment, type ResourcePoolMode, ResourcePoolService, type Review, ReviewService, type Ticket, type Timeslot, type TimeslotFilter, TimeslotService, type UpdateEngineInput, type UpdateTimeslotResult, type UpsertOverridesInput, type UpsertOverridesResult, type Waiver, noopLogger, parseBookingWebhook, parseInstallWebhook, parseWaiverWebhook, verifyInstallWebhook };
package/dist/index.js CHANGED
@@ -4249,18 +4249,6 @@ function extractWaiverNode(payload) {
4249
4249
  return record;
4250
4250
  }
4251
4251
 
4252
- // src/internal/peek/installs/install-webhook.ts
4253
- function verifyInstallWebhook(token, secret) {
4254
- const payload = verifyPeekJwt(token, secret);
4255
- return {
4256
- installId: payload.sub ?? "",
4257
- account: { id: payload.account?.id ?? "" },
4258
- status: payload.status ?? "",
4259
- displayVersion: payload.display_version ?? "",
4260
- user: payload.user ? mapPeekTokenUser(payload.user) : null
4261
- };
4262
- }
4263
-
4264
4252
  // src/models/peek/auth-token.ts
4265
4253
  var PEEK_PLATFORMS = ["peek", "cng", "acme"];
4266
4254
 
@@ -4272,50 +4260,53 @@ var INSTALL_STATUSES = [
4272
4260
  ];
4273
4261
 
4274
4262
  // src/internal/peek/installs/install-converter.ts
4263
+ function extractInstallWebhookBody(payload) {
4264
+ let body = payload;
4265
+ if (typeof body === "string") {
4266
+ try {
4267
+ body = JSON.parse(body);
4268
+ } catch {
4269
+ return {};
4270
+ }
4271
+ }
4272
+ if (!body || typeof body !== "object") return {};
4273
+ return body;
4274
+ }
4275
4275
  function toInstallStatus(raw) {
4276
4276
  return INSTALL_STATUSES.includes(raw) ? raw : null;
4277
4277
  }
4278
4278
  function toPeekPlatform(raw) {
4279
4279
  return PEEK_PLATFORMS.includes(raw) ? raw : null;
4280
4280
  }
4281
- function fromInstallIdentityNode(node) {
4282
- const data = node ?? {};
4283
- const account = data.account ?? {};
4281
+
4282
+ // src/internal/peek/installs/install-webhook.ts
4283
+ function parseInstallWebhook(token, body, secret) {
4284
+ const payload = verifyPeekJwt(token, secret);
4285
+ const account = extractInstallWebhookBody(body).account ?? {};
4286
+ const rawStatus = payload.status ?? "";
4284
4287
  return {
4285
- installId: data.install_id || "",
4286
- accountId: account.id || "",
4288
+ installId: payload.sub ?? "",
4289
+ accountId: payload.account?.id ?? "",
4287
4290
  accountName: account.name || "",
4288
4291
  platform: toPeekPlatform(account.platform || ""),
4289
- isTest: Boolean(account.is_test)
4290
- };
4291
- }
4292
- function fromInstallEventNode(node) {
4293
- const rawStatus = node?.status || "";
4294
- return {
4292
+ isTest: Boolean(account.is_test),
4295
4293
  status: toInstallStatus(rawStatus),
4296
4294
  rawStatus,
4297
- displayVersion: node?.display_version || "",
4298
- identity: fromInstallIdentityNode(node)
4295
+ displayVersion: payload.display_version ?? "",
4296
+ user: payload.user ? mapPeekTokenUser(payload.user) : null
4299
4297
  };
4300
4298
  }
4301
-
4302
- // src/internal/peek/installs/install-event.ts
4303
- function parseInstallEvent(payload) {
4304
- return fromInstallEventNode(extractInstallEventNode(payload));
4305
- }
4306
- function extractInstallEventNode(payload) {
4307
- let body = payload;
4308
- if (typeof body === "string") {
4309
- try {
4310
- body = JSON.parse(body);
4311
- } catch {
4312
- return {};
4313
- }
4314
- }
4315
- if (!body || typeof body !== "object") return {};
4316
- return body;
4299
+ function verifyInstallWebhook(token, secret) {
4300
+ const payload = verifyPeekJwt(token, secret);
4301
+ return {
4302
+ installId: payload.sub ?? "",
4303
+ account: { id: payload.account?.id ?? "" },
4304
+ status: payload.status ?? "",
4305
+ displayVersion: payload.display_version ?? "",
4306
+ user: payload.user ? mapPeekTokenUser(payload.user) : null
4307
+ };
4317
4308
  }
4318
4309
 
4319
- export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, AccountUserService, AcmeAccessService, AcmeApiError, AcmeProductService, AdminAccountRequiredError, AvailabilityService, BookingService, CngAccessService, CngApiError, CngProductService, DailyNoteService, INSTALL_STATUSES, InvalidPeekTokenError, MembershipService, PeekAccessService, PeekGraphQLError, PeekHttpError, PiiAccessDisabledError, PricingService, ProductService, PromoCodeService, RENTAL_PRODUCT_TYPE, RateLimitError, ResellerService, ResourcePoolService, ReviewService, TimeslotService, noopLogger, parseBookingWebhook, parseInstallEvent, parseWaiverWebhook, verifyInstallWebhook };
4310
+ export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, AccountUserService, AcmeAccessService, AcmeApiError, AcmeProductService, AdminAccountRequiredError, AvailabilityService, BookingService, CngAccessService, CngApiError, CngProductService, DailyNoteService, INSTALL_STATUSES, InvalidPeekTokenError, MembershipService, PeekAccessService, PeekGraphQLError, PeekHttpError, PiiAccessDisabledError, PricingService, ProductService, PromoCodeService, RENTAL_PRODUCT_TYPE, RateLimitError, ResellerService, ResourcePoolService, ReviewService, TimeslotService, noopLogger, parseBookingWebhook, parseInstallWebhook, parseWaiverWebhook, verifyInstallWebhook };
4320
4311
  //# sourceMappingURL=index.js.map
4321
4312
  //# sourceMappingURL=index.js.map