castle-web-sdk 0.4.22 → 0.4.24

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/README.md CHANGED
@@ -4,8 +4,10 @@
4
4
  platform — for example, a deck can save per-player data, post and read
5
5
  scores on a leaderboard, or get a server-synced time for daily content.
6
6
 
7
- Comes with `castle-web init`. Import what you need from
8
- `castle-web-sdk`:
7
+ Comes with every deck: the SDK is a file of the `base` kit each deck
8
+ imports (`imports/castle.base/sdk/`), and the bare specifier resolves
9
+ there — nothing to install, and no deck `package.json` names it. Import
10
+ what you need from `castle-web-sdk`:
9
11
 
10
12
  ```js
11
13
  import { setup, initCard, Storage, Leaderboard } from "castle-web-sdk";
@@ -255,7 +257,10 @@ const dailyPuzzle = (date.daysSinceCastleEpoch % 30) + 1;
255
257
  Returns the signed-in player. Throws `CastleError`
256
258
  (`LOGIN_REQUIRED`) when nobody is signed in.
257
259
 
258
- The returned `CastleUser` has `userId`, `username`, and `isActive`.
260
+ The returned `CastleUser` has `userId`, `username`, `isAnonymous`, and `isActive`.
261
+ Anonymous accounts are real logins with generated `anonymous-user-...` names;
262
+ check `isAnonymous` before accepting writes into anything other players see,
263
+ such as a shared gallery.
259
264
 
260
265
  ```js
261
266
  const me = await User.getCurrent();
@@ -136,6 +136,7 @@ export interface CommandResult {
136
136
  user: {
137
137
  userId: string;
138
138
  username: string;
139
+ isAnonymous?: boolean;
139
140
  } | null;
140
141
  };
141
142
  "time.getServerTime": {
@@ -64,16 +64,20 @@ class MultiplayerConnectionImpl {
64
64
  throw castleError;
65
65
  }
66
66
  }
67
+ // Unserializable data is a mistake in the deck and still throws. A missing
68
+ // socket is not: connections drop whenever a phone sleeps or changes network,
69
+ // and the session recovers on its own. A realtime deck sends every frame and
70
+ // has no useful branch to take meanwhile, so those messages are dropped.
71
+ // State arrives in whole snapshots, so the next send past recovery is current.
67
72
  send(data) {
68
73
  const text = serializeClientMessage(data);
69
- if (this.currentState !== "connected") {
74
+ if (this.currentState === "closed") {
70
75
  throw multiplayerError("MULTIPLAYER_NOT_CONNECTED", "Multiplayer.send requires a connected session.", "Multiplayer.send");
71
76
  }
72
77
  if (this.solo)
73
78
  return;
74
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
75
- throw multiplayerError("MULTIPLAYER_NOT_CONNECTED", "The multiplayer connection is not available.", "Multiplayer.send");
76
- }
79
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN)
80
+ return;
77
81
  this.socket.send(text);
78
82
  }
79
83
  onMessage(callback) {
@@ -177,6 +181,10 @@ class MultiplayerConnectionImpl {
177
181
  this.currentPlayerId = frame.playerId;
178
182
  this.currentSessionId = frame.sessionId;
179
183
  this.reconnectToken = frame.reconnectToken;
184
+ // Re-acquisition is limited to one per outage, not one per session. A phone
185
+ // is backgrounded many times in a play, and each of those is its own outage;
186
+ // without this the second one finds the budget spent and closes for good.
187
+ this.reAcquisitionUsed = false;
180
188
  this.transition("connected");
181
189
  this.startKeepalive();
182
190
  }
package/dist/storage.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CastleError } from "./errors";
2
2
  import { hostRequest } from "./transport";
3
- // The child coalesces writes -- a frame can set the same key many times -- and
4
- // hands them to the host on this interval. The host, not the deck, talks to the
3
+ // Writes coalesce by key as they are made, so setting the same key repeatedly
4
+ // costs one entry; this is how often the result is handed to the host. The host, not the deck, talks to the
5
5
  // server and owns retry: it lives in the page, so it is still alive to finish a
6
6
  // write while the deck's frame is being torn down.
7
7
  const FLUSH_INTERVAL_MS = 200;
package/dist/user.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export interface CastleUser {
2
2
  userId: string;
3
3
  username: string;
4
+ isAnonymous: boolean;
4
5
  isActive: boolean;
5
6
  }
6
7
  export interface CastleUserApi {
package/dist/user.js CHANGED
@@ -24,9 +24,13 @@ async function fetchCurrentUser() {
24
24
  operation,
25
25
  });
26
26
  }
27
+ const username = requiredString(user.username, "user.username", operation);
27
28
  return {
28
29
  userId: requiredString(user.userId, "user.userId", operation),
29
- username: requiredString(user.username, "user.username", operation),
30
+ username,
31
+ // A host that predates the flag omits it; anonymous account usernames are
32
+ // always minted as `anonymous-user-<uuid>`, so the prefix is the fallback.
33
+ isAnonymous: user.isAnonymous === true || username.toLowerCase().startsWith("anonymous-user-"),
30
34
  isActive: true,
31
35
  };
32
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "castle-web-sdk",
3
- "version": "0.4.22",
3
+ "version": "0.4.24",
4
4
  "type": "module",
5
5
  "main": "dist/castle.js",
6
6
  "types": "dist/castle.d.ts",
@@ -14,7 +14,7 @@
14
14
  "default": "./dist/server/wrapper.js"
15
15
  }
16
16
  },
17
- "//host": "host.{js,d.ts} is the host-side executor and leaderboardPanel.{js,d.ts} is the host-side leaderboard UI \u2014 both deliberately NOT exported and NOT packaged. They are vendored into host repos via scripts/copy-host-module.mjs; decks must never receive them.",
17
+ "//host": "host.{js,d.ts} is the host-side executor and leaderboardPanel.{js,d.ts} is the host-side leaderboard UI both deliberately NOT exported and NOT packaged. They are vendored into host repos via scripts/copy-host-module.mjs; decks must never receive them.",
18
18
  "files": [
19
19
  "dist",
20
20
  "README.md",