@stackframe/stack-shared 2.7.19 → 2.7.21
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/CHANGELOG.md +12 -0
- package/dist/crud.js +76 -0
- package/dist/hooks/use-strict-memo.js +75 -0
- package/dist/interface/clientInterface.d.ts +1 -0
- package/dist/interface/clientInterface.js +1 -2
- package/dist/known-errors.js +1 -1
- package/dist/utils/arrays.js +112 -1
- package/dist/utils/base64.js +11 -0
- package/dist/utils/booleans.js +24 -0
- package/dist/utils/bytes.js +136 -13
- package/dist/utils/caches.js +33 -0
- package/dist/utils/compile-time.js +17 -0
- package/dist/utils/dates.js +54 -0
- package/dist/utils/functions.js +15 -0
- package/dist/utils/html.js +28 -0
- package/dist/utils/http.js +29 -0
- package/dist/utils/ips.js +29 -0
- package/dist/utils/json.js +135 -0
- package/dist/utils/maps.js +145 -0
- package/dist/utils/math.js +12 -0
- package/dist/utils/numbers.js +43 -0
- package/dist/utils/objects.js +158 -0
- package/dist/utils/promises.js +201 -1
- package/dist/utils/proxies.js +72 -0
- package/dist/utils/react.js +82 -0
- package/dist/utils/results.js +241 -5
- package/dist/utils/strings.js +223 -2
- package/dist/utils/unicode.js +13 -0
- package/dist/utils/urls.js +115 -0
- package/dist/utils/uuids.js +30 -0
- package/package.json +2 -5
|
@@ -6,3 +6,20 @@
|
|
|
6
6
|
export function scrambleDuringCompileTime(t) {
|
|
7
7
|
return t;
|
|
8
8
|
}
|
|
9
|
+
import.meta.vitest?.test("scrambleDuringCompileTime", ({ expect }) => {
|
|
10
|
+
// Test with primitive values
|
|
11
|
+
expect(scrambleDuringCompileTime(42)).toBe(42);
|
|
12
|
+
expect(scrambleDuringCompileTime("hello")).toBe("hello");
|
|
13
|
+
expect(scrambleDuringCompileTime(true)).toBe(true);
|
|
14
|
+
expect(scrambleDuringCompileTime(null)).toBe(null);
|
|
15
|
+
expect(scrambleDuringCompileTime(undefined)).toBe(undefined);
|
|
16
|
+
// Test with objects (reference equality)
|
|
17
|
+
const obj = { a: 1 };
|
|
18
|
+
expect(scrambleDuringCompileTime(obj)).toBe(obj);
|
|
19
|
+
// Test with arrays (reference equality)
|
|
20
|
+
const arr = [1, 2, 3];
|
|
21
|
+
expect(scrambleDuringCompileTime(arr)).toBe(arr);
|
|
22
|
+
// Test with functions (reference equality)
|
|
23
|
+
const fn = () => "test";
|
|
24
|
+
expect(scrambleDuringCompileTime(fn)).toBe(fn);
|
|
25
|
+
});
|
package/dist/utils/dates.js
CHANGED
|
@@ -2,6 +2,16 @@ import { remainder } from "./math";
|
|
|
2
2
|
export function isWeekend(date) {
|
|
3
3
|
return date.getDay() === 0 || date.getDay() === 6;
|
|
4
4
|
}
|
|
5
|
+
import.meta.vitest?.test("isWeekend", ({ expect }) => {
|
|
6
|
+
// Sunday (day 0)
|
|
7
|
+
expect(isWeekend(new Date(2023, 0, 1))).toBe(true);
|
|
8
|
+
// Saturday (day 6)
|
|
9
|
+
expect(isWeekend(new Date(2023, 0, 7))).toBe(true);
|
|
10
|
+
// Monday (day 1)
|
|
11
|
+
expect(isWeekend(new Date(2023, 0, 2))).toBe(false);
|
|
12
|
+
// Friday (day 5)
|
|
13
|
+
expect(isWeekend(new Date(2023, 0, 6))).toBe(false);
|
|
14
|
+
});
|
|
5
15
|
const agoUnits = [
|
|
6
16
|
[60, 'second'],
|
|
7
17
|
[60, 'minute'],
|
|
@@ -12,6 +22,29 @@ const agoUnits = [
|
|
|
12
22
|
export function fromNow(date) {
|
|
13
23
|
return fromNowDetailed(date).result;
|
|
14
24
|
}
|
|
25
|
+
import.meta.vitest?.test("fromNow", ({ expect }) => {
|
|
26
|
+
// Set a fixed date for testing
|
|
27
|
+
const fixedDate = new Date("2023-01-15T12:00:00.000Z");
|
|
28
|
+
// Use Vitest's fake timers
|
|
29
|
+
import.meta.vitest?.vi.useFakeTimers();
|
|
30
|
+
import.meta.vitest?.vi.setSystemTime(fixedDate);
|
|
31
|
+
// Test past times
|
|
32
|
+
expect(fromNow(new Date("2023-01-15T11:59:50.000Z"))).toBe("just now");
|
|
33
|
+
expect(fromNow(new Date("2023-01-15T11:59:00.000Z"))).toBe("1 minute ago");
|
|
34
|
+
expect(fromNow(new Date("2023-01-15T11:00:00.000Z"))).toBe("1 hour ago");
|
|
35
|
+
expect(fromNow(new Date("2023-01-14T12:00:00.000Z"))).toBe("1 day ago");
|
|
36
|
+
expect(fromNow(new Date("2023-01-08T12:00:00.000Z"))).toBe("1 week ago");
|
|
37
|
+
// Test future times
|
|
38
|
+
expect(fromNow(new Date("2023-01-15T12:00:10.000Z"))).toBe("just now");
|
|
39
|
+
expect(fromNow(new Date("2023-01-15T12:01:00.000Z"))).toBe("in 1 minute");
|
|
40
|
+
expect(fromNow(new Date("2023-01-15T13:00:00.000Z"))).toBe("in 1 hour");
|
|
41
|
+
expect(fromNow(new Date("2023-01-16T12:00:00.000Z"))).toBe("in 1 day");
|
|
42
|
+
expect(fromNow(new Date("2023-01-22T12:00:00.000Z"))).toBe("in 1 week");
|
|
43
|
+
// Test very old dates (should use date format)
|
|
44
|
+
expect(fromNow(new Date("2022-01-15T12:00:00.000Z"))).toMatch(/Jan 15, 2022/);
|
|
45
|
+
// Restore real timers
|
|
46
|
+
import.meta.vitest?.vi.useRealTimers();
|
|
47
|
+
});
|
|
15
48
|
export function fromNowDetailed(date) {
|
|
16
49
|
if (!(date instanceof Date)) {
|
|
17
50
|
throw new Error(`fromNow only accepts Date objects (received: ${date})`);
|
|
@@ -58,3 +91,24 @@ export function getInputDatetimeLocalString(date) {
|
|
|
58
91
|
date.setMinutes(date.getMinutes() - date.getTimezoneOffset());
|
|
59
92
|
return date.toISOString().slice(0, 19);
|
|
60
93
|
}
|
|
94
|
+
import.meta.vitest?.test("getInputDatetimeLocalString", ({ expect }) => {
|
|
95
|
+
// Use Vitest's fake timers to ensure consistent timezone behavior
|
|
96
|
+
import.meta.vitest?.vi.useFakeTimers();
|
|
97
|
+
// Test with a specific date
|
|
98
|
+
const mockDate = new Date("2023-01-15T12:30:45.000Z");
|
|
99
|
+
const result = getInputDatetimeLocalString(mockDate);
|
|
100
|
+
// The result should be in the format YYYY-MM-DDTHH:MM:SS
|
|
101
|
+
expect(result).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/);
|
|
102
|
+
// Test with different dates
|
|
103
|
+
const dates = [
|
|
104
|
+
new Date("2023-01-01T00:00:00.000Z"),
|
|
105
|
+
new Date("2023-06-15T23:59:59.000Z"),
|
|
106
|
+
new Date("2023-12-31T12:34:56.000Z"),
|
|
107
|
+
];
|
|
108
|
+
for (const date of dates) {
|
|
109
|
+
const result = getInputDatetimeLocalString(date);
|
|
110
|
+
expect(result).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/);
|
|
111
|
+
}
|
|
112
|
+
// Restore real timers
|
|
113
|
+
import.meta.vitest?.vi.useRealTimers();
|
|
114
|
+
});
|
package/dist/utils/functions.js
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
export function identity(t) {
|
|
2
2
|
return t;
|
|
3
3
|
}
|
|
4
|
+
import.meta.vitest?.test("identity", ({ expect }) => {
|
|
5
|
+
expect(identity(1)).toBe(1);
|
|
6
|
+
expect(identity("test")).toBe("test");
|
|
7
|
+
expect(identity(null)).toBe(null);
|
|
8
|
+
expect(identity(undefined)).toBe(undefined);
|
|
9
|
+
const obj = { a: 1 };
|
|
10
|
+
expect(identity(obj)).toBe(obj);
|
|
11
|
+
});
|
|
4
12
|
export function identityArgs(...args) {
|
|
5
13
|
return args;
|
|
6
14
|
}
|
|
15
|
+
import.meta.vitest?.test("identityArgs", ({ expect }) => {
|
|
16
|
+
expect(identityArgs()).toEqual([]);
|
|
17
|
+
expect(identityArgs(1)).toEqual([1]);
|
|
18
|
+
expect(identityArgs(1, 2, 3)).toEqual([1, 2, 3]);
|
|
19
|
+
expect(identityArgs("a", "b", "c")).toEqual(["a", "b", "c"]);
|
|
20
|
+
expect(identityArgs(null, undefined)).toEqual([null, undefined]);
|
|
21
|
+
});
|
package/dist/utils/html.js
CHANGED
|
@@ -7,6 +7,34 @@ export function escapeHtml(unsafe) {
|
|
|
7
7
|
.replace(/"/g, """)
|
|
8
8
|
.replace(/'/g, "'");
|
|
9
9
|
}
|
|
10
|
+
import.meta.vitest?.test("escapeHtml", ({ expect }) => {
|
|
11
|
+
// Test with empty string
|
|
12
|
+
expect(escapeHtml("")).toBe("");
|
|
13
|
+
// Test with string without special characters
|
|
14
|
+
expect(escapeHtml("hello world")).toBe("hello world");
|
|
15
|
+
// Test with special characters
|
|
16
|
+
expect(escapeHtml("<div>")).toBe("<div>");
|
|
17
|
+
expect(escapeHtml("a & b")).toBe("a & b");
|
|
18
|
+
expect(escapeHtml('a "quoted" string')).toBe("a "quoted" string");
|
|
19
|
+
expect(escapeHtml("it's a test")).toBe("it's a test");
|
|
20
|
+
// Test with multiple special characters
|
|
21
|
+
expect(escapeHtml("<a href=\"test\">It's a link</a>")).toBe("<a href="test">It's a link</a>");
|
|
22
|
+
});
|
|
10
23
|
export function html(strings, ...values) {
|
|
11
24
|
return templateIdentity(strings, ...values.map(v => escapeHtml(`${v}`)));
|
|
12
25
|
}
|
|
26
|
+
import.meta.vitest?.test("html", ({ expect }) => {
|
|
27
|
+
// Test with no interpolation
|
|
28
|
+
expect(html `simple string`).toBe("simple string");
|
|
29
|
+
// Test with string interpolation
|
|
30
|
+
expect(html `Hello, ${"world"}!`).toBe("Hello, world!");
|
|
31
|
+
// Test with number interpolation
|
|
32
|
+
expect(html `Count: ${42}`).toBe("Count: 42");
|
|
33
|
+
// Test with HTML special characters in interpolated values
|
|
34
|
+
expect(html `<div>${"<script>"}</div>`).toBe("<div><script></div>");
|
|
35
|
+
// Test with multiple interpolations
|
|
36
|
+
expect(html `${1} + ${2} = ${"<3"}`).toBe("1 + 2 = <3");
|
|
37
|
+
// Test with object interpolation
|
|
38
|
+
const obj = { toString: () => "<object>" };
|
|
39
|
+
expect(html `Object: ${obj}`).toBe("Object: <object>");
|
|
40
|
+
});
|
package/dist/utils/http.js
CHANGED
|
@@ -51,8 +51,37 @@ export function decodeBasicAuthorizationHeader(value) {
|
|
|
51
51
|
const split = decoded.split(':');
|
|
52
52
|
return [split[0], split.slice(1).join(':')];
|
|
53
53
|
}
|
|
54
|
+
import.meta.vitest?.test("decodeBasicAuthorizationHeader", ({ expect }) => {
|
|
55
|
+
// Test with valid Basic Authorization header
|
|
56
|
+
const username = "user";
|
|
57
|
+
const password = "pass";
|
|
58
|
+
const encoded = encodeBasicAuthorizationHeader(username, password);
|
|
59
|
+
expect(decodeBasicAuthorizationHeader(encoded)).toEqual([username, password]);
|
|
60
|
+
// Test with password containing colons
|
|
61
|
+
const complexPassword = "pass:with:colons";
|
|
62
|
+
const encodedComplex = encodeBasicAuthorizationHeader(username, complexPassword);
|
|
63
|
+
expect(decodeBasicAuthorizationHeader(encodedComplex)).toEqual([username, complexPassword]);
|
|
64
|
+
// Test with invalid headers
|
|
65
|
+
expect(decodeBasicAuthorizationHeader("NotBasic dXNlcjpwYXNz")).toBe(null); // Wrong type
|
|
66
|
+
expect(decodeBasicAuthorizationHeader("Basic")).toBe(null); // Missing encoded part
|
|
67
|
+
expect(decodeBasicAuthorizationHeader("Basic not-base64")).toBe(null); // Not base64
|
|
68
|
+
expect(decodeBasicAuthorizationHeader("Basic dXNlcjpwYXNz extra")).toBe(null); // Extra parts
|
|
69
|
+
});
|
|
54
70
|
export function encodeBasicAuthorizationHeader(id, password) {
|
|
55
71
|
if (id.includes(':'))
|
|
56
72
|
throw new Error("Basic authorization header id cannot contain ':'");
|
|
57
73
|
return `Basic ${encodeBase64(new TextEncoder().encode(`${id}:${password}`))}`;
|
|
58
74
|
}
|
|
75
|
+
import.meta.vitest?.test("encodeBasicAuthorizationHeader", ({ expect }) => {
|
|
76
|
+
// Test with simple username and password
|
|
77
|
+
const encoded = encodeBasicAuthorizationHeader("user", "pass");
|
|
78
|
+
expect(encoded).toMatch(/^Basic [A-Za-z0-9+/=]+$/); // Should start with "Basic " followed by base64
|
|
79
|
+
// Test with empty password
|
|
80
|
+
const encodedEmptyPass = encodeBasicAuthorizationHeader("user", "");
|
|
81
|
+
expect(encodedEmptyPass).toMatch(/^Basic [A-Za-z0-9+/=]+$/);
|
|
82
|
+
// Test with password containing special characters
|
|
83
|
+
const encodedSpecialChars = encodeBasicAuthorizationHeader("user", "p@ss!w0rd");
|
|
84
|
+
expect(encodedSpecialChars).toMatch(/^Basic [A-Za-z0-9+/=]+$/);
|
|
85
|
+
// Test with username containing colon should throw
|
|
86
|
+
expect(() => encodeBasicAuthorizationHeader("user:name", "pass")).toThrow();
|
|
87
|
+
});
|
package/dist/utils/ips.js
CHANGED
|
@@ -2,8 +2,37 @@ import ipRegex from "ip-regex";
|
|
|
2
2
|
export function isIpAddress(ip) {
|
|
3
3
|
return ipRegex({ exact: true }).test(ip);
|
|
4
4
|
}
|
|
5
|
+
import.meta.vitest?.test("isIpAddress", ({ expect }) => {
|
|
6
|
+
// Test valid IPv4 addresses
|
|
7
|
+
expect(isIpAddress("192.168.1.1")).toBe(true);
|
|
8
|
+
expect(isIpAddress("127.0.0.1")).toBe(true);
|
|
9
|
+
expect(isIpAddress("0.0.0.0")).toBe(true);
|
|
10
|
+
expect(isIpAddress("255.255.255.255")).toBe(true);
|
|
11
|
+
// Test valid IPv6 addresses
|
|
12
|
+
expect(isIpAddress("::1")).toBe(true);
|
|
13
|
+
expect(isIpAddress("2001:db8::")).toBe(true);
|
|
14
|
+
expect(isIpAddress("2001:db8:85a3:8d3:1319:8a2e:370:7348")).toBe(true);
|
|
15
|
+
// Test invalid IP addresses
|
|
16
|
+
expect(isIpAddress("")).toBe(false);
|
|
17
|
+
expect(isIpAddress("not an ip")).toBe(false);
|
|
18
|
+
expect(isIpAddress("256.256.256.256")).toBe(false);
|
|
19
|
+
expect(isIpAddress("192.168.1")).toBe(false);
|
|
20
|
+
expect(isIpAddress("192.168.1.1.1")).toBe(false);
|
|
21
|
+
expect(isIpAddress("2001:db8::xyz")).toBe(false);
|
|
22
|
+
});
|
|
5
23
|
export function assertIpAddress(ip) {
|
|
6
24
|
if (!isIpAddress(ip)) {
|
|
7
25
|
throw new Error(`Invalid IP address: ${ip}`);
|
|
8
26
|
}
|
|
9
27
|
}
|
|
28
|
+
import.meta.vitest?.test("assertIpAddress", ({ expect }) => {
|
|
29
|
+
// Test with valid IPv4 address
|
|
30
|
+
expect(() => assertIpAddress("192.168.1.1")).not.toThrow();
|
|
31
|
+
// Test with valid IPv6 address
|
|
32
|
+
expect(() => assertIpAddress("::1")).not.toThrow();
|
|
33
|
+
// Test with invalid IP addresses
|
|
34
|
+
expect(() => assertIpAddress("")).toThrow("Invalid IP address: ");
|
|
35
|
+
expect(() => assertIpAddress("not an ip")).toThrow("Invalid IP address: not an ip");
|
|
36
|
+
expect(() => assertIpAddress("256.256.256.256")).toThrow("Invalid IP address: 256.256.256.256");
|
|
37
|
+
expect(() => assertIpAddress("192.168.1")).toThrow("Invalid IP address: 192.168.1");
|
|
38
|
+
});
|
package/dist/utils/json.js
CHANGED
|
@@ -18,9 +18,144 @@ export function isJson(value) {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
import.meta.vitest?.test("isJson", ({ expect }) => {
|
|
22
|
+
// Test primitive values
|
|
23
|
+
expect(isJson(null)).toBe(true);
|
|
24
|
+
expect(isJson(true)).toBe(true);
|
|
25
|
+
expect(isJson(false)).toBe(true);
|
|
26
|
+
expect(isJson(123)).toBe(true);
|
|
27
|
+
expect(isJson("string")).toBe(true);
|
|
28
|
+
// Test arrays
|
|
29
|
+
expect(isJson([])).toBe(true);
|
|
30
|
+
expect(isJson([1, 2, 3])).toBe(true);
|
|
31
|
+
expect(isJson(["a", "b", "c"])).toBe(true);
|
|
32
|
+
expect(isJson([1, "a", true, null])).toBe(true);
|
|
33
|
+
expect(isJson([1, [2, 3], { a: "b" }])).toBe(true);
|
|
34
|
+
// Test objects
|
|
35
|
+
expect(isJson({})).toBe(true);
|
|
36
|
+
expect(isJson({ a: 1, b: 2 })).toBe(true);
|
|
37
|
+
expect(isJson({ a: "string", b: true, c: null })).toBe(true);
|
|
38
|
+
expect(isJson({ a: [1, 2, 3], b: { c: "d" } })).toBe(true);
|
|
39
|
+
// Test invalid JSON values
|
|
40
|
+
expect(isJson(undefined)).toBe(false);
|
|
41
|
+
expect(isJson(() => { })).toBe(false);
|
|
42
|
+
expect(isJson(Symbol())).toBe(false);
|
|
43
|
+
expect(isJson(BigInt(123))).toBe(false);
|
|
44
|
+
// Test arrays with invalid JSON values
|
|
45
|
+
expect(isJson([1, undefined, 3])).toBe(false);
|
|
46
|
+
expect(isJson([1, () => { }, 3])).toBe(false);
|
|
47
|
+
// Test objects with invalid JSON values
|
|
48
|
+
expect(isJson({ a: 1, b: undefined })).toBe(false);
|
|
49
|
+
expect(isJson({ a: 1, b: () => { } })).toBe(false);
|
|
50
|
+
});
|
|
21
51
|
export function parseJson(json) {
|
|
22
52
|
return Result.fromThrowing(() => JSON.parse(json));
|
|
23
53
|
}
|
|
54
|
+
import.meta.vitest?.test("parseJson", ({ expect }) => {
|
|
55
|
+
// Test valid JSON strings
|
|
56
|
+
const nullResult = parseJson("null");
|
|
57
|
+
expect(nullResult.status).toBe("ok");
|
|
58
|
+
if (nullResult.status === "ok") {
|
|
59
|
+
expect(nullResult.data).toBe(null);
|
|
60
|
+
}
|
|
61
|
+
const trueResult = parseJson("true");
|
|
62
|
+
expect(trueResult.status).toBe("ok");
|
|
63
|
+
if (trueResult.status === "ok") {
|
|
64
|
+
expect(trueResult.data).toBe(true);
|
|
65
|
+
}
|
|
66
|
+
const numberResult = parseJson("123");
|
|
67
|
+
expect(numberResult.status).toBe("ok");
|
|
68
|
+
if (numberResult.status === "ok") {
|
|
69
|
+
expect(numberResult.data).toBe(123);
|
|
70
|
+
}
|
|
71
|
+
const stringResult = parseJson('"string"');
|
|
72
|
+
expect(stringResult.status).toBe("ok");
|
|
73
|
+
if (stringResult.status === "ok") {
|
|
74
|
+
expect(stringResult.data).toBe("string");
|
|
75
|
+
}
|
|
76
|
+
const emptyArrayResult = parseJson("[]");
|
|
77
|
+
expect(emptyArrayResult.status).toBe("ok");
|
|
78
|
+
if (emptyArrayResult.status === "ok") {
|
|
79
|
+
expect(emptyArrayResult.data).toEqual([]);
|
|
80
|
+
}
|
|
81
|
+
const arrayResult = parseJson("[1,2,3]");
|
|
82
|
+
expect(arrayResult.status).toBe("ok");
|
|
83
|
+
if (arrayResult.status === "ok") {
|
|
84
|
+
expect(arrayResult.data).toEqual([1, 2, 3]);
|
|
85
|
+
}
|
|
86
|
+
const emptyObjectResult = parseJson("{}");
|
|
87
|
+
expect(emptyObjectResult.status).toBe("ok");
|
|
88
|
+
if (emptyObjectResult.status === "ok") {
|
|
89
|
+
expect(emptyObjectResult.data).toEqual({});
|
|
90
|
+
}
|
|
91
|
+
const objectResult = parseJson('{"a":1,"b":"string"}');
|
|
92
|
+
expect(objectResult.status).toBe("ok");
|
|
93
|
+
if (objectResult.status === "ok") {
|
|
94
|
+
expect(objectResult.data).toEqual({ a: 1, b: "string" });
|
|
95
|
+
}
|
|
96
|
+
// Test invalid JSON strings
|
|
97
|
+
expect(parseJson("").status).toBe("error");
|
|
98
|
+
expect(parseJson("undefined").status).toBe("error");
|
|
99
|
+
expect(parseJson("{").status).toBe("error");
|
|
100
|
+
expect(parseJson('{"a":1,}').status).toBe("error");
|
|
101
|
+
expect(parseJson("function(){}").status).toBe("error");
|
|
102
|
+
});
|
|
24
103
|
export function stringifyJson(json) {
|
|
25
104
|
return Result.fromThrowing(() => JSON.stringify(json));
|
|
26
105
|
}
|
|
106
|
+
import.meta.vitest?.test("stringifyJson", ({ expect }) => {
|
|
107
|
+
// Test primitive values
|
|
108
|
+
const nullResult = stringifyJson(null);
|
|
109
|
+
expect(nullResult.status).toBe("ok");
|
|
110
|
+
if (nullResult.status === "ok") {
|
|
111
|
+
expect(nullResult.data).toBe("null");
|
|
112
|
+
}
|
|
113
|
+
const trueResult = stringifyJson(true);
|
|
114
|
+
expect(trueResult.status).toBe("ok");
|
|
115
|
+
if (trueResult.status === "ok") {
|
|
116
|
+
expect(trueResult.data).toBe("true");
|
|
117
|
+
}
|
|
118
|
+
const numberResult = stringifyJson(123);
|
|
119
|
+
expect(numberResult.status).toBe("ok");
|
|
120
|
+
if (numberResult.status === "ok") {
|
|
121
|
+
expect(numberResult.data).toBe("123");
|
|
122
|
+
}
|
|
123
|
+
const stringResult = stringifyJson("string");
|
|
124
|
+
expect(stringResult.status).toBe("ok");
|
|
125
|
+
if (stringResult.status === "ok") {
|
|
126
|
+
expect(stringResult.data).toBe('"string"');
|
|
127
|
+
}
|
|
128
|
+
// Test arrays
|
|
129
|
+
const emptyArrayResult = stringifyJson([]);
|
|
130
|
+
expect(emptyArrayResult.status).toBe("ok");
|
|
131
|
+
if (emptyArrayResult.status === "ok") {
|
|
132
|
+
expect(emptyArrayResult.data).toBe("[]");
|
|
133
|
+
}
|
|
134
|
+
const arrayResult = stringifyJson([1, 2, 3]);
|
|
135
|
+
expect(arrayResult.status).toBe("ok");
|
|
136
|
+
if (arrayResult.status === "ok") {
|
|
137
|
+
expect(arrayResult.data).toBe("[1,2,3]");
|
|
138
|
+
}
|
|
139
|
+
// Test objects
|
|
140
|
+
const emptyObjectResult = stringifyJson({});
|
|
141
|
+
expect(emptyObjectResult.status).toBe("ok");
|
|
142
|
+
if (emptyObjectResult.status === "ok") {
|
|
143
|
+
expect(emptyObjectResult.data).toBe("{}");
|
|
144
|
+
}
|
|
145
|
+
const objectResult = stringifyJson({ a: 1, b: "string" });
|
|
146
|
+
expect(objectResult.status).toBe("ok");
|
|
147
|
+
if (objectResult.status === "ok") {
|
|
148
|
+
expect(objectResult.data).toBe('{"a":1,"b":"string"}');
|
|
149
|
+
}
|
|
150
|
+
// Test nested structures
|
|
151
|
+
const nested = { a: [1, 2, 3], b: { c: "d" } };
|
|
152
|
+
const nestedResult = stringifyJson(nested);
|
|
153
|
+
expect(nestedResult.status).toBe("ok");
|
|
154
|
+
if (nestedResult.status === "ok") {
|
|
155
|
+
expect(nestedResult.data).toBe('{"a":[1,2,3],"b":{"c":"d"}}');
|
|
156
|
+
}
|
|
157
|
+
// Test circular references (should error)
|
|
158
|
+
const circular = { a: 1 };
|
|
159
|
+
circular.self = circular;
|
|
160
|
+
expect(stringifyJson(circular).status).toBe("error");
|
|
161
|
+
});
|
package/dist/utils/maps.js
CHANGED
|
@@ -13,6 +13,20 @@ export class WeakRefIfAvailable {
|
|
|
13
13
|
return this._ref.deref();
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
+
import.meta.vitest?.test("WeakRefIfAvailable", ({ expect }) => {
|
|
17
|
+
// Test with an object
|
|
18
|
+
const obj = { id: 1, name: "test" };
|
|
19
|
+
const weakRef = new WeakRefIfAvailable(obj);
|
|
20
|
+
// Test deref returns the original object
|
|
21
|
+
expect(weakRef.deref()).toBe(obj);
|
|
22
|
+
// Test with a different object
|
|
23
|
+
const obj2 = { id: 2, name: "test2" };
|
|
24
|
+
const weakRef2 = new WeakRefIfAvailable(obj2);
|
|
25
|
+
expect(weakRef2.deref()).toBe(obj2);
|
|
26
|
+
expect(weakRef2.deref()).not.toBe(obj);
|
|
27
|
+
// We can't easily test garbage collection in this environment,
|
|
28
|
+
// but we can verify the basic functionality works
|
|
29
|
+
});
|
|
16
30
|
/**
|
|
17
31
|
* A WeakMap-like object that can be iterated over.
|
|
18
32
|
*
|
|
@@ -62,6 +76,56 @@ export class IterableWeakMap {
|
|
|
62
76
|
}
|
|
63
77
|
}
|
|
64
78
|
_a = Symbol.toStringTag;
|
|
79
|
+
import.meta.vitest?.test("IterableWeakMap", ({ expect }) => {
|
|
80
|
+
// Test basic functionality
|
|
81
|
+
const map = new IterableWeakMap();
|
|
82
|
+
// Create object keys
|
|
83
|
+
const obj1 = { id: 1 };
|
|
84
|
+
const obj2 = { id: 2 };
|
|
85
|
+
// Test set and get
|
|
86
|
+
map.set(obj1, "value1");
|
|
87
|
+
expect(map.get(obj1)).toBe("value1");
|
|
88
|
+
// Test has
|
|
89
|
+
expect(map.has(obj1)).toBe(true);
|
|
90
|
+
expect(map.has(obj2)).toBe(false);
|
|
91
|
+
expect(map.has({ id: 1 })).toBe(false); // Different object with same content
|
|
92
|
+
// Test with multiple keys
|
|
93
|
+
map.set(obj2, "value2");
|
|
94
|
+
expect(map.get(obj2)).toBe("value2");
|
|
95
|
+
expect(map.get(obj1)).toBe("value1"); // Original still exists
|
|
96
|
+
// Test delete
|
|
97
|
+
expect(map.delete(obj1)).toBe(true);
|
|
98
|
+
expect(map.has(obj1)).toBe(false);
|
|
99
|
+
expect(map.get(obj1)).toBeUndefined();
|
|
100
|
+
expect(map.has(obj2)).toBe(true); // Other key still exists
|
|
101
|
+
// Test delete non-existent key
|
|
102
|
+
expect(map.delete({ id: 3 })).toBe(false);
|
|
103
|
+
// Test iteration
|
|
104
|
+
const iterMap = new IterableWeakMap();
|
|
105
|
+
const iterObj1 = { id: 1 };
|
|
106
|
+
const iterObj2 = { id: 2 };
|
|
107
|
+
const iterObj3 = { id: 3 };
|
|
108
|
+
iterMap.set(iterObj1, 1);
|
|
109
|
+
iterMap.set(iterObj2, 2);
|
|
110
|
+
iterMap.set(iterObj3, 3);
|
|
111
|
+
const entries = Array.from(iterMap);
|
|
112
|
+
expect(entries.length).toBe(3);
|
|
113
|
+
// Find entries by their values since we can't directly compare objects in the array
|
|
114
|
+
const values = entries.map(entry => entry[1]);
|
|
115
|
+
expect(values).toContain(1);
|
|
116
|
+
expect(values).toContain(2);
|
|
117
|
+
expect(values).toContain(3);
|
|
118
|
+
// Test constructor with entries
|
|
119
|
+
const initialEntries = [
|
|
120
|
+
[{ id: 4 }, "initial1"],
|
|
121
|
+
[{ id: 5 }, "initial2"]
|
|
122
|
+
];
|
|
123
|
+
const mapWithEntries = new IterableWeakMap(initialEntries);
|
|
124
|
+
// We can't directly access the initial entries since they're different object references
|
|
125
|
+
// But we can verify the map has the correct number of entries
|
|
126
|
+
const entriesFromConstructor = Array.from(mapWithEntries);
|
|
127
|
+
expect(entriesFromConstructor.length).toBe(2);
|
|
128
|
+
});
|
|
65
129
|
/**
|
|
66
130
|
* A map that is a IterableWeakMap for object keys and a regular Map for primitive keys. Also provides iteration over both
|
|
67
131
|
* object and primitive keys.
|
|
@@ -117,6 +181,45 @@ export class MaybeWeakMap {
|
|
|
117
181
|
}
|
|
118
182
|
}
|
|
119
183
|
_b = Symbol.toStringTag;
|
|
184
|
+
import.meta.vitest?.test("MaybeWeakMap", ({ expect }) => {
|
|
185
|
+
// Test with primitive keys
|
|
186
|
+
const map = new MaybeWeakMap();
|
|
187
|
+
// Test with string keys
|
|
188
|
+
map.set("key1", 1);
|
|
189
|
+
map.set("key2", 2);
|
|
190
|
+
expect(map.get("key1")).toBe(1);
|
|
191
|
+
expect(map.get("key2")).toBe(2);
|
|
192
|
+
expect(map.has("key1")).toBe(true);
|
|
193
|
+
expect(map.has("nonexistent")).toBe(false);
|
|
194
|
+
// Test with object keys
|
|
195
|
+
const obj1 = { id: 1 };
|
|
196
|
+
const obj2 = { id: 2 };
|
|
197
|
+
map.set(obj1, 3);
|
|
198
|
+
map.set(obj2, 4);
|
|
199
|
+
expect(map.get(obj1)).toBe(3);
|
|
200
|
+
expect(map.get(obj2)).toBe(4);
|
|
201
|
+
expect(map.has(obj1)).toBe(true);
|
|
202
|
+
// Test delete with primitive key
|
|
203
|
+
expect(map.delete("key1")).toBe(true);
|
|
204
|
+
expect(map.has("key1")).toBe(false);
|
|
205
|
+
expect(map.delete("nonexistent")).toBe(false);
|
|
206
|
+
// Test delete with object key
|
|
207
|
+
expect(map.delete(obj1)).toBe(true);
|
|
208
|
+
expect(map.has(obj1)).toBe(false);
|
|
209
|
+
// Test iteration
|
|
210
|
+
const entries = Array.from(map);
|
|
211
|
+
expect(entries.length).toBe(2);
|
|
212
|
+
expect(entries).toContainEqual(["key2", 2]);
|
|
213
|
+
expect(entries).toContainEqual([obj2, 4]);
|
|
214
|
+
// Test constructor with entries
|
|
215
|
+
const initialEntries = [
|
|
216
|
+
["initial1", 10],
|
|
217
|
+
[{ id: 3 }, 20]
|
|
218
|
+
];
|
|
219
|
+
const mapWithEntries = new MaybeWeakMap(initialEntries);
|
|
220
|
+
expect(mapWithEntries.get("initial1")).toBe(10);
|
|
221
|
+
expect(mapWithEntries.get(initialEntries[1][0])).toBe(20);
|
|
222
|
+
});
|
|
120
223
|
/**
|
|
121
224
|
* A map that stores values indexed by an array of keys. If the keys are objects and the environment supports WeakRefs,
|
|
122
225
|
* they are stored in a WeakMap.
|
|
@@ -198,3 +301,45 @@ export class DependenciesMap {
|
|
|
198
301
|
}
|
|
199
302
|
}
|
|
200
303
|
_c = Symbol.toStringTag;
|
|
304
|
+
import.meta.vitest?.test("DependenciesMap", ({ expect }) => {
|
|
305
|
+
// Test basic functionality
|
|
306
|
+
const map = new DependenciesMap();
|
|
307
|
+
// Test set and get
|
|
308
|
+
map.set(["key", 1], "value1");
|
|
309
|
+
expect(map.get(["key", 1])).toBe("value1");
|
|
310
|
+
// Test has
|
|
311
|
+
expect(map.has(["key", 1])).toBe(true);
|
|
312
|
+
expect(map.has(["key", 2])).toBe(false);
|
|
313
|
+
// Test with different dependencies
|
|
314
|
+
map.set(["key", 2], "value2");
|
|
315
|
+
expect(map.get(["key", 2])).toBe("value2");
|
|
316
|
+
expect(map.get(["key", 1])).toBe("value1"); // Original still exists
|
|
317
|
+
// Test delete
|
|
318
|
+
expect(map.delete(["key", 1])).toBe(true);
|
|
319
|
+
expect(map.has(["key", 1])).toBe(false);
|
|
320
|
+
expect(map.get(["key", 1])).toBeUndefined();
|
|
321
|
+
expect(map.has(["key", 2])).toBe(true); // Other key still exists
|
|
322
|
+
// Test delete non-existent key
|
|
323
|
+
expect(map.delete(["nonexistent", 1])).toBe(false);
|
|
324
|
+
// Test clear
|
|
325
|
+
map.clear();
|
|
326
|
+
expect(map.has(["key", 2])).toBe(false);
|
|
327
|
+
// Test with object keys
|
|
328
|
+
const objMap = new DependenciesMap();
|
|
329
|
+
const obj1 = { id: 1 };
|
|
330
|
+
const obj2 = { id: 2 };
|
|
331
|
+
objMap.set([obj1, 1], "object1");
|
|
332
|
+
objMap.set([obj2, 2], "object2");
|
|
333
|
+
expect(objMap.get([obj1, 1])).toBe("object1");
|
|
334
|
+
expect(objMap.get([obj2, 2])).toBe("object2");
|
|
335
|
+
// Test iteration
|
|
336
|
+
const iterMap = new DependenciesMap();
|
|
337
|
+
iterMap.set(["a"], 1);
|
|
338
|
+
iterMap.set(["b"], 2);
|
|
339
|
+
iterMap.set(["c"], 3);
|
|
340
|
+
const entries = Array.from(iterMap);
|
|
341
|
+
expect(entries.length).toBe(3);
|
|
342
|
+
expect(entries).toContainEqual([["a"], 1]);
|
|
343
|
+
expect(entries).toContainEqual([["b"], 2]);
|
|
344
|
+
expect(entries).toContainEqual([["c"], 3]);
|
|
345
|
+
});
|
package/dist/utils/math.js
CHANGED
|
@@ -4,3 +4,15 @@
|
|
|
4
4
|
export function remainder(n, d) {
|
|
5
5
|
return ((n % d) + Math.abs(d)) % d;
|
|
6
6
|
}
|
|
7
|
+
import.meta.vitest?.test("remainder", ({ expect }) => {
|
|
8
|
+
expect(remainder(10, 3)).toBe(1);
|
|
9
|
+
expect(remainder(10, 5)).toBe(0);
|
|
10
|
+
expect(remainder(10, 7)).toBe(3);
|
|
11
|
+
// Test with negative numbers
|
|
12
|
+
expect(remainder(-10, 3)).toBe(2);
|
|
13
|
+
expect(remainder(-5, 2)).toBe(1);
|
|
14
|
+
expect(remainder(-7, 4)).toBe(1);
|
|
15
|
+
// Test with decimal numbers
|
|
16
|
+
expect(remainder(10.5, 3)).toBeCloseTo(1.5);
|
|
17
|
+
expect(remainder(-10.5, 3)).toBeCloseTo(1.5);
|
|
18
|
+
});
|
package/dist/utils/numbers.js
CHANGED
|
@@ -21,9 +21,52 @@ export function prettyPrintWithMagnitudes(num) {
|
|
|
21
21
|
}
|
|
22
22
|
return toFixedMax(num, 1); // Handle numbers less than 1,000 without suffix.
|
|
23
23
|
}
|
|
24
|
+
import.meta.vitest?.test("prettyPrintWithMagnitudes", ({ expect }) => {
|
|
25
|
+
// Test different magnitudes
|
|
26
|
+
expect(prettyPrintWithMagnitudes(1000)).toBe("1k");
|
|
27
|
+
expect(prettyPrintWithMagnitudes(1500)).toBe("1.5k");
|
|
28
|
+
expect(prettyPrintWithMagnitudes(1000000)).toBe("1M");
|
|
29
|
+
expect(prettyPrintWithMagnitudes(1500000)).toBe("1.5M");
|
|
30
|
+
expect(prettyPrintWithMagnitudes(1000000000)).toBe("1bn");
|
|
31
|
+
expect(prettyPrintWithMagnitudes(1500000000)).toBe("1.5bn");
|
|
32
|
+
expect(prettyPrintWithMagnitudes(1000000000000)).toBe("1bln");
|
|
33
|
+
expect(prettyPrintWithMagnitudes(1500000000000)).toBe("1.5bln");
|
|
34
|
+
expect(prettyPrintWithMagnitudes(1000000000000000)).toBe("1trln");
|
|
35
|
+
expect(prettyPrintWithMagnitudes(1500000000000000)).toBe("1.5trln");
|
|
36
|
+
// Test small numbers
|
|
37
|
+
expect(prettyPrintWithMagnitudes(100)).toBe("100");
|
|
38
|
+
expect(prettyPrintWithMagnitudes(0)).toBe("0");
|
|
39
|
+
expect(prettyPrintWithMagnitudes(0.5)).toBe("0.5");
|
|
40
|
+
// Test negative numbers
|
|
41
|
+
expect(prettyPrintWithMagnitudes(-1000)).toBe("-1k");
|
|
42
|
+
expect(prettyPrintWithMagnitudes(-1500000)).toBe("-1.5M");
|
|
43
|
+
// Test special cases
|
|
44
|
+
expect(prettyPrintWithMagnitudes(NaN)).toBe("NaN");
|
|
45
|
+
expect(prettyPrintWithMagnitudes(Infinity)).toBe("∞");
|
|
46
|
+
expect(prettyPrintWithMagnitudes(-Infinity)).toBe("-∞");
|
|
47
|
+
});
|
|
24
48
|
export function toFixedMax(num, maxDecimals) {
|
|
25
49
|
return num.toFixed(maxDecimals).replace(/\.?0+$/, "");
|
|
26
50
|
}
|
|
51
|
+
import.meta.vitest?.test("toFixedMax", ({ expect }) => {
|
|
52
|
+
expect(toFixedMax(1, 2)).toBe("1");
|
|
53
|
+
expect(toFixedMax(1.2, 2)).toBe("1.2");
|
|
54
|
+
expect(toFixedMax(1.23, 2)).toBe("1.23");
|
|
55
|
+
expect(toFixedMax(1.234, 2)).toBe("1.23");
|
|
56
|
+
expect(toFixedMax(1.0, 2)).toBe("1");
|
|
57
|
+
expect(toFixedMax(1.20, 2)).toBe("1.2");
|
|
58
|
+
expect(toFixedMax(0, 2)).toBe("0");
|
|
59
|
+
});
|
|
27
60
|
export function numberCompare(a, b) {
|
|
28
61
|
return Math.sign(a - b);
|
|
29
62
|
}
|
|
63
|
+
import.meta.vitest?.test("numberCompare", ({ expect }) => {
|
|
64
|
+
expect(numberCompare(1, 2)).toBe(-1);
|
|
65
|
+
expect(numberCompare(2, 1)).toBe(1);
|
|
66
|
+
expect(numberCompare(1, 1)).toBe(0);
|
|
67
|
+
expect(numberCompare(0, 0)).toBe(0);
|
|
68
|
+
expect(numberCompare(-1, -2)).toBe(1);
|
|
69
|
+
expect(numberCompare(-2, -1)).toBe(-1);
|
|
70
|
+
expect(numberCompare(-1, 1)).toBe(-1);
|
|
71
|
+
expect(numberCompare(1, -1)).toBe(1);
|
|
72
|
+
});
|