@casekit/toolbox 0.0.0-20250322230249

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.
@@ -0,0 +1,6 @@
1
+ export { interleave } from "./interleave.js";
2
+ export { notNull } from "./notNull.js";
3
+ export { toSentence } from "./toSentence.js";
4
+ export type { Conform } from "./types/Conform.js";
5
+ export type { MarkNonNullable } from "./types/MarkNonNullable.js";
6
+ export type { Simplify } from "./types/Simplify.js";
package/build/index.js ADDED
@@ -0,0 +1,5 @@
1
+ /* v8 ignore start */
2
+ export { interleave } from "./interleave.js";
3
+ export { notNull } from "./notNull.js";
4
+ export { toSentence } from "./toSentence.js";
5
+ /* v8 ignore stop */
@@ -0,0 +1 @@
1
+ export declare const interleave: <T, U>(xs: T[], y: U) => (T | U)[];
@@ -0,0 +1,9 @@
1
+ export const interleave = (xs, y) => {
2
+ const zs = [];
3
+ xs.forEach((x, index) => {
4
+ zs.push(x);
5
+ if (index < xs.length - 1)
6
+ zs.push(y);
7
+ });
8
+ return zs;
9
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { interleave } from "#interleave.js";
3
+ describe("interleave", () => {
4
+ test("it interleaves a value between elements of a list", () => {
5
+ expect(interleave([1, 2, 3], "x")).toEqual([1, "x", 2, "x", 3]);
6
+ });
7
+ test("it works with empty lists", () => {
8
+ expect(interleave([], "x")).toEqual([]);
9
+ });
10
+ test("it works with long lists", () => {
11
+ expect(interleave([1, 2, 3, 1, 2, 3, 1, 2, 3], "x")).toEqual([
12
+ 1,
13
+ "x",
14
+ 2,
15
+ "x",
16
+ 3,
17
+ "x",
18
+ 1,
19
+ "x",
20
+ 2,
21
+ "x",
22
+ 3,
23
+ "x",
24
+ 1,
25
+ "x",
26
+ 2,
27
+ "x",
28
+ 3,
29
+ ]);
30
+ });
31
+ });
@@ -0,0 +1 @@
1
+ export declare const notNull: <T>(x: T) => x is NonNullable<T>;
@@ -0,0 +1 @@
1
+ export const notNull = (x) => x !== null;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import { expect, test } from "vitest";
2
+ import { notNull } from "./notNull.js";
3
+ test("notNull returns false for null", () => {
4
+ expect(notNull(null)).toBe(false);
5
+ });
6
+ test("notNull returns true for non-null values", () => {
7
+ expect(notNull(0)).toBe(true);
8
+ expect(notNull("")).toBe(true);
9
+ expect(notNull(false)).toBe(true);
10
+ expect(notNull({})).toBe(true);
11
+ expect(notNull([])).toBe(true);
12
+ expect(notNull(undefined)).toBe(true);
13
+ });
14
+ test("notNull type guard narrows type correctly", () => {
15
+ const arr = [1, null, 2, null, 3];
16
+ const numbers = arr.filter(notNull);
17
+ // @ts-expect-error numbers should not contain null
18
+ const _test = numbers[0];
19
+ });
@@ -0,0 +1 @@
1
+ export declare const toSentence: (iter: Iterable<string>, conjunction?: string) => string | undefined;
@@ -0,0 +1,13 @@
1
+ export const toSentence = (iter, conjunction = "and") => {
2
+ const items = Array.from(iter);
3
+ switch (items.length) {
4
+ case 0:
5
+ return "";
6
+ case 1:
7
+ return items[0];
8
+ case 2:
9
+ return items.join(` ${conjunction} `);
10
+ default:
11
+ return `${items.slice(0, -1).join(", ")}, ${conjunction} ${items[items.length - 1]}`;
12
+ }
13
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ import { expect, test } from "vitest";
2
+ import { toSentence } from "./toSentence.js";
3
+ test("toSentence returns empty string for empty array", () => {
4
+ expect(toSentence([])).toBe("");
5
+ });
6
+ test("toSentence returns single item for array of one", () => {
7
+ expect(toSentence(["one"])).toBe("one");
8
+ });
9
+ test("toSentence joins two items with conjunction", () => {
10
+ expect(toSentence(["one", "two"])).toBe("one and two");
11
+ });
12
+ test("toSentence joins multiple items with commas and conjunction", () => {
13
+ expect(toSentence(["one", "two", "three"])).toBe("one, two, and three");
14
+ expect(toSentence(["one", "two", "three", "four"])).toBe("one, two, three, and four");
15
+ });
16
+ test("toSentence uses custom conjunction", () => {
17
+ expect(toSentence(["one", "two"], "or")).toBe("one or two");
18
+ expect(toSentence(["one", "two", "three"], "or")).toBe("one, two, or three");
19
+ });
20
+ test("toSentence works with Set", () => {
21
+ expect(toSentence(new Set(["one", "two", "three"]))).toBe("one, two, and three");
22
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Not really an exact type and not exactly what we'd like -
3
+ * all we need is excess property checking, but that's
4
+ * unlikely to happen. See https://github.com/microsoft/TypeScript/issues/58031
5
+ * for more information.
6
+ */
7
+ export type Conform<Base, T extends Base> = {
8
+ [K in keyof T]: T[K];
9
+ } & {
10
+ [K in keyof T as K extends keyof Base ? never : K]: never;
11
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import { describe, test } from "vitest";
2
+ describe("Conform", () => {
3
+ test("should allow exact matching types", () => {
4
+ const _ = {
5
+ name: "John",
6
+ age: 30,
7
+ };
8
+ });
9
+ test("should not allow extra properties", () => {
10
+ const _person = {
11
+ name: "John",
12
+ age: 30,
13
+ // @ts-expect-error - Extra property 'email' is not allowed
14
+ email: "john@example.com",
15
+ };
16
+ });
17
+ test("should not allow missing properties", () => {
18
+ // @ts-expect-error - Missing property 'age'
19
+ const _person = { name: "John" };
20
+ });
21
+ });
@@ -0,0 +1,3 @@
1
+ export type MarkNonNullable<Type, Keys extends keyof Type> = Type extends Type ? Type & {
2
+ [Key in Keys]-?: NonNullable<Type[Key]>;
3
+ } : never;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import { describe, expectTypeOf, test } from "vitest";
2
+ describe("MarkNonNullable", () => {
3
+ test("should make specified properties non-nullable while preserving other properties", () => {
4
+ // name should be string only (not null)
5
+ expectTypeOf().toExtend();
6
+ // other properties should remain unchanged
7
+ expectTypeOf().toEqualTypeOf();
8
+ expectTypeOf().toEqualTypeOf();
9
+ });
10
+ test("should handle multiple properties", () => {
11
+ expectTypeOf().toExtend();
12
+ });
13
+ test("should handle nested properties", () => {
14
+ // The address object itself should remain the same structure
15
+ expectTypeOf().toEqualTypeOf();
16
+ });
17
+ test("should preserve required properties", () => {
18
+ // id was already required, should remain number
19
+ expectTypeOf().toEqualTypeOf();
20
+ });
21
+ test("should handle optional properties", () => {
22
+ // Optional properties should become required and non-nullable
23
+ expectTypeOf().toExtend();
24
+ });
25
+ });
@@ -0,0 +1,3 @@
1
+ export type Simplify<T> = {
2
+ [K in keyof T]: T[K] extends Date ? T[K] : T[K] extends object ? Simplify<T[K]> : T[K];
3
+ } & unknown;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import { describe, expectTypeOf, test } from "vitest";
2
+ describe("Simplify", () => {
3
+ test("simplify doesn't change the structure of a type containing nested objects, strings, numbers, dates and arrays", () => {
4
+ expectTypeOf().toEqualTypeOf();
5
+ });
6
+ });
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@casekit/toolbox",
3
+ "description": "",
4
+ "version": "0.0.0-20250322230249",
5
+ "author": "",
6
+ "devDependencies": {
7
+ "@trivago/prettier-plugin-sort-imports": "^5.2.2",
8
+ "@types/node": "^22.13.11",
9
+ "@types/pg": "^8.11.11",
10
+ "@vitest/coverage-v8": "^3.0.9",
11
+ "dotenv": "^16.4.7",
12
+ "prettier": "^3.5.3",
13
+ "prettier-plugin-svelte": "^3.3.3",
14
+ "typescript": "^5.8.2",
15
+ "vite-tsconfig-paths": "^5.1.4",
16
+ "vitest": "^3.0.9",
17
+ "@casekit/prettier-config": "0.0.0-20250322230249",
18
+ "@casekit/tsconfig": "0.0.0-20250322230249"
19
+ },
20
+ "exports": {
21
+ ".": "./build/index.js"
22
+ },
23
+ "files": [
24
+ "/build"
25
+ ],
26
+ "imports": {
27
+ "#*": "./build/*"
28
+ },
29
+ "keywords": [],
30
+ "license": "ISC",
31
+ "main": "index.js",
32
+ "peerDependencies": {
33
+ "pg": "^8.13.1",
34
+ "zod": "^3.24.2"
35
+ },
36
+ "prettier": "@casekit/prettier-config",
37
+ "type": "module",
38
+ "dependencies": {
39
+ "ts-essentials": "^10.0.4"
40
+ },
41
+ "scripts": {
42
+ "build": "rm -rf ./build && tsc --build",
43
+ "format:check": "prettier --check .",
44
+ "format": "prettier --write .",
45
+ "lint": "eslint src --max-warnings 0",
46
+ "test": "vitest --run --typecheck --coverage",
47
+ "test:watch": "vitest",
48
+ "typecheck": "tsc --noEmit"
49
+ }
50
+ }