@ametie/vue-muza-use 0.2.0 → 0.3.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/dist/index.cjs CHANGED
@@ -198,13 +198,27 @@ function useApi(url, options = {}) {
198
198
  authMode = "default",
199
199
  useGlobalAbort = globalOptions?.useGlobalAbort ?? true,
200
200
  initialLoading = false,
201
+ poll = 0,
201
202
  ...axiosConfig
202
203
  } = options;
203
204
  const startLoading = initialLoading ?? immediate;
204
205
  const state = useApiState(initialData, { initialLoading: startLoading });
205
206
  const abortController2 = (0, import_vue4.ref)(null);
206
207
  const globalAbort = useGlobalAbort ? useAbortController() : null;
208
+ let pollTimer = null;
209
+ const getPollConfig = () => {
210
+ const val = (0, import_vue4.toValue)(poll);
211
+ if (typeof val === "number") return { interval: val, whenHidden: false };
212
+ if (val && typeof val === "object") {
213
+ return {
214
+ interval: (0, import_vue4.toValue)(val.interval),
215
+ whenHidden: (0, import_vue4.toValue)(val.whenHidden) ?? false
216
+ };
217
+ }
218
+ return { interval: 0, whenHidden: false };
219
+ };
207
220
  const executeRequest = async (config) => {
221
+ if (pollTimer) clearTimeout(pollTimer);
208
222
  const requestUrl = typeof url === "string" ? url : url.value;
209
223
  if (abortController2.value) abortController2.value.abort("Cancelled by new request");
210
224
  const controller = new AbortController();
@@ -260,11 +274,25 @@ function useApi(url, options = {}) {
260
274
  if (!wasCancelled) {
261
275
  state.setLoading(false);
262
276
  onFinish?.();
277
+ const { interval, whenHidden } = getPollConfig();
278
+ if (interval > 0) {
279
+ const shouldPoll = whenHidden || typeof document !== "undefined" && !document.hidden;
280
+ if (shouldPoll) {
281
+ pollTimer = setTimeout(() => {
282
+ pollTimer = null;
283
+ const { whenHidden: currentWhenHidden } = getPollConfig();
284
+ if (currentWhenHidden || (typeof document === "undefined" || !document.hidden)) {
285
+ execute();
286
+ }
287
+ }, interval);
288
+ }
289
+ }
263
290
  }
264
291
  }
265
292
  };
266
293
  const execute = debounce > 0 ? debounceFn(executeRequest, debounce) : executeRequest;
267
294
  const abort = (msg) => {
295
+ if (pollTimer) clearTimeout(pollTimer);
268
296
  abortController2.value?.abort(msg);
269
297
  abortController2.value = null;
270
298
  };
@@ -282,6 +310,38 @@ function useApi(url, options = {}) {
282
310
  (0, import_vue4.onScopeDispose)(() => abort("Scope disposed"));
283
311
  }
284
312
  if (immediate) execute();
313
+ if (typeof document !== "undefined") {
314
+ const handleVisibility = () => {
315
+ if (document.hidden) return;
316
+ const { interval } = getPollConfig();
317
+ if (interval > 0 && !pollTimer && !state.loading.value) {
318
+ execute();
319
+ }
320
+ };
321
+ document.addEventListener("visibilitychange", handleVisibility);
322
+ if ((0, import_vue4.getCurrentScope)()) {
323
+ (0, import_vue4.onScopeDispose)(() => document.removeEventListener("visibilitychange", handleVisibility));
324
+ }
325
+ }
326
+ if (poll) {
327
+ (0, import_vue4.watch)(() => (0, import_vue4.toValue)(poll), () => {
328
+ const { interval } = getPollConfig();
329
+ if (interval > 0) {
330
+ if (pollTimer) {
331
+ clearTimeout(pollTimer);
332
+ pollTimer = null;
333
+ }
334
+ if (!state.loading.value) {
335
+ execute();
336
+ }
337
+ } else {
338
+ if (pollTimer) {
339
+ clearTimeout(pollTimer);
340
+ pollTimer = null;
341
+ }
342
+ }
343
+ }, { deep: true });
344
+ }
285
345
  return { ...state, execute, abort, reset };
286
346
  }
287
347
  function useApiGet(url, options) {
package/dist/index.d.cts CHANGED
@@ -34,6 +34,16 @@ interface UseApiOptions<T = unknown, D = unknown> extends ApiRequestConfig<D> {
34
34
  useGlobalAbort?: boolean;
35
35
  initialLoading?: boolean;
36
36
  watch?: WatchSource | WatchSource[];
37
+ /**
38
+ * Polling configuration.
39
+ * - Pass a **number** (ms) for simple polling.
40
+ * - Pass an **object** `{ interval: number, whenHidden?: boolean }` for advanced control.
41
+ * Properties inside the object can also be Refs.
42
+ */
43
+ poll?: MaybeRefOrGetter<number | {
44
+ interval: MaybeRefOrGetter<number>;
45
+ whenHidden?: MaybeRefOrGetter<boolean>;
46
+ }>;
37
47
  }
38
48
  interface UseApiReturn<T = unknown, D = unknown> {
39
49
  data: Ref<T | null>;
package/dist/index.d.ts CHANGED
@@ -34,6 +34,16 @@ interface UseApiOptions<T = unknown, D = unknown> extends ApiRequestConfig<D> {
34
34
  useGlobalAbort?: boolean;
35
35
  initialLoading?: boolean;
36
36
  watch?: WatchSource | WatchSource[];
37
+ /**
38
+ * Polling configuration.
39
+ * - Pass a **number** (ms) for simple polling.
40
+ * - Pass an **object** `{ interval: number, whenHidden?: boolean }` for advanced control.
41
+ * Properties inside the object can also be Refs.
42
+ */
43
+ poll?: MaybeRefOrGetter<number | {
44
+ interval: MaybeRefOrGetter<number>;
45
+ whenHidden?: MaybeRefOrGetter<boolean>;
46
+ }>;
37
47
  }
38
48
  interface UseApiReturn<T = unknown, D = unknown> {
39
49
  data: Ref<T | null>;
package/dist/index.mjs CHANGED
@@ -148,13 +148,27 @@ function useApi(url, options = {}) {
148
148
  authMode = "default",
149
149
  useGlobalAbort = globalOptions?.useGlobalAbort ?? true,
150
150
  initialLoading = false,
151
+ poll = 0,
151
152
  ...axiosConfig
152
153
  } = options;
153
154
  const startLoading = initialLoading ?? immediate;
154
155
  const state = useApiState(initialData, { initialLoading: startLoading });
155
156
  const abortController2 = ref3(null);
156
157
  const globalAbort = useGlobalAbort ? useAbortController() : null;
158
+ let pollTimer = null;
159
+ const getPollConfig = () => {
160
+ const val = toValue(poll);
161
+ if (typeof val === "number") return { interval: val, whenHidden: false };
162
+ if (val && typeof val === "object") {
163
+ return {
164
+ interval: toValue(val.interval),
165
+ whenHidden: toValue(val.whenHidden) ?? false
166
+ };
167
+ }
168
+ return { interval: 0, whenHidden: false };
169
+ };
157
170
  const executeRequest = async (config) => {
171
+ if (pollTimer) clearTimeout(pollTimer);
158
172
  const requestUrl = typeof url === "string" ? url : url.value;
159
173
  if (abortController2.value) abortController2.value.abort("Cancelled by new request");
160
174
  const controller = new AbortController();
@@ -210,11 +224,25 @@ function useApi(url, options = {}) {
210
224
  if (!wasCancelled) {
211
225
  state.setLoading(false);
212
226
  onFinish?.();
227
+ const { interval, whenHidden } = getPollConfig();
228
+ if (interval > 0) {
229
+ const shouldPoll = whenHidden || typeof document !== "undefined" && !document.hidden;
230
+ if (shouldPoll) {
231
+ pollTimer = setTimeout(() => {
232
+ pollTimer = null;
233
+ const { whenHidden: currentWhenHidden } = getPollConfig();
234
+ if (currentWhenHidden || (typeof document === "undefined" || !document.hidden)) {
235
+ execute();
236
+ }
237
+ }, interval);
238
+ }
239
+ }
213
240
  }
214
241
  }
215
242
  };
216
243
  const execute = debounce > 0 ? debounceFn(executeRequest, debounce) : executeRequest;
217
244
  const abort = (msg) => {
245
+ if (pollTimer) clearTimeout(pollTimer);
218
246
  abortController2.value?.abort(msg);
219
247
  abortController2.value = null;
220
248
  };
@@ -232,6 +260,38 @@ function useApi(url, options = {}) {
232
260
  onScopeDispose(() => abort("Scope disposed"));
233
261
  }
234
262
  if (immediate) execute();
263
+ if (typeof document !== "undefined") {
264
+ const handleVisibility = () => {
265
+ if (document.hidden) return;
266
+ const { interval } = getPollConfig();
267
+ if (interval > 0 && !pollTimer && !state.loading.value) {
268
+ execute();
269
+ }
270
+ };
271
+ document.addEventListener("visibilitychange", handleVisibility);
272
+ if (getCurrentScope()) {
273
+ onScopeDispose(() => document.removeEventListener("visibilitychange", handleVisibility));
274
+ }
275
+ }
276
+ if (poll) {
277
+ watch(() => toValue(poll), () => {
278
+ const { interval } = getPollConfig();
279
+ if (interval > 0) {
280
+ if (pollTimer) {
281
+ clearTimeout(pollTimer);
282
+ pollTimer = null;
283
+ }
284
+ if (!state.loading.value) {
285
+ execute();
286
+ }
287
+ } else {
288
+ if (pollTimer) {
289
+ clearTimeout(pollTimer);
290
+ pollTimer = null;
291
+ }
292
+ }
293
+ }, { deep: true });
294
+ }
235
295
  return { ...state, execute, abort, reset };
236
296
  }
237
297
  function useApiGet(url, options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ametie/vue-muza-use",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Powerful Vue 3 API composable (Muza Kit) with Axios, Auto-Refresh & TypeScript",
5
5
  "author": "MortyQ",
6
6
  "license": "MIT",
@@ -44,6 +44,7 @@
44
44
  },
45
45
  "scripts": {
46
46
  "build": "tsup",
47
+ "test": "vitest",
47
48
  "dev": "tsup --watch"
48
49
  },
49
50
  "peerDependencies": {
@@ -55,10 +56,13 @@
55
56
  "@semantic-release/github": "^12.0.3",
56
57
  "@semantic-release/npm": "^13.1.3",
57
58
  "@types/node": "^24.10.10",
59
+ "@vue/test-utils": "^2.4.6",
58
60
  "axios": "^1.13.4",
61
+ "happy-dom": "^20.5.0",
59
62
  "semantic-release": "^25.0.3",
60
63
  "tsup": "^8.5.1",
61
64
  "typescript": "^5.9.3",
65
+ "vitest": "^4.0.18",
62
66
  "vue": "^3.5.27"
63
67
  }
64
68
  }