Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
db18c010aa | |||
b292d5ed17 | |||
a1ea8cdf67 | |||
fbbe3c99ef | |||
1c1a3f6a6e |
12
.npmignore
Normal file
12
.npmignore
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
src
|
||||||
|
tests
|
||||||
|
.editorconfig
|
||||||
|
.gitignore
|
||||||
|
.gitmodules
|
||||||
|
.npmignore
|
||||||
|
CODEOWNERS
|
||||||
|
Dockerfile
|
||||||
|
index.html
|
||||||
|
jest.config.js
|
||||||
|
tsconfig.json
|
||||||
|
vite.config.js
|
14
index.html
Normal file
14
index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!Doctype html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>@ztimson/utils sandbox</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="module">
|
||||||
|
import {PathEvent} from './dist/index.mjs';
|
||||||
|
|
||||||
|
console.log(PathEvent.has(['data/Inventory:n', 'logs:c'], `logs/client:n`));
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ztimson/utils",
|
"name": "@ztimson/utils",
|
||||||
"version": "0.20.12",
|
"version": "0.21.6",
|
||||||
"description": "Utility library",
|
"description": "Utility library",
|
||||||
"author": "Zak Timson",
|
"author": "Zak Timson",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
17
src/aset.ts
17
src/aset.ts
@ -1,3 +1,5 @@
|
|||||||
|
import {isEqual} from './objects.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array which functions as a set. It guarantees unique elements
|
* An array which functions as a set. It guarantees unique elements
|
||||||
* and provides set functions for comparisons
|
* and provides set functions for comparisons
|
||||||
@ -24,6 +26,7 @@ export class ASet<T> extends Array {
|
|||||||
*/
|
*/
|
||||||
add(...items: T[]) {
|
add(...items: T[]) {
|
||||||
items.filter(el => !this.has(el)).forEach(el => this.push(el));
|
items.filter(el => !this.has(el)).forEach(el => this.push(el));
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,8 +36,9 @@ export class ASet<T> extends Array {
|
|||||||
delete(...items: T[]) {
|
delete(...items: T[]) {
|
||||||
items.forEach(el => {
|
items.forEach(el => {
|
||||||
const index = this.indexOf(el);
|
const index = this.indexOf(el);
|
||||||
if(index != -1) this.slice(index, 1);
|
if(index != -1) this.splice(index, 1);
|
||||||
})
|
})
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,6 +59,17 @@ export class ASet<T> extends Array {
|
|||||||
return this.indexOf(el) != -1;
|
return this.indexOf(el) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find index number of element, or -1 if it doesn't exist. Matches by equality not reference
|
||||||
|
*
|
||||||
|
* @param {T} search Element to find
|
||||||
|
* @param {number} fromIndex Starting index position
|
||||||
|
* @return {number} Element index number or -1 if missing
|
||||||
|
*/
|
||||||
|
indexOf(search: T, fromIndex?: number): number {
|
||||||
|
return super.findIndex((el: T) => isEqual(el, search), fromIndex);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create list of elements this set has in common with the comparison set
|
* Create list of elements this set has in common with the comparison set
|
||||||
* @param {ASet<T>} set Set to compare against
|
* @param {ASet<T>} set Set to compare against
|
||||||
|
@ -180,7 +180,6 @@ export class GatewayTimeoutError extends CustomError {
|
|||||||
* @return {CustomError} The proper error type
|
* @return {CustomError} The proper error type
|
||||||
*/
|
*/
|
||||||
export function errorFromCode(code: number, message?: string) {
|
export function errorFromCode(code: number, message?: string) {
|
||||||
if(code >= 200 && code < 300) return null;
|
|
||||||
switch(code) {
|
switch(code) {
|
||||||
case 400:
|
case 400:
|
||||||
return new BadRequestError(message);
|
return new BadRequestError(message);
|
||||||
|
@ -69,19 +69,26 @@ export class PathEvent {
|
|||||||
/** Last sagment of path */
|
/** Last sagment of path */
|
||||||
name!: string;
|
name!: string;
|
||||||
/** List of methods */
|
/** List of methods */
|
||||||
methods!: Method[];
|
methods!: ASet<Method>;
|
||||||
|
|
||||||
/** All/Wildcard specified */
|
/** All/Wildcard specified */
|
||||||
all!: boolean;
|
get all(): boolean { return this.methods.has('*') }
|
||||||
|
set all(v: boolean) { v ? new ASet<Method>(['*']) : this.methods.delete('*'); }
|
||||||
/** None specified */
|
/** None specified */
|
||||||
none!: boolean;
|
get none(): boolean { return this.methods.has('n') }
|
||||||
|
set none(v: boolean) { v ? this.methods = new ASet<Method>(['n']) : this.methods.delete('n'); }
|
||||||
/** Create method specified */
|
/** Create method specified */
|
||||||
create!: boolean;
|
get create(): boolean { return !this.methods.has('n') && (this.methods.has('*') || this.methods.has('c')) }
|
||||||
|
set create(v: boolean) { v ? this.methods.delete('n').add('c') : this.methods.delete('c'); }
|
||||||
/** Read method specified */
|
/** Read method specified */
|
||||||
read!: boolean;
|
get read(): boolean { return !this.methods.has('n') && (this.methods.has('*') || this.methods.has('r')) }
|
||||||
|
set read(v: boolean) { v ? this.methods.delete('n').add('r') : this.methods.delete('r'); }
|
||||||
/** Update method specified */
|
/** Update method specified */
|
||||||
update!: boolean;
|
get update(): boolean { return !this.methods.has('n') && (this.methods.has('*') || this.methods.has('u')) }
|
||||||
|
set update(v: boolean) { v ? this.methods.delete('n').add('u') : this.methods.delete('u'); }
|
||||||
/** Delete method specified */
|
/** Delete method specified */
|
||||||
delete!: boolean;
|
get delete(): boolean { return !this.methods.has('n') && (this.methods.has('*') || this.methods.has('d')) }
|
||||||
|
set delete(v: boolean) { v ? this.methods.delete('n').add('d') : this.methods.delete('d'); }
|
||||||
|
|
||||||
constructor(Event: string | PathEvent) {
|
constructor(Event: string | PathEvent) {
|
||||||
if(typeof Event == 'object') return Object.assign(this, Event);
|
if(typeof Event == 'object') return Object.assign(this, Event);
|
||||||
@ -96,13 +103,7 @@ export class PathEvent {
|
|||||||
this.fullPath = p;
|
this.fullPath = p;
|
||||||
this.path = temp.join('/');
|
this.path = temp.join('/');
|
||||||
this.name = temp.pop() || '';
|
this.name = temp.pop() || '';
|
||||||
this.methods = <Method[]>method.split('');
|
this.methods = new ASet(<any>method.split(''));
|
||||||
this.all = method?.includes('*');
|
|
||||||
this.none = method?.includes('n');
|
|
||||||
this.create = !method?.includes('n') && (method?.includes('*') || method?.includes('w') || method?.includes('c'));
|
|
||||||
this.read = !method?.includes('n') && (method?.includes('*') || method?.includes('r'));
|
|
||||||
this.update = !method?.includes('n') && (method?.includes('*') || method?.includes('w') || method?.includes('u'));
|
|
||||||
this.delete = !method?.includes('n') && (method?.includes('*') || method?.includes('w') || method?.includes('d'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,7 +113,7 @@ export class PathEvent {
|
|||||||
* @param {string | PathEvent} paths Events as strings or pre-parsed
|
* @param {string | PathEvent} paths Events as strings or pre-parsed
|
||||||
* @return {PathEvent} Final combined permission
|
* @return {PathEvent} Final combined permission
|
||||||
*/
|
*/
|
||||||
static combine(paths: (string | PathEvent)[]): PathEvent {
|
static combine(...paths: (string | PathEvent)[]): PathEvent {
|
||||||
let hitNone = false;
|
let hitNone = false;
|
||||||
const combined = paths.map(p => new PathEvent(p))
|
const combined = paths.map(p => new PathEvent(p))
|
||||||
.toSorted((p1, p2) => {
|
.toSorted((p1, p2) => {
|
||||||
@ -122,21 +123,30 @@ export class PathEvent {
|
|||||||
if(p.none) hitNone = true;
|
if(p.none) hitNone = true;
|
||||||
if(!acc) return p;
|
if(!acc) return p;
|
||||||
if(hitNone) return acc;
|
if(hitNone) return acc;
|
||||||
if(p.all) acc.all = true;
|
|
||||||
if(p.all || p.create) acc.create = true;
|
|
||||||
if(p.all || p.read) acc.read = true;
|
|
||||||
if(p.all || p.update) acc.update = true;
|
|
||||||
if(p.all || p.delete) acc.delete = true;
|
|
||||||
acc.methods = [...acc.methods, ...p.methods];
|
acc.methods = [...acc.methods, ...p.methods];
|
||||||
return acc;
|
return acc;
|
||||||
}, <any>null);
|
}, <any>null);
|
||||||
if(combined.all) combined.methods = ['*'];
|
combined.methods = new ASet<Method>(combined.methods);
|
||||||
if(combined.none) combined.methods = ['n'];
|
|
||||||
combined.methods = new ASet(combined.methods); // Make unique
|
|
||||||
combined.raw = PES`${combined.fullPath}:${combined.methods}`;
|
|
||||||
return combined;
|
return combined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter a set of paths based on the target
|
||||||
|
*
|
||||||
|
* @param {string | PathEvent | (string | PathEvent)[]} target Array of events that will filtered
|
||||||
|
* @param filter {...PathEvent} Must container one of
|
||||||
|
* @return {boolean} Whether there is any overlap
|
||||||
|
*/
|
||||||
|
static filter(target: string | PathEvent | (string | PathEvent)[], ...filter: (string | PathEvent)[]): PathEvent[] {
|
||||||
|
const parsedTarget = makeArray(target).map(pe => new PathEvent(pe));
|
||||||
|
const parsedFind = makeArray(filter).map(pe => new PathEvent(pe));
|
||||||
|
return parsedTarget.filter(t => {
|
||||||
|
if(!t.fullPath && t.all) return true;
|
||||||
|
return !!parsedFind.find(f => t.fullPath.startsWith(f.fullPath)
|
||||||
|
&& (f.all || t.all || t.methods.intersection(f.methods).length));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Squash 2 sets of paths & return true if any overlap is found
|
* Squash 2 sets of paths & return true if any overlap is found
|
||||||
*
|
*
|
||||||
@ -151,8 +161,8 @@ export class PathEvent {
|
|||||||
if(!r.fullPath && r.all) return true;
|
if(!r.fullPath && r.all) return true;
|
||||||
const filtered = parsedTarget.filter(p => r.fullPath.startsWith(p.fullPath));
|
const filtered = parsedTarget.filter(p => r.fullPath.startsWith(p.fullPath));
|
||||||
if(!filtered.length) return false;
|
if(!filtered.length) return false;
|
||||||
const combined = PathEvent.combine(filtered);
|
const combined = PathEvent.combine(...filtered);
|
||||||
return !combined.none && (combined.all || new ASet(combined.methods).intersection(new ASet(r.methods)).length);
|
return (!combined.none && (combined.all || r.all)) || combined.methods.intersection(r.methods).length;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,8 +206,19 @@ export class PathEvent {
|
|||||||
*/
|
*/
|
||||||
static toString(path: string | string[], methods: Method | Method[]): string {
|
static toString(path: string | string[], methods: Method | Method[]): string {
|
||||||
let p = makeArray(path).filter(p => p != null).join('/');
|
let p = makeArray(path).filter(p => p != null).join('/');
|
||||||
|
p = p?.trim().replaceAll(/\/{2,}/g, '/').replaceAll(/(^\/|\/$)/g, '');
|
||||||
if(methods?.length) p += `:${makeArray(methods).map(m => m.toLowerCase()).join('')}`;
|
if(methods?.length) p += `:${makeArray(methods).map(m => m.toLowerCase()).join('')}`;
|
||||||
return p?.trim().replaceAll(/\/{2,}/g, '/').replaceAll(/(^\/|\/$)/g, '');
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter a set of paths based on this event
|
||||||
|
*
|
||||||
|
* @param {string | PathEvent | (string | PathEvent)[]} target Array of events that will filtered
|
||||||
|
* @return {boolean} Whether there is any overlap
|
||||||
|
*/
|
||||||
|
filter(target: string | PathEvent | (string | PathEvent)[]): PathEvent[] {
|
||||||
|
return PathEvent.filter(target, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
22
src/time.ts
22
src/time.ts
@ -1,3 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Like setInterval but will adjust the timeout value to account for runtime
|
||||||
|
* @param {Function} cb Callback function that will be ran
|
||||||
|
* @param {number} ms Run function ever x seconds
|
||||||
|
* @return {() => void}
|
||||||
|
*/
|
||||||
|
export function adjustedInterval(cb: Function, ms: number) {
|
||||||
|
let cancel = false, timeout: any = null;
|
||||||
|
const p = async () => {
|
||||||
|
if (cancel) return;
|
||||||
|
const start = new Date().getTime();
|
||||||
|
await cb();
|
||||||
|
const end = new Date().getTime();
|
||||||
|
timeout = setTimeout(() => p(), ms - (end - start) || 1);
|
||||||
|
};
|
||||||
|
p();
|
||||||
|
return () => {
|
||||||
|
cancel = true;
|
||||||
|
if(timeout) clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return date formated highest to lowest: YYYY-MM-DD H:mm AM
|
* Return date formated highest to lowest: YYYY-MM-DD H:mm AM
|
||||||
*
|
*
|
||||||
|
@ -13,7 +13,7 @@ export default defineConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
minify: true,
|
minify: false,
|
||||||
sourcemap: true
|
sourcemap: true
|
||||||
},
|
},
|
||||||
plugins: [dts()],
|
plugins: [dts()],
|
||||||
|
Reference in New Issue
Block a user