Skip to content

Commit 5ff9417

Browse files
authored
Simple config and opt-in namespaces (#90)
* Removing createConfig, allowing empty argument for Config::constructor. * Fix compatibility test. * Test for no namespaces. * REF: introducing createSimpleConfig() and removing fallback from Config. * Update README.md * Update README.md * Upd compat test. * Upd config test. * Update src/config.ts * Changelog: 0.13.0. * Shortening.
1 parent 316af38 commit 5ff9417

12 files changed

+158
-132
lines changed

CHANGELOG.md

+31
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,37 @@
22

33
## Version 0
44

5+
### v0.13.0
6+
7+
- Config creation changes aim to improve the clarity and make it easier to begin using this library for the first time;
8+
- Easier config for a simple applications:
9+
- Replacing `createConfig()` with `createSimpleConfig()` - for a single namespace (root namespace only).
10+
- Making namespaces opt-in feature:
11+
- Use the exposed `new Config()` and its `.addNamespace()` method of each namespace;
12+
- Fallbacks removed from `Config::constructor` — it creates no namespaces by default,
13+
but `addNamespace` creates root namespace when `path` prop is omitted;
14+
- See the migration advice below.
15+
16+
```ts
17+
// if using the root namespace only:
18+
import { createSimpleConfig } from "zod-sockets";
19+
const simpleConfig = createSimpleConfig({
20+
/* logger, timeout, emission, hooks, metadata */
21+
});
22+
23+
// if using namespaces other than "/":
24+
import { Config } from "zod-sockets";
25+
const config = new Config({ logger, timeout })
26+
.addNamespace({
27+
path: "ns1",
28+
/* emission, hooks, metadata */
29+
})
30+
.addNamespace({
31+
path: "ns2",
32+
/* emission, hooks, metadata */
33+
});
34+
```
35+
536
### v0.12.0
637

738
- Switching to AsyncAPI version 3.0.0 for generating documentation:

README.md

+46-47
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ yarn add zod-sockets zod socket.io typescript
4343
## Set up config
4444

4545
```typescript
46-
import { createConfig } from "zod-sockets";
46+
import { createSimpleConfig } from "zod-sockets";
4747

48-
// defaults: root namespace only, console logger, timeout 2s
49-
const config = createConfig();
48+
// shorthand for root namespace only, defaults: console logger, timeout 2s
49+
const config = createSimpleConfig();
5050
```
5151

5252
## Create a factory
@@ -111,36 +111,6 @@ for sending the `ping` event to `ws://localhost:8090` with acknowledgement.
111111

112112
# Basic features
113113

114-
## Namespaces first
115-
116-
Namespaces allow you to separate incoming and outgoing events into groups, in which events can have the same name, but
117-
different essence, payload and handlers. You can add `namespaces` to the argument of `createConfig()` or use
118-
`addNamespace()` method after it. The default namespace is a root one having `path` equal to `/`. Namespaces may have
119-
`emission` and `hooks`.
120-
Read the Socket.IO [documentation on namespaces](https://socket.io/docs/v4/namespaces/).
121-
122-
```typescript
123-
import { createConfig } from "zod-sockets";
124-
125-
const config = createConfig({
126-
namespaces: {
127-
// The namespace "/public"
128-
public: {
129-
emission: { chat: { schema } },
130-
hooks: {
131-
onStartup: () => {},
132-
onConnection: () => {},
133-
onDisconnect: () => {},
134-
onAnyIncoming: () => {},
135-
onAnyOutgoing: () => {},
136-
},
137-
},
138-
},
139-
}).addNamespace({
140-
path: "private", // The namespace "/private" has no emission
141-
});
142-
```
143-
144114
## Emission
145115

146116
The outgoing events should be configured using `z.tuple()` schemas. Those tuples describe the types of the arguments
@@ -151,10 +121,9 @@ development. Consider the following examples of two outgoing events, with and wi
151121

152122
```typescript
153123
import { z } from "zod";
154-
import { createConfig } from "zod-sockets";
124+
import { createSimpleConfig } from "zod-sockets";
155125

156-
const config = createConfig().addNamespace({
157-
// path: "/", // optional, default: root namespace
126+
const config = createSimpleConfig({
158127
emission: {
159128
// enabling Socket::emit("chat", "message", { from: "someone" })
160129
chat: {
@@ -222,15 +191,15 @@ The library supports any logger having `info()`, `debug()`, `error()` and
222191

223192
```typescript
224193
import pino, { Logger } from "pino";
225-
import { createConfig } from "zod-sockets";
194+
import { createSimpleConfig } from "zod-sockets";
226195

227196
const logger = pino({
228197
transport: {
229198
target: "pino-pretty",
230199
options: { colorize: true },
231200
},
232201
});
233-
const config = createConfig({ logger });
202+
const config = createSimpleConfig({ logger });
234203

235204
// Setting the type of logger used
236205
declare module "zod-sockets" {
@@ -241,16 +210,16 @@ declare module "zod-sockets" {
241210
### With Express Zod API
242211

243212
If you're using `express-zod-api`, you can reuse the same logger. If it's a custom logger — supply the same instance to
244-
both `createConfig()` methods of two libraries. In case you're using the default `winston` logger provided by
213+
configs of both libraries. In case you're using the default `winston` logger provided by
245214
`express-zod-api`, you can obtain its instance from the returns of the `createServer()` method.
246215

247216
```typescript
248217
import { createServer } from "express-zod-api";
249-
import { createConfig } from "zod-sockets";
218+
import { createSimpleConfig } from "zod-sockets";
250219
import type { Logger } from "winston";
251220

252221
const { logger } = await createServer();
253-
const config = createConfig({ logger });
222+
const config = createSimpleConfig({ logger });
254223

255224
// Setting the type of logger used
256225
declare module "zod-sockets" {
@@ -342,9 +311,9 @@ emit events regardless the incoming ones by setting the `onConnection` property
342311
argument, which has a similar interface except `input` and fires for every connected client:
343312

344313
```typescript
345-
import { createConfig } from "zod-sockets";
314+
import { createSimpleConfig } from "zod-sockets";
346315

347-
const config = createConfig().addNamespace({
316+
const config = createSimpleConfig({
348317
// emission: { ... },
349318
hooks: {
350319
onConnection: async ({ client, withRooms, all }) => {
@@ -360,9 +329,9 @@ Moreover, you can emit events regardless the client activity at all by setting t
360329
of the `addNamespace()` argument. The implementation may have a `setInterval()` for recurring emission.
361330

362331
```typescript
363-
import { createConfig } from "zod-sockets";
332+
import { createSimpleConfig } from "zod-sockets";
364333

365-
const config = createConfig().addNamespace({
334+
const config = createSimpleConfig({
366335
hooks: {
367336
onStartup: async ({ all, withRooms }) => {
368337
// sending to everyone in a room
@@ -440,9 +409,9 @@ Please avoid transformations in those schemas since they are not going to be app
440409

441410
```typescript
442411
import { z } from "zod";
443-
import { createConfig } from "zod-sockets";
412+
import { createSimpleConfig } from "zod-sockets";
444413

445-
const config = createConfig().addNamespace({
414+
const config = createSimpleConfig({
446415
metadata: z.object({
447416
/** @desc Number of messages sent to the chat */
448417
msgCount: z.number().int(),
@@ -475,6 +444,36 @@ const handler = async ({ client }) => {
475444
};
476445
```
477446

447+
## Namespaces
448+
449+
Namespaces allow you to separate incoming and outgoing events into groups, in which events can have the same name, but
450+
different essence, payload and handlers. For using namespaces replace the `createSimpleConfig()` method with
451+
`new Config()`, then use its `.addNamespace()` method for each namespace. Namespaces may have `emission` and `hooks`.
452+
Read the Socket.IO [documentation on namespaces](https://socket.io/docs/v4/namespaces/).
453+
454+
```typescript
455+
import { Config } from "zod-sockets";
456+
457+
const config = new Config({
458+
logger,
459+
timeout: 2000,
460+
})
461+
.addNamespace({
462+
// The namespace "/public"
463+
emission: { chat: { schema } },
464+
hooks: {
465+
onStartup: () => {},
466+
onConnection: () => {},
467+
onDisconnect: () => {},
468+
onAnyIncoming: () => {},
469+
onAnyOutgoing: () => {},
470+
},
471+
})
472+
.addNamespace({
473+
path: "private", // The namespace "/private" has no emission
474+
});
475+
```
476+
478477
# Integration
479478

480479
## Exporting types for frontend

example/config.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { z } from "zod";
2-
import { createConfig } from "../src";
2+
import { createSimpleConfig } from "../src";
33

4-
export const config = createConfig().addNamespace({
4+
export const config = createSimpleConfig({
55
emission: {
66
time: {
77
schema: z.tuple([

src/actions-factory.spec.ts

+3-10
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,10 @@ import { describe, expect, test, vi } from "vitest";
22
import { z } from "zod";
33
import { Action } from "./action";
44
import { ActionsFactory } from "./actions-factory";
5-
import { createConfig } from "./config";
6-
import { AbstractLogger } from "./logger";
5+
import { createSimpleConfig } from "./config";
76

87
describe("ActionsFactory", () => {
9-
const factory = new ActionsFactory(
10-
createConfig({
11-
namespaces: {},
12-
timeout: 2000,
13-
logger: { debug: vi.fn() } as unknown as AbstractLogger,
14-
}),
15-
);
8+
const factory = new ActionsFactory(createSimpleConfig());
169

1710
describe("constructor", () => {
1811
test("should create a factory", () => {
@@ -24,7 +17,7 @@ describe("ActionsFactory", () => {
2417
factory.build({
2518
event: "test",
2619
input: z.tuple([z.string()]),
27-
handler: vi.fn(),
20+
handler: vi.fn<any>(),
2821
}),
2922
).toBeInstanceOf(Action);
3023
});

src/attach.spec.ts

+5-9
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Server } from "socket.io";
33
import { describe, expect, test, vi } from "vitest";
44
import { z } from "zod";
55
import { attachSockets } from "./attach";
6-
import { createConfig } from "./config";
6+
import { createSimpleConfig } from "./config";
77
import { AbstractLogger } from "./logger";
88

99
describe("Attach", () => {
@@ -67,16 +67,12 @@ describe("Attach", () => {
6767
io: ioMock as unknown as Server,
6868
target: targetMock as unknown as http.Server,
6969
actions: actionsMock,
70-
config: createConfig({
70+
config: createSimpleConfig({
7171
startupLogo: false,
7272
timeout: 100,
73-
namespaces: {
74-
"/": {
75-
emission: {},
76-
hooks,
77-
metadata: z.object({ name: z.string() }),
78-
},
79-
},
73+
emission: {},
74+
hooks,
75+
metadata: z.object({ name: z.string() }),
8076
logger: loggerMock as unknown as AbstractLogger,
8177
}),
8278
});

src/config.spec.ts

+40-14
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,44 @@
11
import { describe, expect, test, vi } from "vitest";
22
import { z } from "zod";
3-
import { Config, createConfig } from "./config";
3+
import { Config, createSimpleConfig } from "./config";
44
import { AbstractLogger } from "./logger";
55

66
describe("Config", () => {
7-
describe("createConfig()", () => {
7+
describe("::constructor", () => {
88
test("should create config without any argument", () => {
9-
const config = createConfig();
9+
const config = new Config();
1010
expect(config).toBeInstanceOf(Config);
1111
expect(config.logger).toEqual(console);
1212
expect(config.timeout).toBe(2000);
13-
expect(config.namespaces).toEqual({
14-
"/": { emission: {}, hooks: {}, metadata: expect.any(z.ZodObject) },
15-
});
13+
expect(config.namespaces).toEqual({});
1614
});
1715

1816
test("should create the class instance from the definition", () => {
19-
const config = createConfig({
17+
const config = new Config({
2018
namespaces: {
2119
"/": { emission: {}, hooks: {}, metadata: z.object({}) },
2220
test: { emission: {}, hooks: {}, metadata: z.object({}) },
2321
},
24-
timeout: 2000,
22+
timeout: 3000,
2523
logger: { debug: vi.fn() } as unknown as AbstractLogger,
2624
});
2725
expect(config).toBeInstanceOf(Config);
2826
expect(config.logger).toEqual({ debug: expect.any(Function) });
29-
expect(config.timeout).toBe(2000);
27+
expect(config.timeout).toBe(3000);
3028
expect(config.namespaces).toEqual({
3129
"/": { emission: {}, hooks: {}, metadata: expect.any(z.ZodObject) },
3230
test: { emission: {}, hooks: {}, metadata: expect.any(z.ZodObject) },
3331
});
3432
});
33+
});
3534

36-
test("should set defaults and provide namespace augmentation method", () => {
37-
const base = createConfig({});
35+
describe(".addNamespace()", () => {
36+
test("should provide namespace augmentation method", () => {
37+
const base = new Config();
3838
expect(base).toBeInstanceOf(Config);
3939
expect(base.logger).toEqual(console);
4040
expect(base.timeout).toBe(2000);
41-
expect(base.namespaces).toEqual({
42-
"/": { emission: {}, hooks: {}, metadata: expect.any(z.ZodObject) },
43-
});
41+
expect(base.namespaces).toEqual({});
4442
const schema = z.tuple([]);
4543
const config = base.addNamespace({
4644
path: "/",
@@ -56,4 +54,32 @@ describe("Config", () => {
5654
});
5755
});
5856
});
57+
58+
describe("createSimpleConfig()", () => {
59+
test("should set defaults", () => {
60+
const config = createSimpleConfig();
61+
expect(config).toBeInstanceOf(Config);
62+
expect(config.logger).toEqual(console);
63+
expect(config.timeout).toBe(2000);
64+
expect(config.namespaces).toEqual({
65+
"/": { emission: {}, hooks: {}, metadata: expect.any(z.ZodObject) },
66+
});
67+
});
68+
69+
test("should set the root namespace properties", () => {
70+
const config = createSimpleConfig({
71+
timeout: 3000,
72+
logger: { debug: vi.fn() } as unknown as AbstractLogger,
73+
emission: {},
74+
hooks: {},
75+
metadata: z.object({}),
76+
});
77+
expect(config).toBeInstanceOf(Config);
78+
expect(config.logger).not.toEqual(console);
79+
expect(config.timeout).toBe(3000);
80+
expect(config.namespaces).toEqual({
81+
"/": { emission: {}, hooks: {}, metadata: expect.any(z.ZodObject) },
82+
});
83+
});
84+
});
5985
});

0 commit comments

Comments
 (0)