Skip to content

Commit 6b82960

Browse files
committed
feat: hide services from top-level
1 parent aaf75d7 commit 6b82960

8 files changed

Lines changed: 581 additions & 5 deletions

File tree

docs/internal/ARCHITECTURE.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ point (`PeekAccessService`) that hands out per-resource services returning clean
77
plain-object data models.
88

99
Consumers never see GraphQL. They construct one access service per install and
10-
call typed methods like `peek.getProductService().getAllProducts()`.
10+
call typed methods like `peek.getProductService().getAllProducts()` or directly
11+
via the top-level short-forms like `peek.getAllProducts()` and
12+
`peek.getAllActivities()`.
1113

1214
## Layers
1315

@@ -44,6 +46,8 @@ call typed methods like `peek.getProductService().getAllProducts()`.
4446
- Constructs a single shared `TokenManager` and `GraphQLClient`.
4547
- Exposes one `get<Resource>Service()` accessor per resource. Each is **lazily
4648
created and memoized** — repeated calls return the same instance.
49+
- Exposes **top-level short-form methods** that delegate directly to the
50+
underlying service, e.g. `peek.getAllProducts()``peek.getProductService().getAllProducts()`. Every public service method has a named proxy on `PeekAccessService`; the names are prefixed with the resource noun where disambiguation is needed (e.g. `getBookingById`, `getTimeslotById`).
4751
- Exposes `verifyPeekAuthToken(token)` to verify HMAC-signed JWTs issued by
4852
the Peek app registry (`iss: "app_registry_v2"`), returning
4953
a fully typed `PeekAuthTokenClaims` (including the nested `PeekAuthTokenUser`
@@ -108,6 +112,10 @@ Resources: `products`, `account-users`, `resource-pools`, `timeslots`,
108112
`resellers`, `promo-codes`, `daily-notes`, `availability`, `memberships`,
109113
`bookings`, `reviews`. Clean data shapes live in `src/models/`.
110114

115+
`ProductService` exposes three top-level product filters in addition to the combined `getAllProducts()`:
116+
- `getAllActivities()` — fetches only the `activities` connection (one request, no add-on pagination).
117+
- `getAllAddons()` — fetches only the `itemOptions` connection, paginated.
118+
111119
`waivers` is a **webhook-only resource**: it has no GraphQL reads (so no
112120
queries/service/converter triad), just `src/internal/waivers/waiver-webhook.ts`
113121
and the `src/models/waiver.ts` model. See the webhook notes below.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@peektravel/app-utilities",
3-
"version": "0.2.3",
3+
"version": "0.2.4",
44
"description": "GraphQL JS mapping utilities extracted from the Peek Pro Autopilot connector.",
55
"license": "MIT",
66
"repository": {

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export { parseWaiverWebhook } from "./internal/waivers/waiver-webhook.js";
4242

4343
export { ReviewService } from "./internal/reviews/review-service.js";
4444

45-
export { ADD_ON_PRODUCT_TYPE } from "./models/product.js";
45+
export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, RENTAL_PRODUCT_TYPE } from "./models/product.js";
4646
export type { Product, ProductTicket } from "./models/product.js";
4747
export type { AccountUser, AssignedActivity } from "./models/account-user.js";
4848
export type {

src/internal/products/product-service.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
*/
88
import { SALES_ENDPOINT } from "../gateway-endpoints.js";
99
import type { GraphQLBody, GraphQLClient } from "../graphql-client.js";
10-
import type { Product } from "../../models/product.js";
10+
import {
11+
ACTIVITY_PRODUCT_TYPE,
12+
RENTAL_PRODUCT_TYPE,
13+
type Product,
14+
} from "../../models/product.js";
1115
import { fromActivities, fromItemOptionNodes } from "./product-converter.js";
1216
import {
1317
ITEM_OPTIONS_QUERY,
@@ -60,6 +64,24 @@ export class ProductService {
6064
return [...fromActivities(activities), ...fromItemOptionNodes(itemOptionNodes)];
6165
}
6266

67+
/** Returns products with type {@link ACTIVITY_PRODUCT_TYPE}. */
68+
async getAllActivities(): Promise<Product[]> {
69+
const activities = await this.fetchActivities();
70+
return fromActivities(activities).filter((p) => p.type === ACTIVITY_PRODUCT_TYPE);
71+
}
72+
73+
/** Returns products with type {@link RENTAL_PRODUCT_TYPE}. */
74+
async getAllRentals(): Promise<Product[]> {
75+
const activities = await this.fetchActivities();
76+
return fromActivities(activities).filter((p) => p.type === RENTAL_PRODUCT_TYPE);
77+
}
78+
79+
/** Returns only add-on products. */
80+
async getAllAddons(): Promise<Product[]> {
81+
const nodes = await this.fetchAllItemOptionNodes();
82+
return fromItemOptionNodes(nodes);
83+
}
84+
6385
private async fetchActivities(): Promise<ProductsResponse["activities"]> {
6486
const body: GraphQLBody<ProductsResponse> =
6587
await this.client.request<ProductsResponse>(

src/models/product.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ export interface ProductTicket {
6363
name: string;
6464
}
6565

66+
/** {@link Product.type} for standard bookable activities. */
67+
export const ACTIVITY_PRODUCT_TYPE = "ACTIVITY";
68+
69+
/** {@link Product.type} for rental products. */
70+
export const RENTAL_PRODUCT_TYPE = "RENTAL";
71+
6672
/**
6773
* The {@link Product.type} value assigned to add-on products.
6874
*

src/peek-access-service.ts

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,19 @@ import { V2_EXTENDABLE_SLUG } from "./internal/gateway-endpoints.js";
2727
import { TokenManager } from "./internal/token-manager.js";
2828
import { noopLogger, type Logger } from "./logger.js";
2929
import type { PeekAuthTokenClaims } from "./models/auth-token.js";
30+
import type { AvailabilityTimesQuery } from "./models/availability-time.js";
31+
import type {
32+
BookingReadOptions,
33+
BookingTimeRangeSearch,
34+
CreateBookingInput,
35+
NoteMode,
36+
} from "./models/booking.js";
37+
import type { MakePaymentInput, RefundInput } from "./models/booking-payment.js";
38+
import type { MembershipPurchaseInput } from "./models/membership.js";
39+
import type { CreatePromoCodeInput } from "./models/promo-code.js";
40+
import type { ResourcePoolMode } from "./models/resource-pool.js";
41+
import type { GuideAssignment, TimeslotFilter } from "./models/timeslot.js";
42+
import type { AddAddonInput } from "./internal/bookings/booking-service.js";
3043

3144
/** Default backoffice GraphQL gateway base URL (v1). */
3245
const DEFAULT_BASE_URL = "https://apps.peekapis.com/backoffice-gql";
@@ -331,6 +344,172 @@ export class PeekAccessService {
331344
}
332345
return this.reviewService;
333346
}
347+
348+
// ─── Product short-forms ─────────────────────────────────────────────────
349+
350+
/** All products (activities + add-ons). Delegates to {@link ProductService.getAllProducts}. */
351+
getAllProducts() { return this.getProductService().getAllProducts(); }
352+
353+
/** All activity products (excludes add-ons). Delegates to {@link ProductService.getAllActivities}. */
354+
getAllActivities() { return this.getProductService().getAllActivities(); }
355+
356+
/** All rental products. Delegates to {@link ProductService.getAllRentals}. */
357+
getAllRentals() { return this.getProductService().getAllRentals(); }
358+
359+
/** All add-on products. Delegates to {@link ProductService.getAllAddons}. */
360+
getAllAddons() { return this.getProductService().getAllAddons(); }
361+
362+
// ─── Account-user short-forms ─────────────────────────────────────────────
363+
364+
/** All active account users. Delegates to {@link AccountUserService.getAll}. */
365+
getAllAccountUsers() { return this.getAccountUserService().getAll(); }
366+
367+
/** Account user by id, or null. Delegates to {@link AccountUserService.getById}. */
368+
getAccountUserById(userId: string) { return this.getAccountUserService().getById(userId); }
369+
370+
// ─── Resource-pool short-forms ────────────────────────────────────────────
371+
372+
/** All resource pools. Delegates to {@link ResourcePoolService.getAll}. */
373+
getAllResourcePools(mode?: ResourcePoolMode) { return this.getResourcePoolService().getAll(mode); }
374+
375+
// ─── Timeslot short-forms ─────────────────────────────────────────────────
376+
377+
/** Timeslots for an activity on a given date. Delegates to {@link TimeslotService.getForDay}. */
378+
getTimeslotsForDay(productId: string, date: string, filter?: TimeslotFilter) {
379+
return this.getTimeslotService().getForDay(productId, date, filter);
380+
}
381+
382+
/** Single timeslot by id. Delegates to {@link TimeslotService.getById}. */
383+
getTimeslotById(timeslotId: string) { return this.getTimeslotService().getById(timeslotId); }
384+
385+
/** Set timeslot status. Delegates to {@link TimeslotService.setAvailability}. */
386+
setTimeslotAvailability(timeslotId: string, status: string) {
387+
return this.getTimeslotService().setAvailability(timeslotId, status);
388+
}
389+
390+
/** Set timeslot manifest notes. Delegates to {@link TimeslotService.setNotes}. */
391+
setTimeslotNotes(timeslotId: string, manifestNotes: string) {
392+
return this.getTimeslotService().setNotes(timeslotId, manifestNotes);
393+
}
394+
395+
/** Assign or unassign guides on timeslots. Delegates to {@link TimeslotService.assignGuide}. */
396+
assignTimeslotGuide(assignment: GuideAssignment) {
397+
return this.getTimeslotService().assignGuide(assignment);
398+
}
399+
400+
// ─── Reseller short-forms ─────────────────────────────────────────────────
401+
402+
/** All reseller channels. Delegates to {@link ResellerService.getAllChannels}. */
403+
getAllChannels(agentsPerChannel?: number) {
404+
return this.getResellerService().getAllChannels(agentsPerChannel);
405+
}
406+
407+
// ─── Promo-code short-forms ───────────────────────────────────────────────
408+
409+
/** All promo codes. Delegates to {@link PromoCodeService.getAll}. */
410+
getAllPromoCodes() { return this.getPromoCodeService().getAll(); }
411+
412+
/** Create a promo code. Delegates to {@link PromoCodeService.create}. */
413+
createPromoCode(input: CreatePromoCodeInput) { return this.getPromoCodeService().create(input); }
414+
415+
// ─── Daily-note short-forms ───────────────────────────────────────────────
416+
417+
/** Today's daily note. Delegates to {@link DailyNoteService.getToday}. */
418+
getDailyNoteToday() { return this.getDailyNoteService().getToday(); }
419+
420+
/** Update today's daily note. Delegates to {@link DailyNoteService.update}. */
421+
updateDailyNote(note: string) { return this.getDailyNoteService().update(note); }
422+
423+
// ─── Availability short-forms ─────────────────────────────────────────────
424+
425+
/** Availability times for an activity. Delegates to {@link AvailabilityService.getAvailabilityTimes}. */
426+
getAvailabilityTimes(query: AvailabilityTimesQuery) {
427+
return this.getAvailabilityService().getAvailabilityTimes(query);
428+
}
429+
430+
// ─── Membership short-forms ───────────────────────────────────────────────
431+
432+
/** All memberships. Delegates to {@link MembershipService.getAll}. */
433+
getAllMemberships() { return this.getMembershipService().getAll(); }
434+
435+
/** Purchase a membership. Delegates to {@link MembershipService.purchase}. */
436+
purchaseMembership(input: MembershipPurchaseInput) {
437+
return this.getMembershipService().purchase(input);
438+
}
439+
440+
// ─── Booking short-forms ──────────────────────────────────────────────────
441+
442+
/** Booking by id. Delegates to {@link BookingService.getById}. */
443+
getBookingById(bookingId: string, options?: BookingReadOptions) {
444+
return this.getBookingService().getById(bookingId, options);
445+
}
446+
447+
/** Bookings by time range. Delegates to {@link BookingService.searchByTimeRange}. */
448+
searchBookingsByTimeRange(input: BookingTimeRangeSearch) {
449+
return this.getBookingService().searchByTimeRange(input);
450+
}
451+
452+
/** Bookings on a timeslot. Delegates to {@link BookingService.searchByTimeslot}. */
453+
searchBookingsByTimeslot(timeslotId: string, options?: BookingReadOptions) {
454+
return this.getBookingService().searchByTimeslot(timeslotId, options);
455+
}
456+
457+
/** Guests on a booking. Delegates to {@link BookingService.getGuests}. */
458+
getBookingGuests(bookingId: string) { return this.getBookingService().getGuests(bookingId); }
459+
460+
/** Payments on file for a booking. Delegates to {@link BookingService.getPaymentsOnFile}. */
461+
getBookingPaymentsOnFile(bookingId: string) {
462+
return this.getBookingService().getPaymentsOnFile(bookingId);
463+
}
464+
465+
/** Append or overwrite operator notes. Delegates to {@link BookingService.appendNote}. */
466+
appendBookingNote(bookingId: string, note: string, mode?: NoteMode) {
467+
return this.getBookingService().appendNote(bookingId, note, mode);
468+
}
469+
470+
/** Set booking check-in status. Delegates to {@link BookingService.setCheckinStatus}. */
471+
setBookingCheckinStatus(bookingId: string, checkedIn: boolean) {
472+
return this.getBookingService().setCheckinStatus(bookingId, checkedIn);
473+
}
474+
475+
/** Cancel a booking. Delegates to {@link BookingService.cancel}. */
476+
cancelBooking(bookingId: string, notes?: string) {
477+
return this.getBookingService().cancel(bookingId, notes);
478+
}
479+
480+
/** Charge a booking. Delegates to {@link BookingService.makePayment}. */
481+
makeBookingPayment(input: MakePaymentInput) { return this.getBookingService().makePayment(input); }
482+
483+
/** Refund a booking payment. Delegates to {@link BookingService.refund}. */
484+
refundBooking(input: RefundInput) { return this.getBookingService().refund(input); }
485+
486+
/** Create an invoice link. Delegates to {@link BookingService.createInvoiceLink}. */
487+
createBookingInvoiceLink(bookingId: string) {
488+
return this.getBookingService().createInvoiceLink(bookingId);
489+
}
490+
491+
/** List add-ons on a booking. Delegates to {@link BookingService.listAddons}. */
492+
listBookingAddons(bookingId: string) { return this.getBookingService().listAddons(bookingId); }
493+
494+
/** Add an add-on to a booking. Delegates to {@link BookingService.addAddon}. */
495+
addBookingAddon(bookingId: string, input: AddAddonInput) {
496+
return this.getBookingService().addAddon(bookingId, input);
497+
}
498+
499+
/** Remove an add-on from a booking. Delegates to {@link BookingService.removeAddon}. */
500+
removeBookingAddon(bookingId: string, input: AddAddonInput) {
501+
return this.getBookingService().removeAddon(bookingId, input);
502+
}
503+
504+
/** Create a booking. Delegates to {@link BookingService.create}. */
505+
createBooking(input: CreateBookingInput) { return this.getBookingService().create(input); }
506+
507+
// ─── Review short-forms ───────────────────────────────────────────────────
508+
509+
/** Reviews for an activity. Delegates to {@link ReviewService.getReviews}. */
510+
getReviews(productId: string, reviewCount?: number, reviewOffset?: number) {
511+
return this.getReviewService().getReviews(productId, reviewCount, reviewOffset);
512+
}
334513
}
335514

336515
function requireNonEmpty(value: string, name: string): void {

0 commit comments

Comments
 (0)