ocpp-ws-io 1.0.0-alpha

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.

Potentially problematic release.


This version of ocpp-ws-io might be problematic. Click here for more details.

Files changed (45) hide show
  1. package/.github/workflows/publish.yml +52 -0
  2. package/LICENSE +21 -0
  3. package/README.md +773 -0
  4. package/dist/adapters/redis.d.mts +73 -0
  5. package/dist/adapters/redis.d.ts +73 -0
  6. package/dist/adapters/redis.js +96 -0
  7. package/dist/adapters/redis.js.map +1 -0
  8. package/dist/adapters/redis.mjs +71 -0
  9. package/dist/adapters/redis.mjs.map +1 -0
  10. package/dist/index.d.mts +268 -0
  11. package/dist/index.d.ts +268 -0
  12. package/dist/index.js +38919 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/index.mjs +38855 -0
  15. package/dist/index.mjs.map +1 -0
  16. package/dist/types-6LVUoXof.d.mts +284 -0
  17. package/dist/types-6LVUoXof.d.ts +284 -0
  18. package/package.json +59 -0
  19. package/src/adapters/adapter.ts +40 -0
  20. package/src/adapters/redis.ts +144 -0
  21. package/src/client.ts +882 -0
  22. package/src/errors.ts +183 -0
  23. package/src/event-buffer.ts +73 -0
  24. package/src/index.ts +68 -0
  25. package/src/queue.ts +65 -0
  26. package/src/schemas/ocpp1_6.json +2376 -0
  27. package/src/schemas/ocpp2_0_1.json +11878 -0
  28. package/src/schemas/ocpp2_1.json +23176 -0
  29. package/src/server-client.ts +65 -0
  30. package/src/server.ts +374 -0
  31. package/src/standard-validators.ts +18 -0
  32. package/src/types.ts +316 -0
  33. package/src/util.ts +119 -0
  34. package/src/validator.ts +148 -0
  35. package/src/ws-util.ts +186 -0
  36. package/test/adapter.test.ts +88 -0
  37. package/test/client.test.ts +297 -0
  38. package/test/errors.test.ts +132 -0
  39. package/test/queue.test.ts +133 -0
  40. package/test/server.test.ts +274 -0
  41. package/test/util.test.ts +103 -0
  42. package/test/ws-util.test.ts +93 -0
  43. package/tsconfig.json +25 -0
  44. package/tsup.config.ts +16 -0
  45. package/vitest.config.ts +10 -0
@@ -0,0 +1,133 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { Queue } from "../src/queue.js";
3
+
4
+ describe("Queue", () => {
5
+ it("should initialize with concurrency of 1", () => {
6
+ const q = new Queue();
7
+ expect(q.concurrency).toBe(1);
8
+ expect(q.pending).toBe(0);
9
+ expect(q.running).toBe(0);
10
+ expect(q.size).toBe(0);
11
+ });
12
+
13
+ it("should accept a custom concurrency", () => {
14
+ const q = new Queue(5);
15
+ expect(q.concurrency).toBe(5);
16
+ });
17
+
18
+ it("should enforce minimum concurrency of 1", () => {
19
+ const q = new Queue(0);
20
+ expect(q.concurrency).toBe(1);
21
+ const q2 = new Queue(-5);
22
+ expect(q2.concurrency).toBe(1);
23
+ });
24
+
25
+ it("should execute a single task", async () => {
26
+ const q = new Queue();
27
+ const result = await q.push(async () => 42);
28
+ expect(result).toBe(42);
29
+ });
30
+
31
+ it("should execute tasks in order with concurrency 1", async () => {
32
+ const q = new Queue(1);
33
+ const order: number[] = [];
34
+
35
+ const results = await Promise.all([
36
+ q.push(async () => {
37
+ order.push(1);
38
+ return "a";
39
+ }),
40
+ q.push(async () => {
41
+ order.push(2);
42
+ return "b";
43
+ }),
44
+ q.push(async () => {
45
+ order.push(3);
46
+ return "c";
47
+ }),
48
+ ]);
49
+
50
+ expect(results).toEqual(["a", "b", "c"]);
51
+ expect(order).toEqual([1, 2, 3]);
52
+ });
53
+
54
+ it("should limit concurrent execution", async () => {
55
+ const q = new Queue(2);
56
+ let maxConcurrent = 0;
57
+ let current = 0;
58
+
59
+ const makeTask = () => async () => {
60
+ current++;
61
+ maxConcurrent = Math.max(maxConcurrent, current);
62
+ await new Promise((r) => setTimeout(r, 10));
63
+ current--;
64
+ return true;
65
+ };
66
+
67
+ await Promise.all([
68
+ q.push(makeTask()),
69
+ q.push(makeTask()),
70
+ q.push(makeTask()),
71
+ q.push(makeTask()),
72
+ ]);
73
+
74
+ expect(maxConcurrent).toBe(2);
75
+ });
76
+
77
+ it("should reject with task errors", async () => {
78
+ const q = new Queue();
79
+ await expect(
80
+ q.push(async () => {
81
+ throw new Error("fail");
82
+ }),
83
+ ).rejects.toThrow("fail");
84
+ });
85
+
86
+ it("should continue after task rejections", async () => {
87
+ const q = new Queue(1);
88
+
89
+ const p1 = q
90
+ .push(async () => {
91
+ throw new Error("first fails");
92
+ })
93
+ .catch(() => "caught");
94
+
95
+ const p2 = q.push(async () => "second ok");
96
+
97
+ const results = await Promise.all([p1, p2]);
98
+ expect(results).toEqual(["caught", "second ok"]);
99
+ });
100
+
101
+ it("should update concurrency dynamically", async () => {
102
+ const q = new Queue(1);
103
+ expect(q.concurrency).toBe(1);
104
+
105
+ q.setConcurrency(5);
106
+ expect(q.concurrency).toBe(5);
107
+ });
108
+
109
+ it("should track size correctly", async () => {
110
+ const q = new Queue(1);
111
+ let resolveFirst!: () => void;
112
+
113
+ const p1 = q.push(
114
+ () =>
115
+ new Promise<void>((r) => {
116
+ resolveFirst = r;
117
+ }),
118
+ );
119
+ const p2 = q.push(async () => "b");
120
+
121
+ // p1 is running, p2 is pending
122
+ expect(q.running).toBe(1);
123
+ expect(q.pending).toBe(1);
124
+ expect(q.size).toBe(2);
125
+
126
+ resolveFirst();
127
+ await Promise.all([p1, p2]);
128
+
129
+ // After resolution, allow microtask queue to drain
130
+ await new Promise((r) => setTimeout(r, 0));
131
+ expect(q.size).toBe(0);
132
+ });
133
+ });
@@ -0,0 +1,274 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { OCPPServer } from "../src/server.js";
3
+ import { OCPPClient } from "../src/client.js";
4
+ import type { OCPPServerClient } from "../src/server-client.js";
5
+
6
+ let server: OCPPServer;
7
+ let port: number;
8
+
9
+ const getPort = (srv: import("node:http").Server): number => {
10
+ const addr = srv.address();
11
+ if (addr && typeof addr !== "string") return addr.port;
12
+ return 0;
13
+ };
14
+
15
+ describe("OCPPServer", () => {
16
+ afterEach(async () => {
17
+ if (server) await server.close({ force: true }).catch(() => {});
18
+ });
19
+
20
+ it("should start listening on a port", async () => {
21
+ server = new OCPPServer({ protocols: ["ocpp1.6"] });
22
+ const httpServer = await server.listen(0);
23
+ const addr = httpServer.address();
24
+ expect(addr).toBeDefined();
25
+ expect(typeof addr !== "string" && addr?.port).toBeGreaterThan(0);
26
+ });
27
+
28
+ it("should reject connections without auth callback set", async () => {
29
+ server = new OCPPServer({ protocols: ["ocpp1.6"] });
30
+ // No auth callback set — should still accept (no auth check)
31
+ const httpServer = await server.listen(0);
32
+ port = getPort(httpServer);
33
+
34
+ const client = new OCPPClient({
35
+ identity: "CS001",
36
+ endpoint: `ws://localhost:${port}`,
37
+ protocols: ["ocpp1.6"],
38
+ reconnect: false,
39
+ });
40
+
41
+ await client.connect();
42
+ expect(client.state).toBe(OCPPClient.OPEN);
43
+ await client.close({ force: true });
44
+ });
45
+
46
+ it("should expose connected clients", async () => {
47
+ server = new OCPPServer({ protocols: ["ocpp1.6"] });
48
+ server.auth((accept) => accept({ protocol: "ocpp1.6" }));
49
+ const httpServer = await server.listen(0);
50
+ port = getPort(httpServer);
51
+
52
+ expect(server.clients.size).toBe(0);
53
+
54
+ const client = new OCPPClient({
55
+ identity: "CS001",
56
+ endpoint: `ws://localhost:${port}`,
57
+ protocols: ["ocpp1.6"],
58
+ reconnect: false,
59
+ });
60
+
61
+ await client.connect();
62
+
63
+ // Wait for server to register the client
64
+ await new Promise((r) => setTimeout(r, 100));
65
+
66
+ expect(server.clients.size).toBe(1);
67
+ const [serverClient] = server.clients;
68
+ expect(serverClient.identity).toBe("CS001");
69
+
70
+ await client.close({ force: true });
71
+ });
72
+
73
+ it('should emit "client" event when a station connects', async () => {
74
+ server = new OCPPServer({ protocols: ["ocpp1.6"] });
75
+ server.auth((accept) => accept({ protocol: "ocpp1.6" }));
76
+ const httpServer = await server.listen(0);
77
+ port = getPort(httpServer);
78
+
79
+ const clientReceived = new Promise<OCPPServerClient>((resolve) => {
80
+ server.on("client", (sc) => resolve(sc));
81
+ });
82
+
83
+ const client = new OCPPClient({
84
+ identity: "CS_EMIT",
85
+ endpoint: `ws://localhost:${port}`,
86
+ protocols: ["ocpp1.6"],
87
+ reconnect: false,
88
+ });
89
+
90
+ await client.connect();
91
+
92
+ const sc = await clientReceived;
93
+ expect(sc.identity).toBe("CS_EMIT");
94
+ expect(sc.handshake).toBeDefined();
95
+ expect(sc.handshake.identity).toBe("CS_EMIT");
96
+
97
+ await client.close({ force: true });
98
+ });
99
+
100
+ it("should reject connections via auth callback", async () => {
101
+ server = new OCPPServer({ protocols: ["ocpp1.6"] });
102
+ server.auth((_accept, reject) => {
103
+ reject(401, "Bad credentials");
104
+ });
105
+ const httpServer = await server.listen(0);
106
+ port = getPort(httpServer);
107
+
108
+ const client = new OCPPClient({
109
+ identity: "CS_REJECTED",
110
+ endpoint: `ws://localhost:${port}`,
111
+ protocols: ["ocpp1.6"],
112
+ reconnect: false,
113
+ });
114
+
115
+ // Suppress the 'error' event to prevent unhandled error
116
+ client.on("error", () => {});
117
+
118
+ await expect(client.connect()).rejects.toThrow();
119
+ });
120
+
121
+ it("should parse identity from URL path", async () => {
122
+ server = new OCPPServer({ protocols: ["ocpp1.6"] });
123
+ let receivedIdentity = "";
124
+
125
+ server.auth((accept, _reject, handshake) => {
126
+ receivedIdentity = handshake.identity;
127
+ accept({ protocol: "ocpp1.6" });
128
+ });
129
+
130
+ const httpServer = await server.listen(0);
131
+ port = getPort(httpServer);
132
+
133
+ const client = new OCPPClient({
134
+ identity: "MY-STATION-123",
135
+ endpoint: `ws://localhost:${port}`,
136
+ protocols: ["ocpp1.6"],
137
+ reconnect: false,
138
+ });
139
+
140
+ await client.connect();
141
+ expect(receivedIdentity).toBe("MY-STATION-123");
142
+ await client.close({ force: true });
143
+ });
144
+
145
+ it("should remove client on disconnect", async () => {
146
+ server = new OCPPServer({ protocols: ["ocpp1.6"] });
147
+ server.auth((accept) => accept({ protocol: "ocpp1.6" }));
148
+ const httpServer = await server.listen(0);
149
+ port = getPort(httpServer);
150
+
151
+ const client = new OCPPClient({
152
+ identity: "CS_DISC",
153
+ endpoint: `ws://localhost:${port}`,
154
+ protocols: ["ocpp1.6"],
155
+ reconnect: false,
156
+ });
157
+
158
+ await client.connect();
159
+ await new Promise((r) => setTimeout(r, 100));
160
+ expect(server.clients.size).toBe(1);
161
+
162
+ await client.close();
163
+ await new Promise((r) => setTimeout(r, 100));
164
+ expect(server.clients.size).toBe(0);
165
+ });
166
+
167
+ it("should close all clients on server close", async () => {
168
+ server = new OCPPServer({ protocols: ["ocpp1.6"] });
169
+ server.auth((accept) => accept({ protocol: "ocpp1.6" }));
170
+ const httpServer = await server.listen(0);
171
+ port = getPort(httpServer);
172
+
173
+ const client = new OCPPClient({
174
+ identity: "CS_CLOSE_ALL",
175
+ endpoint: `ws://localhost:${port}`,
176
+ protocols: ["ocpp1.6"],
177
+ reconnect: false,
178
+ });
179
+
180
+ await client.connect();
181
+ await new Promise((r) => setTimeout(r, 100));
182
+
183
+ await server.close({ force: true });
184
+ expect(server.clients.size).toBe(0);
185
+ });
186
+ });
187
+
188
+ describe("OCPPServerClient", () => {
189
+ afterEach(async () => {
190
+ if (server) await server.close({ force: true }).catch(() => {});
191
+ });
192
+
193
+ it("should inherit options from server", async () => {
194
+ server = new OCPPServer({
195
+ protocols: ["ocpp1.6"],
196
+ callTimeoutMs: 5000,
197
+ });
198
+ server.auth((accept) => accept({ protocol: "ocpp1.6" }));
199
+ const httpServer = await server.listen(0);
200
+ port = getPort(httpServer);
201
+
202
+ const clientReceived = new Promise<OCPPServerClient>((resolve) => {
203
+ server.on("client", (sc) => resolve(sc));
204
+ });
205
+
206
+ const client = new OCPPClient({
207
+ identity: "CS_INHERIT",
208
+ endpoint: `ws://localhost:${port}`,
209
+ protocols: ["ocpp1.6"],
210
+ reconnect: false,
211
+ });
212
+
213
+ await client.connect();
214
+ const sc = await clientReceived;
215
+
216
+ expect(sc.identity).toBe("CS_INHERIT");
217
+ expect(sc.state).toBe(OCPPClient.OPEN);
218
+ expect(sc.protocol).toBe("ocpp1.6");
219
+
220
+ await client.close({ force: true });
221
+ });
222
+
223
+ it("should reject connect() call", async () => {
224
+ server = new OCPPServer({ protocols: ["ocpp1.6"] });
225
+ server.auth((accept) => accept({ protocol: "ocpp1.6" }));
226
+ const httpServer = await server.listen(0);
227
+ port = getPort(httpServer);
228
+
229
+ const clientReceived = new Promise<OCPPServerClient>((resolve) => {
230
+ server.on("client", (sc) => resolve(sc));
231
+ });
232
+
233
+ const client = new OCPPClient({
234
+ identity: "CS_NO_CONNECT",
235
+ endpoint: `ws://localhost:${port}`,
236
+ protocols: ["ocpp1.6"],
237
+ reconnect: false,
238
+ });
239
+
240
+ await client.connect();
241
+ const sc = await clientReceived;
242
+
243
+ await expect(sc.connect()).rejects.toThrow(
244
+ "Cannot connect from server client",
245
+ );
246
+ await client.close({ force: true });
247
+ });
248
+
249
+ it("should expose session data", async () => {
250
+ server = new OCPPServer({ protocols: ["ocpp1.6"] });
251
+ server.auth((accept) => accept({ protocol: "ocpp1.6" }));
252
+ const httpServer = await server.listen(0);
253
+ port = getPort(httpServer);
254
+
255
+ const clientReceived = new Promise<OCPPServerClient>((resolve) => {
256
+ server.on("client", (sc) => resolve(sc));
257
+ });
258
+
259
+ const client = new OCPPClient({
260
+ identity: "CS_SESSION",
261
+ endpoint: `ws://localhost:${port}`,
262
+ protocols: ["ocpp1.6"],
263
+ reconnect: false,
264
+ });
265
+
266
+ await client.connect();
267
+ const sc = await clientReceived;
268
+
269
+ expect(sc.session).toBeDefined();
270
+ expect(typeof sc.session).toBe("object");
271
+
272
+ await client.close({ force: true });
273
+ });
274
+ });
@@ -0,0 +1,103 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ createRPCError,
4
+ getErrorPlainObject,
5
+ getPackageIdent,
6
+ } from "../src/util.js";
7
+ import {
8
+ RPCGenericError,
9
+ RPCNotImplementedError,
10
+ RPCFormatViolationError,
11
+ RPCInternalError,
12
+ RPCSecurityError,
13
+ RPCFrameworkError,
14
+ } from "../src/errors.js";
15
+
16
+ describe("createRPCError", () => {
17
+ it('should create RPCGenericError for "GenericError" code', () => {
18
+ const err = createRPCError("GenericError", "test msg");
19
+ expect(err).toBeInstanceOf(RPCGenericError);
20
+ expect(err.rpcErrorCode).toBe("GenericError");
21
+ expect(err.message).toBe("test msg");
22
+ });
23
+
24
+ it('should create RPCNotImplementedError for "NotImplemented" code', () => {
25
+ const err = createRPCError("NotImplemented");
26
+ expect(err).toBeInstanceOf(RPCNotImplementedError);
27
+ expect(err.rpcErrorCode).toBe("NotImplemented");
28
+ });
29
+
30
+ it("should create RPCFormatViolationError", () => {
31
+ const err = createRPCError("FormatViolation", "bad format");
32
+ expect(err).toBeInstanceOf(RPCFormatViolationError);
33
+ expect(err.rpcErrorCode).toBe("FormatViolation");
34
+ });
35
+
36
+ it("should create RPCInternalError", () => {
37
+ const err = createRPCError("InternalError", "internal");
38
+ expect(err).toBeInstanceOf(RPCInternalError);
39
+ });
40
+
41
+ it("should create RPCSecurityError", () => {
42
+ const err = createRPCError("SecurityError");
43
+ expect(err).toBeInstanceOf(RPCSecurityError);
44
+ });
45
+
46
+ it("should create RPCFrameworkError", () => {
47
+ const err = createRPCError("RpcFrameworkError");
48
+ expect(err).toBeInstanceOf(RPCFrameworkError);
49
+ });
50
+
51
+ it("should fallback to RPCGenericError for unknown error codes", () => {
52
+ const err = createRPCError("SomethingUnknown", "unknown code");
53
+ expect(err).toBeInstanceOf(RPCGenericError);
54
+ expect(err.rpcErrorCode).toBe("GenericError");
55
+ });
56
+
57
+ it("should include details in error when provided", () => {
58
+ const details = { key: "value" };
59
+ const err = createRPCError("GenericError", "msg", details);
60
+ expect(err.details).toEqual({ key: "value" });
61
+ });
62
+
63
+ it("should default details to empty object", () => {
64
+ const err = createRPCError("GenericError", "msg");
65
+ expect(err.details).toEqual({});
66
+ });
67
+ });
68
+
69
+ describe("getErrorPlainObject", () => {
70
+ it("should convert Error to plain object", () => {
71
+ const err = new Error("hello");
72
+ const obj = getErrorPlainObject(err);
73
+ expect(obj.message).toBe("hello");
74
+ expect(typeof obj.stack).toBe("string");
75
+ });
76
+
77
+ it("should handle RPCError with custom properties", () => {
78
+ const err = createRPCError("NotImplemented", "no handler");
79
+ const obj = getErrorPlainObject(err);
80
+ expect(obj.rpcErrorCode).toBe("NotImplemented");
81
+ expect(obj.message).toBe("no handler");
82
+ });
83
+
84
+ it("should handle errors with circular references gracefully", () => {
85
+ const err = new Error("circular");
86
+ (err as unknown as Record<string, unknown>).self = err;
87
+ // Should not throw, should return at least name and message
88
+ const obj = getErrorPlainObject(err);
89
+ expect(obj.name).toBe("Error");
90
+ expect(obj.message).toBe("circular");
91
+ });
92
+ });
93
+
94
+ describe("getPackageIdent", () => {
95
+ it("should return package identifier string", () => {
96
+ const ident = getPackageIdent();
97
+ expect(ident).toContain("ocpp-ws-io");
98
+ });
99
+
100
+ it("should return consistent value on repeated calls", () => {
101
+ expect(getPackageIdent()).toBe(getPackageIdent());
102
+ });
103
+ });
@@ -0,0 +1,93 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { parseSubprotocols, isValidStatusCode } from "../src/ws-util.js";
3
+
4
+ describe("parseSubprotocols", () => {
5
+ it("should parse a single subprotocol", () => {
6
+ const result = parseSubprotocols("ocpp1.6");
7
+ expect(result).toEqual(new Set(["ocpp1.6"]));
8
+ });
9
+
10
+ it("should parse multiple subprotocols", () => {
11
+ const result = parseSubprotocols("ocpp1.6, ocpp2.0.1");
12
+ expect(result).toEqual(new Set(["ocpp1.6", "ocpp2.0.1"]));
13
+ });
14
+
15
+ it("should parse multiple subprotocols with extra whitespace", () => {
16
+ const result = parseSubprotocols("ocpp1.6 , ocpp2.0.1 , ocpp2.1");
17
+ expect(result).toEqual(new Set(["ocpp1.6", "ocpp2.0.1", "ocpp2.1"]));
18
+ });
19
+
20
+ it("should parse a single protocol without commas", () => {
21
+ const result = parseSubprotocols("chat");
22
+ expect(result).toEqual(new Set(["chat"]));
23
+ });
24
+
25
+ it("should throw on empty string", () => {
26
+ expect(() => parseSubprotocols("")).toThrow(SyntaxError);
27
+ });
28
+
29
+ it("should throw on leading comma", () => {
30
+ expect(() => parseSubprotocols(",ocpp1.6")).toThrow(SyntaxError);
31
+ });
32
+
33
+ it("should throw on trailing comma", () => {
34
+ expect(() => parseSubprotocols("ocpp1.6,")).toThrow(SyntaxError);
35
+ });
36
+
37
+ it("should throw on duplicate protocols", () => {
38
+ expect(() => parseSubprotocols("ocpp1.6, ocpp1.6")).toThrow(SyntaxError);
39
+ expect(() => parseSubprotocols("ocpp1.6, ocpp1.6")).toThrow("duplicated");
40
+ });
41
+
42
+ it("should throw on invalid characters", () => {
43
+ expect(() => parseSubprotocols("ocpp[1.6]")).toThrow(SyntaxError);
44
+ });
45
+
46
+ it("should handle tab whitespace", () => {
47
+ const result = parseSubprotocols("ocpp1.6\t,\tocpp2.0.1");
48
+ expect(result).toEqual(new Set(["ocpp1.6", "ocpp2.0.1"]));
49
+ });
50
+ });
51
+
52
+ describe("isValidStatusCode", () => {
53
+ it("should accept 1000 (normal close)", () => {
54
+ expect(isValidStatusCode(1000)).toBe(true);
55
+ });
56
+
57
+ it("should accept 1001 (going away)", () => {
58
+ expect(isValidStatusCode(1001)).toBe(true);
59
+ });
60
+
61
+ it("should accept 1002 (protocol error)", () => {
62
+ expect(isValidStatusCode(1002)).toBe(true);
63
+ });
64
+
65
+ it("should accept 1011 (unexpected condition)", () => {
66
+ expect(isValidStatusCode(1011)).toBe(true);
67
+ });
68
+
69
+ it("should reject 1004 (reserved)", () => {
70
+ expect(isValidStatusCode(1004)).toBe(false);
71
+ });
72
+
73
+ it("should reject 1005 (no status received)", () => {
74
+ expect(isValidStatusCode(1005)).toBe(false);
75
+ });
76
+
77
+ it("should reject 1006 (abnormal closure)", () => {
78
+ expect(isValidStatusCode(1006)).toBe(false);
79
+ });
80
+
81
+ it("should accept 3000-4999 (application defined)", () => {
82
+ expect(isValidStatusCode(3000)).toBe(true);
83
+ expect(isValidStatusCode(4999)).toBe(true);
84
+ expect(isValidStatusCode(4000)).toBe(true);
85
+ });
86
+
87
+ it("should reject codes outside valid ranges", () => {
88
+ expect(isValidStatusCode(999)).toBe(false);
89
+ expect(isValidStatusCode(5000)).toBe(false);
90
+ expect(isValidStatusCode(0)).toBe(false);
91
+ expect(isValidStatusCode(2000)).toBe(false);
92
+ });
93
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "lib": ["ES2022"],
7
+ "outDir": "dist",
8
+ "rootDir": "src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "sourceMap": true,
16
+ "resolveJsonModule": true,
17
+ "isolatedModules": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "noImplicitReturns": true,
21
+ "noFallthroughCasesInSwitch": true
22
+ },
23
+ "include": ["src/**/*"],
24
+ "exclude": ["node_modules", "dist", "test"]
25
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: {
5
+ index: "src/index.ts",
6
+ "adapters/redis": "src/adapters/redis.ts",
7
+ },
8
+ format: ["cjs", "esm"],
9
+ dts: true,
10
+ splitting: false,
11
+ sourcemap: true,
12
+ clean: true,
13
+ outDir: "dist",
14
+ target: "node18",
15
+ shims: true,
16
+ });
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ testTimeout: 10000,
7
+ hookTimeout: 10000,
8
+ include: ["test/**/*.test.ts"],
9
+ },
10
+ });