@tiquo/dom-package 1.1.2 β†’ 1.3.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/README.md CHANGED
@@ -9,6 +9,7 @@ Tiquo SDK for third-party websites. Integrate authentication, customer profiles,
9
9
  - **Multi-Tab Sync** - Auth state automatically syncs across all browser tabs
10
10
  - **Native App Integration** - WebView token injection for iOS/Android hybrid apps
11
11
  - **Customer Flow Integration** - Embed authenticated customer flows with one line of code
12
+ - **Phone Number Utilities** - Validation, formatting, and country code support for 200+ countries
12
13
  - **Framework Agnostic** - Works with React, Vue, Svelte, or vanilla JavaScript
13
14
  - **TypeScript Support** - Full type definitions included
14
15
 
@@ -123,12 +124,14 @@ if (auth.isAuthenticated()) {
123
124
 
124
125
  Update the authenticated customer's profile. Only allows updating the logged-in customer's own data.
125
126
 
127
+ Phone numbers are **automatically normalized** to E.164 format before being sent to the API. You can pass common formats and they'll be cleaned up, but for best results include the country code.
128
+
126
129
  ```typescript
127
130
  const result = await auth.updateProfile({
128
131
  firstName: 'John',
129
132
  lastName: 'Doe',
130
133
  displayName: 'Johnny',
131
- phone: '+1234567890',
134
+ phone: '+1 (555) 456-7890', // Auto-normalized to +15554567890
132
135
  });
133
136
 
134
137
  console.log(result.customer.firstName); // 'John'
@@ -138,8 +141,11 @@ console.log(result.customer.firstName); // 'John'
138
141
  - `firstName` - Customer's first name
139
142
  - `lastName` - Customer's last name
140
143
  - `displayName` - Display name (nickname)
141
- - `phone` - Primary phone number
144
+ - `phone` - Primary phone number (E.164 format recommended: `+[country code][number]`)
142
145
  - `profilePhoto` - URL to profile photo
146
+ - `phones` - Full phone list (array of `{ number, isPrimary, order }`)
147
+
148
+ > **Important:** Phone numbers should include a country code (e.g., `+1` for US, `+44` for UK). Use `TiquoPhone.buildPhone()` to construct properly formatted numbers from a country selector + national number input. See [Phone Number Utilities](#phone-number-utilities) below.
143
149
 
144
150
  #### `logout(): Promise<void>`
145
151
 
@@ -427,6 +433,164 @@ async function logout() {
427
433
  </template>
428
434
  ```
429
435
 
436
+ ## Phone Number Utilities
437
+
438
+ The SDK includes `TiquoPhone`, a static utility class for working with international phone numbers. It supports **200+ countries** and handles validation, normalization, formatting, and country detection β€” everything you need to build a phone input with a country selector.
439
+
440
+ ### Import
441
+
442
+ ```typescript
443
+ import { TiquoPhone } from '@tiquo/dom-package';
444
+ ```
445
+
446
+ ### Get Countries (for building a dropdown)
447
+
448
+ ```typescript
449
+ const countries = TiquoPhone.getCountries();
450
+ // Returns alphabetically sorted array:
451
+ // [
452
+ // { code: "AF", name: "Afghanistan", dialCode: "+93", format: "XX XXX XXXX", flag: "πŸ‡¦πŸ‡«" },
453
+ // ...
454
+ // { code: "GB", name: "United Kingdom", dialCode: "+44", format: "XXXX XXXXXX", flag: "πŸ‡¬πŸ‡§" },
455
+ // { code: "US", name: "United States", dialCode: "+1", format: "(XXX) XXX-XXXX", flag: "πŸ‡ΊπŸ‡Έ" },
456
+ // ...
457
+ // ]
458
+ ```
459
+
460
+ ### Build a Phone Number (country selector + input)
461
+
462
+ ```typescript
463
+ // Combine a country selector value with a local number input
464
+ TiquoPhone.buildPhone("GB", "07911 123456"); // β†’ "+447911123456"
465
+ TiquoPhone.buildPhone("US", "(555) 456-7890"); // β†’ "+15554567890"
466
+ TiquoPhone.buildPhone("FR", "06 65 08 50 44"); // β†’ "+33665085044"
467
+ ```
468
+
469
+ ### Validate a Phone Number
470
+
471
+ ```typescript
472
+ TiquoPhone.validate("+44 7911 123456");
473
+ // β†’ { valid: true, normalized: "+447911123456", countryCode: "GB" }
474
+
475
+ TiquoPhone.validate("555-1234");
476
+ // β†’ { valid: false, reason: "Phone number must include a country code..." }
477
+
478
+ TiquoPhone.validate("+999 123");
479
+ // β†’ { valid: false, reason: "Phone number is too short..." }
480
+ ```
481
+
482
+ ### Normalize to E.164
483
+
484
+ ```typescript
485
+ TiquoPhone.normalize("+1 (555) 456-7890"); // β†’ "+15554567890"
486
+ TiquoPhone.normalize("+44 (0)20 3227 4972"); // β†’ "+442032274972"
487
+ TiquoPhone.normalize("0033 6 65 08 50 44"); // β†’ "+33665085044"
488
+ TiquoPhone.normalize("+49 0151 12345678"); // β†’ "+4915112345678"
489
+ ```
490
+
491
+ ### Format for Display
492
+
493
+ ```typescript
494
+ TiquoPhone.format("+15554567890"); // β†’ "+1 (555) 456-7890"
495
+ TiquoPhone.format("+447911123456"); // β†’ "+44 7911 123456"
496
+ TiquoPhone.format("+33665085044"); // β†’ "+33 6 65 08 50 44"
497
+ ```
498
+
499
+ ### Detect Country
500
+
501
+ ```typescript
502
+ TiquoPhone.detectCountry("+15554567890"); // β†’ "US"
503
+ TiquoPhone.detectCountry("+447911123456"); // β†’ "GB"
504
+ TiquoPhone.detectCountry("+33665085044"); // β†’ "FR"
505
+ ```
506
+
507
+ ### Get Dial Code
508
+
509
+ ```typescript
510
+ TiquoPhone.getDialCode("US"); // β†’ "+1"
511
+ TiquoPhone.getDialCode("GB"); // β†’ "+44"
512
+ TiquoPhone.getDialCode("FR"); // β†’ "+33"
513
+ ```
514
+
515
+ ### Example: Phone Input with Country Selector (React)
516
+
517
+ ```tsx
518
+ import { TiquoPhone } from '@tiquo/dom-package';
519
+ import { useState } from 'react';
520
+
521
+ function PhoneInput({ onChange }) {
522
+ const countries = TiquoPhone.getCountries();
523
+ const [country, setCountry] = useState('US');
524
+ const [number, setNumber] = useState('');
525
+ const [error, setError] = useState('');
526
+
527
+ const handleChange = (newCountry, newNumber) => {
528
+ setCountry(newCountry);
529
+ setNumber(newNumber);
530
+
531
+ if (!newNumber.trim()) {
532
+ setError('');
533
+ onChange('');
534
+ return;
535
+ }
536
+
537
+ const phone = TiquoPhone.buildPhone(newCountry, newNumber);
538
+ if (!phone) {
539
+ setError('Invalid phone number');
540
+ onChange('');
541
+ return;
542
+ }
543
+
544
+ const result = TiquoPhone.validate(phone);
545
+ if (result.valid) {
546
+ setError('');
547
+ onChange(result.normalized);
548
+ } else {
549
+ setError(result.reason);
550
+ onChange('');
551
+ }
552
+ };
553
+
554
+ return (
555
+ <div>
556
+ <select
557
+ value={country}
558
+ onChange={(e) => handleChange(e.target.value, number)}
559
+ >
560
+ {countries.map((c) => (
561
+ <option key={c.code} value={c.code}>
562
+ {c.flag} {c.name} ({c.dialCode})
563
+ </option>
564
+ ))}
565
+ </select>
566
+ <input
567
+ type="tel"
568
+ value={number}
569
+ onChange={(e) => handleChange(country, e.target.value)}
570
+ placeholder={countries.find(c => c.code === country)?.format}
571
+ />
572
+ {error && <p style={{ color: 'red' }}>{error}</p>}
573
+ </div>
574
+ );
575
+ }
576
+
577
+ // Usage with updateProfile:
578
+ function ProfilePage() {
579
+ const [phone, setPhone] = useState('');
580
+
581
+ const handleSave = async () => {
582
+ await auth.updateProfile({ phone }); // Already in E.164 format
583
+ };
584
+
585
+ return (
586
+ <div>
587
+ <PhoneInput onChange={setPhone} />
588
+ <button onClick={handleSave} disabled={!phone}>Save</button>
589
+ </div>
590
+ );
591
+ }
592
+ ```
593
+
430
594
  ## Multi-Tab Session Sync
431
595
 
432
596
  The SDK automatically synchronizes authentication state across all browser tabs using the [BroadcastChannel API](https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel). This means:
package/dist/index.d.mts CHANGED
@@ -4,13 +4,13 @@
4
4
  * This module handles setting a cross-subdomain cookie that stores customer user IDs.
5
5
  * The cookie is shared across all *.tiquo.app subdomains and persists for 1 year.
6
6
  *
7
- * The cookie is NOT set if the user is logged in with Clerk (dashboard staff).
7
+ * The cookie is NOT set if the user is a dashboard staff member.
8
8
  */
9
9
  /**
10
- * Check if the current user is authenticated via Clerk (dashboard staff).
11
- * If they are, we should NOT set the customer cookie.
10
+ * Check if the current user has an active dashboard staff session.
11
+ * If they do, we should NOT set the customer cookie.
12
12
  */
13
- declare function isClerkAuthenticated(): boolean;
13
+ declare function isDashboardSession(): boolean;
14
14
  /**
15
15
  * Get the list of customer user IDs from the cookie.
16
16
  */
@@ -18,7 +18,7 @@ declare function getCustomerUserIds(): string[];
18
18
  /**
19
19
  * Add a customer user ID to the cookie.
20
20
  * Does nothing if:
21
- * - The user is authenticated via Clerk (dashboard staff)
21
+ * - The user has an active dashboard staff session
22
22
  * - The user ID is already in the cookie
23
23
  * - Running in a non-browser environment
24
24
  *
@@ -30,7 +30,7 @@ declare function addCustomerUserId(userId: string): void;
30
30
  * This fetches the user's email associated with the cookie user IDs.
31
31
  * Results are cached for 5 minutes to avoid repeated API calls.
32
32
  *
33
- * @param organizationId - The Clerk organization ID
33
+ * @param organizationId - The organization ID
34
34
  * @returns Promise with email and optional name fields, or null if not found
35
35
  */
36
36
  declare function getPrefilledEmailFromCookie(organizationId: string): Promise<{
@@ -47,7 +47,7 @@ declare function clearCachedEmail(): void;
47
47
  * Track customer presence when the cookie is detected.
48
48
  * This updates the customer's online status and logs activity.
49
49
  *
50
- * @param organizationId - The Clerk organization ID
50
+ * @param organizationId - The organization ID
51
51
  * @returns Promise with tracking results
52
52
  */
53
53
  declare function trackCustomerPresence(organizationId: string): Promise<{
@@ -61,6 +61,109 @@ declare function trackCustomerPresence(organizationId: string): Promise<{
61
61
  error?: string;
62
62
  }>;
63
63
 
64
+ /**
65
+ * @tiquo/dom-package - Phone Utilities
66
+ *
67
+ * Comprehensive phone number validation, normalization, and formatting
68
+ * with worldwide country code support. Designed for use in websites
69
+ * that integrate the Tiquo DOM package.
70
+ */
71
+ interface CountryPhoneInfo {
72
+ /** ISO 3166-1 alpha-2 code (e.g. "US", "GB") */
73
+ code: string;
74
+ /** Country name */
75
+ name: string;
76
+ /** Dial code with + prefix (e.g. "+1", "+44") */
77
+ dialCode: string;
78
+ /** Example format (e.g. "(XXX) XXX-XXXX") */
79
+ format: string;
80
+ /** Flag emoji */
81
+ flag: string;
82
+ }
83
+ interface PhoneValidationResult {
84
+ /** Whether the phone number is valid for submission */
85
+ valid: boolean;
86
+ /** Normalized E.164 format if valid (e.g. "+15551234567") */
87
+ normalized?: string;
88
+ /** Detected country code if identifiable */
89
+ countryCode?: string;
90
+ /** Reason for invalid result */
91
+ reason?: string;
92
+ }
93
+ /**
94
+ * Normalize a phone number to E.164 format (+digits).
95
+ * Handles all worldwide formats: international prefixes, domestic leading zeros,
96
+ * formatting characters, (0) patterns, 00-prefix, Unicode artifacts.
97
+ *
98
+ * @example
99
+ * normalizePhone("+1 (555) 456-7890") // β†’ "+15554567890"
100
+ * normalizePhone("+44 (0)20 3227 4972") // β†’ "+442032274972"
101
+ * normalizePhone("0033 6 65 08 50 44") // β†’ "+33665085044"
102
+ * normalizePhone("+49 0151 12345678") // β†’ "+4915112345678"
103
+ */
104
+ declare function normalizePhone(phone: string): string;
105
+ /**
106
+ * Validate a phone number and return detailed results.
107
+ * Checks format, length, and country code presence.
108
+ *
109
+ * @example
110
+ * validatePhone("+15554567890") // β†’ { valid: true, normalized: "+15554567890", countryCode: "US" }
111
+ * validatePhone("555-1234") // β†’ { valid: false, reason: "Phone number must include a country code..." }
112
+ * validatePhone("+44 7911 123456") // β†’ { valid: true, normalized: "+447911123456", countryCode: "GB" }
113
+ */
114
+ declare function validatePhone(phone: string): PhoneValidationResult;
115
+ /**
116
+ * Detect the country from a phone number's calling code.
117
+ * Returns the ISO 3166-1 alpha-2 country code, or null if unrecognized.
118
+ *
119
+ * @example
120
+ * detectCountry("+15554567890") // β†’ "US"
121
+ * detectCountry("+447911123456") // β†’ "GB"
122
+ * detectCountry("+33665085044") // β†’ "FR"
123
+ */
124
+ declare function detectCountry(phone: string): string | null;
125
+ /**
126
+ * Format a phone number for display using country-specific patterns.
127
+ *
128
+ * @example
129
+ * formatPhone("+15554567890") // β†’ "+1 (555) 456-7890"
130
+ * formatPhone("+447911123456") // β†’ "+44 7911 123456"
131
+ * formatPhone("+33665085044") // β†’ "+33 6 65 08 50 44"
132
+ */
133
+ declare function formatPhone(phone: string): string;
134
+ /**
135
+ * Get the full list of countries with phone info for building UI selectors.
136
+ * Returns countries sorted alphabetically by name.
137
+ *
138
+ * @example
139
+ * const countries = getCountries();
140
+ * // Build a <select> dropdown:
141
+ * countries.forEach(c => {
142
+ * // c.code = "GB", c.name = "United Kingdom", c.dialCode = "+44",
143
+ * // c.format = "XXXX XXXXXX", c.flag = "πŸ‡¬πŸ‡§"
144
+ * });
145
+ */
146
+ declare function getCountries(): CountryPhoneInfo[];
147
+ /**
148
+ * Get the dial code for a specific country.
149
+ *
150
+ * @example
151
+ * getDialCode("US") // β†’ "+1"
152
+ * getDialCode("GB") // β†’ "+44"
153
+ * getDialCode("FR") // β†’ "+33"
154
+ */
155
+ declare function getDialCode(countryCode: string): string | null;
156
+ /**
157
+ * Build a full E.164 phone number from a country code and national number.
158
+ * Strips the domestic leading zero if present.
159
+ *
160
+ * @example
161
+ * buildPhone("GB", "07911 123456") // β†’ "+447911123456"
162
+ * buildPhone("US", "(555) 456-7890") // β†’ "+15554567890"
163
+ * buildPhone("FR", "06 65 08 50 44") // β†’ "+33665085044"
164
+ */
165
+ declare function buildPhone(countryCode: string, nationalNumber: string): string | null;
166
+
64
167
  /**
65
168
  * @tiquo/dom-package
66
169
  *
@@ -190,9 +293,21 @@ interface ProfileUpdateData {
190
293
  firstName?: string;
191
294
  lastName?: string;
192
295
  displayName?: string;
296
+ /**
297
+ * Primary phone number in E.164 format: +[country code][number]
298
+ * Examples: "+15551234567" (US), "+447911123456" (UK), "+33665085044" (FR)
299
+ *
300
+ * The SDK auto-normalizes common formats (strips spaces, dashes, parentheses,
301
+ * handles +/00 prefixes). Use TiquoPhone.buildPhone("GB", "07911 123456")
302
+ * for country+national input, or TiquoPhone.validate() to pre-check.
303
+ */
193
304
  phone?: string;
194
305
  profilePhoto?: string;
195
306
  emails?: TiquoCustomerEmail[];
307
+ /**
308
+ * Full phone list. Each number should be in E.164 format: +[country code][number].
309
+ * The SDK auto-normalizes all numbers before sending to the API.
310
+ */
196
311
  phones?: TiquoCustomerPhone[];
197
312
  }
198
313
  interface ProfileUpdateResult {
@@ -324,7 +439,12 @@ declare class TiquoAuth {
324
439
  isAuthenticated(): boolean;
325
440
  /**
326
441
  * Update the authenticated customer's profile
327
- * Only allows updating the logged-in customer's own data
442
+ * Only allows updating the logged-in customer's own data.
443
+ *
444
+ * Phone numbers are automatically normalized to E.164 format (+digits).
445
+ * For best results, include a country code: "+1 (555) 456-7890" or "+44 7911 123456".
446
+ * Use TiquoPhone.validate() to check before submitting, or TiquoPhone.buildPhone()
447
+ * to construct from a country selector and national number.
328
448
  */
329
449
  updateProfile(updates: ProfileUpdateData): Promise<ProfileUpdateResult>;
330
450
  /**
@@ -438,5 +558,57 @@ declare function useTiquoAuth(auth: TiquoAuth): {
438
558
  }) => Promise<HTMLIFrameElement>;
439
559
  onAuthStateChange: (cb: AuthStateChangeCallback) => () => void;
440
560
  };
561
+ /**
562
+ * Phone number utilities for building phone inputs and validating numbers.
563
+ * All methods are static β€” no instantiation needed.
564
+ *
565
+ * @example
566
+ * ```typescript
567
+ * import { TiquoPhone } from '@tiquo/dom-package';
568
+ *
569
+ * // Get countries for a selector dropdown
570
+ * const countries = TiquoPhone.getCountries();
571
+ *
572
+ * // Build a phone number from country + national number
573
+ * const phone = TiquoPhone.buildPhone("GB", "07911 123456");
574
+ * // β†’ "+447911123456"
575
+ *
576
+ * // Validate before submitting
577
+ * const result = TiquoPhone.validate("+44 7911 123456");
578
+ * // β†’ { valid: true, normalized: "+447911123456", countryCode: "GB" }
579
+ *
580
+ * // Normalize for submission
581
+ * const e164 = TiquoPhone.normalize("+1 (555) 456-7890");
582
+ * // β†’ "+15554567890"
583
+ *
584
+ * // Format for display
585
+ * const display = TiquoPhone.format("+15554567890");
586
+ * // β†’ "+1 (555) 456-7890"
587
+ *
588
+ * // Detect country from a phone number
589
+ * const country = TiquoPhone.detectCountry("+33665085044");
590
+ * // β†’ "FR"
591
+ *
592
+ * // Get dial code for a country
593
+ * const dialCode = TiquoPhone.getDialCode("US");
594
+ * // β†’ "+1"
595
+ * ```
596
+ */
597
+ declare class TiquoPhone {
598
+ /** Normalize a phone number to E.164 format (+digits). */
599
+ static normalize: typeof normalizePhone;
600
+ /** Validate a phone number with detailed result. */
601
+ static validate: typeof validatePhone;
602
+ /** Detect the country from a phone number's calling code. */
603
+ static detectCountry: typeof detectCountry;
604
+ /** Format a phone number for display using country-specific patterns. */
605
+ static format: typeof formatPhone;
606
+ /** Get the full list of countries with phone info for building UI selectors. */
607
+ static getCountries: typeof getCountries;
608
+ /** Get the dial code for a specific country (e.g. "US" β†’ "+1"). */
609
+ static getDialCode: typeof getDialCode;
610
+ /** Build an E.164 phone number from a country code and national number. */
611
+ static buildPhone: typeof buildPhone;
612
+ }
441
613
 
442
- export { type AuthStateChangeCallback, type GetBookingsOptions, type GetBookingsResult, type GetEnquiriesOptions, type GetEnquiriesResult, type GetOrdersOptions, type GetOrdersResult, type IframeTokenResult, type ProfileUpdateData, type ProfileUpdateResult, type SendOTPResult, TiquoAuth, type TiquoAuthConfig, TiquoAuthError, type TiquoBooking, type TiquoCustomer, type TiquoCustomerEmail, type TiquoCustomerPhone, type TiquoEnquiry, type TiquoOrder, type TiquoOrderItem, type TiquoSession, type TiquoUser, type VerifyOTPResult, addCustomerUserId, clearCachedEmail, TiquoAuth as default, getCustomerUserIds, getPrefilledEmailFromCookie, isClerkAuthenticated, trackCustomerPresence, useTiquoAuth };
614
+ 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 };