@syncular/server-hono 0.0.6-73 → 0.0.6-79

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/src/routes.ts CHANGED
@@ -9,7 +9,9 @@
9
9
 
10
10
  import {
11
11
  captureSyncException,
12
+ countSyncMetric,
12
13
  createSyncTimer,
14
+ distributionSyncMetric,
13
15
  ErrorResponseSchema,
14
16
  logSyncEvent,
15
17
  SyncCombinedRequestSchema,
@@ -20,6 +22,7 @@ import type {
20
22
  ServerSyncDialect,
21
23
  ServerTableHandler,
22
24
  SnapshotChunkStorage,
25
+ SqlFamily,
23
26
  SyncCoreDb,
24
27
  SyncRealtimeBroadcaster,
25
28
  SyncRealtimeEvent,
@@ -163,9 +166,10 @@ export interface SyncRoutesConfigWithRateLimit {
163
166
  export interface CreateSyncRoutesOptions<
164
167
  DB extends SyncCoreDb = SyncCoreDb,
165
168
  Auth extends SyncAuthResult = SyncAuthResult,
169
+ F extends SqlFamily = SqlFamily,
166
170
  > {
167
171
  db: Kysely<DB>;
168
- dialect: ServerSyncDialect;
172
+ dialect: ServerSyncDialect<F>;
169
173
  handlers: ServerTableHandler<DB, Auth>[];
170
174
  authenticate: (c: Context) => Promise<Auth | null>;
171
175
  sync?: SyncRoutesConfigWithRateLimit;
@@ -434,7 +438,8 @@ function emitConsoleLiveEvent(
434
438
  export function createSyncRoutes<
435
439
  DB extends SyncCoreDb = SyncCoreDb,
436
440
  Auth extends SyncAuthResult = SyncAuthResult,
437
- >(options: CreateSyncRoutesOptions<DB, Auth>): Hono {
441
+ F extends SqlFamily = SqlFamily,
442
+ >(options: CreateSyncRoutesOptions<DB, Auth, F>): Hono {
438
443
  const routes = new Hono();
439
444
  routes.onError((error, c) => {
440
445
  captureSyncException(error, {
@@ -1315,6 +1320,32 @@ export function createSyncRoutes<
1315
1320
 
1316
1321
  let unregister: (() => void) | null = null;
1317
1322
  let connRef: ReturnType<typeof createWebSocketConnection> | null = null;
1323
+ const connectionCountBeforeUpgrade =
1324
+ wsConnectionManager.getConnectionCount(clientId);
1325
+ let sessionStartedAtMs: number | null = null;
1326
+ let sessionEnded = false;
1327
+
1328
+ const finishRealtimeSession = (reason: 'closed' | 'error') => {
1329
+ if (sessionEnded) return;
1330
+ sessionEnded = true;
1331
+ if (sessionStartedAtMs === null) {
1332
+ return;
1333
+ }
1334
+ const durationMs = Math.max(0, Date.now() - sessionStartedAtMs);
1335
+ countSyncMetric('sync.sessions.ended', 1, {
1336
+ attributes: {
1337
+ transportPath: realtimeTransportPath,
1338
+ reason,
1339
+ },
1340
+ });
1341
+ distributionSyncMetric('sync.sessions.duration_ms', durationMs, {
1342
+ unit: 'millisecond',
1343
+ attributes: {
1344
+ transportPath: realtimeTransportPath,
1345
+ reason,
1346
+ },
1347
+ });
1348
+ };
1318
1349
 
1319
1350
  const upgradeWebSocket = websocketConfig.upgradeWebSocket;
1320
1351
  if (!upgradeWebSocket) {
@@ -1329,6 +1360,20 @@ export function createSyncRoutes<
1329
1360
  transportPath: realtimeTransportPath,
1330
1361
  });
1331
1362
  connRef = conn;
1363
+ sessionStartedAtMs = Date.now();
1364
+ countSyncMetric('sync.sessions.started', 1, {
1365
+ attributes: {
1366
+ transportPath: realtimeTransportPath,
1367
+ },
1368
+ });
1369
+ if (connectionCountBeforeUpgrade > 0) {
1370
+ countSyncMetric('sync.transport.reconnects', 1, {
1371
+ attributes: {
1372
+ transportPath: realtimeTransportPath,
1373
+ source: 'server',
1374
+ },
1375
+ });
1376
+ }
1332
1377
 
1333
1378
  unregister = wsConnectionManager.register(conn, initialScopeKeys);
1334
1379
  conn.sendHeartbeat();
@@ -1345,6 +1390,7 @@ export function createSyncRoutes<
1345
1390
  unregister?.();
1346
1391
  unregister = null;
1347
1392
  connRef = null;
1393
+ finishRealtimeSession('closed');
1348
1394
  logSyncEvent({
1349
1395
  event: 'sync.realtime.disconnect',
1350
1396
  userId: auth.actorId,
@@ -1360,6 +1406,7 @@ export function createSyncRoutes<
1360
1406
  unregister?.();
1361
1407
  unregister = null;
1362
1408
  connRef = null;
1409
+ finishRealtimeSession('error');
1363
1410
  logSyncEvent({
1364
1411
  event: 'sync.realtime.disconnect',
1365
1412
  userId: auth.actorId,
@@ -1672,6 +1719,19 @@ export function createSyncRoutes<
1672
1719
  tables: pushed.affectedTables,
1673
1720
  }));
1674
1721
 
1722
+ const detectedConflicts = pushed.response.results.reduce(
1723
+ (count, result) => count + (result.status === 'conflict' ? 1 : 0),
1724
+ 0
1725
+ );
1726
+ if (detectedConflicts > 0) {
1727
+ countSyncMetric('sync.conflicts.detected', detectedConflicts, {
1728
+ attributes: {
1729
+ syncPath: 'ws-push',
1730
+ transportPath: conn.transportPath,
1731
+ },
1732
+ });
1733
+ }
1734
+
1675
1735
  // WS notifications to other clients
1676
1736
  if (
1677
1737
  wsConnectionManager &&