@decohere/scaffold 0.0.1-dev.125 → 0.0.1-dev.18
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/package.json +9 -19
- package/src/add-api.action.ts +25 -0
- package/src/add-api.command.ts +31 -0
- package/src/add.command.ts +20 -0
- package/src/add.module.ts +11 -0
- package/src/cli.ts +4 -0
- package/src/create.command.ts +20 -0
- package/src/create.module.ts +11 -0
- package/src/domain/prompt.ts +50 -0
- package/{types/src/index.d.ts → src/index.ts} +0 -4
- package/src/models/compose.model.ts +205 -0
- package/src/models/config-yaml.model.ts +83 -0
- package/src/models/const.ts +20 -0
- package/src/models/dev-container.model.ts +136 -0
- package/src/models/docker-image.model.ts +343 -0
- package/src/models/env-file.model.ts +45 -0
- package/src/models/gitignore.model.ts +35 -0
- package/src/models/launch-json.model.ts +60 -0
- package/src/models/package-json.model.ts +169 -0
- package/src/models/project.model.ts +141 -0
- package/{types/src/models/source-file.d.ts → src/models/source-file.ts} +6 -4
- package/src/models/tsconfig.model.ts +29 -0
- package/src/models/vitest-config.model.ts +26 -0
- package/src/models/vscode-settings.model.ts +24 -0
- package/src/program.ts +63 -0
- package/{types/src/resolve-target-dir.d.ts → src/resolve-target-dir.ts} +4 -1
- package/src/services/scaffold.service.ts +89 -0
- package/src/version.ts +56 -0
- package/src/webapi/api-name.ts +73 -0
- package/src/webapi/cli-options.ts +56 -0
- package/{types/src/webapi/const.d.ts → src/webapi/const.ts} +21 -9
- package/src/webapi/create-api.ts +275 -0
- package/src/webapi/devcontainer/host-scripts.ts +35 -0
- package/src/webapi/features/auth.feature.ts +454 -0
- package/src/webapi/features/auth0-cli.ts +421 -0
- package/src/webapi/features/biome-format.ts +34 -0
- package/src/webapi/features/database.feature.ts +522 -0
- package/src/webapi/features/finalize.feature.ts +112 -0
- package/src/webapi/features/postman.feature.ts +287 -0
- package/src/webapi/models/api-package-json.model.ts +60 -0
- package/src/webapi/models/auth-config.model.ts +36 -0
- package/src/webapi/models/controller-source-file.model.ts +232 -0
- package/{es6/src/webapi/models/github-ci.model.js → src/webapi/models/github-ci.model.ts} +29 -27
- package/src/webapi/models/main-source-file.model.ts +208 -0
- package/src/webapi/models/production-dockerfile.model.ts +162 -0
- package/src/webapi/models/sample-files.model.ts +45 -0
- package/src/webapi/models/webapi-image.model.ts +21 -0
- package/src/webapi/postman/hello-openapi.ts +92 -0
- package/src/webapi/postman/local-view.ts +195 -0
- package/src/webapi/postman/openapi-to-postman.ts +208 -0
- package/src/webapi/workspace-readme.ts +192 -0
- package/src/webapi.action.ts +22 -0
- package/src/webapi.command.ts +41 -0
- package/tests/auth-postman.wiring.spec.ts +263 -0
- package/tests/auth0-cli.spec.ts +232 -0
- package/tests/biome-format.spec.ts +49 -0
- package/tests/cli.integration.spec.ts +658 -0
- package/tests/compose.model.spec.ts +51 -0
- package/tests/container-image.model.spec.ts +162 -0
- package/tests/dev-container.model.spec.ts +29 -0
- package/tests/devcontainer-base-image.spec.ts +32 -0
- package/tests/hello-di-inject.scaffold.spec.ts +18 -0
- package/tests/hello-route.contract.spec.ts +94 -0
- package/tests/package-json.order.spec.ts +59 -0
- package/tests/postman-from-openapi.spec.ts +197 -0
- package/tests/seed-devcontainer-templates.ts +40 -0
- package/tests/webapi/devcontainer/codebase-rag-mcp.spec.ts +96 -0
- package/LICENSE +0 -15
- package/commonjs/src/add-api.action.js +0 -37
- package/commonjs/src/add-api.command.js +0 -39
- package/commonjs/src/add.command.js +0 -22
- package/commonjs/src/add.module.js +0 -24
- package/commonjs/src/auth0-configure.action.js +0 -28
- package/commonjs/src/auth0-configure.command.js +0 -24
- package/commonjs/src/auth0-login.action.js +0 -28
- package/commonjs/src/auth0-login.command.js +0 -23
- package/commonjs/src/auth0.command.js +0 -22
- package/commonjs/src/auth0.module.js +0 -28
- package/commonjs/src/cli.js +0 -8
- package/commonjs/src/create.command.js +0 -22
- package/commonjs/src/create.module.js +0 -24
- package/commonjs/src/domain/prompt.js +0 -62
- package/commonjs/src/index.js +0 -48
- package/commonjs/src/models/compose.model.js +0 -258
- package/commonjs/src/models/config-yaml.model.js +0 -86
- package/commonjs/src/models/const.js +0 -30
- package/commonjs/src/models/dev-container.model.js +0 -172
- package/commonjs/src/models/docker-image.model.js +0 -277
- package/commonjs/src/models/env-file.model.js +0 -48
- package/commonjs/src/models/gitignore.model.js +0 -48
- package/commonjs/src/models/launch-json.model.js +0 -70
- package/commonjs/src/models/package-json.model.js +0 -156
- package/commonjs/src/models/project.model.js +0 -132
- package/commonjs/src/models/source-file.js +0 -4
- package/commonjs/src/models/tsconfig.model.js +0 -32
- package/commonjs/src/models/vitest-config.model.js +0 -33
- package/commonjs/src/models/vscode-settings.model.js +0 -29
- package/commonjs/src/program.js +0 -64
- package/commonjs/src/resolve-target-dir.js +0 -17
- package/commonjs/src/services/scaffold.service.js +0 -60
- package/commonjs/src/version.js +0 -55
- package/commonjs/src/webapi/api-name.js +0 -93
- package/commonjs/src/webapi/auth0-commands.js +0 -250
- package/commonjs/src/webapi/authorize-scopes.js +0 -79
- package/commonjs/src/webapi/cli-options.js +0 -99
- package/commonjs/src/webapi/const.js +0 -54
- package/commonjs/src/webapi/create-api.js +0 -265
- package/commonjs/src/webapi/devcontainer/codebase-rag-mcp.js +0 -164
- package/commonjs/src/webapi/devcontainer/codebase-rag-mcp.ts +0 -182
- package/commonjs/src/webapi/devcontainer/host-scripts.js +0 -50
- package/commonjs/src/webapi/devcontainer/templates/codebase-rag.env.example +0 -3
- package/commonjs/src/webapi/devcontainer/templates/initialize.sh +0 -169
- package/commonjs/src/webapi/devcontainer/templates/post-create.sh +0 -35
- package/commonjs/src/webapi/devcontainer/templates/post-start.sh +0 -65
- package/commonjs/src/webapi/devcontainer/templates/tty-prompt.sh +0 -38
- package/commonjs/src/webapi/features/auth.feature.js +0 -408
- package/commonjs/src/webapi/features/auth0-cli.js +0 -472
- package/commonjs/src/webapi/features/biome-format.js +0 -47
- package/commonjs/src/webapi/features/database.feature.js +0 -433
- package/commonjs/src/webapi/features/finalize.feature.js +0 -93
- package/commonjs/src/webapi/features/postman.feature.js +0 -322
- package/commonjs/src/webapi/models/api-package-json.model.js +0 -58
- package/commonjs/src/webapi/models/auth-config.model.js +0 -24
- package/commonjs/src/webapi/models/controller-source-file.model.js +0 -190
- package/commonjs/src/webapi/models/github-ci.model.js +0 -193
- package/commonjs/src/webapi/models/main-source-file.model.js +0 -165
- package/commonjs/src/webapi/models/production-dockerfile.model.js +0 -75
- package/commonjs/src/webapi/models/sample-files.model.js +0 -58
- package/commonjs/src/webapi/models/webapi-image.model.js +0 -25
- package/commonjs/src/webapi/postman/hello-openapi.js +0 -173
- package/commonjs/src/webapi/postman/local-view.js +0 -182
- package/commonjs/src/webapi/postman/openapi-to-postman.js +0 -238
- package/commonjs/src/webapi/workspace-readme.js +0 -109
- package/commonjs/src/webapi.action.js +0 -28
- package/commonjs/src/webapi.command.js +0 -43
- package/es6/src/add-api.action.js +0 -27
- package/es6/src/add-api.command.js +0 -29
- package/es6/src/add.command.js +0 -12
- package/es6/src/add.module.js +0 -14
- package/es6/src/auth0-configure.action.js +0 -18
- package/es6/src/auth0-configure.command.js +0 -14
- package/es6/src/auth0-login.action.js +0 -18
- package/es6/src/auth0-login.command.js +0 -13
- package/es6/src/auth0.command.js +0 -12
- package/es6/src/auth0.module.js +0 -18
- package/es6/src/cli.js +0 -4
- package/es6/src/create.command.js +0 -12
- package/es6/src/create.module.js +0 -14
- package/es6/src/domain/prompt.js +0 -41
- package/es6/src/index.js +0 -9
- package/es6/src/models/compose.model.js +0 -254
- package/es6/src/models/config-yaml.model.js +0 -74
- package/es6/src/models/const.js +0 -20
- package/es6/src/models/dev-container.model.js +0 -162
- package/es6/src/models/docker-image.model.js +0 -235
- package/es6/src/models/env-file.model.js +0 -44
- package/es6/src/models/gitignore.model.js +0 -38
- package/es6/src/models/launch-json.model.js +0 -60
- package/es6/src/models/package-json.model.js +0 -141
- package/es6/src/models/project.model.js +0 -117
- package/es6/src/models/source-file.js +0 -1
- package/es6/src/models/tsconfig.model.js +0 -22
- package/es6/src/models/vitest-config.model.js +0 -23
- package/es6/src/models/vscode-settings.model.js +0 -19
- package/es6/src/program.js +0 -50
- package/es6/src/resolve-target-dir.js +0 -7
- package/es6/src/services/scaffold.service.js +0 -50
- package/es6/src/version.js +0 -34
- package/es6/src/webapi/api-name.js +0 -57
- package/es6/src/webapi/auth0-commands.js +0 -185
- package/es6/src/webapi/authorize-scopes.js +0 -55
- package/es6/src/webapi/cli-options.js +0 -34
- package/es6/src/webapi/const.js +0 -21
- package/es6/src/webapi/create-api.js +0 -245
- package/es6/src/webapi/devcontainer/codebase-rag-mcp.js +0 -154
- package/es6/src/webapi/devcontainer/codebase-rag-mcp.ts +0 -182
- package/es6/src/webapi/devcontainer/host-scripts.js +0 -29
- package/es6/src/webapi/devcontainer/templates/codebase-rag.env.example +0 -3
- package/es6/src/webapi/devcontainer/templates/initialize.sh +0 -169
- package/es6/src/webapi/devcontainer/templates/post-create.sh +0 -35
- package/es6/src/webapi/devcontainer/templates/post-start.sh +0 -65
- package/es6/src/webapi/devcontainer/templates/tty-prompt.sh +0 -38
- package/es6/src/webapi/features/auth.feature.js +0 -357
- package/es6/src/webapi/features/auth0-cli.js +0 -406
- package/es6/src/webapi/features/biome-format.js +0 -40
- package/es6/src/webapi/features/database.feature.js +0 -423
- package/es6/src/webapi/features/finalize.feature.js +0 -83
- package/es6/src/webapi/features/postman.feature.js +0 -256
- package/es6/src/webapi/models/api-package-json.model.js +0 -53
- package/es6/src/webapi/models/auth-config.model.js +0 -20
- package/es6/src/webapi/models/controller-source-file.model.js +0 -181
- package/es6/src/webapi/models/main-source-file.model.js +0 -161
- package/es6/src/webapi/models/production-dockerfile.model.js +0 -66
- package/es6/src/webapi/models/sample-files.model.js +0 -40
- package/es6/src/webapi/models/webapi-image.model.js +0 -18
- package/es6/src/webapi/postman/hello-openapi.js +0 -166
- package/es6/src/webapi/postman/local-view.js +0 -167
- package/es6/src/webapi/postman/openapi-to-postman.js +0 -179
- package/es6/src/webapi/workspace-readme.js +0 -88
- package/es6/src/webapi.action.js +0 -18
- package/es6/src/webapi.command.js +0 -33
- package/types/src/add-api.action.d.ts +0 -13
- package/types/src/add-api.command.d.ts +0 -7
- package/types/src/add.command.d.ts +0 -6
- package/types/src/add.module.d.ts +0 -14
- package/types/src/auth0-configure.action.d.ts +0 -7
- package/types/src/auth0-configure.command.d.ts +0 -6
- package/types/src/auth0-login.action.d.ts +0 -4
- package/types/src/auth0-login.command.d.ts +0 -6
- package/types/src/auth0.command.d.ts +0 -6
- package/types/src/auth0.module.d.ts +0 -18
- package/types/src/cli.d.ts +0 -2
- package/types/src/create.command.d.ts +0 -6
- package/types/src/create.module.d.ts +0 -14
- package/types/src/domain/prompt.d.ts +0 -4
- package/types/src/models/compose.model.d.ts +0 -73
- package/types/src/models/config-yaml.model.d.ts +0 -27
- package/types/src/models/const.d.ts +0 -20
- package/types/src/models/dev-container.model.d.ts +0 -49
- package/types/src/models/docker-image.model.d.ts +0 -90
- package/types/src/models/env-file.model.d.ts +0 -16
- package/types/src/models/gitignore.model.d.ts +0 -9
- package/types/src/models/launch-json.model.d.ts +0 -7
- package/types/src/models/package-json.model.d.ts +0 -36
- package/types/src/models/project.model.d.ts +0 -60
- package/types/src/models/tsconfig.model.d.ts +0 -5
- package/types/src/models/vitest-config.model.d.ts +0 -5
- package/types/src/models/vscode-settings.model.d.ts +0 -7
- package/types/src/program.d.ts +0 -8
- package/types/src/services/scaffold.service.d.ts +0 -22
- package/types/src/version.d.ts +0 -6
- package/types/src/webapi/api-name.d.ts +0 -14
- package/types/src/webapi/auth0-commands.d.ts +0 -11
- package/types/src/webapi/authorize-scopes.d.ts +0 -7
- package/types/src/webapi/cli-options.d.ts +0 -6
- package/types/src/webapi/create-api.d.ts +0 -15
- package/types/src/webapi/devcontainer/codebase-rag-mcp.d.ts +0 -32
- package/types/src/webapi/devcontainer/codebase-rag-mcp.ts +0 -182
- package/types/src/webapi/devcontainer/host-scripts.d.ts +0 -7
- package/types/src/webapi/devcontainer/templates/codebase-rag.env.example +0 -3
- package/types/src/webapi/devcontainer/templates/initialize.sh +0 -169
- package/types/src/webapi/devcontainer/templates/post-create.sh +0 -35
- package/types/src/webapi/devcontainer/templates/post-start.sh +0 -65
- package/types/src/webapi/devcontainer/templates/tty-prompt.sh +0 -38
- package/types/src/webapi/features/auth.feature.d.ts +0 -10
- package/types/src/webapi/features/auth0-cli.d.ts +0 -84
- package/types/src/webapi/features/biome-format.d.ts +0 -5
- package/types/src/webapi/features/database.feature.d.ts +0 -7
- package/types/src/webapi/features/finalize.feature.d.ts +0 -2
- package/types/src/webapi/features/postman.feature.d.ts +0 -16
- package/types/src/webapi/models/api-package-json.model.d.ts +0 -18
- package/types/src/webapi/models/auth-config.model.d.ts +0 -26
- package/types/src/webapi/models/controller-source-file.model.d.ts +0 -46
- package/types/src/webapi/models/github-ci.model.d.ts +0 -9
- package/types/src/webapi/models/main-source-file.model.d.ts +0 -54
- package/types/src/webapi/models/production-dockerfile.model.d.ts +0 -18
- package/types/src/webapi/models/sample-files.model.d.ts +0 -7
- package/types/src/webapi/models/webapi-image.model.d.ts +0 -12
- package/types/src/webapi/postman/hello-openapi.d.ts +0 -19
- package/types/src/webapi/postman/local-view.d.ts +0 -17
- package/types/src/webapi/postman/openapi-to-postman.d.ts +0 -62
- package/types/src/webapi/workspace-readme.d.ts +0 -18
- package/types/src/webapi.action.d.ts +0 -14
- package/types/src/webapi.command.d.ts +0 -11
|
@@ -1,423 +0,0 @@
|
|
|
1
|
-
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { pick } from "../../domain/prompt.js";
|
|
5
|
-
import { WELL_KNOWN_FILES } from "../../models/const.js";
|
|
6
|
-
import { withPostgresqlClient } from "../../models/docker-image.model.js";
|
|
7
|
-
import { CREATE_VERSION } from "../../version.js";
|
|
8
|
-
import { apiEnvPrefix, apiNameFromProjectDir } from "../api-name.js";
|
|
9
|
-
/** Bind path relative to `.devcontainer/docker-compose.yml` (dies with the working tree). */ const POSTGRES_DATA_BIND = "./postgres-data:/var/lib/postgresql/data";
|
|
10
|
-
const POSTGRES_DATA_GITIGNORE = ".devcontainer/postgres-data";
|
|
11
|
-
const DB_RESET_SCRIPT = "rm -rf .devcontainer/postgres-data && docker compose -f .devcontainer/docker-compose.yml down && docker compose -f .devcontainer/docker-compose.yml up -d postgres";
|
|
12
|
-
/** Read a single KEY= from an existing target `.env` (if any). */ function readExistingEnvValue(workspace, key) {
|
|
13
|
-
try {
|
|
14
|
-
const raw = readFileSync(join(workspace.targetDir, ".env"), "utf8");
|
|
15
|
-
const line = raw.split("\n").find((l)=>l.startsWith(`${key}=`));
|
|
16
|
-
const value = line?.slice(key.length + 1).trim();
|
|
17
|
-
return value || undefined;
|
|
18
|
-
} catch {
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
function randomPassword() {
|
|
23
|
-
return randomBytes(16).toString("hex");
|
|
24
|
-
}
|
|
25
|
-
/** Safe Postgres identifier fragment from the project name. */ function toSqlIdentFragment(name) {
|
|
26
|
-
const cleaned = name.replace(/[^A-Za-z0-9_]/g, "_").replace(/^([^A-Za-z_])/, "p_$1");
|
|
27
|
-
return cleaned.length > 0 ? cleaned.slice(0, 40) : "app";
|
|
28
|
-
}
|
|
29
|
-
function passwordOrExisting(workspace, key, fallbackExistingKeys = []) {
|
|
30
|
-
const existing = readExistingEnvValue(workspace, key);
|
|
31
|
-
if (existing) return existing;
|
|
32
|
-
for (const alt of fallbackExistingKeys){
|
|
33
|
-
const v = readExistingEnvValue(workspace, alt);
|
|
34
|
-
if (v) return v;
|
|
35
|
-
}
|
|
36
|
-
return randomPassword();
|
|
37
|
-
}
|
|
38
|
-
// Production: runtime app role only — no bootstrap, no admin/migrate secrets required.
|
|
39
|
-
function renderBaseDataSource(apiName) {
|
|
40
|
-
const p = apiEnvPrefix(apiName);
|
|
41
|
-
return [
|
|
42
|
-
` - name: ${apiName}`,
|
|
43
|
-
` provider: postgres`,
|
|
44
|
-
` host: \${${p}_DB_HOST}`,
|
|
45
|
-
` port: \${${p}_DB_PORT}`,
|
|
46
|
-
` database: \${${p}_DB_NAME}`,
|
|
47
|
-
` username: \${${p}_DB_USER}`,
|
|
48
|
-
` password: \${${p}_DB_PASSWORD}`,
|
|
49
|
-
` synchronize: false`,
|
|
50
|
-
` seed: false`,
|
|
51
|
-
` bootstrap: false`
|
|
52
|
-
].join("\n");
|
|
53
|
-
}
|
|
54
|
-
// Dev overlay: @decohere/config merges it into config.yaml by data source name, so it only states
|
|
55
|
-
// what development changes. Enable bootstrap + migrate role.
|
|
56
|
-
function renderDevDataSource(apiName) {
|
|
57
|
-
const p = apiEnvPrefix(apiName);
|
|
58
|
-
return [
|
|
59
|
-
` - name: ${apiName}`,
|
|
60
|
-
` synchronize: true`,
|
|
61
|
-
` seed: true`,
|
|
62
|
-
` bootstrap: true`,
|
|
63
|
-
` adminUser: \${DB_ADMIN_USER}`,
|
|
64
|
-
` adminPassword: \${DB_ADMIN_PASSWORD}`,
|
|
65
|
-
` migrateUsername: \${${p}_DB_MIGRATE_USER}`,
|
|
66
|
-
` migratePassword: \${${p}_DB_MIGRATE_PASSWORD}`
|
|
67
|
-
].join("\n");
|
|
68
|
-
}
|
|
69
|
-
function renderDatabaseSection(apiNames, items) {
|
|
70
|
-
return `database:\n dataSources:\n${apiNames.map(items).join("\n")}\n`;
|
|
71
|
-
}
|
|
72
|
-
/** Create-or-sync bootstrap role; runs on every Postgres container start (not only empty volume). */ const POSTGRES_ENSURE_BOOTSTRAP_SH = `#!/bin/bash
|
|
73
|
-
set -euo pipefail
|
|
74
|
-
# Superuser aligns decohere_bootstrap with DB_ADMIN_* from env_file on every start.
|
|
75
|
-
# Initdb.d alone is insufficient: passwords in .env can change while the volume persists.
|
|
76
|
-
: "\${POSTGRES_USER:?POSTGRES_USER is required}"
|
|
77
|
-
: "\${DB_ADMIN_USER:?DB_ADMIN_USER is required}"
|
|
78
|
-
: "\${DB_ADMIN_PASSWORD:?DB_ADMIN_PASSWORD is required}"
|
|
79
|
-
|
|
80
|
-
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname postgres \\
|
|
81
|
-
\t--set=bootstrap_user="$DB_ADMIN_USER" \\
|
|
82
|
-
\t--set=bootstrap_password="$DB_ADMIN_PASSWORD" <<'EOSQL'
|
|
83
|
-
SELECT format(
|
|
84
|
-
\t'CREATE ROLE %I LOGIN PASSWORD %L CREATEDB CREATEROLE NOSUPERUSER NOBYPASSRLS',
|
|
85
|
-
\t:'bootstrap_user',
|
|
86
|
-
\t:'bootstrap_password'
|
|
87
|
-
)
|
|
88
|
-
WHERE NOT EXISTS (
|
|
89
|
-
\tSELECT 1 FROM pg_catalog.pg_roles WHERE rolname = :'bootstrap_user'
|
|
90
|
-
)\\gexec
|
|
91
|
-
|
|
92
|
-
SELECT format(
|
|
93
|
-
\t'ALTER ROLE %I WITH LOGIN PASSWORD %L CREATEDB CREATEROLE NOSUPERUSER NOBYPASSRLS',
|
|
94
|
-
\t:'bootstrap_user',
|
|
95
|
-
\t:'bootstrap_password'
|
|
96
|
-
)\\gexec
|
|
97
|
-
|
|
98
|
-
GRANT CONNECT ON DATABASE postgres TO :"bootstrap_user";
|
|
99
|
-
EOSQL
|
|
100
|
-
`;
|
|
101
|
-
const POSTGRES_ENTRYPOINT_SH = `#!/bin/bash
|
|
102
|
-
set -euo pipefail
|
|
103
|
-
(
|
|
104
|
-
\tfor _ in $(seq 1 60); do
|
|
105
|
-
\t\tif pg_isready -U "\${POSTGRES_USER:-postgres}" >/dev/null 2>&1; then
|
|
106
|
-
\t\t\t/usr/local/bin/decohere-ensure-bootstrap.sh
|
|
107
|
-
\t\t\texit 0
|
|
108
|
-
\t\tfi
|
|
109
|
-
\t\tsleep 0.5
|
|
110
|
-
\tdone
|
|
111
|
-
\techo "decohere: timed out waiting for Postgres before ensuring bootstrap role" >&2
|
|
112
|
-
\texit 1
|
|
113
|
-
) &
|
|
114
|
-
exec docker-entrypoint.sh "$@"
|
|
115
|
-
`;
|
|
116
|
-
const POSTGRES_IMAGE_DOCKERFILE = `FROM postgres:16
|
|
117
|
-
COPY ensure-bootstrap-role.sh /usr/local/bin/decohere-ensure-bootstrap.sh
|
|
118
|
-
COPY entrypoint.sh /usr/local/bin/decohere-postgres-entrypoint.sh
|
|
119
|
-
RUN chmod +x /usr/local/bin/decohere-ensure-bootstrap.sh /usr/local/bin/decohere-postgres-entrypoint.sh
|
|
120
|
-
ENTRYPOINT ["/usr/local/bin/decohere-postgres-entrypoint.sh"]
|
|
121
|
-
CMD ["postgres"]
|
|
122
|
-
`;
|
|
123
|
-
const SAMPLE_ENTITY_TS = `import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
|
124
|
-
import type { Entity as IEntity } from "@decohere/data";
|
|
125
|
-
|
|
126
|
-
@Entity()
|
|
127
|
-
export class SampleEntity implements IEntity {
|
|
128
|
-
\t@PrimaryGeneratedColumn()
|
|
129
|
-
\tpublic id!: number;
|
|
130
|
-
|
|
131
|
-
\t@Column()
|
|
132
|
-
\tpublic name!: string;
|
|
133
|
-
}
|
|
134
|
-
`;
|
|
135
|
-
const SAMPLE_REPOSITORY_TS = `import { injectable } from "@decohere/crucible/decorators";
|
|
136
|
-
import { injectUnitOfWork } from "@decohere/data";
|
|
137
|
-
import { TypeOrmRepository, TypeOrmUnitOfWork } from "@decohere/data-typeorm";
|
|
138
|
-
import { SampleEntity } from "../entities/sample.entity.js";
|
|
139
|
-
|
|
140
|
-
@injectable()
|
|
141
|
-
export class SampleEntityRepository extends TypeOrmRepository<SampleEntity> {
|
|
142
|
-
\tpublic constructor(@injectUnitOfWork() unitOfWork: TypeOrmUnitOfWork) {
|
|
143
|
-
\t\tsuper(unitOfWork, SampleEntity);
|
|
144
|
-
\t}
|
|
145
|
-
}
|
|
146
|
-
`;
|
|
147
|
-
/** TypeORM CLI DataSource — connects as the migrate role; never enables synchronize. */ function renderTypeOrmDataSource(apiName) {
|
|
148
|
-
const p = apiEnvPrefix(apiName);
|
|
149
|
-
return `import { dirname, join } from "node:path";
|
|
150
|
-
import { fileURLToPath } from "node:url";
|
|
151
|
-
import { DataSource } from "typeorm";
|
|
152
|
-
|
|
153
|
-
const root = dirname(fileURLToPath(import.meta.url));
|
|
154
|
-
const required = (key: string): string => {
|
|
155
|
-
\tconst value = process.env[key];
|
|
156
|
-
\tif (value == null || value === "") {
|
|
157
|
-
\t\tthrow new Error(\`Missing env \${key} (load .env via --env-file or export it)\`);
|
|
158
|
-
\t}
|
|
159
|
-
\treturn value;
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* CLI-only DataSource for \`pnpm db:migrate\` / \`pnpm db:migrate:generate\`.
|
|
164
|
-
* Uses the migrate role (DDL). Runtime API pods use the app role with synchronize: false.
|
|
165
|
-
*/
|
|
166
|
-
export default new DataSource({
|
|
167
|
-
\ttype: "postgres",
|
|
168
|
-
\thost: required("${p}_DB_HOST"),
|
|
169
|
-
\tport: Number(required("${p}_DB_PORT")),
|
|
170
|
-
\tdatabase: required("${p}_DB_NAME"),
|
|
171
|
-
\tusername: required("${p}_DB_MIGRATE_USER"),
|
|
172
|
-
\tpassword: required("${p}_DB_MIGRATE_PASSWORD"),
|
|
173
|
-
\tsynchronize: false,
|
|
174
|
-
\tentities: [join(root, "entities", "*.{ts,js}")],
|
|
175
|
-
\tmigrations: [join(root, "migrations", "*.{ts,js}")],
|
|
176
|
-
});
|
|
177
|
-
`;
|
|
178
|
-
}
|
|
179
|
-
const DATABASE_DOCS_MD = `# Database (Postgres / TypeORM)
|
|
180
|
-
|
|
181
|
-
## Roles
|
|
182
|
-
|
|
183
|
-
| Role | Env | Purpose |
|
|
184
|
-
|---|---|---|
|
|
185
|
-
| App | \`{PREFIX}_DB_USER\` | Runtime API (DML only in production) |
|
|
186
|
-
| Migrate | \`{PREFIX}_DB_MIGRATE_USER\` | Schema changes / TypeORM migrations (CI/deploy) |
|
|
187
|
-
| Bootstrap | \`DB_ADMIN_USER\` | Create DB + roles when \`bootstrap: true\` (dev) |
|
|
188
|
-
|
|
189
|
-
\`{PREFIX}\` is the API slug in SCREAMING_SNAKE (\`api\` → \`API\`, \`billing\` → \`BILLING\`).
|
|
190
|
-
|
|
191
|
-
## Development (fast iteration)
|
|
192
|
-
|
|
193
|
-
\`config/config.dev.yaml\` sets \`synchronize: true\` and \`seed: true\` so the API can create tables on startup. You can keep using sync while prototyping.
|
|
194
|
-
|
|
195
|
-
Alternatively, turn sync off in the overlay and use migrations the same way as production:
|
|
196
|
-
|
|
197
|
-
\`\`\`bash
|
|
198
|
-
pnpm db:up
|
|
199
|
-
pnpm db:migrate
|
|
200
|
-
pnpm db:migrate:generate -- ./projects/api/src/migrations/AddSomething
|
|
201
|
-
\`\`\`
|
|
202
|
-
|
|
203
|
-
Do **not** mix sync and migrations for the same schema change (pick one path per change).
|
|
204
|
-
|
|
205
|
-
## Production (migrations only)
|
|
206
|
-
|
|
207
|
-
\`config/config.yaml\` keeps \`synchronize: false\` / \`seed: false\` / \`bootstrap: false\`. Deploy steps:
|
|
208
|
-
|
|
209
|
-
1. Ensure the app database and roles already exist (provisioner / ops — not the API pod).
|
|
210
|
-
2. Run migrations with the migrate role: \`pnpm db:migrate\` (or \`pnpm db:migrate:<api>\` for multi-API).
|
|
211
|
-
3. Start the API with the app role only — no migrate/admin secrets on the pod.
|
|
212
|
-
|
|
213
|
-
## Scripts
|
|
214
|
-
|
|
215
|
-
| Script | What it does |
|
|
216
|
-
|---|---|
|
|
217
|
-
| \`db:up\` / \`db:down\` / \`db:reset\` | Local Compose Postgres |
|
|
218
|
-
| \`db:migrate\` | Run pending migrations (\`projects/api\` by default) |
|
|
219
|
-
| \`db:migrate:generate\` | Generate a migration from entity drift (pass path after \`--\`) |
|
|
220
|
-
| \`db:migrate:<api>\` | Same for a named API under \`projects/<api>\` |
|
|
221
|
-
|
|
222
|
-
CLI entry: \`projects/<api>/src/typeorm.data-source.ts\` (migrate credentials, \`synchronize: false\`).
|
|
223
|
-
|
|
224
|
-
## Browse the database
|
|
225
|
-
|
|
226
|
-
Prefer the Dev Container **SQLTools** extension (Postgres driver) or any desktop client against \`127.0.0.1:5433\` (see \`.env\` for credentials).
|
|
227
|
-
|
|
228
|
-
Adminer is **not** scaffolded. If you want a browser UI in Compose, add this under \`services:\` in \`.devcontainer/docker-compose.yml\` (profile \`tools\` so it stays off by default):
|
|
229
|
-
|
|
230
|
-
\`\`\`yaml
|
|
231
|
-
adminer:
|
|
232
|
-
image: adminer
|
|
233
|
-
profiles:
|
|
234
|
-
- tools
|
|
235
|
-
ports:
|
|
236
|
-
- "127.0.0.1:8081:8080"
|
|
237
|
-
networks:
|
|
238
|
-
- <compose-network-key> # same key as postgres / devcontainer
|
|
239
|
-
\`\`\`
|
|
240
|
-
|
|
241
|
-
Then: \`docker compose -f .devcontainer/docker-compose.yml --profile tools up -d adminer\`
|
|
242
|
-
`;
|
|
243
|
-
function typeormCli(args) {
|
|
244
|
-
return `node --import tsx --env-file=.env ./node_modules/typeorm/cli.js ${args}`;
|
|
245
|
-
}
|
|
246
|
-
function migrateRunScript(apiName) {
|
|
247
|
-
return typeormCli(`migration:run -d ./projects/${apiName}/src/typeorm.data-source.ts`);
|
|
248
|
-
}
|
|
249
|
-
function migrateGenerateScript(apiName) {
|
|
250
|
-
return typeormCli(`migration:generate --esm -d ./projects/${apiName}/src/typeorm.data-source.ts`);
|
|
251
|
-
}
|
|
252
|
-
async function getOptions(_workspace, cliOptions) {
|
|
253
|
-
if (cliOptions.databaseProvider != null) return {};
|
|
254
|
-
const choices = [
|
|
255
|
-
{
|
|
256
|
-
label: "none"
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
label: "postgres/typeorm"
|
|
260
|
-
}
|
|
261
|
-
];
|
|
262
|
-
const selected = await pick("\n Database: ", choices, (c)=>c.label);
|
|
263
|
-
return {
|
|
264
|
-
databaseProvider: selected?.label ?? "none"
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
function scaffold(workspace, options) {
|
|
268
|
-
if (options.databaseProvider !== "postgres/typeorm") return;
|
|
269
|
-
const name = workspace.name;
|
|
270
|
-
const ident = toSqlIdentFragment(name);
|
|
271
|
-
const apiProjects = workspace.getProjects();
|
|
272
|
-
const apiNames = apiProjects.map((project)=>apiNameFromProjectDir(project.dir));
|
|
273
|
-
// Container init superuser — never reused as an API runtime role.
|
|
274
|
-
const postgresSuperuser = "postgres";
|
|
275
|
-
const postgresPassword = passwordOrExisting(workspace, "POSTGRES_PASSWORD");
|
|
276
|
-
const bootstrapUser = "decohere_bootstrap";
|
|
277
|
-
const bootstrapPassword = passwordOrExisting(workspace, "DB_ADMIN_PASSWORD", [
|
|
278
|
-
"POSTGRES_PASSWORD"
|
|
279
|
-
]);
|
|
280
|
-
const devContainer = workspace.getModel(WELL_KNOWN_FILES.DEVCONTAINER_JSON);
|
|
281
|
-
const compose = workspace.getModel(WELL_KNOWN_FILES.DOCKER_COMPOSE);
|
|
282
|
-
const networkKey = name;
|
|
283
|
-
// Skip when the workspace already runs Postgres (`decohere add api` on a database workspace).
|
|
284
|
-
if (compose && !compose.hasService("postgres")) {
|
|
285
|
-
// One shared instance. Each API owns a database on it (created at bootstrap).
|
|
286
|
-
// Point `{PREFIX}_DB_HOST` / `{PREFIX}_DB_PORT` at another server to split later.
|
|
287
|
-
// Custom image: sync bootstrap role password on every start (initdb.d is once-only).
|
|
288
|
-
// Default data is a bind mount under the project so `rm -rf` the tree removes it.
|
|
289
|
-
compose.addHeaderComment("Postgres data defaults to ./postgres-data under this folder (bind mount; dies with the working tree).", `For data that outlives the checkout, replace the postgres volumes entry with a named volume`, `e.g. "${name}-postgres-data:/var/lib/postgresql/data" and declare it under top-level volumes:.`).addService("postgres", {
|
|
290
|
-
build: "./postgres",
|
|
291
|
-
container_name: `samples-${name}-postgres`,
|
|
292
|
-
restart: "unless-stopped",
|
|
293
|
-
ports: [
|
|
294
|
-
"127.0.0.1:5433:5432"
|
|
295
|
-
],
|
|
296
|
-
volumes: [
|
|
297
|
-
POSTGRES_DATA_BIND
|
|
298
|
-
],
|
|
299
|
-
networks: [
|
|
300
|
-
{
|
|
301
|
-
[networkKey]: {
|
|
302
|
-
aliases: [
|
|
303
|
-
"postgres"
|
|
304
|
-
]
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
],
|
|
308
|
-
healthcheck: {
|
|
309
|
-
// Ensure role/password match .env before reporting healthy.
|
|
310
|
-
test: [
|
|
311
|
-
"CMD-SHELL",
|
|
312
|
-
`pg_isready -U ${postgresSuperuser} && /usr/local/bin/decohere-ensure-bootstrap.sh`
|
|
313
|
-
],
|
|
314
|
-
interval: "2s",
|
|
315
|
-
timeout: "5s",
|
|
316
|
-
retries: 15
|
|
317
|
-
}
|
|
318
|
-
}).addEnvFile("postgres", "../.env");
|
|
319
|
-
workspace.getModel(WELL_KNOWN_FILES.GITIGNORE)?.add(POSTGRES_DATA_GITIGNORE);
|
|
320
|
-
workspace.addFile({
|
|
321
|
-
files: ()=>[
|
|
322
|
-
{
|
|
323
|
-
filePath: ".devcontainer/postgres/Dockerfile",
|
|
324
|
-
content: POSTGRES_IMAGE_DOCKERFILE
|
|
325
|
-
},
|
|
326
|
-
{
|
|
327
|
-
filePath: ".devcontainer/postgres/entrypoint.sh",
|
|
328
|
-
content: POSTGRES_ENTRYPOINT_SH
|
|
329
|
-
},
|
|
330
|
-
{
|
|
331
|
-
filePath: ".devcontainer/postgres/ensure-bootstrap-role.sh",
|
|
332
|
-
content: POSTGRES_ENSURE_BOOTSTRAP_SH
|
|
333
|
-
}
|
|
334
|
-
]
|
|
335
|
-
});
|
|
336
|
-
compose.addEnvFile("devcontainer", "../.env");
|
|
337
|
-
}
|
|
338
|
-
devContainer?.addExtension("mtxr.sqltools", "mtxr.sqltools-driver-pg");
|
|
339
|
-
workspace.getModel(WELL_KNOWN_FILES.DOCKERFILE)?.addLayer(withPostgresqlClient());
|
|
340
|
-
const pkg = workspace.getModel(WELL_KNOWN_FILES.PACKAGE_JSON)?.addDependency("@decohere/data", CREATE_VERSION).addDependency("typeorm", "^0.3.28").addDependency("@decohere/data-typeorm", CREATE_VERSION).addDependency("tsx", "^4.20.5").addScript("db:up", "docker compose -f .devcontainer/docker-compose.yml up -d postgres").addScript("db:down", "docker compose -f .devcontainer/docker-compose.yml down").addScript("db:reset", DB_RESET_SCRIPT);
|
|
341
|
-
const primaryApi = apiNames[0];
|
|
342
|
-
if (pkg != null) {
|
|
343
|
-
for (const apiName of apiNames){
|
|
344
|
-
pkg.addScript(`db:migrate:${apiName}`, migrateRunScript(apiName)).addScript(`db:migrate:generate:${apiName}`, migrateGenerateScript(apiName));
|
|
345
|
-
}
|
|
346
|
-
// Default aliases target the first API of a new workspace only — never overwrite on `webapi add`.
|
|
347
|
-
if (!options.add && primaryApi != null) {
|
|
348
|
-
pkg.addScript("db:migrate", migrateRunScript(primaryApi)).addScript("db:migrate:generate", migrateGenerateScript(primaryApi));
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
workspace.addFile({
|
|
352
|
-
files: ()=>[
|
|
353
|
-
{
|
|
354
|
-
filePath: "docs/database.md",
|
|
355
|
-
content: DATABASE_DOCS_MD
|
|
356
|
-
}
|
|
357
|
-
]
|
|
358
|
-
});
|
|
359
|
-
writeDatabaseConfig(workspace, apiNames);
|
|
360
|
-
for (const project of apiProjects){
|
|
361
|
-
wireApiDatabase(project);
|
|
362
|
-
}
|
|
363
|
-
const env = workspace.getModel(WELL_KNOWN_FILES.ENV_FILE);
|
|
364
|
-
env?.addEntry("POSTGRES_DB", ident).addEntry("POSTGRES_USER", postgresSuperuser).addEntry("POSTGRES_PASSWORD", postgresPassword).addEntry("DB_ADMIN_USER", bootstrapUser).addEntry("DB_ADMIN_PASSWORD", bootstrapPassword);
|
|
365
|
-
for (const apiName of apiNames){
|
|
366
|
-
writeApiDatabaseEnv(workspace, ident, apiName);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
function writeDatabaseConfig(workspace, apiNames) {
|
|
370
|
-
const config = workspace.getModel(WELL_KNOWN_FILES.CONFIG_YAML);
|
|
371
|
-
const devConfig = workspace.getModel(WELL_KNOWN_FILES.CONFIG_DEV_YAML);
|
|
372
|
-
const pending = apiNames.filter((apiName)=>!config?.hasDataSource(apiName));
|
|
373
|
-
if (pending.length === 0) return;
|
|
374
|
-
if (config?.hasDataSources()) {
|
|
375
|
-
for (const apiName of pending){
|
|
376
|
-
config.appendDataSource(renderBaseDataSource(apiName));
|
|
377
|
-
devConfig?.appendDataSource(renderDevDataSource(apiName));
|
|
378
|
-
}
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
config?.addSection(renderDatabaseSection(pending, renderBaseDataSource));
|
|
382
|
-
devConfig?.addSection(renderDatabaseSection(pending, renderDevDataSource));
|
|
383
|
-
}
|
|
384
|
-
function writeApiDatabaseEnv(workspace, ident, apiName) {
|
|
385
|
-
const prefix = apiEnvPrefix(apiName);
|
|
386
|
-
const sqlApi = toSqlIdentFragment(apiName);
|
|
387
|
-
workspace.getModel(WELL_KNOWN_FILES.ENV_FILE)?.addEntry(`${prefix}_DB_HOST`, "postgres").addEntry(`${prefix}_DB_PORT`, "5432").addEntry(`${prefix}_DB_NAME`, `${ident}_${sqlApi}_dev`).addEntry(`${prefix}_DB_USER`, `${ident}_${sqlApi}_app`).addEntry(`${prefix}_DB_PASSWORD`, passwordOrExisting(workspace, `${prefix}_DB_PASSWORD`)).addEntry(`${prefix}_DB_MIGRATE_USER`, `${ident}_${sqlApi}_migrate`).addEntry(`${prefix}_DB_MIGRATE_PASSWORD`, passwordOrExisting(workspace, `${prefix}_DB_MIGRATE_PASSWORD`));
|
|
388
|
-
}
|
|
389
|
-
function wireApiDatabase(project) {
|
|
390
|
-
const apiName = apiNameFromProjectDir(project.dir);
|
|
391
|
-
project.getModel(WELL_KNOWN_FILES.MAIN_TS)?.setDataSourceName(apiName).addSideEffectImport("@decohere/data-typeorm").addEntity({
|
|
392
|
-
className: "SampleEntity",
|
|
393
|
-
importPath: "./entities/sample.entity.js"
|
|
394
|
-
}).addRepository({
|
|
395
|
-
className: "SampleEntityRepository",
|
|
396
|
-
importPath: "./repositories/sample.repository.js"
|
|
397
|
-
});
|
|
398
|
-
project.getModel(WELL_KNOWN_FILES.API_PACKAGE_JSON)?.addDependency("@decohere/data", CREATE_VERSION).addDependency("@decohere/data-typeorm", CREATE_VERSION);
|
|
399
|
-
project.addFile({
|
|
400
|
-
files: ()=>[
|
|
401
|
-
{
|
|
402
|
-
filePath: "src/entities/sample.entity.ts",
|
|
403
|
-
content: SAMPLE_ENTITY_TS
|
|
404
|
-
},
|
|
405
|
-
{
|
|
406
|
-
filePath: "src/repositories/sample.repository.ts",
|
|
407
|
-
content: SAMPLE_REPOSITORY_TS
|
|
408
|
-
},
|
|
409
|
-
{
|
|
410
|
-
filePath: "src/typeorm.data-source.ts",
|
|
411
|
-
content: renderTypeOrmDataSource(apiName)
|
|
412
|
-
},
|
|
413
|
-
{
|
|
414
|
-
filePath: "src/migrations/.gitkeep",
|
|
415
|
-
content: ""
|
|
416
|
-
}
|
|
417
|
-
]
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
export const DatabaseFeature = {
|
|
421
|
-
getOptions,
|
|
422
|
-
scaffold
|
|
423
|
-
};
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { detectPackageManager, promptForPackageManager } from "@decohere/devkit/interop";
|
|
4
|
-
import { ask, closePrompt } from "../../domain/prompt.js";
|
|
5
|
-
import { WELL_KNOWN_FILES } from "../../models/const.js";
|
|
6
|
-
import { writeWorkspaceReadme } from "../workspace-readme.js";
|
|
7
|
-
import { formatScaffoldedFiles } from "./biome-format.js";
|
|
8
|
-
function readExistingEnvValue(workspace, key) {
|
|
9
|
-
try {
|
|
10
|
-
const raw = readFileSync(join(workspace.targetDir, ".env"), "utf8");
|
|
11
|
-
const line = raw.split("\n").find((l)=>l.startsWith(`${key}=`));
|
|
12
|
-
const value = line?.slice(key.length + 1).trim();
|
|
13
|
-
return value || undefined;
|
|
14
|
-
} catch {
|
|
15
|
-
return undefined;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
// State resolved during scaffold(), consumed by finalize().
|
|
19
|
-
let _pm = null;
|
|
20
|
-
let _shouldInstall = true;
|
|
21
|
-
async function getOptions(_workspace, cliOptions) {
|
|
22
|
-
if (cliOptions.installDependencies != null) {
|
|
23
|
-
_shouldInstall = cliOptions.installDependencies === "yes";
|
|
24
|
-
}
|
|
25
|
-
return {};
|
|
26
|
-
}
|
|
27
|
-
async function scaffold(workspace, options) {
|
|
28
|
-
// Local scaffold default — production deploys must set NODE_ENV explicitly.
|
|
29
|
-
const nodeEnv = readExistingEnvValue(workspace, "NODE_ENV") ?? "dev";
|
|
30
|
-
workspace.getModel(WELL_KNOWN_FILES.ENV_FILE)?.addEntry("NODE_ENV", nodeEnv);
|
|
31
|
-
// Durable day-2 CLI examples (also refreshed on `webapi add`).
|
|
32
|
-
writeWorkspaceReadme(workspace, options);
|
|
33
|
-
if (process.env.DECOHERE_BUILDER === "1") {
|
|
34
|
-
_shouldInstall = false;
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
_pm = detectPackageManager(workspace.targetDir) ?? await promptForPackageManager();
|
|
38
|
-
if (options.installDependencies != null) {
|
|
39
|
-
_shouldInstall = options.installDependencies === "yes";
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const answer = await ask("\nInstall dependencies now? [yes/no] (yes): ", "yes");
|
|
43
|
-
_shouldInstall = answer.toLowerCase() !== "no";
|
|
44
|
-
}
|
|
45
|
-
async function finalize(workspace, options) {
|
|
46
|
-
const formatPaths = options.add ? workspace.getProjects().map((project)=>project.dir) : [
|
|
47
|
-
"."
|
|
48
|
-
];
|
|
49
|
-
formatScaffoldedFiles(workspace.targetDir, formatPaths.length > 0 ? formatPaths : [
|
|
50
|
-
"."
|
|
51
|
-
]);
|
|
52
|
-
if (_shouldInstall) {
|
|
53
|
-
const pm = _pm;
|
|
54
|
-
console.log(`\nScaffolded ${workspace.name}. Installing dependencies...\n`);
|
|
55
|
-
await pm.install({
|
|
56
|
-
cwd: workspace.targetDir
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
const apiNames = workspace.getProjects().map((project)=>project.dir.replace(/^projects\//, ""));
|
|
60
|
-
console.log(`\nDone! ${_shouldInstall ? "Next" : "To get started:"} steps:\n`);
|
|
61
|
-
if (!options.add) {
|
|
62
|
-
console.log(` cd ${workspace.name}`);
|
|
63
|
-
}
|
|
64
|
-
if (!_shouldInstall && options.add) {
|
|
65
|
-
const pmName = _pm?.name ?? "pnpm";
|
|
66
|
-
console.log(` ${pmName} install`);
|
|
67
|
-
}
|
|
68
|
-
if (options.add && apiNames[0] != null) {
|
|
69
|
-
console.log(` pnpm run start:${apiNames[0]}`);
|
|
70
|
-
console.log(` pnpm run package:${apiNames[0]} && docker build projects/${apiNames[0]}`);
|
|
71
|
-
} else {
|
|
72
|
-
console.log(" code . # open in VS Code");
|
|
73
|
-
console.log(" # then: Reopen in Container (first create runs pnpm install)\n");
|
|
74
|
-
}
|
|
75
|
-
console.log(" See README.md for CLI examples.\n");
|
|
76
|
-
// Drop readline's stdin hold so the CLI / container can exit.
|
|
77
|
-
closePrompt();
|
|
78
|
-
}
|
|
79
|
-
export const FinalizeFeature = {
|
|
80
|
-
getOptions,
|
|
81
|
-
scaffold,
|
|
82
|
-
finalize
|
|
83
|
-
};
|