bake canonical build harness (deterministic /ship for forks) [real-estate]

This commit is contained in:
vibeasite-bot
2026-08-22 06:28:17 +01:00
parent c7b1a7218d
commit ce604bf69f
77 changed files with 6560 additions and 245 deletions
+115
View File
@@ -0,0 +1,115 @@
// ---------------------------------------------------------------------------
// TygaRun Client — ready-made SaaS APIs for generated sites
// ---------------------------------------------------------------------------
// Universal client for ALL sdk.tyga.run modules: auth, scheduling (bookings/
// eventtypes/availability), crm, ecommerce, billing, notifications, webhooks...
// Where Lovable gives you raw tables, tyga.run gives you ready-made APIs.
//
// This stub ships unconfigured. When the project connects tyga.run, the
// builder replaces TYGARUN_CONFIG with real values (like lib/anydb.ts).
// Until then isConfigured() is false and hooks fall back to seed data.
// ---------------------------------------------------------------------------
const TYGARUN_CONFIG = {
baseUrl: "__TYGARUN_SDK_URL__", // e.g. https://sdk.tyga.run
// Browser-safe `tc_pub_` publishable key (Supabase-anon model): origin-locked,
// sandbox-pinned, module-restricted server-side. Safe to ship in public code.
publishableKey: "__TYGARUN_ANON_KEY__",
sandbox: "__TYGARUN_SANDBOX__", // "true" | "false"
};
export function isConfigured(): boolean {
return !TYGARUN_CONFIG.publishableKey.startsWith("__");
}
const TOKEN_KEY = "tygarun_token";
export function getToken(): string | null {
try { return localStorage.getItem(TOKEN_KEY); } catch { return null; }
}
export function setToken(token: string | null) {
try {
if (token) localStorage.setItem(TOKEN_KEY, token);
else localStorage.removeItem(TOKEN_KEY);
} catch { /* SSR / private mode */ }
}
export interface TygaRunError extends Error {
statusCode?: number;
body?: any;
}
/**
* Universal call to any tyga.run module endpoint.
* tygarun("scheduling", "/bookings", { query: { page: 1 } })
* tygarun("auth", "/signin", { method: "POST", body: { email, password } })
* tygarun("crm", "/contacts", { method: "POST", body: contact })
*/
export async function tygarun<T = any>(
module: string,
endpoint = "/",
opts: {
method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
body?: any;
query?: Record<string, any>;
headers?: Record<string, string>;
} = {}
): Promise<T> {
if (!isConfigured()) {
const err = new Error("tyga.run not connected") as TygaRunError;
err.statusCode = 0;
throw err;
}
const { method = "GET", body, query = {}, headers = {} } = opts;
const path = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
const url = new URL(`${TYGARUN_CONFIG.baseUrl.replace(/\/+$/, "")}/${module}${path}`);
Object.entries(query).forEach(([k, v]) => {
if (v !== null && v !== undefined) url.searchParams.append(k, String(v));
});
const token = getToken();
const res = await fetch(url.toString(), {
method,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
// Publishable key ALWAYS authenticates the app (never the user).
Authorization: `Bearer ${TYGARUN_CONFIG.publishableKey}`,
// End-user identity (their JWT from auth.signin) rides separately.
...(token ? { "X-User-Token": token } : {}),
"X-Sandbox-Mode": TYGARUN_CONFIG.sandbox,
...headers,
},
body: body && method !== "GET" && method !== "DELETE" ? JSON.stringify(body) : undefined,
});
const text = await res.text();
let json: any = {};
try { json = text ? JSON.parse(text) : {}; } catch { /* non-JSON error body */ }
if (!res.ok) {
const err = new Error(json.error || json.message || `tyga.run ${module} error ${res.status}`) as TygaRunError;
err.statusCode = res.status;
err.body = json;
throw err;
}
return json as T;
}
// ── Auth convenience (JWT stored for user-scoped calls) ─────────────────────
export const auth = {
async signup(data: { email: string; password: string; firstName?: string; lastName?: string; [k: string]: any }) {
const res = await tygarun<any>("auth", "/signup", { method: "POST", body: data });
if (res.token) setToken(res.token);
return res;
},
async signin(email: string, password: string, rememberMe = false) {
const res = await tygarun<any>("auth", "/signin", { method: "POST", body: { email, password, rememberMe } });
if (res.token) setToken(res.token);
return res;
},
signout() { setToken(null); },
isSignedIn(): boolean { return Boolean(getToken()); },
};
export default tygarun;