@korajs/core 1.0.0-beta.10 → 1.0.0-beta.12

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 +1 @@
1
- {"version":3,"sources":["../../src/bindings/index.ts","../../src/bindings/create-mutation-controller.ts"],"sourcesContent":["export type {\n\tAuthSyncBinding,\n\tKoraAppLike,\n\tKoraBindingSyncBridge,\n\tKoraBindingSyncStatus,\n\tKoraContextValue,\n\tUseMutationOptions,\n\tUseMutationResultBase,\n\tUseQueryOptions,\n} from './types'\nexport {\n\tcreateMutationController,\n\ttype CreateMutationControllerOptions,\n\ttype MutationController,\n\ttype MutationControllerState,\n} from './create-mutation-controller'\n","import type { UseMutationOptions } from './types'\n\nexport interface MutationControllerState {\n\tisLoading: boolean\n\terror: Error | null\n}\n\nexport interface MutationController<TData, TArgs extends unknown[], TContext = void> {\n\tgetSnapshot(): MutationControllerState\n\tsubscribe(listener: () => void): () => void\n\tmutate(...args: TArgs): void\n\tmutateAsync(...args: TArgs): Promise<TData>\n\treset(): void\n\tdestroy(): void\n}\n\nexport interface CreateMutationControllerOptions<TData, TArgs extends unknown[], TContext = void> {\n\tmutationFn: (...args: TArgs) => Promise<TData>\n\toptions?: UseMutationOptions<TData, TArgs, TContext>\n\t/** Reads latest options on each mutation (for framework hooks with ref-backed options). */\n\tresolveOptions?: () => UseMutationOptions<TData, TArgs, TContext> | undefined\n\tonStateChange?: (state: MutationControllerState) => void\n}\n\n/**\n * Framework-agnostic mutation runner with optimistic lifecycle hooks.\n */\nexport function createMutationController<TData, TArgs extends unknown[], TContext = void>(\n\toptions: CreateMutationControllerOptions<TData, TArgs, TContext>,\n): MutationController<TData, TArgs, TContext> {\n\tlet snapshot: MutationControllerState = { isLoading: false, error: null }\n\tconst listeners = new Set<() => void>()\n\tlet disposed = false\n\tlet inFlight = 0\n\n\tconst notify = (): void => {\n\t\tfor (const listener of listeners) {\n\t\t\tlistener()\n\t\t}\n\t\toptions.onStateChange?.(snapshot)\n\t}\n\n\tconst setSnapshot = (next: MutationControllerState): void => {\n\t\tsnapshot = next\n\t\tnotify()\n\t}\n\n\tconst getSnapshot = (): MutationControllerState => snapshot\n\n\tconst subscribe = (listener: () => void): (() => void) => {\n\t\tlisteners.add(listener)\n\t\treturn () => {\n\t\t\tlisteners.delete(listener)\n\t\t}\n\t}\n\n\tconst reset = (): void => {\n\t\tif (disposed) return\n\t\tsetSnapshot({ isLoading: false, error: null })\n\t}\n\n\tconst mutateAsync = async (...args: TArgs): Promise<TData> => {\n\t\tif (disposed) {\n\t\t\tthrow new Error('Mutation controller is destroyed')\n\t\t}\n\n\t\tconst opts = options.resolveOptions?.() ?? options.options\n\t\tlet context: TContext | undefined\n\n\t\tinFlight++\n\t\tsetSnapshot({ isLoading: true, error: null })\n\n\t\ttry {\n\t\t\tif (opts?.onMutate) {\n\t\t\t\tcontext = await opts.onMutate(...args)\n\t\t\t}\n\n\t\t\tconst result = await options.mutationFn(...args)\n\t\t\tinFlight = Math.max(0, inFlight - 1)\n\t\t\tsetSnapshot({ isLoading: inFlight > 0, error: null })\n\t\t\topts?.onSuccess?.(result, ...args)\n\t\t\topts?.onSettled?.(result, null, ...args)\n\t\t\treturn result\n\t\t} catch (err) {\n\t\t\tconst error = err instanceof Error ? err : new Error(String(err))\n\n\t\t\tif (context !== undefined && opts?.onRollback) {\n\t\t\t\tawait opts.onRollback(context, ...args)\n\t\t\t}\n\n\t\t\tinFlight = Math.max(0, inFlight - 1)\n\t\t\tsetSnapshot({ isLoading: inFlight > 0, error })\n\t\t\topts?.onError?.(error, ...args)\n\t\t\topts?.onSettled?.(undefined, error, ...args)\n\t\t\tthrow error\n\t\t}\n\t}\n\n\tconst mutate = (...args: TArgs): void => {\n\t\tvoid mutateAsync(...args).catch(() => {})\n\t}\n\n\tconst destroy = (): void => {\n\t\tdisposed = true\n\t\tlisteners.clear()\n\t}\n\n\treturn {\n\t\tgetSnapshot,\n\t\tsubscribe,\n\t\tmutate,\n\t\tmutateAsync,\n\t\treset,\n\t\tdestroy,\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC2BO,SAAS,yBACf,SAC6C;AAC7C,MAAI,WAAoC,EAAE,WAAW,OAAO,OAAO,KAAK;AACxE,QAAM,YAAY,oBAAI,IAAgB;AACtC,MAAI,WAAW;AACf,MAAI,WAAW;AAEf,QAAM,SAAS,MAAY;AAC1B,eAAW,YAAY,WAAW;AACjC,eAAS;AAAA,IACV;AACA,YAAQ,gBAAgB,QAAQ;AAAA,EACjC;AAEA,QAAM,cAAc,CAAC,SAAwC;AAC5D,eAAW;AACX,WAAO;AAAA,EACR;AAEA,QAAM,cAAc,MAA+B;AAEnD,QAAM,YAAY,CAAC,aAAuC;AACzD,cAAU,IAAI,QAAQ;AACtB,WAAO,MAAM;AACZ,gBAAU,OAAO,QAAQ;AAAA,IAC1B;AAAA,EACD;AAEA,QAAM,QAAQ,MAAY;AACzB,QAAI,SAAU;AACd,gBAAY,EAAE,WAAW,OAAO,OAAO,KAAK,CAAC;AAAA,EAC9C;AAEA,QAAM,cAAc,UAAU,SAAgC;AAC7D,QAAI,UAAU;AACb,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACnD;AAEA,UAAM,OAAO,QAAQ,iBAAiB,KAAK,QAAQ;AACnD,QAAI;AAEJ;AACA,gBAAY,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAE5C,QAAI;AACH,UAAI,MAAM,UAAU;AACnB,kBAAU,MAAM,KAAK,SAAS,GAAG,IAAI;AAAA,MACtC;AAEA,YAAM,SAAS,MAAM,QAAQ,WAAW,GAAG,IAAI;AAC/C,iBAAW,KAAK,IAAI,GAAG,WAAW,CAAC;AACnC,kBAAY,EAAE,WAAW,WAAW,GAAG,OAAO,KAAK,CAAC;AACpD,YAAM,YAAY,QAAQ,GAAG,IAAI;AACjC,YAAM,YAAY,QAAQ,MAAM,GAAG,IAAI;AACvC,aAAO;AAAA,IACR,SAAS,KAAK;AACb,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAEhE,UAAI,YAAY,UAAa,MAAM,YAAY;AAC9C,cAAM,KAAK,WAAW,SAAS,GAAG,IAAI;AAAA,MACvC;AAEA,iBAAW,KAAK,IAAI,GAAG,WAAW,CAAC;AACnC,kBAAY,EAAE,WAAW,WAAW,GAAG,MAAM,CAAC;AAC9C,YAAM,UAAU,OAAO,GAAG,IAAI;AAC9B,YAAM,YAAY,QAAW,OAAO,GAAG,IAAI;AAC3C,YAAM;AAAA,IACP;AAAA,EACD;AAEA,QAAM,SAAS,IAAI,SAAsB;AACxC,SAAK,YAAY,GAAG,IAAI,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACzC;AAEA,QAAM,UAAU,MAAY;AAC3B,eAAW;AACX,cAAU,MAAM;AAAA,EACjB;AAEA,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;","names":[]}
1
+ {"version":3,"sources":["../../src/bindings/index.ts","../../src/bindings/create-mutation-controller.ts"],"sourcesContent":["export type {\n\tAuthSyncBinding,\n\tAuthSyncState,\n\tKoraAppLike,\n\tKoraBindingSyncBridge,\n\tKoraBindingSyncStatus,\n\tKoraContextValue,\n\tUseMutationOptions,\n\tUseMutationResultBase,\n\tUseQueryOptions,\n} from './types'\nexport {\n\tcreateMutationController,\n\ttype CreateMutationControllerOptions,\n\ttype MutationController,\n\ttype MutationControllerState,\n} from './create-mutation-controller'\n","import type { UseMutationOptions } from './types'\n\nexport interface MutationControllerState {\n\tisLoading: boolean\n\terror: Error | null\n}\n\nexport interface MutationController<TData, TArgs extends unknown[], TContext = void> {\n\tgetSnapshot(): MutationControllerState\n\tsubscribe(listener: () => void): () => void\n\tmutate(...args: TArgs): void\n\tmutateAsync(...args: TArgs): Promise<TData>\n\treset(): void\n\tdestroy(): void\n}\n\nexport interface CreateMutationControllerOptions<TData, TArgs extends unknown[], TContext = void> {\n\tmutationFn: (...args: TArgs) => Promise<TData>\n\toptions?: UseMutationOptions<TData, TArgs, TContext>\n\t/** Reads latest options on each mutation (for framework hooks with ref-backed options). */\n\tresolveOptions?: () => UseMutationOptions<TData, TArgs, TContext> | undefined\n\tonStateChange?: (state: MutationControllerState) => void\n}\n\n/**\n * Framework-agnostic mutation runner with optimistic lifecycle hooks.\n */\nexport function createMutationController<TData, TArgs extends unknown[], TContext = void>(\n\toptions: CreateMutationControllerOptions<TData, TArgs, TContext>,\n): MutationController<TData, TArgs, TContext> {\n\tlet snapshot: MutationControllerState = { isLoading: false, error: null }\n\tconst listeners = new Set<() => void>()\n\tlet disposed = false\n\tlet inFlight = 0\n\n\tconst notify = (): void => {\n\t\tfor (const listener of listeners) {\n\t\t\tlistener()\n\t\t}\n\t\toptions.onStateChange?.(snapshot)\n\t}\n\n\tconst setSnapshot = (next: MutationControllerState): void => {\n\t\tsnapshot = next\n\t\tnotify()\n\t}\n\n\tconst getSnapshot = (): MutationControllerState => snapshot\n\n\tconst subscribe = (listener: () => void): (() => void) => {\n\t\tlisteners.add(listener)\n\t\treturn () => {\n\t\t\tlisteners.delete(listener)\n\t\t}\n\t}\n\n\tconst reset = (): void => {\n\t\tif (disposed) return\n\t\tsetSnapshot({ isLoading: false, error: null })\n\t}\n\n\tconst mutateAsync = async (...args: TArgs): Promise<TData> => {\n\t\tif (disposed) {\n\t\t\tthrow new Error('Mutation controller is destroyed')\n\t\t}\n\n\t\tconst opts = options.resolveOptions?.() ?? options.options\n\t\tlet context: TContext | undefined\n\n\t\tinFlight++\n\t\tsetSnapshot({ isLoading: true, error: null })\n\n\t\ttry {\n\t\t\tif (opts?.onMutate) {\n\t\t\t\tcontext = await opts.onMutate(...args)\n\t\t\t}\n\n\t\t\tconst result = await options.mutationFn(...args)\n\t\t\tinFlight = Math.max(0, inFlight - 1)\n\t\t\tsetSnapshot({ isLoading: inFlight > 0, error: null })\n\t\t\topts?.onSuccess?.(result, ...args)\n\t\t\topts?.onSettled?.(result, null, ...args)\n\t\t\treturn result\n\t\t} catch (err) {\n\t\t\tconst error = err instanceof Error ? err : new Error(String(err))\n\n\t\t\tif (context !== undefined && opts?.onRollback) {\n\t\t\t\tawait opts.onRollback(context, ...args)\n\t\t\t}\n\n\t\t\tinFlight = Math.max(0, inFlight - 1)\n\t\t\tsetSnapshot({ isLoading: inFlight > 0, error })\n\t\t\topts?.onError?.(error, ...args)\n\t\t\topts?.onSettled?.(undefined, error, ...args)\n\t\t\tthrow error\n\t\t}\n\t}\n\n\tconst mutate = (...args: TArgs): void => {\n\t\tvoid mutateAsync(...args).catch(() => {})\n\t}\n\n\tconst destroy = (): void => {\n\t\tdisposed = true\n\t\tlisteners.clear()\n\t}\n\n\treturn {\n\t\tgetSnapshot,\n\t\tsubscribe,\n\t\tmutate,\n\t\tmutateAsync,\n\t\treset,\n\t\tdestroy,\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC2BO,SAAS,yBACf,SAC6C;AAC7C,MAAI,WAAoC,EAAE,WAAW,OAAO,OAAO,KAAK;AACxE,QAAM,YAAY,oBAAI,IAAgB;AACtC,MAAI,WAAW;AACf,MAAI,WAAW;AAEf,QAAM,SAAS,MAAY;AAC1B,eAAW,YAAY,WAAW;AACjC,eAAS;AAAA,IACV;AACA,YAAQ,gBAAgB,QAAQ;AAAA,EACjC;AAEA,QAAM,cAAc,CAAC,SAAwC;AAC5D,eAAW;AACX,WAAO;AAAA,EACR;AAEA,QAAM,cAAc,MAA+B;AAEnD,QAAM,YAAY,CAAC,aAAuC;AACzD,cAAU,IAAI,QAAQ;AACtB,WAAO,MAAM;AACZ,gBAAU,OAAO,QAAQ;AAAA,IAC1B;AAAA,EACD;AAEA,QAAM,QAAQ,MAAY;AACzB,QAAI,SAAU;AACd,gBAAY,EAAE,WAAW,OAAO,OAAO,KAAK,CAAC;AAAA,EAC9C;AAEA,QAAM,cAAc,UAAU,SAAgC;AAC7D,QAAI,UAAU;AACb,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACnD;AAEA,UAAM,OAAO,QAAQ,iBAAiB,KAAK,QAAQ;AACnD,QAAI;AAEJ;AACA,gBAAY,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAE5C,QAAI;AACH,UAAI,MAAM,UAAU;AACnB,kBAAU,MAAM,KAAK,SAAS,GAAG,IAAI;AAAA,MACtC;AAEA,YAAM,SAAS,MAAM,QAAQ,WAAW,GAAG,IAAI;AAC/C,iBAAW,KAAK,IAAI,GAAG,WAAW,CAAC;AACnC,kBAAY,EAAE,WAAW,WAAW,GAAG,OAAO,KAAK,CAAC;AACpD,YAAM,YAAY,QAAQ,GAAG,IAAI;AACjC,YAAM,YAAY,QAAQ,MAAM,GAAG,IAAI;AACvC,aAAO;AAAA,IACR,SAAS,KAAK;AACb,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAEhE,UAAI,YAAY,UAAa,MAAM,YAAY;AAC9C,cAAM,KAAK,WAAW,SAAS,GAAG,IAAI;AAAA,MACvC;AAEA,iBAAW,KAAK,IAAI,GAAG,WAAW,CAAC;AACnC,kBAAY,EAAE,WAAW,WAAW,GAAG,MAAM,CAAC;AAC9C,YAAM,UAAU,OAAO,GAAG,IAAI;AAC9B,YAAM,YAAY,QAAW,OAAO,GAAG,IAAI;AAC3C,YAAM;AAAA,IACP;AAAA,EACD;AAEA,QAAM,SAAS,IAAI,SAAsB;AACxC,SAAK,YAAY,GAAG,IAAI,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACzC;AAEA,QAAM,UAAU,MAAY;AAC3B,eAAW;AACX,cAAU,MAAM;AAAA,EACjB;AAEA,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;","names":[]}
@@ -1,9 +1,9 @@
1
- import { p as KoraEventEmitter } from '../events-3z0Tema3.cjs';
2
- import { S as ScopeMap } from '../build-scope-map-B8dpCt6P.cjs';
1
+ import { p as KoraEventEmitter } from '../events-D7JCtQSD.cjs';
2
+ import { S as ScopeMap } from '../build-scope-map-CItP1gsz.cjs';
3
3
 
4
4
  /** Sync status shape consumed by framework binding hooks (matches {@link SyncStatusInfo} in `@korajs/sync`). */
5
5
  interface KoraBindingSyncStatus {
6
- status: 'connected' | 'reconnecting' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error';
6
+ status: 'connected' | 'reconnecting' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error' | 'auth-required';
7
7
  reconnecting: boolean;
8
8
  pendingOperations: number;
9
9
  lastSyncedAt: number | null;
@@ -66,6 +66,8 @@ interface AuthSyncBinding {
66
66
  auth: () => Promise<{
67
67
  token: string;
68
68
  }>;
69
+ /** Resolve whether sync may currently open a transport. Tokens are never used as readiness signals. */
70
+ resolveSyncState?: () => Promise<AuthSyncState>;
69
71
  /** Builds a scope map from the current token and schema. */
70
72
  resolveScopeMap?: () => Promise<ScopeMap | undefined>;
71
73
  /**
@@ -78,6 +80,20 @@ interface AuthSyncBinding {
78
80
  /** Notifies when auth state changes so sync can refresh scope or reconnect. */
79
81
  subscribe?: (listener: () => void) => () => void;
80
82
  }
83
+ /** Authentication state consumed by the sync and auth-isolated store lifecycles. */
84
+ type AuthSyncState = {
85
+ state: 'loading';
86
+ } | {
87
+ state: 'signed-out';
88
+ mayConnectAnonymously: false;
89
+ } | {
90
+ state: 'anonymous';
91
+ mayConnectAnonymously: true;
92
+ } | {
93
+ state: 'authenticated';
94
+ userId: string;
95
+ token: string;
96
+ };
81
97
 
82
98
  interface MutationControllerState {
83
99
  isLoading: boolean;
@@ -103,4 +119,4 @@ interface CreateMutationControllerOptions<TData, TArgs extends unknown[], TConte
103
119
  */
104
120
  declare function createMutationController<TData, TArgs extends unknown[], TContext = void>(options: CreateMutationControllerOptions<TData, TArgs, TContext>): MutationController<TData, TArgs, TContext>;
105
121
 
106
- export { type AuthSyncBinding, type CreateMutationControllerOptions, type KoraAppLike, type KoraBindingSyncBridge, type KoraBindingSyncStatus, type KoraContextValue, type MutationController, type MutationControllerState, type UseMutationOptions, type UseMutationResultBase, type UseQueryOptions, createMutationController };
122
+ export { type AuthSyncBinding, type AuthSyncState, type CreateMutationControllerOptions, type KoraAppLike, type KoraBindingSyncBridge, type KoraBindingSyncStatus, type KoraContextValue, type MutationController, type MutationControllerState, type UseMutationOptions, type UseMutationResultBase, type UseQueryOptions, createMutationController };
@@ -1,9 +1,9 @@
1
- import { p as KoraEventEmitter } from '../events-3z0Tema3.js';
2
- import { S as ScopeMap } from '../build-scope-map-CDpIGOsf.js';
1
+ import { p as KoraEventEmitter } from '../events-D7JCtQSD.js';
2
+ import { S as ScopeMap } from '../build-scope-map-Cv5HfCQP.js';
3
3
 
4
4
  /** Sync status shape consumed by framework binding hooks (matches {@link SyncStatusInfo} in `@korajs/sync`). */
5
5
  interface KoraBindingSyncStatus {
6
- status: 'connected' | 'reconnecting' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error';
6
+ status: 'connected' | 'reconnecting' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error' | 'auth-required';
7
7
  reconnecting: boolean;
8
8
  pendingOperations: number;
9
9
  lastSyncedAt: number | null;
@@ -66,6 +66,8 @@ interface AuthSyncBinding {
66
66
  auth: () => Promise<{
67
67
  token: string;
68
68
  }>;
69
+ /** Resolve whether sync may currently open a transport. Tokens are never used as readiness signals. */
70
+ resolveSyncState?: () => Promise<AuthSyncState>;
69
71
  /** Builds a scope map from the current token and schema. */
70
72
  resolveScopeMap?: () => Promise<ScopeMap | undefined>;
71
73
  /**
@@ -78,6 +80,20 @@ interface AuthSyncBinding {
78
80
  /** Notifies when auth state changes so sync can refresh scope or reconnect. */
79
81
  subscribe?: (listener: () => void) => () => void;
80
82
  }
83
+ /** Authentication state consumed by the sync and auth-isolated store lifecycles. */
84
+ type AuthSyncState = {
85
+ state: 'loading';
86
+ } | {
87
+ state: 'signed-out';
88
+ mayConnectAnonymously: false;
89
+ } | {
90
+ state: 'anonymous';
91
+ mayConnectAnonymously: true;
92
+ } | {
93
+ state: 'authenticated';
94
+ userId: string;
95
+ token: string;
96
+ };
81
97
 
82
98
  interface MutationControllerState {
83
99
  isLoading: boolean;
@@ -103,4 +119,4 @@ interface CreateMutationControllerOptions<TData, TArgs extends unknown[], TConte
103
119
  */
104
120
  declare function createMutationController<TData, TArgs extends unknown[], TContext = void>(options: CreateMutationControllerOptions<TData, TArgs, TContext>): MutationController<TData, TArgs, TContext>;
105
121
 
106
- export { type AuthSyncBinding, type CreateMutationControllerOptions, type KoraAppLike, type KoraBindingSyncBridge, type KoraBindingSyncStatus, type KoraContextValue, type MutationController, type MutationControllerState, type UseMutationOptions, type UseMutationResultBase, type UseQueryOptions, createMutationController };
122
+ export { type AuthSyncBinding, type AuthSyncState, type CreateMutationControllerOptions, type KoraAppLike, type KoraBindingSyncBridge, type KoraBindingSyncStatus, type KoraContextValue, type MutationController, type MutationControllerState, type UseMutationOptions, type UseMutationResultBase, type UseQueryOptions, createMutationController };
@@ -1,4 +1,4 @@
1
- import { S as SchemaDefinition } from './events-3z0Tema3.cjs';
1
+ import { S as SchemaDefinition } from './events-D7JCtQSD.cjs';
2
2
 
3
3
  /**
4
4
  * Per-collection scope map: `{ collectionName: { field: value, ... } }`
@@ -1,4 +1,4 @@
1
- import { S as SchemaDefinition } from './events-3z0Tema3.js';
1
+ import { S as SchemaDefinition } from './events-D7JCtQSD.js';
2
2
 
3
3
  /**
4
4
  * Per-collection scope map: `{ collectionName: { field: value, ... } }`
@@ -486,7 +486,7 @@ interface BlobRef {
486
486
  */
487
487
  interface SyncDiagnosticsSnapshot {
488
488
  /** Current developer-facing sync status */
489
- status: 'connected' | 'reconnecting' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error';
489
+ status: 'connected' | 'reconnecting' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error' | 'auth-required';
490
490
  /** Timestamp when the current connection was established, or null if disconnected */
491
491
  connectedAt: number | null;
492
492
  /** Timestamp when the last disconnection occurred, or null if never disconnected */
@@ -738,6 +738,16 @@ interface SequenceConfig {
738
738
  startAt?: number;
739
739
  }
740
740
 
741
+ interface SyncApplyFailureEvent {
742
+ operationId: string;
743
+ collection: string;
744
+ recordId: string;
745
+ code: string;
746
+ message: string;
747
+ retriable: boolean;
748
+ firstSeenAt: number;
749
+ retryCount: number;
750
+ }
741
751
  /**
742
752
  * Trace of a merge decision. Records all inputs and outputs for debugging and DevTools.
743
753
  */
@@ -795,6 +805,21 @@ type KoraEvent = {
795
805
  } | {
796
806
  type: 'sync:auth-failed';
797
807
  reason: string;
808
+ } | {
809
+ type: 'sync:suspended';
810
+ reason: 'auth-loading' | 'auth-required' | 'auth-rejected' | 'device-revoked' | string;
811
+ } | {
812
+ type: 'sync:apply-blocked';
813
+ failure: SyncApplyFailureEvent;
814
+ } | {
815
+ type: 'sync:apply-retrying';
816
+ failure: SyncApplyFailureEvent;
817
+ } | {
818
+ type: 'sync:apply-recovered';
819
+ failure: SyncApplyFailureEvent;
820
+ } | {
821
+ type: 'sync:apply-abandoned';
822
+ failure: SyncApplyFailureEvent;
798
823
  } | {
799
824
  type: 'sync:clock-skew';
800
825
  /** serverTime - localTime in ms. Negative = this device's clock is fast. */
@@ -815,9 +840,18 @@ type KoraEvent = {
815
840
  type: 'sync:received';
816
841
  operations: Operation[];
817
842
  batchSize: number;
843
+ /** Server-ingest outcomes. Omitted for ordinary client-side receive events. */
844
+ uniqueOperations?: number;
845
+ duplicateOperations?: number;
846
+ rejectedOperations?: number;
818
847
  } | {
819
848
  type: 'sync:acknowledged';
820
849
  sequenceNumber: number;
850
+ } | {
851
+ type: 'sync:scope-retracted';
852
+ collection: string;
853
+ recordId: string;
854
+ quarantinedOperationIds: string[];
821
855
  } | {
822
856
  type: 'sync:apply-failed';
823
857
  operationId: string;
@@ -852,7 +886,9 @@ type KoraEvent = {
852
886
  type: 'sync:delivery-stalled';
853
887
  sessionId: string;
854
888
  watermark: number;
889
+ outstandingMaxDeliverySequence: number;
855
890
  repeatCount: number;
891
+ reason: 'unacknowledged-delivery';
856
892
  } | {
857
893
  type: 'query:subscribed';
858
894
  queryId: string;
@@ -486,7 +486,7 @@ interface BlobRef {
486
486
  */
487
487
  interface SyncDiagnosticsSnapshot {
488
488
  /** Current developer-facing sync status */
489
- status: 'connected' | 'reconnecting' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error';
489
+ status: 'connected' | 'reconnecting' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error' | 'auth-required';
490
490
  /** Timestamp when the current connection was established, or null if disconnected */
491
491
  connectedAt: number | null;
492
492
  /** Timestamp when the last disconnection occurred, or null if never disconnected */
@@ -738,6 +738,16 @@ interface SequenceConfig {
738
738
  startAt?: number;
739
739
  }
740
740
 
741
+ interface SyncApplyFailureEvent {
742
+ operationId: string;
743
+ collection: string;
744
+ recordId: string;
745
+ code: string;
746
+ message: string;
747
+ retriable: boolean;
748
+ firstSeenAt: number;
749
+ retryCount: number;
750
+ }
741
751
  /**
742
752
  * Trace of a merge decision. Records all inputs and outputs for debugging and DevTools.
743
753
  */
@@ -795,6 +805,21 @@ type KoraEvent = {
795
805
  } | {
796
806
  type: 'sync:auth-failed';
797
807
  reason: string;
808
+ } | {
809
+ type: 'sync:suspended';
810
+ reason: 'auth-loading' | 'auth-required' | 'auth-rejected' | 'device-revoked' | string;
811
+ } | {
812
+ type: 'sync:apply-blocked';
813
+ failure: SyncApplyFailureEvent;
814
+ } | {
815
+ type: 'sync:apply-retrying';
816
+ failure: SyncApplyFailureEvent;
817
+ } | {
818
+ type: 'sync:apply-recovered';
819
+ failure: SyncApplyFailureEvent;
820
+ } | {
821
+ type: 'sync:apply-abandoned';
822
+ failure: SyncApplyFailureEvent;
798
823
  } | {
799
824
  type: 'sync:clock-skew';
800
825
  /** serverTime - localTime in ms. Negative = this device's clock is fast. */
@@ -815,9 +840,18 @@ type KoraEvent = {
815
840
  type: 'sync:received';
816
841
  operations: Operation[];
817
842
  batchSize: number;
843
+ /** Server-ingest outcomes. Omitted for ordinary client-side receive events. */
844
+ uniqueOperations?: number;
845
+ duplicateOperations?: number;
846
+ rejectedOperations?: number;
818
847
  } | {
819
848
  type: 'sync:acknowledged';
820
849
  sequenceNumber: number;
850
+ } | {
851
+ type: 'sync:scope-retracted';
852
+ collection: string;
853
+ recordId: string;
854
+ quarantinedOperationIds: string[];
821
855
  } | {
822
856
  type: 'sync:apply-failed';
823
857
  operationId: string;
@@ -852,7 +886,9 @@ type KoraEvent = {
852
886
  type: 'sync:delivery-stalled';
853
887
  sessionId: string;
854
888
  watermark: number;
889
+ outstandingMaxDeliverySequence: number;
855
890
  repeatCount: number;
891
+ reason: 'unacknowledged-delivery';
856
892
  } | {
857
893
  type: 'query:subscribed';
858
894
  queryId: string;
package/dist/index.cjs CHANGED
@@ -3194,15 +3194,20 @@ var KORA_OPERATION_MESSAGE = `message KoraOperation {
3194
3194
  var OPERATION_BATCH_MESSAGE = `message OperationBatch {
3195
3195
  repeated KoraOperation operations = 1;
3196
3196
  bool is_final = 2;
3197
+ bytes retractions_json = 3;
3197
3198
  }`;
3198
3199
  var HANDSHAKE_MESSAGE = `message HandshakeMessage {
3199
3200
  map<string, int64> version_vector = 1;
3200
3201
  int32 schema_version = 2;
3201
3202
  string node_id = 3;
3203
+ string scope_exit_policy = 4;
3202
3204
  }`;
3203
3205
  var HANDSHAKE_RESPONSE_MESSAGE = `message HandshakeResponse {
3204
3206
  map<string, int64> version_vector = 1;
3205
3207
  int32 schema_version = 2;
3208
+ bytes accepted_scope_json = 3;
3209
+ bytes accepted_downlink_scopes_json = 4;
3210
+ bytes accepted_uplink_scopes_json = 5;
3206
3211
  }`;
3207
3212
  var ACKNOWLEDGMENT_MESSAGE = `message Acknowledgment {
3208
3213
  int64 sequence_number = 1;
@@ -3268,20 +3273,25 @@ function buildJsonDescriptor(schema) {
3268
3273
  nested.OperationBatch = {
3269
3274
  fields: {
3270
3275
  operations: { type: "KoraOperation", id: 1, rule: "repeated" },
3271
- is_final: { type: "bool", id: 2 }
3276
+ is_final: { type: "bool", id: 2 },
3277
+ retractions_json: { type: "bytes", id: 3 }
3272
3278
  }
3273
3279
  };
3274
3280
  nested.HandshakeMessage = {
3275
3281
  fields: {
3276
3282
  version_vector: { keyType: "string", type: "int64", id: 1 },
3277
3283
  schema_version: { type: "int32", id: 2 },
3278
- node_id: { type: "string", id: 3 }
3284
+ node_id: { type: "string", id: 3 },
3285
+ scope_exit_policy: { type: "string", id: 4 }
3279
3286
  }
3280
3287
  };
3281
3288
  nested.HandshakeResponse = {
3282
3289
  fields: {
3283
3290
  version_vector: { keyType: "string", type: "int64", id: 1 },
3284
- schema_version: { type: "int32", id: 2 }
3291
+ schema_version: { type: "int32", id: 2 },
3292
+ accepted_scope_json: { type: "bytes", id: 3 },
3293
+ accepted_downlink_scopes_json: { type: "bytes", id: 4 },
3294
+ accepted_uplink_scopes_json: { type: "bytes", id: 5 }
3285
3295
  }
3286
3296
  };
3287
3297
  nested.Acknowledgment = {