@helyx/bot 0.4.41 → 0.4.42

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.42
4
+
5
+ ### Patch Changes
6
+
7
+ - Include in-progress giveaway draws linked to a Discord user in privacy access and erasure processing, and reject modules that omit Discord identity record fields from their privacy declarations.
8
+
3
9
  ## 0.4.41
4
10
 
5
11
  ### Patch Changes
@@ -15,6 +15,7 @@ import { assertVersion, dashboardActionPermissionId, declaredManagedResourcePerm
15
15
  import { dashboardCommands, } from "./dashboard-commands.js";
16
16
  import { availabilityCatalogueDefinitions, availabilityCatalogue, dashboardModuleStatus, disabledModuleSummary, moduleAvailabilityService, moduleAvailabilityStates, } from "./module-availability-control.js";
17
17
  import { registerGuildCommandControlRoutes } from "./guild-command-control-routes.js";
18
+ import { createPrivacyProviderRegistry, describePrivacyProviders, } from "./privacy-provider-registry.js";
18
19
  const snowflake = z.string().regex(/^\d{17,20}$/u);
19
20
  const moduleId = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/u);
20
21
  const MAX_DISCORD_SETTING_SELECTIONS = 25;
@@ -284,14 +285,7 @@ export class BotControlService {
284
285
  this.#installations = services.get(HELYX_SERVICE_NAMES.installations);
285
286
  this.#permissions = services.get(HELYX_SERVICE_NAMES.permissions);
286
287
  this.#services = services;
287
- this.#privacyProviders = new Map(privacyExecutor
288
- ? modules
289
- .filter((module) => module.privacy !== undefined)
290
- .map((module) => {
291
- const provider = module.privacy.create(privacyExecutor, this.#services);
292
- return [provider.id, provider];
293
- })
294
- : []);
288
+ this.#privacyProviders = createPrivacyProviderRegistry(modules, privacyExecutor, this.#services);
295
289
  }
296
290
  async cleanupOAuthConsumer(input) {
297
291
  const module = this.#modules.get(input.moduleId);
@@ -391,13 +385,7 @@ export class BotControlService {
391
385
  .then((receipt) => actionReceiptSchema.parse(receipt));
392
386
  }
393
387
  listPrivacyProviders() {
394
- return Promise.resolve([...this.#privacyProviders.values()]
395
- .map((provider) => ({
396
- id: provider.id,
397
- version: provider.version,
398
- containsPersonalData: provider.describe().containsPersonalData,
399
- }))
400
- .sort((left, right) => left.id.localeCompare(right.id)));
388
+ return Promise.resolve(describePrivacyProviders(this.#privacyProviders));
401
389
  }
402
390
  capturePrivacy(input) {
403
391
  const provider = this.#privacyProvider(input.providerId, input.providerVersion);
package/dist/modules.js CHANGED
@@ -204,6 +204,19 @@ function validatePrivacyParity(definition) {
204
204
  }
205
205
  }
206
206
  }
207
+ const subjectFieldsByCollection = new Map(definition.manifest.privacy.collections.map((collection) => [
208
+ collection.name,
209
+ new Set(collection.subjectFields.map(({ field }) => field)),
210
+ ]));
211
+ for (const [collectionName, collection] of Object.entries(definition.records ?? {})) {
212
+ const declaredSubjectFields = subjectFieldsByCollection.get(collectionName);
213
+ for (const field of Object.keys(collection.fields)) {
214
+ if (isDiscordIdentityRecordField(field) &&
215
+ !declaredSubjectFields?.has(field)) {
216
+ throw new Error(`Module record field containing a Discord identity is not declared for privacy access: ${collectionName}.${field}`);
217
+ }
218
+ }
219
+ }
207
220
  const unavailable = () => {
208
221
  throw new Error("Privacy provider executor is unavailable during loading");
209
222
  };
@@ -220,6 +233,11 @@ function validatePrivacyParity(definition) {
220
233
  throw new Error("Module manifest privacy declaration does not match executable provider");
221
234
  }
222
235
  }
236
+ function isDiscordIdentityRecordField(field) {
237
+ return (/(?:^|_)(?:user_id|member_id|subject_id)$/u.test(field) ||
238
+ field === "created_by" ||
239
+ field === "updated_by");
240
+ }
223
241
  function assertSameContributionIds(declared, executable, label) {
224
242
  if (new Set(executable).size !== executable.length) {
225
243
  throw new Error(`Module has duplicate executable ${label}`);
@@ -0,0 +1,8 @@
1
+ import type { ModuleDefinition, PrivacyProvider, PrivacyProviderExecutor, ServiceAccess } from "@helyx/sdk";
2
+ export declare function createPrivacyProviderRegistry(modules: readonly ModuleDefinition[], executor: PrivacyProviderExecutor | undefined, services: ServiceAccess): ReadonlyMap<string, PrivacyProvider>;
3
+ export declare function describePrivacyProviders(providers: ReadonlyMap<string, PrivacyProvider>): readonly {
4
+ id: string;
5
+ version: number;
6
+ containsPersonalData: boolean;
7
+ }[];
8
+ //# sourceMappingURL=privacy-provider-registry.d.ts.map
@@ -0,0 +1,20 @@
1
+ export function createPrivacyProviderRegistry(modules, executor, services) {
2
+ if (!executor)
3
+ return new Map();
4
+ return new Map(modules.flatMap((module) => {
5
+ if (!module.privacy)
6
+ return [];
7
+ const provider = module.privacy.create(executor, services);
8
+ return [[provider.id, provider]];
9
+ }));
10
+ }
11
+ export function describePrivacyProviders(providers) {
12
+ return [...providers.values()]
13
+ .map((provider) => ({
14
+ id: provider.id,
15
+ version: provider.version,
16
+ containsPersonalData: provider.describe().containsPersonalData,
17
+ }))
18
+ .sort((left, right) => left.id.localeCompare(right.id));
19
+ }
20
+ //# sourceMappingURL=privacy-provider-registry.js.map
package/package.json CHANGED
@@ -1,74 +1,74 @@
1
- {
2
- "name": "@helyx/bot",
3
- "version": "0.4.41",
4
- "description": "Production Discord runtime for the modular Helyx platform.",
5
- "keywords": [
6
- "helyx",
7
- "discord",
8
- "bot",
9
- "modular"
10
- ],
11
- "private": false,
12
- "license": "HSAL 1.0",
13
- "homepage": "https://helyx.gg/",
14
- "bugs": {
15
- "url": "https://github.com/ZyC0R3/Helyx/issues"
16
- },
17
- "repository": {
18
- "type": "git",
19
- "url": "git+https://github.com/ZyC0R3/Helyx.git",
20
- "directory": "apps/bot"
21
- },
22
- "engines": {
23
- "node": ">=24 <27",
24
- "npm": ">=11"
25
- },
26
- "publishConfig": {
27
- "access": "public",
28
- "provenance": true
29
- },
30
- "type": "module",
31
- "files": [
32
- "dist",
33
- "!dist/*.tsbuildinfo",
34
- "!dist/**/*.map",
35
- "documentation",
36
- "CHANGELOG.md",
37
- "README.md",
38
- "LICENSE"
39
- ],
40
- "exports": {
41
- ".": {
42
- "types": "./dist/index.d.ts",
43
- "default": "./dist/index.js"
44
- }
45
- },
46
- "scripts": {
47
- "build": "tsc -b",
48
- "prepack": "node ../../scripts/public-documentation.mjs --sync-bot",
49
- "dev": "node --env-file-if-exists=../../.env --import tsx src/index.ts",
50
- "start": "node --env-file-if-exists=../../.env dist/index.js"
51
- },
52
- "dependencies": {
53
- "@helyx/config": ">=0.5.0 <0.6.0",
54
- "@helyx/control-protocol": ">=0.4.0 <0.5.0",
55
- "@helyx/core": ">=0.1.0 <0.2.0",
56
- "@helyx/database": ">=0.8.6 <0.9.0",
57
- "@helyx/framework": ">=0.1.0 <0.2.0",
58
- "@helyx/module-manifest": ">=0.7.0 <0.8.0",
59
- "@helyx/platform": ">=0.6.2 <0.7.0",
60
- "@helyx/sdk": ">=0.8.4 <0.9.0",
61
- "@helyx/status-client": ">=0.1.0 <0.2.0",
62
- "@helyx/verification-contracts": ">=0.3.0 <0.4.0",
63
- "discord.js": "14.27.0",
64
- "fastify": "5.10.0",
65
- "pino": "10.3.1",
66
- "redis": "6.1.0",
67
- "undici": "6.28.0",
68
- "ws": "8.21.1",
69
- "zod": "4.4.3"
70
- },
71
- "devDependencies": {
72
- "@types/ws": "8.18.1"
73
- }
74
- }
1
+ {
2
+ "name": "@helyx/bot",
3
+ "version": "0.4.42",
4
+ "description": "Production Discord runtime for the modular Helyx platform.",
5
+ "keywords": [
6
+ "helyx",
7
+ "discord",
8
+ "bot",
9
+ "modular"
10
+ ],
11
+ "private": false,
12
+ "license": "HSAL 1.0",
13
+ "homepage": "https://helyx.gg/",
14
+ "bugs": {
15
+ "url": "https://github.com/ZyC0R3/Helyx/issues"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/ZyC0R3/Helyx.git",
20
+ "directory": "apps/bot"
21
+ },
22
+ "engines": {
23
+ "node": ">=24 <27",
24
+ "npm": ">=11"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public",
28
+ "provenance": true
29
+ },
30
+ "type": "module",
31
+ "files": [
32
+ "dist",
33
+ "!dist/*.tsbuildinfo",
34
+ "!dist/**/*.map",
35
+ "documentation",
36
+ "CHANGELOG.md",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "exports": {
41
+ ".": {
42
+ "types": "./dist/index.d.ts",
43
+ "default": "./dist/index.js"
44
+ }
45
+ },
46
+ "scripts": {
47
+ "build": "tsc -b",
48
+ "prepack": "node ../../scripts/public-documentation.mjs --sync-bot",
49
+ "dev": "node --env-file-if-exists=../../.env --import tsx src/index.ts",
50
+ "start": "node --env-file-if-exists=../../.env dist/index.js"
51
+ },
52
+ "dependencies": {
53
+ "@helyx/config": ">=0.5.0 <0.6.0",
54
+ "@helyx/control-protocol": ">=0.4.0 <0.5.0",
55
+ "@helyx/core": ">=0.1.0 <0.2.0",
56
+ "@helyx/database": ">=0.8.6 <0.9.0",
57
+ "@helyx/framework": ">=0.1.0 <0.2.0",
58
+ "@helyx/module-manifest": ">=0.7.0 <0.8.0",
59
+ "@helyx/platform": ">=0.6.2 <0.7.0",
60
+ "@helyx/sdk": ">=0.8.4 <0.9.0",
61
+ "@helyx/status-client": ">=0.1.0 <0.2.0",
62
+ "@helyx/verification-contracts": ">=0.3.0 <0.4.0",
63
+ "discord.js": "14.27.0",
64
+ "fastify": "5.10.0",
65
+ "pino": "10.3.1",
66
+ "redis": "6.1.0",
67
+ "undici": "6.28.0",
68
+ "ws": "8.21.1",
69
+ "zod": "4.4.3"
70
+ },
71
+ "devDependencies": {
72
+ "@types/ws": "8.18.1"
73
+ }
74
+ }