@tiquo/dom-package 1.3.2 → 1.4.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.mts CHANGED
@@ -231,6 +231,9 @@ declare global {
231
231
  accessToken: string;
232
232
  refreshToken?: string;
233
233
  };
234
+ __tiquo_get_iframe_token?: () => Promise<{
235
+ token: string;
236
+ }>;
234
237
  }
235
238
  }
236
239
  interface TiquoUser {
@@ -322,6 +325,12 @@ interface TiquoOrderItem {
322
325
  total: number;
323
326
  type: 'product' | 'booking' | 'custom';
324
327
  }
328
+ /**
329
+ * An order belonging to the authenticated customer.
330
+ *
331
+ * Note: abandoned `lost_cart` orders are never returned — they're internal
332
+ * marketing-recovery state, not part of the customer's purchase history.
333
+ */
325
334
  interface TiquoOrder {
326
335
  id: string;
327
336
  orderNumber: string;
@@ -334,7 +343,7 @@ interface TiquoOrder {
334
343
  items: TiquoOrderItem[];
335
344
  createdAt: number;
336
345
  completedAt?: number;
337
- customerRole: 'primary' | 'attendee' | 'recipient' | 'billing' | 'guest' | 'other';
346
+ customerRole: 'primary' | 'attendee' | 'billing' | 'referrer' | 'other';
338
347
  }
339
348
  interface GetOrdersOptions {
340
349
  limit?: number;
@@ -346,6 +355,74 @@ interface GetOrdersResult {
346
355
  hasMore: boolean;
347
356
  nextCursor?: string;
348
357
  }
358
+ interface TiquoReceiptItem {
359
+ id: string;
360
+ name: string;
361
+ quantity: number;
362
+ unitPrice: number;
363
+ subtotal: number;
364
+ tax: number;
365
+ discount: number;
366
+ total: number;
367
+ type: 'product' | 'booking' | 'custom' | 'membership';
368
+ specialInstructions?: string;
369
+ }
370
+ interface TiquoReceiptBusiness {
371
+ name?: string;
372
+ logo?: string;
373
+ brandName?: string;
374
+ primaryColor?: string;
375
+ vatNumber?: string;
376
+ address?: string;
377
+ city?: string;
378
+ postalCode?: string;
379
+ country?: string;
380
+ email?: string;
381
+ phone?: string;
382
+ sublocationName?: string;
383
+ locationName?: string;
384
+ }
385
+ interface TiquoReceiptCustomer {
386
+ id: string;
387
+ customerNumber: string;
388
+ displayName?: string;
389
+ email?: string;
390
+ }
391
+ interface TiquoReceiptOrder {
392
+ id: string;
393
+ orderNumber: string;
394
+ status: TiquoOrder['status'];
395
+ paymentStatus: TiquoOrder['paymentStatus'];
396
+ currency: string;
397
+ subtotal: number;
398
+ taxTotal: number;
399
+ /** "Tax Inclusive" or "Tax Exclusive" — controls how the receipt should render tax lines. */
400
+ taxSetting?: string;
401
+ discountTotal: number;
402
+ discountName?: string;
403
+ serviceChargeAmount?: number;
404
+ tipAmount?: number;
405
+ total: number;
406
+ refundAmount?: number;
407
+ refundedAt?: number;
408
+ paymentMethod?: string;
409
+ cardBrand?: string;
410
+ cardLast4?: string;
411
+ items: TiquoReceiptItem[];
412
+ createdAt: number;
413
+ completedAt?: number;
414
+ }
415
+ /**
416
+ * A printable receipt for a single paid/completed order owned by the
417
+ * authenticated customer. Combines order totals, line items, business
418
+ * branding (logo, VAT, address), and the customer's own details so the
419
+ * consumer can render or print the receipt without further API calls.
420
+ */
421
+ interface TiquoReceipt {
422
+ order: TiquoReceiptOrder;
423
+ business: TiquoReceiptBusiness;
424
+ customer: TiquoReceiptCustomer;
425
+ }
349
426
  interface TiquoBooking {
350
427
  id: string;
351
428
  bookingNumber: string;
@@ -391,7 +468,7 @@ interface TiquoEnquiry {
391
468
  updatedAt: string;
392
469
  firstResponseAt?: string;
393
470
  resolvedAt?: string;
394
- customerRole: 'primary' | 'interested' | 'decision_maker' | 'influencer' | 'referrer' | 'other';
471
+ customerRole: 'primary' | 'attendee' | 'billing' | 'referrer' | 'other';
395
472
  }
396
473
  interface GetEnquiriesOptions {
397
474
  limit?: number;
@@ -418,6 +495,8 @@ declare class TiquoAuth {
418
495
  private refreshTimer;
419
496
  private isRefreshing;
420
497
  private visibilityHandler;
498
+ private iframeObserver;
499
+ private injectedIframes;
421
500
  private broadcastChannel;
422
501
  private tabId;
423
502
  private isProcessingTabSync;
@@ -459,15 +538,48 @@ declare class TiquoAuth {
459
538
  */
460
539
  onAuthStateChange(callback: AuthStateChangeCallback): () => void;
461
540
  /**
462
- * Get the authenticated customer's order history
463
- * Only returns orders for the logged-in customer
541
+ * Get the authenticated customer's order history.
542
+ *
543
+ * Returns orders across every status (draft, pending, processing, completed,
544
+ * cancelled, refunded, open_tab) — except `lost_cart`, which is internal
545
+ * marketing-recovery state and is never exposed to customers.
546
+ *
547
+ * Pass `status` to filter to a single status; pass `cursor` (an order id
548
+ * from a previous `nextCursor`) to paginate. Sorted most recent first.
464
549
  */
465
550
  getOrders(options?: GetOrdersOptions): Promise<GetOrdersResult>;
466
551
  /**
467
- * Get the authenticated customer's booking history
468
- * Only returns bookings for the logged-in customer
552
+ * Get the authenticated customer's booking history.
553
+ *
554
+ * Pass `upcoming: true` to limit to future bookings sorted soonest-first —
555
+ * the common case for rendering "Your upcoming bookings" on a logged-in
556
+ * customer dashboard. Without it, returns all bookings sorted most recent
557
+ * first.
469
558
  */
470
559
  getBookings(options?: GetBookingsOptions): Promise<GetBookingsResult>;
560
+ /**
561
+ * Convenience wrapper around `getBookings({ upcoming: true })` for the
562
+ * common "show me my upcoming bookings" case. Results are sorted
563
+ * soonest-first.
564
+ */
565
+ getUpcomingBookings(options?: Omit<GetBookingsOptions, 'upcoming'>): Promise<GetBookingsResult>;
566
+ /**
567
+ * Get a printable receipt for a single order owned by the authenticated
568
+ * customer. Only resolves for orders that are paid (full or partial),
569
+ * refunded, or completed — drafts and pending orders don't have a receipt
570
+ * yet and will throw `RECEIPT_NOT_AVAILABLE`.
571
+ *
572
+ * The returned payload bundles the order totals & items, the business's
573
+ * receipt branding (logo, VAT number, address), and the customer's own
574
+ * details so the consumer can render or print the receipt without further
575
+ * API calls.
576
+ *
577
+ * Throws `TiquoAuthError` with code:
578
+ * - `RECEIPT_NOT_AVAILABLE` — order doesn't exist for this customer, or
579
+ * it isn't paid/completed yet.
580
+ * - `NOT_AUTHENTICATED` — no valid session.
581
+ */
582
+ getReceipt(orderId: string): Promise<TiquoReceipt>;
471
583
  /**
472
584
  * Get the authenticated customer's enquiry history
473
585
  * Only returns enquiries for the logged-in customer
@@ -490,6 +602,17 @@ declare class TiquoAuth {
490
602
  onLoad?: () => void;
491
603
  onError?: (error: Error) => void;
492
604
  }): Promise<HTMLIFrameElement>;
605
+ /**
606
+ * Automatically find and inject auth tokens into existing Tiquo booking
607
+ * iframes on the page. Also watches for iframes added later via
608
+ * MutationObserver. This enables token injection even when the site uses
609
+ * a static <iframe> embed snippet rather than embedCustomerFlow().
610
+ */
611
+ private autoInjectIframeTokens;
612
+ private scanAndInjectIframes;
613
+ private injectTokenIntoIframe;
614
+ private isTiquoEmbedUrl;
615
+ private observeNewIframes;
493
616
  /**
494
617
  * Get the current access token (for advanced use cases)
495
618
  */
@@ -552,6 +675,8 @@ declare function useTiquoAuth(auth: TiquoAuth): {
552
675
  updateProfile: (updates: ProfileUpdateData) => Promise<ProfileUpdateResult>;
553
676
  getOrders: (options?: GetOrdersOptions) => Promise<GetOrdersResult>;
554
677
  getBookings: (options?: GetBookingsOptions) => Promise<GetBookingsResult>;
678
+ getUpcomingBookings: (options?: Omit<GetBookingsOptions, "upcoming">) => Promise<GetBookingsResult>;
679
+ getReceipt: (orderId: string) => Promise<TiquoReceipt>;
555
680
  getEnquiries: (options?: GetEnquiriesOptions) => Promise<GetEnquiriesResult>;
556
681
  getIframeToken: (flowId?: string) => Promise<IframeTokenResult>;
557
682
  embedCustomerFlow: (flowUrl: string, container: HTMLElement | string, options?: {
@@ -615,4 +740,4 @@ declare class TiquoPhone {
615
740
  static buildPhone: typeof buildPhone;
616
741
  }
617
742
 
618
- export { type AuthStateChangeCallback, type CountryPhoneInfo, type GetBookingsOptions, type GetBookingsResult, type GetEnquiriesOptions, type GetEnquiriesResult, type GetOrdersOptions, type GetOrdersResult, type IframeTokenResult, type PhoneValidationResult, type ProfileUpdateData, type ProfileUpdateResult, type SendOTPResult, TiquoAuth, type TiquoAuthConfig, TiquoAuthError, type TiquoBooking, type TiquoCustomer, type TiquoCustomerEmail, type TiquoCustomerPhone, type TiquoEnquiry, type TiquoOrder, type TiquoOrderItem, TiquoPhone, type TiquoSession, type TiquoUser, type VerifyOTPResult, addCustomerUserId, clearCachedEmail, TiquoAuth as default, getCustomerUserIds, getPrefilledEmailFromCookie, isDashboardSession, trackCustomerPresence, useTiquoAuth };
743
+ export { type AuthStateChangeCallback, type CountryPhoneInfo, type GetBookingsOptions, type GetBookingsResult, type GetEnquiriesOptions, type GetEnquiriesResult, type GetOrdersOptions, type GetOrdersResult, type IframeTokenResult, type PhoneValidationResult, type ProfileUpdateData, type ProfileUpdateResult, type SendOTPResult, TiquoAuth, type TiquoAuthConfig, TiquoAuthError, type TiquoBooking, type TiquoCustomer, type TiquoCustomerEmail, type TiquoCustomerPhone, type TiquoEnquiry, type TiquoOrder, type TiquoOrderItem, TiquoPhone, type TiquoReceipt, type TiquoReceiptBusiness, type TiquoReceiptCustomer, type TiquoReceiptItem, type TiquoReceiptOrder, type TiquoSession, type TiquoUser, type VerifyOTPResult, addCustomerUserId, clearCachedEmail, TiquoAuth as default, getCustomerUserIds, getPrefilledEmailFromCookie, isDashboardSession, trackCustomerPresence, useTiquoAuth };
package/dist/index.d.ts CHANGED
@@ -231,6 +231,9 @@ declare global {
231
231
  accessToken: string;
232
232
  refreshToken?: string;
233
233
  };
234
+ __tiquo_get_iframe_token?: () => Promise<{
235
+ token: string;
236
+ }>;
234
237
  }
235
238
  }
236
239
  interface TiquoUser {
@@ -322,6 +325,12 @@ interface TiquoOrderItem {
322
325
  total: number;
323
326
  type: 'product' | 'booking' | 'custom';
324
327
  }
328
+ /**
329
+ * An order belonging to the authenticated customer.
330
+ *
331
+ * Note: abandoned `lost_cart` orders are never returned — they're internal
332
+ * marketing-recovery state, not part of the customer's purchase history.
333
+ */
325
334
  interface TiquoOrder {
326
335
  id: string;
327
336
  orderNumber: string;
@@ -334,7 +343,7 @@ interface TiquoOrder {
334
343
  items: TiquoOrderItem[];
335
344
  createdAt: number;
336
345
  completedAt?: number;
337
- customerRole: 'primary' | 'attendee' | 'recipient' | 'billing' | 'guest' | 'other';
346
+ customerRole: 'primary' | 'attendee' | 'billing' | 'referrer' | 'other';
338
347
  }
339
348
  interface GetOrdersOptions {
340
349
  limit?: number;
@@ -346,6 +355,74 @@ interface GetOrdersResult {
346
355
  hasMore: boolean;
347
356
  nextCursor?: string;
348
357
  }
358
+ interface TiquoReceiptItem {
359
+ id: string;
360
+ name: string;
361
+ quantity: number;
362
+ unitPrice: number;
363
+ subtotal: number;
364
+ tax: number;
365
+ discount: number;
366
+ total: number;
367
+ type: 'product' | 'booking' | 'custom' | 'membership';
368
+ specialInstructions?: string;
369
+ }
370
+ interface TiquoReceiptBusiness {
371
+ name?: string;
372
+ logo?: string;
373
+ brandName?: string;
374
+ primaryColor?: string;
375
+ vatNumber?: string;
376
+ address?: string;
377
+ city?: string;
378
+ postalCode?: string;
379
+ country?: string;
380
+ email?: string;
381
+ phone?: string;
382
+ sublocationName?: string;
383
+ locationName?: string;
384
+ }
385
+ interface TiquoReceiptCustomer {
386
+ id: string;
387
+ customerNumber: string;
388
+ displayName?: string;
389
+ email?: string;
390
+ }
391
+ interface TiquoReceiptOrder {
392
+ id: string;
393
+ orderNumber: string;
394
+ status: TiquoOrder['status'];
395
+ paymentStatus: TiquoOrder['paymentStatus'];
396
+ currency: string;
397
+ subtotal: number;
398
+ taxTotal: number;
399
+ /** "Tax Inclusive" or "Tax Exclusive" — controls how the receipt should render tax lines. */
400
+ taxSetting?: string;
401
+ discountTotal: number;
402
+ discountName?: string;
403
+ serviceChargeAmount?: number;
404
+ tipAmount?: number;
405
+ total: number;
406
+ refundAmount?: number;
407
+ refundedAt?: number;
408
+ paymentMethod?: string;
409
+ cardBrand?: string;
410
+ cardLast4?: string;
411
+ items: TiquoReceiptItem[];
412
+ createdAt: number;
413
+ completedAt?: number;
414
+ }
415
+ /**
416
+ * A printable receipt for a single paid/completed order owned by the
417
+ * authenticated customer. Combines order totals, line items, business
418
+ * branding (logo, VAT, address), and the customer's own details so the
419
+ * consumer can render or print the receipt without further API calls.
420
+ */
421
+ interface TiquoReceipt {
422
+ order: TiquoReceiptOrder;
423
+ business: TiquoReceiptBusiness;
424
+ customer: TiquoReceiptCustomer;
425
+ }
349
426
  interface TiquoBooking {
350
427
  id: string;
351
428
  bookingNumber: string;
@@ -391,7 +468,7 @@ interface TiquoEnquiry {
391
468
  updatedAt: string;
392
469
  firstResponseAt?: string;
393
470
  resolvedAt?: string;
394
- customerRole: 'primary' | 'interested' | 'decision_maker' | 'influencer' | 'referrer' | 'other';
471
+ customerRole: 'primary' | 'attendee' | 'billing' | 'referrer' | 'other';
395
472
  }
396
473
  interface GetEnquiriesOptions {
397
474
  limit?: number;
@@ -418,6 +495,8 @@ declare class TiquoAuth {
418
495
  private refreshTimer;
419
496
  private isRefreshing;
420
497
  private visibilityHandler;
498
+ private iframeObserver;
499
+ private injectedIframes;
421
500
  private broadcastChannel;
422
501
  private tabId;
423
502
  private isProcessingTabSync;
@@ -459,15 +538,48 @@ declare class TiquoAuth {
459
538
  */
460
539
  onAuthStateChange(callback: AuthStateChangeCallback): () => void;
461
540
  /**
462
- * Get the authenticated customer's order history
463
- * Only returns orders for the logged-in customer
541
+ * Get the authenticated customer's order history.
542
+ *
543
+ * Returns orders across every status (draft, pending, processing, completed,
544
+ * cancelled, refunded, open_tab) — except `lost_cart`, which is internal
545
+ * marketing-recovery state and is never exposed to customers.
546
+ *
547
+ * Pass `status` to filter to a single status; pass `cursor` (an order id
548
+ * from a previous `nextCursor`) to paginate. Sorted most recent first.
464
549
  */
465
550
  getOrders(options?: GetOrdersOptions): Promise<GetOrdersResult>;
466
551
  /**
467
- * Get the authenticated customer's booking history
468
- * Only returns bookings for the logged-in customer
552
+ * Get the authenticated customer's booking history.
553
+ *
554
+ * Pass `upcoming: true` to limit to future bookings sorted soonest-first —
555
+ * the common case for rendering "Your upcoming bookings" on a logged-in
556
+ * customer dashboard. Without it, returns all bookings sorted most recent
557
+ * first.
469
558
  */
470
559
  getBookings(options?: GetBookingsOptions): Promise<GetBookingsResult>;
560
+ /**
561
+ * Convenience wrapper around `getBookings({ upcoming: true })` for the
562
+ * common "show me my upcoming bookings" case. Results are sorted
563
+ * soonest-first.
564
+ */
565
+ getUpcomingBookings(options?: Omit<GetBookingsOptions, 'upcoming'>): Promise<GetBookingsResult>;
566
+ /**
567
+ * Get a printable receipt for a single order owned by the authenticated
568
+ * customer. Only resolves for orders that are paid (full or partial),
569
+ * refunded, or completed — drafts and pending orders don't have a receipt
570
+ * yet and will throw `RECEIPT_NOT_AVAILABLE`.
571
+ *
572
+ * The returned payload bundles the order totals & items, the business's
573
+ * receipt branding (logo, VAT number, address), and the customer's own
574
+ * details so the consumer can render or print the receipt without further
575
+ * API calls.
576
+ *
577
+ * Throws `TiquoAuthError` with code:
578
+ * - `RECEIPT_NOT_AVAILABLE` — order doesn't exist for this customer, or
579
+ * it isn't paid/completed yet.
580
+ * - `NOT_AUTHENTICATED` — no valid session.
581
+ */
582
+ getReceipt(orderId: string): Promise<TiquoReceipt>;
471
583
  /**
472
584
  * Get the authenticated customer's enquiry history
473
585
  * Only returns enquiries for the logged-in customer
@@ -490,6 +602,17 @@ declare class TiquoAuth {
490
602
  onLoad?: () => void;
491
603
  onError?: (error: Error) => void;
492
604
  }): Promise<HTMLIFrameElement>;
605
+ /**
606
+ * Automatically find and inject auth tokens into existing Tiquo booking
607
+ * iframes on the page. Also watches for iframes added later via
608
+ * MutationObserver. This enables token injection even when the site uses
609
+ * a static <iframe> embed snippet rather than embedCustomerFlow().
610
+ */
611
+ private autoInjectIframeTokens;
612
+ private scanAndInjectIframes;
613
+ private injectTokenIntoIframe;
614
+ private isTiquoEmbedUrl;
615
+ private observeNewIframes;
493
616
  /**
494
617
  * Get the current access token (for advanced use cases)
495
618
  */
@@ -552,6 +675,8 @@ declare function useTiquoAuth(auth: TiquoAuth): {
552
675
  updateProfile: (updates: ProfileUpdateData) => Promise<ProfileUpdateResult>;
553
676
  getOrders: (options?: GetOrdersOptions) => Promise<GetOrdersResult>;
554
677
  getBookings: (options?: GetBookingsOptions) => Promise<GetBookingsResult>;
678
+ getUpcomingBookings: (options?: Omit<GetBookingsOptions, "upcoming">) => Promise<GetBookingsResult>;
679
+ getReceipt: (orderId: string) => Promise<TiquoReceipt>;
555
680
  getEnquiries: (options?: GetEnquiriesOptions) => Promise<GetEnquiriesResult>;
556
681
  getIframeToken: (flowId?: string) => Promise<IframeTokenResult>;
557
682
  embedCustomerFlow: (flowUrl: string, container: HTMLElement | string, options?: {
@@ -615,4 +740,4 @@ declare class TiquoPhone {
615
740
  static buildPhone: typeof buildPhone;
616
741
  }
617
742
 
618
- export { type AuthStateChangeCallback, type CountryPhoneInfo, type GetBookingsOptions, type GetBookingsResult, type GetEnquiriesOptions, type GetEnquiriesResult, type GetOrdersOptions, type GetOrdersResult, type IframeTokenResult, type PhoneValidationResult, type ProfileUpdateData, type ProfileUpdateResult, type SendOTPResult, TiquoAuth, type TiquoAuthConfig, TiquoAuthError, type TiquoBooking, type TiquoCustomer, type TiquoCustomerEmail, type TiquoCustomerPhone, type TiquoEnquiry, type TiquoOrder, type TiquoOrderItem, TiquoPhone, type TiquoSession, type TiquoUser, type VerifyOTPResult, addCustomerUserId, clearCachedEmail, TiquoAuth as default, getCustomerUserIds, getPrefilledEmailFromCookie, isDashboardSession, trackCustomerPresence, useTiquoAuth };
743
+ export { type AuthStateChangeCallback, type CountryPhoneInfo, type GetBookingsOptions, type GetBookingsResult, type GetEnquiriesOptions, type GetEnquiriesResult, type GetOrdersOptions, type GetOrdersResult, type IframeTokenResult, type PhoneValidationResult, type ProfileUpdateData, type ProfileUpdateResult, type SendOTPResult, TiquoAuth, type TiquoAuthConfig, TiquoAuthError, type TiquoBooking, type TiquoCustomer, type TiquoCustomerEmail, type TiquoCustomerPhone, type TiquoEnquiry, type TiquoOrder, type TiquoOrderItem, TiquoPhone, type TiquoReceipt, type TiquoReceiptBusiness, type TiquoReceiptCustomer, type TiquoReceiptItem, type TiquoReceiptOrder, type TiquoSession, type TiquoUser, type VerifyOTPResult, addCustomerUserId, clearCachedEmail, TiquoAuth as default, getCustomerUserIds, getPrefilledEmailFromCookie, isDashboardSession, trackCustomerPresence, useTiquoAuth };
package/dist/index.js CHANGED
@@ -712,6 +712,8 @@ var TiquoAuth = class {
712
712
  this.refreshTimer = null;
713
713
  this.isRefreshing = false;
714
714
  this.visibilityHandler = null;
715
+ this.iframeObserver = null;
716
+ this.injectedIframes = /* @__PURE__ */ new WeakSet();
715
717
  // Multi-tab sync
716
718
  this.broadcastChannel = null;
717
719
  this.isProcessingTabSync = false;
@@ -742,6 +744,7 @@ var TiquoAuth = class {
742
744
  this.checkForInjectedTokens();
743
745
  this.restoreTokens();
744
746
  this.initVisibilityHandler();
747
+ this.autoInjectIframeTokens();
745
748
  }
746
749
  // ============================================
747
750
  // PUBLIC METHODS
@@ -916,8 +919,14 @@ var TiquoAuth = class {
916
919
  };
917
920
  }
918
921
  /**
919
- * Get the authenticated customer's order history
920
- * Only returns orders for the logged-in customer
922
+ * Get the authenticated customer's order history.
923
+ *
924
+ * Returns orders across every status (draft, pending, processing, completed,
925
+ * cancelled, refunded, open_tab) — except `lost_cart`, which is internal
926
+ * marketing-recovery state and is never exposed to customers.
927
+ *
928
+ * Pass `status` to filter to a single status; pass `cursor` (an order id
929
+ * from a previous `nextCursor`) to paginate. Sorted most recent first.
921
930
  */
922
931
  async getOrders(options) {
923
932
  await this.ensureValidToken();
@@ -947,8 +956,12 @@ var TiquoAuth = class {
947
956
  return result.data || { orders: [], hasMore: false };
948
957
  }
949
958
  /**
950
- * Get the authenticated customer's booking history
951
- * Only returns bookings for the logged-in customer
959
+ * Get the authenticated customer's booking history.
960
+ *
961
+ * Pass `upcoming: true` to limit to future bookings sorted soonest-first —
962
+ * the common case for rendering "Your upcoming bookings" on a logged-in
963
+ * customer dashboard. Without it, returns all bookings sorted most recent
964
+ * first.
952
965
  */
953
966
  async getBookings(options) {
954
967
  await this.ensureValidToken();
@@ -980,6 +993,50 @@ var TiquoAuth = class {
980
993
  const result = await response.json();
981
994
  return result.data || { bookings: [], hasMore: false };
982
995
  }
996
+ /**
997
+ * Convenience wrapper around `getBookings({ upcoming: true })` for the
998
+ * common "show me my upcoming bookings" case. Results are sorted
999
+ * soonest-first.
1000
+ */
1001
+ async getUpcomingBookings(options) {
1002
+ return this.getBookings({ ...options, upcoming: true });
1003
+ }
1004
+ /**
1005
+ * Get a printable receipt for a single order owned by the authenticated
1006
+ * customer. Only resolves for orders that are paid (full or partial),
1007
+ * refunded, or completed — drafts and pending orders don't have a receipt
1008
+ * yet and will throw `RECEIPT_NOT_AVAILABLE`.
1009
+ *
1010
+ * The returned payload bundles the order totals & items, the business's
1011
+ * receipt branding (logo, VAT number, address), and the customer's own
1012
+ * details so the consumer can render or print the receipt without further
1013
+ * API calls.
1014
+ *
1015
+ * Throws `TiquoAuthError` with code:
1016
+ * - `RECEIPT_NOT_AVAILABLE` — order doesn't exist for this customer, or
1017
+ * it isn't paid/completed yet.
1018
+ * - `NOT_AUTHENTICATED` — no valid session.
1019
+ */
1020
+ async getReceipt(orderId) {
1021
+ await this.ensureValidToken();
1022
+ this.log("Fetching receipt for order:", orderId);
1023
+ const url = new URL(`${this.config.apiEndpoint}/api/client/v1/receipt`);
1024
+ url.searchParams.set("orderId", orderId);
1025
+ const response = await fetch(url.toString(), {
1026
+ method: "GET",
1027
+ headers: {
1028
+ "Authorization": `Bearer ${this.accessToken}`
1029
+ },
1030
+ credentials: "include"
1031
+ });
1032
+ if (!response.ok) {
1033
+ const error = await response.json().catch(() => ({ error: "Failed to get receipt" }));
1034
+ const code = response.status === 404 ? "RECEIPT_NOT_AVAILABLE" : "GET_RECEIPT_FAILED";
1035
+ throw new TiquoAuthError(error.error || "Failed to get receipt", code, response.status);
1036
+ }
1037
+ const result = await response.json();
1038
+ return result.data;
1039
+ }
983
1040
  /**
984
1041
  * Get the authenticated customer's enquiry history
985
1042
  * Only returns enquiries for the logged-in customer
@@ -1073,6 +1130,77 @@ var TiquoAuth = class {
1073
1130
  containerEl.appendChild(iframe);
1074
1131
  return iframe;
1075
1132
  }
1133
+ /**
1134
+ * Automatically find and inject auth tokens into existing Tiquo booking
1135
+ * iframes on the page. Also watches for iframes added later via
1136
+ * MutationObserver. This enables token injection even when the site uses
1137
+ * a static <iframe> embed snippet rather than embedCustomerFlow().
1138
+ */
1139
+ autoInjectIframeTokens() {
1140
+ if (typeof document === "undefined") return;
1141
+ if (!this.accessToken) return;
1142
+ if (typeof window !== "undefined") {
1143
+ window.__tiquo_get_iframe_token = () => this.getIframeToken();
1144
+ }
1145
+ Promise.resolve().then(() => this.scanAndInjectIframes());
1146
+ this.observeNewIframes();
1147
+ }
1148
+ async scanAndInjectIframes() {
1149
+ if (!this.accessToken) return;
1150
+ const iframes = document.querySelectorAll("iframe");
1151
+ for (const iframe of iframes) {
1152
+ await this.injectTokenIntoIframe(iframe);
1153
+ }
1154
+ }
1155
+ async injectTokenIntoIframe(iframe) {
1156
+ if (this.injectedIframes.has(iframe)) return;
1157
+ const src = iframe.src || iframe.getAttribute("src") || "";
1158
+ if (!src) return;
1159
+ try {
1160
+ const url = new URL(src, window.location.origin);
1161
+ if (!this.isTiquoEmbedUrl(url)) return;
1162
+ if (url.searchParams.has("_auth_token")) {
1163
+ this.injectedIframes.add(iframe);
1164
+ return;
1165
+ }
1166
+ const { token } = await this.getIframeToken();
1167
+ url.searchParams.set("_auth_token", token);
1168
+ this.injectedIframes.add(iframe);
1169
+ iframe.src = url.toString();
1170
+ this.log("Injected auth token into existing iframe:", url.pathname);
1171
+ } catch (error) {
1172
+ this.log("Failed to inject token into iframe:", error);
1173
+ }
1174
+ }
1175
+ isTiquoEmbedUrl(url) {
1176
+ const hostname = url.hostname;
1177
+ const isTiquoHost = hostname === "book.tiquo.app" || hostname.endsWith(".tiquo.app") || hostname === "localhost";
1178
+ return isTiquoHost && url.pathname.startsWith("/embed/");
1179
+ }
1180
+ observeNewIframes() {
1181
+ if (typeof MutationObserver === "undefined") return;
1182
+ if (this.iframeObserver) return;
1183
+ this.iframeObserver = new MutationObserver((mutations) => {
1184
+ if (!this.accessToken) return;
1185
+ for (const mutation of mutations) {
1186
+ for (const node of mutation.addedNodes) {
1187
+ if (node instanceof HTMLIFrameElement) {
1188
+ this.injectTokenIntoIframe(node);
1189
+ }
1190
+ if (node instanceof HTMLElement) {
1191
+ const nested = node.querySelectorAll("iframe");
1192
+ for (const iframe of nested) {
1193
+ this.injectTokenIntoIframe(iframe);
1194
+ }
1195
+ }
1196
+ }
1197
+ }
1198
+ });
1199
+ this.iframeObserver.observe(document.body || document.documentElement, {
1200
+ childList: true,
1201
+ subtree: true
1202
+ });
1203
+ }
1076
1204
  /**
1077
1205
  * Get the current access token (for advanced use cases)
1078
1206
  */
@@ -1469,6 +1597,13 @@ var TiquoAuth = class {
1469
1597
  this.broadcastChannel.close();
1470
1598
  this.broadcastChannel = null;
1471
1599
  }
1600
+ if (this.iframeObserver) {
1601
+ this.iframeObserver.disconnect();
1602
+ this.iframeObserver = null;
1603
+ }
1604
+ if (typeof window !== "undefined") {
1605
+ delete window.__tiquo_get_iframe_token;
1606
+ }
1472
1607
  this.listeners.clear();
1473
1608
  }
1474
1609
  };
@@ -1501,6 +1636,8 @@ function useTiquoAuth(auth) {
1501
1636
  updateProfile: (updates) => auth.updateProfile(updates),
1502
1637
  getOrders: (options) => auth.getOrders(options),
1503
1638
  getBookings: (options) => auth.getBookings(options),
1639
+ getUpcomingBookings: (options) => auth.getUpcomingBookings(options),
1640
+ getReceipt: (orderId) => auth.getReceipt(orderId),
1504
1641
  getEnquiries: (options) => auth.getEnquiries(options),
1505
1642
  getIframeToken: (flowId) => auth.getIframeToken(flowId),
1506
1643
  embedCustomerFlow: auth.embedCustomerFlow.bind(auth),
package/dist/index.mjs CHANGED
@@ -676,6 +676,8 @@ var TiquoAuth = class {
676
676
  this.refreshTimer = null;
677
677
  this.isRefreshing = false;
678
678
  this.visibilityHandler = null;
679
+ this.iframeObserver = null;
680
+ this.injectedIframes = /* @__PURE__ */ new WeakSet();
679
681
  // Multi-tab sync
680
682
  this.broadcastChannel = null;
681
683
  this.isProcessingTabSync = false;
@@ -706,6 +708,7 @@ var TiquoAuth = class {
706
708
  this.checkForInjectedTokens();
707
709
  this.restoreTokens();
708
710
  this.initVisibilityHandler();
711
+ this.autoInjectIframeTokens();
709
712
  }
710
713
  // ============================================
711
714
  // PUBLIC METHODS
@@ -880,8 +883,14 @@ var TiquoAuth = class {
880
883
  };
881
884
  }
882
885
  /**
883
- * Get the authenticated customer's order history
884
- * Only returns orders for the logged-in customer
886
+ * Get the authenticated customer's order history.
887
+ *
888
+ * Returns orders across every status (draft, pending, processing, completed,
889
+ * cancelled, refunded, open_tab) — except `lost_cart`, which is internal
890
+ * marketing-recovery state and is never exposed to customers.
891
+ *
892
+ * Pass `status` to filter to a single status; pass `cursor` (an order id
893
+ * from a previous `nextCursor`) to paginate. Sorted most recent first.
885
894
  */
886
895
  async getOrders(options) {
887
896
  await this.ensureValidToken();
@@ -911,8 +920,12 @@ var TiquoAuth = class {
911
920
  return result.data || { orders: [], hasMore: false };
912
921
  }
913
922
  /**
914
- * Get the authenticated customer's booking history
915
- * Only returns bookings for the logged-in customer
923
+ * Get the authenticated customer's booking history.
924
+ *
925
+ * Pass `upcoming: true` to limit to future bookings sorted soonest-first —
926
+ * the common case for rendering "Your upcoming bookings" on a logged-in
927
+ * customer dashboard. Without it, returns all bookings sorted most recent
928
+ * first.
916
929
  */
917
930
  async getBookings(options) {
918
931
  await this.ensureValidToken();
@@ -944,6 +957,50 @@ var TiquoAuth = class {
944
957
  const result = await response.json();
945
958
  return result.data || { bookings: [], hasMore: false };
946
959
  }
960
+ /**
961
+ * Convenience wrapper around `getBookings({ upcoming: true })` for the
962
+ * common "show me my upcoming bookings" case. Results are sorted
963
+ * soonest-first.
964
+ */
965
+ async getUpcomingBookings(options) {
966
+ return this.getBookings({ ...options, upcoming: true });
967
+ }
968
+ /**
969
+ * Get a printable receipt for a single order owned by the authenticated
970
+ * customer. Only resolves for orders that are paid (full or partial),
971
+ * refunded, or completed — drafts and pending orders don't have a receipt
972
+ * yet and will throw `RECEIPT_NOT_AVAILABLE`.
973
+ *
974
+ * The returned payload bundles the order totals & items, the business's
975
+ * receipt branding (logo, VAT number, address), and the customer's own
976
+ * details so the consumer can render or print the receipt without further
977
+ * API calls.
978
+ *
979
+ * Throws `TiquoAuthError` with code:
980
+ * - `RECEIPT_NOT_AVAILABLE` — order doesn't exist for this customer, or
981
+ * it isn't paid/completed yet.
982
+ * - `NOT_AUTHENTICATED` — no valid session.
983
+ */
984
+ async getReceipt(orderId) {
985
+ await this.ensureValidToken();
986
+ this.log("Fetching receipt for order:", orderId);
987
+ const url = new URL(`${this.config.apiEndpoint}/api/client/v1/receipt`);
988
+ url.searchParams.set("orderId", orderId);
989
+ const response = await fetch(url.toString(), {
990
+ method: "GET",
991
+ headers: {
992
+ "Authorization": `Bearer ${this.accessToken}`
993
+ },
994
+ credentials: "include"
995
+ });
996
+ if (!response.ok) {
997
+ const error = await response.json().catch(() => ({ error: "Failed to get receipt" }));
998
+ const code = response.status === 404 ? "RECEIPT_NOT_AVAILABLE" : "GET_RECEIPT_FAILED";
999
+ throw new TiquoAuthError(error.error || "Failed to get receipt", code, response.status);
1000
+ }
1001
+ const result = await response.json();
1002
+ return result.data;
1003
+ }
947
1004
  /**
948
1005
  * Get the authenticated customer's enquiry history
949
1006
  * Only returns enquiries for the logged-in customer
@@ -1037,6 +1094,77 @@ var TiquoAuth = class {
1037
1094
  containerEl.appendChild(iframe);
1038
1095
  return iframe;
1039
1096
  }
1097
+ /**
1098
+ * Automatically find and inject auth tokens into existing Tiquo booking
1099
+ * iframes on the page. Also watches for iframes added later via
1100
+ * MutationObserver. This enables token injection even when the site uses
1101
+ * a static <iframe> embed snippet rather than embedCustomerFlow().
1102
+ */
1103
+ autoInjectIframeTokens() {
1104
+ if (typeof document === "undefined") return;
1105
+ if (!this.accessToken) return;
1106
+ if (typeof window !== "undefined") {
1107
+ window.__tiquo_get_iframe_token = () => this.getIframeToken();
1108
+ }
1109
+ Promise.resolve().then(() => this.scanAndInjectIframes());
1110
+ this.observeNewIframes();
1111
+ }
1112
+ async scanAndInjectIframes() {
1113
+ if (!this.accessToken) return;
1114
+ const iframes = document.querySelectorAll("iframe");
1115
+ for (const iframe of iframes) {
1116
+ await this.injectTokenIntoIframe(iframe);
1117
+ }
1118
+ }
1119
+ async injectTokenIntoIframe(iframe) {
1120
+ if (this.injectedIframes.has(iframe)) return;
1121
+ const src = iframe.src || iframe.getAttribute("src") || "";
1122
+ if (!src) return;
1123
+ try {
1124
+ const url = new URL(src, window.location.origin);
1125
+ if (!this.isTiquoEmbedUrl(url)) return;
1126
+ if (url.searchParams.has("_auth_token")) {
1127
+ this.injectedIframes.add(iframe);
1128
+ return;
1129
+ }
1130
+ const { token } = await this.getIframeToken();
1131
+ url.searchParams.set("_auth_token", token);
1132
+ this.injectedIframes.add(iframe);
1133
+ iframe.src = url.toString();
1134
+ this.log("Injected auth token into existing iframe:", url.pathname);
1135
+ } catch (error) {
1136
+ this.log("Failed to inject token into iframe:", error);
1137
+ }
1138
+ }
1139
+ isTiquoEmbedUrl(url) {
1140
+ const hostname = url.hostname;
1141
+ const isTiquoHost = hostname === "book.tiquo.app" || hostname.endsWith(".tiquo.app") || hostname === "localhost";
1142
+ return isTiquoHost && url.pathname.startsWith("/embed/");
1143
+ }
1144
+ observeNewIframes() {
1145
+ if (typeof MutationObserver === "undefined") return;
1146
+ if (this.iframeObserver) return;
1147
+ this.iframeObserver = new MutationObserver((mutations) => {
1148
+ if (!this.accessToken) return;
1149
+ for (const mutation of mutations) {
1150
+ for (const node of mutation.addedNodes) {
1151
+ if (node instanceof HTMLIFrameElement) {
1152
+ this.injectTokenIntoIframe(node);
1153
+ }
1154
+ if (node instanceof HTMLElement) {
1155
+ const nested = node.querySelectorAll("iframe");
1156
+ for (const iframe of nested) {
1157
+ this.injectTokenIntoIframe(iframe);
1158
+ }
1159
+ }
1160
+ }
1161
+ }
1162
+ });
1163
+ this.iframeObserver.observe(document.body || document.documentElement, {
1164
+ childList: true,
1165
+ subtree: true
1166
+ });
1167
+ }
1040
1168
  /**
1041
1169
  * Get the current access token (for advanced use cases)
1042
1170
  */
@@ -1433,6 +1561,13 @@ var TiquoAuth = class {
1433
1561
  this.broadcastChannel.close();
1434
1562
  this.broadcastChannel = null;
1435
1563
  }
1564
+ if (this.iframeObserver) {
1565
+ this.iframeObserver.disconnect();
1566
+ this.iframeObserver = null;
1567
+ }
1568
+ if (typeof window !== "undefined") {
1569
+ delete window.__tiquo_get_iframe_token;
1570
+ }
1436
1571
  this.listeners.clear();
1437
1572
  }
1438
1573
  };
@@ -1465,6 +1600,8 @@ function useTiquoAuth(auth) {
1465
1600
  updateProfile: (updates) => auth.updateProfile(updates),
1466
1601
  getOrders: (options) => auth.getOrders(options),
1467
1602
  getBookings: (options) => auth.getBookings(options),
1603
+ getUpcomingBookings: (options) => auth.getUpcomingBookings(options),
1604
+ getReceipt: (orderId) => auth.getReceipt(orderId),
1468
1605
  getEnquiries: (options) => auth.getEnquiries(options),
1469
1606
  getIframeToken: (flowId) => auth.getIframeToken(flowId),
1470
1607
  embedCustomerFlow: auth.embedCustomerFlow.bind(auth),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiquo/dom-package",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
4
4
  "description": "Tiquo SDK for third-party websites - authentication, customer profiles, orders, bookings, and enquiries",
5
5
  "sideEffects": true,
6
6
  "publishConfig": {