qortex-core 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/qortex-core.svg)](https://badge.fury.io/js/qortex-core)
6
6
  [![Bundle Size](https://img.shields.io/bundlephobia/minzip/qortex-core)](https://bundlephobia.com/package/qortex-core)
7
+ [![Bundle Size](https://img.shields.io/badge/gzipped-2.1KB-brightgreen)](https://bundlephobia.com/package/qortex-core)
7
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
8
9
 
9
10
  ## ✨ What makes this special?
@@ -92,6 +93,7 @@ function useAuth() {
92
93
 
93
94
  queryManager.subscribeQuery(["auth", "user"], (state) => {
94
95
  user.value = state.data;
96
+ console.log("Auth state:", state.isSuccess, state.isLoading);
95
97
  });
96
98
 
97
99
  return { user, isAuthenticated };
@@ -159,12 +161,23 @@ const { data, isLoading, error, refetch } = useQuery(["todos"]);
159
161
  const todos = useQueryData(["todos"]);
160
162
  ```
161
163
 
162
- ### `queryManager.subscribeQuery(key, callback)`
164
+ ### `queryManager.subscribeQuery(key, callback, options?)`
165
+
166
+ Subscribe to query state changes with flexible callback signatures.
163
167
 
164
168
  ```ts
169
+ // Callback receives the current state
165
170
  const unsubscribe = queryManager.subscribeQuery(["todos"], (state) => {
166
171
  console.log("State changed:", state);
172
+ console.log("Data:", state.data);
173
+ console.log("Loading:", state.isLoading);
174
+ console.log("Success:", state.isSuccess);
167
175
  });
176
+
177
+ // With fetcher for automatic type inference
178
+ const unsubscribe = queryManager.subscribeQuery(["todos"], (state) => {
179
+ console.log("Todos:", state.data); // Automatically typed
180
+ }, { fetcher: fetchTodos });
168
181
  ```
169
182
 
170
183
  ### `queryManager.setDefaultConfig(config)`
package/index.d.ts CHANGED
@@ -10,15 +10,24 @@ type Fetcher<T = any> = () => Promise<T> | T;
10
10
  /** Function that compares two values for equality */
11
11
  type EqualityFn<T = any> = (a: T | undefined, b: T | undefined) => boolean;
12
12
  /**
13
- * Infers the resolved return type of a fetcher function
14
- * Falls back to any for user-friendly experience
13
+ * Infers the return type of a fetcher function
14
+ *
15
+ * This utility type extracts the return type from a fetcher function,
16
+ * handling both synchronous and asynchronous fetchers.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const fetchUser = async (id: string): Promise<User> => { ... };
21
+ * type UserType = InferFetcherResult<typeof fetchUser>; // Promise<User>
22
+ *
23
+ * const fetchConfig = (): Config => { ... };
24
+ * type ConfigType = InferFetcherResult<typeof fetchConfig>; // Config
25
+ * ```
26
+ *
27
+ * @template F - The fetcher function type
28
+ * @returns The inferred return type of the fetcher, or `any` if inference fails
15
29
  */
16
- type InferFetcherResult<F> = F extends (...args: any[]) => Promise<infer R> ? R : F extends (...args: any[]) => infer R ? R : any;
17
- /**
18
- * Infers the return type of a fetcher, handling both sync and async cases
19
- * Falls back to any for user-friendly experience
20
- */
21
- type InferFetcherReturnType<T> = T extends Fetcher<infer R> ? R : any;
30
+ type InferFetcherResult<F> = F extends Fetcher<infer R> ? R : any;
22
31
  /**
23
32
  * Query status types for better type safety
24
33
  */
@@ -120,12 +129,18 @@ declare class QueryManager {
120
129
  * Handles mount logic to potentially start fetching
121
130
  */
122
131
  getQueryData<T = any>(key: QueryKey, opts?: QueryOptions<T>): T | undefined;
132
+ getQueryData<F extends Fetcher>(key: QueryKey, opts: QueryOptions<InferFetcherResult<F>> & {
133
+ fetcher: F;
134
+ }): InferFetcherResult<F> | undefined;
123
135
  /**
124
136
  * Gets comprehensive query state including computed flags
125
137
  * Handles placeholder data and error states appropriately
126
138
  * Handles mount logic to potentially start fetching
127
139
  */
128
140
  getQueryState<T = unknown>(key: QueryKey, opts?: QueryOptions<T>): QueryState<T>;
141
+ getQueryState<F extends Fetcher>(key: QueryKey, opts: QueryOptions<InferFetcherResult<F>> & {
142
+ fetcher: F;
143
+ }): QueryState<InferFetcherResult<F>>;
129
144
  /**
130
145
  * Marks a query as invalidated, triggering refetch
131
146
  */
@@ -134,7 +149,11 @@ declare class QueryManager {
134
149
  * Subscribes to query state changes with automatic subscription management
135
150
  * Handles mount logic to potentially start fetching
136
151
  */
137
- subscribeQuery<T = any>(key: QueryKey, cb: () => void, opts?: QueryOptions<T>): () => void;
152
+ subscribeQuery(key: QueryKey, cb: (state: QueryState<any>) => void): () => void;
153
+ subscribeQuery<F extends Fetcher>(key: QueryKey, cb: (state: QueryState<InferFetcherResult<F>>) => void, opts: QueryOptions<InferFetcherResult<F>> & {
154
+ fetcher: F;
155
+ }): () => void;
156
+ subscribeQuery<T = any>(key: QueryKey, cb: (state: QueryState<T>) => void, opts?: QueryOptions<T>): () => void;
138
157
  /**
139
158
  * Core mount logic that determines when to fetch
140
159
  * Implements robust throttling and race condition prevention
@@ -149,4 +168,4 @@ declare const queryManager: QueryManager;
149
168
  */
150
169
  declare function serializeKey(key: QueryKey): string;
151
170
 
152
- export { DefaultConfig, EqualityFn, Fetcher, InferFetcherResult, InferFetcherReturnType, QueryKey, QueryKeyValue, QueryManager, QueryOptions, QueryState, QueryStatus, queryManager, serializeKey };
171
+ export { DefaultConfig, EqualityFn, Fetcher, InferFetcherResult, QueryKey, QueryKeyValue, QueryManager, QueryOptions, QueryState, QueryStatus, queryManager, serializeKey };
package/index.js CHANGED
@@ -71,7 +71,47 @@ function createDefaultState(opts, refetch) {
71
71
  usePlaceholderOnError: opts?.usePlaceholderOnError ?? false,
72
72
  refetchOnSubscribe: opts?.refetchOnSubscribe ?? "stale",
73
73
  enabled: opts?.enabled === false ? false : true,
74
- refetch: refetch || (() => Promise.resolve(void 0))
74
+ refetch: refetch || (() => Promise.resolve(void 0)),
75
+ isSuccess: false,
76
+ isError: false
77
+ };
78
+ }
79
+ function createPublicState(state) {
80
+ const now = Date.now();
81
+ const isStale = state.updatedAt == null || now - (state.updatedAt || 0) > state.staleTime || state.isInvalidated;
82
+ let returnedData = state.data;
83
+ let isPlaceholderData = false;
84
+ switch (state.status) {
85
+ case "error":
86
+ if (state.usePlaceholderOnError && state.placeholderData !== void 0) {
87
+ returnedData = state.placeholderData;
88
+ isPlaceholderData = true;
89
+ }
90
+ break;
91
+ case "fetching":
92
+ if (!state.data && state.placeholderData) {
93
+ returnedData = state.placeholderData;
94
+ isPlaceholderData = true;
95
+ }
96
+ break;
97
+ case "success":
98
+ case "idle":
99
+ returnedData = state.data ?? state.placeholderData;
100
+ isPlaceholderData = state.data ? false : Boolean(state.placeholderData);
101
+ break;
102
+ }
103
+ return {
104
+ data: returnedData,
105
+ error: state.error,
106
+ status: state.status,
107
+ updatedAt: state.updatedAt,
108
+ isStale,
109
+ isPlaceholderData,
110
+ isLoading: state.status === "fetching" && !state.updatedAt,
111
+ isFetching: state.status === "fetching",
112
+ isError: state.isError,
113
+ isSuccess: state.isSuccess,
114
+ refetch: state.refetch
75
115
  };
76
116
  }
77
117
 
@@ -115,12 +155,14 @@ var QueryManager = class {
115
155
  * Notifies all subscribers of a query state change
116
156
  */
117
157
  emit(key, state) {
118
- this.cache.set(serializeKey(key), state);
119
- const set = this.subs.get(serializeKey(key));
158
+ const stateKey = serializeKey(key);
159
+ this.cache.set(stateKey, state);
160
+ const set = this.subs.get(stateKey);
120
161
  if (!set)
121
162
  return;
163
+ const publicState = createPublicState(state);
122
164
  for (const cb of Array.from(set))
123
- cb();
165
+ cb(publicState);
124
166
  }
125
167
  registerFetcher(key, opts) {
126
168
  this.ensureState(key, opts);
@@ -151,9 +193,13 @@ var QueryManager = class {
151
193
  state.data = result2;
152
194
  state.status = "success";
153
195
  state.updatedAt = Date.now();
196
+ state.isError = false;
197
+ state.isSuccess = true;
154
198
  }).catch((error) => {
155
199
  state.error = error;
156
200
  state.status = "error";
201
+ state.isError = true;
202
+ state.isSuccess = false;
157
203
  }).finally(() => {
158
204
  state.fetchPromise = void 0;
159
205
  this.emit(key, state);
@@ -174,63 +220,19 @@ var QueryManager = class {
174
220
  state.error = void 0;
175
221
  state.status = "success";
176
222
  state.isInvalidated = false;
223
+ state.isError = false;
224
+ state.isSuccess = true;
177
225
  this.emit(key, state);
178
226
  }
179
- /**
180
- * Gets query data
181
- * Handles mount logic to potentially start fetching
182
- */
183
227
  getQueryData(key, opts) {
184
228
  const state = this.ensureState(key, opts);
185
229
  this.handleMountLogic(key, state);
186
230
  return state.data ?? state.placeholderData;
187
231
  }
188
- /**
189
- * Gets comprehensive query state including computed flags
190
- * Handles placeholder data and error states appropriately
191
- * Handles mount logic to potentially start fetching
192
- */
193
232
  getQueryState(key, opts) {
194
233
  let state = this.ensureState(key, opts);
195
- const now = Date.now();
196
- const isStale = state.updatedAt == null || now - (state.updatedAt || 0) > state.staleTime || state.isInvalidated;
197
- let returnedData = state.data;
198
- let isPlaceholderData = false;
199
- const status = state.status;
200
- switch (status) {
201
- case "error":
202
- if (state.usePlaceholderOnError && state.placeholderData !== void 0) {
203
- returnedData = state.placeholderData;
204
- isPlaceholderData = true;
205
- }
206
- break;
207
- case "fetching":
208
- if (!state.data && state.placeholderData) {
209
- returnedData = state.placeholderData;
210
- isPlaceholderData = true;
211
- }
212
- break;
213
- case "success":
214
- case "idle":
215
- returnedData = state.data ?? state.placeholderData;
216
- isPlaceholderData = state.data ? false : Boolean(state.placeholderData);
217
- break;
218
- }
219
234
  this.handleMountLogic(key, state);
220
- const currentState = {
221
- data: returnedData,
222
- error: state.error,
223
- status: state.status,
224
- updatedAt: state.updatedAt,
225
- isStale,
226
- isPlaceholderData,
227
- isLoading: state.status === "fetching" && !state.updatedAt,
228
- // true only for first fetch
229
- isFetching: state.status === "fetching",
230
- isError: state.status === "error",
231
- isSuccess: state.status === "success",
232
- refetch: state.refetch
233
- };
235
+ const currentState = createPublicState(state);
234
236
  const stateKey = serializeKey(key);
235
237
  const lastState = this.lastReturnedState?.get(stateKey);
236
238
  if (!lastState || !shallowEqual(lastState, currentState)) {
@@ -250,10 +252,6 @@ var QueryManager = class {
250
252
  this.emit(key, state);
251
253
  this.fetchQuery(key);
252
254
  }
253
- /**
254
- * Subscribes to query state changes with automatic subscription management
255
- * Handles mount logic to potentially start fetching
256
- */
257
255
  subscribeQuery(key, cb, opts) {
258
256
  const sk = serializeKey(key);
259
257
  const state = this.ensureState(key, opts);
package/index.mjs CHANGED
@@ -43,7 +43,47 @@ function createDefaultState(opts, refetch) {
43
43
  usePlaceholderOnError: opts?.usePlaceholderOnError ?? false,
44
44
  refetchOnSubscribe: opts?.refetchOnSubscribe ?? "stale",
45
45
  enabled: opts?.enabled === false ? false : true,
46
- refetch: refetch || (() => Promise.resolve(void 0))
46
+ refetch: refetch || (() => Promise.resolve(void 0)),
47
+ isSuccess: false,
48
+ isError: false
49
+ };
50
+ }
51
+ function createPublicState(state) {
52
+ const now = Date.now();
53
+ const isStale = state.updatedAt == null || now - (state.updatedAt || 0) > state.staleTime || state.isInvalidated;
54
+ let returnedData = state.data;
55
+ let isPlaceholderData = false;
56
+ switch (state.status) {
57
+ case "error":
58
+ if (state.usePlaceholderOnError && state.placeholderData !== void 0) {
59
+ returnedData = state.placeholderData;
60
+ isPlaceholderData = true;
61
+ }
62
+ break;
63
+ case "fetching":
64
+ if (!state.data && state.placeholderData) {
65
+ returnedData = state.placeholderData;
66
+ isPlaceholderData = true;
67
+ }
68
+ break;
69
+ case "success":
70
+ case "idle":
71
+ returnedData = state.data ?? state.placeholderData;
72
+ isPlaceholderData = state.data ? false : Boolean(state.placeholderData);
73
+ break;
74
+ }
75
+ return {
76
+ data: returnedData,
77
+ error: state.error,
78
+ status: state.status,
79
+ updatedAt: state.updatedAt,
80
+ isStale,
81
+ isPlaceholderData,
82
+ isLoading: state.status === "fetching" && !state.updatedAt,
83
+ isFetching: state.status === "fetching",
84
+ isError: state.isError,
85
+ isSuccess: state.isSuccess,
86
+ refetch: state.refetch
47
87
  };
48
88
  }
49
89
 
@@ -87,12 +127,14 @@ var QueryManager = class {
87
127
  * Notifies all subscribers of a query state change
88
128
  */
89
129
  emit(key, state) {
90
- this.cache.set(serializeKey(key), state);
91
- const set = this.subs.get(serializeKey(key));
130
+ const stateKey = serializeKey(key);
131
+ this.cache.set(stateKey, state);
132
+ const set = this.subs.get(stateKey);
92
133
  if (!set)
93
134
  return;
135
+ const publicState = createPublicState(state);
94
136
  for (const cb of Array.from(set))
95
- cb();
137
+ cb(publicState);
96
138
  }
97
139
  registerFetcher(key, opts) {
98
140
  this.ensureState(key, opts);
@@ -123,9 +165,13 @@ var QueryManager = class {
123
165
  state.data = result2;
124
166
  state.status = "success";
125
167
  state.updatedAt = Date.now();
168
+ state.isError = false;
169
+ state.isSuccess = true;
126
170
  }).catch((error) => {
127
171
  state.error = error;
128
172
  state.status = "error";
173
+ state.isError = true;
174
+ state.isSuccess = false;
129
175
  }).finally(() => {
130
176
  state.fetchPromise = void 0;
131
177
  this.emit(key, state);
@@ -146,63 +192,19 @@ var QueryManager = class {
146
192
  state.error = void 0;
147
193
  state.status = "success";
148
194
  state.isInvalidated = false;
195
+ state.isError = false;
196
+ state.isSuccess = true;
149
197
  this.emit(key, state);
150
198
  }
151
- /**
152
- * Gets query data
153
- * Handles mount logic to potentially start fetching
154
- */
155
199
  getQueryData(key, opts) {
156
200
  const state = this.ensureState(key, opts);
157
201
  this.handleMountLogic(key, state);
158
202
  return state.data ?? state.placeholderData;
159
203
  }
160
- /**
161
- * Gets comprehensive query state including computed flags
162
- * Handles placeholder data and error states appropriately
163
- * Handles mount logic to potentially start fetching
164
- */
165
204
  getQueryState(key, opts) {
166
205
  let state = this.ensureState(key, opts);
167
- const now = Date.now();
168
- const isStale = state.updatedAt == null || now - (state.updatedAt || 0) > state.staleTime || state.isInvalidated;
169
- let returnedData = state.data;
170
- let isPlaceholderData = false;
171
- const status = state.status;
172
- switch (status) {
173
- case "error":
174
- if (state.usePlaceholderOnError && state.placeholderData !== void 0) {
175
- returnedData = state.placeholderData;
176
- isPlaceholderData = true;
177
- }
178
- break;
179
- case "fetching":
180
- if (!state.data && state.placeholderData) {
181
- returnedData = state.placeholderData;
182
- isPlaceholderData = true;
183
- }
184
- break;
185
- case "success":
186
- case "idle":
187
- returnedData = state.data ?? state.placeholderData;
188
- isPlaceholderData = state.data ? false : Boolean(state.placeholderData);
189
- break;
190
- }
191
206
  this.handleMountLogic(key, state);
192
- const currentState = {
193
- data: returnedData,
194
- error: state.error,
195
- status: state.status,
196
- updatedAt: state.updatedAt,
197
- isStale,
198
- isPlaceholderData,
199
- isLoading: state.status === "fetching" && !state.updatedAt,
200
- // true only for first fetch
201
- isFetching: state.status === "fetching",
202
- isError: state.status === "error",
203
- isSuccess: state.status === "success",
204
- refetch: state.refetch
205
- };
207
+ const currentState = createPublicState(state);
206
208
  const stateKey = serializeKey(key);
207
209
  const lastState = this.lastReturnedState?.get(stateKey);
208
210
  if (!lastState || !shallowEqual(lastState, currentState)) {
@@ -222,10 +224,6 @@ var QueryManager = class {
222
224
  this.emit(key, state);
223
225
  this.fetchQuery(key);
224
226
  }
225
- /**
226
- * Subscribes to query state changes with automatic subscription management
227
- * Handles mount logic to potentially start fetching
228
- */
229
227
  subscribeQuery(key, cb, opts) {
230
228
  const sk = serializeKey(key);
231
229
  const state = this.ensureState(key, opts);
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "qortex-core",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Framework-agnostic query cache & fetch registry (MFE friendly).",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",
7
7
  "types": "index.d.ts",
8
+ "sideEffects": false,
8
9
  "exports": {
9
10
  ".": {
10
11
  "types": "./index.d.ts",