@korajs/core 0.5.0 → 1.0.0-beta.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dr. Obed Ehoneah
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/bindings/index.ts
21
+ var bindings_exports = {};
22
+ __export(bindings_exports, {
23
+ createMutationController: () => createMutationController
24
+ });
25
+ module.exports = __toCommonJS(bindings_exports);
26
+
27
+ // src/bindings/create-mutation-controller.ts
28
+ function createMutationController(options) {
29
+ let snapshot = { isLoading: false, error: null };
30
+ const listeners = /* @__PURE__ */ new Set();
31
+ let disposed = false;
32
+ let inFlight = 0;
33
+ const notify = () => {
34
+ for (const listener of listeners) {
35
+ listener();
36
+ }
37
+ options.onStateChange?.(snapshot);
38
+ };
39
+ const setSnapshot = (next) => {
40
+ snapshot = next;
41
+ notify();
42
+ };
43
+ const getSnapshot = () => snapshot;
44
+ const subscribe = (listener) => {
45
+ listeners.add(listener);
46
+ return () => {
47
+ listeners.delete(listener);
48
+ };
49
+ };
50
+ const reset = () => {
51
+ if (disposed) return;
52
+ setSnapshot({ isLoading: false, error: null });
53
+ };
54
+ const mutateAsync = async (...args) => {
55
+ if (disposed) {
56
+ throw new Error("Mutation controller is destroyed");
57
+ }
58
+ const opts = options.resolveOptions?.() ?? options.options;
59
+ let context;
60
+ inFlight++;
61
+ setSnapshot({ isLoading: true, error: null });
62
+ try {
63
+ if (opts?.onMutate) {
64
+ context = await opts.onMutate(...args);
65
+ }
66
+ const result = await options.mutationFn(...args);
67
+ inFlight = Math.max(0, inFlight - 1);
68
+ setSnapshot({ isLoading: inFlight > 0, error: null });
69
+ opts?.onSuccess?.(result, ...args);
70
+ opts?.onSettled?.(result, null, ...args);
71
+ return result;
72
+ } catch (err) {
73
+ const error = err instanceof Error ? err : new Error(String(err));
74
+ if (context !== void 0 && opts?.onRollback) {
75
+ await opts.onRollback(context, ...args);
76
+ }
77
+ inFlight = Math.max(0, inFlight - 1);
78
+ setSnapshot({ isLoading: inFlight > 0, error });
79
+ opts?.onError?.(error, ...args);
80
+ opts?.onSettled?.(void 0, error, ...args);
81
+ throw error;
82
+ }
83
+ };
84
+ const mutate = (...args) => {
85
+ void mutateAsync(...args).catch(() => {
86
+ });
87
+ };
88
+ const destroy = () => {
89
+ disposed = true;
90
+ listeners.clear();
91
+ };
92
+ return {
93
+ getSnapshot,
94
+ subscribe,
95
+ mutate,
96
+ mutateAsync,
97
+ reset,
98
+ destroy
99
+ };
100
+ }
101
+ // Annotate the CommonJS export names for ESM import in node:
102
+ 0 && (module.exports = {
103
+ createMutationController
104
+ });
105
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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":[]}
@@ -0,0 +1,103 @@
1
+ import { p as KoraEventEmitter } from '../events-BynBOsO3.cjs';
2
+ import { S as ScopeMap } from '../build-scope-map-BIeawJzC.cjs';
3
+
4
+ /** Sync status shape consumed by framework binding hooks (matches {@link SyncStatusInfo} in `@korajs/sync`). */
5
+ interface KoraBindingSyncStatus {
6
+ status: 'connected' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error';
7
+ pendingOperations: number;
8
+ lastSyncedAt: number | null;
9
+ lastSuccessfulPush: number | null;
10
+ lastSuccessfulPull: number | null;
11
+ conflicts: number;
12
+ /** serverTime - localTime in ms at last handshake; negative = device clock is fast. */
13
+ clockSkewMs: number | null;
14
+ }
15
+ /** Minimal sync bridge surface exposed on `createApp().sync`. */
16
+ interface KoraBindingSyncBridge {
17
+ subscribeStatus(listener: (status: KoraBindingSyncStatus) => void): () => void;
18
+ }
19
+ /**
20
+ * Structural type for a Kora application instance from `createApp()`.
21
+ * Framework packages specialize `TStore` and `TSyncEngine` with concrete types.
22
+ */
23
+ interface KoraAppLike<TStore = unknown, TSyncEngine = unknown, TQueryCache = unknown> {
24
+ ready: Promise<void>;
25
+ events?: KoraEventEmitter;
26
+ sync?: KoraBindingSyncBridge | null;
27
+ getStore(): TStore;
28
+ getSyncEngine(): TSyncEngine | null;
29
+ getQueryStoreCache?(): TQueryCache;
30
+ }
31
+ /** Value provided by framework `KoraProvider` implementations. */
32
+ interface KoraContextValue<TStore = unknown, TSyncEngine = unknown, TQueryCache = unknown> {
33
+ store: TStore;
34
+ syncEngine: TSyncEngine | null;
35
+ app: KoraAppLike<TStore, TSyncEngine, TQueryCache> | null;
36
+ events: KoraEventEmitter | null;
37
+ subscribeSyncStatus: KoraBindingSyncBridge['subscribeStatus'] | null;
38
+ queryStoreCache: TQueryCache;
39
+ }
40
+ /** Options for reactive query bindings (`useQuery`, `createQueryStore`, etc.). */
41
+ interface UseQueryOptions {
42
+ /** When false, the query subscription is disabled. Defaults to true. */
43
+ enabled?: boolean;
44
+ }
45
+ /** Optimistic mutation lifecycle callbacks shared across framework bindings. */
46
+ interface UseMutationOptions<TData, TArgs extends unknown[], TContext = void> {
47
+ onMutate?: (...args: TArgs) => TContext | Promise<TContext>;
48
+ onRollback?: (context: TContext, ...args: TArgs) => void | Promise<void>;
49
+ onSuccess?: (data: TData, ...args: TArgs) => void;
50
+ onError?: (error: Error, ...args: TArgs) => void;
51
+ onSettled?: (data: TData | undefined, error: Error | null, ...args: TArgs) => void;
52
+ }
53
+ /** Common mutation result surface (React uses plain values; Vue/Svelte extend this). */
54
+ interface UseMutationResultBase<TData, TArgs extends unknown[]> {
55
+ mutate: (...args: TArgs) => void;
56
+ mutateAsync: (...args: TArgs) => Promise<TData>;
57
+ reset: () => void;
58
+ }
59
+ /**
60
+ * Pre-built auth binding for `createApp({ sync: { authClient } })`.
61
+ * Created by `createKoraAuthSync()` in `@korajs/auth`.
62
+ */
63
+ interface AuthSyncBinding {
64
+ /** Returns the access token for sync handshake (empty string when signed out). */
65
+ auth: () => Promise<{
66
+ token: string;
67
+ }>;
68
+ /** Builds a scope map from the current token and schema. */
69
+ resolveScopeMap?: () => Promise<ScopeMap | undefined>;
70
+ /**
71
+ * Returns the device-bound sync node id from the token `dev` claim.
72
+ * Separate from the user id (`sub`).
73
+ */
74
+ resolveNodeId?: () => Promise<string | undefined>;
75
+ /** Notifies when auth state changes so sync can refresh scope or reconnect. */
76
+ subscribe?: (listener: () => void) => () => void;
77
+ }
78
+
79
+ interface MutationControllerState {
80
+ isLoading: boolean;
81
+ error: Error | null;
82
+ }
83
+ interface MutationController<TData, TArgs extends unknown[], TContext = void> {
84
+ getSnapshot(): MutationControllerState;
85
+ subscribe(listener: () => void): () => void;
86
+ mutate(...args: TArgs): void;
87
+ mutateAsync(...args: TArgs): Promise<TData>;
88
+ reset(): void;
89
+ destroy(): void;
90
+ }
91
+ interface CreateMutationControllerOptions<TData, TArgs extends unknown[], TContext = void> {
92
+ mutationFn: (...args: TArgs) => Promise<TData>;
93
+ options?: UseMutationOptions<TData, TArgs, TContext>;
94
+ /** Reads latest options on each mutation (for framework hooks with ref-backed options). */
95
+ resolveOptions?: () => UseMutationOptions<TData, TArgs, TContext> | undefined;
96
+ onStateChange?: (state: MutationControllerState) => void;
97
+ }
98
+ /**
99
+ * Framework-agnostic mutation runner with optimistic lifecycle hooks.
100
+ */
101
+ declare function createMutationController<TData, TArgs extends unknown[], TContext = void>(options: CreateMutationControllerOptions<TData, TArgs, TContext>): MutationController<TData, TArgs, TContext>;
102
+
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 };
@@ -0,0 +1,103 @@
1
+ import { p as KoraEventEmitter } from '../events-BynBOsO3.js';
2
+ import { S as ScopeMap } from '../build-scope-map-DOf4JLTh.js';
3
+
4
+ /** Sync status shape consumed by framework binding hooks (matches {@link SyncStatusInfo} in `@korajs/sync`). */
5
+ interface KoraBindingSyncStatus {
6
+ status: 'connected' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error';
7
+ pendingOperations: number;
8
+ lastSyncedAt: number | null;
9
+ lastSuccessfulPush: number | null;
10
+ lastSuccessfulPull: number | null;
11
+ conflicts: number;
12
+ /** serverTime - localTime in ms at last handshake; negative = device clock is fast. */
13
+ clockSkewMs: number | null;
14
+ }
15
+ /** Minimal sync bridge surface exposed on `createApp().sync`. */
16
+ interface KoraBindingSyncBridge {
17
+ subscribeStatus(listener: (status: KoraBindingSyncStatus) => void): () => void;
18
+ }
19
+ /**
20
+ * Structural type for a Kora application instance from `createApp()`.
21
+ * Framework packages specialize `TStore` and `TSyncEngine` with concrete types.
22
+ */
23
+ interface KoraAppLike<TStore = unknown, TSyncEngine = unknown, TQueryCache = unknown> {
24
+ ready: Promise<void>;
25
+ events?: KoraEventEmitter;
26
+ sync?: KoraBindingSyncBridge | null;
27
+ getStore(): TStore;
28
+ getSyncEngine(): TSyncEngine | null;
29
+ getQueryStoreCache?(): TQueryCache;
30
+ }
31
+ /** Value provided by framework `KoraProvider` implementations. */
32
+ interface KoraContextValue<TStore = unknown, TSyncEngine = unknown, TQueryCache = unknown> {
33
+ store: TStore;
34
+ syncEngine: TSyncEngine | null;
35
+ app: KoraAppLike<TStore, TSyncEngine, TQueryCache> | null;
36
+ events: KoraEventEmitter | null;
37
+ subscribeSyncStatus: KoraBindingSyncBridge['subscribeStatus'] | null;
38
+ queryStoreCache: TQueryCache;
39
+ }
40
+ /** Options for reactive query bindings (`useQuery`, `createQueryStore`, etc.). */
41
+ interface UseQueryOptions {
42
+ /** When false, the query subscription is disabled. Defaults to true. */
43
+ enabled?: boolean;
44
+ }
45
+ /** Optimistic mutation lifecycle callbacks shared across framework bindings. */
46
+ interface UseMutationOptions<TData, TArgs extends unknown[], TContext = void> {
47
+ onMutate?: (...args: TArgs) => TContext | Promise<TContext>;
48
+ onRollback?: (context: TContext, ...args: TArgs) => void | Promise<void>;
49
+ onSuccess?: (data: TData, ...args: TArgs) => void;
50
+ onError?: (error: Error, ...args: TArgs) => void;
51
+ onSettled?: (data: TData | undefined, error: Error | null, ...args: TArgs) => void;
52
+ }
53
+ /** Common mutation result surface (React uses plain values; Vue/Svelte extend this). */
54
+ interface UseMutationResultBase<TData, TArgs extends unknown[]> {
55
+ mutate: (...args: TArgs) => void;
56
+ mutateAsync: (...args: TArgs) => Promise<TData>;
57
+ reset: () => void;
58
+ }
59
+ /**
60
+ * Pre-built auth binding for `createApp({ sync: { authClient } })`.
61
+ * Created by `createKoraAuthSync()` in `@korajs/auth`.
62
+ */
63
+ interface AuthSyncBinding {
64
+ /** Returns the access token for sync handshake (empty string when signed out). */
65
+ auth: () => Promise<{
66
+ token: string;
67
+ }>;
68
+ /** Builds a scope map from the current token and schema. */
69
+ resolveScopeMap?: () => Promise<ScopeMap | undefined>;
70
+ /**
71
+ * Returns the device-bound sync node id from the token `dev` claim.
72
+ * Separate from the user id (`sub`).
73
+ */
74
+ resolveNodeId?: () => Promise<string | undefined>;
75
+ /** Notifies when auth state changes so sync can refresh scope or reconnect. */
76
+ subscribe?: (listener: () => void) => () => void;
77
+ }
78
+
79
+ interface MutationControllerState {
80
+ isLoading: boolean;
81
+ error: Error | null;
82
+ }
83
+ interface MutationController<TData, TArgs extends unknown[], TContext = void> {
84
+ getSnapshot(): MutationControllerState;
85
+ subscribe(listener: () => void): () => void;
86
+ mutate(...args: TArgs): void;
87
+ mutateAsync(...args: TArgs): Promise<TData>;
88
+ reset(): void;
89
+ destroy(): void;
90
+ }
91
+ interface CreateMutationControllerOptions<TData, TArgs extends unknown[], TContext = void> {
92
+ mutationFn: (...args: TArgs) => Promise<TData>;
93
+ options?: UseMutationOptions<TData, TArgs, TContext>;
94
+ /** Reads latest options on each mutation (for framework hooks with ref-backed options). */
95
+ resolveOptions?: () => UseMutationOptions<TData, TArgs, TContext> | undefined;
96
+ onStateChange?: (state: MutationControllerState) => void;
97
+ }
98
+ /**
99
+ * Framework-agnostic mutation runner with optimistic lifecycle hooks.
100
+ */
101
+ declare function createMutationController<TData, TArgs extends unknown[], TContext = void>(options: CreateMutationControllerOptions<TData, TArgs, TContext>): MutationController<TData, TArgs, TContext>;
102
+
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 };
@@ -0,0 +1,78 @@
1
+ // src/bindings/create-mutation-controller.ts
2
+ function createMutationController(options) {
3
+ let snapshot = { isLoading: false, error: null };
4
+ const listeners = /* @__PURE__ */ new Set();
5
+ let disposed = false;
6
+ let inFlight = 0;
7
+ const notify = () => {
8
+ for (const listener of listeners) {
9
+ listener();
10
+ }
11
+ options.onStateChange?.(snapshot);
12
+ };
13
+ const setSnapshot = (next) => {
14
+ snapshot = next;
15
+ notify();
16
+ };
17
+ const getSnapshot = () => snapshot;
18
+ const subscribe = (listener) => {
19
+ listeners.add(listener);
20
+ return () => {
21
+ listeners.delete(listener);
22
+ };
23
+ };
24
+ const reset = () => {
25
+ if (disposed) return;
26
+ setSnapshot({ isLoading: false, error: null });
27
+ };
28
+ const mutateAsync = async (...args) => {
29
+ if (disposed) {
30
+ throw new Error("Mutation controller is destroyed");
31
+ }
32
+ const opts = options.resolveOptions?.() ?? options.options;
33
+ let context;
34
+ inFlight++;
35
+ setSnapshot({ isLoading: true, error: null });
36
+ try {
37
+ if (opts?.onMutate) {
38
+ context = await opts.onMutate(...args);
39
+ }
40
+ const result = await options.mutationFn(...args);
41
+ inFlight = Math.max(0, inFlight - 1);
42
+ setSnapshot({ isLoading: inFlight > 0, error: null });
43
+ opts?.onSuccess?.(result, ...args);
44
+ opts?.onSettled?.(result, null, ...args);
45
+ return result;
46
+ } catch (err) {
47
+ const error = err instanceof Error ? err : new Error(String(err));
48
+ if (context !== void 0 && opts?.onRollback) {
49
+ await opts.onRollback(context, ...args);
50
+ }
51
+ inFlight = Math.max(0, inFlight - 1);
52
+ setSnapshot({ isLoading: inFlight > 0, error });
53
+ opts?.onError?.(error, ...args);
54
+ opts?.onSettled?.(void 0, error, ...args);
55
+ throw error;
56
+ }
57
+ };
58
+ const mutate = (...args) => {
59
+ void mutateAsync(...args).catch(() => {
60
+ });
61
+ };
62
+ const destroy = () => {
63
+ disposed = true;
64
+ listeners.clear();
65
+ };
66
+ return {
67
+ getSnapshot,
68
+ subscribe,
69
+ mutate,
70
+ mutateAsync,
71
+ reset,
72
+ destroy
73
+ };
74
+ }
75
+ export {
76
+ createMutationController
77
+ };
78
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/bindings/create-mutation-controller.ts"],"sourcesContent":["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":";AA2BO,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":[]}
@@ -0,0 +1,33 @@
1
+ import { S as SchemaDefinition } from './events-BynBOsO3.cjs';
2
+
3
+ /**
4
+ * Per-collection scope map: `{ collectionName: { field: value, ... } }`
5
+ *
6
+ * Used to filter which operations a client should receive during sync.
7
+ */
8
+ type ScopeMap = Record<string, Record<string, unknown>>;
9
+ /**
10
+ * Build a per-collection scope map from the schema's scope declarations
11
+ * and the client's flat scope values.
12
+ *
13
+ * Supports both legacy `collection.scope` arrays and declarative `schema.sync`
14
+ * rules (`sync: { todos: { where: { userId: true } } }`).
15
+ *
16
+ * When `schema.sync` is present, only collections with sync rules or legacy
17
+ * scope fields are included in the result. Other collections are omitted so
18
+ * sync engines treat them as out of scope (partial sync).
19
+ *
20
+ * @param schema - The schema definition with scope declarations
21
+ * @param scopeValues - Flat key-value scope values from the client
22
+ * @returns A per-collection scope map
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * // Schema declares: sales.scope = ['orgId', 'storeId']
27
+ * // Client provides: { orgId: 'org-123', storeId: 'store-456' }
28
+ * // Result: { sales: { orgId: 'org-123', storeId: 'store-456' }, products: {} }
29
+ * ```
30
+ */
31
+ declare function buildScopeMap(schema: SchemaDefinition, scopeValues: Record<string, unknown>): ScopeMap;
32
+
33
+ export { type ScopeMap as S, buildScopeMap as b };
@@ -0,0 +1,33 @@
1
+ import { S as SchemaDefinition } from './events-BynBOsO3.js';
2
+
3
+ /**
4
+ * Per-collection scope map: `{ collectionName: { field: value, ... } }`
5
+ *
6
+ * Used to filter which operations a client should receive during sync.
7
+ */
8
+ type ScopeMap = Record<string, Record<string, unknown>>;
9
+ /**
10
+ * Build a per-collection scope map from the schema's scope declarations
11
+ * and the client's flat scope values.
12
+ *
13
+ * Supports both legacy `collection.scope` arrays and declarative `schema.sync`
14
+ * rules (`sync: { todos: { where: { userId: true } } }`).
15
+ *
16
+ * When `schema.sync` is present, only collections with sync rules or legacy
17
+ * scope fields are included in the result. Other collections are omitted so
18
+ * sync engines treat them as out of scope (partial sync).
19
+ *
20
+ * @param schema - The schema definition with scope declarations
21
+ * @param scopeValues - Flat key-value scope values from the client
22
+ * @returns A per-collection scope map
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * // Schema declares: sales.scope = ['orgId', 'storeId']
27
+ * // Client provides: { orgId: 'org-123', storeId: 'store-456' }
28
+ * // Result: { sales: { orgId: 'org-123', storeId: 'store-456' }, products: {} }
29
+ * ```
30
+ */
31
+ declare function buildScopeMap(schema: SchemaDefinition, scopeValues: Record<string, unknown>): ScopeMap;
32
+
33
+ export { type ScopeMap as S, buildScopeMap as b };