bake canonical build harness (deterministic /ship for forks) [restaurant]

This commit is contained in:
vibeasite-bot
2026-08-22 05:54:21 +01:00
parent 051dfacb54
commit a35fa315e4
66 changed files with 6128 additions and 209 deletions
+121
View File
@@ -0,0 +1,121 @@
// ---------------------------------------------------------------------------
// OddSockets Client — realtime pub/sub + presence for generated sites
// ---------------------------------------------------------------------------
// The realtime leg of the tyga backend bundle (alongside lib/anydb.ts data +
// lib/tygarun.ts SaaS). Channels, presence and history via OddSockets.
//
// This stub ships UNCONFIGURED. When the project connects OddSockets, the
// builder replaces ODDSOCKETS_CONFIG with real values (like lib/tygarun.ts)
// and this file is swapped for the real client (oddsocketsService.getClientCode,
// which imports `oddsockets-js`). Until then isConfigured() is false and the
// hooks fall back to a LOCAL in-memory demo bus so the UI is alive out of the
// box. The demo bus is clearly ephemeral — a broken real connect must surface
// as an error, never be masked by the demo state.
// ---------------------------------------------------------------------------
const ODDSOCKETS_CONFIG = {
// Always connect the manager LB — it assigns the worker and the SDK follows
// the handoff. NEVER hardcode a worker URL.
connectUrl: "__ODDSOCKETS_CONNECT_URL__", // e.g. https://connect.oddsockets.tyga.network
// Per-site key (Authorization: Bearer ak_live_...). Isolation boundary = the
// key owner (account). Provisioned per site by our reseller rail on connect.
apiKey: "__ODDSOCKETS_KEY__",
};
export function isConfigured(): boolean {
return !ODDSOCKETS_CONFIG.apiKey.startsWith("__");
}
export const CONNECT_URL = ODDSOCKETS_CONFIG.connectUrl;
export interface PresenceMember {
id: string;
state?: Record<string, any>;
}
export interface ChannelMessage {
id: string;
data: any;
at: string;
}
export interface OddSocketsChannel {
subscribe(cb: (msg: ChannelMessage) => void, opts?: Record<string, any>): () => void;
publish(data: any, opts?: Record<string, any>): Promise<void>;
getHistory(opts?: { count?: number; start?: string; end?: string }): Promise<ChannelMessage[]>;
getPresence(): Promise<PresenceMember[]>;
updateState(state: Record<string, any>): Promise<void>;
unsubscribe(): void;
}
export interface OddSocketsClient {
channel(name: string): OddSocketsChannel;
connected: boolean;
}
// ── Local in-memory demo bus (used until OddSockets is connected) ────────────
// Simulates pub/sub + presence within the tab so realtime UIs render live in
// demo mode. Not shared across clients — purely local, seed/demo only.
type Listener = (msg: ChannelMessage) => void;
function createDemoBus(): OddSocketsClient {
const listeners: Record<string, Set<Listener>> = {};
const history: Record<string, ChannelMessage[]> = {};
const presence: Record<string, Record<string, PresenceMember>> = {};
const selfId = `demo_${Math.random().toString(36).slice(2, 8)}`;
function channel(name: string): OddSocketsChannel {
listeners[name] = listeners[name] || new Set();
history[name] = history[name] || [];
presence[name] = presence[name] || {};
return {
subscribe(cb, _opts) {
listeners[name].add(cb);
// announce demo self-presence
presence[name][selfId] = { id: selfId, state: {} };
return () => listeners[name].delete(cb);
},
async publish(data, _opts) {
const msg: ChannelMessage = {
id: `m_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
data,
at: new Date().toISOString(),
};
history[name].push(msg);
if (history[name].length > 200) history[name].shift();
listeners[name].forEach((cb) => cb(msg));
},
async getHistory(opts = {}) {
const all = history[name] || [];
return opts.count ? all.slice(-opts.count) : all.slice();
},
async getPresence() {
return Object.values(presence[name] || {});
},
async updateState(state) {
presence[name][selfId] = { id: selfId, state };
},
unsubscribe() {
listeners[name].clear();
delete presence[name][selfId];
},
};
}
return { channel, connected: false };
}
// Singleton client — demo bus in the stub; the real client (oddsockets-js)
// replaces this whole file on connect via oddsocketsService.getClientCode.
const client: OddSocketsClient = createDemoBus();
export default client;
// Usage:
// import oddsockets, { isConfigured } from '@/lib/oddsockets';
// const ch = oddsockets.channel('room:lobby');
// const off = ch.subscribe((m) => console.log(m.data));
// await ch.publish({ text: 'hello' });
// await ch.updateState({ typing: true });
// const members = await ch.getPresence();