@syncular/client 0.0.6-219 → 0.0.6-223

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/sync-loop.ts CHANGED
@@ -11,12 +11,15 @@ import type {
11
11
  SyncPushBatchCommitResponse,
12
12
  SyncPushRequest,
13
13
  SyncPushResponse,
14
- SyncSubscriptionRequest,
15
14
  SyncTransport,
16
15
  } from '@syncular/core';
17
16
  import type { Kysely } from 'kysely';
18
17
  import { upsertConflictsForRejectedCommit } from './conflicts';
19
- import type { PushResultInfo } from './engine/types';
18
+ import type {
19
+ PushResultInfo,
20
+ SyncClientSubscription,
21
+ SyncTraceEvent,
22
+ } from './engine/types';
20
23
  import type { ClientHandlerCollection } from './handlers/collection';
21
24
  import {
22
25
  getNextSendableOutboxCommit,
@@ -482,6 +485,16 @@ function mergePullResponse(
482
485
  }
483
486
  }
484
487
 
488
+ function emitTrace(
489
+ onTrace: SyncOnceOptions['onTrace'] | SyncPullOnceOptions['onTrace'],
490
+ event: Omit<SyncTraceEvent, 'timestamp'>
491
+ ): void {
492
+ onTrace?.({
493
+ timestamp: Date.now(),
494
+ ...event,
495
+ });
496
+ }
497
+
485
498
  function canSkipPullAfterLocalWsPush(
486
499
  pullState: SyncPullRequestState,
487
500
  pushResponse: SyncPushResponse | null,
@@ -551,7 +564,7 @@ export interface SyncOnceOptions {
551
564
  clientId: string;
552
565
  actorId?: string;
553
566
  plugins?: SyncPushOnceOptions['plugins'];
554
- subscriptions: Array<Omit<SyncSubscriptionRequest, 'cursor'>>;
567
+ subscriptions: SyncClientSubscription[];
555
568
  limitCommits?: number;
556
569
  limitSnapshotRows?: number;
557
570
  maxSnapshotPages?: number;
@@ -565,6 +578,8 @@ export interface SyncOnceOptions {
565
578
  allowSkipPullOnLocalWsPush?: boolean;
566
579
  /** Custom SHA-256 hash function (for platforms without crypto.subtle) */
567
580
  sha256?: (bytes: Uint8Array) => Promise<string>;
581
+ /** Optional structured tracing hook for pull/apply lifecycle diagnostics. */
582
+ onTrace?: (event: SyncTraceEvent) => void;
568
583
  }
569
584
 
570
585
  export interface SyncOnceResult {
@@ -600,6 +615,7 @@ async function syncOnceCombined<DB extends SyncClientDb>(
600
615
  dedupeRows: options.dedupeRows,
601
616
  stateId: options.stateId,
602
617
  sha256: options.sha256,
618
+ onTrace: options.onTrace,
603
619
  };
604
620
 
605
621
  // Build pull request (reads subscription state)
@@ -682,6 +698,15 @@ async function syncOnceCombined<DB extends SyncClientDb>(
682
698
  advanceSequentialBaseVersionsInBatch(combinedPushCommits);
683
699
  }
684
700
 
701
+ const pullSubscriptionIds = pullState.request.subscriptions.map(
702
+ (subscription) => subscription.id
703
+ );
704
+ emitTrace(options.onTrace, {
705
+ stage: 'pull:start',
706
+ stateId: pullOpts.stateId,
707
+ subscriptionIds: pullSubscriptionIds,
708
+ subscriptionCount: pullSubscriptionIds.length,
709
+ });
685
710
  try {
686
711
  combined = await transport.sync({
687
712
  clientId,
@@ -704,7 +729,31 @@ async function syncOnceCombined<DB extends SyncClientDb>(
704
729
  subscriptions: pullState.request.subscriptions,
705
730
  },
706
731
  });
732
+ const pullSubscriptions = combined.pull?.subscriptions ?? [];
733
+ emitTrace(options.onTrace, {
734
+ stage: 'pull:response',
735
+ stateId: pullOpts.stateId,
736
+ subscriptionIds: pullSubscriptions.map(
737
+ (subscription) => subscription.id
738
+ ),
739
+ subscriptionCount: pullSubscriptions.length,
740
+ commitCount: pullSubscriptions.reduce(
741
+ (sum, subscription) => sum + (subscription.commits?.length ?? 0),
742
+ 0
743
+ ),
744
+ snapshotCount: pullSubscriptions.reduce(
745
+ (sum, subscription) => sum + (subscription.snapshots?.length ?? 0),
746
+ 0
747
+ ),
748
+ });
707
749
  } catch (err) {
750
+ emitTrace(options.onTrace, {
751
+ stage: 'pull:error',
752
+ stateId: pullOpts.stateId,
753
+ subscriptionIds: pullSubscriptionIds,
754
+ subscriptionCount: pullSubscriptionIds.length,
755
+ errorMessage: err instanceof Error ? err.message : String(err),
756
+ });
708
757
  if (combinedPushCommits.length > 0) {
709
758
  const message = err instanceof Error ? err.message : 'Unknown error';
710
759
  await markClaimedOutboxCommitsPending(
package/src/sync.ts CHANGED
@@ -5,9 +5,9 @@ import type {
5
5
  ScopeValue,
6
6
  ScopeValuesFromPatterns,
7
7
  SyncIdentityBase,
8
- SyncSubscriptionRequest,
9
8
  } from '@syncular/core';
10
9
  import { registerTableOrThrow } from '@syncular/core';
10
+ import type { SyncClientSubscription } from './engine/types';
11
11
  import {
12
12
  type CreateClientHandlerOptions,
13
13
  createClientHandler,
@@ -16,7 +16,7 @@ import type { ClientTableHandler } from './handlers/types';
16
16
  import type { SyncClientDb } from './schema';
17
17
 
18
18
  type ClientSyncSubscription<ScopeDefs extends readonly ScopeDefinition[]> =
19
- Omit<SyncSubscriptionRequest, 'cursor' | 'table' | 'scopes'> & {
19
+ Omit<SyncClientSubscription, 'table' | 'scopes'> & {
20
20
  table: string;
21
21
  scopes?: ScopeValuesFromPatterns<ScopeDefs>;
22
22
  };
@@ -51,9 +51,7 @@ export interface ClientSyncConfig<
51
51
  Identity extends SyncIdentityBase = SyncIdentityBase,
52
52
  > {
53
53
  handlers: ClientTableHandler<DB>[];
54
- subscriptions(
55
- identity: Identity
56
- ): Array<Omit<SyncSubscriptionRequest, 'cursor'>>;
54
+ subscriptions(identity: Identity): SyncClientSubscription[];
57
55
  }
58
56
 
59
57
  export interface DefineClientSyncOptions {
@@ -143,10 +141,8 @@ export function defineClientSync<
143
141
  );
144
142
  return sync;
145
143
  },
146
- subscriptions(
147
- identity: Identity
148
- ): Array<Omit<SyncSubscriptionRequest, 'cursor'>> {
149
- const resolved: Array<Omit<SyncSubscriptionRequest, 'cursor'>> = [];
144
+ subscriptions(identity: Identity): SyncClientSubscription[] {
145
+ const resolved: SyncClientSubscription[] = [];
150
146
  for (const [table, subscribe] of subscriptionsByTable.entries()) {
151
147
  if (!subscribe) continue;
152
148
  const value =
@@ -160,6 +156,7 @@ export function defineClientSync<
160
156
  scopes: toScopeValues(entry.scopes),
161
157
  params: entry.params,
162
158
  bootstrapState: entry.bootstrapState,
159
+ bootstrapPhase: entry.bootstrapPhase,
163
160
  });
164
161
  }
165
162
  }