@tkeron/tools 0.2.2 → 0.4.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.
@@ -4,22 +4,26 @@ on:
4
4
  branches:
5
5
  - main
6
6
 
7
+ permissions:
8
+ id-token: write
9
+ contents: read
10
+
7
11
  jobs:
8
12
  build-test-publish:
9
13
  runs-on: ubuntu-latest
14
+
10
15
  steps:
11
16
  - uses: actions/checkout@v4
12
17
 
13
18
  - uses: actions/setup-node@v4
14
19
  with:
15
- node-version: 22.x
16
- registry-url: "https://registry.npmjs.org/"
20
+ node-version: "lts/*"
21
+ registry-url: "https://registry.npmjs.org"
17
22
 
18
23
  - uses: oven-sh/setup-bun@v2
19
24
 
20
- - run: |
21
- bun i
22
- bun test
23
- npm publish --access public
24
- env:
25
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
25
+ - run: npm install -g npm@latest
26
+ - run: bun i
27
+ - run: bun run test
28
+
29
+ - run: npm publish --provenance
package/bun.lock CHANGED
@@ -1,28 +1,28 @@
1
1
  {
2
2
  "lockfileVersion": 1,
3
+ "configVersion": 0,
3
4
  "workspaces": {
4
5
  "": {
5
6
  "name": "@tkeron/tools",
6
7
  "devDependencies": {
7
- "@types/bun": "latest",
8
+ "@types/bun": "^1.3.9",
9
+ "prettier": "^3.8.1",
8
10
  },
9
11
  "peerDependencies": {
10
- "typescript": "^5.7.3",
12
+ "typescript": "^5.9.3",
11
13
  },
12
14
  },
13
15
  },
14
16
  "packages": {
15
- "@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="],
17
+ "@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
16
18
 
17
19
  "@types/node": ["@types/node@22.13.1", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew=="],
18
20
 
19
- "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],
21
+ "bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
20
22
 
21
- "bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="],
23
+ "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
22
24
 
23
- "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
24
-
25
- "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
25
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
26
26
 
27
27
  "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
28
28
  }
package/changelog.md ADDED
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ ## [0.4.0] - 2026-02-15
4
+
5
+ ### Added
6
+
7
+ - **Logger system**: Generic logging abstraction for standardized logging across the ecosystem
8
+ - `Logger` interface: Standard interface with `log`, `error`, `warn`, `info` methods
9
+ - `logger`: Default implementation using console methods
10
+ - `silentLogger`: No-op logger for silent operation (useful in production or tests)
11
+ - `createTestLogger()`: Captures logs in arrays for testing and verification
12
+ - Full TypeScript support with exported types
13
+ - Comprehensive test coverage (45 tests across 3 test files)
14
+
15
+ ### Documentation
16
+
17
+ - Logger system fully tested with edge cases (null, undefined, Error objects, mixed types)
18
+ - Test logger supports independent instances and manual array manipulation
19
+
20
+ ## [0.3.0] - 2026-02-15
21
+
22
+ ### Added
23
+
24
+ - Configured trusted publisher with OIDC for npm deployment
25
+ - Added `lint` script using Prettier for code formatting
26
+ - Added Prettier as devDependency
27
+
28
+ ### Changed
29
+
30
+ - Updated npm deployment workflow to use OIDC authentication instead of NPM_TOKEN
31
+ - Standardized repository URL format in package.json
32
+ - Code formatting applied across all source and test files
33
+
34
+ ### Infrastructure
35
+
36
+ - GitHub Actions now uses `permissions: id-token: write` for secure publishing
37
+ - npm publish now includes `--provenance` flag for supply chain security
38
+ - Node.js version standardized to `lts/*` in CI workflow
39
+
40
+ ## [0.2.2] - Previous releases
41
+
42
+ (Previous version history not documented)
package/package.json CHANGED
@@ -1,20 +1,25 @@
1
1
  {
2
2
  "name": "@tkeron/tools",
3
- "version": "0.2.2",
3
+ "version": "0.4.0",
4
4
  "description": "Useful JavaScript utilities for Bun runtime",
5
5
  "main": "src/index.ts",
6
6
  "module": "src/index.ts",
7
7
  "type": "module",
8
8
  "author": "tkeron",
9
9
  "license": "MIT",
10
+ "scripts": {
11
+ "test": "bun test --concurrent",
12
+ "lint": "prettier --write ."
13
+ },
10
14
  "engines": {
11
15
  "bun": ">=1.0.0"
12
16
  },
13
17
  "devDependencies": {
14
- "@types/bun": "latest"
18
+ "@types/bun": "^1.3.9",
19
+ "prettier": "^3.8.1"
15
20
  },
16
21
  "peerDependencies": {
17
- "typescript": "^5.7.3"
22
+ "typescript": "^5.9.3"
18
23
  },
19
24
  "keywords": [
20
25
  "bun",
@@ -22,7 +27,11 @@
22
27
  "utils",
23
28
  "tools"
24
29
  ],
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
25
33
  "repository": {
26
- "url": "git@github.com:tkeron/tools.git"
34
+ "type": "git",
35
+ "url": "git+https://github.com/tkeron/tools.git"
27
36
  }
28
37
  }
package/readme.md CHANGED
@@ -21,15 +21,94 @@ This package provides a growing collection of utility functions and data structu
21
21
 
22
22
  ```typescript
23
23
  // Import all utilities
24
- import * as tools from '@tkeron/tools';
24
+ import * as tools from "@tkeron/tools";
25
25
 
26
26
  // Or import specific utilities
27
- import { rng, getLIFO, getPaths } from '@tkeron/tools';
27
+ import { rng, getLIFO, getPaths } from "@tkeron/tools";
28
+ ```
29
+
30
+ ## 📚 Available Utilities
31
+
32
+ ### Logger System
33
+
34
+ Generic logging abstraction for standardized logging across applications:
35
+
36
+ ```typescript
37
+ import { Logger, logger, silentLogger, createTestLogger } from "@tkeron/tools";
38
+
39
+ // Default logger (uses console)
40
+ logger.log("Hello world");
41
+ logger.error("Error message");
42
+ logger.warn("Warning message");
43
+ logger.info("Info message");
44
+
45
+ // Silent logger (no output, useful for production or testing)
46
+ silentLogger.log("This won't appear");
47
+
48
+ // Test logger (captures logs in arrays for testing)
49
+ const { logger: testLog, logs, errors, warns, infos } = createTestLogger();
50
+ testLog.log("Test message");
51
+ console.log(logs); // ["Test message"]
52
+
53
+ // Inject logger in your functions
54
+ const myFunction = (data: string, log: Logger = logger) => {
55
+ log.info("Processing:", data);
56
+ // ... your logic
57
+ };
58
+ ```
59
+
60
+ ### File System Utilities
61
+
62
+ Glob pattern matching and path retrieval:
63
+
64
+ ```typescript
65
+ import { getPaths, getFilePaths, getDirectoryPaths } from "@tkeron/tools";
66
+
67
+ // Get all files and directories
68
+ const allPaths = getPaths("/path/to/dir", "**/*", "yes");
69
+
70
+ // Get only files
71
+ const files = getFilePaths("/path/to/dir", "**/*.ts");
72
+
73
+ // Get only directories
74
+ const dirs = getDirectoryPaths("/path/to/dir", "**/");
75
+ ```
76
+
77
+ ### Data Structures
78
+
79
+ Stack implementations (LIFO/FIFO):
80
+
81
+ ```typescript
82
+ import { getLIFO, getFIFO } from "@tkeron/tools";
83
+
84
+ const stack = getLIFO<string>();
85
+ stack.push("first");
86
+ stack.push("second");
87
+ console.log(stack.current); // "second"
88
+ console.log(stack.pop()); // "second"
89
+ ```
90
+
91
+ ### Random Number Generator
92
+
93
+ Seedable random number generator:
94
+
95
+ ```typescript
96
+ import { rng } from "@tkeron/tools";
97
+
98
+ // Generate infinite sequence with seed
99
+ for (const num of rng(42)) {
100
+ console.log(num);
101
+ }
102
+
103
+ // Generate limited sequence
104
+ for (const num of rng(42, 10)) {
105
+ console.log(num); // Only 10 numbers
106
+ }
28
107
  ```
29
108
 
30
109
  ## 📚 Documentation
31
110
 
32
- For detailed documentation of all available utilities, please refer to the TypeScript definitions or explore the source code. Each utility is fully typed and includes JSDoc comments for better IDE support.
111
+ For detailed documentation and TypeScript definitions, refer to the source code. Each utility is fully typed with JSDoc comments for IDE support.
33
112
 
34
113
  ## 🧪 Testing
35
114
 
@@ -39,7 +118,4 @@ The project includes comprehensive tests for all utilities:
39
118
  bun test
40
119
  ```
41
120
 
42
-
43
-
44
-
45
- Built with ❤️ for the Bun ecosystem
121
+ Built with ❤️ for the Bun ecosystem
@@ -0,0 +1,31 @@
1
+ import type { Logger } from "./loggerObj.js";
2
+
3
+ export const createTestLogger = () => {
4
+ const logs: string[] = [];
5
+ const errors: string[] = [];
6
+ const warns: string[] = [];
7
+ const infos: string[] = [];
8
+
9
+ const format = (args: unknown[]) =>
10
+ args
11
+ .map((a) => {
12
+ if (typeof a === "string") return a;
13
+ if (a === undefined) return "undefined";
14
+ if (a instanceof Error) return a.stack || a.message || String(a);
15
+ return JSON.stringify(a);
16
+ })
17
+ .join(" ");
18
+
19
+ return {
20
+ logger: {
21
+ log: (...args: unknown[]) => logs.push(format(args)),
22
+ error: (...args: unknown[]) => errors.push(format(args)),
23
+ warn: (...args: unknown[]) => warns.push(format(args)),
24
+ info: (...args: unknown[]) => infos.push(format(args)),
25
+ } as Logger,
26
+ logs,
27
+ errors,
28
+ warns,
29
+ infos,
30
+ };
31
+ };
package/src/getPaths.ts CHANGED
@@ -2,113 +2,100 @@ import { Glob } from "bun";
2
2
  import { statSync } from "fs";
3
3
  import { join } from "path";
4
4
 
5
- /**
6
- * Gets file paths, directory paths, or both based on the specified parameters
7
- * @param path - The base directory path to search in
8
- * @param pattern - The glob pattern to match files/directories (default: **\/*)
9
- * @param includeDirectories - Controls what to include: yes (files + dirs), no (files only), onlyDirectories (dirs only)
10
- * @param absolute - Whether to return absolute paths (default: true)
11
- * @returns Array of paths matching the criteria
12
- */
13
5
  export const getPaths = (
14
- path: string,
15
- pattern: string = "**/*",
16
- includeDirectories: "yes" | "no" | "onlyDirectories" = "no",
17
- absolute: boolean = true,
6
+ path: string,
7
+ pattern: string = "**/*",
8
+ includeDirectories: "yes" | "no" | "onlyDirectories" = "no",
9
+ absolute: boolean = true,
18
10
  ): string[] => {
19
- try {
20
- if (includeDirectories === "no") return getFilePaths(path, pattern, absolute);
21
- if (includeDirectories === "onlyDirectories") return getDirectoryPaths(path, pattern, absolute);
22
-
23
- const paths: string[] = [];
24
-
25
- const glob = new Glob(pattern);
26
-
27
- const files = glob.scanSync({
28
- cwd: path,
29
- onlyFiles: false,
30
- absolute: absolute,
31
- dot: true,
32
- followSymlinks: false,
33
- });
34
-
35
- paths.push(...files);
36
-
37
- return paths;
38
- } catch (error) {
39
- console.error(`Error getting paths from ${path}:`, error);
40
- return [];
41
- }
11
+ try {
12
+ if (includeDirectories === "no")
13
+ return getFilePaths(path, pattern, absolute);
14
+ if (includeDirectories === "onlyDirectories")
15
+ return getDirectoryPaths(path, pattern, absolute);
16
+
17
+ const paths: string[] = [];
18
+
19
+ const glob = new Glob(pattern);
20
+
21
+ const files = glob.scanSync({
22
+ cwd: path,
23
+ onlyFiles: false,
24
+ absolute: absolute,
25
+ dot: true,
26
+ followSymlinks: false,
27
+ });
28
+
29
+ paths.push(...files);
30
+
31
+ return paths;
32
+ } catch (error) {
33
+ console.error(`Error getting paths from ${path}:`, error);
34
+ return [];
35
+ }
42
36
  };
43
37
 
44
-
45
- /**
46
- * Gets only file paths (excluding directories) matching the pattern
47
- * @param path - The base directory path to search in
48
- * @param pattern - The glob pattern to match files (default: **\/*)
49
- * @param absolute - Whether to return absolute paths (default: true)
50
- * @returns Array of file paths
51
- */
52
- export const getFilePaths = (path: string, pattern: string = "**/*", absolute: boolean = true): string[] => {
53
- try {
54
- const paths: string[] = [];
55
-
56
- const glob = new Glob(pattern);
57
-
58
- const files = glob.scanSync({
59
- cwd: path,
60
- onlyFiles: true,
61
- absolute: absolute,
62
- dot: true,
63
- followSymlinks: false,
64
- });
65
-
66
- paths.push(...files);
67
-
68
- return paths;
69
- } catch (error) {
70
- console.error(`Error getting file paths from ${path}:`, error);
71
- return [];
72
- }
38
+ export const getFilePaths = (
39
+ path: string,
40
+ pattern: string = "**/*",
41
+ absolute: boolean = true,
42
+ ): string[] => {
43
+ try {
44
+ const paths: string[] = [];
45
+
46
+ const glob = new Glob(pattern);
47
+
48
+ const files = glob.scanSync({
49
+ cwd: path,
50
+ onlyFiles: true,
51
+ absolute: absolute,
52
+ dot: true,
53
+ followSymlinks: false,
54
+ });
55
+
56
+ paths.push(...files);
57
+
58
+ return paths;
59
+ } catch (error) {
60
+ console.error(`Error getting file paths from ${path}:`, error);
61
+ return [];
62
+ }
73
63
  };
74
64
 
75
- /**
76
- * Gets only directory paths (excluding files) matching the pattern
77
- * @param path - The base directory path to search in
78
- * @param pattern - The glob pattern to match directories (default: **\/*)
79
- * @param absolute - Whether to return absolute paths (default: true)
80
- * @returns Array of directory paths
81
- */
82
- export const getDirectoryPaths = (path: string, pattern: string = "**/*", absolute: boolean = true): string[] => {
83
- try {
84
- const paths: string[] = [];
85
-
86
- const glob = new Glob(pattern);
87
-
88
- const allItems = [...glob.scanSync({
89
- cwd: path,
90
- onlyFiles: false,
91
- absolute: absolute,
92
- dot: true,
93
- followSymlinks: false,
94
- })];
95
-
96
- const directories = allItems.filter(item => {
97
- try {
98
- const fullPath = absolute ? item : join(path, item);
99
- return statSync(fullPath).isDirectory();
100
- } catch {
101
- return false;
102
- }
103
- });
104
-
105
- paths.push(...directories);
106
-
107
- return paths;
108
- } catch (error) {
109
- console.error(`Error getting directory paths from ${path}:`, error);
110
- return [];
111
- }
65
+ export const getDirectoryPaths = (
66
+ path: string,
67
+ pattern: string = "**/*",
68
+ absolute: boolean = true,
69
+ ): string[] => {
70
+ try {
71
+ const paths: string[] = [];
72
+
73
+ const glob = new Glob(pattern);
74
+
75
+ const allItems = [
76
+ ...glob.scanSync({
77
+ cwd: path,
78
+ onlyFiles: false,
79
+ absolute: absolute,
80
+ dot: true,
81
+ followSymlinks: false,
82
+ }),
83
+ ];
84
+
85
+ const directories = allItems.filter((item) => {
86
+ try {
87
+ const fullPath = absolute ? item : join(path, item);
88
+ return statSync(fullPath).isDirectory();
89
+ } catch {
90
+ return false;
91
+ }
92
+ });
93
+
94
+ paths.push(...directories);
95
+
96
+ return paths;
97
+ } catch (error) {
98
+ console.error(`Error getting directory paths from ${path}:`, error);
99
+ return [];
100
+ }
112
101
  };
113
-
114
-
package/src/index.ts CHANGED
@@ -1,3 +1,6 @@
1
1
  export * from "./random";
2
2
  export * from "./stack";
3
3
  export * from "./getPaths";
4
+ export * from "./loggerObj";
5
+ export * from "./silentLogger";
6
+ export * from "./createTestLogger";
@@ -0,0 +1,13 @@
1
+ export interface Logger {
2
+ log: (...args: unknown[]) => void;
3
+ error: (...args: unknown[]) => void;
4
+ warn: (...args: unknown[]) => void;
5
+ info: (...args: unknown[]) => void;
6
+ }
7
+
8
+ export const logger: Logger = {
9
+ log: (...args: unknown[]) => console.log(...args),
10
+ error: (...args: unknown[]) => console.error(...args),
11
+ warn: (...args: unknown[]) => console.warn(...args),
12
+ info: (...args: unknown[]) => console.info(...args),
13
+ };
@@ -0,0 +1,8 @@
1
+ import type { Logger } from "./loggerObj.js";
2
+
3
+ export const silentLogger: Logger = {
4
+ log: () => {},
5
+ error: () => {},
6
+ warn: () => {},
7
+ info: () => {},
8
+ };
package/src/stack.ts CHANGED
@@ -1,70 +1,53 @@
1
-
2
1
  export interface Stack<T> {
3
- pop: () => T;
4
- push: (item: T) => void;
5
- readonly length: number;
6
- current: T | null;
2
+ pop: () => T;
3
+ push: (item: T) => void;
4
+ readonly length: number;
5
+ current: T | null;
7
6
  }
8
7
 
9
- /**
10
- * Returns a LIFO (Last In First Out) stack.
11
- * Same as FILO (First In Last Out) stack.
12
- * @returns an empty LIFO stack.
13
- */
14
8
  export const getLIFO = <T>(): Stack<T> => {
15
-
16
- const _stack: T[] = [];
17
-
18
-
19
- const stack = <Stack<T>>{
20
- pop: () => _stack.pop(),
21
- push: (item) => _stack.push(item),
22
- length: 0,
23
- current: <T><unknown>null
24
- };
25
-
26
- Object.defineProperty(stack, "current", {
27
- get: () => _stack.length ? _stack[_stack.length - 1] : null,
28
- set: (value) => {
29
- if (_stack.length) _stack[_stack.length - 1] = value;
30
- }
31
- });
32
-
33
- Object.defineProperty(stack, "length", {
34
- get: () => _stack.length,
35
- });
36
-
37
-
38
- return stack;
39
-
40
-
9
+ const _stack: T[] = [];
10
+
11
+ const stack = <Stack<T>>{
12
+ pop: () => _stack.pop(),
13
+ push: (item) => _stack.push(item),
14
+ length: 0,
15
+ current: <T>(<unknown>null),
16
+ };
17
+
18
+ Object.defineProperty(stack, "current", {
19
+ get: () => (_stack.length ? _stack[_stack.length - 1] : null),
20
+ set: (value) => {
21
+ if (_stack.length) _stack[_stack.length - 1] = value;
22
+ },
23
+ });
24
+
25
+ Object.defineProperty(stack, "length", {
26
+ get: () => _stack.length,
27
+ });
28
+
29
+ return stack;
41
30
  };
42
31
 
43
- /**
44
- * Returns a FIFO (First In First Out) stack
45
- * @returns an empty FIFO stack
46
- */
47
32
  export const getFIFO = <T>(): Stack<T> => {
48
-
49
- const _stack: T[] = [];
50
- const stack = <Stack<T>>{
51
- pop: () => _stack.shift(),
52
- push: (item) => _stack.push(item),
53
- length: 0,
54
- current: <T><unknown>null
55
- };
56
-
57
- Object.defineProperty(stack, "current", {
58
- get: () => _stack.length ? _stack[0] : null,
59
- set: (value) => {
60
- if (_stack.length) _stack[0] = value;
61
- }
62
- });
63
-
64
- Object.defineProperty(stack, "length", {
65
- get: () => _stack.length,
66
- });
67
-
68
- return stack;
69
-
70
- }
33
+ const _stack: T[] = [];
34
+ const stack = <Stack<T>>{
35
+ pop: () => _stack.shift(),
36
+ push: (item) => _stack.push(item),
37
+ length: 0,
38
+ current: <T>(<unknown>null),
39
+ };
40
+
41
+ Object.defineProperty(stack, "current", {
42
+ get: () => (_stack.length ? _stack[0] : null),
43
+ set: (value) => {
44
+ if (_stack.length) _stack[0] = value;
45
+ },
46
+ });
47
+
48
+ Object.defineProperty(stack, "length", {
49
+ get: () => _stack.length,
50
+ });
51
+
52
+ return stack;
53
+ };