@maravilla-labs/platform 0.1.30 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -1
- package/dist/index.js +27 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/realtime.ts +25 -1
- package/src/ren.ts +7 -28
package/dist/index.d.ts
CHANGED
|
@@ -696,11 +696,14 @@ declare class RealtimeClient {
|
|
|
696
696
|
private presenceListeners;
|
|
697
697
|
private subscribedChannels;
|
|
698
698
|
private pendingMessages;
|
|
699
|
+
private pingInterval;
|
|
700
|
+
private joinedPresence;
|
|
699
701
|
constructor(opts?: RealtimeClientOptions);
|
|
700
702
|
private detectEndpoint;
|
|
701
703
|
private log;
|
|
702
704
|
/** Connect to the realtime WebSocket server */
|
|
703
705
|
connect(): void;
|
|
706
|
+
private stopPing;
|
|
704
707
|
private scheduleReconnect;
|
|
705
708
|
private sendRaw;
|
|
706
709
|
private dispatch;
|
|
@@ -716,7 +719,7 @@ declare class RealtimeClient {
|
|
|
716
719
|
}): void;
|
|
717
720
|
/** Get a presence handle for a channel */
|
|
718
721
|
presence(channel: string): {
|
|
719
|
-
/** Join the channel with presence */
|
|
722
|
+
/** Join the channel with presence (auto-rejoins on reconnect) */
|
|
720
723
|
join: (userId: string, metadata?: any) => void;
|
|
721
724
|
/** Leave the channel */
|
|
722
725
|
leave: () => void;
|
package/dist/index.js
CHANGED
|
@@ -427,31 +427,14 @@ var RenClient = class {
|
|
|
427
427
|
this.connect();
|
|
428
428
|
}
|
|
429
429
|
detectEndpoint() {
|
|
430
|
-
console.log("[REN detectEndpoint] Starting detection...");
|
|
431
|
-
console.log("[REN detectEndpoint] globalThis:", typeof globalThis);
|
|
432
|
-
console.log("[REN detectEndpoint] globalThis.platform:", globalThis?.platform);
|
|
433
|
-
console.log("[REN detectEndpoint] globalThis.__maravilla_platform:", globalThis?.__maravilla_platform);
|
|
434
|
-
console.log("[REN detectEndpoint] window:", typeof window);
|
|
435
|
-
console.log("[REN detectEndpoint] window.location:", typeof window !== "undefined" ? window.location.href : "N/A");
|
|
436
|
-
if (typeof globalThis !== "undefined" && globalThis.platform) {
|
|
437
|
-
console.log("[REN detectEndpoint] Detected production runtime, using relative endpoint");
|
|
438
|
-
return "/api/maravilla/ren";
|
|
439
|
-
}
|
|
440
430
|
if (typeof window !== "undefined") {
|
|
441
431
|
const port = window.location.port;
|
|
442
|
-
if (port && port !== "3001" && port !== "80" && port !== "443") {
|
|
443
|
-
|
|
444
|
-
console.log("[REN detectEndpoint] Detected dev mode (port " + port + "), using dev server:", devServerUrl);
|
|
445
|
-
return `${devServerUrl}/api/maravilla/ren`;
|
|
432
|
+
if (port && port !== "3001" && port !== "80" && port !== "443" && port !== "") {
|
|
433
|
+
return `http://${window.location.hostname}:3001/api/maravilla/ren`;
|
|
446
434
|
}
|
|
447
435
|
}
|
|
448
|
-
if (typeof globalThis !== "undefined" && globalThis.
|
|
449
|
-
|
|
450
|
-
if (typeof window !== "undefined" && window.location.hostname !== "localhost") {
|
|
451
|
-
devServerUrl = `http://${window.location.hostname}:3001`;
|
|
452
|
-
}
|
|
453
|
-
console.log("[REN detectEndpoint] Detected injected platform, using dev server:", devServerUrl);
|
|
454
|
-
return `${devServerUrl}/api/maravilla/ren`;
|
|
436
|
+
if (typeof globalThis !== "undefined" && globalThis.platform) {
|
|
437
|
+
return "/api/maravilla/ren";
|
|
455
438
|
}
|
|
456
439
|
console.log("[REN detectEndpoint] Using default relative endpoint (preview/production mode)");
|
|
457
440
|
return "/api/maravilla/ren";
|
|
@@ -594,6 +577,8 @@ var RealtimeClient = class {
|
|
|
594
577
|
presenceListeners = /* @__PURE__ */ new Map();
|
|
595
578
|
subscribedChannels = /* @__PURE__ */ new Set();
|
|
596
579
|
pendingMessages = [];
|
|
580
|
+
pingInterval = null;
|
|
581
|
+
joinedPresence = /* @__PURE__ */ new Map();
|
|
597
582
|
constructor(opts = {}) {
|
|
598
583
|
this.wsEndpoint = opts.wsEndpoint || this.detectEndpoint();
|
|
599
584
|
this.clientId = opts.clientId || getOrCreateClientId();
|
|
@@ -634,10 +619,15 @@ var RealtimeClient = class {
|
|
|
634
619
|
for (const ch of this.subscribedChannels) {
|
|
635
620
|
this.sendRaw({ action: "subscribe", channel: ch });
|
|
636
621
|
}
|
|
622
|
+
for (const [channel, info] of this.joinedPresence) {
|
|
623
|
+
this.sendRaw({ action: "presence:join", channel, userId: info.userId, metadata: info.metadata });
|
|
624
|
+
}
|
|
637
625
|
for (const msg of this.pendingMessages) {
|
|
638
626
|
this.ws?.send(msg);
|
|
639
627
|
}
|
|
640
628
|
this.pendingMessages = [];
|
|
629
|
+
this.stopPing();
|
|
630
|
+
this.pingInterval = setInterval(() => this.sendRaw({ action: "ping" }), 25e3);
|
|
641
631
|
};
|
|
642
632
|
this.ws.onmessage = (ev) => {
|
|
643
633
|
try {
|
|
@@ -654,11 +644,18 @@ var RealtimeClient = class {
|
|
|
654
644
|
this.ws.onclose = () => {
|
|
655
645
|
this.log("disconnected");
|
|
656
646
|
this.ws = null;
|
|
647
|
+
this.stopPing();
|
|
657
648
|
if (!this.closed && this.autoReconnect) {
|
|
658
649
|
this.scheduleReconnect();
|
|
659
650
|
}
|
|
660
651
|
};
|
|
661
652
|
}
|
|
653
|
+
stopPing() {
|
|
654
|
+
if (this.pingInterval) {
|
|
655
|
+
clearInterval(this.pingInterval);
|
|
656
|
+
this.pingInterval = null;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
662
659
|
scheduleReconnect() {
|
|
663
660
|
const delay = Math.min(1e3 * Math.pow(2, this.attempt++), this.maxBackoff);
|
|
664
661
|
this.log("reconnecting in", delay, "ms");
|
|
@@ -748,8 +745,9 @@ var RealtimeClient = class {
|
|
|
748
745
|
}
|
|
749
746
|
const listeners = this.presenceListeners.get(channel);
|
|
750
747
|
return {
|
|
751
|
-
/** Join the channel with presence */
|
|
748
|
+
/** Join the channel with presence (auto-rejoins on reconnect) */
|
|
752
749
|
join: (userId, metadata) => {
|
|
750
|
+
this.joinedPresence.set(channel, { userId, metadata });
|
|
753
751
|
this.sendRaw({
|
|
754
752
|
action: "presence:join",
|
|
755
753
|
channel,
|
|
@@ -759,6 +757,7 @@ var RealtimeClient = class {
|
|
|
759
757
|
},
|
|
760
758
|
/** Leave the channel */
|
|
761
759
|
leave: () => {
|
|
760
|
+
this.joinedPresence.delete(channel);
|
|
762
761
|
this.sendRaw({ action: "presence:leave", channel });
|
|
763
762
|
},
|
|
764
763
|
/** Listen for users joining */
|
|
@@ -788,6 +787,11 @@ var RealtimeClient = class {
|
|
|
788
787
|
/** Disconnect and stop reconnecting */
|
|
789
788
|
disconnect() {
|
|
790
789
|
this.closed = true;
|
|
790
|
+
this.stopPing();
|
|
791
|
+
for (const [channel] of this.joinedPresence) {
|
|
792
|
+
this.sendRaw({ action: "presence:leave", channel });
|
|
793
|
+
}
|
|
794
|
+
this.joinedPresence.clear();
|
|
791
795
|
this.ws?.close();
|
|
792
796
|
this.ws = null;
|
|
793
797
|
this.pendingMessages = [];
|
|
@@ -1060,7 +1064,7 @@ function getPlatform(options) {
|
|
|
1060
1064
|
}
|
|
1061
1065
|
}
|
|
1062
1066
|
const devServerUrl = options?.devServerUrl || process.env.MARAVILLA_DEV_SERVER || "http://localhost:3001";
|
|
1063
|
-
const tenant = options?.tenant || process.env.MARAVILLA_TENANT || "dev-tenant";
|
|
1067
|
+
const tenant = options?.tenant || process.env.MARAVILLA_TENANT || "dev-tenant-001";
|
|
1064
1068
|
console.log(`[platform] Creating remote client for ${devServerUrl}`);
|
|
1065
1069
|
cachedPlatform = createRemoteClient(devServerUrl, tenant);
|
|
1066
1070
|
if (typeof globalThis !== "undefined") {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/media.ts","../src/remote-client.ts","../src/ren.ts","../src/realtime.ts","../src/media-room.ts","../src/index.ts"],"sourcesContent":["/**\n * Media service for video/audio room management.\n *\n * In production (Deno runtime) calls go through `Deno.core.ops`.\n * In development they are proxied as HTTP requests to the dev-server.\n */\n\n// ── Types ────────────────────────────────────────────────────────────\n\nexport interface MediaRoomInfo {\n name: string;\n numParticipants: number;\n maxParticipants: number;\n createdAt: number;\n active: boolean;\n}\n\nexport interface MediaRoomInfoSettings {\n maxParticipants?: number;\n emptyTimeoutSecs?: number;\n}\n\nexport interface MediaParticipantInfo {\n identity: string;\n name: string;\n canPublish?: boolean;\n canSubscribe?: boolean;\n canPublishData?: boolean;\n}\n\nexport interface MediaTokenResult {\n token: string;\n url: string;\n}\n\nexport interface MediaService {\n createRoom(roomId: string, settings?: MediaRoomInfoSettings): Promise<MediaRoomInfo>;\n deleteRoom(roomId: string): Promise<void>;\n listRooms(): Promise<MediaRoomInfo[]>;\n generateToken(roomId: string, participant: MediaParticipantInfo): Promise<MediaTokenResult>;\n mediaUrl(): Promise<string | null>;\n}\n\n// ── Remote (dev-server) implementation ───────────────────────────────\n\nexport class RemoteMediaService implements MediaService {\n constructor(\n private baseUrl: string,\n private headers: Record<string, string>,\n ) {}\n\n private async fetch(url: string, options: RequestInit = {}) {\n const response = await fetch(url, {\n ...options,\n headers: { ...this.headers, ...options.headers },\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Media API error: ${response.status} - ${error}`);\n }\n\n return response;\n }\n\n async createRoom(roomId: string, settings: MediaRoomInfoSettings = {}): Promise<MediaRoomInfo> {\n const response = await this.fetch(`${this.baseUrl}/api/media/rooms`, {\n method: 'POST',\n body: JSON.stringify({ roomId, settings }),\n });\n return response.json() as Promise<MediaRoomInfo>;\n }\n\n async deleteRoom(roomId: string): Promise<void> {\n await this.fetch(`${this.baseUrl}/api/media/rooms/${encodeURIComponent(roomId)}`, {\n method: 'DELETE',\n });\n }\n\n async listRooms(): Promise<MediaRoomInfo[]> {\n const response = await this.fetch(`${this.baseUrl}/api/media/rooms`);\n return response.json() as Promise<MediaRoomInfo[]>;\n }\n\n async generateToken(roomId: string, participant: MediaParticipantInfo): Promise<MediaTokenResult> {\n const response = await this.fetch(\n `${this.baseUrl}/api/media/rooms/${encodeURIComponent(roomId)}/token`,\n {\n method: 'POST',\n body: JSON.stringify(participant),\n },\n );\n return response.json() as Promise<MediaTokenResult>;\n }\n\n async mediaUrl(): Promise<string | null> {\n const response = await this.fetch(`${this.baseUrl}/api/media/url`);\n const data = (await response.json()) as { url: string | null };\n return data.url;\n }\n}\n","import type { KvNamespace, KvListResult, Database, DbFindOptions, Storage, RealtimeService, PresenceService } from './types.js';\nimport { RemoteMediaService } from './media.js';\n\n/**\n * Remote KV namespace implementation that communicates with a development server.\n * This class provides the KV interface by making HTTP requests to the dev server.\n * \n * @internal\n */\nclass RemoteKvNamespace implements KvNamespace {\n constructor(\n private baseUrl: string,\n private namespace: string,\n private headers: Record<string, string>\n ) {}\n\n /**\n * Internal method for making HTTP requests to the dev server.\n * Handles error responses and authentication headers.\n * \n * @internal\n */\n private async fetch(url: string, options: RequestInit = {}) {\n const response = await fetch(url, {\n ...options,\n headers: { ...this.headers, ...options.headers },\n });\n\n if (!response.ok && response.status !== 404) {\n const error = await response.text();\n throw new Error(`Platform API error: ${response.status} - ${error}`);\n }\n\n return response;\n }\n\n async get(key: string): Promise<any> {\n const response = await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}/${key}`);\n if (response.status === 404) return null;\n return response.json();\n }\n\n async put(key: string, value: any, options?: { expirationTtl?: number }): Promise<void> {\n const headers: Record<string, string> = {};\n if (options?.expirationTtl) {\n headers['X-TTL'] = options.expirationTtl.toString();\n }\n\n await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}/${key}`, {\n method: 'PUT',\n headers,\n body: JSON.stringify(value),\n });\n }\n\n async delete(key: string): Promise<void> {\n await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}/${key}`, {\n method: 'DELETE',\n });\n }\n\n async list(options?: { prefix?: string; limit?: number; cursor?: string }): Promise<KvListResult> {\n const response = await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}`, {\n method: 'POST',\n body: JSON.stringify(options || {}),\n });\n const data = await response.json() as any;\n return {\n keys: data.result || [],\n list_complete: !data.result_info?.cursor,\n cursor: data.result_info?.cursor,\n };\n }\n}\n\n/**\n * Remote Database implementation that communicates with a development server.\n * This class provides the Database interface by making HTTP requests to the dev server.\n * \n * @internal\n */\nclass RemoteDatabase implements Database {\n constructor(\n private baseUrl: string,\n private headers: Record<string, string>\n ) {}\n\n /**\n * Internal method for making HTTP requests to the dev server.\n * Handles error responses and authentication headers.\n * \n * @internal\n */\n private async fetch(url: string, options: RequestInit = {}) {\n const response = await fetch(url, {\n ...options,\n headers: { ...this.headers, ...options.headers },\n });\n\n if (!response.ok && response.status !== 404) {\n const error = await response.text();\n throw new Error(`Platform API error: ${response.status} - ${error}`);\n }\n\n return response;\n }\n\n async find(collection: string, filter: any = {}, options: DbFindOptions = {}): Promise<any[]> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}`, {\n method: 'POST',\n body: JSON.stringify({ filter, options }),\n });\n return response.json() as Promise<any[]>;\n }\n\n async findOne(collection: string, filter: any): Promise<any | null> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}/findOne`, {\n method: 'POST',\n body: JSON.stringify(filter),\n });\n if (response.status === 404) return null;\n return response.json();\n }\n\n async insertOne(collection: string, document: any): Promise<string> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}`, {\n method: 'PUT',\n body: JSON.stringify(document),\n });\n const result = await response.json() as any;\n return result.id;\n }\n\n async updateOne(collection: string, filter: any, update: any): Promise<{ modified: number }> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}/update`, {\n method: 'POST',\n body: JSON.stringify({ filter, update }),\n });\n return response.json() as Promise<{ modified: number }>;\n }\n\n async deleteOne(collection: string, filter: any): Promise<{ deleted: number }> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}/delete`, {\n method: 'DELETE',\n body: JSON.stringify(filter),\n });\n return response.json() as Promise<{ deleted: number }>;\n }\n\n async deleteMany(collection: string, filter: any): Promise<{ deleted: number }> {\n // For now, deleteMany is the same as deleteOne in the remote client\n // This would need to be implemented properly in the dev server\n return this.deleteOne(collection, filter);\n }\n}\n\n/**\n * Remote Storage implementation that communicates with a development server.\n * This class provides the Storage interface by making HTTP requests to the dev server.\n * \n * @internal\n */\nclass RemoteStorage implements Storage {\n constructor(\n private baseUrl: string,\n private headers: Record<string, string>\n ) {}\n\n /**\n * Internal method for making HTTP requests to the dev server.\n * Handles error responses and authentication headers.\n * \n * @internal\n */\n private async fetch(url: string, options: RequestInit = {}) {\n const response = await fetch(url, {\n ...options,\n headers: { ...this.headers, ...options.headers },\n });\n\n if (!response.ok && response.status !== 404) {\n const error = await response.text();\n throw new Error(`Platform API error: ${response.status} - ${error}`);\n }\n\n return response;\n }\n\n async generateUploadUrl(key: string, contentType: string, options?: { sizeLimit?: number }): Promise<{\n url: string;\n method: string;\n headers: Record<string, string>;\n expiresIn: number;\n }> {\n // For development, return a direct upload URL to the dev server\n // The dev server supports PUT directly to /api/storage/{key}\n return {\n url: `${this.baseUrl}/api/storage/${encodeURIComponent(key)}`,\n method: 'PUT',\n headers: {\n 'Content-Type': contentType,\n ...this.headers, // Include tenant headers\n },\n expiresIn: 3600, // 1 hour\n };\n }\n\n async generateDownloadUrl(key: string, options?: { expiresIn?: number }): Promise<{\n url: string;\n method: string;\n headers: Record<string, string>;\n expiresIn: number;\n }> {\n // For development, return a direct download URL to the dev server\n // In production, this would call the actual storage service for presigned URLs\n return {\n url: `${this.baseUrl}/api/storage/${encodeURIComponent(key)}`,\n method: 'GET',\n headers: {},\n expiresIn: options?.expiresIn || 3600, // Default 1 hour\n };\n }\n\n async get(key: string): Promise<Uint8Array | null> {\n const response = await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`);\n if (response.status === 404) return null;\n const arrayBuffer = await response.arrayBuffer();\n return new Uint8Array(arrayBuffer);\n }\n\n async put(key: string, data: Uint8Array | string, metadata?: any): Promise<void> {\n // Convert string to Uint8Array if needed\n const bodyData = typeof data === 'string' \n ? new TextEncoder().encode(data)\n : data;\n\n await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`, {\n method: 'PUT',\n headers: metadata ? { 'X-Metadata': JSON.stringify(metadata) } : {},\n // Cast because TypeScript's lib.dom.d.ts BodyInit union sometimes misses Uint8Array\n body: bodyData as any,\n });\n }\n\n async putStream(key: string, source: any, metadata?: any): Promise<void> {\n // If it's a Blob we can send directly\n if (typeof Blob !== 'undefined' && source instanceof Blob) {\n await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`, {\n method: 'PUT',\n headers: {\n ...(metadata ? { 'X-Metadata': JSON.stringify(metadata) } : {}),\n 'Content-Type': source.type || 'application/octet-stream'\n },\n body: source as any,\n });\n return;\n }\n\n // ReadableStream\n if (source && typeof source.getReader === 'function') {\n await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`, {\n method: 'PUT',\n headers: metadata ? { 'X-Metadata': JSON.stringify(metadata) } : {},\n body: source as any,\n });\n return;\n }\n\n // Async iterable -> wrap in ReadableStream\n if (Symbol.asyncIterator in Object(source ?? {})) {\n const asyncIt = source as AsyncIterable<any>;\n const stream = new ReadableStream({\n async pull(controller) {\n const { value, done } = await asyncIt[Symbol.asyncIterator]().next();\n if (done) { controller.close(); return; }\n controller.enqueue(RemoteStorage.normalizeChunk(value));\n }\n });\n await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`, {\n method: 'PUT',\n headers: metadata ? { 'X-Metadata': JSON.stringify(metadata) } : {},\n body: stream as any,\n });\n return;\n }\n\n // Synchronous iterable\n if (Symbol.iterator in Object(source ?? {})) {\n const it = (source as Iterable<any>)[Symbol.iterator]();\n const stream = new ReadableStream({\n pull(controller) {\n const res = it.next();\n if (res.done) { controller.close(); return; }\n controller.enqueue(RemoteStorage.normalizeChunk(res.value));\n }\n });\n await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`, {\n method: 'PUT',\n headers: metadata ? { 'X-Metadata': JSON.stringify(metadata) } : {},\n body: stream as any,\n });\n return;\n }\n\n // Fallback: primitive/string/Uint8Array/ArrayBuffer\n await this.put(key, RemoteStorage.normalizeChunk(source), metadata);\n }\n private static normalizeChunk(c: any): Uint8Array {\n if (c == null) return new Uint8Array();\n if (typeof c === 'string') return new TextEncoder().encode(c);\n if (c instanceof Uint8Array) return c;\n if (c instanceof ArrayBuffer) return new Uint8Array(c);\n if (Array.isArray(c)) return Uint8Array.from(c);\n throw new Error('Unsupported chunk type in putStream');\n }\n async delete(key: string): Promise<void> {\n await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`, {\n method: 'DELETE',\n });\n }\n\n async list(options?: { prefix?: string; limit?: number }): Promise<Array<{\n key: string;\n size: number;\n lastModified: string;\n metadata?: any;\n }>> {\n const params = new URLSearchParams();\n if (options?.prefix) params.set('prefix', options.prefix);\n if (options?.limit) params.set('limit', options.limit.toString());\n\n const response = await this.fetch(`${this.baseUrl}/api/storage?${params}`);\n return response.json();\n }\n\n async getMetadata(key: string): Promise<{\n size: number;\n contentType?: string;\n lastModified: string;\n metadata?: any;\n } | null> {\n const response = await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}/metadata`);\n if (response.status === 404) return null;\n return response.json();\n }\n}\n\n/**\n * Remote Realtime service for development — calls dev server HTTP endpoints.\n * @internal\n */\nclass RemotePresenceService implements PresenceService {\n constructor(private baseUrl: string, private headers: Record<string, string>) {}\n\n async join(channel: string, userId: string, metadata?: any): Promise<boolean> {\n const res = await fetch(`${this.baseUrl}/api/realtime/presence/${encodeURIComponent(channel)}/join`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ userId, metadata }),\n });\n const data = await res.json() as any;\n return data.isNew ?? true;\n }\n\n async leave(channel: string, userId: string): Promise<boolean> {\n const res = await fetch(`${this.baseUrl}/api/realtime/presence/${encodeURIComponent(channel)}/leave`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ userId }),\n });\n const data = await res.json() as any;\n return data.wasPresent ?? true;\n }\n\n async members(channel: string): Promise<Array<{ userId: string; metadata?: any; lastSeen?: number }>> {\n const res = await fetch(`${this.baseUrl}/api/realtime/presence/${encodeURIComponent(channel)}`, {\n headers: this.headers,\n });\n const data = await res.json() as any;\n return data.members ?? [];\n }\n}\n\nclass RemoteRealtimeService implements RealtimeService {\n presence: PresenceService;\n\n constructor(private baseUrl: string, private headers: Record<string, string>) {\n this.presence = new RemotePresenceService(baseUrl, headers);\n }\n\n async publish(channel: string, data: any, options?: { userId?: string }): Promise<void> {\n await fetch(`${this.baseUrl}/api/realtime/publish`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ channel, data, userId: options?.userId }),\n });\n }\n\n async channels(): Promise<string[]> {\n const res = await fetch(`${this.baseUrl}/api/realtime/channels`, {\n headers: this.headers,\n });\n const data = await res.json() as any;\n return data.channels ?? [];\n }\n}\n\n/**\n * Create a remote platform client for development environments.\n * \n * This function creates a platform instance that communicates with a development\n * server over HTTP. It's used internally by getPlatform() when running in development\n * mode and no native platform is available.\n * \n * @param baseUrl - The base URL of the development server\n * @param tenant - The tenant identifier for multi-tenancy support\n * @returns A platform instance that proxies requests to the development server\n * \n * @internal\n * \n * @example\n * ```typescript\n * // This is typically called internally by getPlatform()\n * const platform = createRemoteClient('http://localhost:3001', 'dev-tenant');\n * \n * // The returned platform works the same as the native one\n * await platform.env.KV.cache.put('key', 'value');\n * await platform.env.DB.insertOne('users', { name: 'John' });\n * await platform.env.STORAGE.put('file.pdf', pdfData);\n * ```\n */\nexport function createRemoteClient(baseUrl: string, tenant: string) {\n const headers = {\n 'Content-Type': 'application/json',\n 'X-Tenant-Id': tenant,\n };\n\n // Create KV namespaces proxy that dynamically creates namespace instances\n const kvProxy = new Proxy({} as Record<string, KvNamespace>, {\n get(_, namespace: string) {\n return new RemoteKvNamespace(baseUrl, namespace, headers);\n }\n });\n\n const db = new RemoteDatabase(baseUrl, headers);\n const storage = new RemoteStorage(baseUrl, headers);\n const media = new RemoteMediaService(baseUrl, headers);\n const realtime = new RemoteRealtimeService(baseUrl, headers);\n\n return {\n env: {\n KV: kvProxy,\n DB: db,\n STORAGE: storage,\n },\n media,\n realtime,\n };\n}","// REN (Resource Event Notifications) client utilities\n// Thin SSE client for /api/maravilla/ren\n// Reconnect w/ basic backoff; consumer filters self vs others using evt.src\n\nexport interface RenEvent {\n t: string; // event type e.g. storage.object.created, realtime.message, presence.join\n r: string; // resource domain e.g. storage, runtime, realtime, presence\n k?: string; // key (object key, deployment key, etc.)\n v?: string; // version / etag\n ts?: number; // timestamp (ms)\n src?: string;// origin client id (if set on mutation request)\n ns?: string; // future namespace / tenant\n ch?: string; // channel name (for realtime pub/sub)\n data?: any; // arbitrary payload (for realtime messages)\n uid?: string;// user identity (for presence)\n [extra: string]: any;\n}\n\nexport interface RenClientOptions {\n endpoint?: string; // override (default /api/maravilla/ren)\n subscriptions?: string[]; // resource filters, ['*'] means all\n clientId?: string; // supply existing client id\n autoReconnect?: boolean; // default true\n maxBackoffMs?: number; // default 15000\n debug?: boolean; // enable console debug logging\n}\n\ntype Listener = (event: RenEvent) => void;\n\nexport class RenClient {\n private endpoint: string;\n private subs: string[];\n private clientId: string;\n private listeners = new Set<Listener>();\n private es: EventSource | null = null;\n private closed = false;\n private attempt = 0;\n private autoReconnect: boolean;\n private maxBackoff: number;\n private debug: boolean;\n\n constructor(opts: RenClientOptions = {}) {\n this.endpoint = opts.endpoint || this.detectEndpoint();\n this.subs = (opts.subscriptions && opts.subscriptions.length) ? opts.subscriptions : ['*'];\n this.clientId = opts.clientId || getOrCreateClientId();\n this.autoReconnect = opts.autoReconnect !== false;\n this.maxBackoff = opts.maxBackoffMs ?? 15000;\n this.debug = !!opts.debug || (typeof localStorage !== 'undefined' && localStorage.getItem('REN_DEBUG') === '1');\n this.connect();\n }\n\n private detectEndpoint(): string {\n console.log('[REN detectEndpoint] Starting detection...');\n console.log('[REN detectEndpoint] globalThis:', typeof globalThis);\n console.log('[REN detectEndpoint] globalThis.platform:', (globalThis as any)?.platform);\n console.log('[REN detectEndpoint] globalThis.__maravilla_platform:', (globalThis as any)?.__maravilla_platform);\n console.log('[REN detectEndpoint] window:', typeof window);\n console.log('[REN detectEndpoint] window.location:', typeof window !== 'undefined' ? window.location.href : 'N/A');\n\n // Check if we're in production runtime (has native platform)\n if (typeof globalThis !== 'undefined' && (globalThis as any).platform) {\n console.log('[REN detectEndpoint] Detected production runtime, using relative endpoint');\n return '/api/maravilla/ren';\n }\n\n // Check if we're running on a dev server (Vite uses 5173+ but can pick any available port)\n if (typeof window !== 'undefined') {\n const port = window.location.port;\n if (port && port !== '3001' && port !== '80' && port !== '443') {\n const devServerUrl = `http://${window.location.hostname}:3001`;\n console.log('[REN detectEndpoint] Detected dev mode (port ' + port + '), using dev server:', devServerUrl);\n return `${devServerUrl}/api/maravilla/ren`;\n }\n }\n\n // Check if we're in development with injected platform\n if (typeof globalThis !== 'undefined' && (globalThis as any).__maravilla_platform) {\n // In development, use the dev server URL\n let devServerUrl = 'http://localhost:3001';\n // Also check window location to use same host in browser\n if (typeof window !== 'undefined' && window.location.hostname !== 'localhost') {\n devServerUrl = `http://${window.location.hostname}:3001`;\n }\n console.log('[REN detectEndpoint] Detected injected platform, using dev server:', devServerUrl);\n return `${devServerUrl}/api/maravilla/ren`;\n }\n\n // Default to relative URL (works in preview mode and production)\n console.log('[REN detectEndpoint] Using default relative endpoint (preview/production mode)');\n return '/api/maravilla/ren';\n }\n\n private log(...args: any[]) { if (this.debug) console.debug('[RenClient]', ...args); }\n\n private buildUrl(): string {\n const s = (this.subs.length === 1 && this.subs[0] === '*') ? '*' : this.subs.join(',');\n const qs = new URLSearchParams({ cid: this.clientId, s });\n return `${this.endpoint}?${qs.toString()}`;\n }\n\n private connect() {\n const url = this.buildUrl();\n this.log('connecting', { url });\n this.es = new EventSource(url);\n this.closed = false;\n\n const forward = (e: MessageEvent) => {\n try {\n const evt: RenEvent = JSON.parse(e.data);\n this.log('event', evt);\n this.listeners.forEach(l => l(evt));\n } catch (err) {\n this.log('malformed event data', e.data, err);\n }\n };\n\n // Attach generic message handler for events without a specific event: type\n this.es.onmessage = forward;\n\n // Register listeners for all known event types\n // The browser EventSource API does NOT call onmessage for events with an event: field\n // We must explicitly register listeners for each event type we want to handle\n const knownEventTypes = [\n // Database events\n 'db.document.created',\n 'db.document.updated',\n 'db.document.deleted',\n // KV events\n 'kv.put',\n 'kv.delete',\n 'kv.expired',\n // Storage events\n 'storage.object.created',\n 'storage.object.updated',\n 'storage.object.deleted',\n // Runtime events\n 'runtime.snapshot.ready',\n 'runtime.worker.started',\n 'runtime.worker.stopped',\n // Realtime channel events\n 'realtime.message',\n // Presence events\n 'presence.join',\n 'presence.leave',\n 'presence.update',\n // Meta events\n 'ren.meta'\n ];\n // Use the more general addEventListener signature to avoid TypeScript issues\n knownEventTypes.forEach(k => this.es?.addEventListener(k, forward as EventListener));\n\n this.es.onerror = (ev) => {\n this.log('error', ev);\n if (this.closed) return;\n this.es?.close();\n if (!this.autoReconnect) return;\n const delay = Math.min(1000 * Math.pow(2, this.attempt++), this.maxBackoff);\n this.log('reconnecting in', delay, 'ms');\n setTimeout(() => this.connect(), delay);\n };\n\n this.es.onopen = () => { this.attempt = 0; this.log('open'); };\n }\n\n on(listener: Listener): () => void {\n this.listeners.add(listener);\n this.log('listener added; total', this.listeners.size);\n return () => { this.listeners.delete(listener); this.log('listener removed; total', this.listeners.size); };\n }\n\n getClientId() { return this.clientId; }\n\n close() {\n this.closed = true;\n this.es?.close();\n this.log('closed');\n }\n}\n\nexport function getOrCreateClientId(storage?: Storage): string {\n if (!storage) {\n storage = (typeof window !== 'undefined' && window.localStorage) ? window.localStorage : (typeof globalThis !== 'undefined' && (globalThis as any).localStorage) ? (globalThis as any).localStorage : undefined as any;\n if (!storage) {\n // Return a random ID if no storage is available\n return (globalThis.crypto?.randomUUID?.() || randomFallback());\n }\n }\n const key = 'maravillaClientId';\n let id = storage.getItem(key);\n if (!id) {\n id = (globalThis.crypto?.randomUUID?.() || randomFallback());\n try { storage.setItem(key, id); } catch {} // ignore quota errors\n }\n return id;\n}\n\nfunction randomFallback(): string {\n // Very small UUID-ish fallback\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n// Helper to add client header to fetch\nexport async function renFetch(input: string | URL | Request, init: RequestInit = {}, clientId?: string) {\n const cid = clientId || getOrCreateClientId();\n const headers = new Headers(init.headers || {});\n headers.set('X-Ren-Client', cid);\n return fetch(input, { ...init, headers });\n}\n\n// Storage convenience wrappers (example)\nexport async function storageUpload(path: string, file: Blob | File, clientId?: string) {\n const res = await renFetch(`/api/storage/upload?path=${encodeURIComponent(path)}`, { method: 'POST', body: file }, clientId);\n if (!res.ok) throw new Error('upload failed');\n return res.json().catch(() => ({}));\n}\n\nexport async function storageDelete(path: string, clientId?: string) {\n const res = await renFetch(`/api/storage/delete?path=${encodeURIComponent(path)}`, { method: 'DELETE' }, clientId);\n if (!res.ok) throw new Error('delete failed');\n return res.json().catch(() => ({}));\n}\n","// Maravilla Realtime Client\n// Extends REN with channels, pub/sub, and presence support\n// Uses WebSocket for bidirectional communication, SSE as fallback\n\nimport { getOrCreateClientId } from './ren.js';\n\nexport interface RealtimeEvent {\n event: string;\n channel: string;\n data?: any;\n from?: string;\n userId?: string;\n ts?: number;\n metadata?: any;\n}\n\nexport interface PresenceMember {\n userId: string;\n metadata?: any;\n lastSeen?: number;\n}\n\nexport interface RealtimeClientOptions {\n /** WebSocket endpoint (default: auto-detect) */\n wsEndpoint?: string;\n /** Client ID (persistent across sessions) */\n clientId?: string;\n /** Auto-reconnect on disconnect (default: true) */\n autoReconnect?: boolean;\n /** Max reconnect backoff in ms (default: 15000) */\n maxBackoffMs?: number;\n /** Enable debug logging (default: false) */\n debug?: boolean;\n}\n\ntype EventCallback = (event: RealtimeEvent) => void;\ntype Unsubscribe = () => void;\n\nexport class RealtimeClient {\n private wsEndpoint: string;\n private clientId: string;\n private ws: WebSocket | null = null;\n private closed = false;\n private attempt = 0;\n private autoReconnect: boolean;\n private maxBackoff: number;\n private debug: boolean;\n\n private channelListeners = new Map<string, Set<EventCallback>>();\n private globalListeners = new Set<EventCallback>();\n private presenceListeners = new Map<string, {\n onJoin: Set<(member: PresenceMember) => void>;\n onLeave: Set<(member: PresenceMember) => void>;\n }>();\n private subscribedChannels = new Set<string>();\n private pendingMessages: string[] = [];\n\n constructor(opts: RealtimeClientOptions = {}) {\n this.wsEndpoint = opts.wsEndpoint || this.detectEndpoint();\n this.clientId = opts.clientId || getOrCreateClientId();\n this.autoReconnect = opts.autoReconnect !== false;\n this.maxBackoff = opts.maxBackoffMs ?? 15000;\n this.debug = !!opts.debug;\n }\n\n private detectEndpoint(): string {\n if (typeof window !== 'undefined') {\n const port = window.location.port;\n const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:';\n // Development mode: any non-standard port that isn't the dev server (3001)\n // Vite uses 5173+ but can pick any available port\n if (port && port !== '3001' && port !== '80' && port !== '443') {\n return `ws://${window.location.hostname}:3001/_rt/ws`;\n }\n return `${proto}//${window.location.host}/_rt/ws`;\n }\n return 'ws://localhost:3001/_rt/ws';\n }\n\n private log(...args: any[]) {\n if (this.debug) console.debug('[RealtimeClient]', ...args);\n }\n\n /** Connect to the realtime WebSocket server */\n connect(): void {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) return;\n\n const url = `${this.wsEndpoint}?cid=${encodeURIComponent(this.clientId)}`;\n this.log('connecting', url);\n this.closed = false;\n\n try {\n this.ws = new WebSocket(url);\n } catch (e) {\n this.log('WebSocket constructor failed', e);\n this.scheduleReconnect();\n return;\n }\n\n this.ws.onopen = () => {\n this.attempt = 0;\n this.log('connected');\n\n // Re-subscribe to channels\n for (const ch of this.subscribedChannels) {\n this.sendRaw({ action: 'subscribe', channel: ch });\n }\n\n // Flush pending messages\n for (const msg of this.pendingMessages) {\n this.ws?.send(msg);\n }\n this.pendingMessages = [];\n };\n\n this.ws.onmessage = (ev) => {\n try {\n const event: RealtimeEvent = JSON.parse(ev.data);\n this.log('received', event);\n this.dispatch(event);\n } catch (e) {\n this.log('malformed message', ev.data, e);\n }\n };\n\n this.ws.onerror = (ev) => {\n this.log('error', ev);\n };\n\n this.ws.onclose = () => {\n this.log('disconnected');\n this.ws = null;\n if (!this.closed && this.autoReconnect) {\n this.scheduleReconnect();\n }\n };\n }\n\n private scheduleReconnect() {\n const delay = Math.min(1000 * Math.pow(2, this.attempt++), this.maxBackoff);\n this.log('reconnecting in', delay, 'ms');\n setTimeout(() => this.connect(), delay);\n }\n\n private sendRaw(msg: Record<string, any>) {\n const json = JSON.stringify(msg);\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n this.ws.send(json);\n } else {\n this.pendingMessages.push(json);\n }\n }\n\n private dispatch(event: RealtimeEvent) {\n // Notify global listeners\n this.globalListeners.forEach(cb => cb(event));\n\n // Notify channel-specific listeners\n if (event.channel) {\n const listeners = this.channelListeners.get(event.channel);\n if (listeners) {\n listeners.forEach(cb => cb(event));\n }\n }\n\n // Handle presence events\n if (event.event === 'presence:join' || event.event === 'presence:leave') {\n const presenceSet = this.presenceListeners.get(event.channel);\n if (presenceSet) {\n const member: PresenceMember = {\n userId: event.userId || '',\n metadata: event.metadata,\n lastSeen: event.ts,\n };\n if (event.event === 'presence:join') {\n presenceSet.onJoin.forEach(cb => cb(member));\n } else {\n presenceSet.onLeave.forEach(cb => cb(member));\n }\n }\n }\n }\n\n /** Subscribe to messages on a channel */\n subscribe(channel: string, callback: EventCallback, options?: { token?: string }): Unsubscribe {\n if (!this.channelListeners.has(channel)) {\n this.channelListeners.set(channel, new Set());\n }\n this.channelListeners.get(channel)!.add(callback);\n\n // Send subscribe message if not already subscribed\n if (!this.subscribedChannels.has(channel)) {\n this.subscribedChannels.add(channel);\n const msg: Record<string, any> = { action: 'subscribe', channel };\n if (options?.token) {\n msg.token = options.token;\n }\n this.sendRaw(msg);\n }\n\n return () => {\n const listeners = this.channelListeners.get(channel);\n if (listeners) {\n listeners.delete(callback);\n if (listeners.size === 0) {\n this.channelListeners.delete(channel);\n this.subscribedChannels.delete(channel);\n this.sendRaw({ action: 'unsubscribe', channel });\n }\n }\n };\n }\n\n /** Listen to all events across all channels */\n onAny(callback: EventCallback): Unsubscribe {\n this.globalListeners.add(callback);\n return () => { this.globalListeners.delete(callback); };\n }\n\n /** Publish a message to a channel */\n publish(channel: string, data: any, options?: { userId?: string }): void {\n this.sendRaw({\n action: 'publish',\n channel,\n data,\n userId: options?.userId,\n });\n }\n\n /** Get a presence handle for a channel */\n presence(channel: string) {\n if (!this.presenceListeners.has(channel)) {\n this.presenceListeners.set(channel, {\n onJoin: new Set(),\n onLeave: new Set(),\n });\n }\n const listeners = this.presenceListeners.get(channel)!;\n\n return {\n /** Join the channel with presence */\n join: (userId: string, metadata?: any): void => {\n this.sendRaw({\n action: 'presence:join',\n channel,\n userId,\n metadata,\n });\n },\n\n /** Leave the channel */\n leave: (): void => {\n this.sendRaw({ action: 'presence:leave', channel });\n },\n\n /** Listen for users joining */\n onJoin: (callback: (member: PresenceMember) => void): Unsubscribe => {\n listeners.onJoin.add(callback);\n return () => { listeners.onJoin.delete(callback); };\n },\n\n /** Listen for users leaving */\n onLeave: (callback: (member: PresenceMember) => void): Unsubscribe => {\n listeners.onLeave.add(callback);\n return () => { listeners.onLeave.delete(callback); };\n },\n };\n }\n\n /** Get current client ID */\n getClientId(): string {\n return this.clientId;\n }\n\n /** Check if connected */\n isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n /** Disconnect and stop reconnecting */\n disconnect(): void {\n this.closed = true;\n this.ws?.close();\n this.ws = null;\n this.pendingMessages = [];\n this.log('disconnected (manual)');\n }\n}\n","// MediaRoom — SFU-agnostic browser client for video/audio rooms\n// Wraps livekit-client internally. If the SFU is swapped, only this file changes.\n\nimport {\n Room,\n RoomEvent,\n type RemoteParticipant,\n type RemoteTrackPublication,\n type RemoteTrack,\n type LocalParticipant as LKLocalParticipant,\n type Participant as LKParticipant,\n type TrackPublication,\n Track,\n ConnectionState,\n type DisconnectReason,\n type RoomConnectOptions,\n} from 'livekit-client';\n\n// ─── Events ───\n\nexport enum MediaRoomEvent {\n Connected = 'connected',\n Reconnecting = 'reconnecting',\n Reconnected = 'reconnected',\n Disconnected = 'disconnected',\n ParticipantJoined = 'participantJoined',\n ParticipantLeft = 'participantLeft',\n TrackSubscribed = 'trackSubscribed',\n TrackUnsubscribed = 'trackUnsubscribed',\n TrackMuted = 'trackMuted',\n TrackUnmuted = 'trackUnmuted',\n ActiveSpeakersChanged = 'activeSpeakersChanged',\n DataReceived = 'dataReceived',\n RecordingStatusChanged = 'recordingStatusChanged',\n MediaDevicesChanged = 'mediaDevicesChanged',\n}\n\n// ─── Types ───\n\nexport type TrackSource = 'camera' | 'microphone' | 'screen_share' | 'screen_share_audio';\nexport type TrackKind = 'audio' | 'video';\n\nexport interface MediaTrackPublication {\n trackSid: string;\n source: TrackSource;\n kind: TrackKind;\n muted: boolean;\n track?: MediaStreamTrack;\n}\n\nexport interface MediaParticipant {\n identity: string;\n name?: string;\n metadata?: string;\n isSpeaking: boolean;\n audioLevel: number;\n tracks: MediaTrackPublication[];\n}\n\nexport interface VideoResolution {\n width: number;\n height: number;\n frameRate?: number;\n}\n\nexport interface MediaRoomOptions {\n autoSubscribe?: boolean;\n adaptiveStream?: boolean;\n dynacast?: boolean;\n}\n\n// ─── Helpers ───\n\nfunction mapSource(source: Track.Source): TrackSource {\n switch (source) {\n case Track.Source.Camera: return 'camera';\n case Track.Source.Microphone: return 'microphone';\n case Track.Source.ScreenShare: return 'screen_share';\n case Track.Source.ScreenShareAudio: return 'screen_share_audio';\n default: return 'camera';\n }\n}\n\nfunction mapParticipant(p: LKParticipant): MediaParticipant {\n const tracks: MediaTrackPublication[] = [];\n p.trackPublications.forEach((pub: TrackPublication) => {\n tracks.push({\n trackSid: pub.trackSid,\n source: mapSource(pub.source),\n kind: pub.kind === Track.Kind.Video ? 'video' : 'audio',\n muted: pub.isMuted,\n track: pub.track?.mediaStreamTrack,\n });\n });\n return {\n identity: p.identity,\n name: p.name,\n metadata: p.metadata,\n isSpeaking: p.isSpeaking,\n audioLevel: p.audioLevel,\n tracks,\n };\n}\n\n/** Attach a media track to a video or audio element */\nexport function attachTrack(track: MediaStreamTrack, element: HTMLVideoElement | HTMLAudioElement): void {\n const stream = new MediaStream([track]);\n element.srcObject = stream;\n if (element instanceof HTMLVideoElement) {\n element.playsInline = true;\n }\n element.play().catch(() => {}); // autoplay may be blocked\n}\n\n/** Detach media from an element */\nexport function detachTrack(element: HTMLVideoElement | HTMLAudioElement): void {\n element.srcObject = null;\n}\n\n// ─── MediaLocalParticipant ───\n\nexport class MediaLocalParticipant implements MediaParticipant {\n private lp: LKLocalParticipant;\n\n /** @internal */\n constructor(lp: LKLocalParticipant) { this.lp = lp; }\n\n get identity() { return this.lp.identity; }\n get name() { return this.lp.name; }\n get metadata() { return this.lp.metadata; }\n get isSpeaking() { return this.lp.isSpeaking; }\n get audioLevel() { return this.lp.audioLevel; }\n\n get tracks(): MediaTrackPublication[] {\n return mapParticipant(this.lp).tracks;\n }\n\n // Camera\n get isCameraEnabled(): boolean {\n return !!this.lp.getTrackPublication(Track.Source.Camera)?.track && !this.lp.getTrackPublication(Track.Source.Camera)?.isMuted;\n }\n async enableCamera(options?: { deviceId?: string; resolution?: VideoResolution }): Promise<void> {\n await this.lp.setCameraEnabled(true, {\n deviceId: options?.deviceId,\n resolution: options?.resolution ? { width: options.resolution.width, height: options.resolution.height, frameRate: options.resolution.frameRate } : undefined,\n });\n }\n async disableCamera(): Promise<void> {\n await this.lp.setCameraEnabled(false);\n }\n\n // Microphone\n get isMicrophoneEnabled(): boolean {\n return !!this.lp.getTrackPublication(Track.Source.Microphone)?.track && !this.lp.getTrackPublication(Track.Source.Microphone)?.isMuted;\n }\n async enableMicrophone(options?: { deviceId?: string }): Promise<void> {\n await this.lp.setMicrophoneEnabled(true, { deviceId: options?.deviceId });\n }\n async disableMicrophone(): Promise<void> {\n await this.lp.setMicrophoneEnabled(false);\n }\n\n // Screen share\n get isScreenShareEnabled(): boolean {\n return !!this.lp.getTrackPublication(Track.Source.ScreenShare)?.track;\n }\n async enableScreenShare(options?: { audio?: boolean }): Promise<void> {\n await this.lp.setScreenShareEnabled(true, { audio: options?.audio });\n }\n async disableScreenShare(): Promise<void> {\n await this.lp.setScreenShareEnabled(false);\n }\n\n // Metadata\n async setName(name: string): Promise<void> { await this.lp.setName(name); }\n async setMetadata(metadata: string): Promise<void> { await this.lp.setMetadata(metadata); }\n\n // Data\n async sendData(data: Uint8Array, options?: { reliable?: boolean }): Promise<void> {\n await this.lp.publishData(data, { reliable: options?.reliable ?? true });\n }\n}\n\n// ─── MediaRoom ───\n\ntype EventCallback = (...args: any[]) => void;\n\nexport class MediaRoom {\n private room: Room;\n private listeners = new Map<MediaRoomEvent, Set<EventCallback>>();\n private _localParticipant!: MediaLocalParticipant;\n\n constructor() {\n this.room = new Room();\n }\n\n async connect(url: string, token: string, options?: MediaRoomOptions): Promise<void> {\n const opts: RoomConnectOptions = {};\n if (options?.autoSubscribe !== undefined) opts.autoSubscribe = options.autoSubscribe;\n this.room.options.adaptiveStream = options?.adaptiveStream ?? true;\n this.room.options.dynacast = options?.dynacast ?? true;\n\n this.wireEvents();\n await this.room.connect(url, token, opts);\n this._localParticipant = new MediaLocalParticipant(this.room.localParticipant);\n }\n\n async disconnect(): Promise<void> {\n await this.room.disconnect();\n }\n\n get state(): 'disconnected' | 'connecting' | 'connected' | 'reconnecting' {\n switch (this.room.state) {\n case ConnectionState.Connected: return 'connected';\n case ConnectionState.Connecting: return 'connecting';\n case ConnectionState.Reconnecting: return 'reconnecting';\n default: return 'disconnected';\n }\n }\n\n get localParticipant(): MediaLocalParticipant { return this._localParticipant; }\n\n get participants(): Map<string, MediaParticipant> {\n const map = new Map<string, MediaParticipant>();\n this.room.remoteParticipants.forEach((p, id) => map.set(id, mapParticipant(p)));\n return map;\n }\n\n get activeSpeakers(): MediaParticipant[] {\n return this.room.activeSpeakers.map(mapParticipant);\n }\n\n get name(): string { return this.room.name; }\n get numParticipants(): number { return this.room.numParticipants; }\n get isRecording(): boolean { return this.room.isRecording; }\n\n // Events\n on(event: MediaRoomEvent, callback: EventCallback): this {\n if (!this.listeners.has(event)) this.listeners.set(event, new Set());\n this.listeners.get(event)!.add(callback);\n return this;\n }\n\n off(event: MediaRoomEvent, callback: EventCallback): this {\n this.listeners.get(event)?.delete(callback);\n return this;\n }\n\n private emit(event: MediaRoomEvent, ...args: any[]) {\n this.listeners.get(event)?.forEach(cb => cb(...args));\n }\n\n // Device switching\n async switchCamera(deviceId: string): Promise<void> {\n await this.room.switchActiveDevice('videoinput', deviceId);\n }\n async switchMicrophone(deviceId: string): Promise<void> {\n await this.room.switchActiveDevice('audioinput', deviceId);\n }\n async switchSpeaker(deviceId: string): Promise<void> {\n await this.room.switchActiveDevice('audiooutput', deviceId);\n }\n\n // Static device enumeration\n static async getDevices(): Promise<MediaDeviceInfo[]> {\n return navigator.mediaDevices.enumerateDevices();\n }\n static async getCameras(): Promise<MediaDeviceInfo[]> {\n return (await MediaRoom.getDevices()).filter(d => d.kind === 'videoinput');\n }\n static async getMicrophones(): Promise<MediaDeviceInfo[]> {\n return (await MediaRoom.getDevices()).filter(d => d.kind === 'audioinput');\n }\n static async getSpeakers(): Promise<MediaDeviceInfo[]> {\n return (await MediaRoom.getDevices()).filter(d => d.kind === 'audiooutput');\n }\n\n // ─── Event wiring (LiveKit → MediaRoom) ───\n\n private wireEvents() {\n const r = this.room;\n r.on(RoomEvent.Connected, () => this.emit(MediaRoomEvent.Connected));\n r.on(RoomEvent.Reconnecting, () => this.emit(MediaRoomEvent.Reconnecting));\n r.on(RoomEvent.Reconnected, () => this.emit(MediaRoomEvent.Reconnected));\n r.on(RoomEvent.Disconnected, (reason?: DisconnectReason) => this.emit(MediaRoomEvent.Disconnected, reason));\n r.on(RoomEvent.MediaDevicesChanged, () => this.emit(MediaRoomEvent.MediaDevicesChanged));\n r.on(RoomEvent.RecordingStatusChanged, (recording: boolean) => this.emit(MediaRoomEvent.RecordingStatusChanged, recording));\n\n r.on(RoomEvent.ParticipantConnected, (p: RemoteParticipant) =>\n this.emit(MediaRoomEvent.ParticipantJoined, mapParticipant(p)));\n r.on(RoomEvent.ParticipantDisconnected, (p: RemoteParticipant) =>\n this.emit(MediaRoomEvent.ParticipantLeft, mapParticipant(p)));\n\n r.on(RoomEvent.TrackSubscribed, (track: RemoteTrack, pub: RemoteTrackPublication, p: RemoteParticipant) =>\n this.emit(MediaRoomEvent.TrackSubscribed, track.mediaStreamTrack, mapParticipant(p)));\n r.on(RoomEvent.TrackUnsubscribed, (track: RemoteTrack, pub: RemoteTrackPublication, p: RemoteParticipant) =>\n this.emit(MediaRoomEvent.TrackUnsubscribed, track.mediaStreamTrack, mapParticipant(p)));\n\n r.on(RoomEvent.TrackMuted, (pub: TrackPublication, p: LKParticipant) =>\n this.emit(MediaRoomEvent.TrackMuted, mapParticipant(p)));\n r.on(RoomEvent.TrackUnmuted, (pub: TrackPublication, p: LKParticipant) =>\n this.emit(MediaRoomEvent.TrackUnmuted, mapParticipant(p)));\n\n r.on(RoomEvent.ActiveSpeakersChanged, (speakers: LKParticipant[]) =>\n this.emit(MediaRoomEvent.ActiveSpeakersChanged, speakers.map(mapParticipant)));\n\n r.on(RoomEvent.DataReceived, (data: Uint8Array, p?: RemoteParticipant) =>\n this.emit(MediaRoomEvent.DataReceived, data, p ? mapParticipant(p) : undefined));\n }\n}\n","/**\n * @fileoverview Maravilla Platform SDK\n * \n * This package provides the main interface for accessing Maravilla runtime services\n * including Key-Value storage and Database operations. It automatically detects the\n * runtime environment and provides the appropriate implementation.\n * \n * ## Environment Detection\n * \n * The SDK automatically detects and adapts to different environments:\n * - **Production**: Uses native Maravilla runtime APIs\n * - **Development**: Connects to development server via HTTP\n * - **Testing**: Can be mocked or use development server\n * \n * ## Key Features\n * \n * - **KV Storage**: Cloudflare Workers KV-compatible API for key-value operations\n * - **Database**: MongoDB-style document database operations\n * - **Multi-tenancy**: Built-in tenant isolation and support\n * - **TypeScript**: Fully typed APIs with comprehensive JSDoc comments\n * - **Development-friendly**: Seamless development server integration\n * \n * @example\n * ```typescript\n * import { getPlatform } from '@maravilla/platform';\n * \n * const platform = getPlatform();\n * \n * // KV operations\n * await platform.env.KV.cache.put('user:123', { name: 'John' });\n * const user = await platform.env.KV.cache.get('user:123');\n * \n * // Database operations\n * const userId = await platform.env.DB.insertOne('users', {\n * name: 'John Doe',\n * email: 'john@example.com'\n * });\n * \n * const users = await platform.env.DB.find('users', { active: true });\n * ```\n * \n * @author Maravilla Team\n * @since 1.0.0\n */\n\nimport type { Platform } from './types.js';\nimport { createRemoteClient } from './remote-client.js';\n\nexport * from './types.js';\nexport * from './ren.js';\nexport * from './realtime.js';\nexport * from './media.js';\nexport * from './media-room.js';\n\n/**\n * Global platform instance injected by Maravilla runtime or development tools.\n * This should not be accessed directly - use getPlatform() instead.\n * \n * @internal\n */\n\ndeclare global {\n var __maravilla_platform: Platform | undefined;\n var platform: Platform | undefined;\n}\n\nlet cachedPlatform: Platform | null = null;\n\n/**\n * Get the platform instance. This will:\n * 1. Check if running in Maravilla runtime (global.platform exists)\n * 2. Check if a platform was injected via vite plugin or hooks\n * 3. Fall back to remote client for development\n * \n * The platform provides access to KV storage and database operations,\n * with automatic environment detection for seamless development and production.\n * \n * @param options - Optional configuration for development mode\n * @param options.devServerUrl - URL of the development server (defaults to MARAVILLA_DEV_SERVER env var or http://localhost:3001)\n * @param options.tenant - Tenant identifier for multi-tenancy (defaults to MARAVILLA_TENANT env var or 'dev-tenant')\n * @returns Platform instance with access to KV and database services\n * \n * @example\n * ```typescript\n * import { getPlatform } from '@maravilla/platform';\n * \n * // Basic usage (auto-detects environment)\n * const platform = getPlatform();\n * \n * // Development with custom server\n * const platform = getPlatform({\n * devServerUrl: 'http://localhost:3001',\n * tenant: 'my-app'\n * });\n * \n * // Use KV storage\n * await platform.env.KV.cache.put('user:123', { name: 'John' });\n * const user = await platform.env.KV.cache.get('user:123');\n * \n * // Use database\n * const userId = await platform.env.DB.insertOne('users', {\n * name: 'John Doe',\n * email: 'john@example.com'\n * });\n * ```\n */\nexport function getPlatform(options?: {\n devServerUrl?: string;\n tenant?: string;\n}): Platform {\n // Return cached if available\n if (cachedPlatform) {\n return cachedPlatform;\n }\n\n // 1. Check if we're in the real Maravilla runtime\n if (typeof globalThis !== 'undefined') {\n // Check for native platform (in production runtime)\n if (globalThis.platform) {\n console.log('[platform] Using native Maravilla platform');\n cachedPlatform = globalThis.platform;\n return cachedPlatform;\n }\n\n // Check for injected platform (from vite plugin or hooks)\n if (globalThis.__maravilla_platform) {\n console.log('[platform] Using injected platform');\n cachedPlatform = globalThis.__maravilla_platform;\n return cachedPlatform;\n }\n }\n\n // 2. Fall back to remote client for development\n const devServerUrl = options?.devServerUrl || process.env.MARAVILLA_DEV_SERVER || 'http://localhost:3001';\n const tenant = options?.tenant || process.env.MARAVILLA_TENANT || 'dev-tenant';\n \n console.log(`[platform] Creating remote client for ${devServerUrl}`);\n cachedPlatform = createRemoteClient(devServerUrl, tenant);\n \n // Cache it globally for other modules\n if (typeof globalThis !== 'undefined') {\n globalThis.__maravilla_platform = cachedPlatform;\n }\n \n return cachedPlatform;\n}\n\n/**\n * Clear the cached platform instance (useful for testing).\n * \n * This function resets the internal platform cache and clears any globally\n * injected platform instances. Subsequent calls to getPlatform() will\n * re-initialize the platform based on the current environment.\n * \n * @example\n * ```typescript\n * import { clearPlatformCache, getPlatform } from '@maravilla/platform';\n * \n * // In tests, clear cache between test cases\n * afterEach(() => {\n * clearPlatformCache();\n * });\n * \n * // Or when switching between different environments\n * clearPlatformCache();\n * const newPlatform = getPlatform({ devServerUrl: 'http://localhost:3002' });\n * ```\n */\nexport function clearPlatformCache(): void {\n cachedPlatform = null;\n if (typeof globalThis !== 'undefined') {\n globalThis.__maravilla_platform = undefined;\n }\n}"],"mappings":"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","names":["MediaRoomEvent"]}
|
|
1
|
+
{"version":3,"sources":["../src/media.ts","../src/remote-client.ts","../src/ren.ts","../src/realtime.ts","../src/media-room.ts","../src/index.ts"],"sourcesContent":["/**\n * Media service for video/audio room management.\n *\n * In production (Deno runtime) calls go through `Deno.core.ops`.\n * In development they are proxied as HTTP requests to the dev-server.\n */\n\n// ── Types ────────────────────────────────────────────────────────────\n\nexport interface MediaRoomInfo {\n name: string;\n numParticipants: number;\n maxParticipants: number;\n createdAt: number;\n active: boolean;\n}\n\nexport interface MediaRoomInfoSettings {\n maxParticipants?: number;\n emptyTimeoutSecs?: number;\n}\n\nexport interface MediaParticipantInfo {\n identity: string;\n name: string;\n canPublish?: boolean;\n canSubscribe?: boolean;\n canPublishData?: boolean;\n}\n\nexport interface MediaTokenResult {\n token: string;\n url: string;\n}\n\nexport interface MediaService {\n createRoom(roomId: string, settings?: MediaRoomInfoSettings): Promise<MediaRoomInfo>;\n deleteRoom(roomId: string): Promise<void>;\n listRooms(): Promise<MediaRoomInfo[]>;\n generateToken(roomId: string, participant: MediaParticipantInfo): Promise<MediaTokenResult>;\n mediaUrl(): Promise<string | null>;\n}\n\n// ── Remote (dev-server) implementation ───────────────────────────────\n\nexport class RemoteMediaService implements MediaService {\n constructor(\n private baseUrl: string,\n private headers: Record<string, string>,\n ) {}\n\n private async fetch(url: string, options: RequestInit = {}) {\n const response = await fetch(url, {\n ...options,\n headers: { ...this.headers, ...options.headers },\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Media API error: ${response.status} - ${error}`);\n }\n\n return response;\n }\n\n async createRoom(roomId: string, settings: MediaRoomInfoSettings = {}): Promise<MediaRoomInfo> {\n const response = await this.fetch(`${this.baseUrl}/api/media/rooms`, {\n method: 'POST',\n body: JSON.stringify({ roomId, settings }),\n });\n return response.json() as Promise<MediaRoomInfo>;\n }\n\n async deleteRoom(roomId: string): Promise<void> {\n await this.fetch(`${this.baseUrl}/api/media/rooms/${encodeURIComponent(roomId)}`, {\n method: 'DELETE',\n });\n }\n\n async listRooms(): Promise<MediaRoomInfo[]> {\n const response = await this.fetch(`${this.baseUrl}/api/media/rooms`);\n return response.json() as Promise<MediaRoomInfo[]>;\n }\n\n async generateToken(roomId: string, participant: MediaParticipantInfo): Promise<MediaTokenResult> {\n const response = await this.fetch(\n `${this.baseUrl}/api/media/rooms/${encodeURIComponent(roomId)}/token`,\n {\n method: 'POST',\n body: JSON.stringify(participant),\n },\n );\n return response.json() as Promise<MediaTokenResult>;\n }\n\n async mediaUrl(): Promise<string | null> {\n const response = await this.fetch(`${this.baseUrl}/api/media/url`);\n const data = (await response.json()) as { url: string | null };\n return data.url;\n }\n}\n","import type { KvNamespace, KvListResult, Database, DbFindOptions, Storage, RealtimeService, PresenceService } from './types.js';\nimport { RemoteMediaService } from './media.js';\n\n/**\n * Remote KV namespace implementation that communicates with a development server.\n * This class provides the KV interface by making HTTP requests to the dev server.\n * \n * @internal\n */\nclass RemoteKvNamespace implements KvNamespace {\n constructor(\n private baseUrl: string,\n private namespace: string,\n private headers: Record<string, string>\n ) {}\n\n /**\n * Internal method for making HTTP requests to the dev server.\n * Handles error responses and authentication headers.\n * \n * @internal\n */\n private async fetch(url: string, options: RequestInit = {}) {\n const response = await fetch(url, {\n ...options,\n headers: { ...this.headers, ...options.headers },\n });\n\n if (!response.ok && response.status !== 404) {\n const error = await response.text();\n throw new Error(`Platform API error: ${response.status} - ${error}`);\n }\n\n return response;\n }\n\n async get(key: string): Promise<any> {\n const response = await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}/${key}`);\n if (response.status === 404) return null;\n return response.json();\n }\n\n async put(key: string, value: any, options?: { expirationTtl?: number }): Promise<void> {\n const headers: Record<string, string> = {};\n if (options?.expirationTtl) {\n headers['X-TTL'] = options.expirationTtl.toString();\n }\n\n await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}/${key}`, {\n method: 'PUT',\n headers,\n body: JSON.stringify(value),\n });\n }\n\n async delete(key: string): Promise<void> {\n await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}/${key}`, {\n method: 'DELETE',\n });\n }\n\n async list(options?: { prefix?: string; limit?: number; cursor?: string }): Promise<KvListResult> {\n const response = await this.fetch(`${this.baseUrl}/api/kv/${this.namespace}`, {\n method: 'POST',\n body: JSON.stringify(options || {}),\n });\n const data = await response.json() as any;\n return {\n keys: data.result || [],\n list_complete: !data.result_info?.cursor,\n cursor: data.result_info?.cursor,\n };\n }\n}\n\n/**\n * Remote Database implementation that communicates with a development server.\n * This class provides the Database interface by making HTTP requests to the dev server.\n * \n * @internal\n */\nclass RemoteDatabase implements Database {\n constructor(\n private baseUrl: string,\n private headers: Record<string, string>\n ) {}\n\n /**\n * Internal method for making HTTP requests to the dev server.\n * Handles error responses and authentication headers.\n * \n * @internal\n */\n private async fetch(url: string, options: RequestInit = {}) {\n const response = await fetch(url, {\n ...options,\n headers: { ...this.headers, ...options.headers },\n });\n\n if (!response.ok && response.status !== 404) {\n const error = await response.text();\n throw new Error(`Platform API error: ${response.status} - ${error}`);\n }\n\n return response;\n }\n\n async find(collection: string, filter: any = {}, options: DbFindOptions = {}): Promise<any[]> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}`, {\n method: 'POST',\n body: JSON.stringify({ filter, options }),\n });\n return response.json() as Promise<any[]>;\n }\n\n async findOne(collection: string, filter: any): Promise<any | null> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}/findOne`, {\n method: 'POST',\n body: JSON.stringify(filter),\n });\n if (response.status === 404) return null;\n return response.json();\n }\n\n async insertOne(collection: string, document: any): Promise<string> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}`, {\n method: 'PUT',\n body: JSON.stringify(document),\n });\n const result = await response.json() as any;\n return result.id;\n }\n\n async updateOne(collection: string, filter: any, update: any): Promise<{ modified: number }> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}/update`, {\n method: 'POST',\n body: JSON.stringify({ filter, update }),\n });\n return response.json() as Promise<{ modified: number }>;\n }\n\n async deleteOne(collection: string, filter: any): Promise<{ deleted: number }> {\n const response = await this.fetch(`${this.baseUrl}/api/db/${collection}/delete`, {\n method: 'DELETE',\n body: JSON.stringify(filter),\n });\n return response.json() as Promise<{ deleted: number }>;\n }\n\n async deleteMany(collection: string, filter: any): Promise<{ deleted: number }> {\n // For now, deleteMany is the same as deleteOne in the remote client\n // This would need to be implemented properly in the dev server\n return this.deleteOne(collection, filter);\n }\n}\n\n/**\n * Remote Storage implementation that communicates with a development server.\n * This class provides the Storage interface by making HTTP requests to the dev server.\n * \n * @internal\n */\nclass RemoteStorage implements Storage {\n constructor(\n private baseUrl: string,\n private headers: Record<string, string>\n ) {}\n\n /**\n * Internal method for making HTTP requests to the dev server.\n * Handles error responses and authentication headers.\n * \n * @internal\n */\n private async fetch(url: string, options: RequestInit = {}) {\n const response = await fetch(url, {\n ...options,\n headers: { ...this.headers, ...options.headers },\n });\n\n if (!response.ok && response.status !== 404) {\n const error = await response.text();\n throw new Error(`Platform API error: ${response.status} - ${error}`);\n }\n\n return response;\n }\n\n async generateUploadUrl(key: string, contentType: string, options?: { sizeLimit?: number }): Promise<{\n url: string;\n method: string;\n headers: Record<string, string>;\n expiresIn: number;\n }> {\n // For development, return a direct upload URL to the dev server\n // The dev server supports PUT directly to /api/storage/{key}\n return {\n url: `${this.baseUrl}/api/storage/${encodeURIComponent(key)}`,\n method: 'PUT',\n headers: {\n 'Content-Type': contentType,\n ...this.headers, // Include tenant headers\n },\n expiresIn: 3600, // 1 hour\n };\n }\n\n async generateDownloadUrl(key: string, options?: { expiresIn?: number }): Promise<{\n url: string;\n method: string;\n headers: Record<string, string>;\n expiresIn: number;\n }> {\n // For development, return a direct download URL to the dev server\n // In production, this would call the actual storage service for presigned URLs\n return {\n url: `${this.baseUrl}/api/storage/${encodeURIComponent(key)}`,\n method: 'GET',\n headers: {},\n expiresIn: options?.expiresIn || 3600, // Default 1 hour\n };\n }\n\n async get(key: string): Promise<Uint8Array | null> {\n const response = await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`);\n if (response.status === 404) return null;\n const arrayBuffer = await response.arrayBuffer();\n return new Uint8Array(arrayBuffer);\n }\n\n async put(key: string, data: Uint8Array | string, metadata?: any): Promise<void> {\n // Convert string to Uint8Array if needed\n const bodyData = typeof data === 'string' \n ? new TextEncoder().encode(data)\n : data;\n\n await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`, {\n method: 'PUT',\n headers: metadata ? { 'X-Metadata': JSON.stringify(metadata) } : {},\n // Cast because TypeScript's lib.dom.d.ts BodyInit union sometimes misses Uint8Array\n body: bodyData as any,\n });\n }\n\n async putStream(key: string, source: any, metadata?: any): Promise<void> {\n // If it's a Blob we can send directly\n if (typeof Blob !== 'undefined' && source instanceof Blob) {\n await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`, {\n method: 'PUT',\n headers: {\n ...(metadata ? { 'X-Metadata': JSON.stringify(metadata) } : {}),\n 'Content-Type': source.type || 'application/octet-stream'\n },\n body: source as any,\n });\n return;\n }\n\n // ReadableStream\n if (source && typeof source.getReader === 'function') {\n await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`, {\n method: 'PUT',\n headers: metadata ? { 'X-Metadata': JSON.stringify(metadata) } : {},\n body: source as any,\n });\n return;\n }\n\n // Async iterable -> wrap in ReadableStream\n if (Symbol.asyncIterator in Object(source ?? {})) {\n const asyncIt = source as AsyncIterable<any>;\n const stream = new ReadableStream({\n async pull(controller) {\n const { value, done } = await asyncIt[Symbol.asyncIterator]().next();\n if (done) { controller.close(); return; }\n controller.enqueue(RemoteStorage.normalizeChunk(value));\n }\n });\n await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`, {\n method: 'PUT',\n headers: metadata ? { 'X-Metadata': JSON.stringify(metadata) } : {},\n body: stream as any,\n });\n return;\n }\n\n // Synchronous iterable\n if (Symbol.iterator in Object(source ?? {})) {\n const it = (source as Iterable<any>)[Symbol.iterator]();\n const stream = new ReadableStream({\n pull(controller) {\n const res = it.next();\n if (res.done) { controller.close(); return; }\n controller.enqueue(RemoteStorage.normalizeChunk(res.value));\n }\n });\n await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`, {\n method: 'PUT',\n headers: metadata ? { 'X-Metadata': JSON.stringify(metadata) } : {},\n body: stream as any,\n });\n return;\n }\n\n // Fallback: primitive/string/Uint8Array/ArrayBuffer\n await this.put(key, RemoteStorage.normalizeChunk(source), metadata);\n }\n private static normalizeChunk(c: any): Uint8Array {\n if (c == null) return new Uint8Array();\n if (typeof c === 'string') return new TextEncoder().encode(c);\n if (c instanceof Uint8Array) return c;\n if (c instanceof ArrayBuffer) return new Uint8Array(c);\n if (Array.isArray(c)) return Uint8Array.from(c);\n throw new Error('Unsupported chunk type in putStream');\n }\n async delete(key: string): Promise<void> {\n await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}`, {\n method: 'DELETE',\n });\n }\n\n async list(options?: { prefix?: string; limit?: number }): Promise<Array<{\n key: string;\n size: number;\n lastModified: string;\n metadata?: any;\n }>> {\n const params = new URLSearchParams();\n if (options?.prefix) params.set('prefix', options.prefix);\n if (options?.limit) params.set('limit', options.limit.toString());\n\n const response = await this.fetch(`${this.baseUrl}/api/storage?${params}`);\n return response.json();\n }\n\n async getMetadata(key: string): Promise<{\n size: number;\n contentType?: string;\n lastModified: string;\n metadata?: any;\n } | null> {\n const response = await this.fetch(`${this.baseUrl}/api/storage/${encodeURIComponent(key)}/metadata`);\n if (response.status === 404) return null;\n return response.json();\n }\n}\n\n/**\n * Remote Realtime service for development — calls dev server HTTP endpoints.\n * @internal\n */\nclass RemotePresenceService implements PresenceService {\n constructor(private baseUrl: string, private headers: Record<string, string>) {}\n\n async join(channel: string, userId: string, metadata?: any): Promise<boolean> {\n const res = await fetch(`${this.baseUrl}/api/realtime/presence/${encodeURIComponent(channel)}/join`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ userId, metadata }),\n });\n const data = await res.json() as any;\n return data.isNew ?? true;\n }\n\n async leave(channel: string, userId: string): Promise<boolean> {\n const res = await fetch(`${this.baseUrl}/api/realtime/presence/${encodeURIComponent(channel)}/leave`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ userId }),\n });\n const data = await res.json() as any;\n return data.wasPresent ?? true;\n }\n\n async members(channel: string): Promise<Array<{ userId: string; metadata?: any; lastSeen?: number }>> {\n const res = await fetch(`${this.baseUrl}/api/realtime/presence/${encodeURIComponent(channel)}`, {\n headers: this.headers,\n });\n const data = await res.json() as any;\n return data.members ?? [];\n }\n}\n\nclass RemoteRealtimeService implements RealtimeService {\n presence: PresenceService;\n\n constructor(private baseUrl: string, private headers: Record<string, string>) {\n this.presence = new RemotePresenceService(baseUrl, headers);\n }\n\n async publish(channel: string, data: any, options?: { userId?: string }): Promise<void> {\n await fetch(`${this.baseUrl}/api/realtime/publish`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ channel, data, userId: options?.userId }),\n });\n }\n\n async channels(): Promise<string[]> {\n const res = await fetch(`${this.baseUrl}/api/realtime/channels`, {\n headers: this.headers,\n });\n const data = await res.json() as any;\n return data.channels ?? [];\n }\n}\n\n/**\n * Create a remote platform client for development environments.\n * \n * This function creates a platform instance that communicates with a development\n * server over HTTP. It's used internally by getPlatform() when running in development\n * mode and no native platform is available.\n * \n * @param baseUrl - The base URL of the development server\n * @param tenant - The tenant identifier for multi-tenancy support\n * @returns A platform instance that proxies requests to the development server\n * \n * @internal\n * \n * @example\n * ```typescript\n * // This is typically called internally by getPlatform()\n * const platform = createRemoteClient('http://localhost:3001', 'dev-tenant');\n * \n * // The returned platform works the same as the native one\n * await platform.env.KV.cache.put('key', 'value');\n * await platform.env.DB.insertOne('users', { name: 'John' });\n * await platform.env.STORAGE.put('file.pdf', pdfData);\n * ```\n */\nexport function createRemoteClient(baseUrl: string, tenant: string) {\n const headers = {\n 'Content-Type': 'application/json',\n 'X-Tenant-Id': tenant,\n };\n\n // Create KV namespaces proxy that dynamically creates namespace instances\n const kvProxy = new Proxy({} as Record<string, KvNamespace>, {\n get(_, namespace: string) {\n return new RemoteKvNamespace(baseUrl, namespace, headers);\n }\n });\n\n const db = new RemoteDatabase(baseUrl, headers);\n const storage = new RemoteStorage(baseUrl, headers);\n const media = new RemoteMediaService(baseUrl, headers);\n const realtime = new RemoteRealtimeService(baseUrl, headers);\n\n return {\n env: {\n KV: kvProxy,\n DB: db,\n STORAGE: storage,\n },\n media,\n realtime,\n };\n}","// REN (Resource Event Notifications) client utilities\n// Thin SSE client for /api/maravilla/ren\n// Reconnect w/ basic backoff; consumer filters self vs others using evt.src\n\nexport interface RenEvent {\n t: string; // event type e.g. storage.object.created, realtime.message, presence.join\n r: string; // resource domain e.g. storage, runtime, realtime, presence\n k?: string; // key (object key, deployment key, etc.)\n v?: string; // version / etag\n ts?: number; // timestamp (ms)\n src?: string;// origin client id (if set on mutation request)\n ns?: string; // future namespace / tenant\n ch?: string; // channel name (for realtime pub/sub)\n data?: any; // arbitrary payload (for realtime messages)\n uid?: string;// user identity (for presence)\n [extra: string]: any;\n}\n\nexport interface RenClientOptions {\n endpoint?: string; // override (default /api/maravilla/ren)\n subscriptions?: string[]; // resource filters, ['*'] means all\n clientId?: string; // supply existing client id\n autoReconnect?: boolean; // default true\n maxBackoffMs?: number; // default 15000\n debug?: boolean; // enable console debug logging\n}\n\ntype Listener = (event: RenEvent) => void;\n\nexport class RenClient {\n private endpoint: string;\n private subs: string[];\n private clientId: string;\n private listeners = new Set<Listener>();\n private es: EventSource | null = null;\n private closed = false;\n private attempt = 0;\n private autoReconnect: boolean;\n private maxBackoff: number;\n private debug: boolean;\n\n constructor(opts: RenClientOptions = {}) {\n this.endpoint = opts.endpoint || this.detectEndpoint();\n this.subs = (opts.subscriptions && opts.subscriptions.length) ? opts.subscriptions : ['*'];\n this.clientId = opts.clientId || getOrCreateClientId();\n this.autoReconnect = opts.autoReconnect !== false;\n this.maxBackoff = opts.maxBackoffMs ?? 15000;\n this.debug = !!opts.debug || (typeof localStorage !== 'undefined' && localStorage.getItem('REN_DEBUG') === '1');\n this.connect();\n }\n\n private detectEndpoint(): string {\n // Port check FIRST — catches any dev server (Vite, SvelteKit, etc.)\n // regardless of whether globalThis.platform was set by getPlatform()\n if (typeof window !== 'undefined') {\n const port = window.location.port;\n if (port && port !== '3001' && port !== '80' && port !== '443' && port !== '') {\n return `http://${window.location.hostname}:3001/api/maravilla/ren`;\n }\n }\n\n // Production runtime (native Deno platform) — use relative URL\n if (typeof globalThis !== 'undefined' && (globalThis as any).platform) {\n return '/api/maravilla/ren';\n }\n\n // Default to relative URL (works in preview mode and production)\n console.log('[REN detectEndpoint] Using default relative endpoint (preview/production mode)');\n return '/api/maravilla/ren';\n }\n\n private log(...args: any[]) { if (this.debug) console.debug('[RenClient]', ...args); }\n\n private buildUrl(): string {\n const s = (this.subs.length === 1 && this.subs[0] === '*') ? '*' : this.subs.join(',');\n const qs = new URLSearchParams({ cid: this.clientId, s });\n return `${this.endpoint}?${qs.toString()}`;\n }\n\n private connect() {\n const url = this.buildUrl();\n this.log('connecting', { url });\n this.es = new EventSource(url);\n this.closed = false;\n\n const forward = (e: MessageEvent) => {\n try {\n const evt: RenEvent = JSON.parse(e.data);\n this.log('event', evt);\n this.listeners.forEach(l => l(evt));\n } catch (err) {\n this.log('malformed event data', e.data, err);\n }\n };\n\n // Attach generic message handler for events without a specific event: type\n this.es.onmessage = forward;\n\n // Register listeners for all known event types\n // The browser EventSource API does NOT call onmessage for events with an event: field\n // We must explicitly register listeners for each event type we want to handle\n const knownEventTypes = [\n // Database events\n 'db.document.created',\n 'db.document.updated',\n 'db.document.deleted',\n // KV events\n 'kv.put',\n 'kv.delete',\n 'kv.expired',\n // Storage events\n 'storage.object.created',\n 'storage.object.updated',\n 'storage.object.deleted',\n // Runtime events\n 'runtime.snapshot.ready',\n 'runtime.worker.started',\n 'runtime.worker.stopped',\n // Realtime channel events\n 'realtime.message',\n // Presence events\n 'presence.join',\n 'presence.leave',\n 'presence.update',\n // Meta events\n 'ren.meta'\n ];\n // Use the more general addEventListener signature to avoid TypeScript issues\n knownEventTypes.forEach(k => this.es?.addEventListener(k, forward as EventListener));\n\n this.es.onerror = (ev) => {\n this.log('error', ev);\n if (this.closed) return;\n this.es?.close();\n if (!this.autoReconnect) return;\n const delay = Math.min(1000 * Math.pow(2, this.attempt++), this.maxBackoff);\n this.log('reconnecting in', delay, 'ms');\n setTimeout(() => this.connect(), delay);\n };\n\n this.es.onopen = () => { this.attempt = 0; this.log('open'); };\n }\n\n on(listener: Listener): () => void {\n this.listeners.add(listener);\n this.log('listener added; total', this.listeners.size);\n return () => { this.listeners.delete(listener); this.log('listener removed; total', this.listeners.size); };\n }\n\n getClientId() { return this.clientId; }\n\n close() {\n this.closed = true;\n this.es?.close();\n this.log('closed');\n }\n}\n\nexport function getOrCreateClientId(storage?: Storage): string {\n if (!storage) {\n storage = (typeof window !== 'undefined' && window.localStorage) ? window.localStorage : (typeof globalThis !== 'undefined' && (globalThis as any).localStorage) ? (globalThis as any).localStorage : undefined as any;\n if (!storage) {\n // Return a random ID if no storage is available\n return (globalThis.crypto?.randomUUID?.() || randomFallback());\n }\n }\n const key = 'maravillaClientId';\n let id = storage.getItem(key);\n if (!id) {\n id = (globalThis.crypto?.randomUUID?.() || randomFallback());\n try { storage.setItem(key, id); } catch {} // ignore quota errors\n }\n return id;\n}\n\nfunction randomFallback(): string {\n // Very small UUID-ish fallback\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n// Helper to add client header to fetch\nexport async function renFetch(input: string | URL | Request, init: RequestInit = {}, clientId?: string) {\n const cid = clientId || getOrCreateClientId();\n const headers = new Headers(init.headers || {});\n headers.set('X-Ren-Client', cid);\n return fetch(input, { ...init, headers });\n}\n\n// Storage convenience wrappers (example)\nexport async function storageUpload(path: string, file: Blob | File, clientId?: string) {\n const res = await renFetch(`/api/storage/upload?path=${encodeURIComponent(path)}`, { method: 'POST', body: file }, clientId);\n if (!res.ok) throw new Error('upload failed');\n return res.json().catch(() => ({}));\n}\n\nexport async function storageDelete(path: string, clientId?: string) {\n const res = await renFetch(`/api/storage/delete?path=${encodeURIComponent(path)}`, { method: 'DELETE' }, clientId);\n if (!res.ok) throw new Error('delete failed');\n return res.json().catch(() => ({}));\n}\n","// Maravilla Realtime Client\n// Extends REN with channels, pub/sub, and presence support\n// Uses WebSocket for bidirectional communication, SSE as fallback\n\nimport { getOrCreateClientId } from './ren.js';\n\nexport interface RealtimeEvent {\n event: string;\n channel: string;\n data?: any;\n from?: string;\n userId?: string;\n ts?: number;\n metadata?: any;\n}\n\nexport interface PresenceMember {\n userId: string;\n metadata?: any;\n lastSeen?: number;\n}\n\nexport interface RealtimeClientOptions {\n /** WebSocket endpoint (default: auto-detect) */\n wsEndpoint?: string;\n /** Client ID (persistent across sessions) */\n clientId?: string;\n /** Auto-reconnect on disconnect (default: true) */\n autoReconnect?: boolean;\n /** Max reconnect backoff in ms (default: 15000) */\n maxBackoffMs?: number;\n /** Enable debug logging (default: false) */\n debug?: boolean;\n}\n\ntype EventCallback = (event: RealtimeEvent) => void;\ntype Unsubscribe = () => void;\n\nexport class RealtimeClient {\n private wsEndpoint: string;\n private clientId: string;\n private ws: WebSocket | null = null;\n private closed = false;\n private attempt = 0;\n private autoReconnect: boolean;\n private maxBackoff: number;\n private debug: boolean;\n\n private channelListeners = new Map<string, Set<EventCallback>>();\n private globalListeners = new Set<EventCallback>();\n private presenceListeners = new Map<string, {\n onJoin: Set<(member: PresenceMember) => void>;\n onLeave: Set<(member: PresenceMember) => void>;\n }>();\n private subscribedChannels = new Set<string>();\n private pendingMessages: string[] = [];\n private pingInterval: ReturnType<typeof setInterval> | null = null;\n private joinedPresence = new Map<string, { userId: string; metadata?: any }>();\n\n constructor(opts: RealtimeClientOptions = {}) {\n this.wsEndpoint = opts.wsEndpoint || this.detectEndpoint();\n this.clientId = opts.clientId || getOrCreateClientId();\n this.autoReconnect = opts.autoReconnect !== false;\n this.maxBackoff = opts.maxBackoffMs ?? 15000;\n this.debug = !!opts.debug;\n }\n\n private detectEndpoint(): string {\n if (typeof window !== 'undefined') {\n const port = window.location.port;\n const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:';\n // Development mode: any non-standard port that isn't the dev server (3001)\n // Vite uses 5173+ but can pick any available port\n if (port && port !== '3001' && port !== '80' && port !== '443') {\n return `ws://${window.location.hostname}:3001/_rt/ws`;\n }\n return `${proto}//${window.location.host}/_rt/ws`;\n }\n return 'ws://localhost:3001/_rt/ws';\n }\n\n private log(...args: any[]) {\n if (this.debug) console.debug('[RealtimeClient]', ...args);\n }\n\n /** Connect to the realtime WebSocket server */\n connect(): void {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) return;\n\n const url = `${this.wsEndpoint}?cid=${encodeURIComponent(this.clientId)}`;\n this.log('connecting', url);\n this.closed = false;\n\n try {\n this.ws = new WebSocket(url);\n } catch (e) {\n this.log('WebSocket constructor failed', e);\n this.scheduleReconnect();\n return;\n }\n\n this.ws.onopen = () => {\n this.attempt = 0;\n this.log('connected');\n\n // Re-subscribe to channels\n for (const ch of this.subscribedChannels) {\n this.sendRaw({ action: 'subscribe', channel: ch });\n }\n\n // Re-join presence channels (reconnect after drop)\n for (const [channel, info] of this.joinedPresence) {\n this.sendRaw({ action: 'presence:join', channel, userId: info.userId, metadata: info.metadata });\n }\n\n // Flush pending messages\n for (const msg of this.pendingMessages) {\n this.ws?.send(msg);\n }\n this.pendingMessages = [];\n\n // Start auto-ping to keep connection alive (25s interval)\n this.stopPing();\n this.pingInterval = setInterval(() => this.sendRaw({ action: 'ping' }), 25000);\n };\n\n this.ws.onmessage = (ev) => {\n try {\n const event: RealtimeEvent = JSON.parse(ev.data);\n this.log('received', event);\n this.dispatch(event);\n } catch (e) {\n this.log('malformed message', ev.data, e);\n }\n };\n\n this.ws.onerror = (ev) => {\n this.log('error', ev);\n };\n\n this.ws.onclose = () => {\n this.log('disconnected');\n this.ws = null;\n this.stopPing();\n if (!this.closed && this.autoReconnect) {\n this.scheduleReconnect();\n }\n };\n }\n\n private stopPing() {\n if (this.pingInterval) { clearInterval(this.pingInterval); this.pingInterval = null; }\n }\n\n private scheduleReconnect() {\n const delay = Math.min(1000 * Math.pow(2, this.attempt++), this.maxBackoff);\n this.log('reconnecting in', delay, 'ms');\n setTimeout(() => this.connect(), delay);\n }\n\n private sendRaw(msg: Record<string, any>) {\n const json = JSON.stringify(msg);\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n this.ws.send(json);\n } else {\n this.pendingMessages.push(json);\n }\n }\n\n private dispatch(event: RealtimeEvent) {\n // Notify global listeners\n this.globalListeners.forEach(cb => cb(event));\n\n // Notify channel-specific listeners\n if (event.channel) {\n const listeners = this.channelListeners.get(event.channel);\n if (listeners) {\n listeners.forEach(cb => cb(event));\n }\n }\n\n // Handle presence events\n if (event.event === 'presence:join' || event.event === 'presence:leave') {\n const presenceSet = this.presenceListeners.get(event.channel);\n if (presenceSet) {\n const member: PresenceMember = {\n userId: event.userId || '',\n metadata: event.metadata,\n lastSeen: event.ts,\n };\n if (event.event === 'presence:join') {\n presenceSet.onJoin.forEach(cb => cb(member));\n } else {\n presenceSet.onLeave.forEach(cb => cb(member));\n }\n }\n }\n }\n\n /** Subscribe to messages on a channel */\n subscribe(channel: string, callback: EventCallback, options?: { token?: string }): Unsubscribe {\n if (!this.channelListeners.has(channel)) {\n this.channelListeners.set(channel, new Set());\n }\n this.channelListeners.get(channel)!.add(callback);\n\n // Send subscribe message if not already subscribed\n if (!this.subscribedChannels.has(channel)) {\n this.subscribedChannels.add(channel);\n const msg: Record<string, any> = { action: 'subscribe', channel };\n if (options?.token) {\n msg.token = options.token;\n }\n this.sendRaw(msg);\n }\n\n return () => {\n const listeners = this.channelListeners.get(channel);\n if (listeners) {\n listeners.delete(callback);\n if (listeners.size === 0) {\n this.channelListeners.delete(channel);\n this.subscribedChannels.delete(channel);\n this.sendRaw({ action: 'unsubscribe', channel });\n }\n }\n };\n }\n\n /** Listen to all events across all channels */\n onAny(callback: EventCallback): Unsubscribe {\n this.globalListeners.add(callback);\n return () => { this.globalListeners.delete(callback); };\n }\n\n /** Publish a message to a channel */\n publish(channel: string, data: any, options?: { userId?: string }): void {\n this.sendRaw({\n action: 'publish',\n channel,\n data,\n userId: options?.userId,\n });\n }\n\n /** Get a presence handle for a channel */\n presence(channel: string) {\n if (!this.presenceListeners.has(channel)) {\n this.presenceListeners.set(channel, {\n onJoin: new Set(),\n onLeave: new Set(),\n });\n }\n const listeners = this.presenceListeners.get(channel)!;\n\n return {\n /** Join the channel with presence (auto-rejoins on reconnect) */\n join: (userId: string, metadata?: any): void => {\n this.joinedPresence.set(channel, { userId, metadata });\n this.sendRaw({\n action: 'presence:join',\n channel,\n userId,\n metadata,\n });\n },\n\n /** Leave the channel */\n leave: (): void => {\n this.joinedPresence.delete(channel);\n this.sendRaw({ action: 'presence:leave', channel });\n },\n\n /** Listen for users joining */\n onJoin: (callback: (member: PresenceMember) => void): Unsubscribe => {\n listeners.onJoin.add(callback);\n return () => { listeners.onJoin.delete(callback); };\n },\n\n /** Listen for users leaving */\n onLeave: (callback: (member: PresenceMember) => void): Unsubscribe => {\n listeners.onLeave.add(callback);\n return () => { listeners.onLeave.delete(callback); };\n },\n };\n }\n\n /** Get current client ID */\n getClientId(): string {\n return this.clientId;\n }\n\n /** Check if connected */\n isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n /** Disconnect and stop reconnecting */\n disconnect(): void {\n this.closed = true;\n this.stopPing();\n // Send leave for all joined presence channels before closing\n for (const [channel] of this.joinedPresence) {\n this.sendRaw({ action: 'presence:leave', channel });\n }\n this.joinedPresence.clear();\n this.ws?.close();\n this.ws = null;\n this.pendingMessages = [];\n this.log('disconnected (manual)');\n }\n}\n","// MediaRoom — SFU-agnostic browser client for video/audio rooms\n// Wraps livekit-client internally. If the SFU is swapped, only this file changes.\n\nimport {\n Room,\n RoomEvent,\n type RemoteParticipant,\n type RemoteTrackPublication,\n type RemoteTrack,\n type LocalParticipant as LKLocalParticipant,\n type Participant as LKParticipant,\n type TrackPublication,\n Track,\n ConnectionState,\n type DisconnectReason,\n type RoomConnectOptions,\n} from 'livekit-client';\n\n// ─── Events ───\n\nexport enum MediaRoomEvent {\n Connected = 'connected',\n Reconnecting = 'reconnecting',\n Reconnected = 'reconnected',\n Disconnected = 'disconnected',\n ParticipantJoined = 'participantJoined',\n ParticipantLeft = 'participantLeft',\n TrackSubscribed = 'trackSubscribed',\n TrackUnsubscribed = 'trackUnsubscribed',\n TrackMuted = 'trackMuted',\n TrackUnmuted = 'trackUnmuted',\n ActiveSpeakersChanged = 'activeSpeakersChanged',\n DataReceived = 'dataReceived',\n RecordingStatusChanged = 'recordingStatusChanged',\n MediaDevicesChanged = 'mediaDevicesChanged',\n}\n\n// ─── Types ───\n\nexport type TrackSource = 'camera' | 'microphone' | 'screen_share' | 'screen_share_audio';\nexport type TrackKind = 'audio' | 'video';\n\nexport interface MediaTrackPublication {\n trackSid: string;\n source: TrackSource;\n kind: TrackKind;\n muted: boolean;\n track?: MediaStreamTrack;\n}\n\nexport interface MediaParticipant {\n identity: string;\n name?: string;\n metadata?: string;\n isSpeaking: boolean;\n audioLevel: number;\n tracks: MediaTrackPublication[];\n}\n\nexport interface VideoResolution {\n width: number;\n height: number;\n frameRate?: number;\n}\n\nexport interface MediaRoomOptions {\n autoSubscribe?: boolean;\n adaptiveStream?: boolean;\n dynacast?: boolean;\n}\n\n// ─── Helpers ───\n\nfunction mapSource(source: Track.Source): TrackSource {\n switch (source) {\n case Track.Source.Camera: return 'camera';\n case Track.Source.Microphone: return 'microphone';\n case Track.Source.ScreenShare: return 'screen_share';\n case Track.Source.ScreenShareAudio: return 'screen_share_audio';\n default: return 'camera';\n }\n}\n\nfunction mapParticipant(p: LKParticipant): MediaParticipant {\n const tracks: MediaTrackPublication[] = [];\n p.trackPublications.forEach((pub: TrackPublication) => {\n tracks.push({\n trackSid: pub.trackSid,\n source: mapSource(pub.source),\n kind: pub.kind === Track.Kind.Video ? 'video' : 'audio',\n muted: pub.isMuted,\n track: pub.track?.mediaStreamTrack,\n });\n });\n return {\n identity: p.identity,\n name: p.name,\n metadata: p.metadata,\n isSpeaking: p.isSpeaking,\n audioLevel: p.audioLevel,\n tracks,\n };\n}\n\n/** Attach a media track to a video or audio element */\nexport function attachTrack(track: MediaStreamTrack, element: HTMLVideoElement | HTMLAudioElement): void {\n const stream = new MediaStream([track]);\n element.srcObject = stream;\n if (element instanceof HTMLVideoElement) {\n element.playsInline = true;\n }\n element.play().catch(() => {}); // autoplay may be blocked\n}\n\n/** Detach media from an element */\nexport function detachTrack(element: HTMLVideoElement | HTMLAudioElement): void {\n element.srcObject = null;\n}\n\n// ─── MediaLocalParticipant ───\n\nexport class MediaLocalParticipant implements MediaParticipant {\n private lp: LKLocalParticipant;\n\n /** @internal */\n constructor(lp: LKLocalParticipant) { this.lp = lp; }\n\n get identity() { return this.lp.identity; }\n get name() { return this.lp.name; }\n get metadata() { return this.lp.metadata; }\n get isSpeaking() { return this.lp.isSpeaking; }\n get audioLevel() { return this.lp.audioLevel; }\n\n get tracks(): MediaTrackPublication[] {\n return mapParticipant(this.lp).tracks;\n }\n\n // Camera\n get isCameraEnabled(): boolean {\n return !!this.lp.getTrackPublication(Track.Source.Camera)?.track && !this.lp.getTrackPublication(Track.Source.Camera)?.isMuted;\n }\n async enableCamera(options?: { deviceId?: string; resolution?: VideoResolution }): Promise<void> {\n await this.lp.setCameraEnabled(true, {\n deviceId: options?.deviceId,\n resolution: options?.resolution ? { width: options.resolution.width, height: options.resolution.height, frameRate: options.resolution.frameRate } : undefined,\n });\n }\n async disableCamera(): Promise<void> {\n await this.lp.setCameraEnabled(false);\n }\n\n // Microphone\n get isMicrophoneEnabled(): boolean {\n return !!this.lp.getTrackPublication(Track.Source.Microphone)?.track && !this.lp.getTrackPublication(Track.Source.Microphone)?.isMuted;\n }\n async enableMicrophone(options?: { deviceId?: string }): Promise<void> {\n await this.lp.setMicrophoneEnabled(true, { deviceId: options?.deviceId });\n }\n async disableMicrophone(): Promise<void> {\n await this.lp.setMicrophoneEnabled(false);\n }\n\n // Screen share\n get isScreenShareEnabled(): boolean {\n return !!this.lp.getTrackPublication(Track.Source.ScreenShare)?.track;\n }\n async enableScreenShare(options?: { audio?: boolean }): Promise<void> {\n await this.lp.setScreenShareEnabled(true, { audio: options?.audio });\n }\n async disableScreenShare(): Promise<void> {\n await this.lp.setScreenShareEnabled(false);\n }\n\n // Metadata\n async setName(name: string): Promise<void> { await this.lp.setName(name); }\n async setMetadata(metadata: string): Promise<void> { await this.lp.setMetadata(metadata); }\n\n // Data\n async sendData(data: Uint8Array, options?: { reliable?: boolean }): Promise<void> {\n await this.lp.publishData(data, { reliable: options?.reliable ?? true });\n }\n}\n\n// ─── MediaRoom ───\n\ntype EventCallback = (...args: any[]) => void;\n\nexport class MediaRoom {\n private room: Room;\n private listeners = new Map<MediaRoomEvent, Set<EventCallback>>();\n private _localParticipant!: MediaLocalParticipant;\n\n constructor() {\n this.room = new Room();\n }\n\n async connect(url: string, token: string, options?: MediaRoomOptions): Promise<void> {\n const opts: RoomConnectOptions = {};\n if (options?.autoSubscribe !== undefined) opts.autoSubscribe = options.autoSubscribe;\n this.room.options.adaptiveStream = options?.adaptiveStream ?? true;\n this.room.options.dynacast = options?.dynacast ?? true;\n\n this.wireEvents();\n await this.room.connect(url, token, opts);\n this._localParticipant = new MediaLocalParticipant(this.room.localParticipant);\n }\n\n async disconnect(): Promise<void> {\n await this.room.disconnect();\n }\n\n get state(): 'disconnected' | 'connecting' | 'connected' | 'reconnecting' {\n switch (this.room.state) {\n case ConnectionState.Connected: return 'connected';\n case ConnectionState.Connecting: return 'connecting';\n case ConnectionState.Reconnecting: return 'reconnecting';\n default: return 'disconnected';\n }\n }\n\n get localParticipant(): MediaLocalParticipant { return this._localParticipant; }\n\n get participants(): Map<string, MediaParticipant> {\n const map = new Map<string, MediaParticipant>();\n this.room.remoteParticipants.forEach((p, id) => map.set(id, mapParticipant(p)));\n return map;\n }\n\n get activeSpeakers(): MediaParticipant[] {\n return this.room.activeSpeakers.map(mapParticipant);\n }\n\n get name(): string { return this.room.name; }\n get numParticipants(): number { return this.room.numParticipants; }\n get isRecording(): boolean { return this.room.isRecording; }\n\n // Events\n on(event: MediaRoomEvent, callback: EventCallback): this {\n if (!this.listeners.has(event)) this.listeners.set(event, new Set());\n this.listeners.get(event)!.add(callback);\n return this;\n }\n\n off(event: MediaRoomEvent, callback: EventCallback): this {\n this.listeners.get(event)?.delete(callback);\n return this;\n }\n\n private emit(event: MediaRoomEvent, ...args: any[]) {\n this.listeners.get(event)?.forEach(cb => cb(...args));\n }\n\n // Device switching\n async switchCamera(deviceId: string): Promise<void> {\n await this.room.switchActiveDevice('videoinput', deviceId);\n }\n async switchMicrophone(deviceId: string): Promise<void> {\n await this.room.switchActiveDevice('audioinput', deviceId);\n }\n async switchSpeaker(deviceId: string): Promise<void> {\n await this.room.switchActiveDevice('audiooutput', deviceId);\n }\n\n // Static device enumeration\n static async getDevices(): Promise<MediaDeviceInfo[]> {\n return navigator.mediaDevices.enumerateDevices();\n }\n static async getCameras(): Promise<MediaDeviceInfo[]> {\n return (await MediaRoom.getDevices()).filter(d => d.kind === 'videoinput');\n }\n static async getMicrophones(): Promise<MediaDeviceInfo[]> {\n return (await MediaRoom.getDevices()).filter(d => d.kind === 'audioinput');\n }\n static async getSpeakers(): Promise<MediaDeviceInfo[]> {\n return (await MediaRoom.getDevices()).filter(d => d.kind === 'audiooutput');\n }\n\n // ─── Event wiring (LiveKit → MediaRoom) ───\n\n private wireEvents() {\n const r = this.room;\n r.on(RoomEvent.Connected, () => this.emit(MediaRoomEvent.Connected));\n r.on(RoomEvent.Reconnecting, () => this.emit(MediaRoomEvent.Reconnecting));\n r.on(RoomEvent.Reconnected, () => this.emit(MediaRoomEvent.Reconnected));\n r.on(RoomEvent.Disconnected, (reason?: DisconnectReason) => this.emit(MediaRoomEvent.Disconnected, reason));\n r.on(RoomEvent.MediaDevicesChanged, () => this.emit(MediaRoomEvent.MediaDevicesChanged));\n r.on(RoomEvent.RecordingStatusChanged, (recording: boolean) => this.emit(MediaRoomEvent.RecordingStatusChanged, recording));\n\n r.on(RoomEvent.ParticipantConnected, (p: RemoteParticipant) =>\n this.emit(MediaRoomEvent.ParticipantJoined, mapParticipant(p)));\n r.on(RoomEvent.ParticipantDisconnected, (p: RemoteParticipant) =>\n this.emit(MediaRoomEvent.ParticipantLeft, mapParticipant(p)));\n\n r.on(RoomEvent.TrackSubscribed, (track: RemoteTrack, pub: RemoteTrackPublication, p: RemoteParticipant) =>\n this.emit(MediaRoomEvent.TrackSubscribed, track.mediaStreamTrack, mapParticipant(p)));\n r.on(RoomEvent.TrackUnsubscribed, (track: RemoteTrack, pub: RemoteTrackPublication, p: RemoteParticipant) =>\n this.emit(MediaRoomEvent.TrackUnsubscribed, track.mediaStreamTrack, mapParticipant(p)));\n\n r.on(RoomEvent.TrackMuted, (pub: TrackPublication, p: LKParticipant) =>\n this.emit(MediaRoomEvent.TrackMuted, mapParticipant(p)));\n r.on(RoomEvent.TrackUnmuted, (pub: TrackPublication, p: LKParticipant) =>\n this.emit(MediaRoomEvent.TrackUnmuted, mapParticipant(p)));\n\n r.on(RoomEvent.ActiveSpeakersChanged, (speakers: LKParticipant[]) =>\n this.emit(MediaRoomEvent.ActiveSpeakersChanged, speakers.map(mapParticipant)));\n\n r.on(RoomEvent.DataReceived, (data: Uint8Array, p?: RemoteParticipant) =>\n this.emit(MediaRoomEvent.DataReceived, data, p ? mapParticipant(p) : undefined));\n }\n}\n","/**\n * @fileoverview Maravilla Platform SDK\n * \n * This package provides the main interface for accessing Maravilla runtime services\n * including Key-Value storage and Database operations. It automatically detects the\n * runtime environment and provides the appropriate implementation.\n * \n * ## Environment Detection\n * \n * The SDK automatically detects and adapts to different environments:\n * - **Production**: Uses native Maravilla runtime APIs\n * - **Development**: Connects to development server via HTTP\n * - **Testing**: Can be mocked or use development server\n * \n * ## Key Features\n * \n * - **KV Storage**: Cloudflare Workers KV-compatible API for key-value operations\n * - **Database**: MongoDB-style document database operations\n * - **Multi-tenancy**: Built-in tenant isolation and support\n * - **TypeScript**: Fully typed APIs with comprehensive JSDoc comments\n * - **Development-friendly**: Seamless development server integration\n * \n * @example\n * ```typescript\n * import { getPlatform } from '@maravilla/platform';\n * \n * const platform = getPlatform();\n * \n * // KV operations\n * await platform.env.KV.cache.put('user:123', { name: 'John' });\n * const user = await platform.env.KV.cache.get('user:123');\n * \n * // Database operations\n * const userId = await platform.env.DB.insertOne('users', {\n * name: 'John Doe',\n * email: 'john@example.com'\n * });\n * \n * const users = await platform.env.DB.find('users', { active: true });\n * ```\n * \n * @author Maravilla Team\n * @since 1.0.0\n */\n\nimport type { Platform } from './types.js';\nimport { createRemoteClient } from './remote-client.js';\n\nexport * from './types.js';\nexport * from './ren.js';\nexport * from './realtime.js';\nexport * from './media.js';\nexport * from './media-room.js';\n\n/**\n * Global platform instance injected by Maravilla runtime or development tools.\n * This should not be accessed directly - use getPlatform() instead.\n * \n * @internal\n */\n\ndeclare global {\n var __maravilla_platform: Platform | undefined;\n var platform: Platform | undefined;\n}\n\nlet cachedPlatform: Platform | null = null;\n\n/**\n * Get the platform instance. This will:\n * 1. Check if running in Maravilla runtime (global.platform exists)\n * 2. Check if a platform was injected via vite plugin or hooks\n * 3. Fall back to remote client for development\n * \n * The platform provides access to KV storage and database operations,\n * with automatic environment detection for seamless development and production.\n * \n * @param options - Optional configuration for development mode\n * @param options.devServerUrl - URL of the development server (defaults to MARAVILLA_DEV_SERVER env var or http://localhost:3001)\n * @param options.tenant - Tenant identifier for multi-tenancy (defaults to MARAVILLA_TENANT env var or 'dev-tenant')\n * @returns Platform instance with access to KV and database services\n * \n * @example\n * ```typescript\n * import { getPlatform } from '@maravilla/platform';\n * \n * // Basic usage (auto-detects environment)\n * const platform = getPlatform();\n * \n * // Development with custom server\n * const platform = getPlatform({\n * devServerUrl: 'http://localhost:3001',\n * tenant: 'my-app'\n * });\n * \n * // Use KV storage\n * await platform.env.KV.cache.put('user:123', { name: 'John' });\n * const user = await platform.env.KV.cache.get('user:123');\n * \n * // Use database\n * const userId = await platform.env.DB.insertOne('users', {\n * name: 'John Doe',\n * email: 'john@example.com'\n * });\n * ```\n */\nexport function getPlatform(options?: {\n devServerUrl?: string;\n tenant?: string;\n}): Platform {\n // Return cached if available\n if (cachedPlatform) {\n return cachedPlatform;\n }\n\n // 1. Check if we're in the real Maravilla runtime\n if (typeof globalThis !== 'undefined') {\n // Check for native platform (in production runtime)\n if (globalThis.platform) {\n console.log('[platform] Using native Maravilla platform');\n cachedPlatform = globalThis.platform;\n return cachedPlatform;\n }\n\n // Check for injected platform (from vite plugin or hooks)\n if (globalThis.__maravilla_platform) {\n console.log('[platform] Using injected platform');\n cachedPlatform = globalThis.__maravilla_platform;\n return cachedPlatform;\n }\n }\n\n // 2. Fall back to remote client for development\n const devServerUrl = options?.devServerUrl || process.env.MARAVILLA_DEV_SERVER || 'http://localhost:3001';\n const tenant = options?.tenant || process.env.MARAVILLA_TENANT || 'dev-tenant-001';\n \n console.log(`[platform] Creating remote client for ${devServerUrl}`);\n cachedPlatform = createRemoteClient(devServerUrl, tenant);\n \n // Cache it globally for other modules\n if (typeof globalThis !== 'undefined') {\n globalThis.__maravilla_platform = cachedPlatform;\n }\n \n return cachedPlatform;\n}\n\n/**\n * Clear the cached platform instance (useful for testing).\n * \n * This function resets the internal platform cache and clears any globally\n * injected platform instances. Subsequent calls to getPlatform() will\n * re-initialize the platform based on the current environment.\n * \n * @example\n * ```typescript\n * import { clearPlatformCache, getPlatform } from '@maravilla/platform';\n * \n * // In tests, clear cache between test cases\n * afterEach(() => {\n * clearPlatformCache();\n * });\n * \n * // Or when switching between different environments\n * clearPlatformCache();\n * const newPlatform = getPlatform({ devServerUrl: 'http://localhost:3002' });\n * ```\n */\nexport function clearPlatformCache(): void {\n cachedPlatform = null;\n if (typeof globalThis !== 'undefined') {\n globalThis.__maravilla_platform = undefined;\n }\n}"],"mappings":"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","names":["MediaRoomEvent"]}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -132,7 +132,7 @@ export function getPlatform(options?: {
|
|
|
132
132
|
|
|
133
133
|
// 2. Fall back to remote client for development
|
|
134
134
|
const devServerUrl = options?.devServerUrl || process.env.MARAVILLA_DEV_SERVER || 'http://localhost:3001';
|
|
135
|
-
const tenant = options?.tenant || process.env.MARAVILLA_TENANT || 'dev-tenant';
|
|
135
|
+
const tenant = options?.tenant || process.env.MARAVILLA_TENANT || 'dev-tenant-001';
|
|
136
136
|
|
|
137
137
|
console.log(`[platform] Creating remote client for ${devServerUrl}`);
|
|
138
138
|
cachedPlatform = createRemoteClient(devServerUrl, tenant);
|
package/src/realtime.ts
CHANGED
|
@@ -54,6 +54,8 @@ export class RealtimeClient {
|
|
|
54
54
|
}>();
|
|
55
55
|
private subscribedChannels = new Set<string>();
|
|
56
56
|
private pendingMessages: string[] = [];
|
|
57
|
+
private pingInterval: ReturnType<typeof setInterval> | null = null;
|
|
58
|
+
private joinedPresence = new Map<string, { userId: string; metadata?: any }>();
|
|
57
59
|
|
|
58
60
|
constructor(opts: RealtimeClientOptions = {}) {
|
|
59
61
|
this.wsEndpoint = opts.wsEndpoint || this.detectEndpoint();
|
|
@@ -106,11 +108,20 @@ export class RealtimeClient {
|
|
|
106
108
|
this.sendRaw({ action: 'subscribe', channel: ch });
|
|
107
109
|
}
|
|
108
110
|
|
|
111
|
+
// Re-join presence channels (reconnect after drop)
|
|
112
|
+
for (const [channel, info] of this.joinedPresence) {
|
|
113
|
+
this.sendRaw({ action: 'presence:join', channel, userId: info.userId, metadata: info.metadata });
|
|
114
|
+
}
|
|
115
|
+
|
|
109
116
|
// Flush pending messages
|
|
110
117
|
for (const msg of this.pendingMessages) {
|
|
111
118
|
this.ws?.send(msg);
|
|
112
119
|
}
|
|
113
120
|
this.pendingMessages = [];
|
|
121
|
+
|
|
122
|
+
// Start auto-ping to keep connection alive (25s interval)
|
|
123
|
+
this.stopPing();
|
|
124
|
+
this.pingInterval = setInterval(() => this.sendRaw({ action: 'ping' }), 25000);
|
|
114
125
|
};
|
|
115
126
|
|
|
116
127
|
this.ws.onmessage = (ev) => {
|
|
@@ -130,12 +141,17 @@ export class RealtimeClient {
|
|
|
130
141
|
this.ws.onclose = () => {
|
|
131
142
|
this.log('disconnected');
|
|
132
143
|
this.ws = null;
|
|
144
|
+
this.stopPing();
|
|
133
145
|
if (!this.closed && this.autoReconnect) {
|
|
134
146
|
this.scheduleReconnect();
|
|
135
147
|
}
|
|
136
148
|
};
|
|
137
149
|
}
|
|
138
150
|
|
|
151
|
+
private stopPing() {
|
|
152
|
+
if (this.pingInterval) { clearInterval(this.pingInterval); this.pingInterval = null; }
|
|
153
|
+
}
|
|
154
|
+
|
|
139
155
|
private scheduleReconnect() {
|
|
140
156
|
const delay = Math.min(1000 * Math.pow(2, this.attempt++), this.maxBackoff);
|
|
141
157
|
this.log('reconnecting in', delay, 'ms');
|
|
@@ -238,8 +254,9 @@ export class RealtimeClient {
|
|
|
238
254
|
const listeners = this.presenceListeners.get(channel)!;
|
|
239
255
|
|
|
240
256
|
return {
|
|
241
|
-
/** Join the channel with presence */
|
|
257
|
+
/** Join the channel with presence (auto-rejoins on reconnect) */
|
|
242
258
|
join: (userId: string, metadata?: any): void => {
|
|
259
|
+
this.joinedPresence.set(channel, { userId, metadata });
|
|
243
260
|
this.sendRaw({
|
|
244
261
|
action: 'presence:join',
|
|
245
262
|
channel,
|
|
@@ -250,6 +267,7 @@ export class RealtimeClient {
|
|
|
250
267
|
|
|
251
268
|
/** Leave the channel */
|
|
252
269
|
leave: (): void => {
|
|
270
|
+
this.joinedPresence.delete(channel);
|
|
253
271
|
this.sendRaw({ action: 'presence:leave', channel });
|
|
254
272
|
},
|
|
255
273
|
|
|
@@ -280,6 +298,12 @@ export class RealtimeClient {
|
|
|
280
298
|
/** Disconnect and stop reconnecting */
|
|
281
299
|
disconnect(): void {
|
|
282
300
|
this.closed = true;
|
|
301
|
+
this.stopPing();
|
|
302
|
+
// Send leave for all joined presence channels before closing
|
|
303
|
+
for (const [channel] of this.joinedPresence) {
|
|
304
|
+
this.sendRaw({ action: 'presence:leave', channel });
|
|
305
|
+
}
|
|
306
|
+
this.joinedPresence.clear();
|
|
283
307
|
this.ws?.close();
|
|
284
308
|
this.ws = null;
|
|
285
309
|
this.pendingMessages = [];
|
package/src/ren.ts
CHANGED
|
@@ -50,39 +50,18 @@ export class RenClient {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
private detectEndpoint(): string {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
console.log('[REN detectEndpoint] globalThis.platform:', (globalThis as any)?.platform);
|
|
56
|
-
console.log('[REN detectEndpoint] globalThis.__maravilla_platform:', (globalThis as any)?.__maravilla_platform);
|
|
57
|
-
console.log('[REN detectEndpoint] window:', typeof window);
|
|
58
|
-
console.log('[REN detectEndpoint] window.location:', typeof window !== 'undefined' ? window.location.href : 'N/A');
|
|
59
|
-
|
|
60
|
-
// Check if we're in production runtime (has native platform)
|
|
61
|
-
if (typeof globalThis !== 'undefined' && (globalThis as any).platform) {
|
|
62
|
-
console.log('[REN detectEndpoint] Detected production runtime, using relative endpoint');
|
|
63
|
-
return '/api/maravilla/ren';
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Check if we're running on a dev server (Vite uses 5173+ but can pick any available port)
|
|
53
|
+
// Port check FIRST — catches any dev server (Vite, SvelteKit, etc.)
|
|
54
|
+
// regardless of whether globalThis.platform was set by getPlatform()
|
|
67
55
|
if (typeof window !== 'undefined') {
|
|
68
56
|
const port = window.location.port;
|
|
69
|
-
if (port && port !== '3001' && port !== '80' && port !== '443') {
|
|
70
|
-
|
|
71
|
-
console.log('[REN detectEndpoint] Detected dev mode (port ' + port + '), using dev server:', devServerUrl);
|
|
72
|
-
return `${devServerUrl}/api/maravilla/ren`;
|
|
57
|
+
if (port && port !== '3001' && port !== '80' && port !== '443' && port !== '') {
|
|
58
|
+
return `http://${window.location.hostname}:3001/api/maravilla/ren`;
|
|
73
59
|
}
|
|
74
60
|
}
|
|
75
61
|
|
|
76
|
-
//
|
|
77
|
-
if (typeof globalThis !== 'undefined' && (globalThis as any).
|
|
78
|
-
|
|
79
|
-
let devServerUrl = 'http://localhost:3001';
|
|
80
|
-
// Also check window location to use same host in browser
|
|
81
|
-
if (typeof window !== 'undefined' && window.location.hostname !== 'localhost') {
|
|
82
|
-
devServerUrl = `http://${window.location.hostname}:3001`;
|
|
83
|
-
}
|
|
84
|
-
console.log('[REN detectEndpoint] Detected injected platform, using dev server:', devServerUrl);
|
|
85
|
-
return `${devServerUrl}/api/maravilla/ren`;
|
|
62
|
+
// Production runtime (native Deno platform) — use relative URL
|
|
63
|
+
if (typeof globalThis !== 'undefined' && (globalThis as any).platform) {
|
|
64
|
+
return '/api/maravilla/ren';
|
|
86
65
|
}
|
|
87
66
|
|
|
88
67
|
// Default to relative URL (works in preview mode and production)
|