@resira/sdk 0.4.1 → 0.4.2
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.cjs +128 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +135 -5
- package/dist/index.d.ts +135 -5
- package/dist/index.js +128 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -505,6 +505,134 @@ var Resira = class {
|
|
|
505
505
|
`/dishes/${encodeURIComponent(dishId)}`
|
|
506
506
|
);
|
|
507
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* Retrieve a checkout session by its token.
|
|
510
|
+
*
|
|
511
|
+
* Used to hydrate the checkout screen when opening the widget
|
|
512
|
+
* with a pre-created session token (skip service selection).
|
|
513
|
+
*
|
|
514
|
+
* ```ts
|
|
515
|
+
* const { session } = await resira.getCheckoutSession("cs_ab12cd34ef56");
|
|
516
|
+
* console.log(session.productName, session.priceCents);
|
|
517
|
+
* ```
|
|
518
|
+
*/
|
|
519
|
+
async getCheckoutSession(token) {
|
|
520
|
+
if (!token) {
|
|
521
|
+
throw new Error("token is required for getCheckoutSession");
|
|
522
|
+
}
|
|
523
|
+
const url = `${this.getBaseUrl()}/v2/public/checkout-sessions/${encodeURIComponent(token)}`;
|
|
524
|
+
const headers = {
|
|
525
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
526
|
+
Accept: "application/json"
|
|
527
|
+
};
|
|
528
|
+
let lastError;
|
|
529
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
530
|
+
if (attempt > 0) {
|
|
531
|
+
const backoff = this.calculateBackoff(attempt, lastError);
|
|
532
|
+
await sleep(backoff);
|
|
533
|
+
}
|
|
534
|
+
let response;
|
|
535
|
+
try {
|
|
536
|
+
response = await this._fetch(url, { method: "GET", headers });
|
|
537
|
+
} catch (err) {
|
|
538
|
+
lastError = new ResiraNetworkError(
|
|
539
|
+
`Network request failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
540
|
+
err
|
|
541
|
+
);
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
if (response.ok) {
|
|
545
|
+
const raw = await response.json();
|
|
546
|
+
return deepKeysToCamel(raw);
|
|
547
|
+
}
|
|
548
|
+
let errorBody;
|
|
549
|
+
try {
|
|
550
|
+
errorBody = await response.json();
|
|
551
|
+
} catch {
|
|
552
|
+
errorBody = { error: response.statusText || `HTTP ${response.status}` };
|
|
553
|
+
}
|
|
554
|
+
if (response.status === 429) {
|
|
555
|
+
lastError = new ResiraRateLimitError(errorBody, response);
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
if (response.status >= 500) {
|
|
559
|
+
lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
throw new ResiraApiError(response.status, response.statusText, errorBody, response);
|
|
563
|
+
}
|
|
564
|
+
throw lastError;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Create a new checkout session.
|
|
568
|
+
*
|
|
569
|
+
* External sites can create a session with product, slot, and guest
|
|
570
|
+
* details, then pass the returned token to the widget to skip
|
|
571
|
+
* directly to checkout.
|
|
572
|
+
*
|
|
573
|
+
* ```ts
|
|
574
|
+
* const { sessionToken } = await resira.createCheckoutSession({
|
|
575
|
+
* productId: "prod-123",
|
|
576
|
+
* durationMinutes: 30,
|
|
577
|
+
* partySize: 2,
|
|
578
|
+
* date: "2026-07-01",
|
|
579
|
+
* startTime: "2026-07-01T10:00:00Z",
|
|
580
|
+
* endTime: "2026-07-01T10:30:00Z",
|
|
581
|
+
* });
|
|
582
|
+
* ```
|
|
583
|
+
*/
|
|
584
|
+
async createCheckoutSession(payload, options) {
|
|
585
|
+
const idempotencyKey = options?.idempotencyKey ?? uuid();
|
|
586
|
+
const url = `${this.getBaseUrl()}/v2/public/checkout-sessions`;
|
|
587
|
+
const headers = {
|
|
588
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
589
|
+
Accept: "application/json",
|
|
590
|
+
"Content-Type": "application/json",
|
|
591
|
+
"Idempotency-Key": idempotencyKey
|
|
592
|
+
};
|
|
593
|
+
const init = {
|
|
594
|
+
method: "POST",
|
|
595
|
+
headers,
|
|
596
|
+
body: JSON.stringify(payload)
|
|
597
|
+
};
|
|
598
|
+
let lastError;
|
|
599
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
600
|
+
if (attempt > 0) {
|
|
601
|
+
const backoff = this.calculateBackoff(attempt, lastError);
|
|
602
|
+
await sleep(backoff);
|
|
603
|
+
}
|
|
604
|
+
let response;
|
|
605
|
+
try {
|
|
606
|
+
response = await this._fetch(url, init);
|
|
607
|
+
} catch (err) {
|
|
608
|
+
lastError = new ResiraNetworkError(
|
|
609
|
+
`Network request failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
610
|
+
err
|
|
611
|
+
);
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
if (response.ok) {
|
|
615
|
+
const raw = await response.json();
|
|
616
|
+
return deepKeysToCamel(raw);
|
|
617
|
+
}
|
|
618
|
+
let errorBody;
|
|
619
|
+
try {
|
|
620
|
+
errorBody = await response.json();
|
|
621
|
+
} catch {
|
|
622
|
+
errorBody = { error: response.statusText || `HTTP ${response.status}` };
|
|
623
|
+
}
|
|
624
|
+
if (response.status === 429) {
|
|
625
|
+
lastError = new ResiraRateLimitError(errorBody, response);
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
if (response.status >= 500) {
|
|
629
|
+
lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
throw new ResiraApiError(response.status, response.statusText, errorBody, response);
|
|
633
|
+
}
|
|
634
|
+
throw lastError;
|
|
635
|
+
}
|
|
508
636
|
/**
|
|
509
637
|
* Create a Stripe payment intent for a booking.
|
|
510
638
|
*
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/routing.ts","../src/client.ts"],"names":["url"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ApiErrorBody } from \"./types.js\";\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Base error\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Base error class for all SDK errors.\r\n *\r\n * Use `instanceof` checks to distinguish error types:\r\n *\r\n * ```ts\r\n * try { … } catch (e) {\r\n * if (e instanceof ResiraRateLimitError) { … }\r\n * if (e instanceof ResiraApiError) { … }\r\n * if (e instanceof ResiraNetworkError) { … }\r\n * }\r\n * ```\r\n */\r\nexport class ResiraError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = \"ResiraError\";\r\n // Fix prototype chain for instanceof to work after transpilation\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// API error (4xx / 5xx with a parsed body)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Thrown when the API returns a non-2xx response with a JSON body.\r\n *\r\n * ```ts\r\n * err.status // 400, 404, 409, 422, 500, …\r\n * err.code // HTTP status text (e.g. \"Not Found\")\r\n * err.body // Parsed `{ error: \"…\" }` from the server\r\n * ```\r\n */\r\nexport class ResiraApiError extends ResiraError {\r\n /** HTTP status code. */\r\n readonly status: number;\r\n /** HTTP status text (e.g. `\"Bad Request\"`). */\r\n readonly code: string;\r\n /** Parsed error body from the API. */\r\n readonly body: ApiErrorBody;\r\n /** The full `Response` object (headers are still accessible). */\r\n readonly response: Response;\r\n\r\n constructor(status: number, code: string, body: ApiErrorBody, response: Response) {\r\n super(body.error || `API error ${status}`);\r\n this.name = \"ResiraApiError\";\r\n this.status = status;\r\n this.code = code;\r\n this.body = body;\r\n this.response = response;\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n\r\n /** Whether this error is retryable (5xx or 429). */\r\n get retryable(): boolean {\r\n return this.status === 429 || this.status >= 500;\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Rate limit error (429)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Thrown when the API returns 429 Too Many Requests.\r\n *\r\n * ```ts\r\n * err.retryAfter // Seconds until the client should retry\r\n * ```\r\n */\r\nexport class ResiraRateLimitError extends ResiraApiError {\r\n /** Seconds to wait before retrying (from `Retry-After` header). */\r\n readonly retryAfter: number;\r\n\r\n constructor(body: ApiErrorBody, response: Response) {\r\n super(429, \"Too Many Requests\", body, response);\r\n this.name = \"ResiraRateLimitError\";\r\n this.retryAfter =\r\n body.retryAfter ??\r\n (Number.parseInt(response.headers.get(\"retry-after\") ?? \"60\", 10) ||\r\n 60);\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Network error (fetch failed, DNS, timeout, etc.)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Thrown when the request never reached the server.\r\n *\r\n * This wraps the underlying `TypeError` or `DOMException` from\r\n * `fetch()` failures (DNS resolution, TLS, network offline, etc.).\r\n */\r\nexport class ResiraNetworkError extends ResiraError {\r\n /** The original error thrown by `fetch`. */\r\n readonly cause: unknown;\r\n\r\n constructor(message: string, cause: unknown) {\r\n super(message);\r\n this.name = \"ResiraNetworkError\";\r\n this.cause = cause;\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n}\r\n","import type { ResiraConfig, WeightedBaseUrl } from \"./types.js\";\r\n\r\ninterface ResolvedBaseUrl {\r\n url: string;\r\n weight: number;\r\n}\r\n\r\nconst DEFAULT_LOCAL_BASE_URLS: ResolvedBaseUrl[] = [\r\n { url: \"http://localhost:3001\", weight: 100 },\r\n];\r\n\r\nconst DEFAULT_PRODUCTION_BASE_URLS: ResolvedBaseUrl[] = [\r\n { url: \"https://api.resira.app\", weight: 100 },\r\n];\r\n\r\nfunction readEnv(name: string): string | undefined {\r\n if (typeof process === \"undefined\") return undefined;\r\n return process.env?.[name];\r\n}\r\n\r\n\r\nfunction normalizeUrl(url: string): string {\r\n return url.trim().replace(/\\/+$/, \"\");\r\n}\r\n\r\nfunction isPositiveNumber(value: unknown): value is number {\r\n return typeof value === \"number\" && Number.isFinite(value) && value > 0;\r\n}\r\n\r\nfunction toResolvedBaseUrl(value: string | WeightedBaseUrl): ResolvedBaseUrl | null {\r\n if (typeof value === \"string\") {\r\n const url = normalizeUrl(value);\r\n return url ? { url, weight: 1 } : null;\r\n }\r\n\r\n const url = normalizeUrl(value.url);\r\n if (!url) return null;\r\n\r\n return {\r\n url,\r\n weight: isPositiveNumber(value.weight) ? value.weight : 1,\r\n };\r\n}\r\n\r\nfunction parseJsonBaseUrls(value: string): ResolvedBaseUrl[] {\r\n const parsed = JSON.parse(value) as unknown;\r\n if (!Array.isArray(parsed)) return [];\r\n\r\n return parsed.flatMap((entry) => {\r\n const resolved =\r\n typeof entry === \"string\"\r\n ? toResolvedBaseUrl(entry)\r\n : entry && typeof entry === \"object\"\r\n ? toResolvedBaseUrl(entry as WeightedBaseUrl)\r\n : null;\r\n\r\n return resolved ? [resolved] : [];\r\n });\r\n}\r\n\r\nfunction parseDelimitedBaseUrls(value: string): ResolvedBaseUrl[] {\r\n return value\r\n .split(\",\")\r\n .flatMap((entry) => {\r\n const [rawUrl, rawWeight] = entry.split(\"|\");\r\n const url = normalizeUrl(rawUrl ?? \"\");\r\n if (!url) return [];\r\n\r\n const parsedWeight = rawWeight ? Number(rawWeight.trim()) : 1;\r\n return [{\r\n url,\r\n weight: isPositiveNumber(parsedWeight) ? parsedWeight : 1,\r\n }];\r\n });\r\n}\r\n\r\nfunction parseEnvBaseUrls(value: string | undefined): ResolvedBaseUrl[] {\r\n if (!value) return [];\r\n\r\n try {\r\n const parsed = parseJsonBaseUrls(value);\r\n if (parsed.length > 0) return parsed;\r\n } catch {\r\n // Fall back to delimiter parsing.\r\n }\r\n\r\n return parseDelimitedBaseUrls(value);\r\n}\r\n\r\nexport function resolveBaseUrls(config: ResiraConfig): ResolvedBaseUrl[] {\r\n if (config.baseUrl) {\r\n return [{ url: normalizeUrl(config.baseUrl), weight: 100 }];\r\n }\r\n\r\n if (config.baseUrls?.length) {\r\n const explicit = config.baseUrls.flatMap((entry) => {\r\n const resolved = toResolvedBaseUrl(entry);\r\n return resolved ? [resolved] : [];\r\n });\r\n\r\n if (explicit.length > 0) return explicit;\r\n }\r\n\r\n const envBaseUrls = parseEnvBaseUrls(readEnv(\"RESIRA_API_BASE_URLS\"));\r\n if (envBaseUrls.length > 0) {\r\n return envBaseUrls;\r\n }\r\n\r\n if (readEnv(\"NODE_ENV\") === \"development\") {\r\n return DEFAULT_LOCAL_BASE_URLS;\r\n }\r\n\r\n return DEFAULT_PRODUCTION_BASE_URLS;\r\n}\r\n\r\nexport function pickBaseUrl(baseUrls: ResolvedBaseUrl[]): string {\r\n return baseUrls[0]?.url ?? DEFAULT_PRODUCTION_BASE_URLS[0].url;\r\n}\r\n\r\nexport function getRoutingStats(): Record<string, never> {\r\n return {};\r\n}","import { ResiraApiError, ResiraNetworkError, ResiraRateLimitError } from \"./errors.js\";\r\nimport { pickBaseUrl, resolveBaseUrls } from \"./routing.js\";\r\nimport type {\r\n ApiErrorBody,\r\n Availability,\r\n AvailabilityParams,\r\n ConfirmPaymentRequest,\r\n ConfirmPaymentResponse,\r\n CreatePaymentIntentRequest,\r\n CreateReservationRequest,\r\n Dish,\r\n DishListResponse,\r\n DishResponse,\r\n ListReservationsParams,\r\n PaginatedReservations,\r\n PaymentIntentResponse,\r\n ProductListResponse,\r\n PropertyConfig,\r\n Reservation,\r\n ReservationResponse,\r\n ResiraConfig,\r\n ResourceListResponse,\r\n ValidatePromoCodeResponse,\r\n} from \"./types.js\";\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Constants\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\nconst DEFAULT_MAX_RETRIES = 3;\r\nconst DEFAULT_RETRY_BASE_DELAY = 500; // ms\r\nconst API_PREFIX = \"/v1/public\";\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Helpers\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/** Generate a random UUID v4 for idempotency keys. */\r\nfunction uuid(): string {\r\n // Use crypto.randomUUID when available (Node 19+, all modern browsers)\r\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\r\n return globalThis.crypto.randomUUID();\r\n }\r\n // Fallback: manual v4 UUID\r\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\r\n });\r\n}\r\n\r\n/** Build a query string from a flat params object. Skips `undefined` values. */\r\nfunction toQueryString(params: Record<string, string | number | boolean | undefined>): string {\r\n const parts: string[] = [];\r\n for (const [key, value] of Object.entries(params)) {\r\n if (value !== undefined && value !== null) {\r\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);\r\n }\r\n }\r\n return parts.length > 0 ? `?${parts.join(\"&\")}` : \"\";\r\n}\r\n\r\n/** Sleep for `ms` milliseconds. */\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\n/**\r\n * Convert camelCase keys to snake_case for query parameters.\r\n * The API expects snake_case query params (`start_date`, `party_size`).\r\n */\r\nfunction camelToSnake(str: string): string {\r\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\r\n}\r\n\r\n/** Convert an object's keys from camelCase to snake_case. */\r\nfunction keysToSnake(\r\n obj: Record<string, string | number | boolean | undefined>,\r\n): Record<string, string | number | boolean | undefined> {\r\n const result: Record<string, string | number | boolean | undefined> = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n result[camelToSnake(key)] = value;\r\n }\r\n return result;\r\n}\r\n\r\n/** Convert snake_case string to camelCase. */\r\nfunction snakeToCamel(str: string): string {\r\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\r\n}\r\n\r\n/** Deep-convert all keys in an object/array from camelCase to snake_case. */\r\nfunction deepKeysToSnake(obj: unknown): unknown {\r\n if (Array.isArray(obj)) return obj.map(deepKeysToSnake);\r\n if (obj !== null && typeof obj === \"object\") {\r\n const result: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\r\n result[camelToSnake(key)] = deepKeysToSnake(value);\r\n }\r\n return result;\r\n }\r\n return obj;\r\n}\r\n\r\n/** Deep-convert all keys in an object/array from snake_case to camelCase. */\r\nfunction deepKeysToCamel(obj: unknown): unknown {\r\n if (Array.isArray(obj)) return obj.map(deepKeysToCamel);\r\n if (obj !== null && typeof obj === \"object\") {\r\n const result: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\r\n result[snakeToCamel(key)] = deepKeysToCamel(value);\r\n }\r\n return result;\r\n }\r\n return obj;\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Client\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Resira SDK client.\r\n *\r\n * ```ts\r\n * const resira = new Resira({\r\n * apiKey: \"resira_live_abc123…\",\r\n * baseUrl: \"https://api.example.com\", // optional\r\n * });\r\n *\r\n * const availability = await resira.getAvailability(\"prop-1\", {\r\n * startDate: \"2026-07-01\",\r\n * endDate: \"2026-07-07\",\r\n * });\r\n * ```\r\n */\r\nexport class Resira {\r\n private readonly apiKey: string;\r\n private readonly baseUrls: ReturnType<typeof resolveBaseUrls>;\r\n private readonly maxRetries: number;\r\n private readonly retryBaseDelay: number;\r\n private readonly _fetch: typeof globalThis.fetch;\r\n\r\n constructor(config: ResiraConfig) {\r\n if (!config.apiKey) {\r\n throw new Error(\"Resira: apiKey is required\");\r\n }\r\n this.apiKey = config.apiKey;\r\n this.baseUrls = resolveBaseUrls(config);\r\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\r\n this.retryBaseDelay = config.retryBaseDelay ?? DEFAULT_RETRY_BASE_DELAY;\r\n this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);\r\n }\r\n\r\n /** The resolved API origin used by this client instance. */\r\n getBaseUrl(): string {\r\n return pickBaseUrl(this.baseUrls);\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────────\r\n // Public API methods\r\n // ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Fetch the public property configuration.\r\n *\r\n * Returns non-sensitive settings such as the Stripe publishable\r\n * key, deposit percentage, currency, and branding info.\r\n *\r\n * ```ts\r\n * const config = await resira.getConfig();\r\n * console.log(config.stripePublishableKey); // \"pk_test_…\"\r\n * ```\r\n */\r\n async getConfig(): Promise<PropertyConfig> {\r\n return this.request<PropertyConfig>(\"GET\", \"/config\");\r\n }\r\n\r\n /**\r\n * Validate a promo/discount code.\r\n *\r\n * Returns discount details if valid, or an error message if not.\r\n *\r\n * ```ts\r\n * const result = await resira.validatePromoCode(\"SUMMER20\");\r\n * if (result.valid) {\r\n * console.log(result.discountType, result.discountValue);\r\n * }\r\n * ```\r\n */\r\n async validatePromoCode(code: string): Promise<ValidatePromoCodeResponse> {\r\n try {\r\n const raw = await this.request<Record<string, unknown>>(\r\n \"POST\",\r\n \"/promo-codes/validate\",\r\n { code },\r\n );\r\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\r\n return {\r\n valid: true,\r\n discountType: data.discountType as \"percent\" | \"fixed\" | undefined,\r\n discountValue: data.discountValue as number | undefined,\r\n currency: data.currency as string | undefined,\r\n minOrderCents: data.minOrderCents as number | undefined,\r\n };\r\n } catch (err: unknown) {\r\n // 4xx errors mean invalid code\r\n if (\r\n err &&\r\n typeof err === \"object\" &&\r\n \"status\" in err &&\r\n (err as { status: number }).status < 500\r\n ) {\r\n const apiErr = err as { body?: { error?: string } };\r\n return {\r\n valid: false,\r\n error: apiErr.body?.error ?? \"Invalid promo code\",\r\n };\r\n }\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * List all active resources for the organization.\r\n *\r\n * Returns resource cards with name, description, price, duration,\r\n * image, and type — suitable for building a resource picker.\r\n *\r\n * ```ts\r\n * const { resources } = await resira.listResources();\r\n * ```\r\n */\r\n async listResources(): Promise<ResourceListResponse> {\r\n return this.request<ResourceListResponse>(\"GET\", \"/resources\");\r\n }\r\n\r\n /**\r\n * Query availability for a resource.\r\n *\r\n * **Properties** (date-based):\r\n * ```ts\r\n * await resira.getAvailability(\"prop-1\", {\r\n * startDate: \"2026-07-01\",\r\n * endDate: \"2026-07-07\",\r\n * });\r\n * ```\r\n *\r\n * **Restaurants** (time-slot):\r\n * ```ts\r\n * await resira.getAvailability(\"table-5\", {\r\n * date: \"2026-07-01\",\r\n * partySize: 4,\r\n * });\r\n * ```\r\n *\r\n * Omit params to get all blocked dates (for calendar rendering).\r\n */\r\n async getAvailability(\r\n resourceId: string,\r\n params: AvailabilityParams = {},\r\n ): Promise<Availability> {\r\n if (!resourceId) {\r\n throw new Error(\"resourceId is required for getAvailability\");\r\n }\r\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\r\n return this.request<Availability>(\r\n \"GET\",\r\n `/resources/${encodeURIComponent(resourceId)}/availability${qs}`,\r\n );\r\n }\r\n\r\n /**\r\n * Query availability for a product/service.\r\n *\r\n * Aggregates capacity across all linked equipment. If a product\r\n * has 2 jet skis (each capacity=1), slots show capacity: 2.\r\n * Both pending and confirmed reservations count as occupied.\r\n *\r\n * ```ts\r\n * await resira.getProductAvailability(\"prod-123\", {\r\n * date: \"2026-07-01\",\r\n * durationMinutes: 30,\r\n * });\r\n * ```\r\n */\r\n async getProductAvailability(\r\n productId: string,\r\n params: AvailabilityParams = {},\r\n ): Promise<Availability> {\r\n if (!productId) {\r\n throw new Error(\"productId is required for getProductAvailability\");\r\n }\r\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\r\n return this.request<Availability>(\r\n \"GET\",\r\n `/products/${encodeURIComponent(productId)}/availability${qs}`,\r\n );\r\n }\r\n\r\n /**\r\n * Create a reservation.\r\n *\r\n * Uses `POST /v2/api/reservations` — the `resourceId` is sent in\r\n * the request body, **not** in the URL path.\r\n *\r\n * An `Idempotency-Key` header is automatically attached so that\r\n * retries (including those from exponential backoff) are safe.\r\n *\r\n * ```ts\r\n * const { reservation } = await resira.createReservation({\r\n * resourceId: \"prop-1\",\r\n * guestName: \"Jane Doe\",\r\n * guestEmail: \"jane@example.com\",\r\n * startDate: \"2026-07-01\",\r\n * endDate: \"2026-07-07\",\r\n * partySize: 3,\r\n * });\r\n * ```\r\n */\r\n async createReservation(\r\n payload: CreateReservationRequest,\r\n options?: { idempotencyKey?: string },\r\n ): Promise<ReservationResponse> {\r\n if (!payload.resourceId) {\r\n throw new Error(\"resourceId is required for createReservation\");\r\n }\r\n const idempotencyKey = options?.idempotencyKey ?? uuid();\r\n\r\n // Note: uses /v2/api prefix, not /v1/public\r\n const url = `${this.getBaseUrl()}/v2/api/reservations`;\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n Accept: \"application/json\",\r\n \"Content-Type\": \"application/json\",\r\n \"Idempotency-Key\": idempotencyKey,\r\n };\r\n\r\n const init: RequestInit = {\r\n method: \"POST\",\r\n headers,\r\n body: JSON.stringify(payload),\r\n };\r\n\r\n let lastError: ResiraApiError | ResiraNetworkError | ResiraRateLimitError | undefined;\r\n\r\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\r\n if (attempt > 0) {\r\n const backoff = this.calculateBackoff(attempt, lastError);\r\n await sleep(backoff);\r\n }\r\n\r\n let response: Response;\r\n try {\r\n response = await this._fetch(url, init);\r\n } catch (err) {\r\n lastError = new ResiraNetworkError(\r\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\r\n err,\r\n );\r\n continue;\r\n }\r\n\r\n if (response.ok) {\r\n const raw = (await response.json()) as Record<string, unknown>;\r\n // The API may nest under \"reservation\" or \"booking\" depending on endpoint\r\n const resData = (raw.reservation ?? raw.booking ?? {}) as Record<string, unknown>;\r\n\r\n // Augment with request data: API response doesn't include\r\n // startTime/endTime/startDate/endDate/resourceId for v2 reservations\r\n const reservation: Reservation = {\r\n id: (resData.id as string) ?? \"\",\r\n resourceId: (resData.resourceId as string) ?? payload.resourceId,\r\n status: (resData.status as string) ?? \"pending\",\r\n startDate: (resData.startDate as string) ?? payload.startDate,\r\n endDate: (resData.endDate as string) ?? payload.endDate,\r\n startTime: (resData.startTime as string) ?? payload.startTime,\r\n endTime: (resData.endTime as string) ?? payload.endTime,\r\n startDatetime: (resData.startDatetime as string) ?? payload.startTime,\r\n endDatetime: (resData.endDatetime as string) ?? payload.endTime,\r\n guestName: (resData.guestName as string) ?? payload.guestName,\r\n partySize: (resData.partySize as number) ?? (resData.guests as number) ?? payload.partySize ?? 2,\r\n totalPrice: (resData.totalPrice as number) ?? 0,\r\n currency: (resData.currency as string) ?? \"EUR\",\r\n createdAt: (resData.createdAt as string) ?? new Date().toISOString(),\r\n };\r\n\r\n return { reservation };\r\n }\r\n\r\n let errorBody: ApiErrorBody;\r\n try {\r\n errorBody = (await response.json()) as ApiErrorBody;\r\n } catch {\r\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\r\n }\r\n\r\n if (response.status === 429) {\r\n lastError = new ResiraRateLimitError(errorBody, response);\r\n continue;\r\n }\r\n\r\n if (response.status >= 500) {\r\n lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n continue;\r\n }\r\n\r\n throw new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n }\r\n\r\n throw lastError!;\r\n }\r\n\r\n /**\r\n * List reservations for a resource (paginated).\r\n *\r\n * ```ts\r\n * const page = await resira.listReservations(\"prop-1\", {\r\n * status: \"confirmed\",\r\n * page: 1,\r\n * limit: 50,\r\n * });\r\n * console.log(page.data); // Reservation[]\r\n * console.log(page.totalPages); // number\r\n * ```\r\n */\r\n async listReservations(\r\n resourceId: string,\r\n params: ListReservationsParams = {},\r\n ): Promise<PaginatedReservations> {\r\n const qs = toQueryString(params as Record<string, string | number | undefined>);\r\n return this.request<PaginatedReservations>(\r\n \"GET\",\r\n `/resources/${encodeURIComponent(resourceId)}/reservations${qs}`,\r\n );\r\n }\r\n\r\n /**\r\n * Get a single reservation by ID.\r\n *\r\n * ```ts\r\n * const { reservation } = await resira.getReservation(\"prop-1\", \"res-uuid\");\r\n * ```\r\n */\r\n async getReservation(\r\n resourceId: string,\r\n reservationId: string,\r\n ): Promise<ReservationResponse> {\r\n return this.request<ReservationResponse>(\r\n \"GET\",\r\n `/resources/${encodeURIComponent(resourceId)}/reservations/${encodeURIComponent(reservationId)}`,\r\n );\r\n }\r\n\r\n /**\r\n * List all active products/services for the organisation.\r\n *\r\n * Calls `GET /v1/public/products` which returns products with\r\n * pricing, duration, and linked equipment.\r\n *\r\n * Falls back to mapping resources → products if the endpoint\r\n * returns 404 (backend hasn't been updated yet).\r\n *\r\n * ```ts\r\n * const { products } = await resira.listProducts();\r\n * ```\r\n */\r\n async listProducts(): Promise<ProductListResponse> {\r\n try {\r\n return await this.request<ProductListResponse>(\"GET\", \"/products\");\r\n } catch (err: unknown) {\r\n // Fallback: if /products endpoint doesn't exist yet (404),\r\n // map resources → products shape\r\n if (\r\n err &&\r\n typeof err === \"object\" &&\r\n \"status\" in err &&\r\n (err as { status: number }).status === 404\r\n ) {\r\n const resourceResponse = await this.listResources();\r\n const products = resourceResponse.resources.map((r) => ({\r\n id: r.id,\r\n name: r.name,\r\n description: r.description,\r\n durationMinutes: r.durationMinutes ?? 60,\r\n priceCents: r.pricePerSession ?? 0,\r\n currency: r.currency ?? \"EUR\",\r\n imageUrl: r.imageUrl,\r\n active: r.active,\r\n sortOrder: 0,\r\n pricingModel: \"per_session\" as const,\r\n equipmentIds: [r.id],\r\n equipmentNames: [r.name],\r\n }));\r\n return { products, count: products.length };\r\n }\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * List all dishes with 3D model data for the organisation.\r\n *\r\n * Returns dishes with name, description, price, image, and\r\n * 3D model URLs for AR/preview rendering.\r\n *\r\n * ```ts\r\n * const { dishes } = await resira.listDishes();\r\n * ```\r\n */\r\n async listDishes(): Promise<DishListResponse> {\r\n return this.request<DishListResponse>(\"GET\", \"/dishes\");\r\n }\r\n\r\n /**\r\n * Get a single dish by ID.\r\n *\r\n * ```ts\r\n * const { dish } = await resira.getDish(\"dish-uuid\");\r\n * console.log(dish.name, dish.model?.glbUrl);\r\n * ```\r\n */\r\n async getDish(dishId: string): Promise<DishResponse> {\r\n if (!dishId) {\r\n throw new Error(\"dishId is required for getDish\");\r\n }\r\n return this.request<DishResponse>(\r\n \"GET\",\r\n `/dishes/${encodeURIComponent(dishId)}`,\r\n );\r\n }\r\n\r\n /**\r\n * Create a Stripe payment intent for a booking.\r\n *\r\n * The server creates the payment intent, calculates the amount,\r\n * and returns a `clientSecret` to confirm payment on the frontend\r\n * using Stripe Elements.\r\n *\r\n * ```ts\r\n * const { clientSecret, amountNow, amountAtVenue } =\r\n * await resira.createPaymentIntent({\r\n * productId: \"prod-123\",\r\n * resourceId: \"res-456\",\r\n * partySize: 2,\r\n * startDate: \"2026-07-01\",\r\n * startTime: \"2026-07-01T10:00:00Z\",\r\n * endTime: \"2026-07-01T11:00:00Z\",\r\n * guestName: \"Jane Doe\",\r\n * guestEmail: \"jane@example.com\",\r\n * });\r\n * ```\r\n */\r\n async createPaymentIntent(\r\n payload: CreatePaymentIntentRequest,\r\n options?: { idempotencyKey?: string },\r\n ): Promise<PaymentIntentResponse> {\r\n const idempotencyKey = options?.idempotencyKey ?? uuid();\r\n const raw = await this.request<Record<string, unknown>>(\r\n \"POST\",\r\n \"/payments/create-intent\",\r\n payload,\r\n { \"Idempotency-Key\": idempotencyKey },\r\n );\r\n // Normalize response keys to camelCase\r\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\r\n // Map backend field names to SDK field names:\r\n // backend: amountDue, totalPrice\r\n // SDK: amountNow, totalAmount, amountAtVenue\r\n const totalAmount = (data.totalAmount as number) ?? (data.totalPrice as number) ?? 0;\r\n const amountNow = (data.amountNow as number) ?? (data.amountDue as number) ?? totalAmount;\r\n const amountAtVenue = (data.amountAtVenue as number) ?? (totalAmount - amountNow);\r\n return {\r\n clientSecret: data.clientSecret as string,\r\n paymentIntentId: data.paymentIntentId as string,\r\n amountNow,\r\n amountAtVenue,\r\n totalAmount,\r\n currency: (data.currency as string) ?? \"EUR\",\r\n paymentOption: data.paymentOption as string | undefined,\r\n reservationId: data.reservationId as string | undefined,\r\n discountAmount: (data.discountAmount as number) ?? undefined,\r\n originalPrice: (data.originalPrice as number) ?? undefined,\r\n promoCodeApplied: (data.promoCodeApplied as string) ?? undefined,\r\n };\r\n }\r\n\r\n /**\r\n * Confirm that a payment was successful after the guest\r\n * completes the Stripe payment flow.\r\n *\r\n * This transitions the reservation from `pending` to `confirmed`.\r\n *\r\n * ```ts\r\n * const { reservation, paymentStatus } =\r\n * await resira.confirmPayment({\r\n * paymentIntentId: \"pi_xxx\",\r\n * reservationId: \"res-uuid\",\r\n * });\r\n * ```\r\n */\r\n async confirmPayment(\r\n payload: ConfirmPaymentRequest,\r\n ): Promise<ConfirmPaymentResponse> {\r\n const raw = await this.request<Record<string, unknown>>(\r\n \"POST\",\r\n \"/payments/confirm\",\r\n payload,\r\n );\r\n // Normalize response keys to camelCase in case backend returns snake_case\r\n return deepKeysToCamel(raw) as ConfirmPaymentResponse;\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────────\r\n // Internal HTTP layer\r\n // ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Execute an HTTP request with retry logic and error normalization.\r\n *\r\n * - Attaches `Authorization: Bearer <apiKey>` on every request.\r\n * - Retries on 429 and 5xx with exponential backoff + jitter.\r\n * - Parses error bodies and throws typed error classes.\r\n */\r\n private async request<T>(\r\n method: string,\r\n path: string,\r\n body?: unknown,\r\n extraHeaders?: Record<string, string>,\r\n ): Promise<T> {\r\n const url = `${this.getBaseUrl()}${API_PREFIX}${path}`;\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n Accept: \"application/json\",\r\n ...extraHeaders,\r\n };\r\n\r\n if (body !== undefined) {\r\n headers[\"Content-Type\"] = \"application/json\";\r\n }\r\n\r\n const init: RequestInit = {\r\n method,\r\n headers,\r\n body: body !== undefined ? JSON.stringify(body) : undefined,\r\n };\r\n\r\n let lastError: ResiraApiError | ResiraNetworkError | undefined;\r\n\r\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\r\n // ── Wait before retry ────────────────────────────────\r\n if (attempt > 0) {\r\n const backoff = this.calculateBackoff(attempt, lastError);\r\n await sleep(backoff);\r\n }\r\n\r\n // ── Execute the request ──────────────────────────────\r\n let response: Response;\r\n try {\r\n response = await this._fetch(url, init);\r\n } catch (err) {\r\n lastError = new ResiraNetworkError(\r\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\r\n err,\r\n );\r\n // Network errors are always retryable\r\n continue;\r\n }\r\n\r\n // ── Success ──────────────────────────────────────────\r\n if (response.ok) {\r\n return (await response.json()) as T;\r\n }\r\n\r\n // ── Parse error body ─────────────────────────────────\r\n let errorBody: ApiErrorBody;\r\n try {\r\n errorBody = (await response.json()) as ApiErrorBody;\r\n } catch {\r\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\r\n }\r\n\r\n // ── Rate limit ───────────────────────────────────────\r\n if (response.status === 429) {\r\n lastError = new ResiraRateLimitError(errorBody, response);\r\n // Always retry 429s (up to maxRetries)\r\n continue;\r\n }\r\n\r\n // ── Server error (5xx) — retryable ───────────────────\r\n if (response.status >= 500) {\r\n lastError = new ResiraApiError(\r\n response.status,\r\n response.statusText,\r\n errorBody,\r\n response,\r\n );\r\n continue;\r\n }\r\n\r\n // ── Client error (4xx) — NOT retryable ───────────────\r\n throw new ResiraApiError(\r\n response.status,\r\n response.statusText,\r\n errorBody,\r\n response,\r\n );\r\n }\r\n\r\n // All retries exhausted — throw the last error\r\n throw lastError!;\r\n }\r\n\r\n /**\r\n * Calculate backoff delay in milliseconds for a given retry attempt.\r\n *\r\n * Uses exponential backoff with full jitter:\r\n * delay = random(0, base * 2^attempt)\r\n *\r\n * For 429 responses, respects the `Retry-After` header as a minimum.\r\n */\r\n private calculateBackoff(\r\n attempt: number,\r\n lastError?: ResiraApiError | ResiraNetworkError,\r\n ): number {\r\n const exponential = this.retryBaseDelay * 2 ** (attempt - 1);\r\n const jittered = Math.random() * exponential;\r\n\r\n // If the server told us to wait, use that as the floor\r\n if (lastError instanceof ResiraRateLimitError) {\r\n const serverDelay = lastError.retryAfter * 1000;\r\n return Math.max(serverDelay, jittered);\r\n }\r\n\r\n return jittered;\r\n }\r\n\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/routing.ts","../src/client.ts"],"names":["url"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ApiErrorBody } from \"./types.js\";\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Base error\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Base error class for all SDK errors.\r\n *\r\n * Use `instanceof` checks to distinguish error types:\r\n *\r\n * ```ts\r\n * try { … } catch (e) {\r\n * if (e instanceof ResiraRateLimitError) { … }\r\n * if (e instanceof ResiraApiError) { … }\r\n * if (e instanceof ResiraNetworkError) { … }\r\n * }\r\n * ```\r\n */\r\nexport class ResiraError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = \"ResiraError\";\r\n // Fix prototype chain for instanceof to work after transpilation\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// API error (4xx / 5xx with a parsed body)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Thrown when the API returns a non-2xx response with a JSON body.\r\n *\r\n * ```ts\r\n * err.status // 400, 404, 409, 422, 500, …\r\n * err.code // HTTP status text (e.g. \"Not Found\")\r\n * err.body // Parsed `{ error: \"…\" }` from the server\r\n * ```\r\n */\r\nexport class ResiraApiError extends ResiraError {\r\n /** HTTP status code. */\r\n readonly status: number;\r\n /** HTTP status text (e.g. `\"Bad Request\"`). */\r\n readonly code: string;\r\n /** Parsed error body from the API. */\r\n readonly body: ApiErrorBody;\r\n /** The full `Response` object (headers are still accessible). */\r\n readonly response: Response;\r\n\r\n constructor(status: number, code: string, body: ApiErrorBody, response: Response) {\r\n super(body.error || `API error ${status}`);\r\n this.name = \"ResiraApiError\";\r\n this.status = status;\r\n this.code = code;\r\n this.body = body;\r\n this.response = response;\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n\r\n /** Whether this error is retryable (5xx or 429). */\r\n get retryable(): boolean {\r\n return this.status === 429 || this.status >= 500;\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Rate limit error (429)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Thrown when the API returns 429 Too Many Requests.\r\n *\r\n * ```ts\r\n * err.retryAfter // Seconds until the client should retry\r\n * ```\r\n */\r\nexport class ResiraRateLimitError extends ResiraApiError {\r\n /** Seconds to wait before retrying (from `Retry-After` header). */\r\n readonly retryAfter: number;\r\n\r\n constructor(body: ApiErrorBody, response: Response) {\r\n super(429, \"Too Many Requests\", body, response);\r\n this.name = \"ResiraRateLimitError\";\r\n this.retryAfter =\r\n body.retryAfter ??\r\n (Number.parseInt(response.headers.get(\"retry-after\") ?? \"60\", 10) ||\r\n 60);\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Network error (fetch failed, DNS, timeout, etc.)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Thrown when the request never reached the server.\r\n *\r\n * This wraps the underlying `TypeError` or `DOMException` from\r\n * `fetch()` failures (DNS resolution, TLS, network offline, etc.).\r\n */\r\nexport class ResiraNetworkError extends ResiraError {\r\n /** The original error thrown by `fetch`. */\r\n readonly cause: unknown;\r\n\r\n constructor(message: string, cause: unknown) {\r\n super(message);\r\n this.name = \"ResiraNetworkError\";\r\n this.cause = cause;\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n}\r\n","import type { ResiraConfig, WeightedBaseUrl } from \"./types.js\";\r\n\r\ninterface ResolvedBaseUrl {\r\n url: string;\r\n weight: number;\r\n}\r\n\r\nconst DEFAULT_LOCAL_BASE_URLS: ResolvedBaseUrl[] = [\r\n { url: \"http://localhost:3001\", weight: 100 },\r\n];\r\n\r\nconst DEFAULT_PRODUCTION_BASE_URLS: ResolvedBaseUrl[] = [\r\n { url: \"https://api.resira.app\", weight: 100 },\r\n];\r\n\r\nfunction readEnv(name: string): string | undefined {\r\n if (typeof process === \"undefined\") return undefined;\r\n return process.env?.[name];\r\n}\r\n\r\n\r\nfunction normalizeUrl(url: string): string {\r\n return url.trim().replace(/\\/+$/, \"\");\r\n}\r\n\r\nfunction isPositiveNumber(value: unknown): value is number {\r\n return typeof value === \"number\" && Number.isFinite(value) && value > 0;\r\n}\r\n\r\nfunction toResolvedBaseUrl(value: string | WeightedBaseUrl): ResolvedBaseUrl | null {\r\n if (typeof value === \"string\") {\r\n const url = normalizeUrl(value);\r\n return url ? { url, weight: 1 } : null;\r\n }\r\n\r\n const url = normalizeUrl(value.url);\r\n if (!url) return null;\r\n\r\n return {\r\n url,\r\n weight: isPositiveNumber(value.weight) ? value.weight : 1,\r\n };\r\n}\r\n\r\nfunction parseJsonBaseUrls(value: string): ResolvedBaseUrl[] {\r\n const parsed = JSON.parse(value) as unknown;\r\n if (!Array.isArray(parsed)) return [];\r\n\r\n return parsed.flatMap((entry) => {\r\n const resolved =\r\n typeof entry === \"string\"\r\n ? toResolvedBaseUrl(entry)\r\n : entry && typeof entry === \"object\"\r\n ? toResolvedBaseUrl(entry as WeightedBaseUrl)\r\n : null;\r\n\r\n return resolved ? [resolved] : [];\r\n });\r\n}\r\n\r\nfunction parseDelimitedBaseUrls(value: string): ResolvedBaseUrl[] {\r\n return value\r\n .split(\",\")\r\n .flatMap((entry) => {\r\n const [rawUrl, rawWeight] = entry.split(\"|\");\r\n const url = normalizeUrl(rawUrl ?? \"\");\r\n if (!url) return [];\r\n\r\n const parsedWeight = rawWeight ? Number(rawWeight.trim()) : 1;\r\n return [{\r\n url,\r\n weight: isPositiveNumber(parsedWeight) ? parsedWeight : 1,\r\n }];\r\n });\r\n}\r\n\r\nfunction parseEnvBaseUrls(value: string | undefined): ResolvedBaseUrl[] {\r\n if (!value) return [];\r\n\r\n try {\r\n const parsed = parseJsonBaseUrls(value);\r\n if (parsed.length > 0) return parsed;\r\n } catch {\r\n // Fall back to delimiter parsing.\r\n }\r\n\r\n return parseDelimitedBaseUrls(value);\r\n}\r\n\r\nexport function resolveBaseUrls(config: ResiraConfig): ResolvedBaseUrl[] {\r\n if (config.baseUrl) {\r\n return [{ url: normalizeUrl(config.baseUrl), weight: 100 }];\r\n }\r\n\r\n if (config.baseUrls?.length) {\r\n const explicit = config.baseUrls.flatMap((entry) => {\r\n const resolved = toResolvedBaseUrl(entry);\r\n return resolved ? [resolved] : [];\r\n });\r\n\r\n if (explicit.length > 0) return explicit;\r\n }\r\n\r\n const envBaseUrls = parseEnvBaseUrls(readEnv(\"RESIRA_API_BASE_URLS\"));\r\n if (envBaseUrls.length > 0) {\r\n return envBaseUrls;\r\n }\r\n\r\n if (readEnv(\"NODE_ENV\") === \"development\") {\r\n return DEFAULT_LOCAL_BASE_URLS;\r\n }\r\n\r\n return DEFAULT_PRODUCTION_BASE_URLS;\r\n}\r\n\r\nexport function pickBaseUrl(baseUrls: ResolvedBaseUrl[]): string {\r\n return baseUrls[0]?.url ?? DEFAULT_PRODUCTION_BASE_URLS[0].url;\r\n}\r\n\r\nexport function getRoutingStats(): Record<string, never> {\r\n return {};\r\n}","import { ResiraApiError, ResiraNetworkError, ResiraRateLimitError } from \"./errors.js\";\r\nimport { pickBaseUrl, resolveBaseUrls } from \"./routing.js\";\r\nimport type {\r\n ApiErrorBody,\r\n Availability,\r\n AvailabilityParams,\r\n CheckoutSessionResponse,\r\n ConfirmPaymentRequest,\r\n ConfirmPaymentResponse,\r\n CreateCheckoutSessionRequest,\r\n CreateCheckoutSessionResponse,\r\n CreatePaymentIntentRequest,\r\n CreateReservationRequest,\r\n Dish,\r\n DishListResponse,\r\n DishResponse,\r\n ListReservationsParams,\r\n PaginatedReservations,\r\n PaymentIntentResponse,\r\n ProductListResponse,\r\n PropertyConfig,\r\n Reservation,\r\n ReservationResponse,\r\n ResiraConfig,\r\n ResourceListResponse,\r\n ValidatePromoCodeResponse,\r\n} from \"./types.js\";\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Constants\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\nconst DEFAULT_MAX_RETRIES = 3;\r\nconst DEFAULT_RETRY_BASE_DELAY = 500; // ms\r\nconst API_PREFIX = \"/v1/public\";\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Helpers\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/** Generate a random UUID v4 for idempotency keys. */\r\nfunction uuid(): string {\r\n // Use crypto.randomUUID when available (Node 19+, all modern browsers)\r\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\r\n return globalThis.crypto.randomUUID();\r\n }\r\n // Fallback: manual v4 UUID\r\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\r\n });\r\n}\r\n\r\n/** Build a query string from a flat params object. Skips `undefined` values. */\r\nfunction toQueryString(params: Record<string, string | number | boolean | undefined>): string {\r\n const parts: string[] = [];\r\n for (const [key, value] of Object.entries(params)) {\r\n if (value !== undefined && value !== null) {\r\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);\r\n }\r\n }\r\n return parts.length > 0 ? `?${parts.join(\"&\")}` : \"\";\r\n}\r\n\r\n/** Sleep for `ms` milliseconds. */\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\n/**\r\n * Convert camelCase keys to snake_case for query parameters.\r\n * The API expects snake_case query params (`start_date`, `party_size`).\r\n */\r\nfunction camelToSnake(str: string): string {\r\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\r\n}\r\n\r\n/** Convert an object's keys from camelCase to snake_case. */\r\nfunction keysToSnake(\r\n obj: Record<string, string | number | boolean | undefined>,\r\n): Record<string, string | number | boolean | undefined> {\r\n const result: Record<string, string | number | boolean | undefined> = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n result[camelToSnake(key)] = value;\r\n }\r\n return result;\r\n}\r\n\r\n/** Convert snake_case string to camelCase. */\r\nfunction snakeToCamel(str: string): string {\r\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\r\n}\r\n\r\n/** Deep-convert all keys in an object/array from camelCase to snake_case. */\r\nfunction deepKeysToSnake(obj: unknown): unknown {\r\n if (Array.isArray(obj)) return obj.map(deepKeysToSnake);\r\n if (obj !== null && typeof obj === \"object\") {\r\n const result: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\r\n result[camelToSnake(key)] = deepKeysToSnake(value);\r\n }\r\n return result;\r\n }\r\n return obj;\r\n}\r\n\r\n/** Deep-convert all keys in an object/array from snake_case to camelCase. */\r\nfunction deepKeysToCamel(obj: unknown): unknown {\r\n if (Array.isArray(obj)) return obj.map(deepKeysToCamel);\r\n if (obj !== null && typeof obj === \"object\") {\r\n const result: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\r\n result[snakeToCamel(key)] = deepKeysToCamel(value);\r\n }\r\n return result;\r\n }\r\n return obj;\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Client\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Resira SDK client.\r\n *\r\n * ```ts\r\n * const resira = new Resira({\r\n * apiKey: \"resira_live_abc123…\",\r\n * baseUrl: \"https://api.example.com\", // optional\r\n * });\r\n *\r\n * const availability = await resira.getAvailability(\"prop-1\", {\r\n * startDate: \"2026-07-01\",\r\n * endDate: \"2026-07-07\",\r\n * });\r\n * ```\r\n */\r\nexport class Resira {\r\n private readonly apiKey: string;\r\n private readonly baseUrls: ReturnType<typeof resolveBaseUrls>;\r\n private readonly maxRetries: number;\r\n private readonly retryBaseDelay: number;\r\n private readonly _fetch: typeof globalThis.fetch;\r\n\r\n constructor(config: ResiraConfig) {\r\n if (!config.apiKey) {\r\n throw new Error(\"Resira: apiKey is required\");\r\n }\r\n this.apiKey = config.apiKey;\r\n this.baseUrls = resolveBaseUrls(config);\r\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\r\n this.retryBaseDelay = config.retryBaseDelay ?? DEFAULT_RETRY_BASE_DELAY;\r\n this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);\r\n }\r\n\r\n /** The resolved API origin used by this client instance. */\r\n getBaseUrl(): string {\r\n return pickBaseUrl(this.baseUrls);\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────────\r\n // Public API methods\r\n // ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Fetch the public property configuration.\r\n *\r\n * Returns non-sensitive settings such as the Stripe publishable\r\n * key, deposit percentage, currency, and branding info.\r\n *\r\n * ```ts\r\n * const config = await resira.getConfig();\r\n * console.log(config.stripePublishableKey); // \"pk_test_…\"\r\n * ```\r\n */\r\n async getConfig(): Promise<PropertyConfig> {\r\n return this.request<PropertyConfig>(\"GET\", \"/config\");\r\n }\r\n\r\n /**\r\n * Validate a promo/discount code.\r\n *\r\n * Returns discount details if valid, or an error message if not.\r\n *\r\n * ```ts\r\n * const result = await resira.validatePromoCode(\"SUMMER20\");\r\n * if (result.valid) {\r\n * console.log(result.discountType, result.discountValue);\r\n * }\r\n * ```\r\n */\r\n async validatePromoCode(code: string): Promise<ValidatePromoCodeResponse> {\r\n try {\r\n const raw = await this.request<Record<string, unknown>>(\r\n \"POST\",\r\n \"/promo-codes/validate\",\r\n { code },\r\n );\r\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\r\n return {\r\n valid: true,\r\n discountType: data.discountType as \"percent\" | \"fixed\" | undefined,\r\n discountValue: data.discountValue as number | undefined,\r\n currency: data.currency as string | undefined,\r\n minOrderCents: data.minOrderCents as number | undefined,\r\n };\r\n } catch (err: unknown) {\r\n // 4xx errors mean invalid code\r\n if (\r\n err &&\r\n typeof err === \"object\" &&\r\n \"status\" in err &&\r\n (err as { status: number }).status < 500\r\n ) {\r\n const apiErr = err as { body?: { error?: string } };\r\n return {\r\n valid: false,\r\n error: apiErr.body?.error ?? \"Invalid promo code\",\r\n };\r\n }\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * List all active resources for the organization.\r\n *\r\n * Returns resource cards with name, description, price, duration,\r\n * image, and type — suitable for building a resource picker.\r\n *\r\n * ```ts\r\n * const { resources } = await resira.listResources();\r\n * ```\r\n */\r\n async listResources(): Promise<ResourceListResponse> {\r\n return this.request<ResourceListResponse>(\"GET\", \"/resources\");\r\n }\r\n\r\n /**\r\n * Query availability for a resource.\r\n *\r\n * **Properties** (date-based):\r\n * ```ts\r\n * await resira.getAvailability(\"prop-1\", {\r\n * startDate: \"2026-07-01\",\r\n * endDate: \"2026-07-07\",\r\n * });\r\n * ```\r\n *\r\n * **Restaurants** (time-slot):\r\n * ```ts\r\n * await resira.getAvailability(\"table-5\", {\r\n * date: \"2026-07-01\",\r\n * partySize: 4,\r\n * });\r\n * ```\r\n *\r\n * Omit params to get all blocked dates (for calendar rendering).\r\n */\r\n async getAvailability(\r\n resourceId: string,\r\n params: AvailabilityParams = {},\r\n ): Promise<Availability> {\r\n if (!resourceId) {\r\n throw new Error(\"resourceId is required for getAvailability\");\r\n }\r\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\r\n return this.request<Availability>(\r\n \"GET\",\r\n `/resources/${encodeURIComponent(resourceId)}/availability${qs}`,\r\n );\r\n }\r\n\r\n /**\r\n * Query availability for a product/service.\r\n *\r\n * Aggregates capacity across all linked equipment. If a product\r\n * has 2 jet skis (each capacity=1), slots show capacity: 2.\r\n * Both pending and confirmed reservations count as occupied.\r\n *\r\n * ```ts\r\n * await resira.getProductAvailability(\"prod-123\", {\r\n * date: \"2026-07-01\",\r\n * durationMinutes: 30,\r\n * });\r\n * ```\r\n */\r\n async getProductAvailability(\r\n productId: string,\r\n params: AvailabilityParams = {},\r\n ): Promise<Availability> {\r\n if (!productId) {\r\n throw new Error(\"productId is required for getProductAvailability\");\r\n }\r\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\r\n return this.request<Availability>(\r\n \"GET\",\r\n `/products/${encodeURIComponent(productId)}/availability${qs}`,\r\n );\r\n }\r\n\r\n /**\r\n * Create a reservation.\r\n *\r\n * Uses `POST /v2/api/reservations` — the `resourceId` is sent in\r\n * the request body, **not** in the URL path.\r\n *\r\n * An `Idempotency-Key` header is automatically attached so that\r\n * retries (including those from exponential backoff) are safe.\r\n *\r\n * ```ts\r\n * const { reservation } = await resira.createReservation({\r\n * resourceId: \"prop-1\",\r\n * guestName: \"Jane Doe\",\r\n * guestEmail: \"jane@example.com\",\r\n * startDate: \"2026-07-01\",\r\n * endDate: \"2026-07-07\",\r\n * partySize: 3,\r\n * });\r\n * ```\r\n */\r\n async createReservation(\r\n payload: CreateReservationRequest,\r\n options?: { idempotencyKey?: string },\r\n ): Promise<ReservationResponse> {\r\n if (!payload.resourceId) {\r\n throw new Error(\"resourceId is required for createReservation\");\r\n }\r\n const idempotencyKey = options?.idempotencyKey ?? uuid();\r\n\r\n // Note: uses /v2/api prefix, not /v1/public\r\n const url = `${this.getBaseUrl()}/v2/api/reservations`;\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n Accept: \"application/json\",\r\n \"Content-Type\": \"application/json\",\r\n \"Idempotency-Key\": idempotencyKey,\r\n };\r\n\r\n const init: RequestInit = {\r\n method: \"POST\",\r\n headers,\r\n body: JSON.stringify(payload),\r\n };\r\n\r\n let lastError: ResiraApiError | ResiraNetworkError | ResiraRateLimitError | undefined;\r\n\r\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\r\n if (attempt > 0) {\r\n const backoff = this.calculateBackoff(attempt, lastError);\r\n await sleep(backoff);\r\n }\r\n\r\n let response: Response;\r\n try {\r\n response = await this._fetch(url, init);\r\n } catch (err) {\r\n lastError = new ResiraNetworkError(\r\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\r\n err,\r\n );\r\n continue;\r\n }\r\n\r\n if (response.ok) {\r\n const raw = (await response.json()) as Record<string, unknown>;\r\n // The API may nest under \"reservation\" or \"booking\" depending on endpoint\r\n const resData = (raw.reservation ?? raw.booking ?? {}) as Record<string, unknown>;\r\n\r\n // Augment with request data: API response doesn't include\r\n // startTime/endTime/startDate/endDate/resourceId for v2 reservations\r\n const reservation: Reservation = {\r\n id: (resData.id as string) ?? \"\",\r\n resourceId: (resData.resourceId as string) ?? payload.resourceId,\r\n status: (resData.status as string) ?? \"pending\",\r\n startDate: (resData.startDate as string) ?? payload.startDate,\r\n endDate: (resData.endDate as string) ?? payload.endDate,\r\n startTime: (resData.startTime as string) ?? payload.startTime,\r\n endTime: (resData.endTime as string) ?? payload.endTime,\r\n startDatetime: (resData.startDatetime as string) ?? payload.startTime,\r\n endDatetime: (resData.endDatetime as string) ?? payload.endTime,\r\n guestName: (resData.guestName as string) ?? payload.guestName,\r\n partySize: (resData.partySize as number) ?? (resData.guests as number) ?? payload.partySize ?? 2,\r\n totalPrice: (resData.totalPrice as number) ?? 0,\r\n currency: (resData.currency as string) ?? \"EUR\",\r\n createdAt: (resData.createdAt as string) ?? new Date().toISOString(),\r\n };\r\n\r\n return { reservation };\r\n }\r\n\r\n let errorBody: ApiErrorBody;\r\n try {\r\n errorBody = (await response.json()) as ApiErrorBody;\r\n } catch {\r\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\r\n }\r\n\r\n if (response.status === 429) {\r\n lastError = new ResiraRateLimitError(errorBody, response);\r\n continue;\r\n }\r\n\r\n if (response.status >= 500) {\r\n lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n continue;\r\n }\r\n\r\n throw new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n }\r\n\r\n throw lastError!;\r\n }\r\n\r\n /**\r\n * List reservations for a resource (paginated).\r\n *\r\n * ```ts\r\n * const page = await resira.listReservations(\"prop-1\", {\r\n * status: \"confirmed\",\r\n * page: 1,\r\n * limit: 50,\r\n * });\r\n * console.log(page.data); // Reservation[]\r\n * console.log(page.totalPages); // number\r\n * ```\r\n */\r\n async listReservations(\r\n resourceId: string,\r\n params: ListReservationsParams = {},\r\n ): Promise<PaginatedReservations> {\r\n const qs = toQueryString(params as Record<string, string | number | undefined>);\r\n return this.request<PaginatedReservations>(\r\n \"GET\",\r\n `/resources/${encodeURIComponent(resourceId)}/reservations${qs}`,\r\n );\r\n }\r\n\r\n /**\r\n * Get a single reservation by ID.\r\n *\r\n * ```ts\r\n * const { reservation } = await resira.getReservation(\"prop-1\", \"res-uuid\");\r\n * ```\r\n */\r\n async getReservation(\r\n resourceId: string,\r\n reservationId: string,\r\n ): Promise<ReservationResponse> {\r\n return this.request<ReservationResponse>(\r\n \"GET\",\r\n `/resources/${encodeURIComponent(resourceId)}/reservations/${encodeURIComponent(reservationId)}`,\r\n );\r\n }\r\n\r\n /**\r\n * List all active products/services for the organisation.\r\n *\r\n * Calls `GET /v1/public/products` which returns products with\r\n * pricing, duration, and linked equipment.\r\n *\r\n * Falls back to mapping resources → products if the endpoint\r\n * returns 404 (backend hasn't been updated yet).\r\n *\r\n * ```ts\r\n * const { products } = await resira.listProducts();\r\n * ```\r\n */\r\n async listProducts(): Promise<ProductListResponse> {\r\n try {\r\n return await this.request<ProductListResponse>(\"GET\", \"/products\");\r\n } catch (err: unknown) {\r\n // Fallback: if /products endpoint doesn't exist yet (404),\r\n // map resources → products shape\r\n if (\r\n err &&\r\n typeof err === \"object\" &&\r\n \"status\" in err &&\r\n (err as { status: number }).status === 404\r\n ) {\r\n const resourceResponse = await this.listResources();\r\n const products = resourceResponse.resources.map((r) => ({\r\n id: r.id,\r\n name: r.name,\r\n description: r.description,\r\n durationMinutes: r.durationMinutes ?? 60,\r\n priceCents: r.pricePerSession ?? 0,\r\n currency: r.currency ?? \"EUR\",\r\n imageUrl: r.imageUrl,\r\n active: r.active,\r\n sortOrder: 0,\r\n pricingModel: \"per_session\" as const,\r\n equipmentIds: [r.id],\r\n equipmentNames: [r.name],\r\n }));\r\n return { products, count: products.length };\r\n }\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * List all dishes with 3D model data for the organisation.\r\n *\r\n * Returns dishes with name, description, price, image, and\r\n * 3D model URLs for AR/preview rendering.\r\n *\r\n * ```ts\r\n * const { dishes } = await resira.listDishes();\r\n * ```\r\n */\r\n async listDishes(): Promise<DishListResponse> {\r\n return this.request<DishListResponse>(\"GET\", \"/dishes\");\r\n }\r\n\r\n /**\r\n * Get a single dish by ID.\r\n *\r\n * ```ts\r\n * const { dish } = await resira.getDish(\"dish-uuid\");\r\n * console.log(dish.name, dish.model?.glbUrl);\r\n * ```\r\n */\r\n async getDish(dishId: string): Promise<DishResponse> {\r\n if (!dishId) {\r\n throw new Error(\"dishId is required for getDish\");\r\n }\r\n return this.request<DishResponse>(\r\n \"GET\",\r\n `/dishes/${encodeURIComponent(dishId)}`,\r\n );\r\n }\r\n\r\n /**\r\n * Retrieve a checkout session by its token.\r\n *\r\n * Used to hydrate the checkout screen when opening the widget\r\n * with a pre-created session token (skip service selection).\r\n *\r\n * ```ts\r\n * const { session } = await resira.getCheckoutSession(\"cs_ab12cd34ef56\");\r\n * console.log(session.productName, session.priceCents);\r\n * ```\r\n */\r\n async getCheckoutSession(token: string): Promise<CheckoutSessionResponse> {\r\n if (!token) {\r\n throw new Error(\"token is required for getCheckoutSession\");\r\n }\r\n const url = `${this.getBaseUrl()}/v2/public/checkout-sessions/${encodeURIComponent(token)}`;\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n Accept: \"application/json\",\r\n };\r\n\r\n let lastError: ResiraApiError | ResiraNetworkError | undefined;\r\n\r\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\r\n if (attempt > 0) {\r\n const backoff = this.calculateBackoff(attempt, lastError);\r\n await sleep(backoff);\r\n }\r\n\r\n let response: Response;\r\n try {\r\n response = await this._fetch(url, { method: \"GET\", headers });\r\n } catch (err) {\r\n lastError = new ResiraNetworkError(\r\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\r\n err,\r\n );\r\n continue;\r\n }\r\n\r\n if (response.ok) {\r\n const raw = await response.json();\r\n return deepKeysToCamel(raw) as CheckoutSessionResponse;\r\n }\r\n\r\n let errorBody: ApiErrorBody;\r\n try {\r\n errorBody = (await response.json()) as ApiErrorBody;\r\n } catch {\r\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\r\n }\r\n\r\n if (response.status === 429) {\r\n lastError = new ResiraRateLimitError(errorBody, response);\r\n continue;\r\n }\r\n if (response.status >= 500) {\r\n lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n continue;\r\n }\r\n\r\n throw new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n }\r\n\r\n throw lastError!;\r\n }\r\n\r\n /**\r\n * Create a new checkout session.\r\n *\r\n * External sites can create a session with product, slot, and guest\r\n * details, then pass the returned token to the widget to skip\r\n * directly to checkout.\r\n *\r\n * ```ts\r\n * const { sessionToken } = await resira.createCheckoutSession({\r\n * productId: \"prod-123\",\r\n * durationMinutes: 30,\r\n * partySize: 2,\r\n * date: \"2026-07-01\",\r\n * startTime: \"2026-07-01T10:00:00Z\",\r\n * endTime: \"2026-07-01T10:30:00Z\",\r\n * });\r\n * ```\r\n */\r\n async createCheckoutSession(\r\n payload: CreateCheckoutSessionRequest,\r\n options?: { idempotencyKey?: string },\r\n ): Promise<CreateCheckoutSessionResponse> {\r\n const idempotencyKey = options?.idempotencyKey ?? uuid();\r\n const url = `${this.getBaseUrl()}/v2/public/checkout-sessions`;\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n Accept: \"application/json\",\r\n \"Content-Type\": \"application/json\",\r\n \"Idempotency-Key\": idempotencyKey,\r\n };\r\n\r\n const init: RequestInit = {\r\n method: \"POST\",\r\n headers,\r\n body: JSON.stringify(payload),\r\n };\r\n\r\n let lastError: ResiraApiError | ResiraNetworkError | undefined;\r\n\r\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\r\n if (attempt > 0) {\r\n const backoff = this.calculateBackoff(attempt, lastError);\r\n await sleep(backoff);\r\n }\r\n\r\n let response: Response;\r\n try {\r\n response = await this._fetch(url, init);\r\n } catch (err) {\r\n lastError = new ResiraNetworkError(\r\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\r\n err,\r\n );\r\n continue;\r\n }\r\n\r\n if (response.ok) {\r\n const raw = await response.json();\r\n return deepKeysToCamel(raw) as CreateCheckoutSessionResponse;\r\n }\r\n\r\n let errorBody: ApiErrorBody;\r\n try {\r\n errorBody = (await response.json()) as ApiErrorBody;\r\n } catch {\r\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\r\n }\r\n\r\n if (response.status === 429) {\r\n lastError = new ResiraRateLimitError(errorBody, response);\r\n continue;\r\n }\r\n if (response.status >= 500) {\r\n lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n continue;\r\n }\r\n\r\n throw new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n }\r\n\r\n throw lastError!;\r\n }\r\n\r\n /**\r\n * Create a Stripe payment intent for a booking.\r\n *\r\n * The server creates the payment intent, calculates the amount,\r\n * and returns a `clientSecret` to confirm payment on the frontend\r\n * using Stripe Elements.\r\n *\r\n * ```ts\r\n * const { clientSecret, amountNow, amountAtVenue } =\r\n * await resira.createPaymentIntent({\r\n * productId: \"prod-123\",\r\n * resourceId: \"res-456\",\r\n * partySize: 2,\r\n * startDate: \"2026-07-01\",\r\n * startTime: \"2026-07-01T10:00:00Z\",\r\n * endTime: \"2026-07-01T11:00:00Z\",\r\n * guestName: \"Jane Doe\",\r\n * guestEmail: \"jane@example.com\",\r\n * });\r\n * ```\r\n */\r\n async createPaymentIntent(\r\n payload: CreatePaymentIntentRequest,\r\n options?: { idempotencyKey?: string },\r\n ): Promise<PaymentIntentResponse> {\r\n const idempotencyKey = options?.idempotencyKey ?? uuid();\r\n const raw = await this.request<Record<string, unknown>>(\r\n \"POST\",\r\n \"/payments/create-intent\",\r\n payload,\r\n { \"Idempotency-Key\": idempotencyKey },\r\n );\r\n // Normalize response keys to camelCase\r\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\r\n // Map backend field names to SDK field names:\r\n // backend: amountDue, totalPrice\r\n // SDK: amountNow, totalAmount, amountAtVenue\r\n const totalAmount = (data.totalAmount as number) ?? (data.totalPrice as number) ?? 0;\r\n const amountNow = (data.amountNow as number) ?? (data.amountDue as number) ?? totalAmount;\r\n const amountAtVenue = (data.amountAtVenue as number) ?? (totalAmount - amountNow);\r\n return {\r\n clientSecret: data.clientSecret as string,\r\n paymentIntentId: data.paymentIntentId as string,\r\n amountNow,\r\n amountAtVenue,\r\n totalAmount,\r\n currency: (data.currency as string) ?? \"EUR\",\r\n paymentOption: data.paymentOption as string | undefined,\r\n reservationId: data.reservationId as string | undefined,\r\n discountAmount: (data.discountAmount as number) ?? undefined,\r\n originalPrice: (data.originalPrice as number) ?? undefined,\r\n promoCodeApplied: (data.promoCodeApplied as string) ?? undefined,\r\n };\r\n }\r\n\r\n /**\r\n * Confirm that a payment was successful after the guest\r\n * completes the Stripe payment flow.\r\n *\r\n * This transitions the reservation from `pending` to `confirmed`.\r\n *\r\n * ```ts\r\n * const { reservation, paymentStatus } =\r\n * await resira.confirmPayment({\r\n * paymentIntentId: \"pi_xxx\",\r\n * reservationId: \"res-uuid\",\r\n * });\r\n * ```\r\n */\r\n async confirmPayment(\r\n payload: ConfirmPaymentRequest,\r\n ): Promise<ConfirmPaymentResponse> {\r\n const raw = await this.request<Record<string, unknown>>(\r\n \"POST\",\r\n \"/payments/confirm\",\r\n payload,\r\n );\r\n // Normalize response keys to camelCase in case backend returns snake_case\r\n return deepKeysToCamel(raw) as ConfirmPaymentResponse;\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────────\r\n // Internal HTTP layer\r\n // ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Execute an HTTP request with retry logic and error normalization.\r\n *\r\n * - Attaches `Authorization: Bearer <apiKey>` on every request.\r\n * - Retries on 429 and 5xx with exponential backoff + jitter.\r\n * - Parses error bodies and throws typed error classes.\r\n */\r\n private async request<T>(\r\n method: string,\r\n path: string,\r\n body?: unknown,\r\n extraHeaders?: Record<string, string>,\r\n ): Promise<T> {\r\n const url = `${this.getBaseUrl()}${API_PREFIX}${path}`;\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n Accept: \"application/json\",\r\n ...extraHeaders,\r\n };\r\n\r\n if (body !== undefined) {\r\n headers[\"Content-Type\"] = \"application/json\";\r\n }\r\n\r\n const init: RequestInit = {\r\n method,\r\n headers,\r\n body: body !== undefined ? JSON.stringify(body) : undefined,\r\n };\r\n\r\n let lastError: ResiraApiError | ResiraNetworkError | undefined;\r\n\r\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\r\n // ── Wait before retry ────────────────────────────────\r\n if (attempt > 0) {\r\n const backoff = this.calculateBackoff(attempt, lastError);\r\n await sleep(backoff);\r\n }\r\n\r\n // ── Execute the request ──────────────────────────────\r\n let response: Response;\r\n try {\r\n response = await this._fetch(url, init);\r\n } catch (err) {\r\n lastError = new ResiraNetworkError(\r\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\r\n err,\r\n );\r\n // Network errors are always retryable\r\n continue;\r\n }\r\n\r\n // ── Success ──────────────────────────────────────────\r\n if (response.ok) {\r\n return (await response.json()) as T;\r\n }\r\n\r\n // ── Parse error body ─────────────────────────────────\r\n let errorBody: ApiErrorBody;\r\n try {\r\n errorBody = (await response.json()) as ApiErrorBody;\r\n } catch {\r\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\r\n }\r\n\r\n // ── Rate limit ───────────────────────────────────────\r\n if (response.status === 429) {\r\n lastError = new ResiraRateLimitError(errorBody, response);\r\n // Always retry 429s (up to maxRetries)\r\n continue;\r\n }\r\n\r\n // ── Server error (5xx) — retryable ───────────────────\r\n if (response.status >= 500) {\r\n lastError = new ResiraApiError(\r\n response.status,\r\n response.statusText,\r\n errorBody,\r\n response,\r\n );\r\n continue;\r\n }\r\n\r\n // ── Client error (4xx) — NOT retryable ───────────────\r\n throw new ResiraApiError(\r\n response.status,\r\n response.statusText,\r\n errorBody,\r\n response,\r\n );\r\n }\r\n\r\n // All retries exhausted — throw the last error\r\n throw lastError!;\r\n }\r\n\r\n /**\r\n * Calculate backoff delay in milliseconds for a given retry attempt.\r\n *\r\n * Uses exponential backoff with full jitter:\r\n * delay = random(0, base * 2^attempt)\r\n *\r\n * For 429 responses, respects the `Retry-After` header as a minimum.\r\n */\r\n private calculateBackoff(\r\n attempt: number,\r\n lastError?: ResiraApiError | ResiraNetworkError,\r\n ): number {\r\n const exponential = this.retryBaseDelay * 2 ** (attempt - 1);\r\n const jittered = Math.random() * exponential;\r\n\r\n // If the server told us to wait, use that as the floor\r\n if (lastError instanceof ResiraRateLimitError) {\r\n const serverDelay = lastError.retryAfter * 1000;\r\n return Math.max(serverDelay, jittered);\r\n }\r\n\r\n return jittered;\r\n }\r\n\r\n}\r\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -271,6 +271,10 @@ interface Product {
|
|
|
271
271
|
durationPricing?: DurationPrice[];
|
|
272
272
|
/** Rider-tier pricing (per_rider model). Each tier has a rider count and its own duration-price set. */
|
|
273
273
|
riderTierPricing?: RiderTierPrice[];
|
|
274
|
+
/** Normalized product variants with duration and price (returned by v2 API). */
|
|
275
|
+
variants?: ProductVariant[];
|
|
276
|
+
/** Available slot durations in minutes (returned by v2 API). */
|
|
277
|
+
slotDurations?: number[];
|
|
274
278
|
}
|
|
275
279
|
/** Response wrapper for product listing. */
|
|
276
280
|
interface ProductListResponse {
|
|
@@ -280,15 +284,15 @@ interface ProductListResponse {
|
|
|
280
284
|
/** Request body for creating a payment intent. */
|
|
281
285
|
interface CreatePaymentIntentRequest {
|
|
282
286
|
/** Product ID being booked. */
|
|
283
|
-
productId
|
|
287
|
+
productId?: string;
|
|
284
288
|
/** Resource/equipment ID. */
|
|
285
|
-
resourceId
|
|
289
|
+
resourceId?: string;
|
|
286
290
|
/** Party size. */
|
|
287
|
-
partySize
|
|
291
|
+
partySize?: number;
|
|
288
292
|
/** Selected duration in minutes (for multi-duration products). */
|
|
289
293
|
durationMinutes?: number;
|
|
290
294
|
/** Start date (YYYY-MM-DD). */
|
|
291
|
-
startDate
|
|
295
|
+
startDate?: string;
|
|
292
296
|
/** Start time (ISO 8601, optional). */
|
|
293
297
|
startTime?: string;
|
|
294
298
|
/** End time (ISO 8601, optional). */
|
|
@@ -305,6 +309,8 @@ interface CreatePaymentIntentRequest {
|
|
|
305
309
|
promoCode?: string;
|
|
306
310
|
/** Whether the guest accepted terms & conditions. Required when property has termsRequired=true. */
|
|
307
311
|
termsAccepted?: boolean;
|
|
312
|
+
/** Checkout session token. When provided, session data is merged with the payload. */
|
|
313
|
+
checkoutSessionToken?: string;
|
|
308
314
|
}
|
|
309
315
|
/** Response from creating a payment intent. */
|
|
310
316
|
interface PaymentIntentResponse {
|
|
@@ -451,6 +457,97 @@ interface DishListResponse {
|
|
|
451
457
|
dishes: Dish[];
|
|
452
458
|
count: number;
|
|
453
459
|
}
|
|
460
|
+
/** Checkout configuration embedded in a checkout session. */
|
|
461
|
+
interface CheckoutConfig {
|
|
462
|
+
/** Stripe publishable key. */
|
|
463
|
+
stripePublishableKey: string;
|
|
464
|
+
/** Deposit percentage (0–100). */
|
|
465
|
+
depositPercent: number;
|
|
466
|
+
/** Whether terms acceptance is required. */
|
|
467
|
+
termsRequired: boolean;
|
|
468
|
+
/** Terms text to display. */
|
|
469
|
+
termsText?: string;
|
|
470
|
+
}
|
|
471
|
+
/** A checkout session as returned by the API. */
|
|
472
|
+
interface CheckoutSession {
|
|
473
|
+
/** Unique session token. */
|
|
474
|
+
sessionToken: string;
|
|
475
|
+
/** Session status. */
|
|
476
|
+
status: "pending" | "consumed" | "expired";
|
|
477
|
+
/** When the session expires (ISO 8601). */
|
|
478
|
+
expiresAt: string;
|
|
479
|
+
/** Product ID. */
|
|
480
|
+
productId: string;
|
|
481
|
+
/** Product display name. */
|
|
482
|
+
productName: string;
|
|
483
|
+
/** Duration in minutes. */
|
|
484
|
+
durationMinutes: number;
|
|
485
|
+
/** Price in smallest currency unit (cents). */
|
|
486
|
+
priceCents: number;
|
|
487
|
+
/** ISO 4217 currency code. */
|
|
488
|
+
currency: string;
|
|
489
|
+
/** Party size / number of riders. */
|
|
490
|
+
partySize: number;
|
|
491
|
+
/** Pricing model. */
|
|
492
|
+
pricingModel: PricingModel;
|
|
493
|
+
/** Booking date (YYYY-MM-DD). */
|
|
494
|
+
date: string;
|
|
495
|
+
/** Start time (ISO 8601). */
|
|
496
|
+
startTime: string;
|
|
497
|
+
/** End time (ISO 8601). */
|
|
498
|
+
endTime: string;
|
|
499
|
+
/** Service display color. */
|
|
500
|
+
serviceColor?: string;
|
|
501
|
+
/** Product image URL. */
|
|
502
|
+
imageUrl?: string;
|
|
503
|
+
/** Pre-filled guest name. */
|
|
504
|
+
guestName?: string;
|
|
505
|
+
/** Pre-filled guest email. */
|
|
506
|
+
guestEmail?: string;
|
|
507
|
+
/** Pre-applied promo code. */
|
|
508
|
+
promoCode?: string;
|
|
509
|
+
/** Checkout-specific configuration. */
|
|
510
|
+
checkoutConfig: CheckoutConfig;
|
|
511
|
+
}
|
|
512
|
+
/** Response wrapper for a checkout session. */
|
|
513
|
+
interface CheckoutSessionResponse {
|
|
514
|
+
session: CheckoutSession;
|
|
515
|
+
}
|
|
516
|
+
/** Request body for creating a checkout session. */
|
|
517
|
+
interface CreateCheckoutSessionRequest {
|
|
518
|
+
/** Product ID. */
|
|
519
|
+
productId: string;
|
|
520
|
+
/** Duration in minutes. */
|
|
521
|
+
durationMinutes: number;
|
|
522
|
+
/** Party size. */
|
|
523
|
+
partySize: number;
|
|
524
|
+
/** Booking date (YYYY-MM-DD). */
|
|
525
|
+
date: string;
|
|
526
|
+
/** Start time (ISO 8601). */
|
|
527
|
+
startTime: string;
|
|
528
|
+
/** End time (ISO 8601). */
|
|
529
|
+
endTime: string;
|
|
530
|
+
/** Guest name (optional). */
|
|
531
|
+
guestName?: string;
|
|
532
|
+
/** Guest email (optional). */
|
|
533
|
+
guestEmail?: string;
|
|
534
|
+
/** Promo code (optional). */
|
|
535
|
+
promoCode?: string;
|
|
536
|
+
}
|
|
537
|
+
/** Response from creating a checkout session. */
|
|
538
|
+
interface CreateCheckoutSessionResponse {
|
|
539
|
+
sessionToken: string;
|
|
540
|
+
expiresAt: string;
|
|
541
|
+
}
|
|
542
|
+
/** A normalized product variant with duration and price. */
|
|
543
|
+
interface ProductVariant {
|
|
544
|
+
/** Duration in minutes. */
|
|
545
|
+
durationMinutes: number;
|
|
546
|
+
/** Price in smallest currency unit (cents). */
|
|
547
|
+
priceCents: number;
|
|
548
|
+
/** Optional display label (e.g. "Full Hour"). */
|
|
549
|
+
label?: string;
|
|
550
|
+
}
|
|
454
551
|
|
|
455
552
|
/**
|
|
456
553
|
* Resira SDK client.
|
|
@@ -628,6 +725,39 @@ declare class Resira {
|
|
|
628
725
|
* ```
|
|
629
726
|
*/
|
|
630
727
|
getDish(dishId: string): Promise<DishResponse>;
|
|
728
|
+
/**
|
|
729
|
+
* Retrieve a checkout session by its token.
|
|
730
|
+
*
|
|
731
|
+
* Used to hydrate the checkout screen when opening the widget
|
|
732
|
+
* with a pre-created session token (skip service selection).
|
|
733
|
+
*
|
|
734
|
+
* ```ts
|
|
735
|
+
* const { session } = await resira.getCheckoutSession("cs_ab12cd34ef56");
|
|
736
|
+
* console.log(session.productName, session.priceCents);
|
|
737
|
+
* ```
|
|
738
|
+
*/
|
|
739
|
+
getCheckoutSession(token: string): Promise<CheckoutSessionResponse>;
|
|
740
|
+
/**
|
|
741
|
+
* Create a new checkout session.
|
|
742
|
+
*
|
|
743
|
+
* External sites can create a session with product, slot, and guest
|
|
744
|
+
* details, then pass the returned token to the widget to skip
|
|
745
|
+
* directly to checkout.
|
|
746
|
+
*
|
|
747
|
+
* ```ts
|
|
748
|
+
* const { sessionToken } = await resira.createCheckoutSession({
|
|
749
|
+
* productId: "prod-123",
|
|
750
|
+
* durationMinutes: 30,
|
|
751
|
+
* partySize: 2,
|
|
752
|
+
* date: "2026-07-01",
|
|
753
|
+
* startTime: "2026-07-01T10:00:00Z",
|
|
754
|
+
* endTime: "2026-07-01T10:30:00Z",
|
|
755
|
+
* });
|
|
756
|
+
* ```
|
|
757
|
+
*/
|
|
758
|
+
createCheckoutSession(payload: CreateCheckoutSessionRequest, options?: {
|
|
759
|
+
idempotencyKey?: string;
|
|
760
|
+
}): Promise<CreateCheckoutSessionResponse>;
|
|
631
761
|
/**
|
|
632
762
|
* Create a Stripe payment intent for a booking.
|
|
633
763
|
*
|
|
@@ -750,4 +880,4 @@ declare class ResiraNetworkError extends ResiraError {
|
|
|
750
880
|
constructor(message: string, cause: unknown);
|
|
751
881
|
}
|
|
752
882
|
|
|
753
|
-
export { type ApiErrorBody, type Availability, type AvailabilityParams, type ConfirmPaymentRequest, type ConfirmPaymentResponse, type CreatePaymentIntentRequest, type CreateReservationRequest, type DateAvailability, type Dish, type DishListResponse, type DishModel, type DishResponse, type DurationPrice, type ListReservationsParams, type PaginatedReservations, type PaymentIntentResponse, type PricingModel, type Product, type ProductListResponse, type PromoCode, type PropertyConfig, type PublicResource, type RefundRule, type Reservation, type ReservationResponse, Resira, ResiraApiError, type ResiraConfig, ResiraError, ResiraNetworkError, ResiraRateLimitError, type ResourceListResponse, type RiderTierPrice, type TimeSlotAvailability, type ValidatePromoCodeResponse, type WeightedBaseUrl, getRoutingStats };
|
|
883
|
+
export { type ApiErrorBody, type Availability, type AvailabilityParams, type CheckoutConfig, type CheckoutSession, type CheckoutSessionResponse, type ConfirmPaymentRequest, type ConfirmPaymentResponse, type CreateCheckoutSessionRequest, type CreateCheckoutSessionResponse, type CreatePaymentIntentRequest, type CreateReservationRequest, type DateAvailability, type Dish, type DishListResponse, type DishModel, type DishResponse, type DurationPrice, type ListReservationsParams, type PaginatedReservations, type PaymentIntentResponse, type PricingModel, type Product, type ProductListResponse, type ProductVariant, type PromoCode, type PropertyConfig, type PublicResource, type RefundRule, type Reservation, type ReservationResponse, Resira, ResiraApiError, type ResiraConfig, ResiraError, ResiraNetworkError, ResiraRateLimitError, type ResourceListResponse, type RiderTierPrice, type TimeSlotAvailability, type ValidatePromoCodeResponse, type WeightedBaseUrl, getRoutingStats };
|
package/dist/index.d.ts
CHANGED
|
@@ -271,6 +271,10 @@ interface Product {
|
|
|
271
271
|
durationPricing?: DurationPrice[];
|
|
272
272
|
/** Rider-tier pricing (per_rider model). Each tier has a rider count and its own duration-price set. */
|
|
273
273
|
riderTierPricing?: RiderTierPrice[];
|
|
274
|
+
/** Normalized product variants with duration and price (returned by v2 API). */
|
|
275
|
+
variants?: ProductVariant[];
|
|
276
|
+
/** Available slot durations in minutes (returned by v2 API). */
|
|
277
|
+
slotDurations?: number[];
|
|
274
278
|
}
|
|
275
279
|
/** Response wrapper for product listing. */
|
|
276
280
|
interface ProductListResponse {
|
|
@@ -280,15 +284,15 @@ interface ProductListResponse {
|
|
|
280
284
|
/** Request body for creating a payment intent. */
|
|
281
285
|
interface CreatePaymentIntentRequest {
|
|
282
286
|
/** Product ID being booked. */
|
|
283
|
-
productId
|
|
287
|
+
productId?: string;
|
|
284
288
|
/** Resource/equipment ID. */
|
|
285
|
-
resourceId
|
|
289
|
+
resourceId?: string;
|
|
286
290
|
/** Party size. */
|
|
287
|
-
partySize
|
|
291
|
+
partySize?: number;
|
|
288
292
|
/** Selected duration in minutes (for multi-duration products). */
|
|
289
293
|
durationMinutes?: number;
|
|
290
294
|
/** Start date (YYYY-MM-DD). */
|
|
291
|
-
startDate
|
|
295
|
+
startDate?: string;
|
|
292
296
|
/** Start time (ISO 8601, optional). */
|
|
293
297
|
startTime?: string;
|
|
294
298
|
/** End time (ISO 8601, optional). */
|
|
@@ -305,6 +309,8 @@ interface CreatePaymentIntentRequest {
|
|
|
305
309
|
promoCode?: string;
|
|
306
310
|
/** Whether the guest accepted terms & conditions. Required when property has termsRequired=true. */
|
|
307
311
|
termsAccepted?: boolean;
|
|
312
|
+
/** Checkout session token. When provided, session data is merged with the payload. */
|
|
313
|
+
checkoutSessionToken?: string;
|
|
308
314
|
}
|
|
309
315
|
/** Response from creating a payment intent. */
|
|
310
316
|
interface PaymentIntentResponse {
|
|
@@ -451,6 +457,97 @@ interface DishListResponse {
|
|
|
451
457
|
dishes: Dish[];
|
|
452
458
|
count: number;
|
|
453
459
|
}
|
|
460
|
+
/** Checkout configuration embedded in a checkout session. */
|
|
461
|
+
interface CheckoutConfig {
|
|
462
|
+
/** Stripe publishable key. */
|
|
463
|
+
stripePublishableKey: string;
|
|
464
|
+
/** Deposit percentage (0–100). */
|
|
465
|
+
depositPercent: number;
|
|
466
|
+
/** Whether terms acceptance is required. */
|
|
467
|
+
termsRequired: boolean;
|
|
468
|
+
/** Terms text to display. */
|
|
469
|
+
termsText?: string;
|
|
470
|
+
}
|
|
471
|
+
/** A checkout session as returned by the API. */
|
|
472
|
+
interface CheckoutSession {
|
|
473
|
+
/** Unique session token. */
|
|
474
|
+
sessionToken: string;
|
|
475
|
+
/** Session status. */
|
|
476
|
+
status: "pending" | "consumed" | "expired";
|
|
477
|
+
/** When the session expires (ISO 8601). */
|
|
478
|
+
expiresAt: string;
|
|
479
|
+
/** Product ID. */
|
|
480
|
+
productId: string;
|
|
481
|
+
/** Product display name. */
|
|
482
|
+
productName: string;
|
|
483
|
+
/** Duration in minutes. */
|
|
484
|
+
durationMinutes: number;
|
|
485
|
+
/** Price in smallest currency unit (cents). */
|
|
486
|
+
priceCents: number;
|
|
487
|
+
/** ISO 4217 currency code. */
|
|
488
|
+
currency: string;
|
|
489
|
+
/** Party size / number of riders. */
|
|
490
|
+
partySize: number;
|
|
491
|
+
/** Pricing model. */
|
|
492
|
+
pricingModel: PricingModel;
|
|
493
|
+
/** Booking date (YYYY-MM-DD). */
|
|
494
|
+
date: string;
|
|
495
|
+
/** Start time (ISO 8601). */
|
|
496
|
+
startTime: string;
|
|
497
|
+
/** End time (ISO 8601). */
|
|
498
|
+
endTime: string;
|
|
499
|
+
/** Service display color. */
|
|
500
|
+
serviceColor?: string;
|
|
501
|
+
/** Product image URL. */
|
|
502
|
+
imageUrl?: string;
|
|
503
|
+
/** Pre-filled guest name. */
|
|
504
|
+
guestName?: string;
|
|
505
|
+
/** Pre-filled guest email. */
|
|
506
|
+
guestEmail?: string;
|
|
507
|
+
/** Pre-applied promo code. */
|
|
508
|
+
promoCode?: string;
|
|
509
|
+
/** Checkout-specific configuration. */
|
|
510
|
+
checkoutConfig: CheckoutConfig;
|
|
511
|
+
}
|
|
512
|
+
/** Response wrapper for a checkout session. */
|
|
513
|
+
interface CheckoutSessionResponse {
|
|
514
|
+
session: CheckoutSession;
|
|
515
|
+
}
|
|
516
|
+
/** Request body for creating a checkout session. */
|
|
517
|
+
interface CreateCheckoutSessionRequest {
|
|
518
|
+
/** Product ID. */
|
|
519
|
+
productId: string;
|
|
520
|
+
/** Duration in minutes. */
|
|
521
|
+
durationMinutes: number;
|
|
522
|
+
/** Party size. */
|
|
523
|
+
partySize: number;
|
|
524
|
+
/** Booking date (YYYY-MM-DD). */
|
|
525
|
+
date: string;
|
|
526
|
+
/** Start time (ISO 8601). */
|
|
527
|
+
startTime: string;
|
|
528
|
+
/** End time (ISO 8601). */
|
|
529
|
+
endTime: string;
|
|
530
|
+
/** Guest name (optional). */
|
|
531
|
+
guestName?: string;
|
|
532
|
+
/** Guest email (optional). */
|
|
533
|
+
guestEmail?: string;
|
|
534
|
+
/** Promo code (optional). */
|
|
535
|
+
promoCode?: string;
|
|
536
|
+
}
|
|
537
|
+
/** Response from creating a checkout session. */
|
|
538
|
+
interface CreateCheckoutSessionResponse {
|
|
539
|
+
sessionToken: string;
|
|
540
|
+
expiresAt: string;
|
|
541
|
+
}
|
|
542
|
+
/** A normalized product variant with duration and price. */
|
|
543
|
+
interface ProductVariant {
|
|
544
|
+
/** Duration in minutes. */
|
|
545
|
+
durationMinutes: number;
|
|
546
|
+
/** Price in smallest currency unit (cents). */
|
|
547
|
+
priceCents: number;
|
|
548
|
+
/** Optional display label (e.g. "Full Hour"). */
|
|
549
|
+
label?: string;
|
|
550
|
+
}
|
|
454
551
|
|
|
455
552
|
/**
|
|
456
553
|
* Resira SDK client.
|
|
@@ -628,6 +725,39 @@ declare class Resira {
|
|
|
628
725
|
* ```
|
|
629
726
|
*/
|
|
630
727
|
getDish(dishId: string): Promise<DishResponse>;
|
|
728
|
+
/**
|
|
729
|
+
* Retrieve a checkout session by its token.
|
|
730
|
+
*
|
|
731
|
+
* Used to hydrate the checkout screen when opening the widget
|
|
732
|
+
* with a pre-created session token (skip service selection).
|
|
733
|
+
*
|
|
734
|
+
* ```ts
|
|
735
|
+
* const { session } = await resira.getCheckoutSession("cs_ab12cd34ef56");
|
|
736
|
+
* console.log(session.productName, session.priceCents);
|
|
737
|
+
* ```
|
|
738
|
+
*/
|
|
739
|
+
getCheckoutSession(token: string): Promise<CheckoutSessionResponse>;
|
|
740
|
+
/**
|
|
741
|
+
* Create a new checkout session.
|
|
742
|
+
*
|
|
743
|
+
* External sites can create a session with product, slot, and guest
|
|
744
|
+
* details, then pass the returned token to the widget to skip
|
|
745
|
+
* directly to checkout.
|
|
746
|
+
*
|
|
747
|
+
* ```ts
|
|
748
|
+
* const { sessionToken } = await resira.createCheckoutSession({
|
|
749
|
+
* productId: "prod-123",
|
|
750
|
+
* durationMinutes: 30,
|
|
751
|
+
* partySize: 2,
|
|
752
|
+
* date: "2026-07-01",
|
|
753
|
+
* startTime: "2026-07-01T10:00:00Z",
|
|
754
|
+
* endTime: "2026-07-01T10:30:00Z",
|
|
755
|
+
* });
|
|
756
|
+
* ```
|
|
757
|
+
*/
|
|
758
|
+
createCheckoutSession(payload: CreateCheckoutSessionRequest, options?: {
|
|
759
|
+
idempotencyKey?: string;
|
|
760
|
+
}): Promise<CreateCheckoutSessionResponse>;
|
|
631
761
|
/**
|
|
632
762
|
* Create a Stripe payment intent for a booking.
|
|
633
763
|
*
|
|
@@ -750,4 +880,4 @@ declare class ResiraNetworkError extends ResiraError {
|
|
|
750
880
|
constructor(message: string, cause: unknown);
|
|
751
881
|
}
|
|
752
882
|
|
|
753
|
-
export { type ApiErrorBody, type Availability, type AvailabilityParams, type ConfirmPaymentRequest, type ConfirmPaymentResponse, type CreatePaymentIntentRequest, type CreateReservationRequest, type DateAvailability, type Dish, type DishListResponse, type DishModel, type DishResponse, type DurationPrice, type ListReservationsParams, type PaginatedReservations, type PaymentIntentResponse, type PricingModel, type Product, type ProductListResponse, type PromoCode, type PropertyConfig, type PublicResource, type RefundRule, type Reservation, type ReservationResponse, Resira, ResiraApiError, type ResiraConfig, ResiraError, ResiraNetworkError, ResiraRateLimitError, type ResourceListResponse, type RiderTierPrice, type TimeSlotAvailability, type ValidatePromoCodeResponse, type WeightedBaseUrl, getRoutingStats };
|
|
883
|
+
export { type ApiErrorBody, type Availability, type AvailabilityParams, type CheckoutConfig, type CheckoutSession, type CheckoutSessionResponse, type ConfirmPaymentRequest, type ConfirmPaymentResponse, type CreateCheckoutSessionRequest, type CreateCheckoutSessionResponse, type CreatePaymentIntentRequest, type CreateReservationRequest, type DateAvailability, type Dish, type DishListResponse, type DishModel, type DishResponse, type DurationPrice, type ListReservationsParams, type PaginatedReservations, type PaymentIntentResponse, type PricingModel, type Product, type ProductListResponse, type ProductVariant, type PromoCode, type PropertyConfig, type PublicResource, type RefundRule, type Reservation, type ReservationResponse, Resira, ResiraApiError, type ResiraConfig, ResiraError, ResiraNetworkError, ResiraRateLimitError, type ResourceListResponse, type RiderTierPrice, type TimeSlotAvailability, type ValidatePromoCodeResponse, type WeightedBaseUrl, getRoutingStats };
|
package/dist/index.js
CHANGED
|
@@ -503,6 +503,134 @@ var Resira = class {
|
|
|
503
503
|
`/dishes/${encodeURIComponent(dishId)}`
|
|
504
504
|
);
|
|
505
505
|
}
|
|
506
|
+
/**
|
|
507
|
+
* Retrieve a checkout session by its token.
|
|
508
|
+
*
|
|
509
|
+
* Used to hydrate the checkout screen when opening the widget
|
|
510
|
+
* with a pre-created session token (skip service selection).
|
|
511
|
+
*
|
|
512
|
+
* ```ts
|
|
513
|
+
* const { session } = await resira.getCheckoutSession("cs_ab12cd34ef56");
|
|
514
|
+
* console.log(session.productName, session.priceCents);
|
|
515
|
+
* ```
|
|
516
|
+
*/
|
|
517
|
+
async getCheckoutSession(token) {
|
|
518
|
+
if (!token) {
|
|
519
|
+
throw new Error("token is required for getCheckoutSession");
|
|
520
|
+
}
|
|
521
|
+
const url = `${this.getBaseUrl()}/v2/public/checkout-sessions/${encodeURIComponent(token)}`;
|
|
522
|
+
const headers = {
|
|
523
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
524
|
+
Accept: "application/json"
|
|
525
|
+
};
|
|
526
|
+
let lastError;
|
|
527
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
528
|
+
if (attempt > 0) {
|
|
529
|
+
const backoff = this.calculateBackoff(attempt, lastError);
|
|
530
|
+
await sleep(backoff);
|
|
531
|
+
}
|
|
532
|
+
let response;
|
|
533
|
+
try {
|
|
534
|
+
response = await this._fetch(url, { method: "GET", headers });
|
|
535
|
+
} catch (err) {
|
|
536
|
+
lastError = new ResiraNetworkError(
|
|
537
|
+
`Network request failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
538
|
+
err
|
|
539
|
+
);
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (response.ok) {
|
|
543
|
+
const raw = await response.json();
|
|
544
|
+
return deepKeysToCamel(raw);
|
|
545
|
+
}
|
|
546
|
+
let errorBody;
|
|
547
|
+
try {
|
|
548
|
+
errorBody = await response.json();
|
|
549
|
+
} catch {
|
|
550
|
+
errorBody = { error: response.statusText || `HTTP ${response.status}` };
|
|
551
|
+
}
|
|
552
|
+
if (response.status === 429) {
|
|
553
|
+
lastError = new ResiraRateLimitError(errorBody, response);
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
if (response.status >= 500) {
|
|
557
|
+
lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
throw new ResiraApiError(response.status, response.statusText, errorBody, response);
|
|
561
|
+
}
|
|
562
|
+
throw lastError;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Create a new checkout session.
|
|
566
|
+
*
|
|
567
|
+
* External sites can create a session with product, slot, and guest
|
|
568
|
+
* details, then pass the returned token to the widget to skip
|
|
569
|
+
* directly to checkout.
|
|
570
|
+
*
|
|
571
|
+
* ```ts
|
|
572
|
+
* const { sessionToken } = await resira.createCheckoutSession({
|
|
573
|
+
* productId: "prod-123",
|
|
574
|
+
* durationMinutes: 30,
|
|
575
|
+
* partySize: 2,
|
|
576
|
+
* date: "2026-07-01",
|
|
577
|
+
* startTime: "2026-07-01T10:00:00Z",
|
|
578
|
+
* endTime: "2026-07-01T10:30:00Z",
|
|
579
|
+
* });
|
|
580
|
+
* ```
|
|
581
|
+
*/
|
|
582
|
+
async createCheckoutSession(payload, options) {
|
|
583
|
+
const idempotencyKey = options?.idempotencyKey ?? uuid();
|
|
584
|
+
const url = `${this.getBaseUrl()}/v2/public/checkout-sessions`;
|
|
585
|
+
const headers = {
|
|
586
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
587
|
+
Accept: "application/json",
|
|
588
|
+
"Content-Type": "application/json",
|
|
589
|
+
"Idempotency-Key": idempotencyKey
|
|
590
|
+
};
|
|
591
|
+
const init = {
|
|
592
|
+
method: "POST",
|
|
593
|
+
headers,
|
|
594
|
+
body: JSON.stringify(payload)
|
|
595
|
+
};
|
|
596
|
+
let lastError;
|
|
597
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
598
|
+
if (attempt > 0) {
|
|
599
|
+
const backoff = this.calculateBackoff(attempt, lastError);
|
|
600
|
+
await sleep(backoff);
|
|
601
|
+
}
|
|
602
|
+
let response;
|
|
603
|
+
try {
|
|
604
|
+
response = await this._fetch(url, init);
|
|
605
|
+
} catch (err) {
|
|
606
|
+
lastError = new ResiraNetworkError(
|
|
607
|
+
`Network request failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
608
|
+
err
|
|
609
|
+
);
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
if (response.ok) {
|
|
613
|
+
const raw = await response.json();
|
|
614
|
+
return deepKeysToCamel(raw);
|
|
615
|
+
}
|
|
616
|
+
let errorBody;
|
|
617
|
+
try {
|
|
618
|
+
errorBody = await response.json();
|
|
619
|
+
} catch {
|
|
620
|
+
errorBody = { error: response.statusText || `HTTP ${response.status}` };
|
|
621
|
+
}
|
|
622
|
+
if (response.status === 429) {
|
|
623
|
+
lastError = new ResiraRateLimitError(errorBody, response);
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
if (response.status >= 500) {
|
|
627
|
+
lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);
|
|
628
|
+
continue;
|
|
629
|
+
}
|
|
630
|
+
throw new ResiraApiError(response.status, response.statusText, errorBody, response);
|
|
631
|
+
}
|
|
632
|
+
throw lastError;
|
|
633
|
+
}
|
|
506
634
|
/**
|
|
507
635
|
* Create a Stripe payment intent for a booking.
|
|
508
636
|
*
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/routing.ts","../src/client.ts"],"names":["url"],"mappings":"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","file":"index.js","sourcesContent":["import type { ApiErrorBody } from \"./types.js\";\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Base error\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Base error class for all SDK errors.\r\n *\r\n * Use `instanceof` checks to distinguish error types:\r\n *\r\n * ```ts\r\n * try { … } catch (e) {\r\n * if (e instanceof ResiraRateLimitError) { … }\r\n * if (e instanceof ResiraApiError) { … }\r\n * if (e instanceof ResiraNetworkError) { … }\r\n * }\r\n * ```\r\n */\r\nexport class ResiraError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = \"ResiraError\";\r\n // Fix prototype chain for instanceof to work after transpilation\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// API error (4xx / 5xx with a parsed body)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Thrown when the API returns a non-2xx response with a JSON body.\r\n *\r\n * ```ts\r\n * err.status // 400, 404, 409, 422, 500, …\r\n * err.code // HTTP status text (e.g. \"Not Found\")\r\n * err.body // Parsed `{ error: \"…\" }` from the server\r\n * ```\r\n */\r\nexport class ResiraApiError extends ResiraError {\r\n /** HTTP status code. */\r\n readonly status: number;\r\n /** HTTP status text (e.g. `\"Bad Request\"`). */\r\n readonly code: string;\r\n /** Parsed error body from the API. */\r\n readonly body: ApiErrorBody;\r\n /** The full `Response` object (headers are still accessible). */\r\n readonly response: Response;\r\n\r\n constructor(status: number, code: string, body: ApiErrorBody, response: Response) {\r\n super(body.error || `API error ${status}`);\r\n this.name = \"ResiraApiError\";\r\n this.status = status;\r\n this.code = code;\r\n this.body = body;\r\n this.response = response;\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n\r\n /** Whether this error is retryable (5xx or 429). */\r\n get retryable(): boolean {\r\n return this.status === 429 || this.status >= 500;\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Rate limit error (429)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Thrown when the API returns 429 Too Many Requests.\r\n *\r\n * ```ts\r\n * err.retryAfter // Seconds until the client should retry\r\n * ```\r\n */\r\nexport class ResiraRateLimitError extends ResiraApiError {\r\n /** Seconds to wait before retrying (from `Retry-After` header). */\r\n readonly retryAfter: number;\r\n\r\n constructor(body: ApiErrorBody, response: Response) {\r\n super(429, \"Too Many Requests\", body, response);\r\n this.name = \"ResiraRateLimitError\";\r\n this.retryAfter =\r\n body.retryAfter ??\r\n (Number.parseInt(response.headers.get(\"retry-after\") ?? \"60\", 10) ||\r\n 60);\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Network error (fetch failed, DNS, timeout, etc.)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Thrown when the request never reached the server.\r\n *\r\n * This wraps the underlying `TypeError` or `DOMException` from\r\n * `fetch()` failures (DNS resolution, TLS, network offline, etc.).\r\n */\r\nexport class ResiraNetworkError extends ResiraError {\r\n /** The original error thrown by `fetch`. */\r\n readonly cause: unknown;\r\n\r\n constructor(message: string, cause: unknown) {\r\n super(message);\r\n this.name = \"ResiraNetworkError\";\r\n this.cause = cause;\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n}\r\n","import type { ResiraConfig, WeightedBaseUrl } from \"./types.js\";\r\n\r\ninterface ResolvedBaseUrl {\r\n url: string;\r\n weight: number;\r\n}\r\n\r\nconst DEFAULT_LOCAL_BASE_URLS: ResolvedBaseUrl[] = [\r\n { url: \"http://localhost:3001\", weight: 100 },\r\n];\r\n\r\nconst DEFAULT_PRODUCTION_BASE_URLS: ResolvedBaseUrl[] = [\r\n { url: \"https://api.resira.app\", weight: 100 },\r\n];\r\n\r\nfunction readEnv(name: string): string | undefined {\r\n if (typeof process === \"undefined\") return undefined;\r\n return process.env?.[name];\r\n}\r\n\r\n\r\nfunction normalizeUrl(url: string): string {\r\n return url.trim().replace(/\\/+$/, \"\");\r\n}\r\n\r\nfunction isPositiveNumber(value: unknown): value is number {\r\n return typeof value === \"number\" && Number.isFinite(value) && value > 0;\r\n}\r\n\r\nfunction toResolvedBaseUrl(value: string | WeightedBaseUrl): ResolvedBaseUrl | null {\r\n if (typeof value === \"string\") {\r\n const url = normalizeUrl(value);\r\n return url ? { url, weight: 1 } : null;\r\n }\r\n\r\n const url = normalizeUrl(value.url);\r\n if (!url) return null;\r\n\r\n return {\r\n url,\r\n weight: isPositiveNumber(value.weight) ? value.weight : 1,\r\n };\r\n}\r\n\r\nfunction parseJsonBaseUrls(value: string): ResolvedBaseUrl[] {\r\n const parsed = JSON.parse(value) as unknown;\r\n if (!Array.isArray(parsed)) return [];\r\n\r\n return parsed.flatMap((entry) => {\r\n const resolved =\r\n typeof entry === \"string\"\r\n ? toResolvedBaseUrl(entry)\r\n : entry && typeof entry === \"object\"\r\n ? toResolvedBaseUrl(entry as WeightedBaseUrl)\r\n : null;\r\n\r\n return resolved ? [resolved] : [];\r\n });\r\n}\r\n\r\nfunction parseDelimitedBaseUrls(value: string): ResolvedBaseUrl[] {\r\n return value\r\n .split(\",\")\r\n .flatMap((entry) => {\r\n const [rawUrl, rawWeight] = entry.split(\"|\");\r\n const url = normalizeUrl(rawUrl ?? \"\");\r\n if (!url) return [];\r\n\r\n const parsedWeight = rawWeight ? Number(rawWeight.trim()) : 1;\r\n return [{\r\n url,\r\n weight: isPositiveNumber(parsedWeight) ? parsedWeight : 1,\r\n }];\r\n });\r\n}\r\n\r\nfunction parseEnvBaseUrls(value: string | undefined): ResolvedBaseUrl[] {\r\n if (!value) return [];\r\n\r\n try {\r\n const parsed = parseJsonBaseUrls(value);\r\n if (parsed.length > 0) return parsed;\r\n } catch {\r\n // Fall back to delimiter parsing.\r\n }\r\n\r\n return parseDelimitedBaseUrls(value);\r\n}\r\n\r\nexport function resolveBaseUrls(config: ResiraConfig): ResolvedBaseUrl[] {\r\n if (config.baseUrl) {\r\n return [{ url: normalizeUrl(config.baseUrl), weight: 100 }];\r\n }\r\n\r\n if (config.baseUrls?.length) {\r\n const explicit = config.baseUrls.flatMap((entry) => {\r\n const resolved = toResolvedBaseUrl(entry);\r\n return resolved ? [resolved] : [];\r\n });\r\n\r\n if (explicit.length > 0) return explicit;\r\n }\r\n\r\n const envBaseUrls = parseEnvBaseUrls(readEnv(\"RESIRA_API_BASE_URLS\"));\r\n if (envBaseUrls.length > 0) {\r\n return envBaseUrls;\r\n }\r\n\r\n if (readEnv(\"NODE_ENV\") === \"development\") {\r\n return DEFAULT_LOCAL_BASE_URLS;\r\n }\r\n\r\n return DEFAULT_PRODUCTION_BASE_URLS;\r\n}\r\n\r\nexport function pickBaseUrl(baseUrls: ResolvedBaseUrl[]): string {\r\n return baseUrls[0]?.url ?? DEFAULT_PRODUCTION_BASE_URLS[0].url;\r\n}\r\n\r\nexport function getRoutingStats(): Record<string, never> {\r\n return {};\r\n}","import { ResiraApiError, ResiraNetworkError, ResiraRateLimitError } from \"./errors.js\";\r\nimport { pickBaseUrl, resolveBaseUrls } from \"./routing.js\";\r\nimport type {\r\n ApiErrorBody,\r\n Availability,\r\n AvailabilityParams,\r\n ConfirmPaymentRequest,\r\n ConfirmPaymentResponse,\r\n CreatePaymentIntentRequest,\r\n CreateReservationRequest,\r\n Dish,\r\n DishListResponse,\r\n DishResponse,\r\n ListReservationsParams,\r\n PaginatedReservations,\r\n PaymentIntentResponse,\r\n ProductListResponse,\r\n PropertyConfig,\r\n Reservation,\r\n ReservationResponse,\r\n ResiraConfig,\r\n ResourceListResponse,\r\n ValidatePromoCodeResponse,\r\n} from \"./types.js\";\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Constants\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\nconst DEFAULT_MAX_RETRIES = 3;\r\nconst DEFAULT_RETRY_BASE_DELAY = 500; // ms\r\nconst API_PREFIX = \"/v1/public\";\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Helpers\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/** Generate a random UUID v4 for idempotency keys. */\r\nfunction uuid(): string {\r\n // Use crypto.randomUUID when available (Node 19+, all modern browsers)\r\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\r\n return globalThis.crypto.randomUUID();\r\n }\r\n // Fallback: manual v4 UUID\r\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\r\n });\r\n}\r\n\r\n/** Build a query string from a flat params object. Skips `undefined` values. */\r\nfunction toQueryString(params: Record<string, string | number | boolean | undefined>): string {\r\n const parts: string[] = [];\r\n for (const [key, value] of Object.entries(params)) {\r\n if (value !== undefined && value !== null) {\r\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);\r\n }\r\n }\r\n return parts.length > 0 ? `?${parts.join(\"&\")}` : \"\";\r\n}\r\n\r\n/** Sleep for `ms` milliseconds. */\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\n/**\r\n * Convert camelCase keys to snake_case for query parameters.\r\n * The API expects snake_case query params (`start_date`, `party_size`).\r\n */\r\nfunction camelToSnake(str: string): string {\r\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\r\n}\r\n\r\n/** Convert an object's keys from camelCase to snake_case. */\r\nfunction keysToSnake(\r\n obj: Record<string, string | number | boolean | undefined>,\r\n): Record<string, string | number | boolean | undefined> {\r\n const result: Record<string, string | number | boolean | undefined> = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n result[camelToSnake(key)] = value;\r\n }\r\n return result;\r\n}\r\n\r\n/** Convert snake_case string to camelCase. */\r\nfunction snakeToCamel(str: string): string {\r\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\r\n}\r\n\r\n/** Deep-convert all keys in an object/array from camelCase to snake_case. */\r\nfunction deepKeysToSnake(obj: unknown): unknown {\r\n if (Array.isArray(obj)) return obj.map(deepKeysToSnake);\r\n if (obj !== null && typeof obj === \"object\") {\r\n const result: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\r\n result[camelToSnake(key)] = deepKeysToSnake(value);\r\n }\r\n return result;\r\n }\r\n return obj;\r\n}\r\n\r\n/** Deep-convert all keys in an object/array from snake_case to camelCase. */\r\nfunction deepKeysToCamel(obj: unknown): unknown {\r\n if (Array.isArray(obj)) return obj.map(deepKeysToCamel);\r\n if (obj !== null && typeof obj === \"object\") {\r\n const result: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\r\n result[snakeToCamel(key)] = deepKeysToCamel(value);\r\n }\r\n return result;\r\n }\r\n return obj;\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Client\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Resira SDK client.\r\n *\r\n * ```ts\r\n * const resira = new Resira({\r\n * apiKey: \"resira_live_abc123…\",\r\n * baseUrl: \"https://api.example.com\", // optional\r\n * });\r\n *\r\n * const availability = await resira.getAvailability(\"prop-1\", {\r\n * startDate: \"2026-07-01\",\r\n * endDate: \"2026-07-07\",\r\n * });\r\n * ```\r\n */\r\nexport class Resira {\r\n private readonly apiKey: string;\r\n private readonly baseUrls: ReturnType<typeof resolveBaseUrls>;\r\n private readonly maxRetries: number;\r\n private readonly retryBaseDelay: number;\r\n private readonly _fetch: typeof globalThis.fetch;\r\n\r\n constructor(config: ResiraConfig) {\r\n if (!config.apiKey) {\r\n throw new Error(\"Resira: apiKey is required\");\r\n }\r\n this.apiKey = config.apiKey;\r\n this.baseUrls = resolveBaseUrls(config);\r\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\r\n this.retryBaseDelay = config.retryBaseDelay ?? DEFAULT_RETRY_BASE_DELAY;\r\n this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);\r\n }\r\n\r\n /** The resolved API origin used by this client instance. */\r\n getBaseUrl(): string {\r\n return pickBaseUrl(this.baseUrls);\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────────\r\n // Public API methods\r\n // ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Fetch the public property configuration.\r\n *\r\n * Returns non-sensitive settings such as the Stripe publishable\r\n * key, deposit percentage, currency, and branding info.\r\n *\r\n * ```ts\r\n * const config = await resira.getConfig();\r\n * console.log(config.stripePublishableKey); // \"pk_test_…\"\r\n * ```\r\n */\r\n async getConfig(): Promise<PropertyConfig> {\r\n return this.request<PropertyConfig>(\"GET\", \"/config\");\r\n }\r\n\r\n /**\r\n * Validate a promo/discount code.\r\n *\r\n * Returns discount details if valid, or an error message if not.\r\n *\r\n * ```ts\r\n * const result = await resira.validatePromoCode(\"SUMMER20\");\r\n * if (result.valid) {\r\n * console.log(result.discountType, result.discountValue);\r\n * }\r\n * ```\r\n */\r\n async validatePromoCode(code: string): Promise<ValidatePromoCodeResponse> {\r\n try {\r\n const raw = await this.request<Record<string, unknown>>(\r\n \"POST\",\r\n \"/promo-codes/validate\",\r\n { code },\r\n );\r\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\r\n return {\r\n valid: true,\r\n discountType: data.discountType as \"percent\" | \"fixed\" | undefined,\r\n discountValue: data.discountValue as number | undefined,\r\n currency: data.currency as string | undefined,\r\n minOrderCents: data.minOrderCents as number | undefined,\r\n };\r\n } catch (err: unknown) {\r\n // 4xx errors mean invalid code\r\n if (\r\n err &&\r\n typeof err === \"object\" &&\r\n \"status\" in err &&\r\n (err as { status: number }).status < 500\r\n ) {\r\n const apiErr = err as { body?: { error?: string } };\r\n return {\r\n valid: false,\r\n error: apiErr.body?.error ?? \"Invalid promo code\",\r\n };\r\n }\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * List all active resources for the organization.\r\n *\r\n * Returns resource cards with name, description, price, duration,\r\n * image, and type — suitable for building a resource picker.\r\n *\r\n * ```ts\r\n * const { resources } = await resira.listResources();\r\n * ```\r\n */\r\n async listResources(): Promise<ResourceListResponse> {\r\n return this.request<ResourceListResponse>(\"GET\", \"/resources\");\r\n }\r\n\r\n /**\r\n * Query availability for a resource.\r\n *\r\n * **Properties** (date-based):\r\n * ```ts\r\n * await resira.getAvailability(\"prop-1\", {\r\n * startDate: \"2026-07-01\",\r\n * endDate: \"2026-07-07\",\r\n * });\r\n * ```\r\n *\r\n * **Restaurants** (time-slot):\r\n * ```ts\r\n * await resira.getAvailability(\"table-5\", {\r\n * date: \"2026-07-01\",\r\n * partySize: 4,\r\n * });\r\n * ```\r\n *\r\n * Omit params to get all blocked dates (for calendar rendering).\r\n */\r\n async getAvailability(\r\n resourceId: string,\r\n params: AvailabilityParams = {},\r\n ): Promise<Availability> {\r\n if (!resourceId) {\r\n throw new Error(\"resourceId is required for getAvailability\");\r\n }\r\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\r\n return this.request<Availability>(\r\n \"GET\",\r\n `/resources/${encodeURIComponent(resourceId)}/availability${qs}`,\r\n );\r\n }\r\n\r\n /**\r\n * Query availability for a product/service.\r\n *\r\n * Aggregates capacity across all linked equipment. If a product\r\n * has 2 jet skis (each capacity=1), slots show capacity: 2.\r\n * Both pending and confirmed reservations count as occupied.\r\n *\r\n * ```ts\r\n * await resira.getProductAvailability(\"prod-123\", {\r\n * date: \"2026-07-01\",\r\n * durationMinutes: 30,\r\n * });\r\n * ```\r\n */\r\n async getProductAvailability(\r\n productId: string,\r\n params: AvailabilityParams = {},\r\n ): Promise<Availability> {\r\n if (!productId) {\r\n throw new Error(\"productId is required for getProductAvailability\");\r\n }\r\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\r\n return this.request<Availability>(\r\n \"GET\",\r\n `/products/${encodeURIComponent(productId)}/availability${qs}`,\r\n );\r\n }\r\n\r\n /**\r\n * Create a reservation.\r\n *\r\n * Uses `POST /v2/api/reservations` — the `resourceId` is sent in\r\n * the request body, **not** in the URL path.\r\n *\r\n * An `Idempotency-Key` header is automatically attached so that\r\n * retries (including those from exponential backoff) are safe.\r\n *\r\n * ```ts\r\n * const { reservation } = await resira.createReservation({\r\n * resourceId: \"prop-1\",\r\n * guestName: \"Jane Doe\",\r\n * guestEmail: \"jane@example.com\",\r\n * startDate: \"2026-07-01\",\r\n * endDate: \"2026-07-07\",\r\n * partySize: 3,\r\n * });\r\n * ```\r\n */\r\n async createReservation(\r\n payload: CreateReservationRequest,\r\n options?: { idempotencyKey?: string },\r\n ): Promise<ReservationResponse> {\r\n if (!payload.resourceId) {\r\n throw new Error(\"resourceId is required for createReservation\");\r\n }\r\n const idempotencyKey = options?.idempotencyKey ?? uuid();\r\n\r\n // Note: uses /v2/api prefix, not /v1/public\r\n const url = `${this.getBaseUrl()}/v2/api/reservations`;\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n Accept: \"application/json\",\r\n \"Content-Type\": \"application/json\",\r\n \"Idempotency-Key\": idempotencyKey,\r\n };\r\n\r\n const init: RequestInit = {\r\n method: \"POST\",\r\n headers,\r\n body: JSON.stringify(payload),\r\n };\r\n\r\n let lastError: ResiraApiError | ResiraNetworkError | ResiraRateLimitError | undefined;\r\n\r\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\r\n if (attempt > 0) {\r\n const backoff = this.calculateBackoff(attempt, lastError);\r\n await sleep(backoff);\r\n }\r\n\r\n let response: Response;\r\n try {\r\n response = await this._fetch(url, init);\r\n } catch (err) {\r\n lastError = new ResiraNetworkError(\r\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\r\n err,\r\n );\r\n continue;\r\n }\r\n\r\n if (response.ok) {\r\n const raw = (await response.json()) as Record<string, unknown>;\r\n // The API may nest under \"reservation\" or \"booking\" depending on endpoint\r\n const resData = (raw.reservation ?? raw.booking ?? {}) as Record<string, unknown>;\r\n\r\n // Augment with request data: API response doesn't include\r\n // startTime/endTime/startDate/endDate/resourceId for v2 reservations\r\n const reservation: Reservation = {\r\n id: (resData.id as string) ?? \"\",\r\n resourceId: (resData.resourceId as string) ?? payload.resourceId,\r\n status: (resData.status as string) ?? \"pending\",\r\n startDate: (resData.startDate as string) ?? payload.startDate,\r\n endDate: (resData.endDate as string) ?? payload.endDate,\r\n startTime: (resData.startTime as string) ?? payload.startTime,\r\n endTime: (resData.endTime as string) ?? payload.endTime,\r\n startDatetime: (resData.startDatetime as string) ?? payload.startTime,\r\n endDatetime: (resData.endDatetime as string) ?? payload.endTime,\r\n guestName: (resData.guestName as string) ?? payload.guestName,\r\n partySize: (resData.partySize as number) ?? (resData.guests as number) ?? payload.partySize ?? 2,\r\n totalPrice: (resData.totalPrice as number) ?? 0,\r\n currency: (resData.currency as string) ?? \"EUR\",\r\n createdAt: (resData.createdAt as string) ?? new Date().toISOString(),\r\n };\r\n\r\n return { reservation };\r\n }\r\n\r\n let errorBody: ApiErrorBody;\r\n try {\r\n errorBody = (await response.json()) as ApiErrorBody;\r\n } catch {\r\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\r\n }\r\n\r\n if (response.status === 429) {\r\n lastError = new ResiraRateLimitError(errorBody, response);\r\n continue;\r\n }\r\n\r\n if (response.status >= 500) {\r\n lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n continue;\r\n }\r\n\r\n throw new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n }\r\n\r\n throw lastError!;\r\n }\r\n\r\n /**\r\n * List reservations for a resource (paginated).\r\n *\r\n * ```ts\r\n * const page = await resira.listReservations(\"prop-1\", {\r\n * status: \"confirmed\",\r\n * page: 1,\r\n * limit: 50,\r\n * });\r\n * console.log(page.data); // Reservation[]\r\n * console.log(page.totalPages); // number\r\n * ```\r\n */\r\n async listReservations(\r\n resourceId: string,\r\n params: ListReservationsParams = {},\r\n ): Promise<PaginatedReservations> {\r\n const qs = toQueryString(params as Record<string, string | number | undefined>);\r\n return this.request<PaginatedReservations>(\r\n \"GET\",\r\n `/resources/${encodeURIComponent(resourceId)}/reservations${qs}`,\r\n );\r\n }\r\n\r\n /**\r\n * Get a single reservation by ID.\r\n *\r\n * ```ts\r\n * const { reservation } = await resira.getReservation(\"prop-1\", \"res-uuid\");\r\n * ```\r\n */\r\n async getReservation(\r\n resourceId: string,\r\n reservationId: string,\r\n ): Promise<ReservationResponse> {\r\n return this.request<ReservationResponse>(\r\n \"GET\",\r\n `/resources/${encodeURIComponent(resourceId)}/reservations/${encodeURIComponent(reservationId)}`,\r\n );\r\n }\r\n\r\n /**\r\n * List all active products/services for the organisation.\r\n *\r\n * Calls `GET /v1/public/products` which returns products with\r\n * pricing, duration, and linked equipment.\r\n *\r\n * Falls back to mapping resources → products if the endpoint\r\n * returns 404 (backend hasn't been updated yet).\r\n *\r\n * ```ts\r\n * const { products } = await resira.listProducts();\r\n * ```\r\n */\r\n async listProducts(): Promise<ProductListResponse> {\r\n try {\r\n return await this.request<ProductListResponse>(\"GET\", \"/products\");\r\n } catch (err: unknown) {\r\n // Fallback: if /products endpoint doesn't exist yet (404),\r\n // map resources → products shape\r\n if (\r\n err &&\r\n typeof err === \"object\" &&\r\n \"status\" in err &&\r\n (err as { status: number }).status === 404\r\n ) {\r\n const resourceResponse = await this.listResources();\r\n const products = resourceResponse.resources.map((r) => ({\r\n id: r.id,\r\n name: r.name,\r\n description: r.description,\r\n durationMinutes: r.durationMinutes ?? 60,\r\n priceCents: r.pricePerSession ?? 0,\r\n currency: r.currency ?? \"EUR\",\r\n imageUrl: r.imageUrl,\r\n active: r.active,\r\n sortOrder: 0,\r\n pricingModel: \"per_session\" as const,\r\n equipmentIds: [r.id],\r\n equipmentNames: [r.name],\r\n }));\r\n return { products, count: products.length };\r\n }\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * List all dishes with 3D model data for the organisation.\r\n *\r\n * Returns dishes with name, description, price, image, and\r\n * 3D model URLs for AR/preview rendering.\r\n *\r\n * ```ts\r\n * const { dishes } = await resira.listDishes();\r\n * ```\r\n */\r\n async listDishes(): Promise<DishListResponse> {\r\n return this.request<DishListResponse>(\"GET\", \"/dishes\");\r\n }\r\n\r\n /**\r\n * Get a single dish by ID.\r\n *\r\n * ```ts\r\n * const { dish } = await resira.getDish(\"dish-uuid\");\r\n * console.log(dish.name, dish.model?.glbUrl);\r\n * ```\r\n */\r\n async getDish(dishId: string): Promise<DishResponse> {\r\n if (!dishId) {\r\n throw new Error(\"dishId is required for getDish\");\r\n }\r\n return this.request<DishResponse>(\r\n \"GET\",\r\n `/dishes/${encodeURIComponent(dishId)}`,\r\n );\r\n }\r\n\r\n /**\r\n * Create a Stripe payment intent for a booking.\r\n *\r\n * The server creates the payment intent, calculates the amount,\r\n * and returns a `clientSecret` to confirm payment on the frontend\r\n * using Stripe Elements.\r\n *\r\n * ```ts\r\n * const { clientSecret, amountNow, amountAtVenue } =\r\n * await resira.createPaymentIntent({\r\n * productId: \"prod-123\",\r\n * resourceId: \"res-456\",\r\n * partySize: 2,\r\n * startDate: \"2026-07-01\",\r\n * startTime: \"2026-07-01T10:00:00Z\",\r\n * endTime: \"2026-07-01T11:00:00Z\",\r\n * guestName: \"Jane Doe\",\r\n * guestEmail: \"jane@example.com\",\r\n * });\r\n * ```\r\n */\r\n async createPaymentIntent(\r\n payload: CreatePaymentIntentRequest,\r\n options?: { idempotencyKey?: string },\r\n ): Promise<PaymentIntentResponse> {\r\n const idempotencyKey = options?.idempotencyKey ?? uuid();\r\n const raw = await this.request<Record<string, unknown>>(\r\n \"POST\",\r\n \"/payments/create-intent\",\r\n payload,\r\n { \"Idempotency-Key\": idempotencyKey },\r\n );\r\n // Normalize response keys to camelCase\r\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\r\n // Map backend field names to SDK field names:\r\n // backend: amountDue, totalPrice\r\n // SDK: amountNow, totalAmount, amountAtVenue\r\n const totalAmount = (data.totalAmount as number) ?? (data.totalPrice as number) ?? 0;\r\n const amountNow = (data.amountNow as number) ?? (data.amountDue as number) ?? totalAmount;\r\n const amountAtVenue = (data.amountAtVenue as number) ?? (totalAmount - amountNow);\r\n return {\r\n clientSecret: data.clientSecret as string,\r\n paymentIntentId: data.paymentIntentId as string,\r\n amountNow,\r\n amountAtVenue,\r\n totalAmount,\r\n currency: (data.currency as string) ?? \"EUR\",\r\n paymentOption: data.paymentOption as string | undefined,\r\n reservationId: data.reservationId as string | undefined,\r\n discountAmount: (data.discountAmount as number) ?? undefined,\r\n originalPrice: (data.originalPrice as number) ?? undefined,\r\n promoCodeApplied: (data.promoCodeApplied as string) ?? undefined,\r\n };\r\n }\r\n\r\n /**\r\n * Confirm that a payment was successful after the guest\r\n * completes the Stripe payment flow.\r\n *\r\n * This transitions the reservation from `pending` to `confirmed`.\r\n *\r\n * ```ts\r\n * const { reservation, paymentStatus } =\r\n * await resira.confirmPayment({\r\n * paymentIntentId: \"pi_xxx\",\r\n * reservationId: \"res-uuid\",\r\n * });\r\n * ```\r\n */\r\n async confirmPayment(\r\n payload: ConfirmPaymentRequest,\r\n ): Promise<ConfirmPaymentResponse> {\r\n const raw = await this.request<Record<string, unknown>>(\r\n \"POST\",\r\n \"/payments/confirm\",\r\n payload,\r\n );\r\n // Normalize response keys to camelCase in case backend returns snake_case\r\n return deepKeysToCamel(raw) as ConfirmPaymentResponse;\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────────\r\n // Internal HTTP layer\r\n // ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Execute an HTTP request with retry logic and error normalization.\r\n *\r\n * - Attaches `Authorization: Bearer <apiKey>` on every request.\r\n * - Retries on 429 and 5xx with exponential backoff + jitter.\r\n * - Parses error bodies and throws typed error classes.\r\n */\r\n private async request<T>(\r\n method: string,\r\n path: string,\r\n body?: unknown,\r\n extraHeaders?: Record<string, string>,\r\n ): Promise<T> {\r\n const url = `${this.getBaseUrl()}${API_PREFIX}${path}`;\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n Accept: \"application/json\",\r\n ...extraHeaders,\r\n };\r\n\r\n if (body !== undefined) {\r\n headers[\"Content-Type\"] = \"application/json\";\r\n }\r\n\r\n const init: RequestInit = {\r\n method,\r\n headers,\r\n body: body !== undefined ? JSON.stringify(body) : undefined,\r\n };\r\n\r\n let lastError: ResiraApiError | ResiraNetworkError | undefined;\r\n\r\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\r\n // ── Wait before retry ────────────────────────────────\r\n if (attempt > 0) {\r\n const backoff = this.calculateBackoff(attempt, lastError);\r\n await sleep(backoff);\r\n }\r\n\r\n // ── Execute the request ──────────────────────────────\r\n let response: Response;\r\n try {\r\n response = await this._fetch(url, init);\r\n } catch (err) {\r\n lastError = new ResiraNetworkError(\r\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\r\n err,\r\n );\r\n // Network errors are always retryable\r\n continue;\r\n }\r\n\r\n // ── Success ──────────────────────────────────────────\r\n if (response.ok) {\r\n return (await response.json()) as T;\r\n }\r\n\r\n // ── Parse error body ─────────────────────────────────\r\n let errorBody: ApiErrorBody;\r\n try {\r\n errorBody = (await response.json()) as ApiErrorBody;\r\n } catch {\r\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\r\n }\r\n\r\n // ── Rate limit ───────────────────────────────────────\r\n if (response.status === 429) {\r\n lastError = new ResiraRateLimitError(errorBody, response);\r\n // Always retry 429s (up to maxRetries)\r\n continue;\r\n }\r\n\r\n // ── Server error (5xx) — retryable ───────────────────\r\n if (response.status >= 500) {\r\n lastError = new ResiraApiError(\r\n response.status,\r\n response.statusText,\r\n errorBody,\r\n response,\r\n );\r\n continue;\r\n }\r\n\r\n // ── Client error (4xx) — NOT retryable ───────────────\r\n throw new ResiraApiError(\r\n response.status,\r\n response.statusText,\r\n errorBody,\r\n response,\r\n );\r\n }\r\n\r\n // All retries exhausted — throw the last error\r\n throw lastError!;\r\n }\r\n\r\n /**\r\n * Calculate backoff delay in milliseconds for a given retry attempt.\r\n *\r\n * Uses exponential backoff with full jitter:\r\n * delay = random(0, base * 2^attempt)\r\n *\r\n * For 429 responses, respects the `Retry-After` header as a minimum.\r\n */\r\n private calculateBackoff(\r\n attempt: number,\r\n lastError?: ResiraApiError | ResiraNetworkError,\r\n ): number {\r\n const exponential = this.retryBaseDelay * 2 ** (attempt - 1);\r\n const jittered = Math.random() * exponential;\r\n\r\n // If the server told us to wait, use that as the floor\r\n if (lastError instanceof ResiraRateLimitError) {\r\n const serverDelay = lastError.retryAfter * 1000;\r\n return Math.max(serverDelay, jittered);\r\n }\r\n\r\n return jittered;\r\n }\r\n\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/routing.ts","../src/client.ts"],"names":["url"],"mappings":"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","file":"index.js","sourcesContent":["import type { ApiErrorBody } from \"./types.js\";\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Base error\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Base error class for all SDK errors.\r\n *\r\n * Use `instanceof` checks to distinguish error types:\r\n *\r\n * ```ts\r\n * try { … } catch (e) {\r\n * if (e instanceof ResiraRateLimitError) { … }\r\n * if (e instanceof ResiraApiError) { … }\r\n * if (e instanceof ResiraNetworkError) { … }\r\n * }\r\n * ```\r\n */\r\nexport class ResiraError extends Error {\r\n constructor(message: string) {\r\n super(message);\r\n this.name = \"ResiraError\";\r\n // Fix prototype chain for instanceof to work after transpilation\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// API error (4xx / 5xx with a parsed body)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Thrown when the API returns a non-2xx response with a JSON body.\r\n *\r\n * ```ts\r\n * err.status // 400, 404, 409, 422, 500, …\r\n * err.code // HTTP status text (e.g. \"Not Found\")\r\n * err.body // Parsed `{ error: \"…\" }` from the server\r\n * ```\r\n */\r\nexport class ResiraApiError extends ResiraError {\r\n /** HTTP status code. */\r\n readonly status: number;\r\n /** HTTP status text (e.g. `\"Bad Request\"`). */\r\n readonly code: string;\r\n /** Parsed error body from the API. */\r\n readonly body: ApiErrorBody;\r\n /** The full `Response` object (headers are still accessible). */\r\n readonly response: Response;\r\n\r\n constructor(status: number, code: string, body: ApiErrorBody, response: Response) {\r\n super(body.error || `API error ${status}`);\r\n this.name = \"ResiraApiError\";\r\n this.status = status;\r\n this.code = code;\r\n this.body = body;\r\n this.response = response;\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n\r\n /** Whether this error is retryable (5xx or 429). */\r\n get retryable(): boolean {\r\n return this.status === 429 || this.status >= 500;\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Rate limit error (429)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Thrown when the API returns 429 Too Many Requests.\r\n *\r\n * ```ts\r\n * err.retryAfter // Seconds until the client should retry\r\n * ```\r\n */\r\nexport class ResiraRateLimitError extends ResiraApiError {\r\n /** Seconds to wait before retrying (from `Retry-After` header). */\r\n readonly retryAfter: number;\r\n\r\n constructor(body: ApiErrorBody, response: Response) {\r\n super(429, \"Too Many Requests\", body, response);\r\n this.name = \"ResiraRateLimitError\";\r\n this.retryAfter =\r\n body.retryAfter ??\r\n (Number.parseInt(response.headers.get(\"retry-after\") ?? \"60\", 10) ||\r\n 60);\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Network error (fetch failed, DNS, timeout, etc.)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Thrown when the request never reached the server.\r\n *\r\n * This wraps the underlying `TypeError` or `DOMException` from\r\n * `fetch()` failures (DNS resolution, TLS, network offline, etc.).\r\n */\r\nexport class ResiraNetworkError extends ResiraError {\r\n /** The original error thrown by `fetch`. */\r\n readonly cause: unknown;\r\n\r\n constructor(message: string, cause: unknown) {\r\n super(message);\r\n this.name = \"ResiraNetworkError\";\r\n this.cause = cause;\r\n Object.setPrototypeOf(this, new.target.prototype);\r\n }\r\n}\r\n","import type { ResiraConfig, WeightedBaseUrl } from \"./types.js\";\r\n\r\ninterface ResolvedBaseUrl {\r\n url: string;\r\n weight: number;\r\n}\r\n\r\nconst DEFAULT_LOCAL_BASE_URLS: ResolvedBaseUrl[] = [\r\n { url: \"http://localhost:3001\", weight: 100 },\r\n];\r\n\r\nconst DEFAULT_PRODUCTION_BASE_URLS: ResolvedBaseUrl[] = [\r\n { url: \"https://api.resira.app\", weight: 100 },\r\n];\r\n\r\nfunction readEnv(name: string): string | undefined {\r\n if (typeof process === \"undefined\") return undefined;\r\n return process.env?.[name];\r\n}\r\n\r\n\r\nfunction normalizeUrl(url: string): string {\r\n return url.trim().replace(/\\/+$/, \"\");\r\n}\r\n\r\nfunction isPositiveNumber(value: unknown): value is number {\r\n return typeof value === \"number\" && Number.isFinite(value) && value > 0;\r\n}\r\n\r\nfunction toResolvedBaseUrl(value: string | WeightedBaseUrl): ResolvedBaseUrl | null {\r\n if (typeof value === \"string\") {\r\n const url = normalizeUrl(value);\r\n return url ? { url, weight: 1 } : null;\r\n }\r\n\r\n const url = normalizeUrl(value.url);\r\n if (!url) return null;\r\n\r\n return {\r\n url,\r\n weight: isPositiveNumber(value.weight) ? value.weight : 1,\r\n };\r\n}\r\n\r\nfunction parseJsonBaseUrls(value: string): ResolvedBaseUrl[] {\r\n const parsed = JSON.parse(value) as unknown;\r\n if (!Array.isArray(parsed)) return [];\r\n\r\n return parsed.flatMap((entry) => {\r\n const resolved =\r\n typeof entry === \"string\"\r\n ? toResolvedBaseUrl(entry)\r\n : entry && typeof entry === \"object\"\r\n ? toResolvedBaseUrl(entry as WeightedBaseUrl)\r\n : null;\r\n\r\n return resolved ? [resolved] : [];\r\n });\r\n}\r\n\r\nfunction parseDelimitedBaseUrls(value: string): ResolvedBaseUrl[] {\r\n return value\r\n .split(\",\")\r\n .flatMap((entry) => {\r\n const [rawUrl, rawWeight] = entry.split(\"|\");\r\n const url = normalizeUrl(rawUrl ?? \"\");\r\n if (!url) return [];\r\n\r\n const parsedWeight = rawWeight ? Number(rawWeight.trim()) : 1;\r\n return [{\r\n url,\r\n weight: isPositiveNumber(parsedWeight) ? parsedWeight : 1,\r\n }];\r\n });\r\n}\r\n\r\nfunction parseEnvBaseUrls(value: string | undefined): ResolvedBaseUrl[] {\r\n if (!value) return [];\r\n\r\n try {\r\n const parsed = parseJsonBaseUrls(value);\r\n if (parsed.length > 0) return parsed;\r\n } catch {\r\n // Fall back to delimiter parsing.\r\n }\r\n\r\n return parseDelimitedBaseUrls(value);\r\n}\r\n\r\nexport function resolveBaseUrls(config: ResiraConfig): ResolvedBaseUrl[] {\r\n if (config.baseUrl) {\r\n return [{ url: normalizeUrl(config.baseUrl), weight: 100 }];\r\n }\r\n\r\n if (config.baseUrls?.length) {\r\n const explicit = config.baseUrls.flatMap((entry) => {\r\n const resolved = toResolvedBaseUrl(entry);\r\n return resolved ? [resolved] : [];\r\n });\r\n\r\n if (explicit.length > 0) return explicit;\r\n }\r\n\r\n const envBaseUrls = parseEnvBaseUrls(readEnv(\"RESIRA_API_BASE_URLS\"));\r\n if (envBaseUrls.length > 0) {\r\n return envBaseUrls;\r\n }\r\n\r\n if (readEnv(\"NODE_ENV\") === \"development\") {\r\n return DEFAULT_LOCAL_BASE_URLS;\r\n }\r\n\r\n return DEFAULT_PRODUCTION_BASE_URLS;\r\n}\r\n\r\nexport function pickBaseUrl(baseUrls: ResolvedBaseUrl[]): string {\r\n return baseUrls[0]?.url ?? DEFAULT_PRODUCTION_BASE_URLS[0].url;\r\n}\r\n\r\nexport function getRoutingStats(): Record<string, never> {\r\n return {};\r\n}","import { ResiraApiError, ResiraNetworkError, ResiraRateLimitError } from \"./errors.js\";\r\nimport { pickBaseUrl, resolveBaseUrls } from \"./routing.js\";\r\nimport type {\r\n ApiErrorBody,\r\n Availability,\r\n AvailabilityParams,\r\n CheckoutSessionResponse,\r\n ConfirmPaymentRequest,\r\n ConfirmPaymentResponse,\r\n CreateCheckoutSessionRequest,\r\n CreateCheckoutSessionResponse,\r\n CreatePaymentIntentRequest,\r\n CreateReservationRequest,\r\n Dish,\r\n DishListResponse,\r\n DishResponse,\r\n ListReservationsParams,\r\n PaginatedReservations,\r\n PaymentIntentResponse,\r\n ProductListResponse,\r\n PropertyConfig,\r\n Reservation,\r\n ReservationResponse,\r\n ResiraConfig,\r\n ResourceListResponse,\r\n ValidatePromoCodeResponse,\r\n} from \"./types.js\";\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Constants\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\nconst DEFAULT_MAX_RETRIES = 3;\r\nconst DEFAULT_RETRY_BASE_DELAY = 500; // ms\r\nconst API_PREFIX = \"/v1/public\";\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Helpers\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/** Generate a random UUID v4 for idempotency keys. */\r\nfunction uuid(): string {\r\n // Use crypto.randomUUID when available (Node 19+, all modern browsers)\r\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\r\n return globalThis.crypto.randomUUID();\r\n }\r\n // Fallback: manual v4 UUID\r\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\r\n });\r\n}\r\n\r\n/** Build a query string from a flat params object. Skips `undefined` values. */\r\nfunction toQueryString(params: Record<string, string | number | boolean | undefined>): string {\r\n const parts: string[] = [];\r\n for (const [key, value] of Object.entries(params)) {\r\n if (value !== undefined && value !== null) {\r\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);\r\n }\r\n }\r\n return parts.length > 0 ? `?${parts.join(\"&\")}` : \"\";\r\n}\r\n\r\n/** Sleep for `ms` milliseconds. */\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\n/**\r\n * Convert camelCase keys to snake_case for query parameters.\r\n * The API expects snake_case query params (`start_date`, `party_size`).\r\n */\r\nfunction camelToSnake(str: string): string {\r\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\r\n}\r\n\r\n/** Convert an object's keys from camelCase to snake_case. */\r\nfunction keysToSnake(\r\n obj: Record<string, string | number | boolean | undefined>,\r\n): Record<string, string | number | boolean | undefined> {\r\n const result: Record<string, string | number | boolean | undefined> = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n result[camelToSnake(key)] = value;\r\n }\r\n return result;\r\n}\r\n\r\n/** Convert snake_case string to camelCase. */\r\nfunction snakeToCamel(str: string): string {\r\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\r\n}\r\n\r\n/** Deep-convert all keys in an object/array from camelCase to snake_case. */\r\nfunction deepKeysToSnake(obj: unknown): unknown {\r\n if (Array.isArray(obj)) return obj.map(deepKeysToSnake);\r\n if (obj !== null && typeof obj === \"object\") {\r\n const result: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\r\n result[camelToSnake(key)] = deepKeysToSnake(value);\r\n }\r\n return result;\r\n }\r\n return obj;\r\n}\r\n\r\n/** Deep-convert all keys in an object/array from snake_case to camelCase. */\r\nfunction deepKeysToCamel(obj: unknown): unknown {\r\n if (Array.isArray(obj)) return obj.map(deepKeysToCamel);\r\n if (obj !== null && typeof obj === \"object\") {\r\n const result: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\r\n result[snakeToCamel(key)] = deepKeysToCamel(value);\r\n }\r\n return result;\r\n }\r\n return obj;\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Client\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Resira SDK client.\r\n *\r\n * ```ts\r\n * const resira = new Resira({\r\n * apiKey: \"resira_live_abc123…\",\r\n * baseUrl: \"https://api.example.com\", // optional\r\n * });\r\n *\r\n * const availability = await resira.getAvailability(\"prop-1\", {\r\n * startDate: \"2026-07-01\",\r\n * endDate: \"2026-07-07\",\r\n * });\r\n * ```\r\n */\r\nexport class Resira {\r\n private readonly apiKey: string;\r\n private readonly baseUrls: ReturnType<typeof resolveBaseUrls>;\r\n private readonly maxRetries: number;\r\n private readonly retryBaseDelay: number;\r\n private readonly _fetch: typeof globalThis.fetch;\r\n\r\n constructor(config: ResiraConfig) {\r\n if (!config.apiKey) {\r\n throw new Error(\"Resira: apiKey is required\");\r\n }\r\n this.apiKey = config.apiKey;\r\n this.baseUrls = resolveBaseUrls(config);\r\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\r\n this.retryBaseDelay = config.retryBaseDelay ?? DEFAULT_RETRY_BASE_DELAY;\r\n this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);\r\n }\r\n\r\n /** The resolved API origin used by this client instance. */\r\n getBaseUrl(): string {\r\n return pickBaseUrl(this.baseUrls);\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────────\r\n // Public API methods\r\n // ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Fetch the public property configuration.\r\n *\r\n * Returns non-sensitive settings such as the Stripe publishable\r\n * key, deposit percentage, currency, and branding info.\r\n *\r\n * ```ts\r\n * const config = await resira.getConfig();\r\n * console.log(config.stripePublishableKey); // \"pk_test_…\"\r\n * ```\r\n */\r\n async getConfig(): Promise<PropertyConfig> {\r\n return this.request<PropertyConfig>(\"GET\", \"/config\");\r\n }\r\n\r\n /**\r\n * Validate a promo/discount code.\r\n *\r\n * Returns discount details if valid, or an error message if not.\r\n *\r\n * ```ts\r\n * const result = await resira.validatePromoCode(\"SUMMER20\");\r\n * if (result.valid) {\r\n * console.log(result.discountType, result.discountValue);\r\n * }\r\n * ```\r\n */\r\n async validatePromoCode(code: string): Promise<ValidatePromoCodeResponse> {\r\n try {\r\n const raw = await this.request<Record<string, unknown>>(\r\n \"POST\",\r\n \"/promo-codes/validate\",\r\n { code },\r\n );\r\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\r\n return {\r\n valid: true,\r\n discountType: data.discountType as \"percent\" | \"fixed\" | undefined,\r\n discountValue: data.discountValue as number | undefined,\r\n currency: data.currency as string | undefined,\r\n minOrderCents: data.minOrderCents as number | undefined,\r\n };\r\n } catch (err: unknown) {\r\n // 4xx errors mean invalid code\r\n if (\r\n err &&\r\n typeof err === \"object\" &&\r\n \"status\" in err &&\r\n (err as { status: number }).status < 500\r\n ) {\r\n const apiErr = err as { body?: { error?: string } };\r\n return {\r\n valid: false,\r\n error: apiErr.body?.error ?? \"Invalid promo code\",\r\n };\r\n }\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * List all active resources for the organization.\r\n *\r\n * Returns resource cards with name, description, price, duration,\r\n * image, and type — suitable for building a resource picker.\r\n *\r\n * ```ts\r\n * const { resources } = await resira.listResources();\r\n * ```\r\n */\r\n async listResources(): Promise<ResourceListResponse> {\r\n return this.request<ResourceListResponse>(\"GET\", \"/resources\");\r\n }\r\n\r\n /**\r\n * Query availability for a resource.\r\n *\r\n * **Properties** (date-based):\r\n * ```ts\r\n * await resira.getAvailability(\"prop-1\", {\r\n * startDate: \"2026-07-01\",\r\n * endDate: \"2026-07-07\",\r\n * });\r\n * ```\r\n *\r\n * **Restaurants** (time-slot):\r\n * ```ts\r\n * await resira.getAvailability(\"table-5\", {\r\n * date: \"2026-07-01\",\r\n * partySize: 4,\r\n * });\r\n * ```\r\n *\r\n * Omit params to get all blocked dates (for calendar rendering).\r\n */\r\n async getAvailability(\r\n resourceId: string,\r\n params: AvailabilityParams = {},\r\n ): Promise<Availability> {\r\n if (!resourceId) {\r\n throw new Error(\"resourceId is required for getAvailability\");\r\n }\r\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\r\n return this.request<Availability>(\r\n \"GET\",\r\n `/resources/${encodeURIComponent(resourceId)}/availability${qs}`,\r\n );\r\n }\r\n\r\n /**\r\n * Query availability for a product/service.\r\n *\r\n * Aggregates capacity across all linked equipment. If a product\r\n * has 2 jet skis (each capacity=1), slots show capacity: 2.\r\n * Both pending and confirmed reservations count as occupied.\r\n *\r\n * ```ts\r\n * await resira.getProductAvailability(\"prod-123\", {\r\n * date: \"2026-07-01\",\r\n * durationMinutes: 30,\r\n * });\r\n * ```\r\n */\r\n async getProductAvailability(\r\n productId: string,\r\n params: AvailabilityParams = {},\r\n ): Promise<Availability> {\r\n if (!productId) {\r\n throw new Error(\"productId is required for getProductAvailability\");\r\n }\r\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\r\n return this.request<Availability>(\r\n \"GET\",\r\n `/products/${encodeURIComponent(productId)}/availability${qs}`,\r\n );\r\n }\r\n\r\n /**\r\n * Create a reservation.\r\n *\r\n * Uses `POST /v2/api/reservations` — the `resourceId` is sent in\r\n * the request body, **not** in the URL path.\r\n *\r\n * An `Idempotency-Key` header is automatically attached so that\r\n * retries (including those from exponential backoff) are safe.\r\n *\r\n * ```ts\r\n * const { reservation } = await resira.createReservation({\r\n * resourceId: \"prop-1\",\r\n * guestName: \"Jane Doe\",\r\n * guestEmail: \"jane@example.com\",\r\n * startDate: \"2026-07-01\",\r\n * endDate: \"2026-07-07\",\r\n * partySize: 3,\r\n * });\r\n * ```\r\n */\r\n async createReservation(\r\n payload: CreateReservationRequest,\r\n options?: { idempotencyKey?: string },\r\n ): Promise<ReservationResponse> {\r\n if (!payload.resourceId) {\r\n throw new Error(\"resourceId is required for createReservation\");\r\n }\r\n const idempotencyKey = options?.idempotencyKey ?? uuid();\r\n\r\n // Note: uses /v2/api prefix, not /v1/public\r\n const url = `${this.getBaseUrl()}/v2/api/reservations`;\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n Accept: \"application/json\",\r\n \"Content-Type\": \"application/json\",\r\n \"Idempotency-Key\": idempotencyKey,\r\n };\r\n\r\n const init: RequestInit = {\r\n method: \"POST\",\r\n headers,\r\n body: JSON.stringify(payload),\r\n };\r\n\r\n let lastError: ResiraApiError | ResiraNetworkError | ResiraRateLimitError | undefined;\r\n\r\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\r\n if (attempt > 0) {\r\n const backoff = this.calculateBackoff(attempt, lastError);\r\n await sleep(backoff);\r\n }\r\n\r\n let response: Response;\r\n try {\r\n response = await this._fetch(url, init);\r\n } catch (err) {\r\n lastError = new ResiraNetworkError(\r\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\r\n err,\r\n );\r\n continue;\r\n }\r\n\r\n if (response.ok) {\r\n const raw = (await response.json()) as Record<string, unknown>;\r\n // The API may nest under \"reservation\" or \"booking\" depending on endpoint\r\n const resData = (raw.reservation ?? raw.booking ?? {}) as Record<string, unknown>;\r\n\r\n // Augment with request data: API response doesn't include\r\n // startTime/endTime/startDate/endDate/resourceId for v2 reservations\r\n const reservation: Reservation = {\r\n id: (resData.id as string) ?? \"\",\r\n resourceId: (resData.resourceId as string) ?? payload.resourceId,\r\n status: (resData.status as string) ?? \"pending\",\r\n startDate: (resData.startDate as string) ?? payload.startDate,\r\n endDate: (resData.endDate as string) ?? payload.endDate,\r\n startTime: (resData.startTime as string) ?? payload.startTime,\r\n endTime: (resData.endTime as string) ?? payload.endTime,\r\n startDatetime: (resData.startDatetime as string) ?? payload.startTime,\r\n endDatetime: (resData.endDatetime as string) ?? payload.endTime,\r\n guestName: (resData.guestName as string) ?? payload.guestName,\r\n partySize: (resData.partySize as number) ?? (resData.guests as number) ?? payload.partySize ?? 2,\r\n totalPrice: (resData.totalPrice as number) ?? 0,\r\n currency: (resData.currency as string) ?? \"EUR\",\r\n createdAt: (resData.createdAt as string) ?? new Date().toISOString(),\r\n };\r\n\r\n return { reservation };\r\n }\r\n\r\n let errorBody: ApiErrorBody;\r\n try {\r\n errorBody = (await response.json()) as ApiErrorBody;\r\n } catch {\r\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\r\n }\r\n\r\n if (response.status === 429) {\r\n lastError = new ResiraRateLimitError(errorBody, response);\r\n continue;\r\n }\r\n\r\n if (response.status >= 500) {\r\n lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n continue;\r\n }\r\n\r\n throw new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n }\r\n\r\n throw lastError!;\r\n }\r\n\r\n /**\r\n * List reservations for a resource (paginated).\r\n *\r\n * ```ts\r\n * const page = await resira.listReservations(\"prop-1\", {\r\n * status: \"confirmed\",\r\n * page: 1,\r\n * limit: 50,\r\n * });\r\n * console.log(page.data); // Reservation[]\r\n * console.log(page.totalPages); // number\r\n * ```\r\n */\r\n async listReservations(\r\n resourceId: string,\r\n params: ListReservationsParams = {},\r\n ): Promise<PaginatedReservations> {\r\n const qs = toQueryString(params as Record<string, string | number | undefined>);\r\n return this.request<PaginatedReservations>(\r\n \"GET\",\r\n `/resources/${encodeURIComponent(resourceId)}/reservations${qs}`,\r\n );\r\n }\r\n\r\n /**\r\n * Get a single reservation by ID.\r\n *\r\n * ```ts\r\n * const { reservation } = await resira.getReservation(\"prop-1\", \"res-uuid\");\r\n * ```\r\n */\r\n async getReservation(\r\n resourceId: string,\r\n reservationId: string,\r\n ): Promise<ReservationResponse> {\r\n return this.request<ReservationResponse>(\r\n \"GET\",\r\n `/resources/${encodeURIComponent(resourceId)}/reservations/${encodeURIComponent(reservationId)}`,\r\n );\r\n }\r\n\r\n /**\r\n * List all active products/services for the organisation.\r\n *\r\n * Calls `GET /v1/public/products` which returns products with\r\n * pricing, duration, and linked equipment.\r\n *\r\n * Falls back to mapping resources → products if the endpoint\r\n * returns 404 (backend hasn't been updated yet).\r\n *\r\n * ```ts\r\n * const { products } = await resira.listProducts();\r\n * ```\r\n */\r\n async listProducts(): Promise<ProductListResponse> {\r\n try {\r\n return await this.request<ProductListResponse>(\"GET\", \"/products\");\r\n } catch (err: unknown) {\r\n // Fallback: if /products endpoint doesn't exist yet (404),\r\n // map resources → products shape\r\n if (\r\n err &&\r\n typeof err === \"object\" &&\r\n \"status\" in err &&\r\n (err as { status: number }).status === 404\r\n ) {\r\n const resourceResponse = await this.listResources();\r\n const products = resourceResponse.resources.map((r) => ({\r\n id: r.id,\r\n name: r.name,\r\n description: r.description,\r\n durationMinutes: r.durationMinutes ?? 60,\r\n priceCents: r.pricePerSession ?? 0,\r\n currency: r.currency ?? \"EUR\",\r\n imageUrl: r.imageUrl,\r\n active: r.active,\r\n sortOrder: 0,\r\n pricingModel: \"per_session\" as const,\r\n equipmentIds: [r.id],\r\n equipmentNames: [r.name],\r\n }));\r\n return { products, count: products.length };\r\n }\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * List all dishes with 3D model data for the organisation.\r\n *\r\n * Returns dishes with name, description, price, image, and\r\n * 3D model URLs for AR/preview rendering.\r\n *\r\n * ```ts\r\n * const { dishes } = await resira.listDishes();\r\n * ```\r\n */\r\n async listDishes(): Promise<DishListResponse> {\r\n return this.request<DishListResponse>(\"GET\", \"/dishes\");\r\n }\r\n\r\n /**\r\n * Get a single dish by ID.\r\n *\r\n * ```ts\r\n * const { dish } = await resira.getDish(\"dish-uuid\");\r\n * console.log(dish.name, dish.model?.glbUrl);\r\n * ```\r\n */\r\n async getDish(dishId: string): Promise<DishResponse> {\r\n if (!dishId) {\r\n throw new Error(\"dishId is required for getDish\");\r\n }\r\n return this.request<DishResponse>(\r\n \"GET\",\r\n `/dishes/${encodeURIComponent(dishId)}`,\r\n );\r\n }\r\n\r\n /**\r\n * Retrieve a checkout session by its token.\r\n *\r\n * Used to hydrate the checkout screen when opening the widget\r\n * with a pre-created session token (skip service selection).\r\n *\r\n * ```ts\r\n * const { session } = await resira.getCheckoutSession(\"cs_ab12cd34ef56\");\r\n * console.log(session.productName, session.priceCents);\r\n * ```\r\n */\r\n async getCheckoutSession(token: string): Promise<CheckoutSessionResponse> {\r\n if (!token) {\r\n throw new Error(\"token is required for getCheckoutSession\");\r\n }\r\n const url = `${this.getBaseUrl()}/v2/public/checkout-sessions/${encodeURIComponent(token)}`;\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n Accept: \"application/json\",\r\n };\r\n\r\n let lastError: ResiraApiError | ResiraNetworkError | undefined;\r\n\r\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\r\n if (attempt > 0) {\r\n const backoff = this.calculateBackoff(attempt, lastError);\r\n await sleep(backoff);\r\n }\r\n\r\n let response: Response;\r\n try {\r\n response = await this._fetch(url, { method: \"GET\", headers });\r\n } catch (err) {\r\n lastError = new ResiraNetworkError(\r\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\r\n err,\r\n );\r\n continue;\r\n }\r\n\r\n if (response.ok) {\r\n const raw = await response.json();\r\n return deepKeysToCamel(raw) as CheckoutSessionResponse;\r\n }\r\n\r\n let errorBody: ApiErrorBody;\r\n try {\r\n errorBody = (await response.json()) as ApiErrorBody;\r\n } catch {\r\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\r\n }\r\n\r\n if (response.status === 429) {\r\n lastError = new ResiraRateLimitError(errorBody, response);\r\n continue;\r\n }\r\n if (response.status >= 500) {\r\n lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n continue;\r\n }\r\n\r\n throw new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n }\r\n\r\n throw lastError!;\r\n }\r\n\r\n /**\r\n * Create a new checkout session.\r\n *\r\n * External sites can create a session with product, slot, and guest\r\n * details, then pass the returned token to the widget to skip\r\n * directly to checkout.\r\n *\r\n * ```ts\r\n * const { sessionToken } = await resira.createCheckoutSession({\r\n * productId: \"prod-123\",\r\n * durationMinutes: 30,\r\n * partySize: 2,\r\n * date: \"2026-07-01\",\r\n * startTime: \"2026-07-01T10:00:00Z\",\r\n * endTime: \"2026-07-01T10:30:00Z\",\r\n * });\r\n * ```\r\n */\r\n async createCheckoutSession(\r\n payload: CreateCheckoutSessionRequest,\r\n options?: { idempotencyKey?: string },\r\n ): Promise<CreateCheckoutSessionResponse> {\r\n const idempotencyKey = options?.idempotencyKey ?? uuid();\r\n const url = `${this.getBaseUrl()}/v2/public/checkout-sessions`;\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n Accept: \"application/json\",\r\n \"Content-Type\": \"application/json\",\r\n \"Idempotency-Key\": idempotencyKey,\r\n };\r\n\r\n const init: RequestInit = {\r\n method: \"POST\",\r\n headers,\r\n body: JSON.stringify(payload),\r\n };\r\n\r\n let lastError: ResiraApiError | ResiraNetworkError | undefined;\r\n\r\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\r\n if (attempt > 0) {\r\n const backoff = this.calculateBackoff(attempt, lastError);\r\n await sleep(backoff);\r\n }\r\n\r\n let response: Response;\r\n try {\r\n response = await this._fetch(url, init);\r\n } catch (err) {\r\n lastError = new ResiraNetworkError(\r\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\r\n err,\r\n );\r\n continue;\r\n }\r\n\r\n if (response.ok) {\r\n const raw = await response.json();\r\n return deepKeysToCamel(raw) as CreateCheckoutSessionResponse;\r\n }\r\n\r\n let errorBody: ApiErrorBody;\r\n try {\r\n errorBody = (await response.json()) as ApiErrorBody;\r\n } catch {\r\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\r\n }\r\n\r\n if (response.status === 429) {\r\n lastError = new ResiraRateLimitError(errorBody, response);\r\n continue;\r\n }\r\n if (response.status >= 500) {\r\n lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n continue;\r\n }\r\n\r\n throw new ResiraApiError(response.status, response.statusText, errorBody, response);\r\n }\r\n\r\n throw lastError!;\r\n }\r\n\r\n /**\r\n * Create a Stripe payment intent for a booking.\r\n *\r\n * The server creates the payment intent, calculates the amount,\r\n * and returns a `clientSecret` to confirm payment on the frontend\r\n * using Stripe Elements.\r\n *\r\n * ```ts\r\n * const { clientSecret, amountNow, amountAtVenue } =\r\n * await resira.createPaymentIntent({\r\n * productId: \"prod-123\",\r\n * resourceId: \"res-456\",\r\n * partySize: 2,\r\n * startDate: \"2026-07-01\",\r\n * startTime: \"2026-07-01T10:00:00Z\",\r\n * endTime: \"2026-07-01T11:00:00Z\",\r\n * guestName: \"Jane Doe\",\r\n * guestEmail: \"jane@example.com\",\r\n * });\r\n * ```\r\n */\r\n async createPaymentIntent(\r\n payload: CreatePaymentIntentRequest,\r\n options?: { idempotencyKey?: string },\r\n ): Promise<PaymentIntentResponse> {\r\n const idempotencyKey = options?.idempotencyKey ?? uuid();\r\n const raw = await this.request<Record<string, unknown>>(\r\n \"POST\",\r\n \"/payments/create-intent\",\r\n payload,\r\n { \"Idempotency-Key\": idempotencyKey },\r\n );\r\n // Normalize response keys to camelCase\r\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\r\n // Map backend field names to SDK field names:\r\n // backend: amountDue, totalPrice\r\n // SDK: amountNow, totalAmount, amountAtVenue\r\n const totalAmount = (data.totalAmount as number) ?? (data.totalPrice as number) ?? 0;\r\n const amountNow = (data.amountNow as number) ?? (data.amountDue as number) ?? totalAmount;\r\n const amountAtVenue = (data.amountAtVenue as number) ?? (totalAmount - amountNow);\r\n return {\r\n clientSecret: data.clientSecret as string,\r\n paymentIntentId: data.paymentIntentId as string,\r\n amountNow,\r\n amountAtVenue,\r\n totalAmount,\r\n currency: (data.currency as string) ?? \"EUR\",\r\n paymentOption: data.paymentOption as string | undefined,\r\n reservationId: data.reservationId as string | undefined,\r\n discountAmount: (data.discountAmount as number) ?? undefined,\r\n originalPrice: (data.originalPrice as number) ?? undefined,\r\n promoCodeApplied: (data.promoCodeApplied as string) ?? undefined,\r\n };\r\n }\r\n\r\n /**\r\n * Confirm that a payment was successful after the guest\r\n * completes the Stripe payment flow.\r\n *\r\n * This transitions the reservation from `pending` to `confirmed`.\r\n *\r\n * ```ts\r\n * const { reservation, paymentStatus } =\r\n * await resira.confirmPayment({\r\n * paymentIntentId: \"pi_xxx\",\r\n * reservationId: \"res-uuid\",\r\n * });\r\n * ```\r\n */\r\n async confirmPayment(\r\n payload: ConfirmPaymentRequest,\r\n ): Promise<ConfirmPaymentResponse> {\r\n const raw = await this.request<Record<string, unknown>>(\r\n \"POST\",\r\n \"/payments/confirm\",\r\n payload,\r\n );\r\n // Normalize response keys to camelCase in case backend returns snake_case\r\n return deepKeysToCamel(raw) as ConfirmPaymentResponse;\r\n }\r\n\r\n // ─────────────────────────────────────────────────────────────\r\n // Internal HTTP layer\r\n // ─────────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Execute an HTTP request with retry logic and error normalization.\r\n *\r\n * - Attaches `Authorization: Bearer <apiKey>` on every request.\r\n * - Retries on 429 and 5xx with exponential backoff + jitter.\r\n * - Parses error bodies and throws typed error classes.\r\n */\r\n private async request<T>(\r\n method: string,\r\n path: string,\r\n body?: unknown,\r\n extraHeaders?: Record<string, string>,\r\n ): Promise<T> {\r\n const url = `${this.getBaseUrl()}${API_PREFIX}${path}`;\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n Accept: \"application/json\",\r\n ...extraHeaders,\r\n };\r\n\r\n if (body !== undefined) {\r\n headers[\"Content-Type\"] = \"application/json\";\r\n }\r\n\r\n const init: RequestInit = {\r\n method,\r\n headers,\r\n body: body !== undefined ? JSON.stringify(body) : undefined,\r\n };\r\n\r\n let lastError: ResiraApiError | ResiraNetworkError | undefined;\r\n\r\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\r\n // ── Wait before retry ────────────────────────────────\r\n if (attempt > 0) {\r\n const backoff = this.calculateBackoff(attempt, lastError);\r\n await sleep(backoff);\r\n }\r\n\r\n // ── Execute the request ──────────────────────────────\r\n let response: Response;\r\n try {\r\n response = await this._fetch(url, init);\r\n } catch (err) {\r\n lastError = new ResiraNetworkError(\r\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\r\n err,\r\n );\r\n // Network errors are always retryable\r\n continue;\r\n }\r\n\r\n // ── Success ──────────────────────────────────────────\r\n if (response.ok) {\r\n return (await response.json()) as T;\r\n }\r\n\r\n // ── Parse error body ─────────────────────────────────\r\n let errorBody: ApiErrorBody;\r\n try {\r\n errorBody = (await response.json()) as ApiErrorBody;\r\n } catch {\r\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\r\n }\r\n\r\n // ── Rate limit ───────────────────────────────────────\r\n if (response.status === 429) {\r\n lastError = new ResiraRateLimitError(errorBody, response);\r\n // Always retry 429s (up to maxRetries)\r\n continue;\r\n }\r\n\r\n // ── Server error (5xx) — retryable ───────────────────\r\n if (response.status >= 500) {\r\n lastError = new ResiraApiError(\r\n response.status,\r\n response.statusText,\r\n errorBody,\r\n response,\r\n );\r\n continue;\r\n }\r\n\r\n // ── Client error (4xx) — NOT retryable ───────────────\r\n throw new ResiraApiError(\r\n response.status,\r\n response.statusText,\r\n errorBody,\r\n response,\r\n );\r\n }\r\n\r\n // All retries exhausted — throw the last error\r\n throw lastError!;\r\n }\r\n\r\n /**\r\n * Calculate backoff delay in milliseconds for a given retry attempt.\r\n *\r\n * Uses exponential backoff with full jitter:\r\n * delay = random(0, base * 2^attempt)\r\n *\r\n * For 429 responses, respects the `Retry-After` header as a minimum.\r\n */\r\n private calculateBackoff(\r\n attempt: number,\r\n lastError?: ResiraApiError | ResiraNetworkError,\r\n ): number {\r\n const exponential = this.retryBaseDelay * 2 ** (attempt - 1);\r\n const jittered = Math.random() * exponential;\r\n\r\n // If the server told us to wait, use that as the floor\r\n if (lastError instanceof ResiraRateLimitError) {\r\n const serverDelay = lastError.retryAfter * 1000;\r\n return Math.max(serverDelay, jittered);\r\n }\r\n\r\n return jittered;\r\n }\r\n\r\n}\r\n"]}
|