@helyx/bot 1.0.1 → 1.2.0

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.
Files changed (88) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +27 -2
  3. package/dist/auto-moderation-scanner-registry.d.ts +27 -0
  4. package/dist/auto-moderation-scanner-registry.js +172 -0
  5. package/dist/cloud-connector.js +3 -0
  6. package/dist/cloud-control-dispatcher.js +32 -0
  7. package/dist/command-path-contract.js +1 -0
  8. package/dist/control-permissions.d.ts +2 -2
  9. package/dist/control-permissions.js +4 -2
  10. package/dist/control-server.d.ts +72 -16
  11. package/dist/control-server.js +150 -250
  12. package/dist/dashboard-action-control.d.ts +25 -0
  13. package/dist/dashboard-action-control.js +73 -0
  14. package/dist/dashboard-action-routes.d.ts +9 -0
  15. package/dist/dashboard-action-routes.js +36 -0
  16. package/dist/dashboard-feature-control.d.ts +37 -0
  17. package/dist/dashboard-feature-control.js +206 -0
  18. package/dist/dashboard-module-projection.d.ts +9 -0
  19. package/dist/dashboard-module-projection.js +15 -0
  20. package/dist/dashboard-preview-import-routes.d.ts +6 -0
  21. package/dist/dashboard-preview-import-routes.js +121 -0
  22. package/dist/developer-operations-module.d.ts +2 -0
  23. package/dist/developer-operations-module.js +136 -2
  24. package/dist/discord-analytics.d.ts +3 -0
  25. package/dist/discord-analytics.js +49 -5
  26. package/dist/discord-auto-moderation-service.d.ts +21 -0
  27. package/dist/discord-auto-moderation-service.js +425 -0
  28. package/dist/discord-guild-health.d.ts +2 -2
  29. package/dist/discord-member-enforcement-service.d.ts +26 -0
  30. package/dist/discord-member-enforcement-service.js +590 -0
  31. package/dist/discord-message-enforcement-service.d.ts +9 -0
  32. package/dist/discord-message-enforcement-service.js +102 -0
  33. package/dist/discord-violation-thread-service.d.ts +33 -0
  34. package/dist/discord-violation-thread-service.js +789 -0
  35. package/dist/ecko-evidence-receiver.d.ts +32 -0
  36. package/dist/ecko-evidence-receiver.js +173 -0
  37. package/dist/ecko-transport-auth.d.ts +19 -0
  38. package/dist/ecko-transport-auth.js +80 -0
  39. package/dist/ecko-transport-routes.d.ts +19 -0
  40. package/dist/ecko-transport-routes.js +130 -0
  41. package/dist/embedded-auto-moderation-pool.d.ts +46 -0
  42. package/dist/embedded-auto-moderation-pool.js +324 -0
  43. package/dist/embedded-auto-moderation-scanner.d.ts +34 -0
  44. package/dist/embedded-auto-moderation-scanner.js +263 -0
  45. package/dist/embedded-auto-moderation-worker-runtime.d.ts +37 -0
  46. package/dist/embedded-auto-moderation-worker-runtime.js +37 -0
  47. package/dist/embedded-auto-moderation-worker.d.ts +2 -0
  48. package/dist/embedded-auto-moderation-worker.js +13 -0
  49. package/dist/events.d.ts +5 -1
  50. package/dist/events.js +275 -9
  51. package/dist/guilds.d.ts +2 -1
  52. package/dist/guilds.js +164 -5
  53. package/dist/hosted-server-moderation.d.ts +100 -0
  54. package/dist/hosted-server-moderation.js +226 -0
  55. package/dist/index.d.ts +1 -1
  56. package/dist/index.js +101 -12
  57. package/dist/moderation-action-registry.d.ts +11 -0
  58. package/dist/moderation-action-registry.js +375 -0
  59. package/dist/moderation-case-disassociation.d.ts +13 -0
  60. package/dist/moderation-case-disassociation.js +83 -0
  61. package/dist/moderation-platform-services.d.ts +27 -0
  62. package/dist/moderation-platform-services.js +47 -0
  63. package/dist/modules.d.ts +6 -0
  64. package/dist/modules.js +65 -3
  65. package/dist/premium-activation-module.js +2 -0
  66. package/dist/privileged-message-metadata-cache.d.ts +35 -0
  67. package/dist/privileged-message-metadata-cache.js +70 -0
  68. package/dist/scheduled-task-runner.js +24 -1
  69. package/dist/shard-contracts.d.ts +151 -1
  70. package/dist/shard-contracts.js +142 -1
  71. package/dist/shard-coordinator.d.ts +1 -1
  72. package/dist/shard-ecko-scanner-registry.d.ts +38 -0
  73. package/dist/shard-ecko-scanner-registry.js +96 -0
  74. package/dist/shard-router.d.ts +23 -0
  75. package/dist/shard-router.js +69 -5
  76. package/dist/shard-supervisor.d.ts +11 -0
  77. package/dist/shard-supervisor.js +72 -6
  78. package/dist/shard-worker.d.ts +16 -1
  79. package/dist/shard-worker.js +108 -10
  80. package/dist/transient-violation-evidence.d.ts +39 -0
  81. package/dist/transient-violation-evidence.js +132 -0
  82. package/documentation/README.md +2 -0
  83. package/documentation/examples/.env.example +13 -0
  84. package/documentation/guides/self-hosting.md +17 -1
  85. package/documentation/modules/auto-moderation.md +51 -0
  86. package/documentation/modules/moderation.md +42 -0
  87. package/documentation/reference/environment.md +11 -0
  88. package/package.json +17 -5
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ const snowflake = z.string().regex(/^\d{17,20}$/u);
3
+ const moduleId = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/u);
4
+ const actionParameters = z.object({
5
+ guildId: snowflake,
6
+ moduleId,
7
+ actionId: z.string().regex(/^[a-z][a-z0-9_-]*$/u),
8
+ });
9
+ const mutationContext = z.object({
10
+ actorUserId: snowflake,
11
+ correlationId: z.uuid(),
12
+ });
13
+ export function registerDashboardActionRoutes(server, service) {
14
+ server.post("/v1/guilds/:guildId/modules/:moduleId/actions/:actionId/preview", async (request) => {
15
+ const params = actionParameters.parse(request.params);
16
+ const body = mutationContext.parse(request.body);
17
+ return service.previewDashboardAction({ ...params, ...body });
18
+ });
19
+ server.post("/v1/guilds/:guildId/modules/:moduleId/actions/:actionId", async (request) => {
20
+ const params = actionParameters.parse(request.params);
21
+ const body = mutationContext
22
+ .extend({
23
+ inspectionRevision: z.string().min(1).max(200).optional(),
24
+ })
25
+ .parse(request.body);
26
+ return service.executeDashboardAction({
27
+ ...params,
28
+ actorUserId: body.actorUserId,
29
+ correlationId: body.correlationId,
30
+ ...(body.inspectionRevision === undefined
31
+ ? {}
32
+ : { inspectionRevision: body.inspectionRevision }),
33
+ });
34
+ });
35
+ }
36
+ //# sourceMappingURL=dashboard-action-routes.js.map
@@ -0,0 +1,37 @@
1
+ import type { DashboardResourceField, DashboardResourceScalarField, DashboardSetting } from "@helyx/module-manifest";
2
+ import { type ManagedResourceDetail, type ManagedResourcePage, type ManagedResourceRelationPage, type ManagedResourceValue, type ModuleConfigurationService, type ModuleDefinition, type ServiceAccess } from "@helyx/sdk";
3
+ export declare function requireDashboardFeatures(services: ServiceAccess, guildId: string, moduleId: string, featureIds: readonly (string | undefined)[]): Promise<void>;
4
+ export declare function changedSettingFeatureIds(settings: readonly DashboardSetting[], current: Readonly<Record<string, unknown>>, next: Readonly<Record<string, unknown>>): readonly string[];
5
+ export declare function scalarInputFeatureIds(inputs: readonly DashboardResourceScalarField[], value: Readonly<Record<string, ManagedResourceValue>>): readonly string[];
6
+ export declare function configurationPreviewFeatureIds(configurations: ModuleConfigurationService, guildId: string, module: ModuleDefinition, preview: {
7
+ featureId?: string | undefined;
8
+ inputs: readonly DashboardResourceScalarField[];
9
+ }, input: {
10
+ value: Readonly<Record<string, unknown>>;
11
+ input: Readonly<Record<string, ManagedResourceValue>>;
12
+ }): Promise<readonly (string | undefined)[]>;
13
+ export declare function resourceFieldFeatureIds(fields: readonly DashboardResourceField[], value: Readonly<Record<string, ManagedResourceValue>>): readonly string[];
14
+ export declare function validateManagedResourceInput(inputs: readonly DashboardResourceScalarField[], value: Readonly<Record<string, ManagedResourceValue>>): void;
15
+ export declare function isBoundedManagedResourceValue(value: unknown, depth?: number, budget?: {
16
+ keys: number;
17
+ }): value is ManagedResourceValue;
18
+ export declare function hasBoundedManagedResourceBytes(value: unknown): boolean;
19
+ export declare function normalizeManagedResourceDetail(value: Omit<ManagedResourceDetail, "description" | "attributes"> & {
20
+ description?: string | undefined;
21
+ attributes?: Readonly<Record<string, ManagedResourceValue>> | undefined;
22
+ }, attributeKeys: readonly string[]): ManagedResourceDetail;
23
+ export declare function normalizeManagedResourcePage(value: {
24
+ items: readonly (Omit<ManagedResourcePage["items"][number], "description" | "attributes"> & {
25
+ description?: string | undefined;
26
+ attributes?: Readonly<Record<string, ManagedResourceValue>> | undefined;
27
+ })[];
28
+ nextCursor?: string | undefined;
29
+ total?: number | undefined;
30
+ }, attributeKeys: readonly string[]): ManagedResourcePage;
31
+ export declare function normalizeManagedResourceRelationPage(value: {
32
+ items: ManagedResourceRelationPage["items"];
33
+ nextCursor?: string | undefined;
34
+ }, valueKeys: readonly string[]): ManagedResourceRelationPage;
35
+ export declare function projectManagedResourceObject(value: Readonly<Record<string, ManagedResourceValue>>, declaredKeys: readonly string[]): Record<string, ManagedResourceValue>;
36
+ export declare function managedResourceColumnKeys(module: ModuleDefinition, resourceType: string): readonly string[];
37
+ //# sourceMappingURL=dashboard-feature-control.d.ts.map
@@ -0,0 +1,206 @@
1
+ import { DashboardActionValidationError, HELYX_SERVICE_NAMES, } from "@helyx/sdk";
2
+ import { BotControlForbiddenError } from "./control-errors.js";
3
+ const SNOWFLAKE_PATTERN = /^\d{17,20}$/u;
4
+ export async function requireDashboardFeatures(services, guildId, moduleId, featureIds) {
5
+ const unique = [...new Set(featureIds.filter(isDefined))];
6
+ if (unique.length === 0)
7
+ return;
8
+ if (!services.has(HELYX_SERVICE_NAMES.featureAccess))
9
+ throw new BotControlForbiddenError();
10
+ const access = services.get(HELYX_SERVICE_NAMES.featureAccess);
11
+ const decisions = await Promise.all(unique.map((featureId) => access.check({ guildId, moduleId, featureId }))).catch(() => []);
12
+ if (decisions.length !== unique.length ||
13
+ decisions.some(({ decision }) => decision !== "granted"))
14
+ throw new BotControlForbiddenError();
15
+ }
16
+ export function changedSettingFeatureIds(settings, current, next) {
17
+ return settings.flatMap((setting) => {
18
+ if (!Object.hasOwn(next, setting.key))
19
+ return [];
20
+ const changed = !configurationValuesEqual(Object.hasOwn(current, setting.key)
21
+ ? current[setting.key]
22
+ : "default" in setting
23
+ ? setting.default
24
+ : undefined, next[setting.key]);
25
+ if (!changed)
26
+ return [];
27
+ const selectedFeatureId = setting.type === "select" && typeof next[setting.key] === "string"
28
+ ? setting.options.find(({ value }) => value === next[setting.key])
29
+ ?.featureId
30
+ : undefined;
31
+ return [setting.featureId, selectedFeatureId].filter(isDefined);
32
+ });
33
+ }
34
+ export function scalarInputFeatureIds(inputs, value) {
35
+ return inputs.flatMap((input) => input.featureId && Object.hasOwn(value, input.key) ? [input.featureId] : []);
36
+ }
37
+ export async function configurationPreviewFeatureIds(configurations, guildId, module, preview, input) {
38
+ const current = await configurations.get(guildId, module.manifest.id);
39
+ return [
40
+ preview.featureId,
41
+ ...scalarInputFeatureIds(preview.inputs, input.input),
42
+ ...(module.manifest.manifestVersion === "0.5"
43
+ ? changedSettingFeatureIds(module.manifest.dashboard?.settings ?? [], current?.value ?? {}, input.value)
44
+ : []),
45
+ ];
46
+ }
47
+ export function resourceFieldFeatureIds(fields, value) {
48
+ const featureIds = [];
49
+ for (const field of fields) {
50
+ const item = value[field.key];
51
+ if (item === undefined)
52
+ continue;
53
+ if (field.featureId &&
54
+ (!("default" in field) || !configurationValuesEqual(field.default, item)))
55
+ featureIds.push(field.featureId);
56
+ if (field.type !== "list" || !Array.isArray(item))
57
+ continue;
58
+ for (const entry of item) {
59
+ if (!isRecord(entry))
60
+ continue;
61
+ featureIds.push(...scalarInputFeatureIds(field.fields, entry));
62
+ }
63
+ }
64
+ return featureIds;
65
+ }
66
+ export function validateManagedResourceInput(inputs, value) {
67
+ const declared = new Map(inputs.map((input) => [input.key, input]));
68
+ for (const key of Object.keys(value))
69
+ if (!declared.has(key))
70
+ throw new DashboardActionValidationError(`The action value "${key}" is not declared by the module.`);
71
+ for (const input of inputs) {
72
+ const item = value[input.key];
73
+ if (item === undefined) {
74
+ if (input.required)
75
+ throw new DashboardActionValidationError(`${input.label} is required for this action.`);
76
+ continue;
77
+ }
78
+ const valid = input.type === "boolean"
79
+ ? typeof item === "boolean"
80
+ : input.type === "integer"
81
+ ? typeof item === "number" &&
82
+ Number.isInteger(item) &&
83
+ item >= input.minimum &&
84
+ item <= input.maximum &&
85
+ (item - input.minimum) % (input.step ?? 1) === 0
86
+ : input.type === "select"
87
+ ? typeof item === "string" &&
88
+ input.options.some(({ value: option }) => option === item)
89
+ : input.type === "discord-channel" || input.type === "discord-role"
90
+ ? isValidDiscordResourceSelection(input, item)
91
+ : typeof item === "string" &&
92
+ item.length >= input.minLength &&
93
+ item.length <= input.maxLength;
94
+ if (!valid)
95
+ throw new DashboardActionValidationError(`${input.label} is not a valid action value.`);
96
+ }
97
+ }
98
+ function isValidDiscordResourceSelection(input, value) {
99
+ if (input.multiple !== true)
100
+ return typeof value === "string" && SNOWFLAKE_PATTERN.test(value);
101
+ if (!Array.isArray(value))
102
+ return false;
103
+ const minimum = input.minItems ?? (input.required ? 1 : 0);
104
+ const maximum = input.maxItems ?? 25;
105
+ return (value.length >= minimum &&
106
+ value.length <= maximum &&
107
+ new Set(value).size === value.length &&
108
+ value.every((item) => typeof item === "string" && SNOWFLAKE_PATTERN.test(item)));
109
+ }
110
+ export function isBoundedManagedResourceValue(value, depth = 0, budget = { keys: 0 }) {
111
+ if (depth > 8)
112
+ return false;
113
+ if (value === null ||
114
+ typeof value === "boolean" ||
115
+ (typeof value === "number" && Number.isFinite(value)))
116
+ return true;
117
+ if (typeof value === "string")
118
+ return value.length <= 4_000;
119
+ if (Array.isArray(value))
120
+ return (value.length <= 100 &&
121
+ value.every((item) => isBoundedManagedResourceValue(item, depth + 1, budget)));
122
+ if (!isRecord(value))
123
+ return false;
124
+ const prototype = Object.getPrototypeOf(value);
125
+ if (prototype !== Object.prototype && prototype !== null)
126
+ return false;
127
+ const entries = Object.entries(value);
128
+ budget.keys += entries.length;
129
+ return (entries.length <= 100 &&
130
+ budget.keys <= 500 &&
131
+ entries.every(([key, item]) => key.length >= 1 &&
132
+ key.length <= 100 &&
133
+ isBoundedManagedResourceValue(item, depth + 1, budget)));
134
+ }
135
+ export function hasBoundedManagedResourceBytes(value) {
136
+ return (new TextEncoder().encode(JSON.stringify(value)).byteLength <= 48 * 1_024);
137
+ }
138
+ export function normalizeManagedResourceDetail(value, attributeKeys) {
139
+ return {
140
+ id: value.id,
141
+ revision: value.revision,
142
+ status: value.status,
143
+ title: value.title,
144
+ ...(value.description === undefined
145
+ ? {}
146
+ : { description: value.description }),
147
+ ...(value.attributes === undefined || attributeKeys.length === 0
148
+ ? {}
149
+ : {
150
+ attributes: projectManagedResourceObject(value.attributes, attributeKeys),
151
+ }),
152
+ updatedAt: value.updatedAt,
153
+ value: value.value,
154
+ };
155
+ }
156
+ export function normalizeManagedResourcePage(value, attributeKeys) {
157
+ return {
158
+ items: value.items.map((item) => ({
159
+ id: item.id,
160
+ revision: item.revision,
161
+ status: item.status,
162
+ title: item.title,
163
+ ...(item.description === undefined
164
+ ? {}
165
+ : { description: item.description }),
166
+ ...(item.attributes === undefined || attributeKeys.length === 0
167
+ ? {}
168
+ : {
169
+ attributes: projectManagedResourceObject(item.attributes, attributeKeys),
170
+ }),
171
+ updatedAt: item.updatedAt,
172
+ })),
173
+ ...(value.nextCursor === undefined ? {} : { nextCursor: value.nextCursor }),
174
+ ...(value.total === undefined ? {} : { total: value.total }),
175
+ };
176
+ }
177
+ export function normalizeManagedResourceRelationPage(value, valueKeys) {
178
+ return {
179
+ items: value.items.map((item) => ({
180
+ id: item.id,
181
+ value: projectManagedResourceObject(item.value, valueKeys),
182
+ })),
183
+ ...(value.nextCursor === undefined ? {} : { nextCursor: value.nextCursor }),
184
+ };
185
+ }
186
+ export function projectManagedResourceObject(value, declaredKeys) {
187
+ const allowed = new Set(declaredKeys);
188
+ return Object.fromEntries(Object.entries(value)
189
+ .filter(([key]) => allowed.has(key))
190
+ .map(([key, item]) => [key, structuredClone(item)]));
191
+ }
192
+ export function managedResourceColumnKeys(module, resourceType) {
193
+ return (module.manifest.dashboard?.resources
194
+ ?.find(({ id }) => id === resourceType)
195
+ ?.columns?.map(({ key }) => key) ?? []);
196
+ }
197
+ function configurationValuesEqual(left, right) {
198
+ return JSON.stringify(left) === JSON.stringify(right);
199
+ }
200
+ function isDefined(value) {
201
+ return value !== undefined;
202
+ }
203
+ function isRecord(value) {
204
+ return typeof value === "object" && value !== null && !Array.isArray(value);
205
+ }
206
+ //# sourceMappingURL=dashboard-feature-control.js.map
@@ -9,10 +9,15 @@ interface DashboardLogEventDefinition {
9
9
  }
10
10
  interface DashboardActionDefinition {
11
11
  id: string;
12
+ permissionId?: string | undefined;
13
+ featureId?: string | undefined;
12
14
  label: string;
13
15
  description: string;
14
16
  buttonLabel: string;
15
17
  style: "primary" | "secondary" | "danger";
18
+ confirmation?: string | undefined;
19
+ inputs?: readonly unknown[] | undefined;
20
+ inspection?: "optional" | "required" | undefined;
16
21
  }
17
22
  type DashboardResourceDefinition = NonNullable<NonNullable<ModuleDefinition["manifest"]["dashboard"]>["resources"]>[number];
18
23
  export interface DashboardModuleDefinition {
@@ -27,6 +32,10 @@ export interface DashboardModuleDefinition {
27
32
  tier: ModuleTier;
28
33
  requiredPermissions: string[];
29
34
  settings: readonly unknown[];
35
+ settingsMutationPermissionId?: string;
36
+ settingsPreview?: Readonly<Record<string, unknown>>;
37
+ features: readonly unknown[];
38
+ permissionOperations: readonly unknown[];
30
39
  commands: DashboardCommandDefinition[];
31
40
  logEvents: DashboardLogEventDefinition[];
32
41
  actions: DashboardActionDefinition[];
@@ -15,6 +15,21 @@ export function mapDashboardModule(module) {
15
15
  tier: getModuleTier(module.manifest),
16
16
  requiredPermissions: [...module.manifest.permissions],
17
17
  settings: dashboard.settings,
18
+ ...("settingsMutationPermissionId" in dashboard &&
19
+ dashboard.settingsMutationPermissionId
20
+ ? {
21
+ settingsMutationPermissionId: dashboard.settingsMutationPermissionId,
22
+ }
23
+ : {}),
24
+ ...("settingsPreview" in dashboard && dashboard.settingsPreview
25
+ ? { settingsPreview: structuredClone(dashboard.settingsPreview) }
26
+ : {}),
27
+ features: module.manifest.manifestVersion === "0.5"
28
+ ? module.manifest.features.map((feature) => structuredClone(feature))
29
+ : [],
30
+ permissionOperations: module.manifest.manifestVersion === "0.5"
31
+ ? module.manifest.permissionOperations.map((operation) => structuredClone(operation))
32
+ : [],
18
33
  commands: dashboardCommands(module),
19
34
  logEvents: (module.logging?.events ?? []).map((event) => ({ ...event })),
20
35
  actions: (dashboard.actions ?? []).map((action) => ({ ...action })),
@@ -0,0 +1,6 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ import type { BotControlHandler } from "./control-server.js";
3
+ /** Registers the bounded, non-persisting preview and import boundaries. */
4
+ export declare function registerDashboardPreviewImportRoutes(server: FastifyInstance, service: BotControlHandler): void;
5
+ export declare function registerDashboardResourceMutationRoutes(server: FastifyInstance, service: BotControlHandler): void;
6
+ //# sourceMappingURL=dashboard-preview-import-routes.d.ts.map
@@ -0,0 +1,121 @@
1
+ import { z } from "zod";
2
+ import { hasBoundedManagedResourceBytes, isBoundedManagedResourceValue, } from "./dashboard-feature-control.js";
3
+ const snowflake = z.string().regex(/^\d{17,20}$/u);
4
+ const moduleId = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/u);
5
+ const resourceType = z.string().regex(/^[a-z][a-z0-9_-]*$/u);
6
+ const mutationContext = z.object({
7
+ actorUserId: snowflake,
8
+ correlationId: z.uuid(),
9
+ });
10
+ const configurationValue = z.record(z.string(), z.union([
11
+ z.null(),
12
+ z.string(),
13
+ z.boolean(),
14
+ z.number().finite(),
15
+ z.array(z.string()).max(100),
16
+ ]));
17
+ const managedResourceValue = z.custom((value) => isBoundedManagedResourceValue(value), "Managed resource value exceeds the transport limits");
18
+ const managedResourceObject = z
19
+ .record(z.string().min(1).max(100), managedResourceValue)
20
+ .refine(hasBoundedManagedResourceBytes);
21
+ /** Registers the bounded, non-persisting preview and import boundaries. */
22
+ export function registerDashboardPreviewImportRoutes(server, service) {
23
+ server.post("/v1/guilds/:guildId/modules/:moduleId/configuration/preview", async (request) => {
24
+ const params = z
25
+ .object({ guildId: snowflake, moduleId })
26
+ .strict()
27
+ .parse(request.params);
28
+ const body = mutationContext
29
+ .extend({ value: configurationValue, input: managedResourceObject })
30
+ .strict()
31
+ .parse(request.body);
32
+ return service.previewConfiguration({ ...params, ...body });
33
+ });
34
+ server.post("/v1/guilds/:guildId/modules/:moduleId/resources/:resourceType/import", async (request) => {
35
+ const params = z
36
+ .object({ guildId: snowflake, moduleId, resourceType })
37
+ .strict()
38
+ .parse(request.params);
39
+ const body = mutationContext
40
+ .extend({
41
+ format: z.literal("utf8_text"),
42
+ content: z
43
+ .string()
44
+ .min(1)
45
+ .refine((value) => Buffer.byteLength(value, "utf8") <= 48 * 1_024, "Managed resource import exceeds the transport byte limit"),
46
+ })
47
+ .strict()
48
+ .parse(request.body);
49
+ return service.importManagedResource({ ...params, ...body });
50
+ });
51
+ }
52
+ export function registerDashboardResourceMutationRoutes(server, service) {
53
+ const resourceParams = z
54
+ .object({ guildId: snowflake, moduleId, resourceType })
55
+ .strict();
56
+ const identifiedResourceParams = resourceParams.extend({
57
+ resourceId: z.string().trim().min(1).max(100),
58
+ });
59
+ server.post("/v1/guilds/:guildId/modules/:moduleId/resources/:resourceType/preview", async (request) => {
60
+ const params = resourceParams.parse(request.params);
61
+ const body = mutationContext
62
+ .extend({ value: managedResourceObject })
63
+ .strict()
64
+ .parse(request.body);
65
+ return service.previewManagedResource({ ...params, ...body });
66
+ });
67
+ server.post("/v1/guilds/:guildId/modules/:moduleId/resources/:resourceType", async (request) => {
68
+ const params = resourceParams.parse(request.params);
69
+ const body = mutationContext
70
+ .extend({
71
+ intent: z.enum(["draft", "publish"]).optional().default("publish"),
72
+ value: managedResourceObject,
73
+ })
74
+ .strict()
75
+ .parse(request.body);
76
+ return service.createManagedResource({ ...params, ...body });
77
+ });
78
+ server.put("/v1/guilds/:guildId/modules/:moduleId/resources/:resourceType/:resourceId", async (request) => {
79
+ const params = identifiedResourceParams.parse(request.params);
80
+ const body = mutationContext
81
+ .extend({
82
+ expectedRevision: z.number().int().min(0),
83
+ value: managedResourceObject.optional().default({}),
84
+ })
85
+ .strict()
86
+ .parse(request.body);
87
+ return service.updateManagedResource({ ...params, ...body });
88
+ });
89
+ server.delete("/v1/guilds/:guildId/modules/:moduleId/resources/:resourceType/:resourceId", async (request) => {
90
+ const params = identifiedResourceParams.parse(request.params);
91
+ const body = mutationContext
92
+ .extend({ expectedRevision: z.number().int().min(0) })
93
+ .strict()
94
+ .parse(request.body);
95
+ return service.deleteManagedResource({ ...params, ...body });
96
+ });
97
+ server.post("/v1/guilds/:guildId/modules/:moduleId/resources/:resourceType/:resourceId/actions/:actionId", async (request) => {
98
+ const params = identifiedResourceParams
99
+ .extend({ actionId: resourceType })
100
+ .parse(request.params);
101
+ const body = mutationContext
102
+ .extend({
103
+ expectedRevision: z.number().int().min(0),
104
+ value: managedResourceObject.optional().default({}),
105
+ actorAuthenticatedAt: z.iso.datetime({ offset: true }).optional(),
106
+ })
107
+ .strict()
108
+ .parse(request.body);
109
+ return service.executeManagedResourceAction({
110
+ ...params,
111
+ actorUserId: body.actorUserId,
112
+ correlationId: body.correlationId,
113
+ expectedRevision: body.expectedRevision,
114
+ value: body.value,
115
+ ...(body.actorAuthenticatedAt === undefined
116
+ ? {}
117
+ : { actorAuthenticatedAt: body.actorAuthenticatedAt }),
118
+ });
119
+ });
120
+ }
121
+ //# sourceMappingURL=dashboard-preview-import-routes.js.map
@@ -1,6 +1,7 @@
1
1
  import { type ModuleDefinition } from "@helyx/sdk";
2
2
  import { type ShardHealthSnapshot } from "./shard-contracts.js";
3
3
  import type { TriviaDataProvider } from "./trivia-data-provider.js";
4
+ import type { DeveloperServerModeration } from "./hosted-server-moderation.js";
4
5
  export interface ShardHealthProvider {
5
6
  getHealth(): Promise<ShardHealthSnapshot>;
6
7
  }
@@ -19,6 +20,7 @@ export interface DeveloperOperationsInput {
19
20
  processLabel: string;
20
21
  health: ShardHealthProvider;
21
22
  triviaData: TriviaDataProvider;
23
+ moderation?: DeveloperServerModeration;
22
24
  }
23
25
  export declare function createDeveloperOperationsModule(input: DeveloperOperationsInput): ModuleDefinition;
24
26
  export declare class WorkerShardHealthProvider implements ShardHealthProvider {