Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

vue-composable

Package Overview
Dependencies
Maintainers
1
Versions
83
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vue-composable - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

CHANGELOG.md

24

dist/index.d.ts

@@ -1,10 +0,14 @@

export * from './event';
export * from './arrayPagination';
export * from './debounce';
export * from './onMouseMove';
export * from './onResize';
export * from './onScroll';
export * from './pagination';
export * from './promise';
export * from './cancellablePromise';
export * from './fetch';
export * from "./event/event";
export * from "./pagination/arrayPagination";
export * from "./debounce";
export * from "./event/onMouseMove";
export * from "./event/onResize";
export * from "./event/onScroll";
export * from "./pagination/pagination";
export * from "./promise/promise";
export * from "./promise/cancellablePromise";
export * from "./promise/retry";
export * from "./web/fetch";
export * from "./web/axios";
export * from "./web/webSocket";
export * from "./localStorage";
import { Ref } from "@vue/composition-api";
export declare type RefTyped<T> = T | Ref<T>;
export declare type RefElement = RefTyped<Element>;
export declare function unwrap<T>(o: RefTyped<T>): T;
export declare function wrap<T>(o: RefTyped<T>): Ref<T>;
export declare function promisedTimeout(timeout: number): Promise<unknown>;
export declare const isArray: (arg: any) => arg is any[];
export declare const isFunction: (val: unknown) => val is Function;
export declare const isDate: (val: unknown) => val is Date;
export declare const isNumber: (val: unknown) => val is number;
export declare const isObject: (val: unknown) => val is Record<any, any>;
export declare function isPromise<T = any>(val: unknown): val is Promise<T>;
export declare function promisedTimeout(timeout: number): Promise<void>;
export declare function minMax(val: number, min: number, max: number): number;

@@ -5,18 +5,29 @@ 'use strict';

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var compositionApi = require('@vue/composition-api');
var axios = _interopDefault(require('axios'));
function useEvent(el, name, listener, options) {
const element = compositionApi.isRef(el) ? el.value : el;
const remove = () => element.removeEventListener(name, listener);
compositionApi.onMounted(() => element.addEventListener(name, listener, options));
compositionApi.onUnmounted(remove);
return remove;
}
function unwrap(o) {
return compositionApi.isRef(o) ? o.value : o;
}
// export function unwrap<T>(o: RefTyped<T>): T {
// return isRef(o) ? o.value : o;
// }
function wrap(o) {
return compositionApi.isRef(o) ? o : compositionApi.ref(o);
}
const isFunction = (val) => typeof val === "function";
// export const isString = (val: unknown): val is string =>
// typeof val === "string";
// export const isSymbol = (val: unknown): val is symbol =>
// typeof val === "symbol";
const isDate = (val) => isObject(val) && isFunction(val.getTime);
const isNumber = (val) => typeof val === "number";
const isObject = (val) => val !== null && typeof val === "object";
function isPromise(val) {
return isObject(val) && isFunction(val.then) && isFunction(val.catch);
}
function promisedTimeout(timeout) {
return new Promise(res => {
setTimeout(res, timeout);
});
}
function minMax(val, min, max) {

@@ -30,2 +41,10 @@ if (val < min)

function useEvent(el, name, listener, options) {
const element = wrap(el);
const remove = () => element.value.removeEventListener(name, listener);
compositionApi.onMounted(() => element.value.addEventListener(name, listener, options));
compositionApi.onUnmounted(remove);
return remove;
}
function usePagination(options) {

@@ -161,4 +180,3 @@ const _currentPage = wrap(options.currentPage);

function useMouseMove(el, options, wait) {
const element = unwrap(el);
function useOnMouseMove(el, options, wait) {
const mouseX = compositionApi.ref(0);

@@ -175,3 +193,3 @@ const mouseY = compositionApi.ref(0);

}
const remove = useEvent(element, "mousemove", handler, eventOptions);
const remove = useEvent(el, "mousemove", handler, eventOptions);
return {

@@ -185,8 +203,9 @@ mouseX,

function useOnResize(el, options, wait) {
const element = unwrap(el);
const height = compositionApi.ref(element.clientHeight);
const width = compositionApi.ref(element.clientWidth);
let handler = (ev) => {
height.value = element.clientHeight;
width.value = element.clientWidth;
const element = wrap(el);
const height = compositionApi.ref(element.value && element.value.clientHeight);
const width = compositionApi.ref(element.value && element.value.clientWidth);
let handler = () => {
debugger;
height.value = element.value.clientHeight;
width.value = element.value.clientWidth;
};

@@ -207,8 +226,8 @@ const eventOptions = typeof options === "number" ? undefined : options;

function useOnScroll(el, options, wait) {
const element = unwrap(el);
const scrollTop = compositionApi.ref(element.scrollTop);
const scrollLeft = compositionApi.ref(element.scrollLeft);
const element = wrap(el);
const scrollTop = compositionApi.ref(element.value && element.value.scrollTop);
const scrollLeft = compositionApi.ref(element.value && element.value.scrollLeft);
let handler = (ev) => {
scrollTop.value = element.scrollTop;
scrollLeft.value = element.scrollLeft;
scrollTop.value = element.value.scrollTop;
scrollLeft.value = element.value.scrollLeft;
};

@@ -239,3 +258,2 @@ const eventOptions = typeof options === "number" ? undefined : options;

const promise = compositionApi.ref();
let lastPromise = null;
const exec = async (...args) => {

@@ -245,6 +263,6 @@ loading.value = true;

result.value = null;
const currentPromise = (promise.value = lastPromise = fn(...args));
const currentPromise = (promise.value = fn(...args));
try {
const r = await currentPromise;
if (lastPromise === currentPromise) {
if (promise.value === currentPromise) {
result.value = r;

@@ -255,3 +273,3 @@ }

catch (er) {
if (lastPromise === currentPromise) {
if (promise.value === currentPromise) {
error.value = er;

@@ -263,3 +281,3 @@ result.value = null;

finally {
if (lastPromise === currentPromise) {
if (promise.value === currentPromise) {
loading.value = false;

@@ -297,9 +315,151 @@ }

function useFetch(init) {
const MAX_RETRIES = 9000;
/* istanbul ignore next */
const ExecutionId = Symbol(process.env.NODE_ENV !== "production" ? "RetryId" : undefined);
/* istanbul ignore next */
const CancellationToken = Symbol(process.env.NODE_ENV !== "production" ? "CancellationToken" : undefined);
const defaultStrategy = async (options, context, factory, args) => {
const retryId = context[ExecutionId].value;
let i = -1;
const maxRetries = options.maxRetries || MAX_RETRIES + 1;
const delay = options.retryDelay || noDelay;
context.retryErrors.value = [];
context.isRetrying.value = false;
context.nextRetry.value = undefined;
let nextRetry = undefined;
do {
let success = false;
let result = null;
try {
++i;
if (args) {
result = factory(...args);
}
else {
result = factory();
}
if (isPromise(result)) {
result = await result;
}
// is cancelled?
if (context[CancellationToken].value) {
return null;
}
success = true;
}
catch (error) {
result = null;
context.retryErrors.value.push(error);
}
// is our retry current one?
if (retryId !== context[ExecutionId].value) {
return result;
}
if (success) {
context.isRetrying.value = false;
context.nextRetry.value = undefined;
return result;
}
if (i >= maxRetries) {
context.isRetrying.value = false;
context.nextRetry.value = undefined;
return Promise.reject(new Error(`[useRetry] max retries reached ${maxRetries}`));
}
context.isRetrying.value = true;
const now = Date.now();
const pDelayBy = delay(i); // wrapped
const delayBy = isPromise(pDelayBy) ? await pDelayBy : pDelayBy; // unwrap promise
if (!isPromise(pDelayBy) || !!delayBy) {
if (isNumber(delayBy)) {
nextRetry = delayBy;
}
else if (isDate(delayBy)) {
nextRetry = delayBy.getTime();
}
else {
throw new Error(`[useRetry] invalid value received from options.retryDelay '${typeof delayBy}'`);
}
// if the retry is in the past, means we need to await that amount
if (nextRetry < now) {
context.nextRetry.value = now + nextRetry;
}
else {
context.nextRetry.value = nextRetry;
nextRetry = nextRetry - now;
}
if (nextRetry > 0) {
await promisedTimeout(nextRetry);
}
}
// is cancelled?
if (context[CancellationToken].value) {
return null;
}
// is our retry current one?
if (retryId !== context[ExecutionId].value) {
return result;
}
} while (i < MAX_RETRIES);
return null;
};
function useRetry(options, factory) {
const opt = !options || isFunction(options) ? {} : options;
const fn = isFunction(options) ? options : factory;
if (!isFunction(options) && !isObject(options)) {
throw new Error("[useRetry] options needs to be 'object'");
}
if (!!fn && !isFunction(fn)) {
throw new Error("[useRetry] factory needs to be 'function'");
}
const isRetrying = compositionApi.ref(false);
const nextRetry = compositionApi.ref();
const retryErrors = compositionApi.ref([]);
const cancellationToken = { value: false };
const retryId = { value: 0 };
const retryCount = compositionApi.computed(() => retryErrors.value.length);
const context = {
isRetrying,
retryCount,
nextRetry,
retryErrors,
[ExecutionId]: retryId,
[CancellationToken]: cancellationToken
};
const exec = fn
? (...args) => {
++context[ExecutionId].value;
return defaultStrategy(opt, context, fn, args);
}
: (f) => {
++context[ExecutionId].value;
return defaultStrategy(opt, context, f, undefined);
};
const cancel = () => {
context.isRetrying.value = false;
context.retryErrors.value.push(new Error("[useRetry] cancelled"));
context.nextRetry.value = undefined;
cancellationToken.value = true;
};
return {
...context,
cancel,
exec
};
}
const exponentialDelay = retryNumber => {
const delay = Math.pow(2, retryNumber) * 100;
const randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay
return delay + randomSum;
};
const noDelay = () => 0;
function useFetch(options) {
const json = compositionApi.ref(null);
// TODO add text = ref<string> ??
const jsonError = compositionApi.ref(null);
const use = usePromise(async (request) => {
const isJson = options ? options.isJson !== false : true;
const parseImmediate = options ? options.parseImmediate !== false : true;
const use = usePromise(async (request, init) => {
const response = await fetch(request, init);
if (!init || init.isJson !== false) {
if (isJson) {
const pJson = response

@@ -312,3 +472,3 @@ .json()

});
if (!init || init.parseImmediate !== false) {
if (parseImmediate) {
await pJson;

@@ -330,4 +490,154 @@ }

/* istanbul ignore next */
const _axios = axios || (globalThis && globalThis.axios);
function useAxios(config) {
/* istanbul ignore next */
process.env.NODE_ENV !== "production" && !_axios && console.warn(`[axios] not installed, please install it`);
const axiosClient = _axios.create(config);
const client = compositionApi.computed(() => axiosClient);
const use = usePromise(async (request) => {
return axiosClient.request(request);
});
const data = compositionApi.computed(() => (use.result.value && use.result.value.data) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.data) ||
null);
const status = compositionApi.computed(() => (use.result.value && use.result.value.status) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.status) ||
null);
const statusText = compositionApi.computed(() => (use.result.value && use.result.value.statusText) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.statusText) ||
null);
return {
...use,
client,
data,
status,
statusText
};
}
function useWebSocket(url, protocols) {
const ws = new WebSocket(url, protocols);
const messageEvent = compositionApi.ref(null);
const errorEvent = compositionApi.ref();
const data = compositionApi.ref(null);
const isOpen = compositionApi.ref(false);
const isClosed = compositionApi.ref(false);
const errored = compositionApi.ref(false);
/* istanbul ignore next */
let lastMessage = (process.env.NODE_ENV !== "production" && Date.now()) || undefined;
ws.addEventListener("message", x => {
messageEvent.value = x;
data.value = x.data;
// if the messages are to quick, we need to warn
/* istanbul ignore else */
if (process.env.NODE_ENV !== "production") {
if (Date.now() - lastMessage < 2) {
console.warn('[useWebSocket] message rate is too high, if you are using "data" or "messageEvent"' +
" you might not get updated of all the messages." +
' Use "ws..addEventListener("message", handler)" instead');
}
lastMessage = Date.now();
}
});
ws.addEventListener("error", error => {
errorEvent.value = error;
errored.value = true;
});
ws.addEventListener("close", () => {
isOpen.value = false;
isClosed.value = true;
});
ws.addEventListener("open", () => {
isOpen.value = true;
isClosed.value = false;
});
const send = (data) => ws.send(data);
const close = (code, reason) => {
ws.close(code, reason);
};
return {
ws,
send,
close,
messageEvent,
errorEvent,
data,
isOpen,
isClosed,
errored
};
}
// used to store all the instances of weakMap
const keyedMap = new Map();
const weakMap = new WeakMap();
function useLocalStorage(key, defaultValue) {
let lazy = false;
let k = keyedMap.get(key);
const json = localStorage.getItem(key);
const storage = (k && weakMap.get(k)) ||
(!!defaultValue && wrap(defaultValue)) ||
compositionApi.ref(null);
if (json && !k) {
try {
storage.value = JSON.parse(json);
lazy = false;
}
catch (e) {
/* istanbul ignore next */
console.warn("[useLocalStorage] error parsing value from localStorage", key, e);
}
}
// do not watch if we already created the instance
if (!k) {
k = {};
keyedMap.set(key, k);
weakMap.set(k, storage);
compositionApi.watch(storage, storage => {
if (storage === undefined) {
localStorage.removeItem(key);
return;
}
// do not overflow localStorage with updates nor keep doing stringify
debounce(() => localStorage.setItem(key, JSON.stringify(storage)), 100)();
}, {
deep: true,
lazy
});
}
const clear = () => {
keyedMap.forEach((v) => {
const obj = weakMap.get(v);
/* istanbul ignore else */
if (obj) {
obj.value = undefined;
}
weakMap.delete(v);
});
keyedMap.clear();
};
const remove = () => {
keyedMap.delete(key);
weakMap.delete(k);
storage.value = undefined;
};
return {
storage,
clear,
remove
};
}
exports.debounce = debounce;
exports.exponentialDelay = exponentialDelay;
exports.noDelay = noDelay;
exports.useArrayPagination = useArrayPagination;
exports.useAxios = useAxios;
exports.useCancellablePromise = useCancellablePromise;

@@ -337,3 +647,4 @@ exports.useDebounce = useDebounce;

exports.useFetch = useFetch;
exports.useMouseMove = useMouseMove;
exports.useLocalStorage = useLocalStorage;
exports.useOnMouseMove = useOnMouseMove;
exports.useOnResize = useOnResize;

@@ -343,1 +654,3 @@ exports.useOnScroll = useOnScroll;

exports.usePromise = usePromise;
exports.useRetry = useRetry;
exports.useWebSocket = useWebSocket;

@@ -1,17 +0,26 @@

import { isRef, onMounted, onUnmounted, ref, computed, watch } from '@vue/composition-api';
import { isRef, ref, onMounted, onUnmounted, computed, watch } from '@vue/composition-api';
import axios from 'axios';
function useEvent(el, name, listener, options) {
const element = isRef(el) ? el.value : el;
const remove = () => element.removeEventListener(name, listener);
onMounted(() => element.addEventListener(name, listener, options));
onUnmounted(remove);
return remove;
}
function unwrap(o) {
return isRef(o) ? o.value : o;
}
// export function unwrap<T>(o: RefTyped<T>): T {
// return isRef(o) ? o.value : o;
// }
function wrap(o) {
return isRef(o) ? o : ref(o);
}
const isFunction = (val) => typeof val === "function";
// export const isString = (val: unknown): val is string =>
// typeof val === "string";
// export const isSymbol = (val: unknown): val is symbol =>
// typeof val === "symbol";
const isDate = (val) => isObject(val) && isFunction(val.getTime);
const isNumber = (val) => typeof val === "number";
const isObject = (val) => val !== null && typeof val === "object";
function isPromise(val) {
return isObject(val) && isFunction(val.then) && isFunction(val.catch);
}
function promisedTimeout(timeout) {
return new Promise(res => {
setTimeout(res, timeout);
});
}
function minMax(val, min, max) {

@@ -25,2 +34,10 @@ if (val < min)

function useEvent(el, name, listener, options) {
const element = wrap(el);
const remove = () => element.value.removeEventListener(name, listener);
onMounted(() => element.value.addEventListener(name, listener, options));
onUnmounted(remove);
return remove;
}
function usePagination(options) {

@@ -156,4 +173,3 @@ const _currentPage = wrap(options.currentPage);

function useMouseMove(el, options, wait) {
const element = unwrap(el);
function useOnMouseMove(el, options, wait) {
const mouseX = ref(0);

@@ -170,3 +186,3 @@ const mouseY = ref(0);

}
const remove = useEvent(element, "mousemove", handler, eventOptions);
const remove = useEvent(el, "mousemove", handler, eventOptions);
return {

@@ -180,8 +196,9 @@ mouseX,

function useOnResize(el, options, wait) {
const element = unwrap(el);
const height = ref(element.clientHeight);
const width = ref(element.clientWidth);
let handler = (ev) => {
height.value = element.clientHeight;
width.value = element.clientWidth;
const element = wrap(el);
const height = ref(element.value && element.value.clientHeight);
const width = ref(element.value && element.value.clientWidth);
let handler = () => {
debugger;
height.value = element.value.clientHeight;
width.value = element.value.clientWidth;
};

@@ -202,8 +219,8 @@ const eventOptions = typeof options === "number" ? undefined : options;

function useOnScroll(el, options, wait) {
const element = unwrap(el);
const scrollTop = ref(element.scrollTop);
const scrollLeft = ref(element.scrollLeft);
const element = wrap(el);
const scrollTop = ref(element.value && element.value.scrollTop);
const scrollLeft = ref(element.value && element.value.scrollLeft);
let handler = (ev) => {
scrollTop.value = element.scrollTop;
scrollLeft.value = element.scrollLeft;
scrollTop.value = element.value.scrollTop;
scrollLeft.value = element.value.scrollLeft;
};

@@ -234,3 +251,2 @@ const eventOptions = typeof options === "number" ? undefined : options;

const promise = ref();
let lastPromise = null;
const exec = async (...args) => {

@@ -240,6 +256,6 @@ loading.value = true;

result.value = null;
const currentPromise = (promise.value = lastPromise = fn(...args));
const currentPromise = (promise.value = fn(...args));
try {
const r = await currentPromise;
if (lastPromise === currentPromise) {
if (promise.value === currentPromise) {
result.value = r;

@@ -250,3 +266,3 @@ }

catch (er) {
if (lastPromise === currentPromise) {
if (promise.value === currentPromise) {
error.value = er;

@@ -258,3 +274,3 @@ result.value = null;

finally {
if (lastPromise === currentPromise) {
if (promise.value === currentPromise) {
loading.value = false;

@@ -292,9 +308,151 @@ }

function useFetch(init) {
const MAX_RETRIES = 9000;
/* istanbul ignore next */
const ExecutionId = Symbol(process.env.NODE_ENV !== "production" ? "RetryId" : undefined);
/* istanbul ignore next */
const CancellationToken = Symbol(process.env.NODE_ENV !== "production" ? "CancellationToken" : undefined);
const defaultStrategy = async (options, context, factory, args) => {
const retryId = context[ExecutionId].value;
let i = -1;
const maxRetries = options.maxRetries || MAX_RETRIES + 1;
const delay = options.retryDelay || noDelay;
context.retryErrors.value = [];
context.isRetrying.value = false;
context.nextRetry.value = undefined;
let nextRetry = undefined;
do {
let success = false;
let result = null;
try {
++i;
if (args) {
result = factory(...args);
}
else {
result = factory();
}
if (isPromise(result)) {
result = await result;
}
// is cancelled?
if (context[CancellationToken].value) {
return null;
}
success = true;
}
catch (error) {
result = null;
context.retryErrors.value.push(error);
}
// is our retry current one?
if (retryId !== context[ExecutionId].value) {
return result;
}
if (success) {
context.isRetrying.value = false;
context.nextRetry.value = undefined;
return result;
}
if (i >= maxRetries) {
context.isRetrying.value = false;
context.nextRetry.value = undefined;
return Promise.reject(new Error(`[useRetry] max retries reached ${maxRetries}`));
}
context.isRetrying.value = true;
const now = Date.now();
const pDelayBy = delay(i); // wrapped
const delayBy = isPromise(pDelayBy) ? await pDelayBy : pDelayBy; // unwrap promise
if (!isPromise(pDelayBy) || !!delayBy) {
if (isNumber(delayBy)) {
nextRetry = delayBy;
}
else if (isDate(delayBy)) {
nextRetry = delayBy.getTime();
}
else {
throw new Error(`[useRetry] invalid value received from options.retryDelay '${typeof delayBy}'`);
}
// if the retry is in the past, means we need to await that amount
if (nextRetry < now) {
context.nextRetry.value = now + nextRetry;
}
else {
context.nextRetry.value = nextRetry;
nextRetry = nextRetry - now;
}
if (nextRetry > 0) {
await promisedTimeout(nextRetry);
}
}
// is cancelled?
if (context[CancellationToken].value) {
return null;
}
// is our retry current one?
if (retryId !== context[ExecutionId].value) {
return result;
}
} while (i < MAX_RETRIES);
return null;
};
function useRetry(options, factory) {
const opt = !options || isFunction(options) ? {} : options;
const fn = isFunction(options) ? options : factory;
if (!isFunction(options) && !isObject(options)) {
throw new Error("[useRetry] options needs to be 'object'");
}
if (!!fn && !isFunction(fn)) {
throw new Error("[useRetry] factory needs to be 'function'");
}
const isRetrying = ref(false);
const nextRetry = ref();
const retryErrors = ref([]);
const cancellationToken = { value: false };
const retryId = { value: 0 };
const retryCount = computed(() => retryErrors.value.length);
const context = {
isRetrying,
retryCount,
nextRetry,
retryErrors,
[ExecutionId]: retryId,
[CancellationToken]: cancellationToken
};
const exec = fn
? (...args) => {
++context[ExecutionId].value;
return defaultStrategy(opt, context, fn, args);
}
: (f) => {
++context[ExecutionId].value;
return defaultStrategy(opt, context, f, undefined);
};
const cancel = () => {
context.isRetrying.value = false;
context.retryErrors.value.push(new Error("[useRetry] cancelled"));
context.nextRetry.value = undefined;
cancellationToken.value = true;
};
return {
...context,
cancel,
exec
};
}
const exponentialDelay = retryNumber => {
const delay = Math.pow(2, retryNumber) * 100;
const randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay
return delay + randomSum;
};
const noDelay = () => 0;
function useFetch(options) {
const json = ref(null);
// TODO add text = ref<string> ??
const jsonError = ref(null);
const use = usePromise(async (request) => {
const isJson = options ? options.isJson !== false : true;
const parseImmediate = options ? options.parseImmediate !== false : true;
const use = usePromise(async (request, init) => {
const response = await fetch(request, init);
if (!init || init.isJson !== false) {
if (isJson) {
const pJson = response

@@ -307,3 +465,3 @@ .json()

});
if (!init || init.parseImmediate !== false) {
if (parseImmediate) {
await pJson;

@@ -325,2 +483,149 @@ }

export { debounce, useArrayPagination, useCancellablePromise, useDebounce, useEvent, useFetch, useMouseMove, useOnResize, useOnScroll, usePagination, usePromise };
/* istanbul ignore next */
const _axios = axios || (globalThis && globalThis.axios);
function useAxios(config) {
/* istanbul ignore next */
process.env.NODE_ENV !== "production" && !_axios && console.warn(`[axios] not installed, please install it`);
const axiosClient = _axios.create(config);
const client = computed(() => axiosClient);
const use = usePromise(async (request) => {
return axiosClient.request(request);
});
const data = computed(() => (use.result.value && use.result.value.data) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.data) ||
null);
const status = computed(() => (use.result.value && use.result.value.status) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.status) ||
null);
const statusText = computed(() => (use.result.value && use.result.value.statusText) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.statusText) ||
null);
return {
...use,
client,
data,
status,
statusText
};
}
function useWebSocket(url, protocols) {
const ws = new WebSocket(url, protocols);
const messageEvent = ref(null);
const errorEvent = ref();
const data = ref(null);
const isOpen = ref(false);
const isClosed = ref(false);
const errored = ref(false);
/* istanbul ignore next */
let lastMessage = (process.env.NODE_ENV !== "production" && Date.now()) || undefined;
ws.addEventListener("message", x => {
messageEvent.value = x;
data.value = x.data;
// if the messages are to quick, we need to warn
/* istanbul ignore else */
if (process.env.NODE_ENV !== "production") {
if (Date.now() - lastMessage < 2) {
console.warn('[useWebSocket] message rate is too high, if you are using "data" or "messageEvent"' +
" you might not get updated of all the messages." +
' Use "ws..addEventListener("message", handler)" instead');
}
lastMessage = Date.now();
}
});
ws.addEventListener("error", error => {
errorEvent.value = error;
errored.value = true;
});
ws.addEventListener("close", () => {
isOpen.value = false;
isClosed.value = true;
});
ws.addEventListener("open", () => {
isOpen.value = true;
isClosed.value = false;
});
const send = (data) => ws.send(data);
const close = (code, reason) => {
ws.close(code, reason);
};
return {
ws,
send,
close,
messageEvent,
errorEvent,
data,
isOpen,
isClosed,
errored
};
}
// used to store all the instances of weakMap
const keyedMap = new Map();
const weakMap = new WeakMap();
function useLocalStorage(key, defaultValue) {
let lazy = false;
let k = keyedMap.get(key);
const json = localStorage.getItem(key);
const storage = (k && weakMap.get(k)) ||
(!!defaultValue && wrap(defaultValue)) ||
ref(null);
if (json && !k) {
try {
storage.value = JSON.parse(json);
lazy = false;
}
catch (e) {
/* istanbul ignore next */
console.warn("[useLocalStorage] error parsing value from localStorage", key, e);
}
}
// do not watch if we already created the instance
if (!k) {
k = {};
keyedMap.set(key, k);
weakMap.set(k, storage);
watch(storage, storage => {
if (storage === undefined) {
localStorage.removeItem(key);
return;
}
// do not overflow localStorage with updates nor keep doing stringify
debounce(() => localStorage.setItem(key, JSON.stringify(storage)), 100)();
}, {
deep: true,
lazy
});
}
const clear = () => {
keyedMap.forEach((v) => {
const obj = weakMap.get(v);
/* istanbul ignore else */
if (obj) {
obj.value = undefined;
}
weakMap.delete(v);
});
keyedMap.clear();
};
const remove = () => {
keyedMap.delete(key);
weakMap.delete(k);
storage.value = undefined;
};
return {
storage,
clear,
remove
};
}
export { debounce, exponentialDelay, noDelay, useArrayPagination, useAxios, useCancellablePromise, useDebounce, useEvent, useFetch, useLocalStorage, useOnMouseMove, useOnResize, useOnScroll, usePagination, usePromise, useRetry, useWebSocket };
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@vue/composition-api')) :
typeof define === 'function' && define.amd ? define(['exports', '@vue/composition-api'], factory) :
(global = global || self, factory(global.vueComposable = {}, global.vueCompositionApi));
}(this, (function (exports, compositionApi) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@vue/composition-api'), require('axios')) :
typeof define === 'function' && define.amd ? define(['exports', '@vue/composition-api', 'axios'], factory) :
(global = global || self, factory(global.vueComposable = {}, global.vueCompositionApi, global.axios));
}(this, (function (exports, compositionApi, axios) { 'use strict';
function useEvent(el, name, listener, options) {
const element = compositionApi.isRef(el) ? el.value : el;
const remove = () => element.removeEventListener(name, listener);
compositionApi.onMounted(() => element.addEventListener(name, listener, options));
compositionApi.onUnmounted(remove);
return remove;
}
axios = axios && axios.hasOwnProperty('default') ? axios['default'] : axios;
function unwrap(o) {
return compositionApi.isRef(o) ? o.value : o;
}
// export function unwrap<T>(o: RefTyped<T>): T {
// return isRef(o) ? o.value : o;
// }
function wrap(o) {
return compositionApi.isRef(o) ? o : compositionApi.ref(o);
}
const isFunction = (val) => typeof val === "function";
// export const isString = (val: unknown): val is string =>
// typeof val === "string";
// export const isSymbol = (val: unknown): val is symbol =>
// typeof val === "symbol";
const isDate = (val) => isObject(val) && isFunction(val.getTime);
const isNumber = (val) => typeof val === "number";
const isObject = (val) => val !== null && typeof val === "object";
function isPromise(val) {
return isObject(val) && isFunction(val.then) && isFunction(val.catch);
}
function promisedTimeout(timeout) {
return new Promise(res => {
setTimeout(res, timeout);
});
}
function minMax(val, min, max) {

@@ -29,2 +39,10 @@ if (val < min)

function useEvent(el, name, listener, options) {
const element = wrap(el);
const remove = () => element.value.removeEventListener(name, listener);
compositionApi.onMounted(() => element.value.addEventListener(name, listener, options));
compositionApi.onUnmounted(remove);
return remove;
}
function usePagination(options) {

@@ -160,4 +178,3 @@ const _currentPage = wrap(options.currentPage);

function useMouseMove(el, options, wait) {
const element = unwrap(el);
function useOnMouseMove(el, options, wait) {
const mouseX = compositionApi.ref(0);

@@ -174,3 +191,3 @@ const mouseY = compositionApi.ref(0);

}
const remove = useEvent(element, "mousemove", handler, eventOptions);
const remove = useEvent(el, "mousemove", handler, eventOptions);
return {

@@ -184,8 +201,9 @@ mouseX,

function useOnResize(el, options, wait) {
const element = unwrap(el);
const height = compositionApi.ref(element.clientHeight);
const width = compositionApi.ref(element.clientWidth);
let handler = (ev) => {
height.value = element.clientHeight;
width.value = element.clientWidth;
const element = wrap(el);
const height = compositionApi.ref(element.value && element.value.clientHeight);
const width = compositionApi.ref(element.value && element.value.clientWidth);
let handler = () => {
debugger;
height.value = element.value.clientHeight;
width.value = element.value.clientWidth;
};

@@ -206,8 +224,8 @@ const eventOptions = typeof options === "number" ? undefined : options;

function useOnScroll(el, options, wait) {
const element = unwrap(el);
const scrollTop = compositionApi.ref(element.scrollTop);
const scrollLeft = compositionApi.ref(element.scrollLeft);
const element = wrap(el);
const scrollTop = compositionApi.ref(element.value && element.value.scrollTop);
const scrollLeft = compositionApi.ref(element.value && element.value.scrollLeft);
let handler = (ev) => {
scrollTop.value = element.scrollTop;
scrollLeft.value = element.scrollLeft;
scrollTop.value = element.value.scrollTop;
scrollLeft.value = element.value.scrollLeft;
};

@@ -238,3 +256,2 @@ const eventOptions = typeof options === "number" ? undefined : options;

const promise = compositionApi.ref();
let lastPromise = null;
const exec = async (...args) => {

@@ -244,6 +261,6 @@ loading.value = true;

result.value = null;
const currentPromise = (promise.value = lastPromise = fn(...args));
const currentPromise = (promise.value = fn(...args));
try {
const r = await currentPromise;
if (lastPromise === currentPromise) {
if (promise.value === currentPromise) {
result.value = r;

@@ -254,3 +271,3 @@ }

catch (er) {
if (lastPromise === currentPromise) {
if (promise.value === currentPromise) {
error.value = er;

@@ -262,3 +279,3 @@ result.value = null;

finally {
if (lastPromise === currentPromise) {
if (promise.value === currentPromise) {
loading.value = false;

@@ -296,9 +313,151 @@ }

function useFetch(init) {
const MAX_RETRIES = 9000;
/* istanbul ignore next */
const ExecutionId = Symbol( "RetryId" );
/* istanbul ignore next */
const CancellationToken = Symbol( "CancellationToken" );
const defaultStrategy = async (options, context, factory, args) => {
const retryId = context[ExecutionId].value;
let i = -1;
const maxRetries = options.maxRetries || MAX_RETRIES + 1;
const delay = options.retryDelay || noDelay;
context.retryErrors.value = [];
context.isRetrying.value = false;
context.nextRetry.value = undefined;
let nextRetry = undefined;
do {
let success = false;
let result = null;
try {
++i;
if (args) {
result = factory(...args);
}
else {
result = factory();
}
if (isPromise(result)) {
result = await result;
}
// is cancelled?
if (context[CancellationToken].value) {
return null;
}
success = true;
}
catch (error) {
result = null;
context.retryErrors.value.push(error);
}
// is our retry current one?
if (retryId !== context[ExecutionId].value) {
return result;
}
if (success) {
context.isRetrying.value = false;
context.nextRetry.value = undefined;
return result;
}
if (i >= maxRetries) {
context.isRetrying.value = false;
context.nextRetry.value = undefined;
return Promise.reject(new Error(`[useRetry] max retries reached ${maxRetries}`));
}
context.isRetrying.value = true;
const now = Date.now();
const pDelayBy = delay(i); // wrapped
const delayBy = isPromise(pDelayBy) ? await pDelayBy : pDelayBy; // unwrap promise
if (!isPromise(pDelayBy) || !!delayBy) {
if (isNumber(delayBy)) {
nextRetry = delayBy;
}
else if (isDate(delayBy)) {
nextRetry = delayBy.getTime();
}
else {
throw new Error(`[useRetry] invalid value received from options.retryDelay '${typeof delayBy}'`);
}
// if the retry is in the past, means we need to await that amount
if (nextRetry < now) {
context.nextRetry.value = now + nextRetry;
}
else {
context.nextRetry.value = nextRetry;
nextRetry = nextRetry - now;
}
if (nextRetry > 0) {
await promisedTimeout(nextRetry);
}
}
// is cancelled?
if (context[CancellationToken].value) {
return null;
}
// is our retry current one?
if (retryId !== context[ExecutionId].value) {
return result;
}
} while (i < MAX_RETRIES);
return null;
};
function useRetry(options, factory) {
const opt = !options || isFunction(options) ? {} : options;
const fn = isFunction(options) ? options : factory;
if (!isFunction(options) && !isObject(options)) {
throw new Error("[useRetry] options needs to be 'object'");
}
if (!!fn && !isFunction(fn)) {
throw new Error("[useRetry] factory needs to be 'function'");
}
const isRetrying = compositionApi.ref(false);
const nextRetry = compositionApi.ref();
const retryErrors = compositionApi.ref([]);
const cancellationToken = { value: false };
const retryId = { value: 0 };
const retryCount = compositionApi.computed(() => retryErrors.value.length);
const context = {
isRetrying,
retryCount,
nextRetry,
retryErrors,
[ExecutionId]: retryId,
[CancellationToken]: cancellationToken
};
const exec = fn
? (...args) => {
++context[ExecutionId].value;
return defaultStrategy(opt, context, fn, args);
}
: (f) => {
++context[ExecutionId].value;
return defaultStrategy(opt, context, f, undefined);
};
const cancel = () => {
context.isRetrying.value = false;
context.retryErrors.value.push(new Error("[useRetry] cancelled"));
context.nextRetry.value = undefined;
cancellationToken.value = true;
};
return {
...context,
cancel,
exec
};
}
const exponentialDelay = retryNumber => {
const delay = Math.pow(2, retryNumber) * 100;
const randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay
return delay + randomSum;
};
const noDelay = () => 0;
function useFetch(options) {
const json = compositionApi.ref(null);
// TODO add text = ref<string> ??
const jsonError = compositionApi.ref(null);
const use = usePromise(async (request) => {
const isJson = options ? options.isJson !== false : true;
const parseImmediate = options ? options.parseImmediate !== false : true;
const use = usePromise(async (request, init) => {
const response = await fetch(request, init);
if (!init || init.isJson !== false) {
if (isJson) {
const pJson = response

@@ -311,3 +470,3 @@ .json()

});
if (!init || init.parseImmediate !== false) {
if (parseImmediate) {
await pJson;

@@ -329,4 +488,154 @@ }

/* istanbul ignore next */
const _axios = axios || (globalThis && globalThis.axios);
function useAxios(config) {
/* istanbul ignore next */
!_axios && console.warn(`[axios] not installed, please install it`);
const axiosClient = _axios.create(config);
const client = compositionApi.computed(() => axiosClient);
const use = usePromise(async (request) => {
return axiosClient.request(request);
});
const data = compositionApi.computed(() => (use.result.value && use.result.value.data) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.data) ||
null);
const status = compositionApi.computed(() => (use.result.value && use.result.value.status) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.status) ||
null);
const statusText = compositionApi.computed(() => (use.result.value && use.result.value.statusText) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.statusText) ||
null);
return {
...use,
client,
data,
status,
statusText
};
}
function useWebSocket(url, protocols) {
const ws = new WebSocket(url, protocols);
const messageEvent = compositionApi.ref(null);
const errorEvent = compositionApi.ref();
const data = compositionApi.ref(null);
const isOpen = compositionApi.ref(false);
const isClosed = compositionApi.ref(false);
const errored = compositionApi.ref(false);
/* istanbul ignore next */
let lastMessage = ( Date.now()) || undefined;
ws.addEventListener("message", x => {
messageEvent.value = x;
data.value = x.data;
// if the messages are to quick, we need to warn
/* istanbul ignore else */
{
if (Date.now() - lastMessage < 2) {
console.warn('[useWebSocket] message rate is too high, if you are using "data" or "messageEvent"' +
" you might not get updated of all the messages." +
' Use "ws..addEventListener("message", handler)" instead');
}
lastMessage = Date.now();
}
});
ws.addEventListener("error", error => {
errorEvent.value = error;
errored.value = true;
});
ws.addEventListener("close", () => {
isOpen.value = false;
isClosed.value = true;
});
ws.addEventListener("open", () => {
isOpen.value = true;
isClosed.value = false;
});
const send = (data) => ws.send(data);
const close = (code, reason) => {
ws.close(code, reason);
};
return {
ws,
send,
close,
messageEvent,
errorEvent,
data,
isOpen,
isClosed,
errored
};
}
// used to store all the instances of weakMap
const keyedMap = new Map();
const weakMap = new WeakMap();
function useLocalStorage(key, defaultValue) {
let lazy = false;
let k = keyedMap.get(key);
const json = localStorage.getItem(key);
const storage = (k && weakMap.get(k)) ||
(!!defaultValue && wrap(defaultValue)) ||
compositionApi.ref(null);
if (json && !k) {
try {
storage.value = JSON.parse(json);
lazy = false;
}
catch (e) {
/* istanbul ignore next */
console.warn("[useLocalStorage] error parsing value from localStorage", key, e);
}
}
// do not watch if we already created the instance
if (!k) {
k = {};
keyedMap.set(key, k);
weakMap.set(k, storage);
compositionApi.watch(storage, storage => {
if (storage === undefined) {
localStorage.removeItem(key);
return;
}
// do not overflow localStorage with updates nor keep doing stringify
debounce(() => localStorage.setItem(key, JSON.stringify(storage)), 100)();
}, {
deep: true,
lazy
});
}
const clear = () => {
keyedMap.forEach((v) => {
const obj = weakMap.get(v);
/* istanbul ignore else */
if (obj) {
obj.value = undefined;
}
weakMap.delete(v);
});
keyedMap.clear();
};
const remove = () => {
keyedMap.delete(key);
weakMap.delete(k);
storage.value = undefined;
};
return {
storage,
clear,
remove
};
}
exports.debounce = debounce;
exports.exponentialDelay = exponentialDelay;
exports.noDelay = noDelay;
exports.useArrayPagination = useArrayPagination;
exports.useAxios = useAxios;
exports.useCancellablePromise = useCancellablePromise;

@@ -336,3 +645,4 @@ exports.useDebounce = useDebounce;

exports.useFetch = useFetch;
exports.useMouseMove = useMouseMove;
exports.useLocalStorage = useLocalStorage;
exports.useOnMouseMove = useOnMouseMove;
exports.useOnResize = useOnResize;

@@ -342,2 +652,4 @@ exports.useOnScroll = useOnScroll;

exports.usePromise = usePromise;
exports.useRetry = useRetry;
exports.useWebSocket = useWebSocket;

@@ -344,0 +656,0 @@ Object.defineProperty(exports, '__esModule', { value: true });

(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@vue/composition-api')) :
typeof define === 'function' && define.amd ? define(['exports', '@vue/composition-api'], factory) :
(global = global || self, factory(global.vueComposable = {}, global.vueCompositionApi));
}(this, (function (exports, compositionApi) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@vue/composition-api'), require('axios')) :
typeof define === 'function' && define.amd ? define(['exports', '@vue/composition-api', 'axios'], factory) :
(global = global || self, factory(global.vueComposable = {}, global.vueCompositionApi, global.axios));
}(this, (function (exports, compositionApi, axios) { 'use strict';
function useEvent(el, name, listener, options) {
const element = compositionApi.isRef(el) ? el.value : el;
const remove = () => element.removeEventListener(name, listener);
compositionApi.onMounted(() => element.addEventListener(name, listener, options));
compositionApi.onUnmounted(remove);
return remove;
}
axios = axios && axios.hasOwnProperty('default') ? axios['default'] : axios;
function unwrap(o) {
return compositionApi.isRef(o) ? o.value : o;
}
// export function unwrap<T>(o: RefTyped<T>): T {
// return isRef(o) ? o.value : o;
// }
function wrap(o) {
return compositionApi.isRef(o) ? o : compositionApi.ref(o);
}
const isFunction = (val) => typeof val === "function";
// export const isString = (val: unknown): val is string =>
// typeof val === "string";
// export const isSymbol = (val: unknown): val is symbol =>
// typeof val === "symbol";
const isDate = (val) => isObject(val) && isFunction(val.getTime);
const isNumber = (val) => typeof val === "number";
const isObject = (val) => val !== null && typeof val === "object";
function isPromise(val) {
return isObject(val) && isFunction(val.then) && isFunction(val.catch);
}
function promisedTimeout(timeout) {
return new Promise(res => {
setTimeout(res, timeout);
});
}
function minMax(val, min, max) {

@@ -29,2 +39,10 @@ if (val < min)

function useEvent(el, name, listener, options) {
const element = wrap(el);
const remove = () => element.value.removeEventListener(name, listener);
compositionApi.onMounted(() => element.value.addEventListener(name, listener, options));
compositionApi.onUnmounted(remove);
return remove;
}
function usePagination(options) {

@@ -148,4 +166,3 @@ const _currentPage = wrap(options.currentPage);

function useMouseMove(el, options, wait) {
const element = unwrap(el);
function useOnMouseMove(el, options, wait) {
const mouseX = compositionApi.ref(0);

@@ -162,3 +179,3 @@ const mouseY = compositionApi.ref(0);

}
const remove = useEvent(element, "mousemove", handler, eventOptions);
const remove = useEvent(el, "mousemove", handler, eventOptions);
return {

@@ -172,8 +189,9 @@ mouseX,

function useOnResize(el, options, wait) {
const element = unwrap(el);
const height = compositionApi.ref(element.clientHeight);
const width = compositionApi.ref(element.clientWidth);
let handler = (ev) => {
height.value = element.clientHeight;
width.value = element.clientWidth;
const element = wrap(el);
const height = compositionApi.ref(element.value && element.value.clientHeight);
const width = compositionApi.ref(element.value && element.value.clientWidth);
let handler = () => {
debugger;
height.value = element.value.clientHeight;
width.value = element.value.clientWidth;
};

@@ -194,8 +212,8 @@ const eventOptions = typeof options === "number" ? undefined : options;

function useOnScroll(el, options, wait) {
const element = unwrap(el);
const scrollTop = compositionApi.ref(element.scrollTop);
const scrollLeft = compositionApi.ref(element.scrollLeft);
const element = wrap(el);
const scrollTop = compositionApi.ref(element.value && element.value.scrollTop);
const scrollLeft = compositionApi.ref(element.value && element.value.scrollLeft);
let handler = (ev) => {
scrollTop.value = element.scrollTop;
scrollLeft.value = element.scrollLeft;
scrollTop.value = element.value.scrollTop;
scrollLeft.value = element.value.scrollLeft;
};

@@ -226,3 +244,2 @@ const eventOptions = typeof options === "number" ? undefined : options;

const promise = compositionApi.ref();
let lastPromise = null;
const exec = async (...args) => {

@@ -232,6 +249,6 @@ loading.value = true;

result.value = null;
const currentPromise = (promise.value = lastPromise = fn(...args));
const currentPromise = (promise.value = fn(...args));
try {
const r = await currentPromise;
if (lastPromise === currentPromise) {
if (promise.value === currentPromise) {
result.value = r;

@@ -242,3 +259,3 @@ }

catch (er) {
if (lastPromise === currentPromise) {
if (promise.value === currentPromise) {
error.value = er;

@@ -250,3 +267,3 @@ result.value = null;

finally {
if (lastPromise === currentPromise) {
if (promise.value === currentPromise) {
loading.value = false;

@@ -284,9 +301,151 @@ }

function useFetch(init) {
const MAX_RETRIES = 9000;
/* istanbul ignore next */
const ExecutionId = Symbol( undefined);
/* istanbul ignore next */
const CancellationToken = Symbol( undefined);
const defaultStrategy = async (options, context, factory, args) => {
const retryId = context[ExecutionId].value;
let i = -1;
const maxRetries = options.maxRetries || MAX_RETRIES + 1;
const delay = options.retryDelay || noDelay;
context.retryErrors.value = [];
context.isRetrying.value = false;
context.nextRetry.value = undefined;
let nextRetry = undefined;
do {
let success = false;
let result = null;
try {
++i;
if (args) {
result = factory(...args);
}
else {
result = factory();
}
if (isPromise(result)) {
result = await result;
}
// is cancelled?
if (context[CancellationToken].value) {
return null;
}
success = true;
}
catch (error) {
result = null;
context.retryErrors.value.push(error);
}
// is our retry current one?
if (retryId !== context[ExecutionId].value) {
return result;
}
if (success) {
context.isRetrying.value = false;
context.nextRetry.value = undefined;
return result;
}
if (i >= maxRetries) {
context.isRetrying.value = false;
context.nextRetry.value = undefined;
return Promise.reject(new Error(`[useRetry] max retries reached ${maxRetries}`));
}
context.isRetrying.value = true;
const now = Date.now();
const pDelayBy = delay(i); // wrapped
const delayBy = isPromise(pDelayBy) ? await pDelayBy : pDelayBy; // unwrap promise
if (!isPromise(pDelayBy) || !!delayBy) {
if (isNumber(delayBy)) {
nextRetry = delayBy;
}
else if (isDate(delayBy)) {
nextRetry = delayBy.getTime();
}
else {
throw new Error(`[useRetry] invalid value received from options.retryDelay '${typeof delayBy}'`);
}
// if the retry is in the past, means we need to await that amount
if (nextRetry < now) {
context.nextRetry.value = now + nextRetry;
}
else {
context.nextRetry.value = nextRetry;
nextRetry = nextRetry - now;
}
if (nextRetry > 0) {
await promisedTimeout(nextRetry);
}
}
// is cancelled?
if (context[CancellationToken].value) {
return null;
}
// is our retry current one?
if (retryId !== context[ExecutionId].value) {
return result;
}
} while (i < MAX_RETRIES);
return null;
};
function useRetry(options, factory) {
const opt = !options || isFunction(options) ? {} : options;
const fn = isFunction(options) ? options : factory;
if (!isFunction(options) && !isObject(options)) {
throw new Error("[useRetry] options needs to be 'object'");
}
if (!!fn && !isFunction(fn)) {
throw new Error("[useRetry] factory needs to be 'function'");
}
const isRetrying = compositionApi.ref(false);
const nextRetry = compositionApi.ref();
const retryErrors = compositionApi.ref([]);
const cancellationToken = { value: false };
const retryId = { value: 0 };
const retryCount = compositionApi.computed(() => retryErrors.value.length);
const context = {
isRetrying,
retryCount,
nextRetry,
retryErrors,
[ExecutionId]: retryId,
[CancellationToken]: cancellationToken
};
const exec = fn
? (...args) => {
++context[ExecutionId].value;
return defaultStrategy(opt, context, fn, args);
}
: (f) => {
++context[ExecutionId].value;
return defaultStrategy(opt, context, f, undefined);
};
const cancel = () => {
context.isRetrying.value = false;
context.retryErrors.value.push(new Error("[useRetry] cancelled"));
context.nextRetry.value = undefined;
cancellationToken.value = true;
};
return {
...context,
cancel,
exec
};
}
const exponentialDelay = retryNumber => {
const delay = Math.pow(2, retryNumber) * 100;
const randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay
return delay + randomSum;
};
const noDelay = () => 0;
function useFetch(options) {
const json = compositionApi.ref(null);
// TODO add text = ref<string> ??
const jsonError = compositionApi.ref(null);
const use = usePromise(async (request) => {
const isJson = options ? options.isJson !== false : true;
const parseImmediate = options ? options.parseImmediate !== false : true;
const use = usePromise(async (request, init) => {
const response = await fetch(request, init);
if (!init || init.isJson !== false) {
if (isJson) {
const pJson = response

@@ -299,3 +458,3 @@ .json()

});
if (!init || init.parseImmediate !== false) {
if (parseImmediate) {
await pJson;

@@ -317,4 +476,140 @@ }

/* istanbul ignore next */
const _axios = axios || (globalThis && globalThis.axios);
function useAxios(config) {
const axiosClient = _axios.create(config);
const client = compositionApi.computed(() => axiosClient);
const use = usePromise(async (request) => {
return axiosClient.request(request);
});
const data = compositionApi.computed(() => (use.result.value && use.result.value.data) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.data) ||
null);
const status = compositionApi.computed(() => (use.result.value && use.result.value.status) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.status) ||
null);
const statusText = compositionApi.computed(() => (use.result.value && use.result.value.statusText) ||
(use.error.value &&
use.error.value.response &&
use.error.value.response.statusText) ||
null);
return {
...use,
client,
data,
status,
statusText
};
}
function useWebSocket(url, protocols) {
const ws = new WebSocket(url, protocols);
const messageEvent = compositionApi.ref(null);
const errorEvent = compositionApi.ref();
const data = compositionApi.ref(null);
const isOpen = compositionApi.ref(false);
const isClosed = compositionApi.ref(false);
const errored = compositionApi.ref(false);
ws.addEventListener("message", x => {
messageEvent.value = x;
data.value = x.data;
});
ws.addEventListener("error", error => {
errorEvent.value = error;
errored.value = true;
});
ws.addEventListener("close", () => {
isOpen.value = false;
isClosed.value = true;
});
ws.addEventListener("open", () => {
isOpen.value = true;
isClosed.value = false;
});
const send = (data) => ws.send(data);
const close = (code, reason) => {
ws.close(code, reason);
};
return {
ws,
send,
close,
messageEvent,
errorEvent,
data,
isOpen,
isClosed,
errored
};
}
// used to store all the instances of weakMap
const keyedMap = new Map();
const weakMap = new WeakMap();
function useLocalStorage(key, defaultValue) {
let lazy = false;
let k = keyedMap.get(key);
const json = localStorage.getItem(key);
const storage = (k && weakMap.get(k)) ||
(!!defaultValue && wrap(defaultValue)) ||
compositionApi.ref(null);
if (json && !k) {
try {
storage.value = JSON.parse(json);
lazy = false;
}
catch (e) {
/* istanbul ignore next */
console.warn("[useLocalStorage] error parsing value from localStorage", key, e);
}
}
// do not watch if we already created the instance
if (!k) {
k = {};
keyedMap.set(key, k);
weakMap.set(k, storage);
compositionApi.watch(storage, storage => {
if (storage === undefined) {
localStorage.removeItem(key);
return;
}
// do not overflow localStorage with updates nor keep doing stringify
debounce(() => localStorage.setItem(key, JSON.stringify(storage)), 100)();
}, {
deep: true,
lazy
});
}
const clear = () => {
keyedMap.forEach((v) => {
const obj = weakMap.get(v);
/* istanbul ignore else */
if (obj) {
obj.value = undefined;
}
weakMap.delete(v);
});
keyedMap.clear();
};
const remove = () => {
keyedMap.delete(key);
weakMap.delete(k);
storage.value = undefined;
};
return {
storage,
clear,
remove
};
}
exports.debounce = debounce;
exports.exponentialDelay = exponentialDelay;
exports.noDelay = noDelay;
exports.useArrayPagination = useArrayPagination;
exports.useAxios = useAxios;
exports.useCancellablePromise = useCancellablePromise;

@@ -324,3 +619,4 @@ exports.useDebounce = useDebounce;

exports.useFetch = useFetch;
exports.useMouseMove = useMouseMove;
exports.useLocalStorage = useLocalStorage;
exports.useOnMouseMove = useOnMouseMove;
exports.useOnResize = useOnResize;

@@ -330,2 +626,4 @@ exports.useOnScroll = useOnScroll;

exports.usePromise = usePromise;
exports.useRetry = useRetry;
exports.useWebSocket = useWebSocket;

@@ -332,0 +630,0 @@ Object.defineProperty(exports, '__esModule', { value: true });

@@ -0,0 +0,0 @@ 'use strict'

{
"name": "vue-composable",
"version": "0.1.0",
"version": "0.2.0",
"description": "Vue composition-api composable components",

@@ -27,3 +27,5 @@ "main": "index.js",

"test:watch": "jest --coverage --watch",
"test:prod": "npm run lint && npm run test -- --no-cache"
"test:prod": "npm run lint && npm run test -- --no-cache",
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
},

@@ -42,6 +44,11 @@ "bugs": {

"@vue/composition-api": "^0.3.2",
"@vuepress/plugin-back-to-top": "^1.2.0",
"@vuepress/plugin-pwa": "^1.2.0",
"axios": "^0.19.0",
"coveralls": "^3.0.7",
"jest": "^24.9.0",
"jest-junit": "^9.0.0",
"jest-websocket-mock": "^1.5.1",
"lodash.camelcase": "^4.3.0",
"mock-socket": "^9.0.2",
"rimraf": "^3.0.0",

@@ -55,7 +62,8 @@ "rollup": "^1.25.2",

"rollup-plugin-terser": "^5.1.2",
"rollup-plugin-typescript2": "^0.24.3",
"rollup-plugin-typescript2": "^0.25.1",
"ts-jest": "^24.1.0",
"typescript": "^3.6.4",
"vue": "^2.6.10"
"vue": "^2.6.10",
"vuepress": "^1.2.0"
}
}

@@ -5,2 +5,3 @@ # vue-composable

[![Coverage Status](https://coveralls.io/repos/github/pikax/vue-composable/badge.svg?branch=master)](https://coveralls.io/github/pikax/vue-composable?branch=master)
[![npm version](https://badge.fury.io/js/vue-composable.svg)](https://badge.fury.io/js/vue-composable)

@@ -11,27 +12,95 @@ ## Introduction

100% typescript based composable components and full type support out-of-box.
## NOTE
Currently only works with [composition-api](https://github.com/vuejs/composition-api), when [Vue3](https://github.com/vuejs/vue-next) gets release I will update to use the new reactive system (using [@vue/reactivity](https://github.com/vuejs/vue-next/tree/master/packages/reactivity))
## Installing
```bash
# install with yarn
yarn add @vue/composition-api vue-composable
| composable | description | example | extra |
|---|---|---|---|
| [useArrayPagination](src/arrayPagination.ts) | provides pagination for an array | [arrayPagination.html](examples/arrayPagination.html) | |
| [useDebounce](src/debounce.ts) | debounces function | | |
| [useEvent](src/event.ts) | handles the lifecycle of addEventListener/removeEventListener for a component. | | |
| [useOnMouseMove](src/onMouseMove.ts) | gets data from element `movemove` | | |
| [useOnResize](src/onResize.ts) | gets data from element `resize` | | |
| [useOnScroll](src/onScroll.ts) | gets data from element `scroll` | | |
| [usePagination](src/pagination.ts) | provides pagination controls. *NOTE: base type* | | |
| [usePromise](src/promise.ts) | provides information about the state of the promise | | |
| [useFetch](src/fetch.ts) | handles the fetch request | [fetch.html](examples/fetch.html) | |
# install with npm
npm install @vue/composition-api vue-composable
```
## Documentation
## Types
Check our [documentation](https://pikax.me/vue-composable/)
### Event
- [Event](https://pikax.me/vue-composable/composable/event/event) - Attach event listener to a DOM element
- [Mouse Move](https://pikax.me/vue-composable/composable/event/onMoveMove) - Attach `mousemove` listener to a DOM element
- [Resize](https://pikax.me/vue-composable/composable/event/onResize) - Attach `resize` listener to a DOM element
- [Scroll](https://pikax.me/vue-composable/composable/event/onScroll) - Attach `scroll` listener to a DOM element
### MISC
## License
- [localStorage](https://pikax.me/vue-composable/composable/misc/localStorage) - Reactive access to a `localStorage`
### Pagination
- [Pagination](https://pikax.me/vue-composable/composable/pagination/pagination) - Generic reactive pagination controls
- [Array Pagination](https://pikax.me/vue-composable/composable/pagination/arrayPagination) - Array pagination
### Promise
- [Promise](https://pikax.me/vue-composable/composable/promise/promise) - `Promise` reactive resolve and reject
- [Cancellable Promise](https://pikax.me/vue-composable/composable/promise/cancellablePromise) - Allow to cancel promises
- [Retry](https://pikax.me/vue-composable/composable/promise/retry) - Provides functionality to retry `promise`
### Web
- [Axios](https://pikax.me/vue-composable/composable/web/axios) - reactive `axios` wrapper client
- [Fetch](https://pikax.me/vue-composable/composable/web/fetch) - reactive `fetch` wrapper
- [WebSocket](https://pikax.me/vue-composable/composable/web/webSocket) - reactive `WebSocket` wrapper
## Examples
Check out the [examples folder](examples) or start hacking on [codesandbox](https://codesandbox.io/s/vue-composable-examples-yuusf).
[![Edit Vue Composable Examples](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/vue-template-yuusf?fontsize=14)
### NOTE
Currently only works with [composition-api](https://github.com/vuejs/composition-api), when [Vue3](https://github.com/vuejs/vue-next) gets release I will update to use the new reactive system (using [@vue/reactivity](https://github.com/vuejs/vue-next/tree/master/packages/reactivity))
## Usage
```vue
<template>
<div>
<p>page {{ currentPage }} of {{ lastPage }}</p>
<p>
<button @click="prev">prev</button>
<button @click="next">next</button>
</p>
<ul>
<li v-for="n in result" :key="n">
{{ n }}
</li>
</ul>
</div>
</template>
<script>
import { useArrayPagination } from "vue-composable";
export default {
setup() {
const array = new Array(1000).fill(0).map((_, i) => i);
const { result, next, prev, currentPage, lastPage } = useArrayPagination(
array,
{
pageSize: 3
}
);
return { result, next, prev, currentPage, lastPage };
}
};
</script>
```
## License
[MIT](http://opensource.org/licenses/MIT)

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc