@crowdedkingdoms/crowdy-dsh 0.3.3-test.1 → 0.3.4-dev.1

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.
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "package": "@crowdedkingdoms/crowdy-dsh",
3
- "version": "0.3.3",
4
- "builtAt": "2026-09-16T20:47:57.016Z",
3
+ "version": "0.3.4",
4
+ "builtAt": "2026-09-21T13:56:33.466Z",
5
5
  "source": {
6
6
  "repository": "CrowdedKingdoms/crowdy-dsh",
7
- "commit": "fb6ac78258a70479a88e91c6c191345b0c89fda0",
7
+ "commit": "82dfc27359cf226fd25238720d09807282c31118",
8
8
  "dirty": false
9
9
  },
10
10
  "upstream": {
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "crowdyjs": {
17
17
  "name": "@crowdedkingdoms/crowdyjs",
18
- "version": "17.5.0-test.1",
18
+ "version": "17.6.0-dev.1",
19
19
  "source": "node_modules"
20
20
  },
21
21
  "files": {
@@ -112,7 +112,7 @@
112
112
  "manifest.webmanifest": "sha256:9b54d57eea00f2fdd1affe6e7999a4209a515755f12ac1a3299da2d55e178d4c",
113
113
  "preview/bootstrap-CYECZNXt.js": "sha256:9f303a0485c0badfdd19fad0c357ef050c5b9a343744ab5bf0c70015e7d95092",
114
114
  "preview/crowdy-boot-Cklh1qNl.js": "sha256:b21e8eb126e1cd8a52159d2d577e610df6c8af36a436f9ed5028cfabd11a85ff",
115
- "preview/vfs-image.tar.gz": "sha256:d7472c6fdcf9ba884cb6553bd9ad75aac0e03f50e4b51d38b4cb88f1f8ff55b6",
115
+ "preview/vfs-image.tar.gz": "sha256:f35752ca934b215b2634204b8532a81263c807a620af929c1c1806656e186af5",
116
116
  "preview/worker-C8-_ANaz.js": "sha256:81099e5732dfe0c32bacd5897316b7956c5a0a22da01b4a0c6e21d0a3b9d9aa1"
117
117
  }
118
118
  }
@@ -5889,6 +5889,11 @@ function packMessageBundle(members) {
5889
5889
  }
5890
5890
  var WireMessageType = {
5891
5891
  MESSAGE_BUNDLE: 2,
5892
+ // Buddy v0.30.0: the client advertises what it can read; a session that
5893
+ // advertised BUNDLE_SIGNED receives its bundles with ONE trailing HMAC over the
5894
+ // datagram and members without per-member HMACs (containsAuth = 0).
5895
+ CLIENT_CAPABILITIES: 29,
5896
+ MESSAGE_BUNDLE_SIGNED: 30,
5892
5897
  GENERIC_ERROR_MESSAGE: 3,
5893
5898
  CHANNEL_MESSAGE_REQUEST: 17,
5894
5899
  CHANNEL_MESSAGE_NOTIFICATION: 18,
@@ -6016,6 +6021,16 @@ async function serializeSpatial(ctx, messageType, appId, chunk, distance, decayR
6016
6021
  tailView.setUint8(dataLen + HMAC_SIZE + 8, sequenceNumber & 255);
6017
6022
  return out;
6018
6023
  }
6024
+ var ClientCapability = {
6025
+ /** This client parses MESSAGE_BUNDLE_SIGNED (one HMAC per downlink bundle). */
6026
+ BUNDLE_SIGNED: 1
6027
+ };
6028
+ var SDK_CAPABILITIES = ClientCapability.BUNDLE_SIGNED;
6029
+ function serializeClientCapabilities(ctx, appId, flags = SDK_CAPABILITIES, sequenceNumber = 0) {
6030
+ const payload = new Uint8Array(4);
6031
+ new DataView(payload.buffer).setUint32(0, flags >>> 0, true);
6032
+ return serializeSpatial(ctx, WireMessageType.CLIENT_CAPABILITIES, appId, { x: 0, y: 0, z: 0 }, 0, 0, "", payload, sequenceNumber);
6033
+ }
6019
6034
  function serializeActorUpdate(ctx, input) {
6020
6035
  return serializeSpatial(ctx, WireMessageType.ACTOR_UPDATE_REQUEST_2, input.appId, input.chunk, input.distance ?? 8, input.decayRate ?? 1, input.uuid, decodeBase64(input.state ?? ""), input.sequenceNumber ?? 0);
6021
6036
  }
@@ -6278,23 +6293,12 @@ function parseRelayFrame(bytes) {
6278
6293
  if (bytes.length < 1)
6279
6294
  return [];
6280
6295
  if (bytes[0] === WireMessageType.MESSAGE_BUNDLE) {
6281
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
6282
- const out = [];
6283
- let off = 1;
6284
- while (off + 2 <= bytes.length) {
6285
- const len = view.getUint16(off, true);
6286
- off += 2;
6287
- if (off + len > bytes.length)
6288
- break;
6289
- try {
6290
- const parsed = parseOne(bytes.subarray(off, off + len));
6291
- if (parsed)
6292
- out.push(parsed);
6293
- } catch {
6294
- }
6295
- off += len;
6296
- }
6297
- return out;
6296
+ return walkBundleMembers(bytes, bytes.length);
6297
+ }
6298
+ if (bytes[0] === WireMessageType.MESSAGE_BUNDLE_SIGNED) {
6299
+ if (bytes.length <= 1 + HMAC_SIZE)
6300
+ return [];
6301
+ return walkBundleMembers(bytes, bytes.length - HMAC_SIZE);
6298
6302
  }
6299
6303
  try {
6300
6304
  const parsed = parseOne(bytes);
@@ -6303,6 +6307,25 @@ function parseRelayFrame(bytes) {
6303
6307
  return [];
6304
6308
  }
6305
6309
  }
6310
+ function walkBundleMembers(bytes, end) {
6311
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
6312
+ const out = [];
6313
+ let off = 1;
6314
+ while (off + 2 <= end) {
6315
+ const len = view.getUint16(off, true);
6316
+ off += 2;
6317
+ if (off + len > end)
6318
+ break;
6319
+ try {
6320
+ const parsed = parseOne(bytes.subarray(off, off + len));
6321
+ if (parsed)
6322
+ out.push(parsed);
6323
+ } catch {
6324
+ }
6325
+ off += len;
6326
+ }
6327
+ return out;
6328
+ }
6306
6329
 
6307
6330
  var RELAY_SUBPROTOCOL = "crowdy-relay-v1";
6308
6331
  var BEARER_PREFIX = "bearer.";
@@ -6344,6 +6367,7 @@ var BinaryRelayTransport = class {
6344
6367
  this.everReady = false;
6345
6368
  this.retryTimer = null;
6346
6369
  this.generation = 0;
6370
+ this.capsTimer = null;
6347
6371
  this.pendingMembers = [];
6348
6372
  this.pendingBytes = 0;
6349
6373
  this.bundleTimer = null;
@@ -6361,6 +6385,8 @@ var BinaryRelayTransport = class {
6361
6385
  this.retryMaxDelayMs = config.retryMaxDelayMs ?? 5e3;
6362
6386
  this.bundleSends = config.bundleSends ?? true;
6363
6387
  this.bundleWindowMs = Math.max(0, config.bundleWindowMs ?? 1);
6388
+ this.advertiseCapabilities = config.advertiseCapabilities ?? true;
6389
+ this.capabilitiesIntervalMs = Math.max(1e3, config.capabilitiesIntervalMs ?? 15e3);
6364
6390
  }
6365
6391
  /** Snapshot of the uplink counters. */
6366
6392
  stats() {
@@ -6389,9 +6415,11 @@ var BinaryRelayTransport = class {
6389
6415
  this.retryTimer = null;
6390
6416
  }
6391
6417
  this.generation += 1;
6418
+ this.stopCapabilities();
6392
6419
  const ws = this.ws;
6393
6420
  this.ws = null;
6394
6421
  this.signContext = null;
6422
+ this.stopCapabilities();
6395
6423
  if (ws) {
6396
6424
  try {
6397
6425
  ws.close(1e3, "client-disconnect");
@@ -6513,6 +6541,7 @@ var BinaryRelayTransport = class {
6513
6541
  ws.binaryType = "arraybuffer";
6514
6542
  this.ws = ws;
6515
6543
  this.signContext = null;
6544
+ this.stopCapabilities();
6516
6545
  let sawReady = false;
6517
6546
  ws.onmessage = (event) => {
6518
6547
  if (generation !== this.generation)
@@ -6543,6 +6572,8 @@ var BinaryRelayTransport = class {
6543
6572
  this.ws = null;
6544
6573
  const hadReady = this.signContext != null || sawReady;
6545
6574
  this.signContext = null;
6575
+ this.stopCapabilities();
6576
+ this.stopCapabilities();
6546
6577
  if (!this.desired) {
6547
6578
  this.callbacks.onStatus("disconnected");
6548
6579
  return;
@@ -6586,6 +6617,44 @@ var BinaryRelayTransport = class {
6586
6617
  this.preReadyFailures = 0;
6587
6618
  this.retries = 0;
6588
6619
  this.callbacks.onStatus("connected");
6620
+ this.startCapabilities();
6621
+ }
6622
+ /**
6623
+ * Advertise this build's capabilities now and on a timer while ready. The
6624
+ * server binds them to the router serving our flow; a token refresh or a
6625
+ * relay-side Buddy migration silently resets that, and until the next
6626
+ * advertisement we are served in the older per-member form, which we parse too.
6627
+ */
6628
+ startCapabilities() {
6629
+ this.stopCapabilities();
6630
+ if (!this.advertiseCapabilities)
6631
+ return;
6632
+ void this.sendCapabilities();
6633
+ const timer = setInterval(() => {
6634
+ void this.sendCapabilities();
6635
+ }, this.capabilitiesIntervalMs);
6636
+ timer.unref?.();
6637
+ this.capsTimer = timer;
6638
+ }
6639
+ stopCapabilities() {
6640
+ if (this.capsTimer) {
6641
+ clearInterval(this.capsTimer);
6642
+ this.capsTimer = null;
6643
+ }
6644
+ }
6645
+ async sendCapabilities() {
6646
+ const ctx = this.signContext;
6647
+ if (!ctx || this.appId == null || !this.isReady())
6648
+ return;
6649
+ try {
6650
+ const frame = await serializeClientCapabilities(ctx, this.appId);
6651
+ if (this.signContext !== ctx)
6652
+ return;
6653
+ this.sendFrame(frame);
6654
+ this.flushSends();
6655
+ } catch (error) {
6656
+ this.logger.debug?.(`capabilities not sent: ${String(error)}`);
6657
+ }
6589
6658
  }
6590
6659
  /**
6591
6660
  * The server wants this client on a different instance.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@crowdedkingdoms/crowdyjs",
3
- "version": "17.5.0-test.1",
3
+ "version": "17.6.0-dev.1",
4
4
  "source": "node_modules"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdedkingdoms/crowdy-dsh",
3
- "version": "0.3.3-test.1",
3
+ "version": "0.3.4-dev.1",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -94,11 +94,11 @@
94
94
  "crowdyDsh": {
95
95
  "build": {
96
96
  "package": "@crowdedkingdoms/crowdy-dsh",
97
- "version": "0.3.3",
98
- "builtAt": "2026-09-16T20:47:57.016Z",
97
+ "version": "0.3.4",
98
+ "builtAt": "2026-09-21T13:56:33.466Z",
99
99
  "source": {
100
100
  "repository": "CrowdedKingdoms/crowdy-dsh",
101
- "commit": "fb6ac78258a70479a88e91c6c191345b0c89fda0",
101
+ "commit": "82dfc27359cf226fd25238720d09807282c31118",
102
102
  "dirty": false
103
103
  },
104
104
  "upstream": {
@@ -109,7 +109,7 @@
109
109
  },
110
110
  "crowdyjs": {
111
111
  "name": "@crowdedkingdoms/crowdyjs",
112
- "version": "17.5.0-test.1",
112
+ "version": "17.6.0-dev.1",
113
113
  "source": "node_modules"
114
114
  },
115
115
  "files": {
@@ -206,7 +206,7 @@
206
206
  "manifest.webmanifest": "sha256:9b54d57eea00f2fdd1affe6e7999a4209a515755f12ac1a3299da2d55e178d4c",
207
207
  "preview/bootstrap-CYECZNXt.js": "sha256:9f303a0485c0badfdd19fad0c357ef050c5b9a343744ab5bf0c70015e7d95092",
208
208
  "preview/crowdy-boot-Cklh1qNl.js": "sha256:b21e8eb126e1cd8a52159d2d577e610df6c8af36a436f9ed5028cfabd11a85ff",
209
- "preview/vfs-image.tar.gz": "sha256:d7472c6fdcf9ba884cb6553bd9ad75aac0e03f50e4b51d38b4cb88f1f8ff55b6",
209
+ "preview/vfs-image.tar.gz": "sha256:f35752ca934b215b2634204b8532a81263c807a620af929c1c1806656e186af5",
210
210
  "preview/worker-C8-_ANaz.js": "sha256:81099e5732dfe0c32bacd5897316b7956c5a0a22da01b4a0c6e21d0a3b9d9aa1"
211
211
  }
212
212
  }