Skip to content

Commit

Permalink
fix: properly handle manually created dynamic namespaces
Browse files Browse the repository at this point in the history
Namespaces that match the regex of a parent namespace will now be added
as a child of this namespace:

```js
const parentNamespace = io.of(/^\/dynamic-\d+$/);
const childNamespace = io.of("/dynamic-101");
```

Related:

- #4615
- #4164
- #4015
- #3960
  • Loading branch information
darrachequesne committed Feb 20, 2023
1 parent 2a8565f commit 0d0a7a2
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 2 deletions.
22 changes: 20 additions & 2 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,18 @@ export class Server<
ParentNspNameMatchFn,
ParentNamespace<ListenEvents, EmitEvents, ServerSideEvents, SocketData>
> = new Map();

/**
* A subset of the {@link parentNsps} map, only containing {@link ParentNamespace} which are based on a regular
* expression.
*
* @private
*/
private parentNamespacesFromRegExp: Map<
RegExp,
ParentNamespace<ListenEvents, EmitEvents, ServerSideEvents, SocketData>
> = new Map();

private _adapter?: AdapterConstructor;
private _serveClient: boolean;
private readonly opts: Partial<ServerOptions>;
Expand Down Expand Up @@ -314,8 +326,6 @@ export class Server<
}
const namespace = this.parentNsps.get(nextFn.value)!.createChild(name);
debug("dynamic namespace %s was created", name);
// @ts-ignore
this.sockets.emitReserved("new_namespace", namespace);
fn(namespace);
});
};
Expand Down Expand Up @@ -691,6 +701,7 @@ export class Server<
(nsp, conn, next) => next(null, (name as RegExp).test(nsp)),
parentNsp
);
this.parentNamespacesFromRegExp.set(name, parentNsp);
}
if (fn) {
// @ts-ignore
Expand All @@ -703,6 +714,13 @@ export class Server<

let nsp = this._nsps.get(name);
if (!nsp) {
for (const [regex, parentNamespace] of this.parentNamespacesFromRegExp) {
if (regex.test(name as string)) {
debug("attaching namespace %s to parent namespace %s", name, regex);
return parentNamespace.createChild(name as string);
}
}

debug("initializing namespace %s", name);
nsp = new Namespace(this, name);
this._nsps.set(name, nsp);
Expand Down
19 changes: 19 additions & 0 deletions lib/parent-namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ import debugModule from "debug";

const debug = debugModule("socket.io:parent-namespace");

/**
* A parent namespace is a special {@link Namespace} that holds a list of child namespaces which were created either
* with a regular expression or with a function.
*
* @example
* const parentNamespace = io.of(/\/dynamic-\d+/);
*
* parentNamespace.on("connection", (socket) => {
* const childNamespace = socket.nsp;
* }
*
* // will reach all the clients that are in one of the child namespaces, like "/dynamic-101"
* parentNamespace.emit("hello", "world");
*
*/
export class ParentNamespace<
ListenEvents extends EventsMap = DefaultEventsMap,
EmitEvents extends EventsMap = ListenEvents,
Expand Down Expand Up @@ -81,6 +96,10 @@ export class ParentNamespace<
}

this.server._nsps.set(name, namespace);

// @ts-ignore
this.server.sockets.emitReserved("new_namespace", namespace);

return namespace;
}

Expand Down
11 changes: 11 additions & 0 deletions test/namespaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -652,5 +652,16 @@ describe("namespaces", () => {

io.of(/^\/dynamic-\d+$/);
});

it("should attach a child namespace to its parent upon manual creation", () => {
const io = new Server(0);
const parentNamespace = io.of(/^\/dynamic-\d+$/);
const childNamespace = io.of("/dynamic-101");

// @ts-ignore
expect(parentNamespace.children.has(childNamespace)).to.be(true);

io.close();
});
});
});

0 comments on commit 0d0a7a2

Please sign in to comment.