Compare commits

..

6 Commits

Author SHA1 Message Date
19251244d2 sleepUntil async support
All checks were successful
Build / Build NPM Project (push) Successful in 39s
Build / Tag Version (push) Successful in 8s
2024-09-26 23:45:46 -04:00
51549db3d9 Updated download functions & added CSV serializer
All checks were successful
Build / Build NPM Project (push) Successful in 25s
Build / Tag Version (push) Successful in 6s
2024-09-22 03:35:12 -04:00
3bf8c7bd09 Removed deep copy tests since it was replaced with a native function
All checks were successful
Build / Build NPM Project (push) Successful in 43s
Build / Tag Version (push) Successful in 6s
2024-09-22 03:13:22 -04:00
e8e56a3f72 Fixed doc output
Some checks failed
Build / Build NPM Project (push) Failing after 21s
Build / Tag Version (push) Has been skipped
2024-09-22 03:09:52 -04:00
98f867ed4e Updated readme 2024-09-22 02:50:26 -04:00
94cea19728 Updated docs submodule 2024-09-22 02:44:36 -04:00
9 changed files with 68 additions and 58 deletions

2
.gitmodules vendored
View File

@ -2,4 +2,4 @@
path = docs path = docs
url = git@git.zakscode.com:ztimson/utils.wiki.git url = git@git.zakscode.com:ztimson/utils.wiki.git
branch = master branch = master
ignore = any ignore = all

View File

@ -20,9 +20,10 @@ Javascript/Typescript Utilities
--- ---
<div> <div>
<a href="https://git.zakscode.com/ztimson/persist/releases" target="_blank">Release Notes</a> <a href="https://git.zakscode.com/ztimson/utils/wiki" target="_blank">Documentation</a>
<a href="https://git.zakscode.com/ztimson/persist/issues/new?template=.github%2fissue_template%2fbug.md" target="_blank">Report a Bug</a> <a href="https://git.zakscode.com/ztimson/utils/releases" target="_blank">Release Notes</a>
<a href="https://git.zakscode.com/ztimson/persist/issues/new?template=.github%2fissue_template%2fenhancement.md" target="_blank">Request a Feature</a> <a href="https://git.zakscode.com/ztimson/utils/issues/new?template=.github%2fissue_template%2fbug.md" target="_blank">Report a Bug</a>
<a href="https://git.zakscode.com/ztimson/utils/issues/new?template=.github%2fissue_template%2fenhancement.md" target="_blank">Request a Feature</a>
</div> </div>
--- ---

View File

@ -1,6 +1,6 @@
{ {
"name": "@ztimson/utils", "name": "@ztimson/utils",
"version": "0.16.0", "version": "0.16.2",
"description": "Utility library", "description": "Utility library",
"author": "Zak Timson", "author": "Zak Timson",
"license": "MIT", "license": "MIT",
@ -21,7 +21,7 @@
}, },
"scripts": { "scripts": {
"build": "npx tsc && npx vite build", "build": "npx tsc && npx vite build",
"docs": "typedoc --plugin typedoc-plugin-markdown --hidePageHeader --out ./docs --entryPoints src/**/*.ts", "docs": "typedoc --plugin typedoc-plugin-markdown --cleanOutputDir false --outputFileStrategy modules --hidePageHeader --out ./docs --entryPoints src/**/*.ts --readme none --entryFileName Home",
"test": "npx jest", "test": "npx jest",
"test:coverage": "npx jest --coverage", "test:coverage": "npx jest --coverage",
"watch": "npx vite build --watch" "watch": "npx vite build --watch"

26
src/csv.ts Normal file
View File

@ -0,0 +1,26 @@
import {dotNotation, flattenObj} from './objects.ts';
/**
* Convert an object to a CSV string
*
* @param {any[]} target Array of objects to create CSV from
* @param {boolean} flatten Should nested object be flattened or treated as values
* @return {string} CSV string
*/
export function csv(target: any[], flatten=true) {
const headers = target.reduce((acc, row) => {
Object.keys(flatten ? flattenObj(row) : row)
.forEach(key => { if(!acc.includes(key)) acc.push(key); });
return acc;
}, []);
return [
headers.join(','),
...target.map(row => headers.map((h: string) => {
const value = dotNotation<any>(row, h);
const type = typeof value;
if(type == 'string' && value.includes(',')) return `"${value}"`;
if(type == 'object') return `"${JSON.stringify(value)}"`;
return value;
}).join(','))
].join('\n');
}

View File

@ -1,33 +1,35 @@
import {makeArray} from './array.ts';
import {JSONAttemptParse} from './objects.ts'; import {JSONAttemptParse} from './objects.ts';
import {PromiseProgress} from './promise-progress'; import {PromiseProgress} from './promise-progress';
/**
* Download a file from a URL
*
* @param href URL that will be downloaded
* @param {string} name Override download name
*/
export function download(href: any, name?: string) {
const a = document.createElement('a');
a.href = href;
a.download = name || href.split('/').pop();
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
/** /**
* Download blob as a file * Download blob as a file
* *
* @param {Blob} blob File as a blob * @param {Blob} blob File as a blob
* @param {string} name Name blob will be downloaded as * @param {string} name Name blob will be downloaded as
*/ */
export function downloadBlob(blob: Blob, name: string) { export function downloadFile(blob: Blob | string | string[], name: string) {
if(!(blob instanceof Blob)) blob = new Blob(makeArray(blob));
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
download(url, name); downloadUrl(url, name);
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
/**
* Download a file from a URL
*
* @param href URL that will be downloaded
* @param {string} name Override download name
*/
export function downloadUrl(href: any, name?: string) {
const a = document.createElement('a');
a.href = href;
a.download = name || href.split('/').pop();
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
/** /**
* Open filebrowser & return selected file * Open filebrowser & return selected file
* *

View File

@ -1,5 +1,6 @@
export * from './array'; export * from './array';
export * from './aset'; export * from './aset';
export * from './csv';
export * from './files'; export * from './files';
export * from './emitter'; export * from './emitter';
export * from './errors'; export * from './errors';

View File

@ -1,3 +1,5 @@
import {dotNotation, flattenObj} from './objects.ts';
/** /**
* String of all letters * String of all letters
*/ */
@ -18,16 +20,6 @@ const SYMBOL_LIST = '~`!@#$%^&*()_-+={[}]|\\:;"\'<,>.?/';
*/ */
const CHAR_LIST = LETTER_LIST + NUMBER_LIST + SYMBOL_LIST; const CHAR_LIST = LETTER_LIST + NUMBER_LIST + SYMBOL_LIST;
/**
* Generate a random hexadecimal value
*
* @param {number} length Number of hexadecimal place values
* @return {string} Hexadecimal number as a string
*/
export function randomHex(length: number) {
return Array(length).fill(null).map(() => Math.round(Math.random() * 0xF).toString(16)).join('');
}
/** /**
* Convert number of bytes into a human-readable size * Convert number of bytes into a human-readable size
* *
@ -95,6 +87,16 @@ export function pad(text: any, length: number, char: string = ' ', start = true)
return text.toString().padEnd(length, char); return text.toString().padEnd(length, char);
} }
/**
* Generate a random hexadecimal value
*
* @param {number} length Number of hexadecimal place values
* @return {string} Hexadecimal number as a string
*/
export function randomHex(length: number) {
return Array(length).fill(null).map(() => Math.round(Math.random() * 0xF).toString(16)).join('');
}
/** /**
* Generate a string of random characters. * Generate a string of random characters.
* *

View File

@ -39,12 +39,12 @@ export function sleep(ms: number): Promise<void> {
* await sleepUntil(() => loading); // Won't continue until loading flag is false * await sleepUntil(() => loading); // Won't continue until loading flag is false
* ``` * ```
* *
* @param {() => boolean} fn Return true to continue * @param {() => boolean | Promise<boolean>} fn Return true to continue
* @param {number} checkInterval Run function ever x milliseconds * @param {number} checkInterval Run function ever x milliseconds
* @return {Promise<void>} Callback when sleep is over * @return {Promise<void>} Callback when sleep is over
*/ */
export async function sleepUntil(fn : () => boolean, checkInterval=100): Promise<void> { export async function sleepUntil(fn : () => boolean | Promise<boolean>, checkInterval = 100): Promise<void> {
while(fn()) await sleep(checkInterval); while(await fn()) await sleep(checkInterval);
} }
/** /**

View File

@ -29,28 +29,6 @@ describe('Object Utilities', () => {
}); });
}); });
describe('deepCopy', () => {
const copy = deepCopy(TEST_OBJECT);
test('Array of arrays', () => {
const a = [[1, 2], [3, 4]];
const b = deepCopy(a);
b[0][1] = 5;
expect(a).not.toEqual(b);
});
test('Change array inside object', () => {
copy.b[1] = [1, 1, 1];
expect(copy.b[1]).not.toEqual(TEST_OBJECT.b[1]);
});
test('Change object inside object', () => {
copy.g = {h: Math.random()};
expect(copy.g).not.toEqual(TEST_OBJECT.g);
});
test('Change object property inside nested array', () => {
copy.c.d[0][0].e = -1;
expect(copy.c.d[0][0].e).not.toEqual(TEST_OBJECT.c.d[0][0].e);
});
});
describe('dotNotation', () => { describe('dotNotation', () => {
test('no object or properties', () => { test('no object or properties', () => {
expect(dotNotation(undefined, 'z')).toStrictEqual(undefined); expect(dotNotation(undefined, 'z')).toStrictEqual(undefined);