(function initializeCondoApi(windowObject) { "use strict"; const config = windowObject.CONDO_RUNTIME_CONFIG || {}; const requestedMode = new URLSearchParams(windowObject.location.search).get("mode"); const mode = requestedMode === "api" ? "api" : requestedMode === "demo" ? "demo" : config.defaultMode === "api" ? "api" : "demo"; const periodYear = Number.isInteger(config.periodYear) ? config.periodYear : new Date().getUTCFullYear(); const baseUrl = new URL(config.apiBaseUrl || "http://127.0.0.1:3000", windowObject.location.origin); if (!["http:", "https:"].includes(baseUrl.protocol)) { throw new Error("CONDO API base URL must use HTTP or HTTPS."); } class CondoApiError extends Error { constructor(message, { status = 0, code = "NETWORK_ERROR" } = {}) { super(message); this.name = "CondoApiError"; this.status = status; this.code = code; } } function apiUrl(path, query = {}) { const url = new URL(path, `${baseUrl.href.replace(/\/$/, "")}/`); Object.entries(query).forEach(([key, value]) => { if (value !== undefined && value !== null && value !== "") { url.searchParams.set(key, String(value)); } }); return url; } async function request(path, { method = "GET", query, body, signal, timeoutMs = 12_000 } = {}) { const controller = new AbortController(); const abortFromCaller = () => controller.abort(signal.reason); if (signal?.aborted) abortFromCaller(); else signal?.addEventListener("abort", abortFromCaller, { once: true }); const timeout = windowObject.setTimeout(() => controller.abort(), timeoutMs); try { const response = await windowObject.fetch(apiUrl(path, query), { method, credentials: "include", headers: body === undefined ? { Accept: "application/json" } : { Accept: "application/json", "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), signal: controller.signal }); const payload = await response.json().catch(() => null); if (!response.ok) { throw new CondoApiError( payload?.error?.message || `API request failed with status ${response.status}`, { status: response.status, code: payload?.error?.code || "API_ERROR" } ); } return payload; } catch (error) { if (error instanceof CondoApiError) throw error; const aborted = controller.signal.aborted; throw new CondoApiError( aborted ? "The API request timed out or was cancelled." : "The API is unavailable.", { code: aborted ? "REQUEST_ABORTED" : "NETWORK_ERROR" } ); } finally { windowObject.clearTimeout(timeout); signal?.removeEventListener("abort", abortFromCaller); } } async function collectPages(loader, options = {}) { const first = await loader({ ...options, page: 1, pageSize: 100 }); const items = [...first.items]; const pageCount = Math.ceil(first.total / first.pageSize); for (let page = 2; page <= pageCount; page += 1) { const next = await loader({ ...options, page, pageSize: 100 }); items.push(...next.items); } return { items, total: first.total }; } const client = Object.freeze({ getSession: options => request("auth/session", options), login: (credentials, options = {}) => request("auth/login", { ...options, method: "POST", body: credentials }), logout: options => request("auth/logout", { ...options, method: "POST" }), health: options => request("health", options), listRoomTypes: options => request("room-types", options), listOwnerAccounts: ({ q, roomType, year = periodYear, page = 1, pageSize = 100, ...options } = {}) => request("owner-accounts", { ...options, query: { q, roomType, year, page, pageSize } }), listAllOwnerAccounts: ({ q, roomType, year = periodYear, ...options } = {}) => collectPages( pageOptions => client.listOwnerAccounts(pageOptions), { q, roomType, year, ...options } ), getOwnerAccount: (id, { year = periodYear, ...options } = {}) => request(`owner-accounts/${encodeURIComponent(id)}`, { ...options, query: { year } }), listUsageRecords: ({ ownerAccountId, confirmationNo, usedRoomType, page = 1, pageSize = 100, ...options } = {}) => request("usage-records", { ...options, query: { ownerAccountId, confirmationNo, usedRoomType, page, pageSize } }), listAllUsageRecords: ({ ownerAccountId, confirmationNo, usedRoomType, ...options } = {}) => collectPages( pageOptions => client.listUsageRecords(pageOptions), { ownerAccountId, confirmationNo, usedRoomType, ...options } ), getDashboard: ({ year = periodYear, ...options } = {}) => request("dashboard", { ...options, query: { year } }), createUsageRecord: (input, options = {}) => request("usage-records", { ...options, method: "POST", body: input }), deleteUsageRecord: (id, options = {}) => request(`usage-records/${encodeURIComponent(id)}`, { ...options, method: "DELETE" }) }); windowObject.CONDO_API = Object.freeze({ runtime: Object.freeze({ mode, apiBaseUrl: baseUrl.href.replace(/\/$/, ""), periodYear }), client, CondoApiError }); })(window);