• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

RobinTail / zod-sockets / 15533565214

09 Jun 2025 11:34AM UTC coverage: 99.932% (-0.07%) from 100.0%
15533565214

Pull #490

github

RobinTail
rm redundant condition from ensureCompliance.
Pull Request #490: Next: v4

391 of 412 branches covered (94.9%)

710 of 710 new or added lines in 17 files covered. (100.0%)

1 existing line in 1 file now uncovered.

1463 of 1464 relevant lines covered (99.93%)

178.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.35
/src/integration.ts
1
import ts from "typescript";
6✔
2
import { z } from "zod/v4";
3
import { AbstractAction } from "./action";
4
import { makeCleanId } from "./common-helpers";
6✔
5
import { Config } from "./config";
6
import { makeEventFnSchema } from "./integration-helpers";
6✔
7
import { Namespaces, normalizeNS } from "./namespace";
6✔
8
import { zodToTs } from "./zts";
6✔
9
import {
6✔
10
  addJsDoc,
11
  makeType,
12
  printNode,
13
  f,
14
  exportModifier,
15
} from "./typescript-api";
16

17
interface IntegrationProps {
18
  config: Config<Namespaces>;
19
  actions: AbstractAction[];
20
  /**
21
   * @desc When event has both .rest() and an acknowledgement, the "...rest" can not be placed in a middle.
22
   * @desc In this case, overloads are used to reflect variations on different number of the function arguments.
23
   * @default 3
24
   * @example ( (cb) => void ) | ( (rest1, cb) => void ) | ( (rest1, rest2, cb) => void )
25
   */
26
  maxOverloads?: number;
27
}
28

29
const fallbackNs = "root";
6✔
30
const registryScopes = ["emission", "actions"];
6✔
31

32
export class Integration {
6✔
33
  #program: ts.Node[] = [];
10✔
34
  #aliases: Record<
12✔
35
    string, // namespace
36
    Map<object, ts.TypeAliasDeclaration>
37
  > = {};
12✔
38
  #ids = {
12✔
39
    path: f.createIdentifier("path"),
12✔
40
    socket: f.createIdentifier("Socket"),
12✔
41
    socketBase: f.createIdentifier("SocketBase"),
12✔
42
    ioClient: f.createStringLiteral("socket.io-client"),
12✔
43
    emission: f.createIdentifier(makeCleanId(registryScopes[0])),
12✔
44
    actions: f.createIdentifier(makeCleanId(registryScopes[1])),
12✔
45
  };
12✔
46
  protected registry: Record<
12✔
47
    string, // namespace
48
    Record<
49
      (typeof registryScopes)[number],
50
      { event: string; node: ts.TypeNode }[]
51
    >
52
  > = {};
12✔
53

54
  #makeAlias(
10✔
55
    ns: string,
12✔
56
    key: object,
12✔
57
    produce: () => ts.TypeNode,
12✔
58
  ): ts.TypeReferenceNode {
12✔
59
    let name = this.#aliases[ns].get(key)?.name?.text;
12✔
60
    if (!name) {
12✔
61
      name = `Type${this.#aliases[ns].size + 1}`;
6✔
62
      const temp = f.createLiteralTypeNode(f.createNull());
6✔
63
      this.#aliases[ns].set(key, makeType(name, temp));
6✔
64
      this.#aliases[ns].set(key, makeType(name, produce()));
6✔
65
    }
6✔
66
    return f.createTypeReferenceNode(name);
12✔
67
  }
12✔
68

69
  constructor({
10✔
70
    config: { namespaces },
12✔
71
    actions,
12✔
72
    maxOverloads = 3,
12✔
73
  }: IntegrationProps) {
12✔
74
    this.#program.push(
12✔
75
      f.createImportDeclaration(
12✔
76
        undefined,
12✔
77
        f.createImportClause(
12✔
78
          true,
12✔
79
          undefined,
12✔
80
          f.createNamedImports([
12✔
81
            f.createImportSpecifier(
12✔
82
              false,
12✔
83
              this.#ids.socket,
12✔
84
              this.#ids.socketBase,
12✔
85
            ),
12✔
86
          ]),
12✔
87
        ),
12✔
88
        this.#ids.ioClient,
12✔
89
      ),
12✔
90
    );
12✔
91

92
    for (const [ns, { emission }] of Object.entries(namespaces)) {
12✔
93
      this.#aliases[ns] = new Map<z.ZodTypeAny, ts.TypeAliasDeclaration>();
12✔
94
      this.registry[ns] = { emission: [], actions: [] };
12✔
95
      const commons = { makeAlias: this.#makeAlias.bind(this, ns) };
12✔
96
      for (const [event, { schema, ack }] of Object.entries(emission)) {
12✔
97
        const node = zodToTs(makeEventFnSchema(schema, ack, maxOverloads), {
48✔
98
          isResponse: true,
48✔
99
          ...commons,
48✔
100
        });
48✔
101
        this.registry[ns].emission.push({ event, node });
48✔
102
      }
48✔
103
      for (const action of actions) {
12✔
104
        if (action.namespace === ns) {
30✔
105
          const { event, inputSchema, outputSchema } = action;
30✔
106
          const node = zodToTs(
30✔
107
            makeEventFnSchema(inputSchema, outputSchema, maxOverloads),
30✔
108
            { isResponse: false, ...commons },
30✔
109
          );
30✔
110
          this.registry[ns].actions.push({ event, node });
30✔
111
        }
30✔
112
      }
30✔
113
    }
12✔
114

115
    for (const ns in this.registry) {
12✔
116
      const publicName = makeCleanId(ns) || makeCleanId(fallbackNs);
12✔
117

118
      const nsNameNode = f.createVariableStatement(
12✔
119
        exportModifier,
12✔
120
        f.createVariableDeclarationList(
12✔
121
          [
12✔
122
            f.createVariableDeclaration(
12✔
123
              this.#ids.path,
12✔
124
              undefined,
12✔
125
              undefined,
12✔
126
              f.createStringLiteral(normalizeNS(ns)),
12✔
127
            ),
12✔
128
          ],
12✔
129
          ts.NodeFlags.Const,
12✔
130
        ),
12✔
131
      );
12✔
132
      addJsDoc(
12✔
133
        nsNameNode,
12✔
134
        `@desc The actual path of the ${publicName} namespace`,
12✔
135
      );
12✔
136

137
      const interfaces = Object.entries(this.registry[ns]).map(
12✔
138
        ([scope, events]) =>
12✔
139
          f.createInterfaceDeclaration(
24✔
140
            exportModifier,
24✔
141
            makeCleanId(scope),
24✔
142
            undefined,
24✔
143
            undefined,
24✔
144
            events.map(({ event, node }) =>
24✔
145
              f.createPropertySignature(undefined, event, undefined, node),
78✔
146
            ),
24✔
147
          ),
24✔
148
      );
12✔
149
      const socketNode = f.createTypeAliasDeclaration(
12✔
150
        exportModifier,
12✔
151
        this.#ids.socket,
12✔
152
        undefined,
12✔
153
        f.createTypeReferenceNode(this.#ids.socketBase, [
12✔
154
          f.createTypeReferenceNode(this.#ids.emission),
12✔
155
          f.createTypeReferenceNode(this.#ids.actions),
12✔
156
        ]),
12✔
157
      );
12✔
158
      addJsDoc(
12✔
159
        socketNode,
12✔
160
        `@example const socket: ${publicName}.${this.#ids.socket.text} = io(${publicName}.${this.#ids.path.text})`,
12✔
161
      );
12✔
162
      this.#program.push(
12✔
163
        f.createModuleDeclaration(
12✔
164
          exportModifier,
12✔
165
          f.createIdentifier(publicName),
12✔
166
          f.createModuleBlock([
12✔
167
            nsNameNode,
12✔
168
            ...this.#aliases[ns].values(),
12✔
169
            ...interfaces,
12✔
170
            socketNode,
12✔
171
          ]),
12✔
172
          ts.NodeFlags.Namespace,
12✔
173
        ),
12✔
174
      );
12✔
175
    }
12✔
176
  }
12✔
177

178
  public print(printerOptions?: ts.PrinterOptions) {
10✔
179
    return this.#program
12✔
180
      .map((node, index) =>
12✔
181
        printNode(
24✔
182
          node,
24✔
183
          index < this.#program.length
24✔
184
            ? printerOptions
24!
UNCOV
185
            : { ...printerOptions, omitTrailingSemicolon: true },
×
186
        ),
24✔
187
      )
12✔
188
      .join("\n\n");
12✔
189
  }
12✔
190
}
10✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc