Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
b7aeee4706 | |||
b3eab0d6c9 | |||
ad4194a981 | |||
e1f22a01a6 | |||
91c0858d9f | |||
8094b6507f | |||
e40f410830 | |||
c1043e65e2 | |||
67d9928a61 | |||
e6636d373b | |||
811d797e1b | |||
0909c4f648 |
10
.github/workflows/build.yaml
vendored
10
.github/workflows/build.yaml
vendored
@ -48,3 +48,13 @@ jobs:
|
||||
uses: ztimson/actions/tag@develop
|
||||
with:
|
||||
tag: ${{env.VERSION}}
|
||||
|
||||
|
||||
docs:
|
||||
name: Publish Documentation
|
||||
needs: build
|
||||
uses: ztimson/actions/.github/workflows/docker.yaml@develop
|
||||
with:
|
||||
name: ztimson/utils
|
||||
repository: ${{github.server_url}}/${{github.repository}}.git
|
||||
pass: ${{secrets.DEPLOY_TOKEN}}
|
||||
|
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@ -0,0 +1,13 @@
|
||||
# Build application
|
||||
FROM node:alpine as build
|
||||
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN if [ ! -d "node_modules" ]; then npm i; fi && \
|
||||
if [ ! -d "dist" ]; then npm run docs; fi
|
||||
|
||||
# Use Nginx to serve
|
||||
FROM nginx:1.23-alpine
|
||||
|
||||
COPY --from=build /app/docs /usr/share/nginx/html
|
1655
package-lock.json
generated
1655
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ztimson/utils",
|
||||
"version": "0.16.3",
|
||||
"version": "0.17.0",
|
||||
"description": "Utility library",
|
||||
"author": "Zak Timson",
|
||||
"license": "MIT",
|
||||
@ -21,7 +21,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npx tsc && npx vite build",
|
||||
"docs": "typedoc --plugin typedoc-plugin-markdown --cleanOutputDir false --outputFileStrategy modules --hidePageHeader --out ./docs --entryPoints src/**/*.ts --readme none --entryFileName Home",
|
||||
"docs": "typedoc --cleanOutputDir false --out ./docs --entryPoints src/**/*.ts --readme none",
|
||||
"test": "npx jest",
|
||||
"test:coverage": "npx jest --coverage",
|
||||
"watch": "npx vite build --watch"
|
||||
@ -32,12 +32,14 @@
|
||||
"jest-junit": "^16.0.0",
|
||||
"ts-jest": "^29.1.2",
|
||||
"typedoc": "^0.26.7",
|
||||
"typedoc-plugin-markdown": "^4.2.7",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.0.12",
|
||||
"vite-plugin-dts": "^3.7.2"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
],
|
||||
"dependencies": {
|
||||
"var-persist": "^1.0.1"
|
||||
}
|
||||
}
|
||||
|
80
src/cache.ts
80
src/cache.ts
@ -1,8 +1,11 @@
|
||||
/**
|
||||
* Map of data which tracks whether it is a complete collection & offers optional expiry of cached values
|
||||
*/
|
||||
export class Cache<K, T> extends Map<K, T> {
|
||||
export class Cache<K extends string | number | symbol, T> {
|
||||
private store = <Record<K, T>>{};
|
||||
|
||||
/** Support index lookups */
|
||||
[key: string | number | symbol]: T | any;
|
||||
/** Whether cache is complete */
|
||||
complete = false;
|
||||
|
||||
@ -12,11 +15,22 @@ export class Cache<K, T> extends Map<K, T> {
|
||||
* @param {keyof T} key Default property to use as primary key
|
||||
* @param {number} ttl Default expiry in milliseconds
|
||||
*/
|
||||
constructor(public readonly key: keyof T, public ttl?: number) {
|
||||
super();
|
||||
constructor(public readonly key?: keyof T, public ttl?: number) {
|
||||
return new Proxy(this, {
|
||||
get: (target: this, prop: string | symbol) => {
|
||||
if (prop in target) return (target as any)[prop];
|
||||
return target.store[prop as K];
|
||||
},
|
||||
set: (target: this, prop: string | symbol, value: any) => {
|
||||
if (prop in target) (target as any)[prop] = value;
|
||||
else target.store[prop as K] = value;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getKey(value: T): K {
|
||||
if(!this.key) throw new Error('No key defined');
|
||||
return <K>value[this.key];
|
||||
}
|
||||
|
||||
@ -25,8 +39,8 @@ export class Cache<K, T> extends Map<K, T> {
|
||||
*
|
||||
* @return {T[]} Array of items
|
||||
*/
|
||||
all() {
|
||||
return Array.from(this.values());
|
||||
all(): T[] {
|
||||
return Object.values(this.store);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,19 +69,71 @@ export class Cache<K, T> extends Map<K, T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an item from the cache
|
||||
*
|
||||
* @param {K} key Item's primary key
|
||||
*/
|
||||
delete(key: K) {
|
||||
delete this.store[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return cache as an array of key-value pairs
|
||||
* @return {[K, T][]} Key-value pairs array
|
||||
*/
|
||||
entries(): [K, T][] {
|
||||
return <[K, T][]>Object.entries(this.store);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item from the cache
|
||||
* @param {K} key Key to lookup
|
||||
* @return {T} Cached item
|
||||
*/
|
||||
get(key: K): T {
|
||||
return this.store[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of cached keys
|
||||
*
|
||||
* @return {K[]} Array of keys
|
||||
*/
|
||||
keys(): K[] {
|
||||
return <K[]>Object.keys(this.store);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get map of cached items
|
||||
*
|
||||
* @return {Record<K, T>}
|
||||
*/
|
||||
map(): Record<K, T> {
|
||||
return structuredClone(this.store);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item to the cache manually specifying the key
|
||||
*
|
||||
* @param {K} key Key item will be cached under
|
||||
* @param {T} value Item to cache
|
||||
* @param {number | undefined} ttl Override default expiry
|
||||
* @return {this}
|
||||
*/
|
||||
set(key: K, value: T, ttl = this.ttl): this {
|
||||
super.set(key, value);
|
||||
this.store[key] = value;
|
||||
if(ttl) setTimeout(() => {
|
||||
this.complete = false;
|
||||
super.delete(key)
|
||||
this.delete(key);
|
||||
}, ttl);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all cached items
|
||||
*
|
||||
* @return {T[]} Array of items
|
||||
*/
|
||||
values = this.all();
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
export type Listener = (...args: any[]) => any;
|
||||
export type TypedEvents = {[k in string | symbol]: Listener} & {'*': (event: string, ...args: any[]) => any};
|
||||
|
||||
export type NamespaceEvents<Namespace extends string, Events extends TypedEvents> = {
|
||||
[K in keyof Events as `${Namespace}:${Extract<K, string>}`]: Events[K];
|
||||
};
|
||||
|
||||
export class TypedEmitter<T extends TypedEvents = TypedEvents> {
|
||||
private static listeners: {[key: string]: Listener[]} = {};
|
||||
|
||||
|
@ -6,6 +6,7 @@ export * from './files';
|
||||
export * from './emitter';
|
||||
export * from './errors';
|
||||
export * from './http';
|
||||
export * from './jwt';
|
||||
export * from './logger';
|
||||
export * from './math';
|
||||
export * from './misc';
|
||||
@ -14,3 +15,4 @@ export * from './promise-progress';
|
||||
export * from './string';
|
||||
export * from './time';
|
||||
export * from './types';
|
||||
export * from 'var-persist';
|
||||
|
15
src/jwt.ts
Normal file
15
src/jwt.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import {JSONAttemptParse} from './objects.ts';
|
||||
|
||||
/**
|
||||
* Decode a JWT payload, this will not check for tampering so be careful
|
||||
*
|
||||
* @param {string} token JWT to decode
|
||||
* @return {unknown} JWT payload
|
||||
*/
|
||||
export function jwtDecode<T>(token: string): T {
|
||||
const base64 = token.split('.')[1]
|
||||
.replace(/-/g, '+').replace(/_/g, '/');
|
||||
return <T>JSONAttemptParse(decodeURIComponent(atob(base64).split('').map(function(c) {
|
||||
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||
}).join('')));
|
||||
}
|
Reference in New Issue
Block a user