@playcademy/sdk 0.0.1-beta.32 → 0.0.1-beta.34

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.
@@ -433,7 +433,7 @@ export declare class PlaycademyClient {
433
433
  token: {
434
434
  get: () => Promise<import("./namespaces/realtime").RealtimeTokenResponse>;
435
435
  };
436
- open(channel?: string): Promise<import("@playcademy/realtime/server/types").RealtimeChannel>;
436
+ open(channel?: string, url?: string): Promise<import("@playcademy/realtime/server/types").RealtimeChannel>;
437
437
  };
438
438
  /** Auto-initializes a PlaycademyClient with context from the environment */
439
439
  static init: typeof init;
@@ -43,7 +43,7 @@ export declare function createRealtimeNamespace(client: PlaycademyClient): {
43
43
  *
44
44
  * **Channel Naming**:
45
45
  * - Channels are automatically scoped to the current game
46
- * - Channel names should be descriptive (e.g., "chat", "lobby", "map_forest")
46
+ * - Channel names should be descriptive (e.g. "chat", "lobby", "map_forest")
47
47
  * - The "default" channel is used if no name is specified
48
48
  *
49
49
  * **Connection Management**:
@@ -52,12 +52,15 @@ export declare function createRealtimeNamespace(client: PlaycademyClient): {
52
52
  * - Remember to call `channel.close()` when done to prevent resource leaks
53
53
  *
54
54
  * **Error Handling**:
55
- * - Throws if no gameId is configured on the client
55
+ * - Throws if no `gameId` is configured on the client
56
56
  * - Throws if token retrieval fails
57
57
  * - Throws if WebSocket connection fails
58
58
  *
59
59
  * @param channel - Channel name (defaults to "default")
60
- * @returns Promise resolving to a RealtimeChannel instance
60
+ * @param url - Optional WebSocket **base** URL (e.g. `ws://localhost:3000`).
61
+ * If omitted, the client's `baseUrl` is used and automatically converted to
62
+ * a WebSocket URL (`http` → `ws`, `https` → `wss`).
63
+ * @returns Promise resolving to a `RealtimeChannel` instance
61
64
  *
62
65
  * @example
63
66
  * ```typescript
@@ -78,7 +81,10 @@ export declare function createRealtimeNamespace(client: PlaycademyClient): {
78
81
  *
79
82
  * // Open a named channel
80
83
  * const chatChannel = await client.realtime.open('chat')
84
+ *
85
+ * // Open a channel against a custom realtime endpoint
86
+ * const localChannel = await client.realtime.open('dev', 'ws://localhost:3000')
81
87
  * ```
82
88
  */
83
- open(channel?: string): Promise<RealtimeChannel>;
89
+ open(channel?: string, url?: string): Promise<RealtimeChannel>;
84
90
  };
package/dist/index.js CHANGED
@@ -1499,8 +1499,23 @@ class RealtimeChannelClient {
1499
1499
  async connect() {
1500
1500
  try {
1501
1501
  const token = await this.getToken();
1502
- const wsUrl = this.baseUrl.replace(/^http/, "ws");
1503
- const url = `${wsUrl}?token=${encodeURIComponent(token)}&c=${encodeURIComponent(this._channelName)}`;
1502
+ let wsBase;
1503
+ if (/^ws(s)?:\/\//.test(this.baseUrl)) {
1504
+ wsBase = this.baseUrl;
1505
+ } else if (/^http(s)?:\/\//.test(this.baseUrl)) {
1506
+ wsBase = this.baseUrl.replace(/^http/, "ws");
1507
+ } else {
1508
+ const isBrowser2 = typeof window !== "undefined";
1509
+ if (isBrowser2) {
1510
+ const proto = window.location.protocol === "https:" ? "wss" : "ws";
1511
+ wsBase = `${proto}://${window.location.host}${this.baseUrl}`;
1512
+ } else {
1513
+ wsBase = `ws://${this.baseUrl.replace(/^\//, "")}`;
1514
+ }
1515
+ }
1516
+ const url = new URL(wsBase);
1517
+ url.searchParams.set("token", token);
1518
+ url.searchParams.set("c", this._channelName);
1504
1519
  this.ws = new WebSocket(url);
1505
1520
  this.setupEventHandlers();
1506
1521
  this.setupTokenRefreshListener();
@@ -1680,11 +1695,18 @@ function createRealtimeNamespace(client) {
1680
1695
  return client["request"]("/realtime/token", "POST");
1681
1696
  }
1682
1697
  },
1683
- async open(channel = "default") {
1698
+ async open(channel = "default", url) {
1684
1699
  if (!client["gameId"]) {
1685
1700
  throw new Error("gameId is required for realtime channels");
1686
1701
  }
1687
- const realtimeClient = new RealtimeChannelClient(client["gameId"], channel, () => client.realtime.token.get().then((r) => r.token), client.getBaseUrl());
1702
+ let wsBaseUrl = url;
1703
+ if (!wsBaseUrl && typeof window !== "undefined") {
1704
+ const ctx = window.PLAYCADEMY;
1705
+ if (ctx?.realtimeUrl) {
1706
+ wsBaseUrl = ctx.realtimeUrl;
1707
+ }
1708
+ }
1709
+ const realtimeClient = new RealtimeChannelClient(client["gameId"], channel, () => client.realtime.token.get().then((r) => r.token), wsBaseUrl ?? client.getBaseUrl());
1688
1710
  return realtimeClient.connect();
1689
1711
  }
1690
1712
  };
@@ -1739,7 +1761,8 @@ function createStandaloneConfig() {
1739
1761
  const mockConfig = {
1740
1762
  baseUrl: "/api",
1741
1763
  token: "mock-game-token-for-local-dev",
1742
- gameId: "mock-game-id-from-template"
1764
+ gameId: "mock-game-id-from-template",
1765
+ realtimeUrl: undefined
1743
1766
  };
1744
1767
  window.PLAYCADEMY = mockConfig;
1745
1768
  return mockConfig;
package/dist/types.d.ts CHANGED
@@ -29,6 +29,7 @@ export interface ClientEvents {
29
29
  export type GameContextPayload = {
30
30
  token: string;
31
31
  baseUrl: string;
32
+ realtimeUrl: string;
32
33
  gameId: string;
33
34
  forwardKeys?: string[];
34
35
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/sdk",
3
- "version": "0.0.1-beta.32",
3
+ "version": "0.0.1-beta.34",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -27,16 +27,14 @@
27
27
  "test": "bun test",
28
28
  "test:watch": "bun test --watch"
29
29
  },
30
- "dependencies": {
31
- "@playcademy/logger": "0.0.1"
32
- },
33
30
  "devDependencies": {
34
31
  "@playcademy/data": "0.0.1",
35
32
  "@playcademy/sandbox": "0.1.0-beta.14",
36
33
  "@playcademy/test": "0.0.1",
37
34
  "@types/bun": "latest",
38
35
  "typescript": "^5.7.2",
39
- "yocto-spinner": "^0.2.2"
36
+ "yocto-spinner": "^0.2.2",
37
+ "@playcademy/logger": "0.0.1"
40
38
  },
41
39
  "peerDependencies": {
42
40
  "drizzle-orm": "^0.42.0",