Skip to content

Commit 09eb8e9

Browse files
committed
feat: update to djs 14.19.3, node 22, zod 4
1 parent 595e1a0 commit 09eb8e9

189 files changed

Lines changed: 1244 additions & 900 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
18
1+
22

backend/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"migrate-rollback-prod": "npm run migrate-rollback",
3030
"migrate-rollback-dev": "npm run build && npm run migrate-rollback",
3131
"validate-active-configs": "node --enable-source-maps dist/validateActiveConfigs.js > ../config-errors.txt",
32-
"export-config-json-schema": "node --enable-source-maps dist/exportSchemas.js > ../config-schema.json",
32+
"export-config-json-schema": "node --enable-source-maps dist/exportSchemas.js",
3333
"test": "npm run build && npm run run-tests",
3434
"run-tests": "ava",
3535
"test-watch": "tsc-watch --build --onSuccess \"npx ava\""
@@ -41,15 +41,15 @@
4141
"cors": "^2.8.5",
4242
"cross-env": "^7.0.3",
4343
"deep-diff": "^1.0.2",
44-
"discord.js": "^14.14.1",
44+
"discord.js": "^14.19.3",
4545
"dotenv": "^4.0.0",
4646
"emoji-regex": "^8.0.0",
4747
"escape-string-regexp": "^1.0.5",
4848
"express": "^4.19.2",
4949
"fp-ts": "^2.0.1",
5050
"humanize-duration": "^3.15.0",
5151
"js-yaml": "^4.1.0",
52-
"knub": "^32.0.0-next.21",
52+
"knub": "^32.0.0-next.23",
5353
"knub-command-manager": "^9.1.0",
5454
"last-commit-log": "^2.1.0",
5555
"lodash-es": "^4.17.21",
@@ -76,7 +76,7 @@
7676
"uuid": "^9.0.0",
7777
"yawn-yaml": "github:dragory/yawn-yaml#string-number-fix-build",
7878
"zlib-sync": "^0.1.7",
79-
"zod": "^3.7.2"
79+
"zod": "^3.25.17"
8080
},
8181
"devDependencies": {
8282
"@types/cors": "^2.8.5",

backend/src/RegExpRunner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { EventEmitter } from "events";
1+
import { EventEmitter } from "node:events";
22
import { CooldownManager } from "knub";
33
import { RegExpWorker, TimeoutError } from "regexp-worker";
44
import { MINUTES, SECONDS } from "./utils.js";

backend/src/SimpleCache.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export class SimpleCache<T = any> {
4646
});
4747

4848
if (this.maxItems && this.store.size > this.maxItems) {
49-
const keyToDelete = this.store.keys().next().value;
49+
const keyToDelete = this.store.keys().next().value!;
5050
this.store.delete(keyToDelete);
5151
}
5252
}

backend/src/api/docs.ts

Lines changed: 32 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,90 @@
11
import express from "express";
2-
import z from "zod";
2+
import z from "zod/v4";
33
import { availableGuildPlugins } from "../plugins/availablePlugins.js";
44
import { ZeppelinGuildPluginInfo } from "../types.js";
55
import { indentLines } from "../utils.js";
66
import { notFound } from "./responses.js";
77

8-
function isZodObject(schema: z.ZodTypeAny): schema is z.ZodObject<any> {
9-
return schema._def.typeName === "ZodObject";
8+
function isZodObject(schema: z.ZodType): schema is z.ZodObject<any> {
9+
return schema.def.type === "object";
1010
}
1111

12-
function isZodRecord(schema: z.ZodTypeAny): schema is z.ZodRecord<any> {
13-
return schema._def.typeName === "ZodRecord";
12+
function isZodRecord(schema: z.ZodType): schema is z.ZodRecord<any> {
13+
return schema.def.type === "record";
1414
}
1515

16-
function isZodEffects(schema: z.ZodTypeAny): schema is z.ZodEffects<any, any> {
17-
return schema._def.typeName === "ZodEffects";
16+
function isZodOptional(schema: z.ZodType): schema is z.ZodOptional<any> {
17+
return schema.def.type === "optional";
1818
}
1919

20-
function isZodOptional(schema: z.ZodTypeAny): schema is z.ZodOptional<any> {
21-
return schema._def.typeName === "ZodOptional";
20+
function isZodArray(schema: z.ZodType): schema is z.ZodArray<any> {
21+
return schema.def.type === "array";
2222
}
2323

24-
function isZodArray(schema: z.ZodTypeAny): schema is z.ZodArray<any> {
25-
return schema._def.typeName === "ZodArray";
24+
function isZodUnion(schema: z.ZodType): schema is z.ZodUnion<any> {
25+
return schema.def.type === "union";
2626
}
2727

28-
function isZodUnion(schema: z.ZodTypeAny): schema is z.ZodUnion<any> {
29-
return schema._def.typeName === "ZodUnion";
28+
function isZodNullable(schema: z.ZodType): schema is z.ZodNullable<any> {
29+
return schema.def.type === "nullable";
3030
}
3131

32-
function isZodNullable(schema: z.ZodTypeAny): schema is z.ZodNullable<any> {
33-
return schema._def.typeName === "ZodNullable";
32+
function isZodDefault(schema: z.ZodType): schema is z.ZodDefault<any> {
33+
return schema.def.type === "default";
3434
}
3535

36-
function isZodDefault(schema: z.ZodTypeAny): schema is z.ZodDefault<any> {
37-
return schema._def.typeName === "ZodDefault";
36+
function isZodLiteral(schema: z.ZodType): schema is z.ZodLiteral<any> {
37+
return schema.def.type === "literal";
3838
}
3939

40-
function isZodLiteral(schema: z.ZodTypeAny): schema is z.ZodLiteral<any> {
41-
return schema._def.typeName === "ZodLiteral";
40+
function isZodIntersection(schema: z.ZodType): schema is z.ZodIntersection<any, any> {
41+
return schema.def.type === "intersection";
4242
}
4343

44-
function isZodIntersection(schema: z.ZodTypeAny): schema is z.ZodIntersection<any, any> {
45-
return schema._def.typeName === "ZodIntersection";
46-
}
47-
48-
function formatZodConfigSchema(schema: z.ZodTypeAny) {
44+
function formatZodConfigSchema(schema: z.ZodType) {
4945
if (isZodObject(schema)) {
5046
return (
5147
`{\n` +
5248
Object.entries(schema._def.shape())
53-
.map(([k, value]) => indentLines(`${k}: ${formatZodConfigSchema(value as z.ZodTypeAny)}`, 2))
49+
.map(([k, value]) => indentLines(`${k}: ${formatZodConfigSchema(value as z.ZodType)}`, 2))
5450
.join("\n") +
5551
"\n}"
5652
);
5753
}
5854
if (isZodRecord(schema)) {
59-
return "{\n" + indentLines(`[string]: ${formatZodConfigSchema(schema._def.valueType)}`, 2) + "\n}";
60-
}
61-
if (isZodEffects(schema)) {
62-
return formatZodConfigSchema(schema._def.schema);
55+
return "{\n" + indentLines(`[string]: ${formatZodConfigSchema(schema.valueType as z.ZodType)}`, 2) + "\n}";
6356
}
6457
if (isZodOptional(schema)) {
65-
return `Optional<${formatZodConfigSchema(schema._def.innerType)}>`;
58+
return `Optional<${formatZodConfigSchema(schema.def.innerType)}>`;
6659
}
6760
if (isZodArray(schema)) {
68-
return `Array<${formatZodConfigSchema(schema._def.type)}>`;
61+
return `Array<${formatZodConfigSchema(schema.def.element)}>`;
6962
}
7063
if (isZodUnion(schema)) {
7164
return schema._def.options.map((t) => formatZodConfigSchema(t)).join(" | ");
7265
}
7366
if (isZodNullable(schema)) {
74-
return `Nullable<${formatZodConfigSchema(schema._def.innerType)}>`;
67+
return `Nullable<${formatZodConfigSchema(schema.def.innerType)}>`;
7568
}
7669
if (isZodDefault(schema)) {
77-
return formatZodConfigSchema(schema._def.innerType);
70+
return formatZodConfigSchema(schema.def.innerType);
7871
}
7972
if (isZodLiteral(schema)) {
80-
return schema._def.value;
73+
return schema.def.values;
8174
}
8275
if (isZodIntersection(schema)) {
83-
return [formatZodConfigSchema(schema._def.left), formatZodConfigSchema(schema._def.right)].join(" & ");
76+
return [formatZodConfigSchema(schema.def.left as z.ZodType), formatZodConfigSchema(schema.def.right as z.ZodType)].join(" & ");
8477
}
85-
if (schema._def.typeName === "ZodString") {
78+
if (schema.def.type === "string") {
8679
return "string";
8780
}
88-
if (schema._def.typeName === "ZodNumber") {
81+
if (schema.def.type === "number") {
8982
return "number";
9083
}
91-
if (schema._def.typeName === "ZodBoolean") {
84+
if (schema.def.type === "boolean") {
9285
return "boolean";
9386
}
94-
if (schema._def.typeName === "ZodNever") {
87+
if (schema.def.type === "never") {
9588
return "never";
9689
}
9790
return "unknown";

backend/src/api/guilds/importExport.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ApiPermissions } from "@zeppelinbot/shared/apiPermissions.js";
22
import express, { Request, Response } from "express";
33
import moment from "moment-timezone";
4-
import { z } from "zod";
4+
import { z } from "zod/v4";
55
import { GuildCases } from "../../data/GuildCases.js";
66
import { Case } from "../../data/entities/Case.js";
77
import { MINUTES } from "../../utils.js";

backend/src/configValidator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { BaseConfig, ConfigValidationError, GuildPluginBlueprint, PluginConfigManager } from "knub";
2-
import { ZodError } from "zod";
2+
import { ZodError } from "zod/v4";
33
import { availableGuildPlugins } from "./plugins/availablePlugins.js";
44
import { zZeppelinGuildConfig } from "./types.js";
55
import { formatZodIssue } from "./utils/formatZodIssue.js";

backend/src/env.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import dotenv from "dotenv";
22
import fs from "fs";
33
import path from "path";
4-
import { z } from "zod";
4+
import { z } from "zod/v4";
55
import { rootDir } from "./paths.js";
66

77
const envType = z.object({

backend/src/exportSchemas.ts

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,89 @@
1-
import { z } from "zod";
2-
import { zodToJsonSchema } from "zod-to-json-schema";
1+
import { z } from "zod/v4";
32
import { availableGuildPlugins } from "./plugins/availablePlugins.js";
43
import { zZeppelinGuildConfig } from "./types.js";
4+
import { deepPartial } from "./utils/zodDeepPartial.js";
5+
import fs from "node:fs";
6+
7+
const basePluginOverrideCriteriaSchema = z.strictObject({
8+
channel: z
9+
.union([z.string(), z.array(z.string())])
10+
.nullable()
11+
.optional(),
12+
category: z
13+
.union([z.string(), z.array(z.string())])
14+
.nullable()
15+
.optional(),
16+
level: z
17+
.union([z.string(), z.array(z.string())])
18+
.nullable()
19+
.optional(),
20+
user: z
21+
.union([z.string(), z.array(z.string())])
22+
.nullable()
23+
.optional(),
24+
role: z
25+
.union([z.string(), z.array(z.string())])
26+
.nullable()
27+
.optional(),
28+
thread: z
29+
.union([z.string(), z.array(z.string())])
30+
.nullable()
31+
.optional(),
32+
is_thread: z.boolean().nullable().optional(),
33+
thread_type: z.literal(["public", "private"]).nullable().optional(),
34+
extra: z.any().optional(),
35+
});
36+
37+
const pluginOverrideCriteriaSchema = basePluginOverrideCriteriaSchema.extend({
38+
get zzz_dummy_property_do_not_use() {
39+
return pluginOverrideCriteriaSchema.optional();
40+
},
41+
get all() {
42+
return z.array(pluginOverrideCriteriaSchema).optional();
43+
},
44+
get any() {
45+
return z.array(pluginOverrideCriteriaSchema).optional();
46+
},
47+
get not() {
48+
return pluginOverrideCriteriaSchema.optional();
49+
},
50+
});
51+
52+
const outputPath = process.argv[2];
53+
if (!outputPath) {
54+
console.error("Output path required");
55+
process.exit(1);
56+
}
57+
58+
const partialConfigs = new Map<any, z.ZodType>();
59+
function getPartialConfig(configSchema: z.ZodType) {
60+
if (!partialConfigs.has(configSchema)) {
61+
partialConfigs.set(configSchema, deepPartial(configSchema));
62+
}
63+
return partialConfigs.get(configSchema)!;
64+
}
65+
66+
function overrides(configSchema: z.ZodType): z.ZodType {
67+
const partialConfig = getPartialConfig(configSchema);
68+
return pluginOverrideCriteriaSchema.extend({
69+
config: partialConfig,
70+
});
71+
}
572

673
const pluginSchemaMap = availableGuildPlugins.reduce((map, pluginInfo) => {
7-
map[pluginInfo.plugin.name] = pluginInfo.docs.configSchema;
74+
map[pluginInfo.plugin.name] = z.object({
75+
config: pluginInfo.docs.configSchema.optional(),
76+
overrides: overrides(pluginInfo.docs.configSchema).optional(),
77+
});
878
return map;
979
}, {});
1080

11-
const fullSchema = zZeppelinGuildConfig.omit({ plugins: true }).merge(
12-
z.strictObject({
13-
plugins: z.strictObject(pluginSchemaMap).partial(),
14-
}),
15-
);
81+
const fullSchema = zZeppelinGuildConfig.omit({ plugins: true }).extend({
82+
plugins: z.strictObject(pluginSchemaMap).partial().optional(),
83+
});
1684

17-
const jsonSchema = zodToJsonSchema(fullSchema);
85+
const jsonSchema = z.toJSONSchema(fullSchema, { io: "input", cycles: "ref" });
1886

19-
console.log(JSON.stringify(jsonSchema, null, 2));
87+
fs.writeFileSync(outputPath, JSON.stringify(jsonSchema, null, 2), { encoding: "utf8" });
2088

2189
process.exit(0);

backend/src/index.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
TextChannel,
1313
ThreadChannel,
1414
} from "discord.js";
15-
import { EventEmitter } from "events";
1615
import { Knub, PluginError, PluginLoadError, PluginNotLoadedError } from "knub";
1716
import moment from "moment-timezone";
1817
import { performance } from "perf_hooks";
@@ -252,9 +251,8 @@ connect().then(async () => {
252251
GatewayIntentBits.GuildVoiceStates,
253252
],
254253
});
255-
// FIXME: TS doesn't see Client as a child of EventEmitter for some reason
256-
// If you're here because of an error from TS 5.5.2, see https://github.com/discordjs/discord.js/issues/10358
257-
(client as unknown as EventEmitter).setMaxListeners(200);
254+
255+
client.setMaxListeners(200);
258256

259257
const safe429DecayInterval = 5 * SECONDS;
260258
const safe429MaxCount = 5;

0 commit comments

Comments
 (0)