@stackframe/stack-shared 2.7.20 → 2.7.22

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @stackframe/stack-shared
2
2
 
3
+ ## 2.7.22
4
+
5
+ ### Patch Changes
6
+
7
+ - Various changes
8
+
9
+ ## 2.7.21
10
+
11
+ ### Patch Changes
12
+
13
+ - Various changes
14
+
3
15
  ## 2.7.20
4
16
 
5
17
  ### Patch Changes
package/dist/crud.js CHANGED
@@ -52,3 +52,79 @@ export function createCrud(options) {
52
52
  hasDelete: !!admin.deleteSchema,
53
53
  };
54
54
  }
55
+ import { yupObject, yupString } from './schema-fields';
56
+ import.meta.vitest?.test("createCrud", ({ expect }) => {
57
+ // Test with empty options
58
+ const emptyCrud = createCrud({});
59
+ expect(emptyCrud.hasCreate).toBe(false);
60
+ expect(emptyCrud.hasRead).toBe(false);
61
+ expect(emptyCrud.hasUpdate).toBe(false);
62
+ expect(emptyCrud.hasDelete).toBe(false);
63
+ expect(emptyCrud.client.createSchema).toBeUndefined();
64
+ expect(emptyCrud.server.createSchema).toBeUndefined();
65
+ expect(emptyCrud.admin.createSchema).toBeUndefined();
66
+ // Test with client schemas only
67
+ const mockSchema = yupObject().shape({
68
+ name: yupString().defined(),
69
+ });
70
+ const clientOnlyCrud = createCrud({
71
+ clientCreateSchema: mockSchema,
72
+ clientReadSchema: mockSchema,
73
+ });
74
+ expect(clientOnlyCrud.hasCreate).toBe(true);
75
+ expect(clientOnlyCrud.hasRead).toBe(true);
76
+ expect(clientOnlyCrud.hasUpdate).toBe(false);
77
+ expect(clientOnlyCrud.hasDelete).toBe(false);
78
+ expect(clientOnlyCrud.client.createSchema).toBe(mockSchema);
79
+ expect(clientOnlyCrud.server.createSchema).toBe(mockSchema);
80
+ expect(clientOnlyCrud.admin.createSchema).toBe(mockSchema);
81
+ // Test with server overrides
82
+ const serverSchema = yupObject().shape({
83
+ name: yupString().defined(),
84
+ internalField: yupString().defined(),
85
+ });
86
+ const serverOverrideCrud = createCrud({
87
+ clientCreateSchema: mockSchema,
88
+ serverCreateSchema: serverSchema,
89
+ });
90
+ expect(serverOverrideCrud.hasCreate).toBe(true);
91
+ expect(serverOverrideCrud.client.createSchema).toBe(mockSchema);
92
+ expect(serverOverrideCrud.server.createSchema).toBe(serverSchema);
93
+ expect(serverOverrideCrud.admin.createSchema).toBe(serverSchema);
94
+ // Test with admin overrides
95
+ const adminSchema = yupObject().shape({
96
+ name: yupString().defined(),
97
+ internalField: yupString().defined(),
98
+ adminField: yupString().defined(),
99
+ });
100
+ const fullOverrideCrud = createCrud({
101
+ clientCreateSchema: mockSchema,
102
+ serverCreateSchema: serverSchema,
103
+ adminCreateSchema: adminSchema,
104
+ });
105
+ expect(fullOverrideCrud.hasCreate).toBe(true);
106
+ expect(fullOverrideCrud.client.createSchema).toBe(mockSchema);
107
+ expect(fullOverrideCrud.server.createSchema).toBe(serverSchema);
108
+ expect(fullOverrideCrud.admin.createSchema).toBe(adminSchema);
109
+ // Test with documentation
110
+ const crudWithDocs = createCrud({
111
+ clientCreateSchema: mockSchema,
112
+ docs: {
113
+ clientCreate: {
114
+ summary: "Create a resource",
115
+ description: "Creates a new resource",
116
+ tags: ["resources"],
117
+ },
118
+ },
119
+ });
120
+ expect(crudWithDocs.client.createDocs).toEqual({
121
+ summary: "Create a resource",
122
+ description: "Creates a new resource",
123
+ tags: ["resources"],
124
+ });
125
+ expect(crudWithDocs.server.createDocs).toEqual({
126
+ summary: "Create a resource",
127
+ description: "Creates a new resource",
128
+ tags: ["resources"],
129
+ });
130
+ });
@@ -27,6 +27,41 @@ function unwrapFromInner(dependencies, inner) {
27
27
  }
28
28
  }
29
29
  }
30
+ import.meta.vitest?.test("unwrapFromInner", ({ expect }) => {
31
+ // Test with empty dependencies and non-nested map
32
+ const nonNestedMap = { isNotNestedMap: true, value: "test" };
33
+ const result1 = unwrapFromInner([], nonNestedMap);
34
+ expect(result1.status).toBe("ok");
35
+ if (result1.status === "ok") {
36
+ expect(result1.data).toBe("test");
37
+ }
38
+ // Test with non-empty dependencies and non-nested map (should error)
39
+ expect(unwrapFromInner(["key"], nonNestedMap).status).toBe("error");
40
+ // Test with empty dependencies and nested map (should error)
41
+ const nestedMap = new Map([["key", { isNotNestedMap: true, value: "test" }]]);
42
+ expect(unwrapFromInner([], nestedMap).status).toBe("error");
43
+ // Test with matching dependencies and nested map
44
+ const result2 = unwrapFromInner(["key"], nestedMap);
45
+ expect(result2.status).toBe("ok");
46
+ if (result2.status === "ok") {
47
+ expect(result2.data).toBe("test");
48
+ }
49
+ // Test with non-matching dependencies and nested map
50
+ expect(unwrapFromInner(["wrongKey"], nestedMap).status).toBe("error");
51
+ // Test with deeply nested map
52
+ const deeplyNestedMap = new Map([
53
+ ["key1", new Map([
54
+ ["key2", { isNotNestedMap: true, value: "nested" }]
55
+ ])]
56
+ ]);
57
+ const result3 = unwrapFromInner(["key1", "key2"], deeplyNestedMap);
58
+ expect(result3.status).toBe("ok");
59
+ if (result3.status === "ok") {
60
+ expect(result3.data).toBe("nested");
61
+ }
62
+ // Test with partial match in deeply nested map
63
+ expect(unwrapFromInner(["key1", "wrongKey"], deeplyNestedMap).status).toBe("error");
64
+ });
30
65
  function wrapToInner(dependencies, value) {
31
66
  if (dependencies.length === 0) {
32
67
  return { isNotNestedMap: true, value };
@@ -39,6 +74,46 @@ function wrapToInner(dependencies, value) {
39
74
  const mapType = isWeak ? WeakMap : Map;
40
75
  return new mapType([[key, inner]]);
41
76
  }
77
+ import.meta.vitest?.test("wrapToInner", ({ expect }) => {
78
+ // Test with empty dependencies
79
+ const emptyResult = wrapToInner([], "test");
80
+ expect(emptyResult).toEqual({ isNotNestedMap: true, value: "test" });
81
+ // Test with single string dependency
82
+ const singleResult = wrapToInner(["key"], "test");
83
+ expect(singleResult instanceof Map).toBe(true);
84
+ // Need to cast to access Map methods
85
+ const singleMap = singleResult;
86
+ expect(singleMap.get("key")).toEqual({ isNotNestedMap: true, value: "test" });
87
+ // Test with multiple string dependencies
88
+ const multiResult = wrapToInner(["key1", "key2"], "test");
89
+ expect(multiResult instanceof Map).toBe(true);
90
+ // Need to cast to access Map methods
91
+ const multiMap = multiResult;
92
+ const innerMap = multiMap.get("key1");
93
+ expect(innerMap instanceof Map).toBe(true);
94
+ expect(innerMap.get("key2")).toEqual({ isNotNestedMap: true, value: "test" });
95
+ // Test with object dependency (should use WeakMap)
96
+ const obj = { test: true };
97
+ const objResult = wrapToInner([obj], "test");
98
+ expect(objResult instanceof WeakMap).toBe(true);
99
+ // Need to cast to access WeakMap methods
100
+ const objMap = objResult;
101
+ expect(objMap.get(obj)).toEqual({ isNotNestedMap: true, value: "test" });
102
+ // Test with unregistered symbol dependency (should use WeakMap)
103
+ const symbolObj = Symbol("test");
104
+ const symbolResult = wrapToInner([symbolObj], "test");
105
+ expect(symbolResult instanceof WeakMap).toBe(true);
106
+ // Need to cast to access WeakMap methods
107
+ const symbolMap = symbolResult;
108
+ expect(symbolMap.get(symbolObj)).toEqual({ isNotNestedMap: true, value: "test" });
109
+ // Test with registered symbol dependency (should use Map)
110
+ const registeredSymbol = Symbol.for("test");
111
+ const registeredSymbolResult = wrapToInner([registeredSymbol], "test");
112
+ expect(registeredSymbolResult instanceof Map).toBe(true);
113
+ // Need to cast to access Map methods
114
+ const registeredSymbolMap = registeredSymbolResult;
115
+ expect(registeredSymbolMap.get(registeredSymbol)).toEqual({ isNotNestedMap: true, value: "test" });
116
+ });
42
117
  /**
43
118
  * Like memo, but minimizes recomputation of the value at all costs (instead of useMemo which recomputes whenever the renderer feels like it).
44
119
  *
@@ -89,7 +89,7 @@ export declare class StackServerInterface extends StackClientInterface {
89
89
  }): Promise<void>;
90
90
  grantServerTeamUserPermission(teamId: string, userId: string, permissionId: string): Promise<void>;
91
91
  revokeServerTeamUserPermission(teamId: string, userId: string, permissionId: string): Promise<void>;
92
- deleteServerServerUser(userId: string): Promise<void>;
92
+ deleteServerUser(userId: string): Promise<void>;
93
93
  createServerContactChannel(data: ContactChannelsCrud['Server']['Create']): Promise<ContactChannelsCrud['Server']['Read']>;
94
94
  updateServerContactChannel(userId: string, contactChannelId: string, data: ContactChannelsCrud['Server']['Update']): Promise<ContactChannelsCrud['Server']['Read']>;
95
95
  deleteServerContactChannel(userId: string, contactChannelId: string): Promise<void>;
@@ -235,7 +235,7 @@ export class StackServerInterface extends StackClientInterface {
235
235
  body: JSON.stringify({}),
236
236
  }, null);
237
237
  }
238
- async deleteServerServerUser(userId) {
238
+ async deleteServerUser(userId) {
239
239
  await this.sendServerRequest(urlString `/users/${userId}`, {
240
240
  method: "DELETE",
241
241
  headers: {
@@ -54,7 +54,7 @@ function createKnownErrorConstructor(SuperClass, errorCode, create, constructorA
54
54
  // @ts-expect-error this is not a mixin, but TS detects it as one
55
55
  class KnownErrorImpl extends SuperClass {
56
56
  constructor(...args) {
57
- // @ts-expect-error
57
+ // @ts-expect-error DEVIN, STOP REMOVING THIS LINE YOU DUMBASS, YES TYPESCRIPT DOES THROW AN ERROR IF YOU DON'T HAVE IT, NO THIS IS NOT "UNUSED", AND YES YOU ARE BREAKING OUR CI PIPELINE EVERY TIME YOU DO IT
58
58
  super(...createFn(...args));
59
59
  this.name = `KnownError<${errorCode}>`;
60
60
  this.constructorArgs = args;
@@ -2,9 +2,23 @@ import { remainder } from "./math";
2
2
  export function typedIncludes(arr, item) {
3
3
  return arr.includes(item);
4
4
  }
5
+ import.meta.vitest?.test("typedIncludes", ({ expect }) => {
6
+ const arr = [1, 2, 3];
7
+ expect(typedIncludes(arr, 1)).toBe(true);
8
+ expect(typedIncludes(arr, 4)).toBe(false);
9
+ expect(typedIncludes(arr, "1")).toBe(false);
10
+ const strArr = ["a", "b", "c"];
11
+ expect(typedIncludes(strArr, "a")).toBe(true);
12
+ expect(typedIncludes(strArr, "d")).toBe(false);
13
+ });
5
14
  export function enumerate(arr) {
6
15
  return arr.map((item, index) => [index, item]);
7
16
  }
17
+ import.meta.vitest?.test("enumerate", ({ expect }) => {
18
+ expect(enumerate([])).toEqual([]);
19
+ expect(enumerate([1, 2, 3])).toEqual([[0, 1], [1, 2], [2, 3]]);
20
+ expect(enumerate(["a", "b", "c"])).toEqual([[0, "a"], [1, "b"], [2, "c"]]);
21
+ });
8
22
  export function isShallowEqual(a, b) {
9
23
  if (a.length !== b.length)
10
24
  return false;
@@ -53,6 +67,22 @@ export function groupBy(arr, key) {
53
67
  }
54
68
  return result;
55
69
  }
70
+ import.meta.vitest?.test("groupBy", ({ expect }) => {
71
+ expect(groupBy([], (x) => x)).toEqual(new Map());
72
+ const numbers = [1, 2, 3, 4, 5, 6];
73
+ const grouped = groupBy(numbers, (n) => n % 2 === 0 ? "even" : "odd");
74
+ expect(grouped.get("even")).toEqual([2, 4, 6]);
75
+ expect(grouped.get("odd")).toEqual([1, 3, 5]);
76
+ // Check the actual lengths of the words to ensure our test is correct
77
+ const words = ["apple", "banana", "cherry", "date", "elderberry"];
78
+ console.log("Word lengths:", words.map(w => `${w}: ${w.length}`));
79
+ const byLength = groupBy(words, (w) => w.length);
80
+ // Adjust expectations based on actual word lengths
81
+ expect(byLength.get(5)).toEqual(["apple"]);
82
+ expect(byLength.get(6)).toEqual(["banana", "cherry"]);
83
+ expect(byLength.get(4)).toEqual(["date"]);
84
+ expect(byLength.get(10)).toEqual(["elderberry"]);
85
+ });
56
86
  export function range(startInclusive, endExclusive, step) {
57
87
  if (endExclusive === undefined) {
58
88
  endExclusive = startInclusive;
@@ -75,12 +105,30 @@ import.meta.vitest?.test("range", ({ expect }) => {
75
105
  expect(range(0, 10, 3)).toEqual([0, 3, 6, 9]);
76
106
  });
77
107
  export function rotateLeft(arr, n) {
108
+ if (arr.length === 0)
109
+ return [];
78
110
  const index = remainder(n, arr.length);
79
- return [...arr.slice(n), arr.slice(0, n)];
111
+ return [...arr.slice(index), ...arr.slice(0, index)];
80
112
  }
113
+ import.meta.vitest?.test("rotateLeft", ({ expect }) => {
114
+ expect(rotateLeft([], 1)).toEqual([]);
115
+ expect(rotateLeft([1, 2, 3, 4, 5], 0)).toEqual([1, 2, 3, 4, 5]);
116
+ expect(rotateLeft([1, 2, 3, 4, 5], 1)).toEqual([2, 3, 4, 5, 1]);
117
+ expect(rotateLeft([1, 2, 3, 4, 5], 3)).toEqual([4, 5, 1, 2, 3]);
118
+ expect(rotateLeft([1, 2, 3, 4, 5], 5)).toEqual([1, 2, 3, 4, 5]);
119
+ expect(rotateLeft([1, 2, 3, 4, 5], 6)).toEqual([2, 3, 4, 5, 1]);
120
+ });
81
121
  export function rotateRight(arr, n) {
82
122
  return rotateLeft(arr, -n);
83
123
  }
124
+ import.meta.vitest?.test("rotateRight", ({ expect }) => {
125
+ expect(rotateRight([], 1)).toEqual([]);
126
+ expect(rotateRight([1, 2, 3, 4, 5], 0)).toEqual([1, 2, 3, 4, 5]);
127
+ expect(rotateRight([1, 2, 3, 4, 5], 1)).toEqual([5, 1, 2, 3, 4]);
128
+ expect(rotateRight([1, 2, 3, 4, 5], 3)).toEqual([3, 4, 5, 1, 2]);
129
+ expect(rotateRight([1, 2, 3, 4, 5], 5)).toEqual([1, 2, 3, 4, 5]);
130
+ expect(rotateRight([1, 2, 3, 4, 5], 6)).toEqual([5, 1, 2, 3, 4]);
131
+ });
84
132
  export function shuffle(arr) {
85
133
  const result = [...arr];
86
134
  for (let i = result.length - 1; i > 0; i--) {
@@ -89,9 +137,35 @@ export function shuffle(arr) {
89
137
  }
90
138
  return result;
91
139
  }
140
+ import.meta.vitest?.test("shuffle", ({ expect }) => {
141
+ // Test empty array
142
+ expect(shuffle([])).toEqual([]);
143
+ // Test single element array
144
+ expect(shuffle([1])).toEqual([1]);
145
+ // Test that shuffle returns a new array
146
+ const original = [1, 2, 3, 4, 5];
147
+ const shuffled = shuffle(original);
148
+ expect(shuffled).not.toBe(original);
149
+ // Test that all elements are preserved
150
+ expect(shuffled.sort((a, b) => a - b)).toEqual(original);
151
+ // Test with a larger array to ensure randomness
152
+ // This is a probabilistic test, but it's very unlikely to fail
153
+ const large = Array.from({ length: 100 }, (_, i) => i);
154
+ const shuffledLarge = shuffle(large);
155
+ expect(shuffledLarge).not.toEqual(large);
156
+ expect(shuffledLarge.sort((a, b) => a - b)).toEqual(large);
157
+ });
92
158
  export function outerProduct(arr1, arr2) {
93
159
  return arr1.flatMap((item1) => arr2.map((item2) => [item1, item2]));
94
160
  }
161
+ import.meta.vitest?.test("outerProduct", ({ expect }) => {
162
+ expect(outerProduct([], [])).toEqual([]);
163
+ expect(outerProduct([1], [])).toEqual([]);
164
+ expect(outerProduct([], [1])).toEqual([]);
165
+ expect(outerProduct([1], [2])).toEqual([[1, 2]]);
166
+ expect(outerProduct([1, 2], [3, 4])).toEqual([[1, 3], [1, 4], [2, 3], [2, 4]]);
167
+ expect(outerProduct(["a", "b"], [1, 2])).toEqual([["a", 1], ["a", 2], ["b", 1], ["b", 2]]);
168
+ });
95
169
  export function unique(arr) {
96
170
  return [...new Set(arr)];
97
171
  }
@@ -16,6 +16,39 @@ export function cacheFunction(f) {
16
16
  return value;
17
17
  });
18
18
  }
19
+ import.meta.vitest?.test("cacheFunction", ({ expect }) => {
20
+ // Test with a simple function
21
+ let callCount = 0;
22
+ const add = (a, b) => {
23
+ callCount++;
24
+ return a + b;
25
+ };
26
+ const cachedAdd = cacheFunction(add);
27
+ // First call should execute the function
28
+ expect(cachedAdd(1, 2)).toBe(3);
29
+ expect(callCount).toBe(1);
30
+ // Second call with same args should use cached result
31
+ expect(cachedAdd(1, 2)).toBe(3);
32
+ expect(callCount).toBe(1);
33
+ // Call with different args should execute the function again
34
+ expect(cachedAdd(2, 3)).toBe(5);
35
+ expect(callCount).toBe(2);
36
+ // Test with a function that returns objects
37
+ let objectCallCount = 0;
38
+ const createObject = (id) => {
39
+ objectCallCount++;
40
+ return { id };
41
+ };
42
+ const cachedCreateObject = cacheFunction(createObject);
43
+ // First call should execute the function
44
+ const obj1 = cachedCreateObject(1);
45
+ expect(obj1).toEqual({ id: 1 });
46
+ expect(objectCallCount).toBe(1);
47
+ // Second call with same args should use cached result
48
+ const obj2 = cachedCreateObject(1);
49
+ expect(obj2).toBe(obj1); // Same reference
50
+ expect(objectCallCount).toBe(1);
51
+ });
19
52
  export class AsyncCache {
20
53
  constructor(_fetcher, _options = {}) {
21
54
  this._fetcher = _fetcher;
@@ -4,5 +4,8 @@
4
4
  * to exist at runtime).
5
5
  */
6
6
  export function scrambleDuringCompileTime(t) {
7
+ if (Math.random() < 0.00001 && Math.random() > 0.99999 && Math.random() < 0.00001 && Math.random() > 0.99999) {
8
+ return "this will never happen";
9
+ }
7
10
  return t;
8
11
  }
@@ -4,13 +4,13 @@ export function isWeekend(date) {
4
4
  }
5
5
  import.meta.vitest?.test("isWeekend", ({ expect }) => {
6
6
  // Sunday (day 0)
7
- expect(isWeekend(new Date("2023-01-01"))).toBe(true);
7
+ expect(isWeekend(new Date(2023, 0, 1))).toBe(true);
8
8
  // Saturday (day 6)
9
- expect(isWeekend(new Date("2023-01-07"))).toBe(true);
9
+ expect(isWeekend(new Date(2023, 0, 7))).toBe(true);
10
10
  // Monday (day 1)
11
- expect(isWeekend(new Date("2023-01-02"))).toBe(false);
11
+ expect(isWeekend(new Date(2023, 0, 2))).toBe(false);
12
12
  // Friday (day 5)
13
- expect(isWeekend(new Date("2023-01-06"))).toBe(false);
13
+ expect(isWeekend(new Date(2023, 0, 6))).toBe(false);
14
14
  });
15
15
  const agoUnits = [
16
16
  [60, 'second'],
@@ -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
+ });
@@ -7,6 +7,34 @@ export function escapeHtml(unsafe) {
7
7
  .replace(/"/g, "&quot;")
8
8
  .replace(/'/g, "&#039;");
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("&lt;div&gt;");
17
+ expect(escapeHtml("a & b")).toBe("a &amp; b");
18
+ expect(escapeHtml('a "quoted" string')).toBe("a &quot;quoted&quot; string");
19
+ expect(escapeHtml("it's a test")).toBe("it&#039;s a test");
20
+ // Test with multiple special characters
21
+ expect(escapeHtml("<a href=\"test\">It's a link</a>")).toBe("&lt;a href=&quot;test&quot;&gt;It&#039;s a link&lt;/a&gt;");
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>&lt;script&gt;</div>");
35
+ // Test with multiple interpolations
36
+ expect(html `${1} + ${2} = ${"<3"}`).toBe("1 + 2 = &lt;3");
37
+ // Test with object interpolation
38
+ const obj = { toString: () => "<object>" };
39
+ expect(html `Object: ${obj}`).toBe("Object: &lt;object&gt;");
40
+ });
@@ -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
+ });