@helyx/cli 1.2.2 → 1.4.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.
- package/CHANGELOG.md +52 -19
- package/README.md +43 -2
- package/dist/cli.js +62 -20
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/self-host/catalogue.d.ts +28 -0
- package/dist/self-host/catalogue.js +25 -0
- package/dist/self-host/command.js +7 -4
- package/dist/self-host/database-adapters.d.ts +9 -0
- package/dist/self-host/database-adapters.js +11 -0
- package/dist/self-host/manifest.d.ts +6 -2
- package/dist/self-host/manifest.js +20 -6
- package/dist/self-host/project-generator.d.ts +3 -1
- package/dist/self-host/project-generator.js +60 -24
- package/dist/self-host/update.d.ts +4 -3
- package/dist/self-host/update.js +72 -25
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +6 -0
- package/package.json +8 -5
- package/self-host-catalogue.json +169 -98
- package/templates/self-host/.env.example +15 -11
- package/templates/self-host/DOCUMENTATION.md +6 -0
- package/templates/self-host/Dockerfile +1 -1
- package/templates/self-host/README.md +142 -23
- package/templates/self-host/compose.mysql.yaml +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Prepare the coordinated Phase 8 minor release and align internal package dependencies. Existing deployment and database-support restrictions are unchanged.
|
|
8
|
+
|
|
9
|
+
Start the generated Docker bot through Node directly so container stop signals
|
|
10
|
+
reach Helyx's shutdown handlers instead of passing through npm. Local npm start
|
|
11
|
+
behaviour is unchanged.
|
|
12
|
+
|
|
13
|
+
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.
|
|
14
|
+
|
|
15
|
+
Align the starter's optional database startup setting with the bot's `apply`
|
|
16
|
+
default, preserving automatic self-host migrations and hosted-only inspect mode.
|
|
17
|
+
|
|
18
|
+
Keep the optional local Verification public URL in the hosted-only environment
|
|
19
|
+
exclusion, so it is not required or emitted in generated self-host projects.
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
- @helyx/bot@1.4.0
|
|
25
|
+
- @helyx/config@1.4.0
|
|
26
|
+
- @helyx/database-mysql@0.2.0
|
|
27
|
+
- @helyx/database@2.4.0
|
|
28
|
+
|
|
29
|
+
## 1.3.0
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
- Add optional MySQL-compatible core migration and doctor commands with
|
|
34
|
+
fail-closed adapter loading, while retaining PostgreSQL as the default and
|
|
35
|
+
blocking full MySQL bot startup until its repositories are complete.
|
|
36
|
+
- Include both reviewed database adapters in generated Self-Hosted projects so
|
|
37
|
+
`DATABASE_ENGINE` can select either backend without rebuilding the project.
|
|
38
|
+
|
|
39
|
+
## 1.2.4
|
|
40
|
+
|
|
41
|
+
### Patch Changes
|
|
42
|
+
|
|
43
|
+
- Refresh the prepared module catalogue for dependency-bound patch releases.
|
|
44
|
+
- Prepare the self-host catalogue for the bot patch that forwards the hosted
|
|
45
|
+
moderation control credential to enabled shard workers.
|
|
46
|
+
|
|
47
|
+
## 1.2.3
|
|
48
|
+
|
|
49
|
+
### Patch Changes
|
|
50
|
+
|
|
51
|
+
- Refresh the reviewed self-host catalogue for the Phase 7 package set, including
|
|
52
|
+
the updated SDK compatibility line and current module release versions.
|
|
53
|
+
|
|
3
54
|
## 1.2.2
|
|
4
55
|
|
|
5
56
|
### Patch Changes
|
|
@@ -305,25 +356,7 @@
|
|
|
305
356
|
|
|
306
357
|
### Patch Changes
|
|
307
358
|
|
|
308
|
-
- Accept compatible internal Helyx releases within explicit minor-safe pre-1.0 or major-safe stable ranges, preventing dependency-only patch cascades
|
|
309
|
-
"@helyx/bot": patch
|
|
310
|
-
"@helyx/cli": patch
|
|
311
|
-
"@helyx/core": patch
|
|
312
|
-
"@helyx/framework": patch
|
|
313
|
-
"@helyx/platform": patch
|
|
314
|
-
"@helyx/sdk": patch
|
|
315
|
-
"@helyx/module-account-gate": patch
|
|
316
|
-
"@helyx/module-feedback": patch
|
|
317
|
-
"@helyx/module-giveaways": patch
|
|
318
|
-
"@helyx/module-levels": patch
|
|
319
|
-
"@helyx/module-logging": patch
|
|
320
|
-
"@helyx/module-polls": patch
|
|
321
|
-
"@helyx/module-reactionroles": patch
|
|
322
|
-
"@helyx/module-suggestions": patch
|
|
323
|
-
"@helyx/module-welcome": patch
|
|
324
|
-
---
|
|
325
|
-
|
|
326
|
-
Accept compatible internal Helyx releases within explicit minor-safe pre-1.0 or major-safe stable ranges, preventing dependency-only patch cascades.
|
|
359
|
+
- Accept compatible internal Helyx releases within explicit minor-safe pre-1.0 or major-safe stable ranges, preventing dependency-only patch cascades.
|
|
327
360
|
|
|
328
361
|
## 0.4.28
|
|
329
362
|
|
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`
|
|
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
|
|
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 {
|
|
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 {
|
|
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(
|
|
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
|
|
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
|
|
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:
|
|
69
|
+
maxConnections: 4,
|
|
65
70
|
});
|
|
66
71
|
try {
|
|
67
|
-
await
|
|
68
|
-
|
|
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
|
|
76
|
+
await session.close();
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
async function doctor() {
|
|
76
80
|
const environment = parseBotEnvironment(process.env);
|
|
77
|
-
const
|
|
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:
|
|
86
|
+
maxConnections: 4,
|
|
87
|
+
inspectionOnly: true,
|
|
82
88
|
});
|
|
83
89
|
try {
|
|
84
|
-
await
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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 =
|
|
213
|
-
if (!isDeepStrictEqual(
|
|
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;
|