qortex-core 0.1.8 → 0.2.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.
Files changed (4) hide show
  1. package/index.d.ts +9 -35
  2. package/index.js +14 -350
  3. package/index.mjs +2 -313
  4. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -182,41 +182,15 @@ declare class QueryManagerCore {
182
182
  }
183
183
 
184
184
  declare const _queryManager: QueryManagerCore;
185
- declare const registerFetcher: {
186
- <T = any>(key: QueryKey, opts: QueryOptions<T>): void;
187
- <F extends Fetcher>(key: QueryKey, opts: QueryOptions<InferFetcherResult<F>> & {
188
- fetcher: F;
189
- }): void;
190
- };
191
- declare const fetchQuery: {
192
- <T = any>(key: QueryKey, opts?: QueryOptions<T>): Promise<T>;
193
- <F extends Fetcher>(key: QueryKey, opts: QueryOptions<InferFetcherResult<F>> & {
194
- fetcher: F;
195
- }): Promise<InferFetcherResult<F>>;
196
- };
197
- declare const setQueryData: <T = any>(key: QueryKey, data: T) => void;
198
- declare const getQueryData: {
199
- <T = any>(key: QueryKey, opts?: QueryOptions<T>): T | undefined;
200
- <F extends Fetcher>(key: QueryKey, opts: QueryOptions<InferFetcherResult<F>> & {
201
- fetcher: F;
202
- }): InferFetcherResult<F> | undefined;
203
- };
204
- declare const getQueryState: {
205
- <T = unknown>(key: QueryKey, opts?: QueryOptions<T>): QueryState<T>;
206
- <F extends Fetcher>(key: QueryKey, opts: QueryOptions<InferFetcherResult<F>> & {
207
- fetcher: F;
208
- }): QueryState<InferFetcherResult<F>>;
209
- };
210
- declare const invalidateQuery: (key: QueryKey) => void;
211
- declare const subscribeQuery: {
212
- (key: QueryKey, cb: (state: QueryState<any>) => void): () => void;
213
- <F extends Fetcher>(key: QueryKey, cb: (state: QueryState<InferFetcherResult<F>>) => void, opts: QueryOptions<InferFetcherResult<F>> & {
214
- fetcher: F;
215
- }): () => void;
216
- <T = any>(key: QueryKey, cb: (state: QueryState<T>) => void, opts?: QueryOptions<T>): () => void;
217
- };
218
- declare const setDefaultConfig: ({ throttleTime, ...config }: DefaultConfig) => void;
219
- declare const dangerClearCache: () => void;
185
+ declare const registerFetcher: QueryManagerCore["registerFetcher"];
186
+ declare const fetchQuery: QueryManagerCore["fetchQuery"];
187
+ declare const setQueryData: QueryManagerCore["setQueryData"];
188
+ declare const getQueryData: QueryManagerCore["getQueryData"];
189
+ declare const getQueryState: QueryManagerCore["getQueryState"];
190
+ declare const invalidateQuery: QueryManagerCore["invalidateQuery"];
191
+ declare const subscribeQuery: QueryManagerCore["subscribeQuery"];
192
+ declare const setDefaultConfig: QueryManagerCore["setDefaultConfig"];
193
+ declare const dangerClearCache: QueryManagerCore["dangerClearCache"];
220
194
 
221
195
  /**
222
196
  * Normalizes query keys to a consistent string format for internal storage
package/index.js CHANGED
@@ -1,352 +1,16 @@
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);
1
+ 'use strict';
19
2
 
20
- // src/index.ts
21
- var src_exports = {};
22
- __export(src_exports, {
23
- QueryManagerCore: () => QueryManagerCore,
24
- _queryManager: () => _queryManager,
25
- dangerClearCache: () => dangerClearCache,
26
- fetchQuery: () => fetchQuery,
27
- getQueryData: () => getQueryData,
28
- getQueryState: () => getQueryState,
29
- invalidateQuery: () => invalidateQuery,
30
- registerFetcher: () => registerFetcher,
31
- serializeKey: () => serializeKey,
32
- setDefaultConfig: () => setDefaultConfig,
33
- setQueryData: () => setQueryData,
34
- subscribeQuery: () => subscribeQuery
35
- });
36
- module.exports = __toCommonJS(src_exports);
3
+ function c(t){return Array.isArray(t)?t.join(","):String(t)}function l(t,r){if(t===r)return !0;if(t==null||r==null)return t===r;if(typeof t!="object"||typeof r!="object")return !1;try{let a=t,e=r,s=Object.keys(a),i=Object.keys(e);if(s.length!==i.length)return !1;for(let n=0;n<s.length;n++){let o=s[n];if(a[o]!==e[o])return !1}return !0}catch{return !1}}function f(t,r){return {status:"idle",updatedAt:void 0,staleTime:t?.staleTime??0,isInvalidated:!1,fetcher:t?.fetcher,equalityFn:t?.equalityFn??l,placeholderData:t?.placeholderData,usePreviousDataOnError:t?.usePreviousDataOnError??!1,usePlaceholderOnError:t?.usePlaceholderOnError??!1,refetchOnSubscribe:t?.refetchOnSubscribe??"stale",enabled:t?.enabled!==!1,refetch:r||(()=>Promise.resolve(void 0)),isSuccess:!1,isError:!1}}function d(t){let r=Date.now(),a=t.updatedAt==null||r-(t.updatedAt||0)>t.staleTime||t.isInvalidated,e=t.data,s=!1;switch(t.status){case"error":t.usePlaceholderOnError&&t.placeholderData!==void 0&&(e=t.placeholderData,s=!0);break;case"fetching":!t.data&&t.placeholderData&&(e=t.placeholderData,s=!0);break;case"success":case"idle":e=t.data??t.placeholderData,s=t.data?!1:!!t.placeholderData;break}return {data:e,error:t.error,status:t.status,updatedAt:t.updatedAt,isStale:a,isPlaceholderData:s,isLoading:t.status==="fetching"&&!t.updatedAt,isFetching:t.status==="fetching",isError:t.isError,isSuccess:t.isSuccess,refetch:t.refetch}}function h(t){console.warn(`[qortex] No fetcher or data for key "${c(t)}". Register a fetcher or set initial data.`);}var y=class{constructor(){this.cache=new Map;this.subs=new Map;this.lastReturnedState=new Map;this.defaultConfig={};this.throttleTime=50;}dangerClearCache(){this.cache.clear(),this.subs.clear(),this.lastReturnedState.clear();}setDefaultConfig({throttleTime:r,...a}){this.defaultConfig={...this.defaultConfig,...a},r!==void 0&&(this.throttleTime=r);}ensureState(r,a={}){let e=c(r),s=this.cache.get(e),i={...this.defaultConfig,...a};if(s)Object.assign(s,i),s.enabled=i.enabled!==!1,this.cache.set(e,s);else {let n=f(i,()=>this.fetchQuery(r));this.cache.set(e,n);}return this.cache.get(e)}emit(r,a){let e=c(r);this.cache.set(e,a);let s=this.subs.get(e);if(!s)return;let i=d(a);for(let n of Array.from(s))n(i);}registerFetcher(r,a){let e=this.ensureState(r,a);this.handleMountLogic(r,e);}fetchQuery(r,a){let e=this.ensureState(r,a);if(e.fetchPromise)return e.fetchPromise;let s=e.fetcher;if(!s)return e.updatedAt===void 0&&h(r),Promise.resolve(e.data);e.status="fetching",e.lastFetchTime=Date.now(),this.emit(r,e);let i=s(),n=Promise.resolve(i);return e.fetchPromise=n,n.then(o=>{e.data=e.equalityFn(e.data,o)?e.data:o,e.status="success",e.updatedAt=Date.now(),e.isError=!1,e.isSuccess=!0;}).catch(o=>{e.error=o,e.status="error",e.isError=!0,e.isSuccess=!1;}).finally(()=>{e.fetchPromise=void 0,this.emit(r,e);}),n}setQueryData(r,a){let e=this.ensureState(r),s=e.data;e.equalityFn(s,a)||(e.data=a,e.updatedAt=Date.now(),e.error=void 0,e.status="success",e.isInvalidated=!1,e.isError=!1,e.isSuccess=!0,this.emit(r,e));}getQueryData(r,a){let e=this.ensureState(r,a);return this.handleMountLogic(r,e),e.data??e.placeholderData}getQueryState(r,a){let e=this.ensureState(r,a);this.handleMountLogic(r,e);let s=d(e),i=c(r),n=this.lastReturnedState?.get(i);return !n||!l(n,s)?(this.lastReturnedState||(this.lastReturnedState=new Map),this.lastReturnedState.set(i,s),s):n}invalidateQuery(r){let a=this.ensureState(r);a.isInvalidated=!0,this.emit(r,a),this.fetchQuery(r);}subscribeQuery(r,a,e){let s=c(r),i=this.ensureState(r,e);return this.subs.has(s)||this.subs.set(s,new Set),this.subs.get(s).add(a),this.handleMountLogic(r,i),()=>{this.subs.get(s).delete(a);}}handleMountLogic(r,a){let e=a.lastFetchTime&&Date.now()-a.lastFetchTime<this.throttleTime;if(a?.status==="fetching"||!a?.enabled||e||!a?.fetcher)return;let s=Date.now(),i=a?.updatedAt==null||s-(a.updatedAt||0)>a.staleTime||a.isInvalidated,n=!1;a.refetchOnSubscribe==="always"&&(n=!0),a.refetchOnSubscribe==="stale"&&(n=i),n&&this.fetchQuery(r);}},Q=y;var u=new Q,m=u.registerFetcher.bind(u),D=u.fetchQuery.bind(u),v=u.setQueryData.bind(u),O=u.getQueryData.bind(u),K=u.getQueryState.bind(u),C=u.invalidateQuery.bind(u),x=u.subscribeQuery.bind(u),w=u.setDefaultConfig.bind(u),I=u.dangerClearCache.bind(u);
37
4
 
38
- // src/constants.ts
39
- var DEFAULT_STALE_TIME = 0;
40
- var THROTTLE_TIME = 50;
41
-
42
- // src/utils.ts
43
- function serializeKey(key) {
44
- return Array.isArray(key) ? key.join(",") : String(key);
45
- }
46
- function shallowEqual(a, b) {
47
- if (a === b)
48
- return true;
49
- if (a == null || b == null)
50
- return a === b;
51
- if (typeof a !== "object" || typeof b !== "object")
52
- return false;
53
- try {
54
- const aAny = a;
55
- const bAny = b;
56
- const aKeys = Object.keys(aAny);
57
- const bKeys = Object.keys(bAny);
58
- if (aKeys.length !== bKeys.length)
59
- return false;
60
- for (let i = 0; i < aKeys.length; i++) {
61
- const k = aKeys[i];
62
- if (aAny[k] !== bAny[k])
63
- return false;
64
- }
65
- return true;
66
- } catch {
67
- return false;
68
- }
69
- }
70
- function createDefaultState(opts, refetch) {
71
- return {
72
- status: "idle",
73
- updatedAt: void 0,
74
- staleTime: opts?.staleTime ?? DEFAULT_STALE_TIME,
75
- isInvalidated: false,
76
- fetcher: opts?.fetcher,
77
- equalityFn: opts?.equalityFn ?? shallowEqual,
78
- placeholderData: opts?.placeholderData,
79
- usePreviousDataOnError: opts?.usePreviousDataOnError ?? false,
80
- usePlaceholderOnError: opts?.usePlaceholderOnError ?? false,
81
- refetchOnSubscribe: opts?.refetchOnSubscribe ?? "stale",
82
- enabled: opts?.enabled === false ? false : true,
83
- refetch: refetch || (() => Promise.resolve(void 0)),
84
- isSuccess: false,
85
- isError: false
86
- };
87
- }
88
- function createPublicState(state) {
89
- const now = Date.now();
90
- const isStale = state.updatedAt == null || now - (state.updatedAt || 0) > state.staleTime || state.isInvalidated;
91
- let returnedData = state.data;
92
- let isPlaceholderData = false;
93
- switch (state.status) {
94
- case "error":
95
- if (state.usePlaceholderOnError && state.placeholderData !== void 0) {
96
- returnedData = state.placeholderData;
97
- isPlaceholderData = true;
98
- }
99
- break;
100
- case "fetching":
101
- if (!state.data && state.placeholderData) {
102
- returnedData = state.placeholderData;
103
- isPlaceholderData = true;
104
- }
105
- break;
106
- case "success":
107
- case "idle":
108
- returnedData = state.data ?? state.placeholderData;
109
- isPlaceholderData = state.data ? false : Boolean(state.placeholderData);
110
- break;
111
- }
112
- return {
113
- data: returnedData,
114
- error: state.error,
115
- status: state.status,
116
- updatedAt: state.updatedAt,
117
- isStale,
118
- isPlaceholderData,
119
- isLoading: state.status === "fetching" && !state.updatedAt,
120
- isFetching: state.status === "fetching",
121
- isError: state.isError,
122
- isSuccess: state.isSuccess,
123
- refetch: state.refetch
124
- };
125
- }
126
- function warnNoFetcherOrData(key) {
127
- console.warn(
128
- `[qortex] No fetcher or data for key "${serializeKey(key)}". Register a fetcher or set initial data.`
129
- );
130
- }
131
-
132
- // src/queryManagerCore.ts
133
- var QueryManagerCore = class {
134
- constructor() {
135
- this.cache = /* @__PURE__ */ new Map();
136
- this.subs = /* @__PURE__ */ new Map();
137
- this.lastReturnedState = /* @__PURE__ */ new Map();
138
- this.defaultConfig = {};
139
- this.throttleTime = THROTTLE_TIME;
140
- }
141
- /**
142
- * ⚠️ DANGER: Clear all cached data and subscriptions
143
- *
144
- * This method completely wipes all internal state including:
145
- * - All cached query data
146
- * - All active subscriptions
147
- * - All state references
148
- *
149
- * @warning This should ONLY be used in testing environments or when you need to completely reset the query manager state. Using this in production will cause all active queries to lose their data and subscriptions to break.
150
- *
151
- * @example
152
- * ```typescript
153
- * // ✅ Safe usage in tests
154
- * beforeEach(() => {
155
- * queryManager.dangerClearCache();
156
- * });
157
- *
158
- * // ❌ Dangerous usage in production
159
- * // queryManager.dangerClearCache(); // Don't do this!
160
- * ```
161
- */
162
- dangerClearCache() {
163
- this.cache.clear();
164
- this.subs.clear();
165
- this.lastReturnedState.clear();
166
- }
167
- /**
168
- * Set default configuration for all queries
169
- */
170
- setDefaultConfig({ throttleTime, ...config }) {
171
- this.defaultConfig = { ...this.defaultConfig, ...config };
172
- if (throttleTime !== void 0) {
173
- this.throttleTime = throttleTime;
174
- }
175
- }
176
- /**
177
- * Ensures a query state exists in cache, creating it if necessary
178
- * User-friendly with any fallback for better developer experience
179
- */
180
- ensureState(key, opts = {}) {
181
- const sk = serializeKey(key);
182
- const state = this.cache.get(sk);
183
- const mergedOpts = { ...this.defaultConfig, ...opts };
184
- if (state) {
185
- Object.assign(state, mergedOpts);
186
- state.enabled = mergedOpts.enabled === false ? false : true;
187
- this.cache.set(sk, state);
188
- } else {
189
- const newState = createDefaultState(mergedOpts, () => this.fetchQuery(key));
190
- this.cache.set(sk, newState);
191
- }
192
- return this.cache.get(sk);
193
- }
194
- /**
195
- * Notifies all subscribers of a query state change
196
- */
197
- emit(key, state) {
198
- const stateKey = serializeKey(key);
199
- this.cache.set(stateKey, state);
200
- const set = this.subs.get(stateKey);
201
- if (!set)
202
- return;
203
- const publicState = createPublicState(state);
204
- for (const cb of Array.from(set))
205
- cb(publicState);
206
- }
207
- registerFetcher(key, opts) {
208
- const state = this.ensureState(key, opts);
209
- this.handleMountLogic(key, state);
210
- }
211
- fetchQuery(key, opts) {
212
- const state = this.ensureState(key, opts);
213
- if (state.fetchPromise)
214
- return state.fetchPromise;
215
- const fetcher = state.fetcher;
216
- if (!fetcher) {
217
- if (state.updatedAt === void 0) {
218
- warnNoFetcherOrData(key);
219
- }
220
- return Promise.resolve(state.data);
221
- }
222
- ;
223
- state.status = "fetching";
224
- state.lastFetchTime = Date.now();
225
- this.emit(key, state);
226
- const result = fetcher();
227
- const promise = Promise.resolve(result);
228
- state.fetchPromise = promise;
229
- promise.then((result2) => {
230
- state.data = state.equalityFn(state.data, result2) ? state.data : result2;
231
- state.status = "success";
232
- state.updatedAt = Date.now();
233
- state.isError = false;
234
- state.isSuccess = true;
235
- }).catch((error) => {
236
- state.error = error;
237
- state.status = "error";
238
- state.isError = true;
239
- state.isSuccess = false;
240
- }).finally(() => {
241
- state.fetchPromise = void 0;
242
- this.emit(key, state);
243
- });
244
- return promise;
245
- }
246
- /**
247
- * Manually sets query data without triggering a fetch
248
- * Marks query as successful
249
- */
250
- setQueryData(key, data) {
251
- const state = this.ensureState(key);
252
- const old = state.data;
253
- if (state.equalityFn(old, data))
254
- return;
255
- state.data = data;
256
- state.updatedAt = Date.now();
257
- state.error = void 0;
258
- state.status = "success";
259
- state.isInvalidated = false;
260
- state.isError = false;
261
- state.isSuccess = true;
262
- this.emit(key, state);
263
- }
264
- getQueryData(key, opts) {
265
- const state = this.ensureState(key, opts);
266
- this.handleMountLogic(key, state);
267
- return state.data ?? state.placeholderData;
268
- }
269
- getQueryState(key, opts) {
270
- let state = this.ensureState(key, opts);
271
- this.handleMountLogic(key, state);
272
- const currentState = createPublicState(state);
273
- const stateKey = serializeKey(key);
274
- const lastState = this.lastReturnedState?.get(stateKey);
275
- if (!lastState || !shallowEqual(lastState, currentState)) {
276
- if (!this.lastReturnedState)
277
- this.lastReturnedState = /* @__PURE__ */ new Map();
278
- this.lastReturnedState.set(stateKey, currentState);
279
- return currentState;
280
- }
281
- return lastState;
282
- }
283
- /**
284
- * Marks a query as invalidated, triggering refetch
285
- */
286
- invalidateQuery(key) {
287
- const state = this.ensureState(key);
288
- state.isInvalidated = true;
289
- this.emit(key, state);
290
- this.fetchQuery(key);
291
- }
292
- subscribeQuery(key, cb, opts) {
293
- const sk = serializeKey(key);
294
- const state = this.ensureState(key, opts);
295
- if (!this.subs.has(sk))
296
- this.subs.set(sk, /* @__PURE__ */ new Set());
297
- this.subs.get(sk).add(cb);
298
- this.handleMountLogic(key, state);
299
- return () => {
300
- this.subs.get(sk).delete(cb);
301
- };
302
- }
303
- /**
304
- * Core mount logic that determines when to fetch
305
- * Implements robust throttling and race condition prevention
306
- */
307
- handleMountLogic(key, state) {
308
- const isThrottled = state.lastFetchTime && Date.now() - state.lastFetchTime < this.throttleTime;
309
- if (state?.status === "fetching" || !state?.enabled || isThrottled || !state?.fetcher)
310
- return;
311
- const now = Date.now();
312
- const isStale = state?.updatedAt == null || now - (state.updatedAt || 0) > state.staleTime || state.isInvalidated;
313
- let shouldRefetch = false;
314
- if (state.refetchOnSubscribe === "always") {
315
- shouldRefetch = true;
316
- }
317
- if (state.refetchOnSubscribe === "stale") {
318
- shouldRefetch = isStale;
319
- }
320
- if (shouldRefetch) {
321
- this.fetchQuery(key);
322
- }
323
- }
324
- };
325
- var queryManagerCore_default = QueryManagerCore;
326
-
327
- // src/queryManager.ts
328
- var _queryManager = new queryManagerCore_default();
329
- var registerFetcher = _queryManager.registerFetcher.bind(_queryManager);
330
- var fetchQuery = _queryManager.fetchQuery.bind(_queryManager);
331
- var setQueryData = _queryManager.setQueryData.bind(_queryManager);
332
- var getQueryData = _queryManager.getQueryData.bind(_queryManager);
333
- var getQueryState = _queryManager.getQueryState.bind(_queryManager);
334
- var invalidateQuery = _queryManager.invalidateQuery.bind(_queryManager);
335
- var subscribeQuery = _queryManager.subscribeQuery.bind(_queryManager);
336
- var setDefaultConfig = _queryManager.setDefaultConfig.bind(_queryManager);
337
- var dangerClearCache = _queryManager.dangerClearCache.bind(_queryManager);
338
- // Annotate the CommonJS export names for ESM import in node:
339
- 0 && (module.exports = {
340
- QueryManagerCore,
341
- _queryManager,
342
- dangerClearCache,
343
- fetchQuery,
344
- getQueryData,
345
- getQueryState,
346
- invalidateQuery,
347
- registerFetcher,
348
- serializeKey,
349
- setDefaultConfig,
350
- setQueryData,
351
- subscribeQuery
352
- });
5
+ exports.QueryManagerCore = y;
6
+ exports._queryManager = u;
7
+ exports.dangerClearCache = I;
8
+ exports.fetchQuery = D;
9
+ exports.getQueryData = O;
10
+ exports.getQueryState = K;
11
+ exports.invalidateQuery = C;
12
+ exports.registerFetcher = m;
13
+ exports.serializeKey = c;
14
+ exports.setDefaultConfig = w;
15
+ exports.setQueryData = v;
16
+ exports.subscribeQuery = x;
package/index.mjs CHANGED
@@ -1,314 +1,3 @@
1
- // src/constants.ts
2
- var DEFAULT_STALE_TIME = 0;
3
- var THROTTLE_TIME = 50;
1
+ function c(t){return Array.isArray(t)?t.join(","):String(t)}function l(t,r){if(t===r)return !0;if(t==null||r==null)return t===r;if(typeof t!="object"||typeof r!="object")return !1;try{let a=t,e=r,s=Object.keys(a),i=Object.keys(e);if(s.length!==i.length)return !1;for(let n=0;n<s.length;n++){let o=s[n];if(a[o]!==e[o])return !1}return !0}catch{return !1}}function f(t,r){return {status:"idle",updatedAt:void 0,staleTime:t?.staleTime??0,isInvalidated:!1,fetcher:t?.fetcher,equalityFn:t?.equalityFn??l,placeholderData:t?.placeholderData,usePreviousDataOnError:t?.usePreviousDataOnError??!1,usePlaceholderOnError:t?.usePlaceholderOnError??!1,refetchOnSubscribe:t?.refetchOnSubscribe??"stale",enabled:t?.enabled!==!1,refetch:r||(()=>Promise.resolve(void 0)),isSuccess:!1,isError:!1}}function d(t){let r=Date.now(),a=t.updatedAt==null||r-(t.updatedAt||0)>t.staleTime||t.isInvalidated,e=t.data,s=!1;switch(t.status){case"error":t.usePlaceholderOnError&&t.placeholderData!==void 0&&(e=t.placeholderData,s=!0);break;case"fetching":!t.data&&t.placeholderData&&(e=t.placeholderData,s=!0);break;case"success":case"idle":e=t.data??t.placeholderData,s=t.data?!1:!!t.placeholderData;break}return {data:e,error:t.error,status:t.status,updatedAt:t.updatedAt,isStale:a,isPlaceholderData:s,isLoading:t.status==="fetching"&&!t.updatedAt,isFetching:t.status==="fetching",isError:t.isError,isSuccess:t.isSuccess,refetch:t.refetch}}function h(t){console.warn(`[qortex] No fetcher or data for key "${c(t)}". Register a fetcher or set initial data.`);}var y=class{constructor(){this.cache=new Map;this.subs=new Map;this.lastReturnedState=new Map;this.defaultConfig={};this.throttleTime=50;}dangerClearCache(){this.cache.clear(),this.subs.clear(),this.lastReturnedState.clear();}setDefaultConfig({throttleTime:r,...a}){this.defaultConfig={...this.defaultConfig,...a},r!==void 0&&(this.throttleTime=r);}ensureState(r,a={}){let e=c(r),s=this.cache.get(e),i={...this.defaultConfig,...a};if(s)Object.assign(s,i),s.enabled=i.enabled!==!1,this.cache.set(e,s);else {let n=f(i,()=>this.fetchQuery(r));this.cache.set(e,n);}return this.cache.get(e)}emit(r,a){let e=c(r);this.cache.set(e,a);let s=this.subs.get(e);if(!s)return;let i=d(a);for(let n of Array.from(s))n(i);}registerFetcher(r,a){let e=this.ensureState(r,a);this.handleMountLogic(r,e);}fetchQuery(r,a){let e=this.ensureState(r,a);if(e.fetchPromise)return e.fetchPromise;let s=e.fetcher;if(!s)return e.updatedAt===void 0&&h(r),Promise.resolve(e.data);e.status="fetching",e.lastFetchTime=Date.now(),this.emit(r,e);let i=s(),n=Promise.resolve(i);return e.fetchPromise=n,n.then(o=>{e.data=e.equalityFn(e.data,o)?e.data:o,e.status="success",e.updatedAt=Date.now(),e.isError=!1,e.isSuccess=!0;}).catch(o=>{e.error=o,e.status="error",e.isError=!0,e.isSuccess=!1;}).finally(()=>{e.fetchPromise=void 0,this.emit(r,e);}),n}setQueryData(r,a){let e=this.ensureState(r),s=e.data;e.equalityFn(s,a)||(e.data=a,e.updatedAt=Date.now(),e.error=void 0,e.status="success",e.isInvalidated=!1,e.isError=!1,e.isSuccess=!0,this.emit(r,e));}getQueryData(r,a){let e=this.ensureState(r,a);return this.handleMountLogic(r,e),e.data??e.placeholderData}getQueryState(r,a){let e=this.ensureState(r,a);this.handleMountLogic(r,e);let s=d(e),i=c(r),n=this.lastReturnedState?.get(i);return !n||!l(n,s)?(this.lastReturnedState||(this.lastReturnedState=new Map),this.lastReturnedState.set(i,s),s):n}invalidateQuery(r){let a=this.ensureState(r);a.isInvalidated=!0,this.emit(r,a),this.fetchQuery(r);}subscribeQuery(r,a,e){let s=c(r),i=this.ensureState(r,e);return this.subs.has(s)||this.subs.set(s,new Set),this.subs.get(s).add(a),this.handleMountLogic(r,i),()=>{this.subs.get(s).delete(a);}}handleMountLogic(r,a){let e=a.lastFetchTime&&Date.now()-a.lastFetchTime<this.throttleTime;if(a?.status==="fetching"||!a?.enabled||e||!a?.fetcher)return;let s=Date.now(),i=a?.updatedAt==null||s-(a.updatedAt||0)>a.staleTime||a.isInvalidated,n=!1;a.refetchOnSubscribe==="always"&&(n=!0),a.refetchOnSubscribe==="stale"&&(n=i),n&&this.fetchQuery(r);}},Q=y;var u=new Q,m=u.registerFetcher.bind(u),D=u.fetchQuery.bind(u),v=u.setQueryData.bind(u),O=u.getQueryData.bind(u),K=u.getQueryState.bind(u),C=u.invalidateQuery.bind(u),x=u.subscribeQuery.bind(u),w=u.setDefaultConfig.bind(u),I=u.dangerClearCache.bind(u);
4
2
 
5
- // src/utils.ts
6
- function serializeKey(key) {
7
- return Array.isArray(key) ? key.join(",") : String(key);
8
- }
9
- function shallowEqual(a, b) {
10
- if (a === b)
11
- return true;
12
- if (a == null || b == null)
13
- return a === b;
14
- if (typeof a !== "object" || typeof b !== "object")
15
- return false;
16
- try {
17
- const aAny = a;
18
- const bAny = b;
19
- const aKeys = Object.keys(aAny);
20
- const bKeys = Object.keys(bAny);
21
- if (aKeys.length !== bKeys.length)
22
- return false;
23
- for (let i = 0; i < aKeys.length; i++) {
24
- const k = aKeys[i];
25
- if (aAny[k] !== bAny[k])
26
- return false;
27
- }
28
- return true;
29
- } catch {
30
- return false;
31
- }
32
- }
33
- function createDefaultState(opts, refetch) {
34
- return {
35
- status: "idle",
36
- updatedAt: void 0,
37
- staleTime: opts?.staleTime ?? DEFAULT_STALE_TIME,
38
- isInvalidated: false,
39
- fetcher: opts?.fetcher,
40
- equalityFn: opts?.equalityFn ?? shallowEqual,
41
- placeholderData: opts?.placeholderData,
42
- usePreviousDataOnError: opts?.usePreviousDataOnError ?? false,
43
- usePlaceholderOnError: opts?.usePlaceholderOnError ?? false,
44
- refetchOnSubscribe: opts?.refetchOnSubscribe ?? "stale",
45
- enabled: opts?.enabled === false ? false : true,
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
87
- };
88
- }
89
- function warnNoFetcherOrData(key) {
90
- console.warn(
91
- `[qortex] No fetcher or data for key "${serializeKey(key)}". Register a fetcher or set initial data.`
92
- );
93
- }
94
-
95
- // src/queryManagerCore.ts
96
- var QueryManagerCore = class {
97
- constructor() {
98
- this.cache = /* @__PURE__ */ new Map();
99
- this.subs = /* @__PURE__ */ new Map();
100
- this.lastReturnedState = /* @__PURE__ */ new Map();
101
- this.defaultConfig = {};
102
- this.throttleTime = THROTTLE_TIME;
103
- }
104
- /**
105
- * ⚠️ DANGER: Clear all cached data and subscriptions
106
- *
107
- * This method completely wipes all internal state including:
108
- * - All cached query data
109
- * - All active subscriptions
110
- * - All state references
111
- *
112
- * @warning This should ONLY be used in testing environments or when you need to completely reset the query manager state. Using this in production will cause all active queries to lose their data and subscriptions to break.
113
- *
114
- * @example
115
- * ```typescript
116
- * // ✅ Safe usage in tests
117
- * beforeEach(() => {
118
- * queryManager.dangerClearCache();
119
- * });
120
- *
121
- * // ❌ Dangerous usage in production
122
- * // queryManager.dangerClearCache(); // Don't do this!
123
- * ```
124
- */
125
- dangerClearCache() {
126
- this.cache.clear();
127
- this.subs.clear();
128
- this.lastReturnedState.clear();
129
- }
130
- /**
131
- * Set default configuration for all queries
132
- */
133
- setDefaultConfig({ throttleTime, ...config }) {
134
- this.defaultConfig = { ...this.defaultConfig, ...config };
135
- if (throttleTime !== void 0) {
136
- this.throttleTime = throttleTime;
137
- }
138
- }
139
- /**
140
- * Ensures a query state exists in cache, creating it if necessary
141
- * User-friendly with any fallback for better developer experience
142
- */
143
- ensureState(key, opts = {}) {
144
- const sk = serializeKey(key);
145
- const state = this.cache.get(sk);
146
- const mergedOpts = { ...this.defaultConfig, ...opts };
147
- if (state) {
148
- Object.assign(state, mergedOpts);
149
- state.enabled = mergedOpts.enabled === false ? false : true;
150
- this.cache.set(sk, state);
151
- } else {
152
- const newState = createDefaultState(mergedOpts, () => this.fetchQuery(key));
153
- this.cache.set(sk, newState);
154
- }
155
- return this.cache.get(sk);
156
- }
157
- /**
158
- * Notifies all subscribers of a query state change
159
- */
160
- emit(key, state) {
161
- const stateKey = serializeKey(key);
162
- this.cache.set(stateKey, state);
163
- const set = this.subs.get(stateKey);
164
- if (!set)
165
- return;
166
- const publicState = createPublicState(state);
167
- for (const cb of Array.from(set))
168
- cb(publicState);
169
- }
170
- registerFetcher(key, opts) {
171
- const state = this.ensureState(key, opts);
172
- this.handleMountLogic(key, state);
173
- }
174
- fetchQuery(key, opts) {
175
- const state = this.ensureState(key, opts);
176
- if (state.fetchPromise)
177
- return state.fetchPromise;
178
- const fetcher = state.fetcher;
179
- if (!fetcher) {
180
- if (state.updatedAt === void 0) {
181
- warnNoFetcherOrData(key);
182
- }
183
- return Promise.resolve(state.data);
184
- }
185
- ;
186
- state.status = "fetching";
187
- state.lastFetchTime = Date.now();
188
- this.emit(key, state);
189
- const result = fetcher();
190
- const promise = Promise.resolve(result);
191
- state.fetchPromise = promise;
192
- promise.then((result2) => {
193
- state.data = state.equalityFn(state.data, result2) ? state.data : result2;
194
- state.status = "success";
195
- state.updatedAt = Date.now();
196
- state.isError = false;
197
- state.isSuccess = true;
198
- }).catch((error) => {
199
- state.error = error;
200
- state.status = "error";
201
- state.isError = true;
202
- state.isSuccess = false;
203
- }).finally(() => {
204
- state.fetchPromise = void 0;
205
- this.emit(key, state);
206
- });
207
- return promise;
208
- }
209
- /**
210
- * Manually sets query data without triggering a fetch
211
- * Marks query as successful
212
- */
213
- setQueryData(key, data) {
214
- const state = this.ensureState(key);
215
- const old = state.data;
216
- if (state.equalityFn(old, data))
217
- return;
218
- state.data = data;
219
- state.updatedAt = Date.now();
220
- state.error = void 0;
221
- state.status = "success";
222
- state.isInvalidated = false;
223
- state.isError = false;
224
- state.isSuccess = true;
225
- this.emit(key, state);
226
- }
227
- getQueryData(key, opts) {
228
- const state = this.ensureState(key, opts);
229
- this.handleMountLogic(key, state);
230
- return state.data ?? state.placeholderData;
231
- }
232
- getQueryState(key, opts) {
233
- let state = this.ensureState(key, opts);
234
- this.handleMountLogic(key, state);
235
- const currentState = createPublicState(state);
236
- const stateKey = serializeKey(key);
237
- const lastState = this.lastReturnedState?.get(stateKey);
238
- if (!lastState || !shallowEqual(lastState, currentState)) {
239
- if (!this.lastReturnedState)
240
- this.lastReturnedState = /* @__PURE__ */ new Map();
241
- this.lastReturnedState.set(stateKey, currentState);
242
- return currentState;
243
- }
244
- return lastState;
245
- }
246
- /**
247
- * Marks a query as invalidated, triggering refetch
248
- */
249
- invalidateQuery(key) {
250
- const state = this.ensureState(key);
251
- state.isInvalidated = true;
252
- this.emit(key, state);
253
- this.fetchQuery(key);
254
- }
255
- subscribeQuery(key, cb, opts) {
256
- const sk = serializeKey(key);
257
- const state = this.ensureState(key, opts);
258
- if (!this.subs.has(sk))
259
- this.subs.set(sk, /* @__PURE__ */ new Set());
260
- this.subs.get(sk).add(cb);
261
- this.handleMountLogic(key, state);
262
- return () => {
263
- this.subs.get(sk).delete(cb);
264
- };
265
- }
266
- /**
267
- * Core mount logic that determines when to fetch
268
- * Implements robust throttling and race condition prevention
269
- */
270
- handleMountLogic(key, state) {
271
- const isThrottled = state.lastFetchTime && Date.now() - state.lastFetchTime < this.throttleTime;
272
- if (state?.status === "fetching" || !state?.enabled || isThrottled || !state?.fetcher)
273
- return;
274
- const now = Date.now();
275
- const isStale = state?.updatedAt == null || now - (state.updatedAt || 0) > state.staleTime || state.isInvalidated;
276
- let shouldRefetch = false;
277
- if (state.refetchOnSubscribe === "always") {
278
- shouldRefetch = true;
279
- }
280
- if (state.refetchOnSubscribe === "stale") {
281
- shouldRefetch = isStale;
282
- }
283
- if (shouldRefetch) {
284
- this.fetchQuery(key);
285
- }
286
- }
287
- };
288
- var queryManagerCore_default = QueryManagerCore;
289
-
290
- // src/queryManager.ts
291
- var _queryManager = new queryManagerCore_default();
292
- var registerFetcher = _queryManager.registerFetcher.bind(_queryManager);
293
- var fetchQuery = _queryManager.fetchQuery.bind(_queryManager);
294
- var setQueryData = _queryManager.setQueryData.bind(_queryManager);
295
- var getQueryData = _queryManager.getQueryData.bind(_queryManager);
296
- var getQueryState = _queryManager.getQueryState.bind(_queryManager);
297
- var invalidateQuery = _queryManager.invalidateQuery.bind(_queryManager);
298
- var subscribeQuery = _queryManager.subscribeQuery.bind(_queryManager);
299
- var setDefaultConfig = _queryManager.setDefaultConfig.bind(_queryManager);
300
- var dangerClearCache = _queryManager.dangerClearCache.bind(_queryManager);
301
- export {
302
- QueryManagerCore,
303
- _queryManager,
304
- dangerClearCache,
305
- fetchQuery,
306
- getQueryData,
307
- getQueryState,
308
- invalidateQuery,
309
- registerFetcher,
310
- serializeKey,
311
- setDefaultConfig,
312
- setQueryData,
313
- subscribeQuery
314
- };
3
+ export { y as QueryManagerCore, u as _queryManager, I as dangerClearCache, D as fetchQuery, O as getQueryData, K as getQueryState, C as invalidateQuery, m as registerFetcher, c as serializeKey, w as setDefaultConfig, v as setQueryData, x as subscribeQuery };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qortex-core",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "description": "Framework-agnostic query cache & fetch registry (MFE friendly).",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",