@helyx/cli 1.2.3 → 1.4.1

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,57 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Refresh the packaged self-host catalogue for the prepared Channel audit release.
8
+ The bundled generator metadata references the new compatible core and module
9
+ versions. This prepares a new CLI artifact; publication remains separate.
10
+
11
+ ## 1.4.0
12
+
13
+ ### Minor Changes
14
+
15
+ - Prepare the coordinated Phase 8 minor release and align internal package dependencies. Existing deployment and database-support restrictions are unchanged.
16
+
17
+ Start the generated Docker bot through Node directly so container stop signals
18
+ reach Helyx's shutdown handlers instead of passing through npm. Local npm start
19
+ behaviour is unchanged.
20
+
21
+ Correct the generated starter's Cloud Connect command and document explicit server-command allowlists, required Discord intents, database TLS modes and the one-database Cloud Connect topology without changing runtime defaults.
22
+
23
+ Align the starter's optional database startup setting with the bot's `apply`
24
+ default, preserving automatic self-host migrations and hosted-only inspect mode.
25
+
26
+ Keep the optional local Verification public URL in the hosted-only environment
27
+ exclusion, so it is not required or emitted in generated self-host projects.
28
+
29
+ ### Patch Changes
30
+
31
+ - Updated dependencies
32
+ - @helyx/bot@1.4.0
33
+ - @helyx/config@1.4.0
34
+ - @helyx/database-mysql@0.2.0
35
+ - @helyx/database@2.4.0
36
+
37
+ ## 1.3.0
38
+
39
+ ### Minor Changes
40
+
41
+ - Add optional MySQL-compatible core migration and doctor commands with
42
+ fail-closed adapter loading, while retaining PostgreSQL as the default and
43
+ blocking full MySQL bot startup until its repositories are complete.
44
+ - Include both reviewed database adapters in generated Self-Hosted projects so
45
+ `DATABASE_ENGINE` can select either backend without rebuilding the project.
46
+
47
+ ## 1.2.4
48
+
49
+ ### Patch Changes
50
+
51
+ - Refresh the prepared module catalogue for dependency-bound patch releases.
52
+ - Prepare the self-host catalogue for the bot patch that forwards the hosted
53
+ moderation control credential to enabled shard workers.
54
+
3
55
  ## 1.2.3
4
56
 
5
57
  ### Patch Changes
@@ -312,25 +364,7 @@
312
364
 
313
365
  ### Patch Changes
314
366
 
315
- - Accept compatible internal Helyx releases within explicit minor-safe pre-1.0 or major-safe stable ranges, preventing dependency-only patch cascades.---
316
- "@helyx/bot": patch
317
- "@helyx/cli": patch
318
- "@helyx/core": patch
319
- "@helyx/framework": patch
320
- "@helyx/platform": patch
321
- "@helyx/sdk": patch
322
- "@helyx/module-account-gate": patch
323
- "@helyx/module-feedback": patch
324
- "@helyx/module-giveaways": patch
325
- "@helyx/module-levels": patch
326
- "@helyx/module-logging": patch
327
- "@helyx/module-polls": patch
328
- "@helyx/module-reactionroles": patch
329
- "@helyx/module-suggestions": patch
330
- "@helyx/module-welcome": patch
331
- ---
332
-
333
- Accept compatible internal Helyx releases within explicit minor-safe pre-1.0 or major-safe stable ranges, preventing dependency-only patch cascades.
367
+ - Accept compatible internal Helyx releases within explicit minor-safe pre-1.0 or major-safe stable ranges, preventing dependency-only patch cascades.
334
368
 
335
369
  ## 0.4.28
336
370
 
package/README.md CHANGED
@@ -12,7 +12,20 @@ helyx self-host update --check
12
12
  helyx self-host update --confirm
13
13
  ```
14
14
 
15
- `setup` safely creates `.env` from a generated starter's `.env.example`, never overwrites existing configuration and reports whether Docker Compose is available. Direct Node.js operation remains fully supported with a local or hosted PostgreSQL database. `start` loads local configuration without overriding hosting-platform variables and performs the production bot bootstrap, including database and installed-module migrations. `migrate` applies only core migrations and exits. `doctor` validates the complete bot environment, PostgreSQL connectivity, immutable core migration history and configured module package metadata without logging in to Discord or modifying the database.
15
+ `setup` creates `.env` when needed, adds a missing, blank or example local
16
+ data-encryption key, and reports whether Docker Compose is available. It
17
+ preserves other existing configuration and a non-placeholder key. Direct Node.js
18
+ operation supports a local or hosted PostgreSQL or MySQL-compatible database.
19
+ `start` loads local configuration without overriding hosting-platform variables
20
+ and performs the bot bootstrap, including core and installed-module migrations.
21
+ `migrate` applies only core migrations and exits.
22
+
23
+ `doctor` checks the complete bot environment, connectivity, immutable core
24
+ migration history, package metadata and selected module/engine compatibility
25
+ without logging in to Discord or changing the database. It inspects module
26
+ migrations only after no core migrations remain pending. Pending migrations are
27
+ reported without applying them; required database capabilities are probed by
28
+ `start`. A passing doctor is not a Discord or full runtime acceptance test.
16
29
 
17
30
  `self-host init` retrieves the current reviewed catalogue from the fixed Helyx API endpoint, validates a downloaded build manifest against it, creates a new starter-project directory, pins the runtime and selected official modules, generates a lockfile with lifecycle scripts disabled, and writes checksums. The request has strict timeout, response-size, JSON-schema and official-package checks; no arbitrary catalogue URL or stale bundled fallback is accepted. Add `--install` only when the dependencies should also be installed locally. Unknown, unpublished, duplicated, stale or modified package selections fail closed.
18
31
 
@@ -24,6 +37,34 @@ Generated projects include `DOCUMENTATION.md`, which points to the
24
37
  version-matched public documentation bundled with the installed `@helyx/bot`
25
38
  package.
26
39
 
27
- `self-host update --check` reads the current project and the reviewed catalogue from `api.helyx.gg`, then reports the complete compatible release-set change without writing files. `--confirm` backs up the managed metadata under `.helyx/backups`, updates exact core, CLI and selected live-module versions together, refreshes and validates the lockfile with lifecycle scripts disabled, and restores the original metadata if generation fails. Run `npm ci` and `npm run doctor` after applying an update. The updater never changes `.env` or customer database data.
40
+ `self-host update --check` reads the project and reviewed catalogue from
41
+ `api.helyx.gg`, then reports the complete compatible package-set change without
42
+ writing files. `--confirm` backs up `.gitignore`, `package.json`,
43
+ `package-lock.json`, `helyx.modules.json` and `SHA256SUMS` under
44
+ `.helyx/backups/<timestamp>/`. It updates exact Helyx versions and internal
45
+ dependency selections together, then refreshes and validates the lockfile with
46
+ lifecycle scripts disabled. It does not install the refreshed dependencies.
47
+
48
+ If metadata generation or lockfile validation fails within `--confirm`, those
49
+ managed files are restored. A later install, doctor or startup failure does not
50
+ trigger that restoration. The backup contains metadata only; it cannot restore
51
+ the database, `.env`, installed dependencies or a container image.
52
+
53
+ For a supported ordinary update, stop the bot and back up its database and
54
+ configuration before applying. Run `npm ci` after a raw `--confirm`; the
55
+ generated `npm run update` script already chains those commands. Then run
56
+ doctor and restart. Docker users rebuild the image and run
57
+ `docker compose run --rm bot node node_modules/@helyx/cli/dist/cli.js doctor`
58
+ with the database reachable before recreating the bot. Managed hosts perform
59
+ installation and doctor through their provider's facilities. Direct Node.js
60
+ starters use compiled packages and need no local source build.
61
+
62
+ This release's consolidated fresh-install baselines are not an ordinary upgrade
63
+ path for a database on an earlier migration chain. The updater neither resets
64
+ data nor provides a data-preserving conversion; retain the matching older
65
+ packages and [contact Helyx support](https://docs.helyx.gg/contact/support/)
66
+ before proceeding. No approved old-database transition is available yet. It never changes
67
+ `.env` or customer database data. Normal startup can apply migrations, so
68
+ restoring project metadata after startup does not undo database changes.
28
69
 
29
70
  The package is published under the Helyx Source Available Licence. Generated projects contain deployment configuration and exact public npm dependencies; they do not contain Helyx source code or credentials.
package/dist/cli.js CHANGED
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { loadConfiguredModulePackages, resolveModulePackageSpecifiers, startBot, } from "@helyx/bot";
2
+ import { stat } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { assertConfiguredModuleDatabaseCompatibility, loadConfiguredModulePackages, resolveModulePackageSpecifiers, startBot, } from "@helyx/bot";
3
5
  import { parseBotEnvironment, parseDatabaseEnvironment } from "@helyx/config";
4
- import { checkDatabaseConnection, createDatabasePool, inspectCoreMigrations, runCoreMigrations, } from "@helyx/database";
6
+ import { resolveDatabaseAdapter } from "@helyx/database/adapters";
5
7
  import { formatCommandError } from "./errors.js";
6
8
  import { runSelfHostCommand } from "./self-host/command.js";
7
9
  import { loadLocalEnvironment, setupSelfHostEnvironment } from "./setup.js";
@@ -47,60 +49,100 @@ async function setup() {
47
49
  ? "Added the missing private data-encryption key to the existing .env file."
48
50
  : "The existing .env file and data-encryption key were left unchanged.");
49
51
  if (result.dockerComposeAvailable) {
50
- console.log("Docker Compose is available. You may use the included bot and PostgreSQL services.");
52
+ console.log(result.databaseEngine === "mysql"
53
+ ? "Docker Compose is available. The included service runs only the bot and connects to your existing MySQL-compatible database."
54
+ : "Docker Compose is available. You may use the included bot and PostgreSQL services.");
51
55
  }
52
56
  else {
53
- console.log("Docker Compose was not detected. Direct Node.js operation is supported, but you must provide a local or hosted PostgreSQL database.");
57
+ console.log("Docker Compose was not detected. Direct Node.js operation is supported, but you must provide one supported PostgreSQL or MySQL-compatible database.");
54
58
  }
55
59
  console.log("Next: edit .env, replace every placeholder, then run npm run doctor.");
56
60
  console.log("Managed hosts may set the same variables in their control panel instead of using .env.");
57
61
  }
58
62
  async function migrate() {
59
63
  const environment = parseDatabaseEnvironment(process.env);
60
- const database = createDatabasePool({
64
+ const adapter = await resolveDatabaseAdapter(environment.DATABASE_ENGINE);
65
+ const session = await adapter.connect({
61
66
  connectionString: environment.DATABASE_URL,
62
67
  sslMode: environment.DATABASE_SSL,
63
68
  applicationName: "helyx-cli-migrate",
64
- maxConnections: 1,
69
+ maxConnections: 4,
65
70
  });
66
71
  try {
67
- await checkDatabaseConnection(database);
68
- const result = await runCoreMigrations(database);
69
- console.log(`Core database ready: ${result.applied.length} applied, ${result.verified.length} verified.`);
72
+ const result = await session.migrations.runCore();
73
+ console.log(`Core ${session.identity.product} database ready: ${result.applied.length} applied, ${result.verified.length} verified.`);
70
74
  }
71
75
  finally {
72
- await database.end();
76
+ await session.close();
73
77
  }
74
78
  }
75
79
  async function doctor() {
76
80
  const environment = parseBotEnvironment(process.env);
77
- const database = createDatabasePool({
81
+ const adapter = await resolveDatabaseAdapter(environment.DATABASE_ENGINE);
82
+ const session = await adapter.connect({
78
83
  connectionString: environment.DATABASE_URL,
79
84
  sslMode: environment.DATABASE_SSL,
80
85
  applicationName: "helyx-cli-doctor",
81
- maxConnections: 1,
86
+ maxConnections: 4,
87
+ inspectionOnly: true,
82
88
  });
83
89
  try {
84
- await checkDatabaseConnection(database);
85
- const migrations = await inspectCoreMigrations(database);
86
- const moduleSpecifiers = await resolveModulePackageSpecifiers(environment.HELYX_MODULE_PACKAGES, undefined, undefined, environment.HELYX_MODULE_CATALOGUE_PATH);
87
- const modules = await loadConfiguredModulePackages(moduleSpecifiers);
88
- console.log(`Helyx doctor passed: PostgreSQL connected, ${migrations.verified.length} core migrations verified, ${modules.length} module packages validated.`);
90
+ const migrations = await session.migrations.inspectCore();
91
+ const modules = await validateConfiguredModules(environment);
92
+ assertConfiguredModuleDatabaseCompatibility(modules, environment.DATABASE_ENGINE);
93
+ let verifiedModuleMigrations = 0;
94
+ let pendingModuleMigrations = 0;
95
+ if (migrations.pending.length === 0) {
96
+ for (const { definition, packageRoot } of modules) {
97
+ const directory = join(packageRoot, environment.DATABASE_ENGINE === "mysql"
98
+ ? "migrations-mysql"
99
+ : "migrations");
100
+ if (environment.DATABASE_ENGINE === "mysql") {
101
+ const [mysqlDirectory, postgresDirectory] = await Promise.all([
102
+ stat(directory).catch(missingDirectory),
103
+ stat(join(packageRoot, "migrations")).catch(missingDirectory),
104
+ ]);
105
+ if (!mysqlDirectory?.isDirectory() &&
106
+ postgresDirectory?.isDirectory())
107
+ throw new Error(`Module ${definition.manifest.id} is missing its MySQL migration directory: ${directory}`);
108
+ }
109
+ const inspection = await session.migrations.inspectModule({
110
+ moduleId: definition.manifest.id,
111
+ moduleVersion: definition.manifest.version,
112
+ migrationsDirectory: directory,
113
+ });
114
+ verifiedModuleMigrations += inspection.verified.length;
115
+ pendingModuleMigrations += inspection.pending.length;
116
+ }
117
+ }
118
+ console.log(`Helyx doctor passed: ${session.identity.product} ${session.identity.version} connected through the ${session.identity.engine} adapter, ${migrations.verified.length} core and ${verifiedModuleMigrations} module migrations verified, ${modules.length} module packages validated.`);
119
+ console.log("Required database capabilities are probed at startup, not during this read-only inspection.");
89
120
  if (migrations.pending.length > 0) {
90
- console.log(`${migrations.pending.length} core migration(s) are pending and will be applied automatically by npm start.`);
121
+ console.log(`${migrations.pending.length} core migration(s) are pending; run helyx migrate before continuing.`);
91
122
  }
123
+ if (pendingModuleMigrations > 0)
124
+ console.log(`${pendingModuleMigrations} module migration(s) are pending; start Helyx to apply them before continuing.`);
92
125
  }
93
126
  finally {
94
- await database.end();
127
+ await session.close();
95
128
  }
96
129
  }
130
+ function missingDirectory(error) {
131
+ if (error.code === "ENOENT")
132
+ return undefined;
133
+ throw error;
134
+ }
135
+ async function validateConfiguredModules(environment) {
136
+ const moduleSpecifiers = await resolveModulePackageSpecifiers(environment.HELYX_MODULE_PACKAGES, undefined, undefined, environment.HELYX_MODULE_CATALOGUE_PATH, environment.HELYX_RUNTIME_PROFILE);
137
+ return loadConfiguredModulePackages(moduleSpecifiers);
138
+ }
97
139
  function printHelp() {
98
140
  console.log(`Usage: helyx <command>
99
141
 
100
142
  Commands:
101
143
  setup Create .env safely and explain the available hosting paths
102
144
  start Start the Helyx Discord bot
103
- migrate Apply core PostgreSQL migrations and exit
145
+ migrate Apply core migrations for the selected database engine and exit
104
146
  doctor Validate environment, database schema and module packages
105
147
  self-host init --manifest <path> [--output <path>] [--install]
106
148
  Generate a self-hosted starter project from a reviewed manifest
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./self-host/catalogue.js";
2
2
  export * from "./self-host/command.js";
3
+ export * from "./self-host/database-adapters.js";
3
4
  export * from "./self-host/manifest.js";
4
5
  export * from "./self-host/project-generator.js";
5
6
  export * from "./self-host/update.js";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./self-host/catalogue.js";
2
2
  export * from "./self-host/command.js";
3
+ export * from "./self-host/database-adapters.js";
3
4
  export * from "./self-host/manifest.js";
4
5
  export * from "./self-host/project-generator.js";
5
6
  export * from "./self-host/update.js";
@@ -6,6 +6,11 @@ export declare const selfHostReleaseStatusSchema: z.ZodEnum<{
6
6
  beta: "beta";
7
7
  live: "live";
8
8
  }>;
9
+ export declare const selfHostDatabaseEngineSchema: z.ZodEnum<{
10
+ postgresql: "postgresql";
11
+ mysql: "mysql";
12
+ }>;
13
+ export type SelfHostDatabaseEngine = z.infer<typeof selfHostDatabaseEngineSchema>;
9
14
  export declare const selfHostModuleSchema: z.ZodObject<{
10
15
  id: z.ZodString;
11
16
  package: z.ZodString;
@@ -71,6 +76,10 @@ export declare const selfHostModuleV2Schema: z.ZodDiscriminatedUnion<[z.ZodObjec
71
76
  free: "free";
72
77
  premium: "premium";
73
78
  }>>;
79
+ databaseEngines: z.ZodArray<z.ZodEnum<{
80
+ postgresql: "postgresql";
81
+ mysql: "mysql";
82
+ }>>;
74
83
  dependencies: z.ZodArray<z.ZodObject<{
75
84
  id: z.ZodString;
76
85
  version: z.ZodString;
@@ -121,6 +130,10 @@ export declare const selfHostModuleV2Schema: z.ZodDiscriminatedUnion<[z.ZodObjec
121
130
  free: "free";
122
131
  premium: "premium";
123
132
  }>>;
133
+ databaseEngines: z.ZodArray<z.ZodEnum<{
134
+ postgresql: "postgresql";
135
+ mysql: "mysql";
136
+ }>>;
124
137
  dependencies: z.ZodArray<z.ZodObject<{
125
138
  id: z.ZodString;
126
139
  version: z.ZodString;
@@ -213,6 +226,13 @@ export declare const selfHostCatalogueSchema: z.ZodDiscriminatedUnion<[z.ZodObje
213
226
  }>;
214
227
  command: z.ZodString;
215
228
  }, z.core.$strict>;
229
+ databaseAdapters: z.ZodTuple<[z.ZodObject<{
230
+ package: z.ZodLiteral<"@helyx/database">;
231
+ version: z.ZodString;
232
+ }, z.core.$strict>, z.ZodObject<{
233
+ package: z.ZodLiteral<"@helyx/database-mysql">;
234
+ version: z.ZodString;
235
+ }, z.core.$strict>], null>;
216
236
  modules: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
217
237
  id: z.ZodString;
218
238
  package: z.ZodString;
@@ -245,6 +265,10 @@ export declare const selfHostCatalogueSchema: z.ZodDiscriminatedUnion<[z.ZodObje
245
265
  free: "free";
246
266
  premium: "premium";
247
267
  }>>;
268
+ databaseEngines: z.ZodArray<z.ZodEnum<{
269
+ postgresql: "postgresql";
270
+ mysql: "mysql";
271
+ }>>;
248
272
  dependencies: z.ZodArray<z.ZodObject<{
249
273
  id: z.ZodString;
250
274
  version: z.ZodString;
@@ -295,6 +319,10 @@ export declare const selfHostCatalogueSchema: z.ZodDiscriminatedUnion<[z.ZodObje
295
319
  free: "free";
296
320
  premium: "premium";
297
321
  }>>;
322
+ databaseEngines: z.ZodArray<z.ZodEnum<{
323
+ postgresql: "postgresql";
324
+ mysql: "mysql";
325
+ }>>;
298
326
  dependencies: z.ZodArray<z.ZodObject<{
299
327
  id: z.ZodString;
300
328
  version: z.ZodString;
@@ -40,6 +40,14 @@ export const selfHostReleaseStatusSchema = z.enum([
40
40
  "live",
41
41
  ]);
42
42
  const moduleTier = z.enum(["free", "premium"]);
43
+ export const selfHostDatabaseEngineSchema = z.enum(["postgresql", "mysql"]);
44
+ const databaseEngines = z
45
+ .array(selfHostDatabaseEngineSchema)
46
+ .min(1)
47
+ .max(2)
48
+ .refine((engines) => new Set(engines).size === engines.length, {
49
+ message: "database engines must be unique",
50
+ });
43
51
  const selfHostDeploymentPolicy = {
44
52
  deploymentSupport: z.enum(["self-hosted", "both", "unclassified"]),
45
53
  credentialRisk: z.enum(["none", "unclassified"]),
@@ -52,6 +60,20 @@ const componentSchema = z
52
60
  availability: z.enum(["unpublished", "published"]),
53
61
  })
54
62
  .strict();
63
+ const databaseAdaptersSchema = z.tuple([
64
+ z
65
+ .object({
66
+ package: z.literal("@helyx/database"),
67
+ version: exactVersion,
68
+ })
69
+ .strict(),
70
+ z
71
+ .object({
72
+ package: z.literal("@helyx/database-mysql"),
73
+ version: exactVersion,
74
+ })
75
+ .strict(),
76
+ ]);
55
77
  const generatorSchema = componentSchema
56
78
  .extend({
57
79
  command: z
@@ -107,6 +129,7 @@ export const selfHostModuleSchema = z
107
129
  });
108
130
  const selfHostCustomerModuleV2Schema = selfHostModuleSchema.safeExtend({
109
131
  ...selfHostDeploymentPolicy,
132
+ databaseEngines,
110
133
  dependencies: z.array(moduleDependency),
111
134
  moduleKind: z.literal("customer_feature"),
112
135
  customerSelectable: z.literal(true),
@@ -115,6 +138,7 @@ const selfHostCustomerModuleV2Schema = selfHostModuleSchema.safeExtend({
115
138
  });
116
139
  const selfHostInternalModuleV2Schema = selfHostModuleSchema.safeExtend({
117
140
  ...selfHostDeploymentPolicy,
141
+ databaseEngines,
118
142
  dependencies: z.array(moduleDependency),
119
143
  moduleKind: z.literal("internal_dependency"),
120
144
  customerSelectable: z.literal(false),
@@ -140,6 +164,7 @@ const selfHostCatalogueV2Schema = z
140
164
  catalogueVersion: z.number().int().positive(),
141
165
  core: componentSchema,
142
166
  generator: generatorSchema,
167
+ databaseAdapters: databaseAdaptersSchema,
143
168
  modules: z.array(selfHostModuleV2Schema),
144
169
  })
145
170
  .strict();
@@ -91,11 +91,14 @@ function printUpdatePlan(plan) {
91
91
  }
92
92
  console.log(`Reviewed catalogue ${plan.catalogueVersion} changes:`);
93
93
  for (const change of plan.changes) {
94
- console.log(` ${change.package}: ${change.currentVersion} -> ${change.targetVersion}`);
94
+ console.log(` ${change.package}: ${change.currentVersion ?? "(not installed)"} -> ${change.targetVersion ?? "(removed)"}`);
95
95
  }
96
- console.log(plan.changes.length === 1
97
- ? "1 Helyx package requires an update."
98
- : `${plan.changes.length} Helyx packages require an update.`);
96
+ if (plan.changes.length > 0)
97
+ console.log(plan.changes.length === 1
98
+ ? "1 Helyx package requires an update."
99
+ : `${plan.changes.length} Helyx packages require an update.`);
100
+ if (plan.moduleSelectionChanged)
101
+ console.log("The generated module selection also requires an update.");
99
102
  }
100
103
  export function parseSelfHostInitArguments(arguments_) {
101
104
  let manifestPath;
@@ -0,0 +1,9 @@
1
+ import type { SelfHostCatalogue } from "./catalogue.js";
2
+ export interface SelfHostDatabaseAdapter {
3
+ readonly package: string;
4
+ readonly version: string;
5
+ }
6
+ export declare const selfHostDatabaseAdapters: readonly SelfHostDatabaseAdapter[];
7
+ /** Release metadata, not the installed CLI version, controls upgrade targets. */
8
+ export declare function databaseAdaptersForCatalogue(catalogue: SelfHostCatalogue): readonly SelfHostDatabaseAdapter[];
9
+ //# sourceMappingURL=database-adapters.d.ts.map
@@ -0,0 +1,11 @@
1
+ export const selfHostDatabaseAdapters = Object.freeze([
2
+ Object.freeze({ package: "@helyx/database", version: "2.3.1" }),
3
+ Object.freeze({ package: "@helyx/database-mysql", version: "0.1.0" }),
4
+ ]);
5
+ /** Release metadata, not the installed CLI version, controls upgrade targets. */
6
+ export function databaseAdaptersForCatalogue(catalogue) {
7
+ return catalogue.schemaVersion === 2
8
+ ? catalogue.databaseAdapters
9
+ : selfHostDatabaseAdapters;
10
+ }
11
+ //# sourceMappingURL=database-adapters.js.map
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { type SelfHostCatalogue } from "./catalogue.js";
2
+ import { type SelfHostCatalogue, type SelfHostDatabaseEngine } from "./catalogue.js";
3
3
  export declare const selfHostBuildManifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
4
4
  schemaVersion: z.ZodLiteral<1>;
5
5
  catalogueVersion: z.ZodNumber;
@@ -18,6 +18,10 @@ export declare const selfHostBuildManifestSchema: z.ZodDiscriminatedUnion<[z.Zod
18
18
  }, z.core.$strict>>;
19
19
  }, z.core.$strict>, z.ZodObject<{
20
20
  schemaVersion: z.ZodLiteral<2>;
21
+ databaseEngine: z.ZodOptional<z.ZodEnum<{
22
+ postgresql: "postgresql";
23
+ mysql: "mysql";
24
+ }>>;
21
25
  catalogueVersion: z.ZodNumber;
22
26
  core: z.ZodObject<{
23
27
  package: z.ZodString;
@@ -48,6 +52,6 @@ export declare const selfHostBuildManifestSchema: z.ZodDiscriminatedUnion<[z.Zod
48
52
  }, z.core.$strict>>;
49
53
  }, z.core.$strict>], "schemaVersion">;
50
54
  export type SelfHostBuildManifest = z.infer<typeof selfHostBuildManifestSchema>;
51
- export declare function createSelfHostBuildManifest(catalogue: SelfHostCatalogue, selectedModuleIds: readonly string[]): SelfHostBuildManifest;
55
+ export declare function createSelfHostBuildManifest(catalogue: SelfHostCatalogue, selectedModuleIds: readonly string[], databaseEngine?: SelfHostDatabaseEngine): SelfHostBuildManifest;
52
56
  export declare function parseSelfHostBuildManifest(input: unknown, catalogue: SelfHostCatalogue): SelfHostBuildManifest;
53
57
  //# sourceMappingURL=manifest.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import semver from "semver";
2
2
  import { isDeepStrictEqual } from "node:util";
3
3
  import { z } from "zod";
4
- import { isSelfHostModuleCustomerSelectable, } from "./catalogue.js";
4
+ import { isSelfHostModuleCustomerSelectable, selfHostDatabaseEngineSchema, } from "./catalogue.js";
5
5
  const exactVersion = z
6
6
  .string()
7
7
  .refine((value) => semver.valid(value) === value, "must be exact semver");
@@ -48,6 +48,7 @@ const buildModuleV2Schema = buildModuleSchema
48
48
  const selfHostBuildManifestV2Schema = z
49
49
  .object({
50
50
  schemaVersion: z.literal(2),
51
+ databaseEngine: selfHostDatabaseEngineSchema.optional(),
51
52
  catalogueVersion: z.number().int().positive(),
52
53
  core: buildComponentSchema,
53
54
  generator: buildComponentSchema,
@@ -81,14 +82,18 @@ export const selfHostBuildManifestSchema = z
81
82
  packages.add(module.package);
82
83
  }
83
84
  });
84
- export function createSelfHostBuildManifest(catalogue, selectedModuleIds) {
85
+ export function createSelfHostBuildManifest(catalogue, selectedModuleIds, databaseEngine = "postgresql") {
86
+ selfHostDatabaseEngineSchema.parse(databaseEngine);
85
87
  assertPublishedComponent(catalogue.core, "Helyx runtime");
86
88
  assertPublishedComponent(catalogue.generator, "Helyx generator");
87
89
  if (new Set(selectedModuleIds).size !== selectedModuleIds.length) {
88
90
  throw new Error("Self-host selection contains duplicate module IDs");
89
91
  }
90
92
  if (catalogue.schemaVersion === 2) {
91
- return createSelfHostBuildManifestV2(catalogue, selectedModuleIds);
93
+ return createSelfHostBuildManifestV2(catalogue, selectedModuleIds, databaseEngine);
94
+ }
95
+ if (databaseEngine !== "postgresql") {
96
+ throw new Error("Legacy self-host catalogue supports PostgreSQL only");
92
97
  }
93
98
  const byId = new Map(catalogue.modules.map((module) => [module.id, module]));
94
99
  const modules = [...selectedModuleIds]
@@ -122,7 +127,7 @@ export function createSelfHostBuildManifest(catalogue, selectedModuleIds) {
122
127
  modules,
123
128
  };
124
129
  }
125
- function createSelfHostBuildManifestV2(catalogue, selectedModuleIds) {
130
+ function createSelfHostBuildManifestV2(catalogue, selectedModuleIds, databaseEngine) {
126
131
  const byId = new Map(catalogue.modules.map((module) => [module.id, module]));
127
132
  const explicitlySelected = new Set(selectedModuleIds);
128
133
  const modules = [];
@@ -144,6 +149,9 @@ function createSelfHostBuildManifestV2(catalogue, selectedModuleIds) {
144
149
  if (!module)
145
150
  throw new Error(`Unknown self-host module: ${id}`);
146
151
  assertLiveModule(module, id);
152
+ if (!module.databaseEngines.includes(databaseEngine)) {
153
+ throw new Error(`Self-host module does not support ${databaseEngine}: ${id}`);
154
+ }
147
155
  if (module.deploymentSupport === "unclassified" ||
148
156
  module.credentialRisk === "unclassified")
149
157
  throw new Error(`Self-host module policy is unclassified: ${id}`);
@@ -181,6 +189,7 @@ function createSelfHostBuildManifestV2(catalogue, selectedModuleIds) {
181
189
  include(id);
182
190
  return {
183
191
  schemaVersion: 2,
192
+ ...(databaseEngine === "mysql" ? { databaseEngine } : {}),
184
193
  catalogueVersion: catalogue.catalogueVersion,
185
194
  core: {
186
195
  package: catalogue.core.package,
@@ -209,8 +218,13 @@ export function parseSelfHostBuildManifest(input, catalogue) {
209
218
  const selectedModuleIds = manifest.modules
210
219
  .filter(({ moduleKind }) => moduleKind === "customer_feature")
211
220
  .map(({ id }) => id);
212
- const expected = createSelfHostBuildManifest(catalogue, selectedModuleIds);
213
- if (!isDeepStrictEqual(manifest, expected)) {
221
+ const expected = createSelfHostBuildManifestV2(catalogue, selectedModuleIds, manifest.databaseEngine ?? "postgresql");
222
+ if (!isDeepStrictEqual({
223
+ ...manifest,
224
+ databaseEngine: manifest.databaseEngine === "postgresql"
225
+ ? undefined
226
+ : manifest.databaseEngine,
227
+ }, { ...expected, databaseEngine: expected.databaseEngine })) {
214
228
  throw new Error("Build manifest modules do not match the trusted catalogue dependency graph");
215
229
  }
216
230
  return expected;
@@ -1,11 +1,13 @@
1
1
  import type { BotEnvironmentInput } from "@helyx/config";
2
2
  import { type SelfHostCatalogue } from "./catalogue.js";
3
3
  import { type SelfHostBuildManifest } from "./manifest.js";
4
+ import { type SelfHostDatabaseAdapter } from "./database-adapters.js";
4
5
  export type SelfHostCommandRunner = (arguments_: readonly string[], workingDirectory: string, signal: AbortSignal) => Promise<void>;
5
6
  export interface GenerateSelfHostProjectOptions {
6
7
  catalogue: SelfHostCatalogue;
7
8
  manifest: unknown;
8
9
  targetDirectory: string;
10
+ databaseAdapters?: readonly SelfHostDatabaseAdapter[];
9
11
  install?: boolean;
10
12
  commandRunner?: SelfHostCommandRunner;
11
13
  signal?: AbortSignal;
@@ -16,7 +18,7 @@ export interface GeneratedSelfHostProject {
16
18
  files: string[];
17
19
  }
18
20
  export declare function generateSelfHostProject(options: GenerateSelfHostProjectOptions): Promise<GeneratedSelfHostProject>;
19
- type HostedOnlyBotEnvironment = "HELYX_SUPPORT_SERVICE_SECRET" | "HELYX_SUPPORT_STAFF_CHANNEL_ID" | "HELYX_VERIFICATION_ENABLED" | "HELYX_VERIFICATION_INSTALLATION_ID" | "HELYX_VERIFICATION_URL" | "HELYX_VERIFICATION_INTERNAL_SECRET" | "HELYX_VERIFICATION_SIGNING_PUBLIC_KEYS" | "HELYX_ECKO_TRANSPORT_SECRET" | "HELYX_ECKO_TRANSPORT_PREVIOUS_SECRET" | "STRIPE_ENVIRONMENT";
21
+ type HostedOnlyBotEnvironment = "HELYX_PRIVACY_ENABLED" | "HELYX_PRIVACY_EXECUTOR_DATABASE_URL" | "HELYX_PRIVACY_EXECUTOR_DATABASE_SSL" | "HELYX_SUPPORT_SERVICE_SECRET" | "HELYX_SUPPORT_STAFF_CHANNEL_ID" | "HELYX_VERIFICATION_ENABLED" | "HELYX_VERIFICATION_INSTALLATION_ID" | "HELYX_VERIFICATION_URL" | "HELYX_VERIFICATION_PUBLIC_URL" | "HELYX_VERIFICATION_INTERNAL_SECRET" | "HELYX_VERIFICATION_SIGNING_PUBLIC_KEYS" | "HELYX_ECKO_TRANSPORT_SECRET" | "HELYX_ECKO_TRANSPORT_PREVIOUS_SECRET" | "STRIPE_ENVIRONMENT";
20
22
  type EnvironmentExample = Record<Exclude<keyof BotEnvironmentInput, HostedOnlyBotEnvironment>, {
21
23
  value: string;
22
24
  optional: boolean;