@resira/sdk 0.2.6 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,7 +2,14 @@
2
2
 
3
3
  All notable SDK and public API tracking updates are documented here.
4
4
 
5
- ## 0.2.6
5
+ ## 0.3.0
6
+
7
+ - Added `Dish`, `DishModel`, `DishResponse`, and `DishListResponse` types for 3D model / AR dish data.
8
+ - Added `listDishes()` method to fetch all dishes with 3D model URLs for the organisation.
9
+ - Added `getDish(dishId)` method to fetch a single dish by ID.
10
+ - New public endpoints: `GET /v1/public/dishes` and `GET /v1/public/dishes/:id`.
11
+
12
+ ## 0.2.7
6
13
 
7
14
  - Updated SDK routing to ignore ambient host-app env API overrides by default.
8
15
  - Kept explicit `baseUrl` and `baseUrls` support for integrator-controlled routing.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @resira/sdk v0.2.6
1
+ # @resira/sdk v0.3.0
2
2
 
3
3
  TypeScript SDK for the Resira public reservation API.
4
4
 
@@ -7,7 +7,7 @@ Version history lives in [CHANGELOG.md](./CHANGELOG.md).
7
7
  ## Installation
8
8
 
9
9
  ```bash
10
- npm install @resira/sdk@0.2.6
10
+ npm install @resira/sdk@0.3.0
11
11
  ```
12
12
 
13
13
  ## Quick start
@@ -27,12 +27,6 @@ const resira = new Resira({
27
27
  apiKey: "resira_live_your_api_key_here",
28
28
  maxRetries: 3,
29
29
  retryBaseDelay: 500,
30
- // Optional explicit overrides:
31
- // baseUrl: "https://your-api.example.com",
32
- // baseUrls: [
33
- // { url: "https://origin-a.example.com", weight: 1 },
34
- // { url: "https://origin-b.example.com", weight: 1 },
35
- // ],
36
30
  });
37
31
  ```
38
32
 
@@ -49,7 +43,7 @@ const resira = new Resira({
49
43
 
50
44
  - The SDK keeps a browser session sticky to the selected origin.
51
45
  - Ambient host-app env vars such as `NEXT_PUBLIC_API_URL` are ignored.
52
- - Only explicit SDK config via `baseUrl` or `baseUrls` overrides the built-in production defaults.
46
+ - Only explicit SDK config via `baseUrl` or `baseUrls` overrides the default routing behavior.
53
47
  - In development, the SDK defaults to `http://localhost:3001`.
54
48
 
55
49
  ## Core methods
@@ -100,6 +94,26 @@ const { resources } = await resira.listResources();
100
94
  const { products } = await resira.listProducts();
101
95
  ```
102
96
 
97
+ ### `resira.listDishes()`
98
+
99
+ Fetch all dishes with 3D model data for the organisation.
100
+
101
+ ```ts
102
+ const { dishes } = await resira.listDishes();
103
+ dishes.forEach((dish) => {
104
+ console.log(dish.name, dish.model?.glbUrl);
105
+ });
106
+ ```
107
+
108
+ ### `resira.getDish(dishId)`
109
+
110
+ Fetch a single dish by ID.
111
+
112
+ ```ts
113
+ const { dish } = await resira.getDish("dish-uuid");
114
+ console.log(dish.name, dish.model?.glbUrl, dish.model?.usdzUrl);
115
+ ```
116
+
103
117
  ### `resira.createPaymentIntent(payload, options?)`
104
118
 
105
119
  ```ts
package/dist/index.cjs CHANGED
@@ -567,6 +567,36 @@ var Resira = class {
567
567
  throw err;
568
568
  }
569
569
  }
570
+ /**
571
+ * List all dishes with 3D model data for the organisation.
572
+ *
573
+ * Returns dishes with name, description, price, image, and
574
+ * 3D model URLs for AR/preview rendering.
575
+ *
576
+ * ```ts
577
+ * const { dishes } = await resira.listDishes();
578
+ * ```
579
+ */
580
+ async listDishes() {
581
+ return this.request("GET", "/dishes");
582
+ }
583
+ /**
584
+ * Get a single dish by ID.
585
+ *
586
+ * ```ts
587
+ * const { dish } = await resira.getDish("dish-uuid");
588
+ * console.log(dish.name, dish.model?.glbUrl);
589
+ * ```
590
+ */
591
+ async getDish(dishId) {
592
+ if (!dishId) {
593
+ throw new Error("dishId is required for getDish");
594
+ }
595
+ return this.request(
596
+ "GET",
597
+ `/dishes/${encodeURIComponent(dishId)}`
598
+ );
599
+ }
570
600
  /**
571
601
  * Create a Stripe payment intent for a booking.
572
602
  *
@@ -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\";\n\n// ═══════════════════════════════════════════════════════════════\n// Base error\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Base error class for all SDK errors.\n *\n * Use `instanceof` checks to distinguish error types:\n *\n * ```ts\n * try { … } catch (e) {\n * if (e instanceof ResiraRateLimitError) { … }\n * if (e instanceof ResiraApiError) { … }\n * if (e instanceof ResiraNetworkError) { … }\n * }\n * ```\n */\nexport class ResiraError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ResiraError\";\n // Fix prototype chain for instanceof to work after transpilation\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// API error (4xx / 5xx with a parsed body)\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Thrown when the API returns a non-2xx response with a JSON body.\n *\n * ```ts\n * err.status // 400, 404, 409, 422, 500, …\n * err.code // HTTP status text (e.g. \"Not Found\")\n * err.body // Parsed `{ error: \"…\" }` from the server\n * ```\n */\nexport class ResiraApiError extends ResiraError {\n /** HTTP status code. */\n readonly status: number;\n /** HTTP status text (e.g. `\"Bad Request\"`). */\n readonly code: string;\n /** Parsed error body from the API. */\n readonly body: ApiErrorBody;\n /** The full `Response` object (headers are still accessible). */\n readonly response: Response;\n\n constructor(status: number, code: string, body: ApiErrorBody, response: Response) {\n super(body.error || `API error ${status}`);\n this.name = \"ResiraApiError\";\n this.status = status;\n this.code = code;\n this.body = body;\n this.response = response;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n /** Whether this error is retryable (5xx or 429). */\n get retryable(): boolean {\n return this.status === 429 || this.status >= 500;\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Rate limit error (429)\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Thrown when the API returns 429 Too Many Requests.\n *\n * ```ts\n * err.retryAfter // Seconds until the client should retry\n * ```\n */\nexport class ResiraRateLimitError extends ResiraApiError {\n /** Seconds to wait before retrying (from `Retry-After` header). */\n readonly retryAfter: number;\n\n constructor(body: ApiErrorBody, response: Response) {\n super(429, \"Too Many Requests\", body, response);\n this.name = \"ResiraRateLimitError\";\n this.retryAfter =\n body.retryAfter ??\n (Number.parseInt(response.headers.get(\"retry-after\") ?? \"60\", 10) ||\n 60);\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Network error (fetch failed, DNS, timeout, etc.)\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Thrown when the request never reached the server.\n *\n * This wraps the underlying `TypeError` or `DOMException` from\n * `fetch()` failures (DNS resolution, TLS, network offline, etc.).\n */\nexport class ResiraNetworkError extends ResiraError {\n /** The original error thrown by `fetch`. */\n readonly cause: unknown;\n\n constructor(message: string, cause: unknown) {\n super(message);\n this.name = \"ResiraNetworkError\";\n this.cause = cause;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import type { ResiraConfig, WeightedBaseUrl } from \"./types.js\";\n\ninterface ResolvedBaseUrl {\n url: string;\n weight: number;\n}\n\ninterface StickySelection {\n signature: string;\n url: string;\n assignedAt: string;\n}\n\ninterface RoutingStatsEntry {\n lastAssignedAt: string;\n lastOrigin: string;\n selections: number;\n origins: Record<string, number>;\n}\n\ntype AnalyticsWindow = Window & {\n va?: (...args: unknown[]) => void;\n vaq?: unknown[][];\n};\n\nconst DEFAULT_LOCAL_BASE_URLS: ResolvedBaseUrl[] = [\n { url: \"http://localhost:3001\", weight: 100 },\n];\n\nconst DEFAULT_PRODUCTION_BASE_URLS: ResolvedBaseUrl[] = [\n { url: \"https://resira-api-sips-production.up.railway.app\", weight: 50 },\n { url: \"https://api.resira.app\", weight: 50 },\n];\n\nconst STICKY_SELECTION_KEY = \"resira_sdk_api_origin_sticky_v1\";\nconst ROUTING_STATS_KEY = \"resira_sdk_api_routing_stats_v1\";\n\nconst stickySelectionCache = new Map<string, StickySelection>();\n\nfunction readEnv(name: string): string | undefined {\n if (typeof process === \"undefined\") return undefined;\n return process.env?.[name];\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\";\n}\n\nfunction getStorage(type: \"session\" | \"local\"): Storage | null {\n if (!isBrowser()) return null;\n\n try {\n return type === \"session\" ? window.sessionStorage : window.localStorage;\n } catch {\n return null;\n }\n}\n\nfunction getBaseUrlSignature(baseUrls: ResolvedBaseUrl[]): string {\n return baseUrls.map((entry) => `${entry.url}|${entry.weight}`).join(\",\");\n}\n\nfunction pickWeightedBaseUrl(baseUrls: ResolvedBaseUrl[]): ResolvedBaseUrl {\n const totalWeight = baseUrls.reduce((sum, entry) => sum + entry.weight, 0);\n if (totalWeight <= 0) {\n return DEFAULT_PRODUCTION_BASE_URLS[0];\n }\n\n let cursor = Math.random() * totalWeight;\n for (const entry of baseUrls) {\n cursor -= entry.weight;\n if (cursor < 0) {\n return entry;\n }\n }\n\n return baseUrls[baseUrls.length - 1] ?? DEFAULT_PRODUCTION_BASE_URLS[0];\n}\n\nfunction ensureAnalyticsQueue(): AnalyticsWindow | null {\n if (!isBrowser()) return null;\n\n const analyticsWindow = window as AnalyticsWindow;\n if (!analyticsWindow.va) {\n analyticsWindow.va = (...params: unknown[]) => {\n analyticsWindow.vaq = analyticsWindow.vaq || [];\n analyticsWindow.vaq.push(params);\n };\n }\n\n return analyticsWindow;\n}\n\nfunction readStickySelection(signature: string): StickySelection | null {\n const cached = stickySelectionCache.get(signature);\n if (cached) return cached;\n\n const storage = getStorage(\"session\");\n const raw = storage?.getItem(STICKY_SELECTION_KEY);\n if (!raw) return null;\n\n try {\n const parsed = JSON.parse(raw) as StickySelection;\n if (!parsed?.signature || !parsed?.url || parsed.signature !== signature) {\n return null;\n }\n stickySelectionCache.set(signature, parsed);\n return parsed;\n } catch {\n return null;\n }\n}\n\nfunction persistStickySelection(signature: string, url: string): void {\n const selection: StickySelection = {\n signature,\n url,\n assignedAt: new Date().toISOString(),\n };\n\n stickySelectionCache.set(signature, selection);\n getStorage(\"session\")?.setItem(STICKY_SELECTION_KEY, JSON.stringify(selection));\n}\n\nfunction recordRoutingStats(signature: string, entry: ResolvedBaseUrl): void {\n const storage = getStorage(\"local\");\n if (!storage) return;\n\n let stats: Record<string, RoutingStatsEntry> = {};\n try {\n stats = JSON.parse(storage.getItem(ROUTING_STATS_KEY) ?? \"{}\") as Record<string, RoutingStatsEntry>;\n } catch {\n stats = {};\n }\n\n const current = stats[signature] ?? {\n lastAssignedAt: \"\",\n lastOrigin: \"\",\n selections: 0,\n origins: {},\n };\n\n current.lastAssignedAt = new Date().toISOString();\n current.lastOrigin = entry.url;\n current.selections += 1;\n current.origins[entry.url] = (current.origins[entry.url] ?? 0) + 1;\n\n stats[signature] = current;\n storage.setItem(ROUTING_STATS_KEY, JSON.stringify(stats));\n}\n\nfunction emitRoutingSelection(entry: ResolvedBaseUrl, signature: string, totalWeight: number): void {\n if (!isBrowser()) return;\n\n const detail = {\n source: \"sdk\",\n strategy: \"session-sticky\",\n originHost: new URL(entry.url).host,\n originUrl: entry.url,\n originWeight: entry.weight,\n totalWeight,\n signature,\n };\n\n ensureAnalyticsQueue()?.va?.(\"event\", {\n name: \"api_origin_selected\",\n data: detail,\n });\n\n window.dispatchEvent(\n new CustomEvent(\"resira:api-origin-selected\", {\n detail,\n }),\n );\n\n const debugEnabled = /^(1|true)$/i.test(\n readEnv(\"NEXT_PUBLIC_RESIRA_API_ROUTING_DEBUG\") ?? readEnv(\"NEXT_PUBLIC_API_ROUTING_DEBUG\") ?? \"\",\n );\n if (debugEnabled) {\n console.info(\"[resira-sdk] sticky origin selected\", detail);\n }\n}\n\nfunction normalizeUrl(url: string): string {\n return url.trim().replace(/\\/+$/, \"\");\n}\n\nfunction isPositiveNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0;\n}\n\nfunction toResolvedBaseUrl(value: string | WeightedBaseUrl): ResolvedBaseUrl | null {\n if (typeof value === \"string\") {\n const url = normalizeUrl(value);\n return url ? { url, weight: 1 } : null;\n }\n\n const url = normalizeUrl(value.url);\n if (!url) return null;\n\n return {\n url,\n weight: isPositiveNumber(value.weight) ? value.weight : 1,\n };\n}\n\nfunction parseJsonBaseUrls(value: string): ResolvedBaseUrl[] {\n const parsed = JSON.parse(value) as unknown;\n if (!Array.isArray(parsed)) return [];\n\n return parsed.flatMap((entry) => {\n const resolved =\n typeof entry === \"string\"\n ? toResolvedBaseUrl(entry)\n : entry && typeof entry === \"object\"\n ? toResolvedBaseUrl(entry as WeightedBaseUrl)\n : null;\n\n return resolved ? [resolved] : [];\n });\n}\n\nfunction parseDelimitedBaseUrls(value: string): ResolvedBaseUrl[] {\n return value\n .split(\",\")\n .flatMap((entry) => {\n const [rawUrl, rawWeight] = entry.split(\"|\");\n const url = normalizeUrl(rawUrl ?? \"\");\n if (!url) return [];\n\n const parsedWeight = rawWeight ? Number(rawWeight.trim()) : 1;\n return [{\n url,\n weight: isPositiveNumber(parsedWeight) ? parsedWeight : 1,\n }];\n });\n}\n\nfunction parseEnvBaseUrls(value: string | undefined): ResolvedBaseUrl[] {\n if (!value) return [];\n\n try {\n const parsed = parseJsonBaseUrls(value);\n if (parsed.length > 0) return parsed;\n } catch {\n // Fall back to delimiter parsing.\n }\n\n return parseDelimitedBaseUrls(value);\n}\n\nexport function resolveBaseUrls(config: ResiraConfig): ResolvedBaseUrl[] {\n if (config.baseUrl) {\n return [{ url: normalizeUrl(config.baseUrl), weight: 100 }];\n }\n\n if (config.baseUrls?.length) {\n const explicit = config.baseUrls.flatMap((entry) => {\n const resolved = toResolvedBaseUrl(entry);\n return resolved ? [resolved] : [];\n });\n\n if (explicit.length > 0) return explicit;\n }\n\n // Intentionally ignore ambient host-app env vars here.\n // Widgets using the SDK should not inherit a parent app's\n // NEXT_PUBLIC_API_URL / endpoint overrides by accident.\n // Only explicit SDK config (baseUrl/baseUrls) may override\n // the built-in routing defaults.\n\n if (readEnv(\"NODE_ENV\") === \"development\") {\n return DEFAULT_LOCAL_BASE_URLS;\n }\n\n return DEFAULT_PRODUCTION_BASE_URLS;\n}\n\nexport function pickBaseUrl(baseUrls: ResolvedBaseUrl[]): string {\n const signature = getBaseUrlSignature(baseUrls);\n const stickySelection = readStickySelection(signature);\n const stickyEntry = stickySelection\n ? baseUrls.find((entry) => entry.url === stickySelection.url)\n : null;\n if (stickyEntry) {\n return stickyEntry.url;\n }\n\n const selectedEntry = pickWeightedBaseUrl(baseUrls);\n persistStickySelection(signature, selectedEntry.url);\n recordRoutingStats(signature, selectedEntry);\n emitRoutingSelection(\n selectedEntry,\n signature,\n baseUrls.reduce((sum, entry) => sum + entry.weight, 0),\n );\n\n return selectedEntry.url;\n}\n\nexport function getRoutingStats(): Record<string, RoutingStatsEntry> {\n const storage = getStorage(\"local\");\n if (!storage) return {};\n\n try {\n return JSON.parse(storage.getItem(ROUTING_STATS_KEY) ?? \"{}\") as Record<string, RoutingStatsEntry>;\n } catch {\n return {};\n }\n}","import { ResiraApiError, ResiraNetworkError, ResiraRateLimitError } from \"./errors.js\";\nimport { pickBaseUrl, resolveBaseUrls } from \"./routing.js\";\nimport type {\n ApiErrorBody,\n Availability,\n AvailabilityParams,\n ConfirmPaymentRequest,\n ConfirmPaymentResponse,\n CreatePaymentIntentRequest,\n CreateReservationRequest,\n ListReservationsParams,\n PaginatedReservations,\n PaymentIntentResponse,\n ProductListResponse,\n PropertyConfig,\n Reservation,\n ReservationResponse,\n ResiraConfig,\n ResourceListResponse,\n ValidatePromoCodeResponse,\n} from \"./types.js\";\n\n// ═══════════════════════════════════════════════════════════════\n// Constants\n// ═══════════════════════════════════════════════════════════════\n\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_RETRY_BASE_DELAY = 500; // ms\nconst API_PREFIX = \"/v1/public\";\n\n// ═══════════════════════════════════════════════════════════════\n// Helpers\n// ═══════════════════════════════════════════════════════════════\n\n/** Generate a random UUID v4 for idempotency keys. */\nfunction uuid(): string {\n // Use crypto.randomUUID when available (Node 19+, all modern browsers)\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n // Fallback: manual v4 UUID\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\n });\n}\n\n/** Build a query string from a flat params object. Skips `undefined` values. */\nfunction toQueryString(params: Record<string, string | number | boolean | undefined>): string {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);\n }\n }\n return parts.length > 0 ? `?${parts.join(\"&\")}` : \"\";\n}\n\n/** Sleep for `ms` milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Convert camelCase keys to snake_case for query parameters.\n * The API expects snake_case query params (`start_date`, `party_size`).\n */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/** Convert an object's keys from camelCase to snake_case. */\nfunction keysToSnake(\n obj: Record<string, string | number | boolean | undefined>,\n): Record<string, string | number | boolean | undefined> {\n const result: Record<string, string | number | boolean | undefined> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[camelToSnake(key)] = value;\n }\n return result;\n}\n\n/** Convert snake_case string to camelCase. */\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\n/** Deep-convert all keys in an object/array from camelCase to snake_case. */\nfunction deepKeysToSnake(obj: unknown): unknown {\n if (Array.isArray(obj)) return obj.map(deepKeysToSnake);\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[camelToSnake(key)] = deepKeysToSnake(value);\n }\n return result;\n }\n return obj;\n}\n\n/** Deep-convert all keys in an object/array from snake_case to camelCase. */\nfunction deepKeysToCamel(obj: unknown): unknown {\n if (Array.isArray(obj)) return obj.map(deepKeysToCamel);\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[snakeToCamel(key)] = deepKeysToCamel(value);\n }\n return result;\n }\n return obj;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Client\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Resira SDK client.\n *\n * ```ts\n * const resira = new Resira({\n * apiKey: \"resira_live_abc123…\",\n * baseUrl: \"https://api.example.com\", // optional\n * });\n *\n * const availability = await resira.getAvailability(\"prop-1\", {\n * startDate: \"2026-07-01\",\n * endDate: \"2026-07-07\",\n * });\n * ```\n */\nexport class Resira {\n private readonly apiKey: string;\n private readonly baseUrls: ReturnType<typeof resolveBaseUrls>;\n private readonly maxRetries: number;\n private readonly retryBaseDelay: number;\n private readonly _fetch: typeof globalThis.fetch;\n\n constructor(config: ResiraConfig) {\n if (!config.apiKey) {\n throw new Error(\"Resira: apiKey is required\");\n }\n this.apiKey = config.apiKey;\n this.baseUrls = resolveBaseUrls(config);\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\n this.retryBaseDelay = config.retryBaseDelay ?? DEFAULT_RETRY_BASE_DELAY;\n this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n // ─────────────────────────────────────────────────────────────\n // Public API methods\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Fetch the public property configuration.\n *\n * Returns non-sensitive settings such as the Stripe publishable\n * key, deposit percentage, currency, and branding info.\n *\n * ```ts\n * const config = await resira.getConfig();\n * console.log(config.stripePublishableKey); // \"pk_test_…\"\n * ```\n */\n async getConfig(): Promise<PropertyConfig> {\n return this.request<PropertyConfig>(\"GET\", \"/config\");\n }\n\n /**\n * Validate a promo/discount code.\n *\n * Returns discount details if valid, or an error message if not.\n *\n * ```ts\n * const result = await resira.validatePromoCode(\"SUMMER20\");\n * if (result.valid) {\n * console.log(result.discountType, result.discountValue);\n * }\n * ```\n */\n async validatePromoCode(code: string): Promise<ValidatePromoCodeResponse> {\n try {\n const raw = await this.request<Record<string, unknown>>(\n \"POST\",\n \"/promo-codes/validate\",\n { code },\n );\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\n return {\n valid: true,\n discountType: data.discountType as \"percent\" | \"fixed\" | undefined,\n discountValue: data.discountValue as number | undefined,\n currency: data.currency as string | undefined,\n minOrderCents: data.minOrderCents as number | undefined,\n };\n } catch (err: unknown) {\n // 4xx errors mean invalid code\n if (\n err &&\n typeof err === \"object\" &&\n \"status\" in err &&\n (err as { status: number }).status < 500\n ) {\n const apiErr = err as { body?: { error?: string } };\n return {\n valid: false,\n error: apiErr.body?.error ?? \"Invalid promo code\",\n };\n }\n throw err;\n }\n }\n\n /**\n * List all active resources for the organization.\n *\n * Returns resource cards with name, description, price, duration,\n * image, and type — suitable for building a resource picker.\n *\n * ```ts\n * const { resources } = await resira.listResources();\n * ```\n */\n async listResources(): Promise<ResourceListResponse> {\n return this.request<ResourceListResponse>(\"GET\", \"/resources\");\n }\n\n /**\n * Query availability for a resource.\n *\n * **Properties** (date-based):\n * ```ts\n * await resira.getAvailability(\"prop-1\", {\n * startDate: \"2026-07-01\",\n * endDate: \"2026-07-07\",\n * });\n * ```\n *\n * **Restaurants** (time-slot):\n * ```ts\n * await resira.getAvailability(\"table-5\", {\n * date: \"2026-07-01\",\n * partySize: 4,\n * });\n * ```\n *\n * Omit params to get all blocked dates (for calendar rendering).\n */\n async getAvailability(\n resourceId: string,\n params: AvailabilityParams = {},\n ): Promise<Availability> {\n if (!resourceId) {\n throw new Error(\"resourceId is required for getAvailability\");\n }\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\n return this.request<Availability>(\n \"GET\",\n `/resources/${encodeURIComponent(resourceId)}/availability${qs}`,\n );\n }\n\n /**\n * Query availability for a product/service.\n *\n * Aggregates capacity across all linked equipment. If a product\n * has 2 jet skis (each capacity=1), slots show capacity: 2.\n * Both pending and confirmed reservations count as occupied.\n *\n * ```ts\n * await resira.getProductAvailability(\"prod-123\", {\n * date: \"2026-07-01\",\n * durationMinutes: 30,\n * });\n * ```\n */\n async getProductAvailability(\n productId: string,\n params: AvailabilityParams = {},\n ): Promise<Availability> {\n if (!productId) {\n throw new Error(\"productId is required for getProductAvailability\");\n }\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\n return this.request<Availability>(\n \"GET\",\n `/products/${encodeURIComponent(productId)}/availability${qs}`,\n );\n }\n\n /**\n * Create a reservation.\n *\n * Uses `POST /v2/api/reservations` — the `resourceId` is sent in\n * the request body, **not** in the URL path.\n *\n * An `Idempotency-Key` header is automatically attached so that\n * retries (including those from exponential backoff) are safe.\n *\n * ```ts\n * const { reservation } = await resira.createReservation({\n * resourceId: \"prop-1\",\n * guestName: \"Jane Doe\",\n * guestEmail: \"jane@example.com\",\n * startDate: \"2026-07-01\",\n * endDate: \"2026-07-07\",\n * partySize: 3,\n * });\n * ```\n */\n async createReservation(\n payload: CreateReservationRequest,\n options?: { idempotencyKey?: string },\n ): Promise<ReservationResponse> {\n if (!payload.resourceId) {\n throw new Error(\"resourceId is required for createReservation\");\n }\n const idempotencyKey = options?.idempotencyKey ?? uuid();\n\n // Note: uses /v2/api prefix, not /v1/public\n const url = `${this.getBaseUrl()}/v2/api/reservations`;\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"Idempotency-Key\": idempotencyKey,\n };\n\n const init: RequestInit = {\n method: \"POST\",\n headers,\n body: JSON.stringify(payload),\n };\n\n let lastError: ResiraApiError | ResiraNetworkError | ResiraRateLimitError | undefined;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n if (attempt > 0) {\n const backoff = this.calculateBackoff(attempt, lastError);\n await sleep(backoff);\n }\n\n let response: Response;\n try {\n response = await this._fetch(url, init);\n } catch (err) {\n lastError = new ResiraNetworkError(\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\n err,\n );\n continue;\n }\n\n if (response.ok) {\n const raw = (await response.json()) as Record<string, unknown>;\n // The API may nest under \"reservation\" or \"booking\" depending on endpoint\n const resData = (raw.reservation ?? raw.booking ?? {}) as Record<string, unknown>;\n\n // Augment with request data: API response doesn't include\n // startTime/endTime/startDate/endDate/resourceId for v2 reservations\n const reservation: Reservation = {\n id: (resData.id as string) ?? \"\",\n resourceId: (resData.resourceId as string) ?? payload.resourceId,\n status: (resData.status as string) ?? \"pending\",\n startDate: (resData.startDate as string) ?? payload.startDate,\n endDate: (resData.endDate as string) ?? payload.endDate,\n startTime: (resData.startTime as string) ?? payload.startTime,\n endTime: (resData.endTime as string) ?? payload.endTime,\n startDatetime: (resData.startDatetime as string) ?? payload.startTime,\n endDatetime: (resData.endDatetime as string) ?? payload.endTime,\n guestName: (resData.guestName as string) ?? payload.guestName,\n partySize: (resData.partySize as number) ?? (resData.guests as number) ?? payload.partySize ?? 2,\n totalPrice: (resData.totalPrice as number) ?? 0,\n currency: (resData.currency as string) ?? \"EUR\",\n createdAt: (resData.createdAt as string) ?? new Date().toISOString(),\n };\n\n return { reservation };\n }\n\n let errorBody: ApiErrorBody;\n try {\n errorBody = (await response.json()) as ApiErrorBody;\n } catch {\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\n }\n\n if (response.status === 429) {\n lastError = new ResiraRateLimitError(errorBody, response);\n continue;\n }\n\n if (response.status >= 500) {\n lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);\n continue;\n }\n\n throw new ResiraApiError(response.status, response.statusText, errorBody, response);\n }\n\n throw lastError!;\n }\n\n /**\n * List reservations for a resource (paginated).\n *\n * ```ts\n * const page = await resira.listReservations(\"prop-1\", {\n * status: \"confirmed\",\n * page: 1,\n * limit: 50,\n * });\n * console.log(page.data); // Reservation[]\n * console.log(page.totalPages); // number\n * ```\n */\n async listReservations(\n resourceId: string,\n params: ListReservationsParams = {},\n ): Promise<PaginatedReservations> {\n const qs = toQueryString(params as Record<string, string | number | undefined>);\n return this.request<PaginatedReservations>(\n \"GET\",\n `/resources/${encodeURIComponent(resourceId)}/reservations${qs}`,\n );\n }\n\n /**\n * Get a single reservation by ID.\n *\n * ```ts\n * const { reservation } = await resira.getReservation(\"prop-1\", \"res-uuid\");\n * ```\n */\n async getReservation(\n resourceId: string,\n reservationId: string,\n ): Promise<ReservationResponse> {\n return this.request<ReservationResponse>(\n \"GET\",\n `/resources/${encodeURIComponent(resourceId)}/reservations/${encodeURIComponent(reservationId)}`,\n );\n }\n\n /**\n * List all active products/services for the organisation.\n *\n * Calls `GET /v1/public/products` which returns products with\n * pricing, duration, and linked equipment.\n *\n * Falls back to mapping resources → products if the endpoint\n * returns 404 (backend hasn't been updated yet).\n *\n * ```ts\n * const { products } = await resira.listProducts();\n * ```\n */\n async listProducts(): Promise<ProductListResponse> {\n try {\n return await this.request<ProductListResponse>(\"GET\", \"/products\");\n } catch (err: unknown) {\n // Fallback: if /products endpoint doesn't exist yet (404),\n // map resources → products shape\n if (\n err &&\n typeof err === \"object\" &&\n \"status\" in err &&\n (err as { status: number }).status === 404\n ) {\n const resourceResponse = await this.listResources();\n const products = resourceResponse.resources.map((r) => ({\n id: r.id,\n name: r.name,\n description: r.description,\n durationMinutes: r.durationMinutes ?? 60,\n priceCents: r.pricePerSession ?? 0,\n currency: r.currency ?? \"EUR\",\n imageUrl: r.imageUrl,\n active: r.active,\n sortOrder: 0,\n pricingModel: \"per_session\" as const,\n equipmentIds: [r.id],\n equipmentNames: [r.name],\n }));\n return { products, count: products.length };\n }\n throw err;\n }\n }\n\n /**\n * Create a Stripe payment intent for a booking.\n *\n * The server creates the payment intent, calculates the amount,\n * and returns a `clientSecret` to confirm payment on the frontend\n * using Stripe Elements.\n *\n * ```ts\n * const { clientSecret, amountNow, amountAtVenue } =\n * await resira.createPaymentIntent({\n * productId: \"prod-123\",\n * resourceId: \"res-456\",\n * partySize: 2,\n * startDate: \"2026-07-01\",\n * startTime: \"2026-07-01T10:00:00Z\",\n * endTime: \"2026-07-01T11:00:00Z\",\n * guestName: \"Jane Doe\",\n * guestEmail: \"jane@example.com\",\n * });\n * ```\n */\n async createPaymentIntent(\n payload: CreatePaymentIntentRequest,\n options?: { idempotencyKey?: string },\n ): Promise<PaymentIntentResponse> {\n const idempotencyKey = options?.idempotencyKey ?? uuid();\n const raw = await this.request<Record<string, unknown>>(\n \"POST\",\n \"/payments/create-intent\",\n payload,\n { \"Idempotency-Key\": idempotencyKey },\n );\n // Normalize response keys to camelCase\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\n // Map backend field names to SDK field names:\n // backend: amountDue, totalPrice\n // SDK: amountNow, totalAmount, amountAtVenue\n const totalAmount = (data.totalAmount as number) ?? (data.totalPrice as number) ?? 0;\n const amountNow = (data.amountNow as number) ?? (data.amountDue as number) ?? totalAmount;\n const amountAtVenue = (data.amountAtVenue as number) ?? (totalAmount - amountNow);\n return {\n clientSecret: data.clientSecret as string,\n paymentIntentId: data.paymentIntentId as string,\n amountNow,\n amountAtVenue,\n totalAmount,\n currency: (data.currency as string) ?? \"EUR\",\n paymentOption: data.paymentOption as string | undefined,\n reservationId: data.reservationId as string | undefined,\n discountAmount: (data.discountAmount as number) ?? undefined,\n originalPrice: (data.originalPrice as number) ?? undefined,\n promoCodeApplied: (data.promoCodeApplied as string) ?? undefined,\n };\n }\n\n /**\n * Confirm that a payment was successful after the guest\n * completes the Stripe payment flow.\n *\n * This transitions the reservation from `pending` to `confirmed`.\n *\n * ```ts\n * const { reservation, paymentStatus } =\n * await resira.confirmPayment({\n * paymentIntentId: \"pi_xxx\",\n * reservationId: \"res-uuid\",\n * });\n * ```\n */\n async confirmPayment(\n payload: ConfirmPaymentRequest,\n ): Promise<ConfirmPaymentResponse> {\n const raw = await this.request<Record<string, unknown>>(\n \"POST\",\n \"/payments/confirm\",\n payload,\n );\n // Normalize response keys to camelCase in case backend returns snake_case\n return deepKeysToCamel(raw) as ConfirmPaymentResponse;\n }\n\n // ─────────────────────────────────────────────────────────────\n // Internal HTTP layer\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Execute an HTTP request with retry logic and error normalization.\n *\n * - Attaches `Authorization: Bearer <apiKey>` on every request.\n * - Retries on 429 and 5xx with exponential backoff + jitter.\n * - Parses error bodies and throws typed error classes.\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n const url = `${this.getBaseUrl()}${API_PREFIX}${path}`;\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n ...extraHeaders,\n };\n\n if (body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const init: RequestInit = {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n };\n\n let lastError: ResiraApiError | ResiraNetworkError | undefined;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n // ── Wait before retry ────────────────────────────────\n if (attempt > 0) {\n const backoff = this.calculateBackoff(attempt, lastError);\n await sleep(backoff);\n }\n\n // ── Execute the request ──────────────────────────────\n let response: Response;\n try {\n response = await this._fetch(url, init);\n } catch (err) {\n lastError = new ResiraNetworkError(\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\n err,\n );\n // Network errors are always retryable\n continue;\n }\n\n // ── Success ──────────────────────────────────────────\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n // ── Parse error body ─────────────────────────────────\n let errorBody: ApiErrorBody;\n try {\n errorBody = (await response.json()) as ApiErrorBody;\n } catch {\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\n }\n\n // ── Rate limit ───────────────────────────────────────\n if (response.status === 429) {\n lastError = new ResiraRateLimitError(errorBody, response);\n // Always retry 429s (up to maxRetries)\n continue;\n }\n\n // ── Server error (5xx) — retryable ───────────────────\n if (response.status >= 500) {\n lastError = new ResiraApiError(\n response.status,\n response.statusText,\n errorBody,\n response,\n );\n continue;\n }\n\n // ── Client error (4xx) — NOT retryable ───────────────\n throw new ResiraApiError(\n response.status,\n response.statusText,\n errorBody,\n response,\n );\n }\n\n // All retries exhausted — throw the last error\n throw lastError!;\n }\n\n /**\n * Calculate backoff delay in milliseconds for a given retry attempt.\n *\n * Uses exponential backoff with full jitter:\n * delay = random(0, base * 2^attempt)\n *\n * For 429 responses, respects the `Retry-After` header as a minimum.\n */\n private calculateBackoff(\n attempt: number,\n lastError?: ResiraApiError | ResiraNetworkError,\n ): number {\n const exponential = this.retryBaseDelay * 2 ** (attempt - 1);\n const jittered = Math.random() * exponential;\n\n // If the server told us to wait, use that as the floor\n if (lastError instanceof ResiraRateLimitError) {\n const serverDelay = lastError.retryAfter * 1000;\n return Math.max(serverDelay, jittered);\n }\n\n return jittered;\n }\n\n private getBaseUrl(): string {\n return pickBaseUrl(this.baseUrls);\n }\n}\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\";\n\n// ═══════════════════════════════════════════════════════════════\n// Base error\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Base error class for all SDK errors.\n *\n * Use `instanceof` checks to distinguish error types:\n *\n * ```ts\n * try { … } catch (e) {\n * if (e instanceof ResiraRateLimitError) { … }\n * if (e instanceof ResiraApiError) { … }\n * if (e instanceof ResiraNetworkError) { … }\n * }\n * ```\n */\nexport class ResiraError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ResiraError\";\n // Fix prototype chain for instanceof to work after transpilation\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// API error (4xx / 5xx with a parsed body)\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Thrown when the API returns a non-2xx response with a JSON body.\n *\n * ```ts\n * err.status // 400, 404, 409, 422, 500, …\n * err.code // HTTP status text (e.g. \"Not Found\")\n * err.body // Parsed `{ error: \"…\" }` from the server\n * ```\n */\nexport class ResiraApiError extends ResiraError {\n /** HTTP status code. */\n readonly status: number;\n /** HTTP status text (e.g. `\"Bad Request\"`). */\n readonly code: string;\n /** Parsed error body from the API. */\n readonly body: ApiErrorBody;\n /** The full `Response` object (headers are still accessible). */\n readonly response: Response;\n\n constructor(status: number, code: string, body: ApiErrorBody, response: Response) {\n super(body.error || `API error ${status}`);\n this.name = \"ResiraApiError\";\n this.status = status;\n this.code = code;\n this.body = body;\n this.response = response;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n /** Whether this error is retryable (5xx or 429). */\n get retryable(): boolean {\n return this.status === 429 || this.status >= 500;\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Rate limit error (429)\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Thrown when the API returns 429 Too Many Requests.\n *\n * ```ts\n * err.retryAfter // Seconds until the client should retry\n * ```\n */\nexport class ResiraRateLimitError extends ResiraApiError {\n /** Seconds to wait before retrying (from `Retry-After` header). */\n readonly retryAfter: number;\n\n constructor(body: ApiErrorBody, response: Response) {\n super(429, \"Too Many Requests\", body, response);\n this.name = \"ResiraRateLimitError\";\n this.retryAfter =\n body.retryAfter ??\n (Number.parseInt(response.headers.get(\"retry-after\") ?? \"60\", 10) ||\n 60);\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Network error (fetch failed, DNS, timeout, etc.)\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Thrown when the request never reached the server.\n *\n * This wraps the underlying `TypeError` or `DOMException` from\n * `fetch()` failures (DNS resolution, TLS, network offline, etc.).\n */\nexport class ResiraNetworkError extends ResiraError {\n /** The original error thrown by `fetch`. */\n readonly cause: unknown;\n\n constructor(message: string, cause: unknown) {\n super(message);\n this.name = \"ResiraNetworkError\";\n this.cause = cause;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import type { ResiraConfig, WeightedBaseUrl } from \"./types.js\";\n\ninterface ResolvedBaseUrl {\n url: string;\n weight: number;\n}\n\ninterface StickySelection {\n signature: string;\n url: string;\n assignedAt: string;\n}\n\ninterface RoutingStatsEntry {\n lastAssignedAt: string;\n lastOrigin: string;\n selections: number;\n origins: Record<string, number>;\n}\n\ntype AnalyticsWindow = Window & {\n va?: (...args: unknown[]) => void;\n vaq?: unknown[][];\n};\n\nconst DEFAULT_LOCAL_BASE_URLS: ResolvedBaseUrl[] = [\n { url: \"http://localhost:3001\", weight: 100 },\n];\n\nconst DEFAULT_PRODUCTION_BASE_URLS: ResolvedBaseUrl[] = [\n { url: \"https://resira-api-sips-production.up.railway.app\", weight: 50 },\n { url: \"https://api.resira.app\", weight: 50 },\n];\n\nconst STICKY_SELECTION_KEY = \"resira_sdk_api_origin_sticky_v1\";\nconst ROUTING_STATS_KEY = \"resira_sdk_api_routing_stats_v1\";\n\nconst stickySelectionCache = new Map<string, StickySelection>();\n\nfunction readEnv(name: string): string | undefined {\n if (typeof process === \"undefined\") return undefined;\n return process.env?.[name];\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\";\n}\n\nfunction getStorage(type: \"session\" | \"local\"): Storage | null {\n if (!isBrowser()) return null;\n\n try {\n return type === \"session\" ? window.sessionStorage : window.localStorage;\n } catch {\n return null;\n }\n}\n\nfunction getBaseUrlSignature(baseUrls: ResolvedBaseUrl[]): string {\n return baseUrls.map((entry) => `${entry.url}|${entry.weight}`).join(\",\");\n}\n\nfunction pickWeightedBaseUrl(baseUrls: ResolvedBaseUrl[]): ResolvedBaseUrl {\n const totalWeight = baseUrls.reduce((sum, entry) => sum + entry.weight, 0);\n if (totalWeight <= 0) {\n return DEFAULT_PRODUCTION_BASE_URLS[0];\n }\n\n let cursor = Math.random() * totalWeight;\n for (const entry of baseUrls) {\n cursor -= entry.weight;\n if (cursor < 0) {\n return entry;\n }\n }\n\n return baseUrls[baseUrls.length - 1] ?? DEFAULT_PRODUCTION_BASE_URLS[0];\n}\n\nfunction ensureAnalyticsQueue(): AnalyticsWindow | null {\n if (!isBrowser()) return null;\n\n const analyticsWindow = window as AnalyticsWindow;\n if (!analyticsWindow.va) {\n analyticsWindow.va = (...params: unknown[]) => {\n analyticsWindow.vaq = analyticsWindow.vaq || [];\n analyticsWindow.vaq.push(params);\n };\n }\n\n return analyticsWindow;\n}\n\nfunction readStickySelection(signature: string): StickySelection | null {\n const cached = stickySelectionCache.get(signature);\n if (cached) return cached;\n\n const storage = getStorage(\"session\");\n const raw = storage?.getItem(STICKY_SELECTION_KEY);\n if (!raw) return null;\n\n try {\n const parsed = JSON.parse(raw) as StickySelection;\n if (!parsed?.signature || !parsed?.url || parsed.signature !== signature) {\n return null;\n }\n stickySelectionCache.set(signature, parsed);\n return parsed;\n } catch {\n return null;\n }\n}\n\nfunction persistStickySelection(signature: string, url: string): void {\n const selection: StickySelection = {\n signature,\n url,\n assignedAt: new Date().toISOString(),\n };\n\n stickySelectionCache.set(signature, selection);\n getStorage(\"session\")?.setItem(STICKY_SELECTION_KEY, JSON.stringify(selection));\n}\n\nfunction recordRoutingStats(signature: string, entry: ResolvedBaseUrl): void {\n const storage = getStorage(\"local\");\n if (!storage) return;\n\n let stats: Record<string, RoutingStatsEntry> = {};\n try {\n stats = JSON.parse(storage.getItem(ROUTING_STATS_KEY) ?? \"{}\") as Record<string, RoutingStatsEntry>;\n } catch {\n stats = {};\n }\n\n const current = stats[signature] ?? {\n lastAssignedAt: \"\",\n lastOrigin: \"\",\n selections: 0,\n origins: {},\n };\n\n current.lastAssignedAt = new Date().toISOString();\n current.lastOrigin = entry.url;\n current.selections += 1;\n current.origins[entry.url] = (current.origins[entry.url] ?? 0) + 1;\n\n stats[signature] = current;\n storage.setItem(ROUTING_STATS_KEY, JSON.stringify(stats));\n}\n\nfunction emitRoutingSelection(entry: ResolvedBaseUrl, signature: string, totalWeight: number): void {\n if (!isBrowser()) return;\n\n const detail = {\n source: \"sdk\",\n strategy: \"session-sticky\",\n originHost: new URL(entry.url).host,\n originUrl: entry.url,\n originWeight: entry.weight,\n totalWeight,\n signature,\n };\n\n ensureAnalyticsQueue()?.va?.(\"event\", {\n name: \"api_origin_selected\",\n data: detail,\n });\n\n window.dispatchEvent(\n new CustomEvent(\"resira:api-origin-selected\", {\n detail,\n }),\n );\n\n const debugEnabled = /^(1|true)$/i.test(\n readEnv(\"NEXT_PUBLIC_RESIRA_API_ROUTING_DEBUG\") ?? readEnv(\"NEXT_PUBLIC_API_ROUTING_DEBUG\") ?? \"\",\n );\n if (debugEnabled) {\n console.info(\"[resira-sdk] sticky origin selected\", detail);\n }\n}\n\nfunction normalizeUrl(url: string): string {\n return url.trim().replace(/\\/+$/, \"\");\n}\n\nfunction isPositiveNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0;\n}\n\nfunction toResolvedBaseUrl(value: string | WeightedBaseUrl): ResolvedBaseUrl | null {\n if (typeof value === \"string\") {\n const url = normalizeUrl(value);\n return url ? { url, weight: 1 } : null;\n }\n\n const url = normalizeUrl(value.url);\n if (!url) return null;\n\n return {\n url,\n weight: isPositiveNumber(value.weight) ? value.weight : 1,\n };\n}\n\nfunction parseJsonBaseUrls(value: string): ResolvedBaseUrl[] {\n const parsed = JSON.parse(value) as unknown;\n if (!Array.isArray(parsed)) return [];\n\n return parsed.flatMap((entry) => {\n const resolved =\n typeof entry === \"string\"\n ? toResolvedBaseUrl(entry)\n : entry && typeof entry === \"object\"\n ? toResolvedBaseUrl(entry as WeightedBaseUrl)\n : null;\n\n return resolved ? [resolved] : [];\n });\n}\n\nfunction parseDelimitedBaseUrls(value: string): ResolvedBaseUrl[] {\n return value\n .split(\",\")\n .flatMap((entry) => {\n const [rawUrl, rawWeight] = entry.split(\"|\");\n const url = normalizeUrl(rawUrl ?? \"\");\n if (!url) return [];\n\n const parsedWeight = rawWeight ? Number(rawWeight.trim()) : 1;\n return [{\n url,\n weight: isPositiveNumber(parsedWeight) ? parsedWeight : 1,\n }];\n });\n}\n\nfunction parseEnvBaseUrls(value: string | undefined): ResolvedBaseUrl[] {\n if (!value) return [];\n\n try {\n const parsed = parseJsonBaseUrls(value);\n if (parsed.length > 0) return parsed;\n } catch {\n // Fall back to delimiter parsing.\n }\n\n return parseDelimitedBaseUrls(value);\n}\n\nexport function resolveBaseUrls(config: ResiraConfig): ResolvedBaseUrl[] {\n if (config.baseUrl) {\n return [{ url: normalizeUrl(config.baseUrl), weight: 100 }];\n }\n\n if (config.baseUrls?.length) {\n const explicit = config.baseUrls.flatMap((entry) => {\n const resolved = toResolvedBaseUrl(entry);\n return resolved ? [resolved] : [];\n });\n\n if (explicit.length > 0) return explicit;\n }\n\n // Intentionally ignore ambient host-app env vars here.\n // Widgets using the SDK should not inherit a parent app's\n // NEXT_PUBLIC_API_URL / endpoint overrides by accident.\n // Only explicit SDK config (baseUrl/baseUrls) may override\n // the built-in routing defaults.\n\n if (readEnv(\"NODE_ENV\") === \"development\") {\n return DEFAULT_LOCAL_BASE_URLS;\n }\n\n return DEFAULT_PRODUCTION_BASE_URLS;\n}\n\nexport function pickBaseUrl(baseUrls: ResolvedBaseUrl[]): string {\n const signature = getBaseUrlSignature(baseUrls);\n const stickySelection = readStickySelection(signature);\n const stickyEntry = stickySelection\n ? baseUrls.find((entry) => entry.url === stickySelection.url)\n : null;\n if (stickyEntry) {\n return stickyEntry.url;\n }\n\n const selectedEntry = pickWeightedBaseUrl(baseUrls);\n persistStickySelection(signature, selectedEntry.url);\n recordRoutingStats(signature, selectedEntry);\n emitRoutingSelection(\n selectedEntry,\n signature,\n baseUrls.reduce((sum, entry) => sum + entry.weight, 0),\n );\n\n return selectedEntry.url;\n}\n\nexport function getRoutingStats(): Record<string, RoutingStatsEntry> {\n const storage = getStorage(\"local\");\n if (!storage) return {};\n\n try {\n return JSON.parse(storage.getItem(ROUTING_STATS_KEY) ?? \"{}\") as Record<string, RoutingStatsEntry>;\n } catch {\n return {};\n }\n}","import { ResiraApiError, ResiraNetworkError, ResiraRateLimitError } from \"./errors.js\";\nimport { pickBaseUrl, resolveBaseUrls } from \"./routing.js\";\nimport type {\n ApiErrorBody,\n Availability,\n AvailabilityParams,\n ConfirmPaymentRequest,\n ConfirmPaymentResponse,\n CreatePaymentIntentRequest,\n CreateReservationRequest,\n Dish,\n DishListResponse,\n DishResponse,\n ListReservationsParams,\n PaginatedReservations,\n PaymentIntentResponse,\n ProductListResponse,\n PropertyConfig,\n Reservation,\n ReservationResponse,\n ResiraConfig,\n ResourceListResponse,\n ValidatePromoCodeResponse,\n} from \"./types.js\";\n\n// ═══════════════════════════════════════════════════════════════\n// Constants\n// ═══════════════════════════════════════════════════════════════\n\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_RETRY_BASE_DELAY = 500; // ms\nconst API_PREFIX = \"/v1/public\";\n\n// ═══════════════════════════════════════════════════════════════\n// Helpers\n// ═══════════════════════════════════════════════════════════════\n\n/** Generate a random UUID v4 for idempotency keys. */\nfunction uuid(): string {\n // Use crypto.randomUUID when available (Node 19+, all modern browsers)\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n // Fallback: manual v4 UUID\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\n });\n}\n\n/** Build a query string from a flat params object. Skips `undefined` values. */\nfunction toQueryString(params: Record<string, string | number | boolean | undefined>): string {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);\n }\n }\n return parts.length > 0 ? `?${parts.join(\"&\")}` : \"\";\n}\n\n/** Sleep for `ms` milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Convert camelCase keys to snake_case for query parameters.\n * The API expects snake_case query params (`start_date`, `party_size`).\n */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/** Convert an object's keys from camelCase to snake_case. */\nfunction keysToSnake(\n obj: Record<string, string | number | boolean | undefined>,\n): Record<string, string | number | boolean | undefined> {\n const result: Record<string, string | number | boolean | undefined> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[camelToSnake(key)] = value;\n }\n return result;\n}\n\n/** Convert snake_case string to camelCase. */\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\n/** Deep-convert all keys in an object/array from camelCase to snake_case. */\nfunction deepKeysToSnake(obj: unknown): unknown {\n if (Array.isArray(obj)) return obj.map(deepKeysToSnake);\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[camelToSnake(key)] = deepKeysToSnake(value);\n }\n return result;\n }\n return obj;\n}\n\n/** Deep-convert all keys in an object/array from snake_case to camelCase. */\nfunction deepKeysToCamel(obj: unknown): unknown {\n if (Array.isArray(obj)) return obj.map(deepKeysToCamel);\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[snakeToCamel(key)] = deepKeysToCamel(value);\n }\n return result;\n }\n return obj;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Client\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Resira SDK client.\n *\n * ```ts\n * const resira = new Resira({\n * apiKey: \"resira_live_abc123…\",\n * baseUrl: \"https://api.example.com\", // optional\n * });\n *\n * const availability = await resira.getAvailability(\"prop-1\", {\n * startDate: \"2026-07-01\",\n * endDate: \"2026-07-07\",\n * });\n * ```\n */\nexport class Resira {\n private readonly apiKey: string;\n private readonly baseUrls: ReturnType<typeof resolveBaseUrls>;\n private readonly maxRetries: number;\n private readonly retryBaseDelay: number;\n private readonly _fetch: typeof globalThis.fetch;\n\n constructor(config: ResiraConfig) {\n if (!config.apiKey) {\n throw new Error(\"Resira: apiKey is required\");\n }\n this.apiKey = config.apiKey;\n this.baseUrls = resolveBaseUrls(config);\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\n this.retryBaseDelay = config.retryBaseDelay ?? DEFAULT_RETRY_BASE_DELAY;\n this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n // ─────────────────────────────────────────────────────────────\n // Public API methods\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Fetch the public property configuration.\n *\n * Returns non-sensitive settings such as the Stripe publishable\n * key, deposit percentage, currency, and branding info.\n *\n * ```ts\n * const config = await resira.getConfig();\n * console.log(config.stripePublishableKey); // \"pk_test_…\"\n * ```\n */\n async getConfig(): Promise<PropertyConfig> {\n return this.request<PropertyConfig>(\"GET\", \"/config\");\n }\n\n /**\n * Validate a promo/discount code.\n *\n * Returns discount details if valid, or an error message if not.\n *\n * ```ts\n * const result = await resira.validatePromoCode(\"SUMMER20\");\n * if (result.valid) {\n * console.log(result.discountType, result.discountValue);\n * }\n * ```\n */\n async validatePromoCode(code: string): Promise<ValidatePromoCodeResponse> {\n try {\n const raw = await this.request<Record<string, unknown>>(\n \"POST\",\n \"/promo-codes/validate\",\n { code },\n );\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\n return {\n valid: true,\n discountType: data.discountType as \"percent\" | \"fixed\" | undefined,\n discountValue: data.discountValue as number | undefined,\n currency: data.currency as string | undefined,\n minOrderCents: data.minOrderCents as number | undefined,\n };\n } catch (err: unknown) {\n // 4xx errors mean invalid code\n if (\n err &&\n typeof err === \"object\" &&\n \"status\" in err &&\n (err as { status: number }).status < 500\n ) {\n const apiErr = err as { body?: { error?: string } };\n return {\n valid: false,\n error: apiErr.body?.error ?? \"Invalid promo code\",\n };\n }\n throw err;\n }\n }\n\n /**\n * List all active resources for the organization.\n *\n * Returns resource cards with name, description, price, duration,\n * image, and type — suitable for building a resource picker.\n *\n * ```ts\n * const { resources } = await resira.listResources();\n * ```\n */\n async listResources(): Promise<ResourceListResponse> {\n return this.request<ResourceListResponse>(\"GET\", \"/resources\");\n }\n\n /**\n * Query availability for a resource.\n *\n * **Properties** (date-based):\n * ```ts\n * await resira.getAvailability(\"prop-1\", {\n * startDate: \"2026-07-01\",\n * endDate: \"2026-07-07\",\n * });\n * ```\n *\n * **Restaurants** (time-slot):\n * ```ts\n * await resira.getAvailability(\"table-5\", {\n * date: \"2026-07-01\",\n * partySize: 4,\n * });\n * ```\n *\n * Omit params to get all blocked dates (for calendar rendering).\n */\n async getAvailability(\n resourceId: string,\n params: AvailabilityParams = {},\n ): Promise<Availability> {\n if (!resourceId) {\n throw new Error(\"resourceId is required for getAvailability\");\n }\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\n return this.request<Availability>(\n \"GET\",\n `/resources/${encodeURIComponent(resourceId)}/availability${qs}`,\n );\n }\n\n /**\n * Query availability for a product/service.\n *\n * Aggregates capacity across all linked equipment. If a product\n * has 2 jet skis (each capacity=1), slots show capacity: 2.\n * Both pending and confirmed reservations count as occupied.\n *\n * ```ts\n * await resira.getProductAvailability(\"prod-123\", {\n * date: \"2026-07-01\",\n * durationMinutes: 30,\n * });\n * ```\n */\n async getProductAvailability(\n productId: string,\n params: AvailabilityParams = {},\n ): Promise<Availability> {\n if (!productId) {\n throw new Error(\"productId is required for getProductAvailability\");\n }\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\n return this.request<Availability>(\n \"GET\",\n `/products/${encodeURIComponent(productId)}/availability${qs}`,\n );\n }\n\n /**\n * Create a reservation.\n *\n * Uses `POST /v2/api/reservations` — the `resourceId` is sent in\n * the request body, **not** in the URL path.\n *\n * An `Idempotency-Key` header is automatically attached so that\n * retries (including those from exponential backoff) are safe.\n *\n * ```ts\n * const { reservation } = await resira.createReservation({\n * resourceId: \"prop-1\",\n * guestName: \"Jane Doe\",\n * guestEmail: \"jane@example.com\",\n * startDate: \"2026-07-01\",\n * endDate: \"2026-07-07\",\n * partySize: 3,\n * });\n * ```\n */\n async createReservation(\n payload: CreateReservationRequest,\n options?: { idempotencyKey?: string },\n ): Promise<ReservationResponse> {\n if (!payload.resourceId) {\n throw new Error(\"resourceId is required for createReservation\");\n }\n const idempotencyKey = options?.idempotencyKey ?? uuid();\n\n // Note: uses /v2/api prefix, not /v1/public\n const url = `${this.getBaseUrl()}/v2/api/reservations`;\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"Idempotency-Key\": idempotencyKey,\n };\n\n const init: RequestInit = {\n method: \"POST\",\n headers,\n body: JSON.stringify(payload),\n };\n\n let lastError: ResiraApiError | ResiraNetworkError | ResiraRateLimitError | undefined;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n if (attempt > 0) {\n const backoff = this.calculateBackoff(attempt, lastError);\n await sleep(backoff);\n }\n\n let response: Response;\n try {\n response = await this._fetch(url, init);\n } catch (err) {\n lastError = new ResiraNetworkError(\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\n err,\n );\n continue;\n }\n\n if (response.ok) {\n const raw = (await response.json()) as Record<string, unknown>;\n // The API may nest under \"reservation\" or \"booking\" depending on endpoint\n const resData = (raw.reservation ?? raw.booking ?? {}) as Record<string, unknown>;\n\n // Augment with request data: API response doesn't include\n // startTime/endTime/startDate/endDate/resourceId for v2 reservations\n const reservation: Reservation = {\n id: (resData.id as string) ?? \"\",\n resourceId: (resData.resourceId as string) ?? payload.resourceId,\n status: (resData.status as string) ?? \"pending\",\n startDate: (resData.startDate as string) ?? payload.startDate,\n endDate: (resData.endDate as string) ?? payload.endDate,\n startTime: (resData.startTime as string) ?? payload.startTime,\n endTime: (resData.endTime as string) ?? payload.endTime,\n startDatetime: (resData.startDatetime as string) ?? payload.startTime,\n endDatetime: (resData.endDatetime as string) ?? payload.endTime,\n guestName: (resData.guestName as string) ?? payload.guestName,\n partySize: (resData.partySize as number) ?? (resData.guests as number) ?? payload.partySize ?? 2,\n totalPrice: (resData.totalPrice as number) ?? 0,\n currency: (resData.currency as string) ?? \"EUR\",\n createdAt: (resData.createdAt as string) ?? new Date().toISOString(),\n };\n\n return { reservation };\n }\n\n let errorBody: ApiErrorBody;\n try {\n errorBody = (await response.json()) as ApiErrorBody;\n } catch {\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\n }\n\n if (response.status === 429) {\n lastError = new ResiraRateLimitError(errorBody, response);\n continue;\n }\n\n if (response.status >= 500) {\n lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);\n continue;\n }\n\n throw new ResiraApiError(response.status, response.statusText, errorBody, response);\n }\n\n throw lastError!;\n }\n\n /**\n * List reservations for a resource (paginated).\n *\n * ```ts\n * const page = await resira.listReservations(\"prop-1\", {\n * status: \"confirmed\",\n * page: 1,\n * limit: 50,\n * });\n * console.log(page.data); // Reservation[]\n * console.log(page.totalPages); // number\n * ```\n */\n async listReservations(\n resourceId: string,\n params: ListReservationsParams = {},\n ): Promise<PaginatedReservations> {\n const qs = toQueryString(params as Record<string, string | number | undefined>);\n return this.request<PaginatedReservations>(\n \"GET\",\n `/resources/${encodeURIComponent(resourceId)}/reservations${qs}`,\n );\n }\n\n /**\n * Get a single reservation by ID.\n *\n * ```ts\n * const { reservation } = await resira.getReservation(\"prop-1\", \"res-uuid\");\n * ```\n */\n async getReservation(\n resourceId: string,\n reservationId: string,\n ): Promise<ReservationResponse> {\n return this.request<ReservationResponse>(\n \"GET\",\n `/resources/${encodeURIComponent(resourceId)}/reservations/${encodeURIComponent(reservationId)}`,\n );\n }\n\n /**\n * List all active products/services for the organisation.\n *\n * Calls `GET /v1/public/products` which returns products with\n * pricing, duration, and linked equipment.\n *\n * Falls back to mapping resources → products if the endpoint\n * returns 404 (backend hasn't been updated yet).\n *\n * ```ts\n * const { products } = await resira.listProducts();\n * ```\n */\n async listProducts(): Promise<ProductListResponse> {\n try {\n return await this.request<ProductListResponse>(\"GET\", \"/products\");\n } catch (err: unknown) {\n // Fallback: if /products endpoint doesn't exist yet (404),\n // map resources → products shape\n if (\n err &&\n typeof err === \"object\" &&\n \"status\" in err &&\n (err as { status: number }).status === 404\n ) {\n const resourceResponse = await this.listResources();\n const products = resourceResponse.resources.map((r) => ({\n id: r.id,\n name: r.name,\n description: r.description,\n durationMinutes: r.durationMinutes ?? 60,\n priceCents: r.pricePerSession ?? 0,\n currency: r.currency ?? \"EUR\",\n imageUrl: r.imageUrl,\n active: r.active,\n sortOrder: 0,\n pricingModel: \"per_session\" as const,\n equipmentIds: [r.id],\n equipmentNames: [r.name],\n }));\n return { products, count: products.length };\n }\n throw err;\n }\n }\n\n /**\n * List all dishes with 3D model data for the organisation.\n *\n * Returns dishes with name, description, price, image, and\n * 3D model URLs for AR/preview rendering.\n *\n * ```ts\n * const { dishes } = await resira.listDishes();\n * ```\n */\n async listDishes(): Promise<DishListResponse> {\n return this.request<DishListResponse>(\"GET\", \"/dishes\");\n }\n\n /**\n * Get a single dish by ID.\n *\n * ```ts\n * const { dish } = await resira.getDish(\"dish-uuid\");\n * console.log(dish.name, dish.model?.glbUrl);\n * ```\n */\n async getDish(dishId: string): Promise<DishResponse> {\n if (!dishId) {\n throw new Error(\"dishId is required for getDish\");\n }\n return this.request<DishResponse>(\n \"GET\",\n `/dishes/${encodeURIComponent(dishId)}`,\n );\n }\n\n /**\n * Create a Stripe payment intent for a booking.\n *\n * The server creates the payment intent, calculates the amount,\n * and returns a `clientSecret` to confirm payment on the frontend\n * using Stripe Elements.\n *\n * ```ts\n * const { clientSecret, amountNow, amountAtVenue } =\n * await resira.createPaymentIntent({\n * productId: \"prod-123\",\n * resourceId: \"res-456\",\n * partySize: 2,\n * startDate: \"2026-07-01\",\n * startTime: \"2026-07-01T10:00:00Z\",\n * endTime: \"2026-07-01T11:00:00Z\",\n * guestName: \"Jane Doe\",\n * guestEmail: \"jane@example.com\",\n * });\n * ```\n */\n async createPaymentIntent(\n payload: CreatePaymentIntentRequest,\n options?: { idempotencyKey?: string },\n ): Promise<PaymentIntentResponse> {\n const idempotencyKey = options?.idempotencyKey ?? uuid();\n const raw = await this.request<Record<string, unknown>>(\n \"POST\",\n \"/payments/create-intent\",\n payload,\n { \"Idempotency-Key\": idempotencyKey },\n );\n // Normalize response keys to camelCase\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\n // Map backend field names to SDK field names:\n // backend: amountDue, totalPrice\n // SDK: amountNow, totalAmount, amountAtVenue\n const totalAmount = (data.totalAmount as number) ?? (data.totalPrice as number) ?? 0;\n const amountNow = (data.amountNow as number) ?? (data.amountDue as number) ?? totalAmount;\n const amountAtVenue = (data.amountAtVenue as number) ?? (totalAmount - amountNow);\n return {\n clientSecret: data.clientSecret as string,\n paymentIntentId: data.paymentIntentId as string,\n amountNow,\n amountAtVenue,\n totalAmount,\n currency: (data.currency as string) ?? \"EUR\",\n paymentOption: data.paymentOption as string | undefined,\n reservationId: data.reservationId as string | undefined,\n discountAmount: (data.discountAmount as number) ?? undefined,\n originalPrice: (data.originalPrice as number) ?? undefined,\n promoCodeApplied: (data.promoCodeApplied as string) ?? undefined,\n };\n }\n\n /**\n * Confirm that a payment was successful after the guest\n * completes the Stripe payment flow.\n *\n * This transitions the reservation from `pending` to `confirmed`.\n *\n * ```ts\n * const { reservation, paymentStatus } =\n * await resira.confirmPayment({\n * paymentIntentId: \"pi_xxx\",\n * reservationId: \"res-uuid\",\n * });\n * ```\n */\n async confirmPayment(\n payload: ConfirmPaymentRequest,\n ): Promise<ConfirmPaymentResponse> {\n const raw = await this.request<Record<string, unknown>>(\n \"POST\",\n \"/payments/confirm\",\n payload,\n );\n // Normalize response keys to camelCase in case backend returns snake_case\n return deepKeysToCamel(raw) as ConfirmPaymentResponse;\n }\n\n // ─────────────────────────────────────────────────────────────\n // Internal HTTP layer\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Execute an HTTP request with retry logic and error normalization.\n *\n * - Attaches `Authorization: Bearer <apiKey>` on every request.\n * - Retries on 429 and 5xx with exponential backoff + jitter.\n * - Parses error bodies and throws typed error classes.\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n const url = `${this.getBaseUrl()}${API_PREFIX}${path}`;\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n ...extraHeaders,\n };\n\n if (body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const init: RequestInit = {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n };\n\n let lastError: ResiraApiError | ResiraNetworkError | undefined;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n // ── Wait before retry ────────────────────────────────\n if (attempt > 0) {\n const backoff = this.calculateBackoff(attempt, lastError);\n await sleep(backoff);\n }\n\n // ── Execute the request ──────────────────────────────\n let response: Response;\n try {\n response = await this._fetch(url, init);\n } catch (err) {\n lastError = new ResiraNetworkError(\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\n err,\n );\n // Network errors are always retryable\n continue;\n }\n\n // ── Success ──────────────────────────────────────────\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n // ── Parse error body ─────────────────────────────────\n let errorBody: ApiErrorBody;\n try {\n errorBody = (await response.json()) as ApiErrorBody;\n } catch {\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\n }\n\n // ── Rate limit ───────────────────────────────────────\n if (response.status === 429) {\n lastError = new ResiraRateLimitError(errorBody, response);\n // Always retry 429s (up to maxRetries)\n continue;\n }\n\n // ── Server error (5xx) — retryable ───────────────────\n if (response.status >= 500) {\n lastError = new ResiraApiError(\n response.status,\n response.statusText,\n errorBody,\n response,\n );\n continue;\n }\n\n // ── Client error (4xx) — NOT retryable ───────────────\n throw new ResiraApiError(\n response.status,\n response.statusText,\n errorBody,\n response,\n );\n }\n\n // All retries exhausted — throw the last error\n throw lastError!;\n }\n\n /**\n * Calculate backoff delay in milliseconds for a given retry attempt.\n *\n * Uses exponential backoff with full jitter:\n * delay = random(0, base * 2^attempt)\n *\n * For 429 responses, respects the `Retry-After` header as a minimum.\n */\n private calculateBackoff(\n attempt: number,\n lastError?: ResiraApiError | ResiraNetworkError,\n ): number {\n const exponential = this.retryBaseDelay * 2 ** (attempt - 1);\n const jittered = Math.random() * exponential;\n\n // If the server told us to wait, use that as the floor\n if (lastError instanceof ResiraRateLimitError) {\n const serverDelay = lastError.retryAfter * 1000;\n return Math.max(serverDelay, jittered);\n }\n\n return jittered;\n }\n\n private getBaseUrl(): string {\n return pickBaseUrl(this.baseUrls);\n }\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -222,7 +222,7 @@ interface ApiErrorBody {
222
222
  retryAfter?: number;
223
223
  }
224
224
  /** Pricing model for a product/service. */
225
- type PricingModel = "per_session" | "per_person";
225
+ type PricingModel = "per_session" | "per_person" | "per_rider";
226
226
  /** A duration-price pair for multi-duration services. */
227
227
  interface DurationPrice {
228
228
  /** Duration in minutes. */
@@ -230,6 +230,13 @@ interface DurationPrice {
230
230
  /** Price in the smallest currency unit (cents). */
231
231
  priceCents: number;
232
232
  }
233
+ /** A rider-tier with its own set of duration-price pairs. */
234
+ interface RiderTierPrice {
235
+ /** Number of riders this tier applies to. */
236
+ riders: number;
237
+ /** Duration-price options for this rider count. */
238
+ durationPricing: DurationPrice[];
239
+ }
233
240
  /** A bookable product/service as returned by the public API. */
234
241
  interface Product {
235
242
  /** Unique product identifier. */
@@ -246,6 +253,8 @@ interface Product {
246
253
  currency: string;
247
254
  /** Optional image URL. */
248
255
  imageUrl?: string;
256
+ /** Optional calendar/display color for this service, usually configured in admin settings. */
257
+ serviceColor?: string;
249
258
  /** Whether the product is active. */
250
259
  active: boolean;
251
260
  /** Sort order. */
@@ -260,6 +269,8 @@ interface Product {
260
269
  maxPartySize?: number;
261
270
  /** Available duration options with per-duration pricing. If absent, single duration. */
262
271
  durationPricing?: DurationPrice[];
272
+ /** Rider-tier pricing (per_rider model). Each tier has a rider count and its own duration-price set. */
273
+ riderTierPricing?: RiderTierPrice[];
263
274
  }
264
275
  /** Response wrapper for product listing. */
265
276
  interface ProductListResponse {
@@ -395,6 +406,51 @@ interface PromoCode {
395
406
  createdAt: string;
396
407
  updatedAt: string;
397
408
  }
409
+ /** A 3D model asset attached to a dish. */
410
+ interface DishModel {
411
+ /** URL to the GLB/glTF model file. */
412
+ glbUrl: string;
413
+ /** URL to the USDZ model file (iOS AR Quick Look). */
414
+ usdzUrl?: string;
415
+ /** Optional poster/thumbnail shown while the model loads. */
416
+ posterUrl?: string;
417
+ /** Alt text for accessibility. */
418
+ alt?: string;
419
+ }
420
+ /** A dish as returned by the public API. */
421
+ interface Dish {
422
+ /** Unique dish identifier (shown in admin for easy copy). */
423
+ id: string;
424
+ /** Human-readable dish name. */
425
+ name: string;
426
+ /** Optional description. */
427
+ description?: string;
428
+ /** Price in the smallest currency unit (cents). */
429
+ priceCents?: number;
430
+ /** ISO 4217 currency code (e.g. `"EUR"`). */
431
+ currency?: string;
432
+ /** Category or tag (e.g. "Starter", "Main", "Dessert"). */
433
+ category?: string;
434
+ /** Optional flat image URL (fallback when 3D is unavailable). */
435
+ imageUrl?: string;
436
+ /** 3D model data for AR/preview. */
437
+ model?: DishModel;
438
+ /** Whether the dish is currently available. */
439
+ available?: boolean;
440
+ /** Allergen tags (e.g. ["gluten", "dairy"]). */
441
+ allergens?: string[];
442
+ /** Sort order. */
443
+ sortOrder?: number;
444
+ }
445
+ /** Response wrapper for a single dish. */
446
+ interface DishResponse {
447
+ dish: Dish;
448
+ }
449
+ /** Response wrapper for dish listing. */
450
+ interface DishListResponse {
451
+ dishes: Dish[];
452
+ count: number;
453
+ }
398
454
 
399
455
  /**
400
456
  * Resira SDK client.
@@ -550,6 +606,26 @@ declare class Resira {
550
606
  * ```
551
607
  */
552
608
  listProducts(): Promise<ProductListResponse>;
609
+ /**
610
+ * List all dishes with 3D model data for the organisation.
611
+ *
612
+ * Returns dishes with name, description, price, image, and
613
+ * 3D model URLs for AR/preview rendering.
614
+ *
615
+ * ```ts
616
+ * const { dishes } = await resira.listDishes();
617
+ * ```
618
+ */
619
+ listDishes(): Promise<DishListResponse>;
620
+ /**
621
+ * Get a single dish by ID.
622
+ *
623
+ * ```ts
624
+ * const { dish } = await resira.getDish("dish-uuid");
625
+ * console.log(dish.name, dish.model?.glbUrl);
626
+ * ```
627
+ */
628
+ getDish(dishId: string): Promise<DishResponse>;
553
629
  /**
554
630
  * Create a Stripe payment intent for a booking.
555
631
  *
@@ -671,4 +747,4 @@ declare class ResiraNetworkError extends ResiraError {
671
747
  constructor(message: string, cause: unknown);
672
748
  }
673
749
 
674
- export { type ApiErrorBody, type Availability, type AvailabilityParams, type ConfirmPaymentRequest, type ConfirmPaymentResponse, type CreatePaymentIntentRequest, type CreateReservationRequest, type DateAvailability, 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 TimeSlotAvailability, type ValidatePromoCodeResponse, type WeightedBaseUrl };
750
+ 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 };
package/dist/index.d.ts CHANGED
@@ -222,7 +222,7 @@ interface ApiErrorBody {
222
222
  retryAfter?: number;
223
223
  }
224
224
  /** Pricing model for a product/service. */
225
- type PricingModel = "per_session" | "per_person";
225
+ type PricingModel = "per_session" | "per_person" | "per_rider";
226
226
  /** A duration-price pair for multi-duration services. */
227
227
  interface DurationPrice {
228
228
  /** Duration in minutes. */
@@ -230,6 +230,13 @@ interface DurationPrice {
230
230
  /** Price in the smallest currency unit (cents). */
231
231
  priceCents: number;
232
232
  }
233
+ /** A rider-tier with its own set of duration-price pairs. */
234
+ interface RiderTierPrice {
235
+ /** Number of riders this tier applies to. */
236
+ riders: number;
237
+ /** Duration-price options for this rider count. */
238
+ durationPricing: DurationPrice[];
239
+ }
233
240
  /** A bookable product/service as returned by the public API. */
234
241
  interface Product {
235
242
  /** Unique product identifier. */
@@ -246,6 +253,8 @@ interface Product {
246
253
  currency: string;
247
254
  /** Optional image URL. */
248
255
  imageUrl?: string;
256
+ /** Optional calendar/display color for this service, usually configured in admin settings. */
257
+ serviceColor?: string;
249
258
  /** Whether the product is active. */
250
259
  active: boolean;
251
260
  /** Sort order. */
@@ -260,6 +269,8 @@ interface Product {
260
269
  maxPartySize?: number;
261
270
  /** Available duration options with per-duration pricing. If absent, single duration. */
262
271
  durationPricing?: DurationPrice[];
272
+ /** Rider-tier pricing (per_rider model). Each tier has a rider count and its own duration-price set. */
273
+ riderTierPricing?: RiderTierPrice[];
263
274
  }
264
275
  /** Response wrapper for product listing. */
265
276
  interface ProductListResponse {
@@ -395,6 +406,51 @@ interface PromoCode {
395
406
  createdAt: string;
396
407
  updatedAt: string;
397
408
  }
409
+ /** A 3D model asset attached to a dish. */
410
+ interface DishModel {
411
+ /** URL to the GLB/glTF model file. */
412
+ glbUrl: string;
413
+ /** URL to the USDZ model file (iOS AR Quick Look). */
414
+ usdzUrl?: string;
415
+ /** Optional poster/thumbnail shown while the model loads. */
416
+ posterUrl?: string;
417
+ /** Alt text for accessibility. */
418
+ alt?: string;
419
+ }
420
+ /** A dish as returned by the public API. */
421
+ interface Dish {
422
+ /** Unique dish identifier (shown in admin for easy copy). */
423
+ id: string;
424
+ /** Human-readable dish name. */
425
+ name: string;
426
+ /** Optional description. */
427
+ description?: string;
428
+ /** Price in the smallest currency unit (cents). */
429
+ priceCents?: number;
430
+ /** ISO 4217 currency code (e.g. `"EUR"`). */
431
+ currency?: string;
432
+ /** Category or tag (e.g. "Starter", "Main", "Dessert"). */
433
+ category?: string;
434
+ /** Optional flat image URL (fallback when 3D is unavailable). */
435
+ imageUrl?: string;
436
+ /** 3D model data for AR/preview. */
437
+ model?: DishModel;
438
+ /** Whether the dish is currently available. */
439
+ available?: boolean;
440
+ /** Allergen tags (e.g. ["gluten", "dairy"]). */
441
+ allergens?: string[];
442
+ /** Sort order. */
443
+ sortOrder?: number;
444
+ }
445
+ /** Response wrapper for a single dish. */
446
+ interface DishResponse {
447
+ dish: Dish;
448
+ }
449
+ /** Response wrapper for dish listing. */
450
+ interface DishListResponse {
451
+ dishes: Dish[];
452
+ count: number;
453
+ }
398
454
 
399
455
  /**
400
456
  * Resira SDK client.
@@ -550,6 +606,26 @@ declare class Resira {
550
606
  * ```
551
607
  */
552
608
  listProducts(): Promise<ProductListResponse>;
609
+ /**
610
+ * List all dishes with 3D model data for the organisation.
611
+ *
612
+ * Returns dishes with name, description, price, image, and
613
+ * 3D model URLs for AR/preview rendering.
614
+ *
615
+ * ```ts
616
+ * const { dishes } = await resira.listDishes();
617
+ * ```
618
+ */
619
+ listDishes(): Promise<DishListResponse>;
620
+ /**
621
+ * Get a single dish by ID.
622
+ *
623
+ * ```ts
624
+ * const { dish } = await resira.getDish("dish-uuid");
625
+ * console.log(dish.name, dish.model?.glbUrl);
626
+ * ```
627
+ */
628
+ getDish(dishId: string): Promise<DishResponse>;
553
629
  /**
554
630
  * Create a Stripe payment intent for a booking.
555
631
  *
@@ -671,4 +747,4 @@ declare class ResiraNetworkError extends ResiraError {
671
747
  constructor(message: string, cause: unknown);
672
748
  }
673
749
 
674
- export { type ApiErrorBody, type Availability, type AvailabilityParams, type ConfirmPaymentRequest, type ConfirmPaymentResponse, type CreatePaymentIntentRequest, type CreateReservationRequest, type DateAvailability, 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 TimeSlotAvailability, type ValidatePromoCodeResponse, type WeightedBaseUrl };
750
+ 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 };
package/dist/index.js CHANGED
@@ -565,6 +565,36 @@ var Resira = class {
565
565
  throw err;
566
566
  }
567
567
  }
568
+ /**
569
+ * List all dishes with 3D model data for the organisation.
570
+ *
571
+ * Returns dishes with name, description, price, image, and
572
+ * 3D model URLs for AR/preview rendering.
573
+ *
574
+ * ```ts
575
+ * const { dishes } = await resira.listDishes();
576
+ * ```
577
+ */
578
+ async listDishes() {
579
+ return this.request("GET", "/dishes");
580
+ }
581
+ /**
582
+ * Get a single dish by ID.
583
+ *
584
+ * ```ts
585
+ * const { dish } = await resira.getDish("dish-uuid");
586
+ * console.log(dish.name, dish.model?.glbUrl);
587
+ * ```
588
+ */
589
+ async getDish(dishId) {
590
+ if (!dishId) {
591
+ throw new Error("dishId is required for getDish");
592
+ }
593
+ return this.request(
594
+ "GET",
595
+ `/dishes/${encodeURIComponent(dishId)}`
596
+ );
597
+ }
568
598
  /**
569
599
  * Create a Stripe payment intent for a booking.
570
600
  *
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\";\n\n// ═══════════════════════════════════════════════════════════════\n// Base error\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Base error class for all SDK errors.\n *\n * Use `instanceof` checks to distinguish error types:\n *\n * ```ts\n * try { … } catch (e) {\n * if (e instanceof ResiraRateLimitError) { … }\n * if (e instanceof ResiraApiError) { … }\n * if (e instanceof ResiraNetworkError) { … }\n * }\n * ```\n */\nexport class ResiraError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ResiraError\";\n // Fix prototype chain for instanceof to work after transpilation\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// API error (4xx / 5xx with a parsed body)\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Thrown when the API returns a non-2xx response with a JSON body.\n *\n * ```ts\n * err.status // 400, 404, 409, 422, 500, …\n * err.code // HTTP status text (e.g. \"Not Found\")\n * err.body // Parsed `{ error: \"…\" }` from the server\n * ```\n */\nexport class ResiraApiError extends ResiraError {\n /** HTTP status code. */\n readonly status: number;\n /** HTTP status text (e.g. `\"Bad Request\"`). */\n readonly code: string;\n /** Parsed error body from the API. */\n readonly body: ApiErrorBody;\n /** The full `Response` object (headers are still accessible). */\n readonly response: Response;\n\n constructor(status: number, code: string, body: ApiErrorBody, response: Response) {\n super(body.error || `API error ${status}`);\n this.name = \"ResiraApiError\";\n this.status = status;\n this.code = code;\n this.body = body;\n this.response = response;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n /** Whether this error is retryable (5xx or 429). */\n get retryable(): boolean {\n return this.status === 429 || this.status >= 500;\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Rate limit error (429)\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Thrown when the API returns 429 Too Many Requests.\n *\n * ```ts\n * err.retryAfter // Seconds until the client should retry\n * ```\n */\nexport class ResiraRateLimitError extends ResiraApiError {\n /** Seconds to wait before retrying (from `Retry-After` header). */\n readonly retryAfter: number;\n\n constructor(body: ApiErrorBody, response: Response) {\n super(429, \"Too Many Requests\", body, response);\n this.name = \"ResiraRateLimitError\";\n this.retryAfter =\n body.retryAfter ??\n (Number.parseInt(response.headers.get(\"retry-after\") ?? \"60\", 10) ||\n 60);\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Network error (fetch failed, DNS, timeout, etc.)\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Thrown when the request never reached the server.\n *\n * This wraps the underlying `TypeError` or `DOMException` from\n * `fetch()` failures (DNS resolution, TLS, network offline, etc.).\n */\nexport class ResiraNetworkError extends ResiraError {\n /** The original error thrown by `fetch`. */\n readonly cause: unknown;\n\n constructor(message: string, cause: unknown) {\n super(message);\n this.name = \"ResiraNetworkError\";\n this.cause = cause;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import type { ResiraConfig, WeightedBaseUrl } from \"./types.js\";\n\ninterface ResolvedBaseUrl {\n url: string;\n weight: number;\n}\n\ninterface StickySelection {\n signature: string;\n url: string;\n assignedAt: string;\n}\n\ninterface RoutingStatsEntry {\n lastAssignedAt: string;\n lastOrigin: string;\n selections: number;\n origins: Record<string, number>;\n}\n\ntype AnalyticsWindow = Window & {\n va?: (...args: unknown[]) => void;\n vaq?: unknown[][];\n};\n\nconst DEFAULT_LOCAL_BASE_URLS: ResolvedBaseUrl[] = [\n { url: \"http://localhost:3001\", weight: 100 },\n];\n\nconst DEFAULT_PRODUCTION_BASE_URLS: ResolvedBaseUrl[] = [\n { url: \"https://resira-api-sips-production.up.railway.app\", weight: 50 },\n { url: \"https://api.resira.app\", weight: 50 },\n];\n\nconst STICKY_SELECTION_KEY = \"resira_sdk_api_origin_sticky_v1\";\nconst ROUTING_STATS_KEY = \"resira_sdk_api_routing_stats_v1\";\n\nconst stickySelectionCache = new Map<string, StickySelection>();\n\nfunction readEnv(name: string): string | undefined {\n if (typeof process === \"undefined\") return undefined;\n return process.env?.[name];\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\";\n}\n\nfunction getStorage(type: \"session\" | \"local\"): Storage | null {\n if (!isBrowser()) return null;\n\n try {\n return type === \"session\" ? window.sessionStorage : window.localStorage;\n } catch {\n return null;\n }\n}\n\nfunction getBaseUrlSignature(baseUrls: ResolvedBaseUrl[]): string {\n return baseUrls.map((entry) => `${entry.url}|${entry.weight}`).join(\",\");\n}\n\nfunction pickWeightedBaseUrl(baseUrls: ResolvedBaseUrl[]): ResolvedBaseUrl {\n const totalWeight = baseUrls.reduce((sum, entry) => sum + entry.weight, 0);\n if (totalWeight <= 0) {\n return DEFAULT_PRODUCTION_BASE_URLS[0];\n }\n\n let cursor = Math.random() * totalWeight;\n for (const entry of baseUrls) {\n cursor -= entry.weight;\n if (cursor < 0) {\n return entry;\n }\n }\n\n return baseUrls[baseUrls.length - 1] ?? DEFAULT_PRODUCTION_BASE_URLS[0];\n}\n\nfunction ensureAnalyticsQueue(): AnalyticsWindow | null {\n if (!isBrowser()) return null;\n\n const analyticsWindow = window as AnalyticsWindow;\n if (!analyticsWindow.va) {\n analyticsWindow.va = (...params: unknown[]) => {\n analyticsWindow.vaq = analyticsWindow.vaq || [];\n analyticsWindow.vaq.push(params);\n };\n }\n\n return analyticsWindow;\n}\n\nfunction readStickySelection(signature: string): StickySelection | null {\n const cached = stickySelectionCache.get(signature);\n if (cached) return cached;\n\n const storage = getStorage(\"session\");\n const raw = storage?.getItem(STICKY_SELECTION_KEY);\n if (!raw) return null;\n\n try {\n const parsed = JSON.parse(raw) as StickySelection;\n if (!parsed?.signature || !parsed?.url || parsed.signature !== signature) {\n return null;\n }\n stickySelectionCache.set(signature, parsed);\n return parsed;\n } catch {\n return null;\n }\n}\n\nfunction persistStickySelection(signature: string, url: string): void {\n const selection: StickySelection = {\n signature,\n url,\n assignedAt: new Date().toISOString(),\n };\n\n stickySelectionCache.set(signature, selection);\n getStorage(\"session\")?.setItem(STICKY_SELECTION_KEY, JSON.stringify(selection));\n}\n\nfunction recordRoutingStats(signature: string, entry: ResolvedBaseUrl): void {\n const storage = getStorage(\"local\");\n if (!storage) return;\n\n let stats: Record<string, RoutingStatsEntry> = {};\n try {\n stats = JSON.parse(storage.getItem(ROUTING_STATS_KEY) ?? \"{}\") as Record<string, RoutingStatsEntry>;\n } catch {\n stats = {};\n }\n\n const current = stats[signature] ?? {\n lastAssignedAt: \"\",\n lastOrigin: \"\",\n selections: 0,\n origins: {},\n };\n\n current.lastAssignedAt = new Date().toISOString();\n current.lastOrigin = entry.url;\n current.selections += 1;\n current.origins[entry.url] = (current.origins[entry.url] ?? 0) + 1;\n\n stats[signature] = current;\n storage.setItem(ROUTING_STATS_KEY, JSON.stringify(stats));\n}\n\nfunction emitRoutingSelection(entry: ResolvedBaseUrl, signature: string, totalWeight: number): void {\n if (!isBrowser()) return;\n\n const detail = {\n source: \"sdk\",\n strategy: \"session-sticky\",\n originHost: new URL(entry.url).host,\n originUrl: entry.url,\n originWeight: entry.weight,\n totalWeight,\n signature,\n };\n\n ensureAnalyticsQueue()?.va?.(\"event\", {\n name: \"api_origin_selected\",\n data: detail,\n });\n\n window.dispatchEvent(\n new CustomEvent(\"resira:api-origin-selected\", {\n detail,\n }),\n );\n\n const debugEnabled = /^(1|true)$/i.test(\n readEnv(\"NEXT_PUBLIC_RESIRA_API_ROUTING_DEBUG\") ?? readEnv(\"NEXT_PUBLIC_API_ROUTING_DEBUG\") ?? \"\",\n );\n if (debugEnabled) {\n console.info(\"[resira-sdk] sticky origin selected\", detail);\n }\n}\n\nfunction normalizeUrl(url: string): string {\n return url.trim().replace(/\\/+$/, \"\");\n}\n\nfunction isPositiveNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0;\n}\n\nfunction toResolvedBaseUrl(value: string | WeightedBaseUrl): ResolvedBaseUrl | null {\n if (typeof value === \"string\") {\n const url = normalizeUrl(value);\n return url ? { url, weight: 1 } : null;\n }\n\n const url = normalizeUrl(value.url);\n if (!url) return null;\n\n return {\n url,\n weight: isPositiveNumber(value.weight) ? value.weight : 1,\n };\n}\n\nfunction parseJsonBaseUrls(value: string): ResolvedBaseUrl[] {\n const parsed = JSON.parse(value) as unknown;\n if (!Array.isArray(parsed)) return [];\n\n return parsed.flatMap((entry) => {\n const resolved =\n typeof entry === \"string\"\n ? toResolvedBaseUrl(entry)\n : entry && typeof entry === \"object\"\n ? toResolvedBaseUrl(entry as WeightedBaseUrl)\n : null;\n\n return resolved ? [resolved] : [];\n });\n}\n\nfunction parseDelimitedBaseUrls(value: string): ResolvedBaseUrl[] {\n return value\n .split(\",\")\n .flatMap((entry) => {\n const [rawUrl, rawWeight] = entry.split(\"|\");\n const url = normalizeUrl(rawUrl ?? \"\");\n if (!url) return [];\n\n const parsedWeight = rawWeight ? Number(rawWeight.trim()) : 1;\n return [{\n url,\n weight: isPositiveNumber(parsedWeight) ? parsedWeight : 1,\n }];\n });\n}\n\nfunction parseEnvBaseUrls(value: string | undefined): ResolvedBaseUrl[] {\n if (!value) return [];\n\n try {\n const parsed = parseJsonBaseUrls(value);\n if (parsed.length > 0) return parsed;\n } catch {\n // Fall back to delimiter parsing.\n }\n\n return parseDelimitedBaseUrls(value);\n}\n\nexport function resolveBaseUrls(config: ResiraConfig): ResolvedBaseUrl[] {\n if (config.baseUrl) {\n return [{ url: normalizeUrl(config.baseUrl), weight: 100 }];\n }\n\n if (config.baseUrls?.length) {\n const explicit = config.baseUrls.flatMap((entry) => {\n const resolved = toResolvedBaseUrl(entry);\n return resolved ? [resolved] : [];\n });\n\n if (explicit.length > 0) return explicit;\n }\n\n // Intentionally ignore ambient host-app env vars here.\n // Widgets using the SDK should not inherit a parent app's\n // NEXT_PUBLIC_API_URL / endpoint overrides by accident.\n // Only explicit SDK config (baseUrl/baseUrls) may override\n // the built-in routing defaults.\n\n if (readEnv(\"NODE_ENV\") === \"development\") {\n return DEFAULT_LOCAL_BASE_URLS;\n }\n\n return DEFAULT_PRODUCTION_BASE_URLS;\n}\n\nexport function pickBaseUrl(baseUrls: ResolvedBaseUrl[]): string {\n const signature = getBaseUrlSignature(baseUrls);\n const stickySelection = readStickySelection(signature);\n const stickyEntry = stickySelection\n ? baseUrls.find((entry) => entry.url === stickySelection.url)\n : null;\n if (stickyEntry) {\n return stickyEntry.url;\n }\n\n const selectedEntry = pickWeightedBaseUrl(baseUrls);\n persistStickySelection(signature, selectedEntry.url);\n recordRoutingStats(signature, selectedEntry);\n emitRoutingSelection(\n selectedEntry,\n signature,\n baseUrls.reduce((sum, entry) => sum + entry.weight, 0),\n );\n\n return selectedEntry.url;\n}\n\nexport function getRoutingStats(): Record<string, RoutingStatsEntry> {\n const storage = getStorage(\"local\");\n if (!storage) return {};\n\n try {\n return JSON.parse(storage.getItem(ROUTING_STATS_KEY) ?? \"{}\") as Record<string, RoutingStatsEntry>;\n } catch {\n return {};\n }\n}","import { ResiraApiError, ResiraNetworkError, ResiraRateLimitError } from \"./errors.js\";\nimport { pickBaseUrl, resolveBaseUrls } from \"./routing.js\";\nimport type {\n ApiErrorBody,\n Availability,\n AvailabilityParams,\n ConfirmPaymentRequest,\n ConfirmPaymentResponse,\n CreatePaymentIntentRequest,\n CreateReservationRequest,\n ListReservationsParams,\n PaginatedReservations,\n PaymentIntentResponse,\n ProductListResponse,\n PropertyConfig,\n Reservation,\n ReservationResponse,\n ResiraConfig,\n ResourceListResponse,\n ValidatePromoCodeResponse,\n} from \"./types.js\";\n\n// ═══════════════════════════════════════════════════════════════\n// Constants\n// ═══════════════════════════════════════════════════════════════\n\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_RETRY_BASE_DELAY = 500; // ms\nconst API_PREFIX = \"/v1/public\";\n\n// ═══════════════════════════════════════════════════════════════\n// Helpers\n// ═══════════════════════════════════════════════════════════════\n\n/** Generate a random UUID v4 for idempotency keys. */\nfunction uuid(): string {\n // Use crypto.randomUUID when available (Node 19+, all modern browsers)\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n // Fallback: manual v4 UUID\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\n });\n}\n\n/** Build a query string from a flat params object. Skips `undefined` values. */\nfunction toQueryString(params: Record<string, string | number | boolean | undefined>): string {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);\n }\n }\n return parts.length > 0 ? `?${parts.join(\"&\")}` : \"\";\n}\n\n/** Sleep for `ms` milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Convert camelCase keys to snake_case for query parameters.\n * The API expects snake_case query params (`start_date`, `party_size`).\n */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/** Convert an object's keys from camelCase to snake_case. */\nfunction keysToSnake(\n obj: Record<string, string | number | boolean | undefined>,\n): Record<string, string | number | boolean | undefined> {\n const result: Record<string, string | number | boolean | undefined> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[camelToSnake(key)] = value;\n }\n return result;\n}\n\n/** Convert snake_case string to camelCase. */\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\n/** Deep-convert all keys in an object/array from camelCase to snake_case. */\nfunction deepKeysToSnake(obj: unknown): unknown {\n if (Array.isArray(obj)) return obj.map(deepKeysToSnake);\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[camelToSnake(key)] = deepKeysToSnake(value);\n }\n return result;\n }\n return obj;\n}\n\n/** Deep-convert all keys in an object/array from snake_case to camelCase. */\nfunction deepKeysToCamel(obj: unknown): unknown {\n if (Array.isArray(obj)) return obj.map(deepKeysToCamel);\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[snakeToCamel(key)] = deepKeysToCamel(value);\n }\n return result;\n }\n return obj;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Client\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Resira SDK client.\n *\n * ```ts\n * const resira = new Resira({\n * apiKey: \"resira_live_abc123…\",\n * baseUrl: \"https://api.example.com\", // optional\n * });\n *\n * const availability = await resira.getAvailability(\"prop-1\", {\n * startDate: \"2026-07-01\",\n * endDate: \"2026-07-07\",\n * });\n * ```\n */\nexport class Resira {\n private readonly apiKey: string;\n private readonly baseUrls: ReturnType<typeof resolveBaseUrls>;\n private readonly maxRetries: number;\n private readonly retryBaseDelay: number;\n private readonly _fetch: typeof globalThis.fetch;\n\n constructor(config: ResiraConfig) {\n if (!config.apiKey) {\n throw new Error(\"Resira: apiKey is required\");\n }\n this.apiKey = config.apiKey;\n this.baseUrls = resolveBaseUrls(config);\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\n this.retryBaseDelay = config.retryBaseDelay ?? DEFAULT_RETRY_BASE_DELAY;\n this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n // ─────────────────────────────────────────────────────────────\n // Public API methods\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Fetch the public property configuration.\n *\n * Returns non-sensitive settings such as the Stripe publishable\n * key, deposit percentage, currency, and branding info.\n *\n * ```ts\n * const config = await resira.getConfig();\n * console.log(config.stripePublishableKey); // \"pk_test_…\"\n * ```\n */\n async getConfig(): Promise<PropertyConfig> {\n return this.request<PropertyConfig>(\"GET\", \"/config\");\n }\n\n /**\n * Validate a promo/discount code.\n *\n * Returns discount details if valid, or an error message if not.\n *\n * ```ts\n * const result = await resira.validatePromoCode(\"SUMMER20\");\n * if (result.valid) {\n * console.log(result.discountType, result.discountValue);\n * }\n * ```\n */\n async validatePromoCode(code: string): Promise<ValidatePromoCodeResponse> {\n try {\n const raw = await this.request<Record<string, unknown>>(\n \"POST\",\n \"/promo-codes/validate\",\n { code },\n );\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\n return {\n valid: true,\n discountType: data.discountType as \"percent\" | \"fixed\" | undefined,\n discountValue: data.discountValue as number | undefined,\n currency: data.currency as string | undefined,\n minOrderCents: data.minOrderCents as number | undefined,\n };\n } catch (err: unknown) {\n // 4xx errors mean invalid code\n if (\n err &&\n typeof err === \"object\" &&\n \"status\" in err &&\n (err as { status: number }).status < 500\n ) {\n const apiErr = err as { body?: { error?: string } };\n return {\n valid: false,\n error: apiErr.body?.error ?? \"Invalid promo code\",\n };\n }\n throw err;\n }\n }\n\n /**\n * List all active resources for the organization.\n *\n * Returns resource cards with name, description, price, duration,\n * image, and type — suitable for building a resource picker.\n *\n * ```ts\n * const { resources } = await resira.listResources();\n * ```\n */\n async listResources(): Promise<ResourceListResponse> {\n return this.request<ResourceListResponse>(\"GET\", \"/resources\");\n }\n\n /**\n * Query availability for a resource.\n *\n * **Properties** (date-based):\n * ```ts\n * await resira.getAvailability(\"prop-1\", {\n * startDate: \"2026-07-01\",\n * endDate: \"2026-07-07\",\n * });\n * ```\n *\n * **Restaurants** (time-slot):\n * ```ts\n * await resira.getAvailability(\"table-5\", {\n * date: \"2026-07-01\",\n * partySize: 4,\n * });\n * ```\n *\n * Omit params to get all blocked dates (for calendar rendering).\n */\n async getAvailability(\n resourceId: string,\n params: AvailabilityParams = {},\n ): Promise<Availability> {\n if (!resourceId) {\n throw new Error(\"resourceId is required for getAvailability\");\n }\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\n return this.request<Availability>(\n \"GET\",\n `/resources/${encodeURIComponent(resourceId)}/availability${qs}`,\n );\n }\n\n /**\n * Query availability for a product/service.\n *\n * Aggregates capacity across all linked equipment. If a product\n * has 2 jet skis (each capacity=1), slots show capacity: 2.\n * Both pending and confirmed reservations count as occupied.\n *\n * ```ts\n * await resira.getProductAvailability(\"prod-123\", {\n * date: \"2026-07-01\",\n * durationMinutes: 30,\n * });\n * ```\n */\n async getProductAvailability(\n productId: string,\n params: AvailabilityParams = {},\n ): Promise<Availability> {\n if (!productId) {\n throw new Error(\"productId is required for getProductAvailability\");\n }\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\n return this.request<Availability>(\n \"GET\",\n `/products/${encodeURIComponent(productId)}/availability${qs}`,\n );\n }\n\n /**\n * Create a reservation.\n *\n * Uses `POST /v2/api/reservations` — the `resourceId` is sent in\n * the request body, **not** in the URL path.\n *\n * An `Idempotency-Key` header is automatically attached so that\n * retries (including those from exponential backoff) are safe.\n *\n * ```ts\n * const { reservation } = await resira.createReservation({\n * resourceId: \"prop-1\",\n * guestName: \"Jane Doe\",\n * guestEmail: \"jane@example.com\",\n * startDate: \"2026-07-01\",\n * endDate: \"2026-07-07\",\n * partySize: 3,\n * });\n * ```\n */\n async createReservation(\n payload: CreateReservationRequest,\n options?: { idempotencyKey?: string },\n ): Promise<ReservationResponse> {\n if (!payload.resourceId) {\n throw new Error(\"resourceId is required for createReservation\");\n }\n const idempotencyKey = options?.idempotencyKey ?? uuid();\n\n // Note: uses /v2/api prefix, not /v1/public\n const url = `${this.getBaseUrl()}/v2/api/reservations`;\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"Idempotency-Key\": idempotencyKey,\n };\n\n const init: RequestInit = {\n method: \"POST\",\n headers,\n body: JSON.stringify(payload),\n };\n\n let lastError: ResiraApiError | ResiraNetworkError | ResiraRateLimitError | undefined;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n if (attempt > 0) {\n const backoff = this.calculateBackoff(attempt, lastError);\n await sleep(backoff);\n }\n\n let response: Response;\n try {\n response = await this._fetch(url, init);\n } catch (err) {\n lastError = new ResiraNetworkError(\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\n err,\n );\n continue;\n }\n\n if (response.ok) {\n const raw = (await response.json()) as Record<string, unknown>;\n // The API may nest under \"reservation\" or \"booking\" depending on endpoint\n const resData = (raw.reservation ?? raw.booking ?? {}) as Record<string, unknown>;\n\n // Augment with request data: API response doesn't include\n // startTime/endTime/startDate/endDate/resourceId for v2 reservations\n const reservation: Reservation = {\n id: (resData.id as string) ?? \"\",\n resourceId: (resData.resourceId as string) ?? payload.resourceId,\n status: (resData.status as string) ?? \"pending\",\n startDate: (resData.startDate as string) ?? payload.startDate,\n endDate: (resData.endDate as string) ?? payload.endDate,\n startTime: (resData.startTime as string) ?? payload.startTime,\n endTime: (resData.endTime as string) ?? payload.endTime,\n startDatetime: (resData.startDatetime as string) ?? payload.startTime,\n endDatetime: (resData.endDatetime as string) ?? payload.endTime,\n guestName: (resData.guestName as string) ?? payload.guestName,\n partySize: (resData.partySize as number) ?? (resData.guests as number) ?? payload.partySize ?? 2,\n totalPrice: (resData.totalPrice as number) ?? 0,\n currency: (resData.currency as string) ?? \"EUR\",\n createdAt: (resData.createdAt as string) ?? new Date().toISOString(),\n };\n\n return { reservation };\n }\n\n let errorBody: ApiErrorBody;\n try {\n errorBody = (await response.json()) as ApiErrorBody;\n } catch {\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\n }\n\n if (response.status === 429) {\n lastError = new ResiraRateLimitError(errorBody, response);\n continue;\n }\n\n if (response.status >= 500) {\n lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);\n continue;\n }\n\n throw new ResiraApiError(response.status, response.statusText, errorBody, response);\n }\n\n throw lastError!;\n }\n\n /**\n * List reservations for a resource (paginated).\n *\n * ```ts\n * const page = await resira.listReservations(\"prop-1\", {\n * status: \"confirmed\",\n * page: 1,\n * limit: 50,\n * });\n * console.log(page.data); // Reservation[]\n * console.log(page.totalPages); // number\n * ```\n */\n async listReservations(\n resourceId: string,\n params: ListReservationsParams = {},\n ): Promise<PaginatedReservations> {\n const qs = toQueryString(params as Record<string, string | number | undefined>);\n return this.request<PaginatedReservations>(\n \"GET\",\n `/resources/${encodeURIComponent(resourceId)}/reservations${qs}`,\n );\n }\n\n /**\n * Get a single reservation by ID.\n *\n * ```ts\n * const { reservation } = await resira.getReservation(\"prop-1\", \"res-uuid\");\n * ```\n */\n async getReservation(\n resourceId: string,\n reservationId: string,\n ): Promise<ReservationResponse> {\n return this.request<ReservationResponse>(\n \"GET\",\n `/resources/${encodeURIComponent(resourceId)}/reservations/${encodeURIComponent(reservationId)}`,\n );\n }\n\n /**\n * List all active products/services for the organisation.\n *\n * Calls `GET /v1/public/products` which returns products with\n * pricing, duration, and linked equipment.\n *\n * Falls back to mapping resources → products if the endpoint\n * returns 404 (backend hasn't been updated yet).\n *\n * ```ts\n * const { products } = await resira.listProducts();\n * ```\n */\n async listProducts(): Promise<ProductListResponse> {\n try {\n return await this.request<ProductListResponse>(\"GET\", \"/products\");\n } catch (err: unknown) {\n // Fallback: if /products endpoint doesn't exist yet (404),\n // map resources → products shape\n if (\n err &&\n typeof err === \"object\" &&\n \"status\" in err &&\n (err as { status: number }).status === 404\n ) {\n const resourceResponse = await this.listResources();\n const products = resourceResponse.resources.map((r) => ({\n id: r.id,\n name: r.name,\n description: r.description,\n durationMinutes: r.durationMinutes ?? 60,\n priceCents: r.pricePerSession ?? 0,\n currency: r.currency ?? \"EUR\",\n imageUrl: r.imageUrl,\n active: r.active,\n sortOrder: 0,\n pricingModel: \"per_session\" as const,\n equipmentIds: [r.id],\n equipmentNames: [r.name],\n }));\n return { products, count: products.length };\n }\n throw err;\n }\n }\n\n /**\n * Create a Stripe payment intent for a booking.\n *\n * The server creates the payment intent, calculates the amount,\n * and returns a `clientSecret` to confirm payment on the frontend\n * using Stripe Elements.\n *\n * ```ts\n * const { clientSecret, amountNow, amountAtVenue } =\n * await resira.createPaymentIntent({\n * productId: \"prod-123\",\n * resourceId: \"res-456\",\n * partySize: 2,\n * startDate: \"2026-07-01\",\n * startTime: \"2026-07-01T10:00:00Z\",\n * endTime: \"2026-07-01T11:00:00Z\",\n * guestName: \"Jane Doe\",\n * guestEmail: \"jane@example.com\",\n * });\n * ```\n */\n async createPaymentIntent(\n payload: CreatePaymentIntentRequest,\n options?: { idempotencyKey?: string },\n ): Promise<PaymentIntentResponse> {\n const idempotencyKey = options?.idempotencyKey ?? uuid();\n const raw = await this.request<Record<string, unknown>>(\n \"POST\",\n \"/payments/create-intent\",\n payload,\n { \"Idempotency-Key\": idempotencyKey },\n );\n // Normalize response keys to camelCase\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\n // Map backend field names to SDK field names:\n // backend: amountDue, totalPrice\n // SDK: amountNow, totalAmount, amountAtVenue\n const totalAmount = (data.totalAmount as number) ?? (data.totalPrice as number) ?? 0;\n const amountNow = (data.amountNow as number) ?? (data.amountDue as number) ?? totalAmount;\n const amountAtVenue = (data.amountAtVenue as number) ?? (totalAmount - amountNow);\n return {\n clientSecret: data.clientSecret as string,\n paymentIntentId: data.paymentIntentId as string,\n amountNow,\n amountAtVenue,\n totalAmount,\n currency: (data.currency as string) ?? \"EUR\",\n paymentOption: data.paymentOption as string | undefined,\n reservationId: data.reservationId as string | undefined,\n discountAmount: (data.discountAmount as number) ?? undefined,\n originalPrice: (data.originalPrice as number) ?? undefined,\n promoCodeApplied: (data.promoCodeApplied as string) ?? undefined,\n };\n }\n\n /**\n * Confirm that a payment was successful after the guest\n * completes the Stripe payment flow.\n *\n * This transitions the reservation from `pending` to `confirmed`.\n *\n * ```ts\n * const { reservation, paymentStatus } =\n * await resira.confirmPayment({\n * paymentIntentId: \"pi_xxx\",\n * reservationId: \"res-uuid\",\n * });\n * ```\n */\n async confirmPayment(\n payload: ConfirmPaymentRequest,\n ): Promise<ConfirmPaymentResponse> {\n const raw = await this.request<Record<string, unknown>>(\n \"POST\",\n \"/payments/confirm\",\n payload,\n );\n // Normalize response keys to camelCase in case backend returns snake_case\n return deepKeysToCamel(raw) as ConfirmPaymentResponse;\n }\n\n // ─────────────────────────────────────────────────────────────\n // Internal HTTP layer\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Execute an HTTP request with retry logic and error normalization.\n *\n * - Attaches `Authorization: Bearer <apiKey>` on every request.\n * - Retries on 429 and 5xx with exponential backoff + jitter.\n * - Parses error bodies and throws typed error classes.\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n const url = `${this.getBaseUrl()}${API_PREFIX}${path}`;\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n ...extraHeaders,\n };\n\n if (body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const init: RequestInit = {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n };\n\n let lastError: ResiraApiError | ResiraNetworkError | undefined;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n // ── Wait before retry ────────────────────────────────\n if (attempt > 0) {\n const backoff = this.calculateBackoff(attempt, lastError);\n await sleep(backoff);\n }\n\n // ── Execute the request ──────────────────────────────\n let response: Response;\n try {\n response = await this._fetch(url, init);\n } catch (err) {\n lastError = new ResiraNetworkError(\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\n err,\n );\n // Network errors are always retryable\n continue;\n }\n\n // ── Success ──────────────────────────────────────────\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n // ── Parse error body ─────────────────────────────────\n let errorBody: ApiErrorBody;\n try {\n errorBody = (await response.json()) as ApiErrorBody;\n } catch {\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\n }\n\n // ── Rate limit ───────────────────────────────────────\n if (response.status === 429) {\n lastError = new ResiraRateLimitError(errorBody, response);\n // Always retry 429s (up to maxRetries)\n continue;\n }\n\n // ── Server error (5xx) — retryable ───────────────────\n if (response.status >= 500) {\n lastError = new ResiraApiError(\n response.status,\n response.statusText,\n errorBody,\n response,\n );\n continue;\n }\n\n // ── Client error (4xx) — NOT retryable ───────────────\n throw new ResiraApiError(\n response.status,\n response.statusText,\n errorBody,\n response,\n );\n }\n\n // All retries exhausted — throw the last error\n throw lastError!;\n }\n\n /**\n * Calculate backoff delay in milliseconds for a given retry attempt.\n *\n * Uses exponential backoff with full jitter:\n * delay = random(0, base * 2^attempt)\n *\n * For 429 responses, respects the `Retry-After` header as a minimum.\n */\n private calculateBackoff(\n attempt: number,\n lastError?: ResiraApiError | ResiraNetworkError,\n ): number {\n const exponential = this.retryBaseDelay * 2 ** (attempt - 1);\n const jittered = Math.random() * exponential;\n\n // If the server told us to wait, use that as the floor\n if (lastError instanceof ResiraRateLimitError) {\n const serverDelay = lastError.retryAfter * 1000;\n return Math.max(serverDelay, jittered);\n }\n\n return jittered;\n }\n\n private getBaseUrl(): string {\n return pickBaseUrl(this.baseUrls);\n }\n}\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\";\n\n// ═══════════════════════════════════════════════════════════════\n// Base error\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Base error class for all SDK errors.\n *\n * Use `instanceof` checks to distinguish error types:\n *\n * ```ts\n * try { … } catch (e) {\n * if (e instanceof ResiraRateLimitError) { … }\n * if (e instanceof ResiraApiError) { … }\n * if (e instanceof ResiraNetworkError) { … }\n * }\n * ```\n */\nexport class ResiraError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ResiraError\";\n // Fix prototype chain for instanceof to work after transpilation\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// API error (4xx / 5xx with a parsed body)\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Thrown when the API returns a non-2xx response with a JSON body.\n *\n * ```ts\n * err.status // 400, 404, 409, 422, 500, …\n * err.code // HTTP status text (e.g. \"Not Found\")\n * err.body // Parsed `{ error: \"…\" }` from the server\n * ```\n */\nexport class ResiraApiError extends ResiraError {\n /** HTTP status code. */\n readonly status: number;\n /** HTTP status text (e.g. `\"Bad Request\"`). */\n readonly code: string;\n /** Parsed error body from the API. */\n readonly body: ApiErrorBody;\n /** The full `Response` object (headers are still accessible). */\n readonly response: Response;\n\n constructor(status: number, code: string, body: ApiErrorBody, response: Response) {\n super(body.error || `API error ${status}`);\n this.name = \"ResiraApiError\";\n this.status = status;\n this.code = code;\n this.body = body;\n this.response = response;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n /** Whether this error is retryable (5xx or 429). */\n get retryable(): boolean {\n return this.status === 429 || this.status >= 500;\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Rate limit error (429)\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Thrown when the API returns 429 Too Many Requests.\n *\n * ```ts\n * err.retryAfter // Seconds until the client should retry\n * ```\n */\nexport class ResiraRateLimitError extends ResiraApiError {\n /** Seconds to wait before retrying (from `Retry-After` header). */\n readonly retryAfter: number;\n\n constructor(body: ApiErrorBody, response: Response) {\n super(429, \"Too Many Requests\", body, response);\n this.name = \"ResiraRateLimitError\";\n this.retryAfter =\n body.retryAfter ??\n (Number.parseInt(response.headers.get(\"retry-after\") ?? \"60\", 10) ||\n 60);\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Network error (fetch failed, DNS, timeout, etc.)\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Thrown when the request never reached the server.\n *\n * This wraps the underlying `TypeError` or `DOMException` from\n * `fetch()` failures (DNS resolution, TLS, network offline, etc.).\n */\nexport class ResiraNetworkError extends ResiraError {\n /** The original error thrown by `fetch`. */\n readonly cause: unknown;\n\n constructor(message: string, cause: unknown) {\n super(message);\n this.name = \"ResiraNetworkError\";\n this.cause = cause;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import type { ResiraConfig, WeightedBaseUrl } from \"./types.js\";\n\ninterface ResolvedBaseUrl {\n url: string;\n weight: number;\n}\n\ninterface StickySelection {\n signature: string;\n url: string;\n assignedAt: string;\n}\n\ninterface RoutingStatsEntry {\n lastAssignedAt: string;\n lastOrigin: string;\n selections: number;\n origins: Record<string, number>;\n}\n\ntype AnalyticsWindow = Window & {\n va?: (...args: unknown[]) => void;\n vaq?: unknown[][];\n};\n\nconst DEFAULT_LOCAL_BASE_URLS: ResolvedBaseUrl[] = [\n { url: \"http://localhost:3001\", weight: 100 },\n];\n\nconst DEFAULT_PRODUCTION_BASE_URLS: ResolvedBaseUrl[] = [\n { url: \"https://resira-api-sips-production.up.railway.app\", weight: 50 },\n { url: \"https://api.resira.app\", weight: 50 },\n];\n\nconst STICKY_SELECTION_KEY = \"resira_sdk_api_origin_sticky_v1\";\nconst ROUTING_STATS_KEY = \"resira_sdk_api_routing_stats_v1\";\n\nconst stickySelectionCache = new Map<string, StickySelection>();\n\nfunction readEnv(name: string): string | undefined {\n if (typeof process === \"undefined\") return undefined;\n return process.env?.[name];\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\";\n}\n\nfunction getStorage(type: \"session\" | \"local\"): Storage | null {\n if (!isBrowser()) return null;\n\n try {\n return type === \"session\" ? window.sessionStorage : window.localStorage;\n } catch {\n return null;\n }\n}\n\nfunction getBaseUrlSignature(baseUrls: ResolvedBaseUrl[]): string {\n return baseUrls.map((entry) => `${entry.url}|${entry.weight}`).join(\",\");\n}\n\nfunction pickWeightedBaseUrl(baseUrls: ResolvedBaseUrl[]): ResolvedBaseUrl {\n const totalWeight = baseUrls.reduce((sum, entry) => sum + entry.weight, 0);\n if (totalWeight <= 0) {\n return DEFAULT_PRODUCTION_BASE_URLS[0];\n }\n\n let cursor = Math.random() * totalWeight;\n for (const entry of baseUrls) {\n cursor -= entry.weight;\n if (cursor < 0) {\n return entry;\n }\n }\n\n return baseUrls[baseUrls.length - 1] ?? DEFAULT_PRODUCTION_BASE_URLS[0];\n}\n\nfunction ensureAnalyticsQueue(): AnalyticsWindow | null {\n if (!isBrowser()) return null;\n\n const analyticsWindow = window as AnalyticsWindow;\n if (!analyticsWindow.va) {\n analyticsWindow.va = (...params: unknown[]) => {\n analyticsWindow.vaq = analyticsWindow.vaq || [];\n analyticsWindow.vaq.push(params);\n };\n }\n\n return analyticsWindow;\n}\n\nfunction readStickySelection(signature: string): StickySelection | null {\n const cached = stickySelectionCache.get(signature);\n if (cached) return cached;\n\n const storage = getStorage(\"session\");\n const raw = storage?.getItem(STICKY_SELECTION_KEY);\n if (!raw) return null;\n\n try {\n const parsed = JSON.parse(raw) as StickySelection;\n if (!parsed?.signature || !parsed?.url || parsed.signature !== signature) {\n return null;\n }\n stickySelectionCache.set(signature, parsed);\n return parsed;\n } catch {\n return null;\n }\n}\n\nfunction persistStickySelection(signature: string, url: string): void {\n const selection: StickySelection = {\n signature,\n url,\n assignedAt: new Date().toISOString(),\n };\n\n stickySelectionCache.set(signature, selection);\n getStorage(\"session\")?.setItem(STICKY_SELECTION_KEY, JSON.stringify(selection));\n}\n\nfunction recordRoutingStats(signature: string, entry: ResolvedBaseUrl): void {\n const storage = getStorage(\"local\");\n if (!storage) return;\n\n let stats: Record<string, RoutingStatsEntry> = {};\n try {\n stats = JSON.parse(storage.getItem(ROUTING_STATS_KEY) ?? \"{}\") as Record<string, RoutingStatsEntry>;\n } catch {\n stats = {};\n }\n\n const current = stats[signature] ?? {\n lastAssignedAt: \"\",\n lastOrigin: \"\",\n selections: 0,\n origins: {},\n };\n\n current.lastAssignedAt = new Date().toISOString();\n current.lastOrigin = entry.url;\n current.selections += 1;\n current.origins[entry.url] = (current.origins[entry.url] ?? 0) + 1;\n\n stats[signature] = current;\n storage.setItem(ROUTING_STATS_KEY, JSON.stringify(stats));\n}\n\nfunction emitRoutingSelection(entry: ResolvedBaseUrl, signature: string, totalWeight: number): void {\n if (!isBrowser()) return;\n\n const detail = {\n source: \"sdk\",\n strategy: \"session-sticky\",\n originHost: new URL(entry.url).host,\n originUrl: entry.url,\n originWeight: entry.weight,\n totalWeight,\n signature,\n };\n\n ensureAnalyticsQueue()?.va?.(\"event\", {\n name: \"api_origin_selected\",\n data: detail,\n });\n\n window.dispatchEvent(\n new CustomEvent(\"resira:api-origin-selected\", {\n detail,\n }),\n );\n\n const debugEnabled = /^(1|true)$/i.test(\n readEnv(\"NEXT_PUBLIC_RESIRA_API_ROUTING_DEBUG\") ?? readEnv(\"NEXT_PUBLIC_API_ROUTING_DEBUG\") ?? \"\",\n );\n if (debugEnabled) {\n console.info(\"[resira-sdk] sticky origin selected\", detail);\n }\n}\n\nfunction normalizeUrl(url: string): string {\n return url.trim().replace(/\\/+$/, \"\");\n}\n\nfunction isPositiveNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0;\n}\n\nfunction toResolvedBaseUrl(value: string | WeightedBaseUrl): ResolvedBaseUrl | null {\n if (typeof value === \"string\") {\n const url = normalizeUrl(value);\n return url ? { url, weight: 1 } : null;\n }\n\n const url = normalizeUrl(value.url);\n if (!url) return null;\n\n return {\n url,\n weight: isPositiveNumber(value.weight) ? value.weight : 1,\n };\n}\n\nfunction parseJsonBaseUrls(value: string): ResolvedBaseUrl[] {\n const parsed = JSON.parse(value) as unknown;\n if (!Array.isArray(parsed)) return [];\n\n return parsed.flatMap((entry) => {\n const resolved =\n typeof entry === \"string\"\n ? toResolvedBaseUrl(entry)\n : entry && typeof entry === \"object\"\n ? toResolvedBaseUrl(entry as WeightedBaseUrl)\n : null;\n\n return resolved ? [resolved] : [];\n });\n}\n\nfunction parseDelimitedBaseUrls(value: string): ResolvedBaseUrl[] {\n return value\n .split(\",\")\n .flatMap((entry) => {\n const [rawUrl, rawWeight] = entry.split(\"|\");\n const url = normalizeUrl(rawUrl ?? \"\");\n if (!url) return [];\n\n const parsedWeight = rawWeight ? Number(rawWeight.trim()) : 1;\n return [{\n url,\n weight: isPositiveNumber(parsedWeight) ? parsedWeight : 1,\n }];\n });\n}\n\nfunction parseEnvBaseUrls(value: string | undefined): ResolvedBaseUrl[] {\n if (!value) return [];\n\n try {\n const parsed = parseJsonBaseUrls(value);\n if (parsed.length > 0) return parsed;\n } catch {\n // Fall back to delimiter parsing.\n }\n\n return parseDelimitedBaseUrls(value);\n}\n\nexport function resolveBaseUrls(config: ResiraConfig): ResolvedBaseUrl[] {\n if (config.baseUrl) {\n return [{ url: normalizeUrl(config.baseUrl), weight: 100 }];\n }\n\n if (config.baseUrls?.length) {\n const explicit = config.baseUrls.flatMap((entry) => {\n const resolved = toResolvedBaseUrl(entry);\n return resolved ? [resolved] : [];\n });\n\n if (explicit.length > 0) return explicit;\n }\n\n // Intentionally ignore ambient host-app env vars here.\n // Widgets using the SDK should not inherit a parent app's\n // NEXT_PUBLIC_API_URL / endpoint overrides by accident.\n // Only explicit SDK config (baseUrl/baseUrls) may override\n // the built-in routing defaults.\n\n if (readEnv(\"NODE_ENV\") === \"development\") {\n return DEFAULT_LOCAL_BASE_URLS;\n }\n\n return DEFAULT_PRODUCTION_BASE_URLS;\n}\n\nexport function pickBaseUrl(baseUrls: ResolvedBaseUrl[]): string {\n const signature = getBaseUrlSignature(baseUrls);\n const stickySelection = readStickySelection(signature);\n const stickyEntry = stickySelection\n ? baseUrls.find((entry) => entry.url === stickySelection.url)\n : null;\n if (stickyEntry) {\n return stickyEntry.url;\n }\n\n const selectedEntry = pickWeightedBaseUrl(baseUrls);\n persistStickySelection(signature, selectedEntry.url);\n recordRoutingStats(signature, selectedEntry);\n emitRoutingSelection(\n selectedEntry,\n signature,\n baseUrls.reduce((sum, entry) => sum + entry.weight, 0),\n );\n\n return selectedEntry.url;\n}\n\nexport function getRoutingStats(): Record<string, RoutingStatsEntry> {\n const storage = getStorage(\"local\");\n if (!storage) return {};\n\n try {\n return JSON.parse(storage.getItem(ROUTING_STATS_KEY) ?? \"{}\") as Record<string, RoutingStatsEntry>;\n } catch {\n return {};\n }\n}","import { ResiraApiError, ResiraNetworkError, ResiraRateLimitError } from \"./errors.js\";\nimport { pickBaseUrl, resolveBaseUrls } from \"./routing.js\";\nimport type {\n ApiErrorBody,\n Availability,\n AvailabilityParams,\n ConfirmPaymentRequest,\n ConfirmPaymentResponse,\n CreatePaymentIntentRequest,\n CreateReservationRequest,\n Dish,\n DishListResponse,\n DishResponse,\n ListReservationsParams,\n PaginatedReservations,\n PaymentIntentResponse,\n ProductListResponse,\n PropertyConfig,\n Reservation,\n ReservationResponse,\n ResiraConfig,\n ResourceListResponse,\n ValidatePromoCodeResponse,\n} from \"./types.js\";\n\n// ═══════════════════════════════════════════════════════════════\n// Constants\n// ═══════════════════════════════════════════════════════════════\n\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_RETRY_BASE_DELAY = 500; // ms\nconst API_PREFIX = \"/v1/public\";\n\n// ═══════════════════════════════════════════════════════════════\n// Helpers\n// ═══════════════════════════════════════════════════════════════\n\n/** Generate a random UUID v4 for idempotency keys. */\nfunction uuid(): string {\n // Use crypto.randomUUID when available (Node 19+, all modern browsers)\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n // Fallback: manual v4 UUID\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\n });\n}\n\n/** Build a query string from a flat params object. Skips `undefined` values. */\nfunction toQueryString(params: Record<string, string | number | boolean | undefined>): string {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);\n }\n }\n return parts.length > 0 ? `?${parts.join(\"&\")}` : \"\";\n}\n\n/** Sleep for `ms` milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Convert camelCase keys to snake_case for query parameters.\n * The API expects snake_case query params (`start_date`, `party_size`).\n */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/** Convert an object's keys from camelCase to snake_case. */\nfunction keysToSnake(\n obj: Record<string, string | number | boolean | undefined>,\n): Record<string, string | number | boolean | undefined> {\n const result: Record<string, string | number | boolean | undefined> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[camelToSnake(key)] = value;\n }\n return result;\n}\n\n/** Convert snake_case string to camelCase. */\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\n/** Deep-convert all keys in an object/array from camelCase to snake_case. */\nfunction deepKeysToSnake(obj: unknown): unknown {\n if (Array.isArray(obj)) return obj.map(deepKeysToSnake);\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[camelToSnake(key)] = deepKeysToSnake(value);\n }\n return result;\n }\n return obj;\n}\n\n/** Deep-convert all keys in an object/array from snake_case to camelCase. */\nfunction deepKeysToCamel(obj: unknown): unknown {\n if (Array.isArray(obj)) return obj.map(deepKeysToCamel);\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[snakeToCamel(key)] = deepKeysToCamel(value);\n }\n return result;\n }\n return obj;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Client\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Resira SDK client.\n *\n * ```ts\n * const resira = new Resira({\n * apiKey: \"resira_live_abc123…\",\n * baseUrl: \"https://api.example.com\", // optional\n * });\n *\n * const availability = await resira.getAvailability(\"prop-1\", {\n * startDate: \"2026-07-01\",\n * endDate: \"2026-07-07\",\n * });\n * ```\n */\nexport class Resira {\n private readonly apiKey: string;\n private readonly baseUrls: ReturnType<typeof resolveBaseUrls>;\n private readonly maxRetries: number;\n private readonly retryBaseDelay: number;\n private readonly _fetch: typeof globalThis.fetch;\n\n constructor(config: ResiraConfig) {\n if (!config.apiKey) {\n throw new Error(\"Resira: apiKey is required\");\n }\n this.apiKey = config.apiKey;\n this.baseUrls = resolveBaseUrls(config);\n this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\n this.retryBaseDelay = config.retryBaseDelay ?? DEFAULT_RETRY_BASE_DELAY;\n this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n // ─────────────────────────────────────────────────────────────\n // Public API methods\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Fetch the public property configuration.\n *\n * Returns non-sensitive settings such as the Stripe publishable\n * key, deposit percentage, currency, and branding info.\n *\n * ```ts\n * const config = await resira.getConfig();\n * console.log(config.stripePublishableKey); // \"pk_test_…\"\n * ```\n */\n async getConfig(): Promise<PropertyConfig> {\n return this.request<PropertyConfig>(\"GET\", \"/config\");\n }\n\n /**\n * Validate a promo/discount code.\n *\n * Returns discount details if valid, or an error message if not.\n *\n * ```ts\n * const result = await resira.validatePromoCode(\"SUMMER20\");\n * if (result.valid) {\n * console.log(result.discountType, result.discountValue);\n * }\n * ```\n */\n async validatePromoCode(code: string): Promise<ValidatePromoCodeResponse> {\n try {\n const raw = await this.request<Record<string, unknown>>(\n \"POST\",\n \"/promo-codes/validate\",\n { code },\n );\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\n return {\n valid: true,\n discountType: data.discountType as \"percent\" | \"fixed\" | undefined,\n discountValue: data.discountValue as number | undefined,\n currency: data.currency as string | undefined,\n minOrderCents: data.minOrderCents as number | undefined,\n };\n } catch (err: unknown) {\n // 4xx errors mean invalid code\n if (\n err &&\n typeof err === \"object\" &&\n \"status\" in err &&\n (err as { status: number }).status < 500\n ) {\n const apiErr = err as { body?: { error?: string } };\n return {\n valid: false,\n error: apiErr.body?.error ?? \"Invalid promo code\",\n };\n }\n throw err;\n }\n }\n\n /**\n * List all active resources for the organization.\n *\n * Returns resource cards with name, description, price, duration,\n * image, and type — suitable for building a resource picker.\n *\n * ```ts\n * const { resources } = await resira.listResources();\n * ```\n */\n async listResources(): Promise<ResourceListResponse> {\n return this.request<ResourceListResponse>(\"GET\", \"/resources\");\n }\n\n /**\n * Query availability for a resource.\n *\n * **Properties** (date-based):\n * ```ts\n * await resira.getAvailability(\"prop-1\", {\n * startDate: \"2026-07-01\",\n * endDate: \"2026-07-07\",\n * });\n * ```\n *\n * **Restaurants** (time-slot):\n * ```ts\n * await resira.getAvailability(\"table-5\", {\n * date: \"2026-07-01\",\n * partySize: 4,\n * });\n * ```\n *\n * Omit params to get all blocked dates (for calendar rendering).\n */\n async getAvailability(\n resourceId: string,\n params: AvailabilityParams = {},\n ): Promise<Availability> {\n if (!resourceId) {\n throw new Error(\"resourceId is required for getAvailability\");\n }\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\n return this.request<Availability>(\n \"GET\",\n `/resources/${encodeURIComponent(resourceId)}/availability${qs}`,\n );\n }\n\n /**\n * Query availability for a product/service.\n *\n * Aggregates capacity across all linked equipment. If a product\n * has 2 jet skis (each capacity=1), slots show capacity: 2.\n * Both pending and confirmed reservations count as occupied.\n *\n * ```ts\n * await resira.getProductAvailability(\"prod-123\", {\n * date: \"2026-07-01\",\n * durationMinutes: 30,\n * });\n * ```\n */\n async getProductAvailability(\n productId: string,\n params: AvailabilityParams = {},\n ): Promise<Availability> {\n if (!productId) {\n throw new Error(\"productId is required for getProductAvailability\");\n }\n const qs = toQueryString(keysToSnake(params as Record<string, string | number | undefined>));\n return this.request<Availability>(\n \"GET\",\n `/products/${encodeURIComponent(productId)}/availability${qs}`,\n );\n }\n\n /**\n * Create a reservation.\n *\n * Uses `POST /v2/api/reservations` — the `resourceId` is sent in\n * the request body, **not** in the URL path.\n *\n * An `Idempotency-Key` header is automatically attached so that\n * retries (including those from exponential backoff) are safe.\n *\n * ```ts\n * const { reservation } = await resira.createReservation({\n * resourceId: \"prop-1\",\n * guestName: \"Jane Doe\",\n * guestEmail: \"jane@example.com\",\n * startDate: \"2026-07-01\",\n * endDate: \"2026-07-07\",\n * partySize: 3,\n * });\n * ```\n */\n async createReservation(\n payload: CreateReservationRequest,\n options?: { idempotencyKey?: string },\n ): Promise<ReservationResponse> {\n if (!payload.resourceId) {\n throw new Error(\"resourceId is required for createReservation\");\n }\n const idempotencyKey = options?.idempotencyKey ?? uuid();\n\n // Note: uses /v2/api prefix, not /v1/public\n const url = `${this.getBaseUrl()}/v2/api/reservations`;\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"Idempotency-Key\": idempotencyKey,\n };\n\n const init: RequestInit = {\n method: \"POST\",\n headers,\n body: JSON.stringify(payload),\n };\n\n let lastError: ResiraApiError | ResiraNetworkError | ResiraRateLimitError | undefined;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n if (attempt > 0) {\n const backoff = this.calculateBackoff(attempt, lastError);\n await sleep(backoff);\n }\n\n let response: Response;\n try {\n response = await this._fetch(url, init);\n } catch (err) {\n lastError = new ResiraNetworkError(\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\n err,\n );\n continue;\n }\n\n if (response.ok) {\n const raw = (await response.json()) as Record<string, unknown>;\n // The API may nest under \"reservation\" or \"booking\" depending on endpoint\n const resData = (raw.reservation ?? raw.booking ?? {}) as Record<string, unknown>;\n\n // Augment with request data: API response doesn't include\n // startTime/endTime/startDate/endDate/resourceId for v2 reservations\n const reservation: Reservation = {\n id: (resData.id as string) ?? \"\",\n resourceId: (resData.resourceId as string) ?? payload.resourceId,\n status: (resData.status as string) ?? \"pending\",\n startDate: (resData.startDate as string) ?? payload.startDate,\n endDate: (resData.endDate as string) ?? payload.endDate,\n startTime: (resData.startTime as string) ?? payload.startTime,\n endTime: (resData.endTime as string) ?? payload.endTime,\n startDatetime: (resData.startDatetime as string) ?? payload.startTime,\n endDatetime: (resData.endDatetime as string) ?? payload.endTime,\n guestName: (resData.guestName as string) ?? payload.guestName,\n partySize: (resData.partySize as number) ?? (resData.guests as number) ?? payload.partySize ?? 2,\n totalPrice: (resData.totalPrice as number) ?? 0,\n currency: (resData.currency as string) ?? \"EUR\",\n createdAt: (resData.createdAt as string) ?? new Date().toISOString(),\n };\n\n return { reservation };\n }\n\n let errorBody: ApiErrorBody;\n try {\n errorBody = (await response.json()) as ApiErrorBody;\n } catch {\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\n }\n\n if (response.status === 429) {\n lastError = new ResiraRateLimitError(errorBody, response);\n continue;\n }\n\n if (response.status >= 500) {\n lastError = new ResiraApiError(response.status, response.statusText, errorBody, response);\n continue;\n }\n\n throw new ResiraApiError(response.status, response.statusText, errorBody, response);\n }\n\n throw lastError!;\n }\n\n /**\n * List reservations for a resource (paginated).\n *\n * ```ts\n * const page = await resira.listReservations(\"prop-1\", {\n * status: \"confirmed\",\n * page: 1,\n * limit: 50,\n * });\n * console.log(page.data); // Reservation[]\n * console.log(page.totalPages); // number\n * ```\n */\n async listReservations(\n resourceId: string,\n params: ListReservationsParams = {},\n ): Promise<PaginatedReservations> {\n const qs = toQueryString(params as Record<string, string | number | undefined>);\n return this.request<PaginatedReservations>(\n \"GET\",\n `/resources/${encodeURIComponent(resourceId)}/reservations${qs}`,\n );\n }\n\n /**\n * Get a single reservation by ID.\n *\n * ```ts\n * const { reservation } = await resira.getReservation(\"prop-1\", \"res-uuid\");\n * ```\n */\n async getReservation(\n resourceId: string,\n reservationId: string,\n ): Promise<ReservationResponse> {\n return this.request<ReservationResponse>(\n \"GET\",\n `/resources/${encodeURIComponent(resourceId)}/reservations/${encodeURIComponent(reservationId)}`,\n );\n }\n\n /**\n * List all active products/services for the organisation.\n *\n * Calls `GET /v1/public/products` which returns products with\n * pricing, duration, and linked equipment.\n *\n * Falls back to mapping resources → products if the endpoint\n * returns 404 (backend hasn't been updated yet).\n *\n * ```ts\n * const { products } = await resira.listProducts();\n * ```\n */\n async listProducts(): Promise<ProductListResponse> {\n try {\n return await this.request<ProductListResponse>(\"GET\", \"/products\");\n } catch (err: unknown) {\n // Fallback: if /products endpoint doesn't exist yet (404),\n // map resources → products shape\n if (\n err &&\n typeof err === \"object\" &&\n \"status\" in err &&\n (err as { status: number }).status === 404\n ) {\n const resourceResponse = await this.listResources();\n const products = resourceResponse.resources.map((r) => ({\n id: r.id,\n name: r.name,\n description: r.description,\n durationMinutes: r.durationMinutes ?? 60,\n priceCents: r.pricePerSession ?? 0,\n currency: r.currency ?? \"EUR\",\n imageUrl: r.imageUrl,\n active: r.active,\n sortOrder: 0,\n pricingModel: \"per_session\" as const,\n equipmentIds: [r.id],\n equipmentNames: [r.name],\n }));\n return { products, count: products.length };\n }\n throw err;\n }\n }\n\n /**\n * List all dishes with 3D model data for the organisation.\n *\n * Returns dishes with name, description, price, image, and\n * 3D model URLs for AR/preview rendering.\n *\n * ```ts\n * const { dishes } = await resira.listDishes();\n * ```\n */\n async listDishes(): Promise<DishListResponse> {\n return this.request<DishListResponse>(\"GET\", \"/dishes\");\n }\n\n /**\n * Get a single dish by ID.\n *\n * ```ts\n * const { dish } = await resira.getDish(\"dish-uuid\");\n * console.log(dish.name, dish.model?.glbUrl);\n * ```\n */\n async getDish(dishId: string): Promise<DishResponse> {\n if (!dishId) {\n throw new Error(\"dishId is required for getDish\");\n }\n return this.request<DishResponse>(\n \"GET\",\n `/dishes/${encodeURIComponent(dishId)}`,\n );\n }\n\n /**\n * Create a Stripe payment intent for a booking.\n *\n * The server creates the payment intent, calculates the amount,\n * and returns a `clientSecret` to confirm payment on the frontend\n * using Stripe Elements.\n *\n * ```ts\n * const { clientSecret, amountNow, amountAtVenue } =\n * await resira.createPaymentIntent({\n * productId: \"prod-123\",\n * resourceId: \"res-456\",\n * partySize: 2,\n * startDate: \"2026-07-01\",\n * startTime: \"2026-07-01T10:00:00Z\",\n * endTime: \"2026-07-01T11:00:00Z\",\n * guestName: \"Jane Doe\",\n * guestEmail: \"jane@example.com\",\n * });\n * ```\n */\n async createPaymentIntent(\n payload: CreatePaymentIntentRequest,\n options?: { idempotencyKey?: string },\n ): Promise<PaymentIntentResponse> {\n const idempotencyKey = options?.idempotencyKey ?? uuid();\n const raw = await this.request<Record<string, unknown>>(\n \"POST\",\n \"/payments/create-intent\",\n payload,\n { \"Idempotency-Key\": idempotencyKey },\n );\n // Normalize response keys to camelCase\n const data = deepKeysToCamel(raw) as Record<string, unknown>;\n // Map backend field names to SDK field names:\n // backend: amountDue, totalPrice\n // SDK: amountNow, totalAmount, amountAtVenue\n const totalAmount = (data.totalAmount as number) ?? (data.totalPrice as number) ?? 0;\n const amountNow = (data.amountNow as number) ?? (data.amountDue as number) ?? totalAmount;\n const amountAtVenue = (data.amountAtVenue as number) ?? (totalAmount - amountNow);\n return {\n clientSecret: data.clientSecret as string,\n paymentIntentId: data.paymentIntentId as string,\n amountNow,\n amountAtVenue,\n totalAmount,\n currency: (data.currency as string) ?? \"EUR\",\n paymentOption: data.paymentOption as string | undefined,\n reservationId: data.reservationId as string | undefined,\n discountAmount: (data.discountAmount as number) ?? undefined,\n originalPrice: (data.originalPrice as number) ?? undefined,\n promoCodeApplied: (data.promoCodeApplied as string) ?? undefined,\n };\n }\n\n /**\n * Confirm that a payment was successful after the guest\n * completes the Stripe payment flow.\n *\n * This transitions the reservation from `pending` to `confirmed`.\n *\n * ```ts\n * const { reservation, paymentStatus } =\n * await resira.confirmPayment({\n * paymentIntentId: \"pi_xxx\",\n * reservationId: \"res-uuid\",\n * });\n * ```\n */\n async confirmPayment(\n payload: ConfirmPaymentRequest,\n ): Promise<ConfirmPaymentResponse> {\n const raw = await this.request<Record<string, unknown>>(\n \"POST\",\n \"/payments/confirm\",\n payload,\n );\n // Normalize response keys to camelCase in case backend returns snake_case\n return deepKeysToCamel(raw) as ConfirmPaymentResponse;\n }\n\n // ─────────────────────────────────────────────────────────────\n // Internal HTTP layer\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Execute an HTTP request with retry logic and error normalization.\n *\n * - Attaches `Authorization: Bearer <apiKey>` on every request.\n * - Retries on 429 and 5xx with exponential backoff + jitter.\n * - Parses error bodies and throws typed error classes.\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n const url = `${this.getBaseUrl()}${API_PREFIX}${path}`;\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n ...extraHeaders,\n };\n\n if (body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const init: RequestInit = {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n };\n\n let lastError: ResiraApiError | ResiraNetworkError | undefined;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n // ── Wait before retry ────────────────────────────────\n if (attempt > 0) {\n const backoff = this.calculateBackoff(attempt, lastError);\n await sleep(backoff);\n }\n\n // ── Execute the request ──────────────────────────────\n let response: Response;\n try {\n response = await this._fetch(url, init);\n } catch (err) {\n lastError = new ResiraNetworkError(\n `Network request failed: ${err instanceof Error ? err.message : String(err)}`,\n err,\n );\n // Network errors are always retryable\n continue;\n }\n\n // ── Success ──────────────────────────────────────────\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n // ── Parse error body ─────────────────────────────────\n let errorBody: ApiErrorBody;\n try {\n errorBody = (await response.json()) as ApiErrorBody;\n } catch {\n errorBody = { error: response.statusText || `HTTP ${response.status}` };\n }\n\n // ── Rate limit ───────────────────────────────────────\n if (response.status === 429) {\n lastError = new ResiraRateLimitError(errorBody, response);\n // Always retry 429s (up to maxRetries)\n continue;\n }\n\n // ── Server error (5xx) — retryable ───────────────────\n if (response.status >= 500) {\n lastError = new ResiraApiError(\n response.status,\n response.statusText,\n errorBody,\n response,\n );\n continue;\n }\n\n // ── Client error (4xx) — NOT retryable ───────────────\n throw new ResiraApiError(\n response.status,\n response.statusText,\n errorBody,\n response,\n );\n }\n\n // All retries exhausted — throw the last error\n throw lastError!;\n }\n\n /**\n * Calculate backoff delay in milliseconds for a given retry attempt.\n *\n * Uses exponential backoff with full jitter:\n * delay = random(0, base * 2^attempt)\n *\n * For 429 responses, respects the `Retry-After` header as a minimum.\n */\n private calculateBackoff(\n attempt: number,\n lastError?: ResiraApiError | ResiraNetworkError,\n ): number {\n const exponential = this.retryBaseDelay * 2 ** (attempt - 1);\n const jittered = Math.random() * exponential;\n\n // If the server told us to wait, use that as the floor\n if (lastError instanceof ResiraRateLimitError) {\n const serverDelay = lastError.retryAfter * 1000;\n return Math.max(serverDelay, jittered);\n }\n\n return jittered;\n }\n\n private getBaseUrl(): string {\n return pickBaseUrl(this.baseUrls);\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resira/sdk",
3
- "version": "0.2.6",
3
+ "version": "0.3.0",
4
4
  "description": "TypeScript SDK for the Resira public reservation API",
5
5
  "license": "MIT",
6
6
  "type": "module",