@myko/core 4.8.1 → 4.10.0

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/client.d.ts CHANGED
@@ -29,7 +29,7 @@ export declare enum ConnectionStatus {
29
29
  /** Wire protocol for encoding messages */
30
30
  export declare enum MykoProtocol {
31
31
  JSON = "JSON",
32
- MSGPACK = "MSGPACK"
32
+ CBOR = "CBOR"
33
33
  }
34
34
  type ConnectionLogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug' | 'verbose';
35
35
  /** Query class interface */
@@ -107,6 +107,14 @@ export declare class MykoClient {
107
107
  private reportErrors;
108
108
  private pingResponses;
109
109
  private commandIncoming;
110
+ private reportResponseRoutes;
111
+ private queryResponseRoutes;
112
+ private viewResponseRoutes;
113
+ private queryErrorRoutes;
114
+ private viewErrorRoutes;
115
+ private reportErrorRoutes;
116
+ private reportValueCallbacks;
117
+ private reportErrorCallbacks;
110
118
  private connectionStatusSubject;
111
119
  private currentServerSubject;
112
120
  private activeQueries;
@@ -137,7 +145,7 @@ export declare class MykoClient {
137
145
  constructor();
138
146
  /** Set connection log verbosity at runtime. */
139
147
  setConnectionLogLevel(level: ConnectionLogLevel): void;
140
- /** Set the wire protocol (JSON or MSGPACK). Default is MSGPACK. */
148
+ /** Set the wire protocol (JSON or CBOR). Default is JSON. */
141
149
  setProtocol(protocol: MykoProtocol): void;
142
150
  /** Set a single server address, clearing any existing connections */
143
151
  setAddress(address: string | null): void;
@@ -212,6 +220,20 @@ export declare class MykoClient {
212
220
  windowInfo$: Observable<QueryWindowInfo>;
213
221
  setWindow: (window: QueryWindow | null) => void;
214
222
  };
223
+ /**
224
+ * Subscribe a single report with direct callbacks, bypassing RxJS entirely.
225
+ *
226
+ * `watchReport` returns an Observable wrapped in pipe(map, finalize,
227
+ * shareReplay, map) — each subscriber walks that operator chain, allocates
228
+ * a Subject per tx, and walks Subscriber chains on emission. This bypass
229
+ * stores callbacks in a flat Map and lets routeMessage call them directly,
230
+ * which matters when a workspace mounts hundreds of anchor subscriptions
231
+ * in a single Svelte microtask.
232
+ *
233
+ * Caller gets a dispose function. No deduplication — the caller owns the
234
+ * subscription lifetime.
235
+ */
236
+ subscribeReport<R extends Report<unknown>>(report: R, onValue: (value: ReportResult<R>) => void, onError?: (error: Error) => void): () => void;
215
237
  /** Watch a report with automatic deduplication */
216
238
  watchReport<R extends Report<unknown>>(report: R): Observable<ReportResult<R>>;
217
239
  /** Send an event to the server */
@@ -246,6 +268,12 @@ export declare class MykoClient {
246
268
  private connectionLogLevel;
247
269
  private static resolveDefaultConnectionLogLevel;
248
270
  private shouldLogConnection;
271
+ /**
272
+ * Check whether a given event would be logged at the current verbosity.
273
+ * Use this to gate expensive details-object construction at hot call sites
274
+ * (e.g. per-response log calls during scene-load bursts).
275
+ */
276
+ willLogConnection(event: string): boolean;
249
277
  private logConnection;
250
278
  }
251
279
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -5,15 +5,106 @@
5
5
  * When the current server disconnects, instantly switches to another open connection.
6
6
  */
7
7
  import { GetPeerServers, MykoEvent, } from './generated';
8
- import { Packr, Unpackr } from 'msgpackr';
8
+ import { Decoder, Encoder } from 'cbor-x';
9
9
  import { bufferCount, bufferTime, catchError, combineLatest, filter, finalize, firstValueFrom, interval, map, merge, Observable, of, ReplaySubject, scan, shareReplay, Subject, switchMap, } from 'rxjs';
10
10
  import { v4 as uuid } from 'uuid';
11
- // msgpackr defaults can emit extension types for values that don't exist in JSON (notably
12
- // `undefined`). Our Rust server deserializes msgpack into `serde_json::Value`, so ensure we
13
- // encode `undefined` as nil/null instead of an extension.
14
- const packr = new Packr({ encodeUndefinedAsNil: true });
15
- const unpackr = new Unpackr({});
11
+ // cbor-x's defaults emit several non-standard extensions (the "records" structure
12
+ // compression, tag 259 for maps with non-string keys, typed-array tags) that other
13
+ // CBOR implementations including the Rust server's `ciborium` decoder targeting
14
+ // `MykoMessage` / `serde_json::Value` don't understand. Force plain RFC 8949
15
+ // CBOR so the wire is interchangeable.
16
+ const cborEncoder = new Encoder({
17
+ useRecords: false,
18
+ mapsAsObjects: true,
19
+ // useTag259ForMaps: false,
20
+ tagUint8Array: false,
21
+ });
22
+ const cborDecoder = new Decoder({
23
+ useRecords: false,
24
+ mapsAsObjects: true,
25
+ // useTag259ForMaps: false,
26
+ });
27
+ // Recursively strip `undefined` object values before CBOR encoding. cbor-x writes
28
+ // undefined as the standard CBOR simple-value 23 (0xf7), and Rust's serde decoder
29
+ // rejects that for fields like `Vec<T>` (no `null`-to-empty coercion). Omitting the
30
+ // key entirely lets the server's `#[serde(default)]` attributes fill in defaults,
31
+ // which matches how MessagePack used to behave with `encodeUndefinedAsNil: true`.
32
+ // Arrays preserve their length and pass undefined through as null (the only valid
33
+ // CBOR encoding for a missing positional value).
34
+ function stripUndefined(value) {
35
+ if (value === undefined)
36
+ return null;
37
+ if (value === null)
38
+ return value;
39
+ if (Array.isArray(value)) {
40
+ const out = new Array(value.length);
41
+ for (let i = 0; i < value.length; i++) {
42
+ const v = value[i];
43
+ out[i] = v === undefined ? null : stripUndefined(v);
44
+ }
45
+ return out;
46
+ }
47
+ if (typeof value === 'object') {
48
+ const obj = value;
49
+ const out = {};
50
+ for (const key in obj) {
51
+ const v = obj[key];
52
+ if (v === undefined)
53
+ continue;
54
+ out[key] = stripUndefined(v);
55
+ }
56
+ return out;
57
+ }
58
+ return value;
59
+ }
16
60
  const EVENT_BATCH = 'ws:m:event-batch';
61
+ // ─────────────────────────────────────────────────────────────────────────────
62
+ // WS message-throughput instrumentation
63
+ // ─────────────────────────────────────────────────────────────────────────────
64
+ // Counterpart to the server's `ws_timing` module. Counts inbound/outbound
65
+ // messages by `event` kind into per-window buckets, logs a single summary
66
+ // line every WINDOW_MS so we can diagnose "server CPU idle but loads slow"
67
+ // — comparing client send/recv rates to the server's ws_timing log lines
68
+ // tells us whether time is in server-reply latency, client-send pacing, or
69
+ // network round-trip.
70
+ const WS_TIMING_WINDOW_MS = 250;
71
+ const wsTimingInbound = new Map();
72
+ const wsTimingOutbound = new Map();
73
+ let wsTimingTimer = null;
74
+ function recordWsInbound(kind) {
75
+ wsTimingInbound.set(kind, (wsTimingInbound.get(kind) ?? 0) + 1);
76
+ }
77
+ function recordWsOutbound(kind) {
78
+ wsTimingOutbound.set(kind, (wsTimingOutbound.get(kind) ?? 0) + 1);
79
+ }
80
+ /** Strip the `ws:m:` prefix and CamelCase the kind so it matches the
81
+ * server-side `message_kind` tag verbatim (e.g. "ws:m:report" → "Report",
82
+ * "ws:m:query-response" → "QueryResponse"). */
83
+ function eventKindShort(event) {
84
+ const stripped = event.startsWith('ws:m:') ? event.slice(5) : event;
85
+ return stripped
86
+ .split('-')
87
+ .map((part) => part.length > 0 ? part[0].toUpperCase() + part.slice(1) : part)
88
+ .join('');
89
+ }
90
+ function ensureWsTimingLogger() {
91
+ if (wsTimingTimer)
92
+ return;
93
+ wsTimingTimer = setInterval(() => {
94
+ if (wsTimingInbound.size === 0 && wsTimingOutbound.size === 0)
95
+ return;
96
+ const inEntries = [...wsTimingInbound.entries()].sort((a, b) => b[1] - a[1]);
97
+ const outEntries = [...wsTimingOutbound.entries()].sort((a, b) => b[1] - a[1]);
98
+ const inTotal = inEntries.reduce((s, [, n]) => s + n, 0);
99
+ const outTotal = outEntries.reduce((s, [, n]) => s + n, 0);
100
+ const inFmt = inEntries.map(([k, n]) => `${k}=${n}`).join(', ');
101
+ const outFmt = outEntries.map(([k, n]) => `${k}=${n}`).join(', ');
102
+ // eslint-disable-next-line no-console
103
+ console.info(`[ws_timing window=${WS_TIMING_WINDOW_MS}ms] in=${inTotal} [${inFmt}] out=${outTotal} [${outFmt}]`);
104
+ wsTimingInbound.clear();
105
+ wsTimingOutbound.clear();
106
+ }, WS_TIMING_WINDOW_MS);
107
+ }
17
108
  function stableStringify(value) {
18
109
  const seen = new WeakSet();
19
110
  try {
@@ -49,7 +140,7 @@ export var ConnectionStatus;
49
140
  export var MykoProtocol;
50
141
  (function (MykoProtocol) {
51
142
  MykoProtocol["JSON"] = "JSON";
52
- MykoProtocol["MSGPACK"] = "MSGPACK";
143
+ MykoProtocol["CBOR"] = "CBOR";
53
144
  })(MykoProtocol || (MykoProtocol = {}));
54
145
  function queryCacheKey(query, options) {
55
146
  const queryPayload = stableStringify(query.query) ?? '__unstable_query__';
@@ -86,6 +177,21 @@ export class MykoClient {
86
177
  reportErrors = new Subject();
87
178
  pingResponses = new Subject();
88
179
  commandIncoming = new Subject();
180
+ // Per-tx response routing. Replaces the previous pattern of every
181
+ // `watch{Report,Query,View}` subscription doing `filter(r => r.data.tx === tx)`
182
+ // on a shared Subject — that scaled as O(active_subscriptions × messages),
183
+ // costing measurable seconds during scene-load bursts. Direct dispatch is O(1).
184
+ reportResponseRoutes = new Map();
185
+ queryResponseRoutes = new Map();
186
+ viewResponseRoutes = new Map();
187
+ queryErrorRoutes = new Map();
188
+ viewErrorRoutes = new Map();
189
+ reportErrorRoutes = new Map();
190
+ // Lighter-weight direct-callback routing for `subscribeReport` — avoids
191
+ // allocating an RxJS Subject per anchor when the consumer just needs a
192
+ // single callback (which is the case for liveReport in the Svelte runtime).
193
+ reportValueCallbacks = new Map();
194
+ reportErrorCallbacks = new Map();
89
195
  // State observables
90
196
  connectionStatusSubject = new ReplaySubject(1);
91
197
  currentServerSubject = new ReplaySubject(1);
@@ -116,7 +222,7 @@ export class MykoClient {
116
222
  peerDiscoveryEnabled = true;
117
223
  peerDiscoverySubscription = null;
118
224
  useSecureWebSocket = false;
119
- // Protocol defaults to JSON for maximum compatibility (no msgpack extensions, bigint issues, etc).
225
+ // Protocol defaults to JSON for maximum compatibility (no CBOR tag handling, bigint issues, etc).
120
226
  protocol = MykoProtocol.JSON;
121
227
  constructor() {
122
228
  this.setConnectionStatus(ConnectionStatus.Disconnected, 'init');
@@ -126,7 +232,7 @@ export class MykoClient {
126
232
  setConnectionLogLevel(level) {
127
233
  this.connectionLogLevelThreshold = level;
128
234
  }
129
- /** Set the wire protocol (JSON or MSGPACK). Default is MSGPACK. */
235
+ /** Set the wire protocol (JSON or CBOR). Default is JSON. */
130
236
  setProtocol(protocol) {
131
237
  this.protocol = protocol;
132
238
  }
@@ -270,8 +376,8 @@ export class MykoClient {
270
376
  const nowMs = Date.now();
271
377
  // IMPORTANT: the Rust server expects `timestamp: i64`.
272
378
  // - JSON cannot encode bigint, so use number in JSON mode.
273
- // - msgpack can encode bigint as int64, so use bigint in MSGPACK mode.
274
- const timestamp = this.protocol === MykoProtocol.MSGPACK ? BigInt(nowMs) : nowMs;
379
+ // - CBOR can encode bigint as a tagged integer, so use bigint in CBOR mode.
380
+ const timestamp = this.protocol === MykoProtocol.CBOR ? BigInt(nowMs) : nowMs;
275
381
  this.send({
276
382
  event: MykoEvent.Ping,
277
383
  data: { id, timestamp },
@@ -322,16 +428,19 @@ export class MykoClient {
322
428
  activeQueries: this.activeQueries.size,
323
429
  });
324
430
  this.send({ event: MykoEvent.Query, data: wrappedQuery });
431
+ // Direct per-tx routing: see reportResponseRoutes/queryResponseRoutes for
432
+ // background. Each subscription registers its Subject in routeMessage's map
433
+ // so dispatch is O(1) instead of an O(active) filter walk per WS frame.
434
+ const responseRoute = new Subject();
435
+ const errorRoute = new Subject();
436
+ this.queryResponseRoutes.set(tx, responseRoute);
437
+ this.queryErrorRoutes.set(tx, errorRoute);
325
438
  const responses$ = new Observable((subscriber) => {
326
- const responseSub = this.queryResponses
327
- .pipe(filter((r) => r.data.tx === tx))
328
- .subscribe({
439
+ const responseSub = responseRoute.subscribe({
329
440
  next: (response) => subscriber.next(response),
330
441
  error: (error) => subscriber.error(error),
331
442
  });
332
- const errorSub = this.queryErrors
333
- .pipe(filter((error) => error.data.tx === tx))
334
- .subscribe((error) => {
443
+ const errorSub = errorRoute.subscribe((error) => {
335
444
  subscriber.error(new Error(error.data.message));
336
445
  });
337
446
  return () => {
@@ -350,6 +459,10 @@ export class MykoClient {
350
459
  this.activeQueryNames.delete(tx);
351
460
  this.subscriptionStartMs.delete(tx);
352
461
  this.firstResponseLogged.delete(tx);
462
+ this.queryResponseRoutes.delete(tx);
463
+ this.queryErrorRoutes.delete(tx);
464
+ responseRoute.complete();
465
+ errorRoute.complete();
353
466
  this.send({ event: MykoEvent.QueryCancel, data: { tx } });
354
467
  }));
355
468
  return [tx, responses$];
@@ -410,16 +523,16 @@ export class MykoClient {
410
523
  activeViews: this.activeViews.size,
411
524
  });
412
525
  this.send({ event: MykoEvent.View, data: wrappedView });
526
+ const responseRoute = new Subject();
527
+ const errorRoute = new Subject();
528
+ this.viewResponseRoutes.set(tx, responseRoute);
529
+ this.viewErrorRoutes.set(tx, errorRoute);
413
530
  const responses$ = new Observable((subscriber) => {
414
- const responseSub = this.queryResponses
415
- .pipe(filter((r) => r.data.tx === tx))
416
- .subscribe({
531
+ const responseSub = responseRoute.subscribe({
417
532
  next: (response) => subscriber.next(response),
418
533
  error: (error) => subscriber.error(error),
419
534
  });
420
- const errorSub = this.queryErrors
421
- .pipe(filter((error) => error.data.tx === tx))
422
- .subscribe((error) => {
535
+ const errorSub = errorRoute.subscribe((error) => {
423
536
  subscriber.error(new Error(error.data.message));
424
537
  });
425
538
  return () => {
@@ -433,6 +546,10 @@ export class MykoClient {
433
546
  viewName,
434
547
  activeViewsBefore: this.activeViews.size,
435
548
  });
549
+ this.viewResponseRoutes.delete(tx);
550
+ this.viewErrorRoutes.delete(tx);
551
+ responseRoute.complete();
552
+ errorRoute.complete();
436
553
  this.activeViews.delete(tx);
437
554
  this.activeViewNames.delete(tx);
438
555
  this.subscriptionStartMs.delete(tx);
@@ -667,6 +784,59 @@ export class MykoClient {
667
784
  setWindow: (window) => this.setViewWindow(tx, window),
668
785
  };
669
786
  }
787
+ /**
788
+ * Subscribe a single report with direct callbacks, bypassing RxJS entirely.
789
+ *
790
+ * `watchReport` returns an Observable wrapped in pipe(map, finalize,
791
+ * shareReplay, map) — each subscriber walks that operator chain, allocates
792
+ * a Subject per tx, and walks Subscriber chains on emission. This bypass
793
+ * stores callbacks in a flat Map and lets routeMessage call them directly,
794
+ * which matters when a workspace mounts hundreds of anchor subscriptions
795
+ * in a single Svelte microtask.
796
+ *
797
+ * Caller gets a dispose function. No deduplication — the caller owns the
798
+ * subscription lifetime.
799
+ */
800
+ subscribeReport(report, onValue, onError) {
801
+ const tx = uuid();
802
+ const reportName = report.constructor?.name ??
803
+ report.reportId;
804
+ const wrappedReport = {
805
+ report: { ...report.report, tx },
806
+ reportId: report.reportId,
807
+ };
808
+ this.activeReports.set(tx, wrappedReport);
809
+ this.activeReportNames.set(tx, reportName);
810
+ if (this.willLogConnection('report_subscribe')) {
811
+ this.logConnection('report_subscribe', {
812
+ tx,
813
+ reportId: wrappedReport.reportId,
814
+ reportName,
815
+ report: report.report,
816
+ activeReports: this.activeReports.size,
817
+ });
818
+ }
819
+ this.send({ event: MykoEvent.Report, data: wrappedReport });
820
+ this.reportValueCallbacks.set(tx, (r) => {
821
+ onValue(r.data.response);
822
+ });
823
+ if (onError) {
824
+ this.reportErrorCallbacks.set(tx, (r) => {
825
+ onError(new Error(r.data.message));
826
+ });
827
+ }
828
+ let disposed = false;
829
+ return () => {
830
+ if (disposed)
831
+ return;
832
+ disposed = true;
833
+ this.reportValueCallbacks.delete(tx);
834
+ this.reportErrorCallbacks.delete(tx);
835
+ this.activeReports.delete(tx);
836
+ this.activeReportNames.delete(tx);
837
+ this.send({ event: MykoEvent.ReportCancel, data: { tx } });
838
+ };
839
+ }
670
840
  /** Watch a report with automatic deduplication */
671
841
  watchReport(report) {
672
842
  const cacheKey = reportCacheKey(report);
@@ -690,14 +860,9 @@ export class MykoClient {
690
860
  activeReports: this.activeReports.size,
691
861
  });
692
862
  this.send({ event: MykoEvent.Report, data: wrappedReport });
693
- const shared$ = this.reportResponses.pipe(filter((r) => r.data.tx === tx), map((r) => {
694
- this.logConnection('report_response', {
695
- tx,
696
- reportId: wrappedReport.reportId,
697
- reportName,
698
- });
699
- return r;
700
- }), map((r) => r.data.response), finalize(() => {
863
+ const responseRoute = new Subject();
864
+ this.reportResponseRoutes.set(tx, responseRoute);
865
+ const shared$ = responseRoute.pipe(map((r) => r.data.response), finalize(() => {
701
866
  this.logConnection('report_cancel', {
702
867
  tx,
703
868
  reportId: wrappedReport.reportId,
@@ -707,6 +872,8 @@ export class MykoClient {
707
872
  this.sharedReports.delete(cacheKey);
708
873
  this.activeReports.delete(tx);
709
874
  this.activeReportNames.delete(tx);
875
+ this.reportResponseRoutes.delete(tx);
876
+ responseRoute.complete();
710
877
  this.send({ event: MykoEvent.ReportCancel, data: { tx } });
711
878
  }), shareReplay({ bufferSize: 1, refCount: true }),
712
879
  // Defensive copy: prevent one subscriber's mutations from affecting others
@@ -873,7 +1040,7 @@ export class MykoClient {
873
1040
  knownServers: this.getServers(),
874
1041
  });
875
1042
  const ws = new WebSocket(address);
876
- ws.binaryType = 'arraybuffer'; // Receive binary messages as ArrayBuffer for msgpack
1043
+ ws.binaryType = 'arraybuffer'; // Receive binary messages as ArrayBuffer for CBOR
877
1044
  const managed = {
878
1045
  ws,
879
1046
  address,
@@ -956,13 +1123,15 @@ export class MykoClient {
956
1123
  message = JSON.parse(data);
957
1124
  }
958
1125
  else if (data instanceof ArrayBuffer) {
959
- // Binary msgpack message
960
- message = unpackr.unpack(new Uint8Array(data));
1126
+ // Binary CBOR message
1127
+ message = cborDecoder.decode(new Uint8Array(data));
961
1128
  }
962
1129
  else if (data instanceof Blob) {
963
1130
  // Handle Blob asynchronously - convert to ArrayBuffer first
964
1131
  data.arrayBuffer().then((buffer) => {
965
- const decoded = unpackr.unpack(new Uint8Array(buffer));
1132
+ const decoded = cborDecoder.decode(new Uint8Array(buffer));
1133
+ recordWsInbound(eventKindShort(decoded.event));
1134
+ ensureWsTimingLogger();
966
1135
  this.routeMessage(decoded);
967
1136
  });
968
1137
  return;
@@ -970,6 +1139,8 @@ export class MykoClient {
970
1139
  else {
971
1140
  return;
972
1141
  }
1142
+ recordWsInbound(eventKindShort(message.event));
1143
+ ensureWsTimingLogger();
973
1144
  this.routeMessage(message);
974
1145
  }
975
1146
  catch {
@@ -979,72 +1150,69 @@ export class MykoClient {
979
1150
  routeMessage(message) {
980
1151
  switch (message.event) {
981
1152
  case MykoEvent.QueryResponse:
982
- this.maybeLogFirstResponseTiming('query', message.data.tx, {
983
- queryId: this.activeQueries.get(message.data.tx)?.queryId,
984
- queryName: this.activeQueryNames.get(message.data.tx) ??
985
- this.activeQueries.get(message.data.tx)?.queryId ??
986
- 'unknown',
987
- sequence: message.data.sequence,
988
- upserts: message.data.upserts.length,
989
- deletes: message.data.deletes.length,
990
- });
991
- this.logConnection('query_response', {
992
- tx: message.data.tx,
993
- queryId: this.activeQueries.get(message.data.tx)?.queryId,
994
- queryItemType: this.activeQueries.get(message.data.tx)?.queryItemType,
995
- queryName: this.activeQueryNames.get(message.data.tx) ??
996
- this.activeQueries.get(message.data.tx)?.queryId ??
997
- 'unknown',
998
- sequence: message.data.sequence,
999
- upserts: message.data.upserts.length,
1000
- deletes: message.data.deletes.length,
1001
- changes: message.data.changes?.length ?? 0,
1002
- totalCount: message.data.totalCount ??
1003
- message.data.total_count ??
1004
- null,
1005
- window: message.data.window ?? null,
1006
- });
1007
- const queryPublishStarted = this.nowMs();
1153
+ if (this.willLogConnection('query_response')) {
1154
+ this.maybeLogFirstResponseTiming('query', message.data.tx, {
1155
+ queryId: this.activeQueries.get(message.data.tx)?.queryId,
1156
+ queryName: this.activeQueryNames.get(message.data.tx) ??
1157
+ this.activeQueries.get(message.data.tx)?.queryId ??
1158
+ 'unknown',
1159
+ sequence: message.data.sequence,
1160
+ upserts: message.data.upserts.length,
1161
+ deletes: message.data.deletes.length,
1162
+ });
1163
+ this.logConnection('query_response', {
1164
+ tx: message.data.tx,
1165
+ queryId: this.activeQueries.get(message.data.tx)?.queryId,
1166
+ queryItemType: this.activeQueries.get(message.data.tx)?.queryItemType,
1167
+ queryName: this.activeQueryNames.get(message.data.tx) ??
1168
+ this.activeQueries.get(message.data.tx)?.queryId ??
1169
+ 'unknown',
1170
+ sequence: message.data.sequence,
1171
+ upserts: message.data.upserts.length,
1172
+ deletes: message.data.deletes.length,
1173
+ changes: message.data.changes?.length ?? 0,
1174
+ totalCount: message.data.totalCount ??
1175
+ message.data.total_count ??
1176
+ null,
1177
+ window: message.data.window ?? null,
1178
+ });
1179
+ }
1180
+ this.queryResponseRoutes.get(message.data.tx)?.next(message);
1008
1181
  this.queryResponses.next(message);
1009
- this.logConnection('query_publish_ms', {
1010
- tx: message.data.tx,
1011
- sequence: message.data.sequence,
1012
- publishMs: Number((this.nowMs() - queryPublishStarted).toFixed(2)),
1013
- });
1014
1182
  break;
1015
- case MykoEvent.ViewResponse:
1183
+ case MykoEvent.ViewResponse: {
1016
1184
  // ViewResponse and QueryResponse share the same payload shape.
1017
1185
  const viewMessage = message;
1018
- this.maybeLogFirstResponseTiming('view', viewMessage.data.tx, {
1019
- viewId: this.activeViews.get(viewMessage.data.tx)?.viewId,
1020
- viewName: this.activeViewNames.get(viewMessage.data.tx) ??
1021
- this.activeViews.get(viewMessage.data.tx)?.viewId ??
1022
- 'unknown',
1023
- sequence: viewMessage.data.sequence,
1024
- upserts: viewMessage.data.upserts.length,
1025
- deletes: viewMessage.data.deletes.length,
1026
- });
1027
- this.logConnection('view_response', {
1028
- tx: viewMessage.data.tx,
1029
- sequence: viewMessage.data.sequence,
1030
- upserts: viewMessage.data.upserts.length,
1031
- deletes: viewMessage.data.deletes.length,
1032
- changes: viewMessage.data.changes?.length ?? 0,
1033
- totalCount: viewMessage.data
1034
- .totalCount ??
1035
- viewMessage.data.total_count ??
1036
- null,
1037
- window: viewMessage.data.window ?? null,
1038
- });
1039
- const viewPublishStarted = this.nowMs();
1186
+ if (this.willLogConnection('view_response')) {
1187
+ this.maybeLogFirstResponseTiming('view', viewMessage.data.tx, {
1188
+ viewId: this.activeViews.get(viewMessage.data.tx)?.viewId,
1189
+ viewName: this.activeViewNames.get(viewMessage.data.tx) ??
1190
+ this.activeViews.get(viewMessage.data.tx)?.viewId ??
1191
+ 'unknown',
1192
+ sequence: viewMessage.data.sequence,
1193
+ upserts: viewMessage.data.upserts.length,
1194
+ deletes: viewMessage.data.deletes.length,
1195
+ });
1196
+ this.logConnection('view_response', {
1197
+ tx: viewMessage.data.tx,
1198
+ sequence: viewMessage.data.sequence,
1199
+ upserts: viewMessage.data.upserts.length,
1200
+ deletes: viewMessage.data.deletes.length,
1201
+ changes: viewMessage.data.changes?.length ?? 0,
1202
+ totalCount: viewMessage.data
1203
+ .totalCount ??
1204
+ viewMessage.data.total_count ??
1205
+ null,
1206
+ window: viewMessage.data.window ?? null,
1207
+ });
1208
+ }
1209
+ this.viewResponseRoutes.get(viewMessage.data.tx)?.next(viewMessage);
1040
1210
  this.queryResponses.next(viewMessage);
1041
- this.logConnection('view_publish_ms', {
1042
- tx: viewMessage.data.tx,
1043
- sequence: viewMessage.data.sequence,
1044
- publishMs: Number((this.nowMs() - viewPublishStarted).toFixed(2)),
1045
- });
1046
1211
  break;
1212
+ }
1047
1213
  case MykoEvent.ReportResponse:
1214
+ this.reportValueCallbacks.get(message.data.tx)?.(message);
1215
+ this.reportResponseRoutes.get(message.data.tx)?.next(message);
1048
1216
  this.reportResponses.next(message);
1049
1217
  break;
1050
1218
  case MykoEvent.CommandResponse:
@@ -1054,6 +1222,7 @@ export class MykoClient {
1054
1222
  this.commandErrors.next(message);
1055
1223
  break;
1056
1224
  case MykoEvent.QueryError:
1225
+ this.queryErrorRoutes.get(message.data.tx)?.next(message);
1057
1226
  this.queryErrors.next(message);
1058
1227
  this.logConnection('query_error', {
1059
1228
  tx: message.data.tx,
@@ -1066,6 +1235,7 @@ export class MykoClient {
1066
1235
  });
1067
1236
  break;
1068
1237
  case MykoEvent.ViewError:
1238
+ this.viewErrorRoutes.get(message.data.tx)?.next(message);
1069
1239
  this.queryErrors.next(message);
1070
1240
  this.logConnection('view_error', {
1071
1241
  tx: message.data.tx,
@@ -1078,6 +1248,8 @@ export class MykoClient {
1078
1248
  });
1079
1249
  break;
1080
1250
  case MykoEvent.ReportError:
1251
+ this.reportErrorCallbacks.get(message.data.tx)?.(message);
1252
+ this.reportErrorRoutes.get(message.data.tx)?.next(message);
1081
1253
  this.reportErrors.next(message);
1082
1254
  this.logConnection('report_error', {
1083
1255
  tx: message.data.tx,
@@ -1129,11 +1301,15 @@ export class MykoClient {
1129
1301
  if (this.currentServer) {
1130
1302
  const managed = this.sockets.get(this.currentServer);
1131
1303
  if (managed?.ws.readyState === WebSocket.OPEN) {
1132
- const encoded = this.protocol === MykoProtocol.MSGPACK
1133
- ? new Uint8Array(packr.pack(message))
1304
+ const encoded = this.protocol === MykoProtocol.CBOR
1305
+ ? cborEncoder.encode(stripUndefined(message))
1134
1306
  : JSON.stringify(message);
1135
1307
  managed.ws.send(encoded);
1136
1308
  this.upMsgCounter.next();
1309
+ // Tap for ws_timing summary. Use the short kind suffix (after
1310
+ // `ws:m:`) for a tighter log line.
1311
+ recordWsOutbound(eventKindShort(event));
1312
+ ensureWsTimingLogger();
1137
1313
  return;
1138
1314
  }
1139
1315
  }
@@ -1292,6 +1468,15 @@ export class MykoClient {
1292
1468
  };
1293
1469
  return priority[level] <= priority[this.connectionLogLevelThreshold];
1294
1470
  }
1471
+ /**
1472
+ * Check whether a given event would be logged at the current verbosity.
1473
+ * Use this to gate expensive details-object construction at hot call sites
1474
+ * (e.g. per-response log calls during scene-load bursts).
1475
+ */
1476
+ willLogConnection(event) {
1477
+ const level = this.connectionLogLevel(event);
1478
+ return this.shouldLogConnection(level);
1479
+ }
1295
1480
  logConnection(event, details) {
1296
1481
  const level = this.connectionLogLevel(event);
1297
1482
  if (!this.shouldLogConnection(level))