@korajs/core 1.0.0-beta.1 → 1.0.0-beta.11

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,10 @@
1
- import { p as KoraEventEmitter } from '../events-CqDkr1LN.cjs';
2
- import { S as ScopeMap } from '../build-scope-map-DtTr9C7A.cjs';
1
+ import { p as KoraEventEmitter } from '../events-BPbYUbrE.cjs';
2
+ import { S as ScopeMap } from '../build-scope-map--0H8l6Tw.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' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error';
6
+ status: 'connected' | 'reconnecting' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error' | 'auth-required';
7
+ reconnecting: boolean;
7
8
  pendingOperations: number;
8
9
  lastSyncedAt: number | null;
9
10
  lastSuccessfulPush: number | null;
@@ -65,6 +66,8 @@ interface AuthSyncBinding {
65
66
  auth: () => Promise<{
66
67
  token: string;
67
68
  }>;
69
+ /** Resolve whether sync may currently open a transport. Tokens are never used as readiness signals. */
70
+ resolveSyncState?: () => Promise<AuthSyncState>;
68
71
  /** Builds a scope map from the current token and schema. */
69
72
  resolveScopeMap?: () => Promise<ScopeMap | undefined>;
70
73
  /**
@@ -72,9 +75,25 @@ interface AuthSyncBinding {
72
75
  * Separate from the user id (`sub`).
73
76
  */
74
77
  resolveNodeId?: () => Promise<string | undefined>;
78
+ /** Returns the authenticated user id used for local store namespacing. */
79
+ resolveUserId?: () => Promise<string | undefined>;
75
80
  /** Notifies when auth state changes so sync can refresh scope or reconnect. */
76
81
  subscribe?: (listener: () => void) => () => void;
77
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
+ };
78
97
 
79
98
  interface MutationControllerState {
80
99
  isLoading: boolean;
@@ -100,4 +119,4 @@ interface CreateMutationControllerOptions<TData, TArgs extends unknown[], TConte
100
119
  */
101
120
  declare function createMutationController<TData, TArgs extends unknown[], TContext = void>(options: CreateMutationControllerOptions<TData, TArgs, TContext>): MutationController<TData, TArgs, TContext>;
102
121
 
103
- 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,10 @@
1
- import { p as KoraEventEmitter } from '../events-CqDkr1LN.js';
2
- import { S as ScopeMap } from '../build-scope-map-DvOHbYjn.js';
1
+ import { p as KoraEventEmitter } from '../events-BPbYUbrE.js';
2
+ import { S as ScopeMap } from '../build-scope-map-B3R_emUU.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' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error';
6
+ status: 'connected' | 'reconnecting' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error' | 'auth-required';
7
+ reconnecting: boolean;
7
8
  pendingOperations: number;
8
9
  lastSyncedAt: number | null;
9
10
  lastSuccessfulPush: number | null;
@@ -65,6 +66,8 @@ interface AuthSyncBinding {
65
66
  auth: () => Promise<{
66
67
  token: string;
67
68
  }>;
69
+ /** Resolve whether sync may currently open a transport. Tokens are never used as readiness signals. */
70
+ resolveSyncState?: () => Promise<AuthSyncState>;
68
71
  /** Builds a scope map from the current token and schema. */
69
72
  resolveScopeMap?: () => Promise<ScopeMap | undefined>;
70
73
  /**
@@ -72,9 +75,25 @@ interface AuthSyncBinding {
72
75
  * Separate from the user id (`sub`).
73
76
  */
74
77
  resolveNodeId?: () => Promise<string | undefined>;
78
+ /** Returns the authenticated user id used for local store namespacing. */
79
+ resolveUserId?: () => Promise<string | undefined>;
75
80
  /** Notifies when auth state changes so sync can refresh scope or reconnect. */
76
81
  subscribe?: (listener: () => void) => () => void;
77
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
+ };
78
97
 
79
98
  interface MutationControllerState {
80
99
  isLoading: boolean;
@@ -100,4 +119,4 @@ interface CreateMutationControllerOptions<TData, TArgs extends unknown[], TConte
100
119
  */
101
120
  declare function createMutationController<TData, TArgs extends unknown[], TContext = void>(options: CreateMutationControllerOptions<TData, TArgs, TContext>): MutationController<TData, TArgs, TContext>;
102
121
 
103
- 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-CqDkr1LN.cjs';
1
+ import { S as SchemaDefinition } from './events-BPbYUbrE.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-CqDkr1LN.js';
1
+ import { S as SchemaDefinition } from './events-BPbYUbrE.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' | '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. */
@@ -826,6 +851,33 @@ type KoraEvent = {
826
851
  code: string;
827
852
  message: string;
828
853
  retriable: boolean;
854
+ } | {
855
+ /**
856
+ * The server rejected one of THIS client's outbound operations before it
857
+ * became authoritative. The op has been diverted out of the pending sync
858
+ * queue into the durable rejected store (kept, not retried); the app can
859
+ * surface the reason and decide whether to roll back the optimistic local
860
+ * write or let the user edit and resubmit.
861
+ */
862
+ type: 'sync:operation-rejected';
863
+ operationId: string;
864
+ collection: string;
865
+ recordId: string;
866
+ code: string;
867
+ message: string;
868
+ retriable: boolean;
869
+ } | {
870
+ type: 'sync:delivery-gap';
871
+ expectedBase: number;
872
+ receivedBase: number;
873
+ currentWatermark: number;
874
+ messageId: string;
875
+ repeatCount: number;
876
+ } | {
877
+ type: 'sync:delivery-stalled';
878
+ sessionId: string;
879
+ watermark: number;
880
+ repeatCount: number;
829
881
  } | {
830
882
  type: 'query:subscribed';
831
883
  queryId: string;
@@ -880,6 +932,46 @@ type KoraEvent = {
880
932
  type: 'store:quota-exceeded';
881
933
  dbName: string;
882
934
  message: string;
935
+ } | {
936
+ /**
937
+ * OPFS persistence was unavailable, so the store fell back to a
938
+ * NON-PERSISTENT in-memory database. Anything written this session is lost
939
+ * on reload. This is emitted instead of failing silently so the condition
940
+ * is observable rather than a data-loss trap.
941
+ */
942
+ type: 'store:opfs-unavailable';
943
+ dbName: string;
944
+ /**
945
+ * Why OPFS could not be used. `lock-conflict` means another runtime on this
946
+ * origin already holds the OPFS pool for this database; `timeout` means the
947
+ * VFS install did not complete in time (common in headless CI); `unsupported`
948
+ * means the runtime has no usable OPFS.
949
+ */
950
+ reason: 'lock-conflict' | 'timeout' | 'unsupported';
951
+ message: string;
952
+ } | {
953
+ /**
954
+ * A preferred storage backend could not provide durable storage, so Kora
955
+ * promoted the app to another durable backend before user code observed the
956
+ * store. This is informational: data still survives reloads.
957
+ */
958
+ type: 'store:storage-fallback';
959
+ dbName: string;
960
+ from: 'opfs' | 'sqlite-wasm';
961
+ to: 'indexeddb';
962
+ reason: 'lock-conflict' | 'timeout' | 'unsupported';
963
+ message: string;
964
+ } | {
965
+ /**
966
+ * Another runtime on this origin was already using this database name, so
967
+ * this runtime attached to it as a follower and now SHARES that one
968
+ * database. That is intended for multiple tabs of the SAME app; it is a bug
969
+ * if these are logically separate apps, which should each use a distinct
970
+ * store name (`store: { name: '...' }`) to stay isolated.
971
+ */
972
+ type: 'store:db-name-collision';
973
+ dbName: string;
974
+ message: string;
883
975
  } | {
884
976
  type: 'replay:completed';
885
977
  targetOperationId: string;
@@ -486,7 +486,7 @@ interface BlobRef {
486
486
  */
487
487
  interface SyncDiagnosticsSnapshot {
488
488
  /** Current developer-facing sync status */
489
- status: 'connected' | '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. */
@@ -826,6 +851,33 @@ type KoraEvent = {
826
851
  code: string;
827
852
  message: string;
828
853
  retriable: boolean;
854
+ } | {
855
+ /**
856
+ * The server rejected one of THIS client's outbound operations before it
857
+ * became authoritative. The op has been diverted out of the pending sync
858
+ * queue into the durable rejected store (kept, not retried); the app can
859
+ * surface the reason and decide whether to roll back the optimistic local
860
+ * write or let the user edit and resubmit.
861
+ */
862
+ type: 'sync:operation-rejected';
863
+ operationId: string;
864
+ collection: string;
865
+ recordId: string;
866
+ code: string;
867
+ message: string;
868
+ retriable: boolean;
869
+ } | {
870
+ type: 'sync:delivery-gap';
871
+ expectedBase: number;
872
+ receivedBase: number;
873
+ currentWatermark: number;
874
+ messageId: string;
875
+ repeatCount: number;
876
+ } | {
877
+ type: 'sync:delivery-stalled';
878
+ sessionId: string;
879
+ watermark: number;
880
+ repeatCount: number;
829
881
  } | {
830
882
  type: 'query:subscribed';
831
883
  queryId: string;
@@ -880,6 +932,46 @@ type KoraEvent = {
880
932
  type: 'store:quota-exceeded';
881
933
  dbName: string;
882
934
  message: string;
935
+ } | {
936
+ /**
937
+ * OPFS persistence was unavailable, so the store fell back to a
938
+ * NON-PERSISTENT in-memory database. Anything written this session is lost
939
+ * on reload. This is emitted instead of failing silently so the condition
940
+ * is observable rather than a data-loss trap.
941
+ */
942
+ type: 'store:opfs-unavailable';
943
+ dbName: string;
944
+ /**
945
+ * Why OPFS could not be used. `lock-conflict` means another runtime on this
946
+ * origin already holds the OPFS pool for this database; `timeout` means the
947
+ * VFS install did not complete in time (common in headless CI); `unsupported`
948
+ * means the runtime has no usable OPFS.
949
+ */
950
+ reason: 'lock-conflict' | 'timeout' | 'unsupported';
951
+ message: string;
952
+ } | {
953
+ /**
954
+ * A preferred storage backend could not provide durable storage, so Kora
955
+ * promoted the app to another durable backend before user code observed the
956
+ * store. This is informational: data still survives reloads.
957
+ */
958
+ type: 'store:storage-fallback';
959
+ dbName: string;
960
+ from: 'opfs' | 'sqlite-wasm';
961
+ to: 'indexeddb';
962
+ reason: 'lock-conflict' | 'timeout' | 'unsupported';
963
+ message: string;
964
+ } | {
965
+ /**
966
+ * Another runtime on this origin was already using this database name, so
967
+ * this runtime attached to it as a follower and now SHARES that one
968
+ * database. That is intended for multiple tabs of the SAME app; it is a bug
969
+ * if these are logically separate apps, which should each use a distinct
970
+ * store name (`store: { name: '...' }`) to stay isolated.
971
+ */
972
+ type: 'store:db-name-collision';
973
+ dbName: string;
974
+ message: string;
883
975
  } | {
884
976
  type: 'replay:completed';
885
977
  targetOperationId: string;