@forthtilliath/ts-kit 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Forthtilliath
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # @forthtilliath/ts-kit
2
+
3
+ Framework-agnostic TypeScript utility functions — arrays, maths, browser file
4
+ downloads, and a couple of `Array` extensions with better type-narrowing.
5
+
6
+ > **Status**: not currently consumed by any other package in this monorepo.
7
+ > Built, linted, tested and type-checked like everything else, but nothing
8
+ > imports it yet.
9
+
10
+ ## Install
11
+
12
+ Within the workspace:
13
+
14
+ ```json
15
+ {
16
+ "dependencies": {
17
+ "@forthtilliath/ts-kit": "workspace:*"
18
+ }
19
+ }
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ Each module is exported individually — import the file you need directly
25
+ rather than a single barrel:
26
+
27
+ ```ts
28
+ import { chunk } from "@forthtilliath/ts-kit/array/chunk";
29
+ import { flattenDeep } from "@forthtilliath/ts-kit/array/flattenDeep";
30
+ import { FArray } from "@forthtilliath/ts-kit/classes/array";
31
+ import {
32
+ downloadText,
33
+ downloadTextBlob,
34
+ } from "@forthtilliath/ts-kit/files/downloadText";
35
+ import { avg } from "@forthtilliath/ts-kit/maths/average";
36
+ import { sum } from "@forthtilliath/ts-kit/maths/sum";
37
+ ```
38
+
39
+ ### `array`
40
+
41
+ - `chunk(array, size)` — splits an array into chunks of a given size.
42
+ - `flattenDeep(array)` — recursively flattens nested arrays.
43
+
44
+ ### `classes`
45
+
46
+ - `FArray` — an `Array` subclass whose `includes()` narrows the type of the
47
+ searched value when it's found (unlike the native `Array.prototype.includes`,
48
+ which only narrows the _array's_ element type).
49
+
50
+ ### `files`
51
+
52
+ - `downloadText(filename, text)` — triggers a browser download of a text file
53
+ via a `data:` URI.
54
+ - `downloadTextBlob(filename, text)` — same, but via a `Blob` + object URL
55
+ (better for larger content).
56
+
57
+ ### `maths`
58
+
59
+ - `sum(numbers)` — sum of an array of numbers.
60
+ - `avg(numbers)` — average of an array of numbers.
61
+
62
+ ## Scripts
63
+
64
+ ```bash
65
+ pnpm run build # tsc -> dist/ (excludes *.test.ts)
66
+ pnpm run dev # tsc --watch, same exclusion
67
+ pnpm run check-types # tsc --noEmit, includes test files
68
+ pnpm run lint # eslint
69
+ pnpm run test # vitest run
70
+ ```
71
+
72
+ Type-checking and building use different `tsconfig`s on purpose:
73
+ `tsconfig.json` includes `*.test.ts` (so lint/check-types see them),
74
+ `tsconfig.build.json` excludes them (so they never end up in `dist/`).
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Splits an array into chunks of a specified size.
3
+ *
4
+ * @param array - The array to chunk.
5
+ * @param size - The size of each chunk.
6
+ * @returns An array of arrays containing the chunked elements.
7
+ * @example
8
+ * chunk([1, 2, 3, 4, 5], 2); // => [[1, 2], [3, 4], [5]]
9
+ * chunk([1, 2, 3, 4, 5], 3); // => [[1, 2, 3], [4, 5]]
10
+ * chunk([1, 2, 3, 4, 5], 6); // => [[1, 2, 3, 4, 5]]
11
+ * chunk([1, 2, 3, 4, 5], 0); // => []
12
+ */
13
+ export declare function chunk<T>(array: T[], size: number): T[][];
14
+ //# sourceMappingURL=chunk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk.d.ts","sourceRoot":"","sources":["../../src/array/chunk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,EAAE,EAAE,CAOxD"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Splits an array into chunks of a specified size.
3
+ *
4
+ * @param array - The array to chunk.
5
+ * @param size - The size of each chunk.
6
+ * @returns An array of arrays containing the chunked elements.
7
+ * @example
8
+ * chunk([1, 2, 3, 4, 5], 2); // => [[1, 2], [3, 4], [5]]
9
+ * chunk([1, 2, 3, 4, 5], 3); // => [[1, 2, 3], [4, 5]]
10
+ * chunk([1, 2, 3, 4, 5], 6); // => [[1, 2, 3, 4, 5]]
11
+ * chunk([1, 2, 3, 4, 5], 0); // => []
12
+ */
13
+ export function chunk(array, size) {
14
+ if (size <= 0)
15
+ return [];
16
+ const chunks = [];
17
+ for (let i = 0; i < array.length; i += size) {
18
+ chunks.push(array.slice(i, i + size));
19
+ }
20
+ return chunks;
21
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Recursively flattens a nested array.
3
+ *
4
+ * @template T - The type of elements in the array.
5
+ * @param {T[]} array - The array to flatten.
6
+ * @return {T[]} The flattened array.
7
+ */
8
+ export declare function flattenDeep<T>(array: T[]): T[];
9
+ //# sourceMappingURL=flattenDeep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flattenDeep.d.ts","sourceRoot":"","sources":["../../src/array/flattenDeep.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAM9C"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Recursively flattens a nested array.
3
+ *
4
+ * @template T - The type of elements in the array.
5
+ * @param {T[]} array - The array to flatten.
6
+ * @return {T[]} The flattened array.
7
+ */
8
+ export function flattenDeep(array) {
9
+ return array.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
10
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./chunk.js";
2
+ export * from "./flattenDeep.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/array/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from "./chunk.js";
2
+ export * from "./flattenDeep.js";
@@ -0,0 +1,53 @@
1
+ export declare class FArray<T> extends Array<T> {
2
+ /**
3
+ * Similar to `Array.includes`, but narrows the type of `searchElement` to `Include & T` if it is included in the array.
4
+ *
5
+ * @param searchElement The value to search for.
6
+ * @param fromIndex The position in this array at which to begin searching for searchElement.
7
+ * @returns true if the value is found, otherwise false.
8
+ *
9
+ * @example
10
+ * // Normal operation of Array.includes
11
+ * const bar = "bar";
12
+ * type FBB = "foo" | "bar" | "baz";
13
+ * const arr: FBB[] = ["foo", "bar", "baz"];
14
+ * if (arr.includes(bar)) {
15
+ * // bar is typed as "bar"
16
+ * } else {
17
+ * // bar is typed as "bar"
18
+ * }
19
+ *
20
+ * @example
21
+ * // FArray.includes
22
+ * const bar = "bar";
23
+ * type FBB = "foo" | "bar" | "baz";
24
+ * const arr = new FArray<FBB>("foo", "bar", "baz");
25
+ * if (arr.includes(bar)) {
26
+ * // bar is typed as "bar"
27
+ * } else {
28
+ * // bar is typed as never
29
+ * }
30
+ *
31
+ * @example
32
+ * // Normal operation of Array.includes
33
+ * const buz = "buz";
34
+ * type FBB = "foo" | "bar" | "baz";
35
+ * const arr: FBB[] = ["foo", "bar", "baz"];
36
+ * if (arr.includes(buz)) {}
37
+ * // ---------------^
38
+ * // The argument of type 'buz' is not assignable to the parameter of type 'FBB'.
39
+ *
40
+ * @example
41
+ * // FArray.includes
42
+ * const buz = "buz";
43
+ * type FBB = "foo" | "bar" | "baz";
44
+ * const arr = new FArray<FBB>("foo", "bar", "baz");
45
+ * if (arr.includes(buz)) {
46
+ * // bar is typed as never
47
+ * } else {
48
+ * // bar is typed as "buz"
49
+ * }
50
+ */
51
+ includes<S>(searchElement: S | T, fromIndex?: number): searchElement is S & T;
52
+ }
53
+ //# sourceMappingURL=array.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../../src/classes/array.ts"],"names":[],"mappings":"AAAA,qBAAa,MAAM,CAAC,CAAC,CAAE,SAAQ,KAAK,CAAC,CAAC,CAAC;IACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDG;IACH,QAAQ,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,GAAG,CAAC,EAAE,SAAS,SAAI,GAAG,aAAa,IAAI,CAAC,GAAG,CAAC;CAIzE"}
@@ -0,0 +1,55 @@
1
+ export class FArray extends Array {
2
+ /**
3
+ * Similar to `Array.includes`, but narrows the type of `searchElement` to `Include & T` if it is included in the array.
4
+ *
5
+ * @param searchElement The value to search for.
6
+ * @param fromIndex The position in this array at which to begin searching for searchElement.
7
+ * @returns true if the value is found, otherwise false.
8
+ *
9
+ * @example
10
+ * // Normal operation of Array.includes
11
+ * const bar = "bar";
12
+ * type FBB = "foo" | "bar" | "baz";
13
+ * const arr: FBB[] = ["foo", "bar", "baz"];
14
+ * if (arr.includes(bar)) {
15
+ * // bar is typed as "bar"
16
+ * } else {
17
+ * // bar is typed as "bar"
18
+ * }
19
+ *
20
+ * @example
21
+ * // FArray.includes
22
+ * const bar = "bar";
23
+ * type FBB = "foo" | "bar" | "baz";
24
+ * const arr = new FArray<FBB>("foo", "bar", "baz");
25
+ * if (arr.includes(bar)) {
26
+ * // bar is typed as "bar"
27
+ * } else {
28
+ * // bar is typed as never
29
+ * }
30
+ *
31
+ * @example
32
+ * // Normal operation of Array.includes
33
+ * const buz = "buz";
34
+ * type FBB = "foo" | "bar" | "baz";
35
+ * const arr: FBB[] = ["foo", "bar", "baz"];
36
+ * if (arr.includes(buz)) {}
37
+ * // ---------------^
38
+ * // The argument of type 'buz' is not assignable to the parameter of type 'FBB'.
39
+ *
40
+ * @example
41
+ * // FArray.includes
42
+ * const buz = "buz";
43
+ * type FBB = "foo" | "bar" | "baz";
44
+ * const arr = new FArray<FBB>("foo", "bar", "baz");
45
+ * if (arr.includes(buz)) {
46
+ * // bar is typed as never
47
+ * } else {
48
+ * // bar is typed as "buz"
49
+ * }
50
+ */
51
+ includes(searchElement, fromIndex = 0) {
52
+ // @ts-expect-error searchElement is unknown
53
+ return super.includes(searchElement, fromIndex);
54
+ }
55
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Downloads a text blob as a file.
3
+ * @param filename The filename to save to.
4
+ * @param text The text to save.
5
+ */
6
+ export declare function downloadText(filename: string, text: string): void;
7
+ /**
8
+ * Downloads a text blob as a file.
9
+ * @param filename The filename to save to.
10
+ * @param text The text to save.
11
+ */
12
+ export declare function downloadTextBlob(filename: string, text: string): void;
13
+ //# sourceMappingURL=downloadText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"downloadText.d.ts","sourceRoot":"","sources":["../../src/files/downloadText.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAcjE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAYrE"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Downloads a text blob as a file.
3
+ * @param filename The filename to save to.
4
+ * @param text The text to save.
5
+ */
6
+ export function downloadText(filename, text) {
7
+ const element = document.createElement("a");
8
+ element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
9
+ element.setAttribute("download", filename);
10
+ element.style.display = "none";
11
+ document.body.appendChild(element);
12
+ element.click();
13
+ document.body.removeChild(element);
14
+ }
15
+ /**
16
+ * Downloads a text blob as a file.
17
+ * @param filename The filename to save to.
18
+ * @param text The text to save.
19
+ */
20
+ export function downloadTextBlob(filename, text) {
21
+ const url = URL.createObjectURL(new Blob([text], { type: "text/plain" }));
22
+ const a = document.createElement("a");
23
+ a.setAttribute("download", filename);
24
+ a.setAttribute("href", url);
25
+ a.style.setProperty("display", "none");
26
+ document.body.appendChild(a);
27
+ a.click();
28
+ URL.revokeObjectURL(url);
29
+ document.body.removeChild(a);
30
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Calculates the average of all numbers in an array.
3
+ *
4
+ * @param {number[]} arr - The array of numbers to average.
5
+ * @return {number} The average of all numbers in the array.
6
+ */
7
+ export declare function avg(arr: number[]): number;
8
+ //# sourceMappingURL=average.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"average.d.ts","sourceRoot":"","sources":["../../src/maths/average.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,CAEzC"}
@@ -0,0 +1,10 @@
1
+ import { sum } from "./sum.js";
2
+ /**
3
+ * Calculates the average of all numbers in an array.
4
+ *
5
+ * @param {number[]} arr - The array of numbers to average.
6
+ * @return {number} The average of all numbers in the array.
7
+ */
8
+ export function avg(arr) {
9
+ return sum(arr) / arr.length;
10
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./average.js";
2
+ export * from "./sum.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/maths/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from "./average.js";
2
+ export * from "./sum.js";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Calculates the sum of all numbers in an array.
3
+ *
4
+ * @param {number[]} arr - The array of numbers to sum.
5
+ * @return {number} The sum of all numbers in the array.
6
+ */
7
+ export declare function sum(arr: number[]): number;
8
+ //# sourceMappingURL=sum.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sum.d.ts","sourceRoot":"","sources":["../../src/maths/sum.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,CAEzC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Calculates the sum of all numbers in an array.
3
+ *
4
+ * @param {number[]} arr - The array of numbers to sum.
5
+ * @return {number} The sum of all numbers in the array.
6
+ */
7
+ export function sum(arr) {
8
+ return arr.reduce((total, n) => total + n, 0);
9
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@forthtilliath/ts-kit",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/Forthtilliath/forthtilliath-packages.git",
12
+ "directory": "packages/ts-kit"
13
+ },
14
+ "homepage": "https://github.com/Forthtilliath/forthtilliath-packages/tree/main/packages/ts-kit#readme",
15
+ "bugs": "https://github.com/Forthtilliath/forthtilliath-packages/issues",
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "exports": {
20
+ "./*": "./dist/*.js"
21
+ },
22
+ "devDependencies": {
23
+ "eslint": "10.7.0",
24
+ "jsdom": "^29.1.1",
25
+ "typescript": "^5.9.3",
26
+ "vitest": "^4.1.10",
27
+ "@forthtilliath/eslint-config": "0.0.0",
28
+ "@forthtilliath/typescript-config": "0.0.0"
29
+ },
30
+ "scripts": {
31
+ "dev": "tsc --watch -p tsconfig.build.json",
32
+ "build": "tsc -p tsconfig.build.json",
33
+ "check-types": "tsc --noEmit",
34
+ "lint": "eslint . --max-warnings 0",
35
+ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
36
+ "test": "vitest run",
37
+ "test:watch": "vitest"
38
+ }
39
+ }