@decohere/scaffold 0.0.1-dev.18 → 0.0.1-dev.81
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/commonjs/src/add-api.action.js +39 -0
- package/commonjs/src/add-api.command.js +41 -0
- package/commonjs/src/add.command.js +24 -0
- package/commonjs/src/add.module.js +26 -0
- package/commonjs/src/cli.js +9 -0
- package/commonjs/src/create.command.js +24 -0
- package/commonjs/src/create.module.js +26 -0
- package/commonjs/src/domain/prompt.js +64 -0
- package/commonjs/src/index.js +38 -0
- package/commonjs/src/models/compose.model.js +150 -0
- package/commonjs/src/models/config-yaml.model.js +88 -0
- package/commonjs/src/models/const.js +32 -0
- package/commonjs/src/models/dev-container.model.js +116 -0
- package/commonjs/src/models/docker-image.model.js +259 -0
- package/commonjs/src/models/env-file.model.js +50 -0
- package/commonjs/src/models/gitignore.model.js +43 -0
- package/commonjs/src/models/launch-json.model.js +74 -0
- package/commonjs/src/models/package-json.model.js +149 -0
- package/commonjs/src/models/project.model.js +134 -0
- package/commonjs/src/models/source-file.js +6 -0
- package/commonjs/src/models/tsconfig.model.js +34 -0
- package/commonjs/src/models/vitest-config.model.js +35 -0
- package/commonjs/src/models/vscode-settings.model.js +31 -0
- package/commonjs/src/program.js +57 -0
- package/commonjs/src/resolve-target-dir.js +19 -0
- package/commonjs/src/services/scaffold.service.js +62 -0
- package/commonjs/src/version.js +57 -0
- package/commonjs/src/webapi/api-name.js +95 -0
- package/commonjs/src/webapi/cli-options.js +101 -0
- package/commonjs/src/webapi/const.js +50 -0
- package/commonjs/src/webapi/create-api.js +237 -0
- package/commonjs/src/webapi/devcontainer/codebase-rag-mcp.js +166 -0
- package/commonjs/src/webapi/devcontainer/codebase-rag-mcp.ts +182 -0
- package/commonjs/src/webapi/devcontainer/host-scripts.js +52 -0
- package/commonjs/src/webapi/devcontainer/templates/codebase-rag.env.example +3 -0
- package/commonjs/src/webapi/devcontainer/templates/initialize.sh +169 -0
- package/commonjs/src/webapi/devcontainer/templates/post-create.sh +35 -0
- package/commonjs/src/webapi/devcontainer/templates/post-start.sh +65 -0
- package/commonjs/src/webapi/devcontainer/templates/tty-prompt.sh +38 -0
- package/commonjs/src/webapi/features/auth.feature.js +441 -0
- package/commonjs/src/webapi/features/auth0-cli.js +435 -0
- package/commonjs/src/webapi/features/biome-format.js +49 -0
- package/commonjs/src/webapi/features/database.feature.js +433 -0
- package/commonjs/src/webapi/features/finalize.feature.js +95 -0
- package/commonjs/src/webapi/features/postman.feature.js +324 -0
- package/commonjs/src/webapi/models/api-package-json.model.js +60 -0
- package/commonjs/src/webapi/models/auth-config.model.js +26 -0
- package/commonjs/src/webapi/models/controller-source-file.model.js +193 -0
- package/commonjs/src/webapi/models/github-ci.model.js +193 -0
- package/commonjs/src/webapi/models/main-source-file.model.js +167 -0
- package/commonjs/src/webapi/models/production-dockerfile.model.js +157 -0
- package/commonjs/src/webapi/models/sample-files.model.js +60 -0
- package/commonjs/src/webapi/models/webapi-image.model.js +27 -0
- package/commonjs/src/webapi/postman/hello-openapi.js +175 -0
- package/commonjs/src/webapi/postman/local-view.js +184 -0
- package/commonjs/src/webapi/postman/openapi-to-postman.js +202 -0
- package/commonjs/src/webapi/workspace-readme.js +110 -0
- package/commonjs/src/webapi.action.js +30 -0
- package/commonjs/src/webapi.command.js +45 -0
- package/es6/src/add-api.action.js +29 -0
- package/es6/src/add-api.command.js +31 -0
- package/es6/src/add.command.js +14 -0
- package/es6/src/add.module.js +16 -0
- package/es6/src/cli.js +5 -0
- package/es6/src/create.command.js +14 -0
- package/es6/src/create.module.js +16 -0
- package/es6/src/domain/prompt.js +43 -0
- package/es6/src/index.js +8 -0
- package/es6/src/models/compose.model.js +144 -0
- package/es6/src/models/config-yaml.model.js +76 -0
- package/es6/src/models/const.js +22 -0
- package/es6/src/models/dev-container.model.js +106 -0
- package/es6/src/models/docker-image.model.js +217 -0
- package/es6/src/models/env-file.model.js +46 -0
- package/es6/src/models/gitignore.model.js +33 -0
- package/es6/src/models/launch-json.model.js +64 -0
- package/es6/src/models/package-json.model.js +137 -0
- package/es6/src/models/project.model.js +119 -0
- package/es6/src/models/source-file.js +3 -0
- package/es6/src/models/tsconfig.model.js +24 -0
- package/es6/src/models/vitest-config.model.js +25 -0
- package/es6/src/models/vscode-settings.model.js +21 -0
- package/es6/src/program.js +39 -0
- package/es6/src/resolve-target-dir.js +9 -0
- package/es6/src/services/scaffold.service.js +52 -0
- package/es6/src/version.js +36 -0
- package/es6/src/webapi/api-name.js +59 -0
- package/es6/src/webapi/cli-options.js +36 -0
- package/es6/src/webapi/const.js +20 -0
- package/es6/src/webapi/create-api.js +217 -0
- package/es6/src/webapi/devcontainer/codebase-rag-mcp.js +156 -0
- package/es6/src/webapi/devcontainer/codebase-rag-mcp.ts +182 -0
- package/es6/src/webapi/devcontainer/host-scripts.js +31 -0
- package/es6/src/webapi/devcontainer/templates/codebase-rag.env.example +3 -0
- package/es6/src/webapi/devcontainer/templates/initialize.sh +169 -0
- package/es6/src/webapi/devcontainer/templates/post-create.sh +35 -0
- package/es6/src/webapi/devcontainer/templates/post-start.sh +65 -0
- package/es6/src/webapi/devcontainer/templates/tty-prompt.sh +38 -0
- package/es6/src/webapi/features/auth.feature.js +390 -0
- package/es6/src/webapi/features/auth0-cli.js +375 -0
- package/es6/src/webapi/features/biome-format.js +42 -0
- package/es6/src/webapi/features/database.feature.js +423 -0
- package/es6/src/webapi/features/finalize.feature.js +85 -0
- package/es6/src/webapi/features/postman.feature.js +258 -0
- package/es6/src/webapi/models/api-package-json.model.js +55 -0
- package/es6/src/webapi/models/auth-config.model.js +22 -0
- package/es6/src/webapi/models/controller-source-file.model.js +184 -0
- package/es6/src/webapi/models/github-ci.model.js +186 -0
- package/es6/src/webapi/models/main-source-file.model.js +163 -0
- package/es6/src/webapi/models/production-dockerfile.model.js +148 -0
- package/es6/src/webapi/models/sample-files.model.js +42 -0
- package/es6/src/webapi/models/webapi-image.model.js +20 -0
- package/es6/src/webapi/postman/hello-openapi.js +168 -0
- package/es6/src/webapi/postman/local-view.js +169 -0
- package/es6/src/webapi/postman/openapi-to-postman.js +179 -0
- package/es6/src/webapi/workspace-readme.js +89 -0
- package/es6/src/webapi.action.js +20 -0
- package/es6/src/webapi.command.js +35 -0
- package/package.json +8 -8
- package/types/src/add-api.action.d.ts +13 -0
- package/types/src/add-api.command.d.ts +7 -0
- package/types/src/add.command.d.ts +6 -0
- package/types/src/add.module.d.ts +14 -0
- package/types/src/cli.d.ts +2 -0
- package/types/src/create.command.d.ts +6 -0
- package/types/src/create.module.d.ts +14 -0
- package/types/src/domain/prompt.d.ts +4 -0
- package/types/src/models/compose.model.d.ts +64 -0
- package/types/src/models/config-yaml.model.d.ts +27 -0
- package/types/src/models/const.d.ts +20 -0
- package/types/src/models/dev-container.model.d.ts +37 -0
- package/types/src/models/docker-image.model.d.ts +84 -0
- package/types/src/models/env-file.model.d.ts +16 -0
- package/types/src/models/gitignore.model.d.ts +7 -0
- package/types/src/models/launch-json.model.d.ts +7 -0
- package/types/src/models/package-json.model.d.ts +35 -0
- package/types/src/models/project.model.d.ts +60 -0
- package/{src/models/source-file.ts → types/src/models/source-file.d.ts} +4 -6
- package/types/src/models/tsconfig.model.d.ts +5 -0
- package/types/src/models/vitest-config.model.d.ts +5 -0
- package/types/src/models/vscode-settings.model.d.ts +7 -0
- package/types/src/program.d.ts +3 -0
- package/{src/resolve-target-dir.ts → types/src/resolve-target-dir.d.ts} +1 -4
- package/types/src/services/scaffold.service.d.ts +22 -0
- package/types/src/version.d.ts +6 -0
- package/types/src/webapi/api-name.d.ts +14 -0
- package/types/src/webapi/cli-options.d.ts +6 -0
- package/{src/webapi/const.ts → types/src/webapi/const.d.ts} +7 -21
- package/types/src/webapi/create-api.d.ts +15 -0
- package/types/src/webapi/devcontainer/codebase-rag-mcp.d.ts +32 -0
- package/types/src/webapi/devcontainer/codebase-rag-mcp.ts +182 -0
- package/types/src/webapi/devcontainer/host-scripts.d.ts +7 -0
- package/types/src/webapi/devcontainer/templates/codebase-rag.env.example +3 -0
- package/types/src/webapi/devcontainer/templates/initialize.sh +169 -0
- package/types/src/webapi/devcontainer/templates/post-create.sh +35 -0
- package/types/src/webapi/devcontainer/templates/post-start.sh +65 -0
- package/types/src/webapi/devcontainer/templates/tty-prompt.sh +38 -0
- package/types/src/webapi/features/auth.feature.d.ts +10 -0
- package/types/src/webapi/features/auth0-cli.d.ts +80 -0
- package/types/src/webapi/features/biome-format.d.ts +5 -0
- package/types/src/webapi/features/database.feature.d.ts +7 -0
- package/types/src/webapi/features/finalize.feature.d.ts +2 -0
- package/types/src/webapi/features/postman.feature.d.ts +16 -0
- package/types/src/webapi/models/api-package-json.model.d.ts +18 -0
- package/types/src/webapi/models/auth-config.model.d.ts +26 -0
- package/types/src/webapi/models/controller-source-file.model.d.ts +46 -0
- package/types/src/webapi/models/github-ci.model.d.ts +9 -0
- package/types/src/webapi/models/main-source-file.model.d.ts +54 -0
- package/types/src/webapi/models/production-dockerfile.model.d.ts +23 -0
- package/types/src/webapi/models/sample-files.model.d.ts +7 -0
- package/types/src/webapi/models/webapi-image.model.d.ts +12 -0
- package/types/src/webapi/postman/hello-openapi.d.ts +19 -0
- package/types/src/webapi/postman/local-view.d.ts +17 -0
- package/types/src/webapi/postman/openapi-to-postman.d.ts +62 -0
- package/types/src/webapi/workspace-readme.d.ts +18 -0
- package/types/src/webapi.action.d.ts +14 -0
- package/types/src/webapi.command.d.ts +11 -0
- package/src/add-api.action.ts +0 -25
- package/src/add-api.command.ts +0 -31
- package/src/add.command.ts +0 -20
- package/src/add.module.ts +0 -11
- package/src/cli.ts +0 -4
- package/src/create.command.ts +0 -20
- package/src/create.module.ts +0 -11
- package/src/domain/prompt.ts +0 -50
- package/src/models/compose.model.ts +0 -205
- package/src/models/config-yaml.model.ts +0 -83
- package/src/models/const.ts +0 -20
- package/src/models/dev-container.model.ts +0 -136
- package/src/models/docker-image.model.ts +0 -343
- package/src/models/env-file.model.ts +0 -45
- package/src/models/gitignore.model.ts +0 -35
- package/src/models/launch-json.model.ts +0 -60
- package/src/models/package-json.model.ts +0 -169
- package/src/models/project.model.ts +0 -141
- package/src/models/tsconfig.model.ts +0 -29
- package/src/models/vitest-config.model.ts +0 -26
- package/src/models/vscode-settings.model.ts +0 -24
- package/src/program.ts +0 -63
- package/src/services/scaffold.service.ts +0 -89
- package/src/version.ts +0 -56
- package/src/webapi/api-name.ts +0 -73
- package/src/webapi/cli-options.ts +0 -56
- package/src/webapi/create-api.ts +0 -275
- package/src/webapi/devcontainer/host-scripts.ts +0 -35
- package/src/webapi/features/auth.feature.ts +0 -454
- package/src/webapi/features/auth0-cli.ts +0 -421
- package/src/webapi/features/biome-format.ts +0 -34
- package/src/webapi/features/database.feature.ts +0 -522
- package/src/webapi/features/finalize.feature.ts +0 -112
- package/src/webapi/features/postman.feature.ts +0 -287
- package/src/webapi/models/api-package-json.model.ts +0 -60
- package/src/webapi/models/auth-config.model.ts +0 -36
- package/src/webapi/models/controller-source-file.model.ts +0 -232
- package/src/webapi/models/github-ci.model.ts +0 -188
- package/src/webapi/models/main-source-file.model.ts +0 -208
- package/src/webapi/models/production-dockerfile.model.ts +0 -162
- package/src/webapi/models/sample-files.model.ts +0 -45
- package/src/webapi/models/webapi-image.model.ts +0 -21
- package/src/webapi/postman/hello-openapi.ts +0 -92
- package/src/webapi/postman/local-view.ts +0 -195
- package/src/webapi/postman/openapi-to-postman.ts +0 -208
- package/src/webapi/workspace-readme.ts +0 -192
- package/src/webapi.action.ts +0 -22
- package/src/webapi.command.ts +0 -41
- package/tests/auth-postman.wiring.spec.ts +0 -263
- package/tests/auth0-cli.spec.ts +0 -232
- package/tests/biome-format.spec.ts +0 -49
- package/tests/cli.integration.spec.ts +0 -658
- package/tests/compose.model.spec.ts +0 -51
- package/tests/container-image.model.spec.ts +0 -162
- package/tests/dev-container.model.spec.ts +0 -29
- package/tests/devcontainer-base-image.spec.ts +0 -32
- package/tests/hello-di-inject.scaffold.spec.ts +0 -18
- package/tests/hello-route.contract.spec.ts +0 -94
- package/tests/package-json.order.spec.ts +0 -59
- package/tests/postman-from-openapi.spec.ts +0 -197
- package/tests/seed-devcontainer-templates.ts +0 -40
- package/tests/webapi/devcontainer/codebase-rag-mcp.spec.ts +0 -96
- /package/{src/index.ts → types/src/index.d.ts} +0 -0
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
import { WELL_KNOWN_FILES } from "../../models/const.js";
|
|
2
|
-
import type { KeyedModel, SourceFile } from "../../models/source-file.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Minimal GitHub Actions for scaffolded apps: lint ∥ test, build gate on default branch,
|
|
6
|
-
* npm publish after CI (skips private packages). No default forge docker --push.
|
|
7
|
-
*/
|
|
8
|
-
export class GithubCiWorkflows implements KeyedModel {
|
|
9
|
-
public readonly key = WELL_KNOWN_FILES.GITHUB_CI;
|
|
10
|
-
|
|
11
|
-
public files(): SourceFile[] {
|
|
12
|
-
return [
|
|
13
|
-
{
|
|
14
|
-
filePath: ".github/actions/setup-node-pnpm/action.yml",
|
|
15
|
-
content: `name: Setup Node + pnpm
|
|
16
|
-
description: Install pnpm and Node, configure npm registry auth, install deps
|
|
17
|
-
inputs:
|
|
18
|
-
node-version:
|
|
19
|
-
description: Node.js version
|
|
20
|
-
required: false
|
|
21
|
-
default: "24"
|
|
22
|
-
pnpm-version:
|
|
23
|
-
description: pnpm version
|
|
24
|
-
required: false
|
|
25
|
-
default: "10"
|
|
26
|
-
install:
|
|
27
|
-
description: Run pnpm install --frozen-lockfile
|
|
28
|
-
required: false
|
|
29
|
-
default: "true"
|
|
30
|
-
|
|
31
|
-
runs:
|
|
32
|
-
using: composite
|
|
33
|
-
steps:
|
|
34
|
-
- uses: pnpm/action-setup@v4
|
|
35
|
-
with:
|
|
36
|
-
version: \${{ inputs.pnpm-version }}
|
|
37
|
-
|
|
38
|
-
- uses: actions/setup-node@v4
|
|
39
|
-
with:
|
|
40
|
-
node-version: \${{ inputs.node-version }}
|
|
41
|
-
cache: pnpm
|
|
42
|
-
cache-dependency-path: pnpm-lock.yaml
|
|
43
|
-
|
|
44
|
-
- name: Configure npm auth
|
|
45
|
-
shell: bash
|
|
46
|
-
run: |
|
|
47
|
-
if [[ -n "\${NODE_AUTH_TOKEN:-}" ]]; then
|
|
48
|
-
echo "//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}" >> .npmrc
|
|
49
|
-
fi
|
|
50
|
-
|
|
51
|
-
- name: Install dependencies
|
|
52
|
-
if: inputs.install == 'true'
|
|
53
|
-
shell: bash
|
|
54
|
-
run: |
|
|
55
|
-
if [[ -f pnpm-lock.yaml ]]; then
|
|
56
|
-
pnpm install --frozen-lockfile
|
|
57
|
-
else
|
|
58
|
-
pnpm install
|
|
59
|
-
fi
|
|
60
|
-
`,
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
filePath: ".github/workflows/ci.yml",
|
|
64
|
-
content: `name: CI
|
|
65
|
-
on:
|
|
66
|
-
push:
|
|
67
|
-
branches:
|
|
68
|
-
- "*"
|
|
69
|
-
pull_request:
|
|
70
|
-
workflow_dispatch:
|
|
71
|
-
concurrency:
|
|
72
|
-
group: ci-\${{ github.ref }}
|
|
73
|
-
cancel-in-progress: true
|
|
74
|
-
env:
|
|
75
|
-
NODE_AUTH_TOKEN: \${{ secrets.NODE_AUTH_TOKEN }}
|
|
76
|
-
SCRIBE_LOG_LEVEL: info
|
|
77
|
-
|
|
78
|
-
jobs:
|
|
79
|
-
lint:
|
|
80
|
-
runs-on: ubuntu-latest
|
|
81
|
-
timeout-minutes: 15
|
|
82
|
-
permissions:
|
|
83
|
-
contents: read
|
|
84
|
-
steps:
|
|
85
|
-
- uses: actions/checkout@v4
|
|
86
|
-
with:
|
|
87
|
-
fetch-depth: 1
|
|
88
|
-
|
|
89
|
-
- uses: ./.github/actions/setup-node-pnpm
|
|
90
|
-
|
|
91
|
-
- name: Lint and typecheck
|
|
92
|
-
run: |
|
|
93
|
-
set +e
|
|
94
|
-
pnpm run lint:check &
|
|
95
|
-
lint_pid=$!
|
|
96
|
-
pnpm run typecheck &
|
|
97
|
-
tsc_pid=$!
|
|
98
|
-
wait "$lint_pid"
|
|
99
|
-
lint_code=$?
|
|
100
|
-
wait "$tsc_pid"
|
|
101
|
-
tsc_code=$?
|
|
102
|
-
set -e
|
|
103
|
-
if [[ "$lint_code" -ne 0 || "$tsc_code" -ne 0 ]]; then
|
|
104
|
-
exit 1
|
|
105
|
-
fi
|
|
106
|
-
|
|
107
|
-
test:
|
|
108
|
-
runs-on: ubuntu-latest
|
|
109
|
-
timeout-minutes: 20
|
|
110
|
-
permissions:
|
|
111
|
-
contents: read
|
|
112
|
-
steps:
|
|
113
|
-
- uses: actions/checkout@v4
|
|
114
|
-
with:
|
|
115
|
-
fetch-depth: 1
|
|
116
|
-
|
|
117
|
-
- uses: ./.github/actions/setup-node-pnpm
|
|
118
|
-
|
|
119
|
-
- name: Test
|
|
120
|
-
run: pnpm test
|
|
121
|
-
|
|
122
|
-
- name: Build (default branch)
|
|
123
|
-
if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
|
|
124
|
-
run: pnpm run build
|
|
125
|
-
|
|
126
|
-
ci:
|
|
127
|
-
needs: [lint, test]
|
|
128
|
-
if: always()
|
|
129
|
-
runs-on: ubuntu-latest
|
|
130
|
-
timeout-minutes: 5
|
|
131
|
-
steps:
|
|
132
|
-
- name: Aggregate
|
|
133
|
-
run: |
|
|
134
|
-
if [[ "\${{ needs.lint.result }}" != "success" || "\${{ needs.test.result }}" != "success" ]]; then
|
|
135
|
-
echo "CI failed (lint=\${{ needs.lint.result }} test=\${{ needs.test.result }})"
|
|
136
|
-
exit 1
|
|
137
|
-
fi
|
|
138
|
-
echo "CI passed"
|
|
139
|
-
`,
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
filePath: ".github/workflows/publish.yml",
|
|
143
|
-
content: `name: Publish
|
|
144
|
-
|
|
145
|
-
on:
|
|
146
|
-
workflow_run:
|
|
147
|
-
workflows: ["CI"]
|
|
148
|
-
types: [completed]
|
|
149
|
-
|
|
150
|
-
concurrency:
|
|
151
|
-
group: publish-\${{ github.event.workflow_run.head_branch }}
|
|
152
|
-
cancel-in-progress: false
|
|
153
|
-
|
|
154
|
-
env:
|
|
155
|
-
NODE_AUTH_TOKEN: \${{ secrets.NODE_AUTH_TOKEN }}
|
|
156
|
-
SCRIBE_LOG_LEVEL: info
|
|
157
|
-
|
|
158
|
-
jobs:
|
|
159
|
-
publish:
|
|
160
|
-
if: >
|
|
161
|
-
github.event.workflow_run.conclusion == 'success' &&
|
|
162
|
-
github.event.workflow_run.head_branch == github.event.repository.default_branch
|
|
163
|
-
runs-on: ubuntu-latest
|
|
164
|
-
timeout-minutes: 30
|
|
165
|
-
permissions:
|
|
166
|
-
contents: read
|
|
167
|
-
id-token: write
|
|
168
|
-
steps:
|
|
169
|
-
- uses: actions/checkout@v4
|
|
170
|
-
with:
|
|
171
|
-
ref: \${{ github.event.workflow_run.head_sha }}
|
|
172
|
-
fetch-depth: 1
|
|
173
|
-
|
|
174
|
-
- uses: ./.github/actions/setup-node-pnpm
|
|
175
|
-
|
|
176
|
-
- name: Build release
|
|
177
|
-
run: pnpm run build
|
|
178
|
-
|
|
179
|
-
- name: Publish packages
|
|
180
|
-
run: pnpm exec forge publish --configuration release
|
|
181
|
-
# Skips packages with "private": true (e.g. the webapi app).
|
|
182
|
-
# Optional registry images: add \`pnpm exec forge docker --configuration release --push\`
|
|
183
|
-
# after configuring GHCR/OIDC — not enabled by default (PaaS builds the Dockerfile).
|
|
184
|
-
`,
|
|
185
|
-
},
|
|
186
|
-
];
|
|
187
|
-
}
|
|
188
|
-
}
|
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
import { WELL_KNOWN_FILES } from "../../models/const.js";
|
|
2
|
-
import type { KeyedModel, SourceFile } from "../../models/source-file.js";
|
|
3
|
-
|
|
4
|
-
export type TypeOrmImport = { className: string; importPath: string };
|
|
5
|
-
export type ServiceImport = TypeOrmImport & { scope?: "singleton" | "request" };
|
|
6
|
-
|
|
7
|
-
/** Tracks named imports from a single module path, split into values and types. */
|
|
8
|
-
class ImportGroup {
|
|
9
|
-
private readonly _values: string[] = [];
|
|
10
|
-
private readonly _types: string[] = [];
|
|
11
|
-
|
|
12
|
-
public add(name: string, typeOnly = false): this {
|
|
13
|
-
if (typeOnly) {
|
|
14
|
-
// Value import already present — value wins, skip
|
|
15
|
-
if (!this._values.includes(name) && !this._types.includes(name)) {
|
|
16
|
-
this._types.push(name);
|
|
17
|
-
}
|
|
18
|
-
} else {
|
|
19
|
-
// Promote: remove from types if it was previously type-only
|
|
20
|
-
const typeIdx = this._types.indexOf(name);
|
|
21
|
-
if (typeIdx !== -1) this._types.splice(typeIdx, 1);
|
|
22
|
-
if (!this._values.includes(name)) this._values.push(name);
|
|
23
|
-
}
|
|
24
|
-
return this;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
public render(from: string): string {
|
|
28
|
-
const values = [...this._values].sort();
|
|
29
|
-
const types = [...this._types].sort().map((n) => `type ${n}`);
|
|
30
|
-
return `import { ${[...values, ...types].join(", ")} } from "${from}";`;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Model for `projects/api/src/main.ts`.
|
|
36
|
-
*
|
|
37
|
-
* Starts with the minimal HTTP + YAML config setup. Features mutate the model
|
|
38
|
-
* by calling addSideEffectImport / addImport / addBuilderCall / addEntity /
|
|
39
|
-
* addRepository. files() renders whatever has been accumulated.
|
|
40
|
-
*/
|
|
41
|
-
export class MainSourceFile implements KeyedModel {
|
|
42
|
-
public readonly key = WELL_KNOWN_FILES.MAIN_TS;
|
|
43
|
-
|
|
44
|
-
private readonly _sideEffectImports: string[] = [];
|
|
45
|
-
private readonly _extraImports = new Map<string, ImportGroup>();
|
|
46
|
-
private readonly _builderCalls: string[] = [];
|
|
47
|
-
private readonly _entities: TypeOrmImport[] = [];
|
|
48
|
-
private readonly _repositories: TypeOrmImport[] = [];
|
|
49
|
-
private readonly _services: ServiceImport[] = [];
|
|
50
|
-
private _dataSourceName: string | undefined;
|
|
51
|
-
private _typeOrmSeed: string | undefined;
|
|
52
|
-
|
|
53
|
-
public constructor(private readonly appName: string) {}
|
|
54
|
-
|
|
55
|
-
/** Adds a bare side-effect import, e.g. `@decohere/cortex/auth`. */
|
|
56
|
-
public addSideEffectImport(path: string): this {
|
|
57
|
-
if (!this._sideEffectImports.includes(path)) this._sideEffectImports.push(path);
|
|
58
|
-
return this;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Adds a named import, e.g. `import { Foo } from "bar"`. */
|
|
62
|
-
public addImport(name: string, from: string, typeOnly = false): this {
|
|
63
|
-
const group = this._extraImports.get(from) ?? new ImportGroup();
|
|
64
|
-
group.add(name, typeOnly);
|
|
65
|
-
this._extraImports.set(from, group);
|
|
66
|
-
return this;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Adds a builder call inserted after `.withConfiguration()` and before
|
|
71
|
-
* `.withServices()`. Pass the call without leading dot, e.g. `.withAuth()`.
|
|
72
|
-
*/
|
|
73
|
-
public addBuilderCall(call: string): this {
|
|
74
|
-
this._builderCalls.push(call);
|
|
75
|
-
return this;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** Adds a TypeORM entity — import and entry in `withTypeOrm({ entities })`. */
|
|
79
|
-
public addEntity(entity: TypeOrmImport): this {
|
|
80
|
-
this._entities.push(entity);
|
|
81
|
-
return this;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Adds a TypeORM repository — import and entry in `withTypeOrm({ repositories })`. */
|
|
85
|
-
public addRepository(repo: TypeOrmImport): this {
|
|
86
|
-
this._repositories.push(repo);
|
|
87
|
-
return this;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Adds a service registered in `.withServices()`. */
|
|
91
|
-
public addService(service: ServiceImport): this {
|
|
92
|
-
this._services.push(service);
|
|
93
|
-
return this;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Datasource name passed to `withTypeOrm({ name })`. Must match
|
|
98
|
-
* `database.dataSources[].name` in config.yaml.
|
|
99
|
-
*/
|
|
100
|
-
public setDataSourceName(name: string): this {
|
|
101
|
-
this._dataSourceName = name;
|
|
102
|
-
return this;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Inline seed callback body for `withTypeOrm({ seed })`.
|
|
107
|
-
* Receives `dataSource` in scope. Example: `await dataSource.getRepository(User).save(...)`.
|
|
108
|
-
*/
|
|
109
|
-
public setTypeOrmSeed(seedBody: string): this {
|
|
110
|
-
this._typeOrmSeed = seedBody;
|
|
111
|
-
return this;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
public files(): SourceFile[] {
|
|
115
|
-
const imports: string[] = [
|
|
116
|
-
'import "@decohere/scribe-pino";',
|
|
117
|
-
'import "@decohere/cortex/http";',
|
|
118
|
-
];
|
|
119
|
-
|
|
120
|
-
for (const path of this._sideEffectImports) {
|
|
121
|
-
imports.push(`import "${path}";`);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
imports.push(
|
|
125
|
-
'import "@decohere/protocol";',
|
|
126
|
-
'import { YamlDataSource } from "@decohere/protocol";',
|
|
127
|
-
'import { Builder } from "@decohere/cortex";',
|
|
128
|
-
'import { FastifyProvider } from "@decohere/cortex/providers";',
|
|
129
|
-
'import { scribe } from "@decohere/scribe";',
|
|
130
|
-
'import { SampleService } from "./services/sample.service.js";',
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
for (const e of this._entities) {
|
|
134
|
-
imports.push(`import { ${e.className} } from "${e.importPath}";`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
for (const r of this._repositories) {
|
|
138
|
-
imports.push(`import { ${r.className} } from "${r.importPath}";`);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
for (const s of this._services) {
|
|
142
|
-
imports.push(`import { ${s.className} } from "${s.importPath}";`);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
for (const [from, group] of this._extraImports) {
|
|
146
|
-
imports.push(group.render(from));
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
imports.push('import { HelloController } from "./controllers/hello.controller.js";');
|
|
150
|
-
|
|
151
|
-
const serviceRegs = [
|
|
152
|
-
"cb.registerSingleton(SampleService);",
|
|
153
|
-
...this._services.map((s) =>
|
|
154
|
-
s.scope === "request"
|
|
155
|
-
? `cb.registerRequestScoped(${s.className});`
|
|
156
|
-
: `cb.registerSingleton(${s.className});`,
|
|
157
|
-
),
|
|
158
|
-
].join(" ");
|
|
159
|
-
|
|
160
|
-
const builderCalls: string[] = [
|
|
161
|
-
`\t.withHttp(FastifyProvider)`,
|
|
162
|
-
`\t.withConfiguration(YamlDataSource)`,
|
|
163
|
-
`\t.withHealth()`,
|
|
164
|
-
`\t.withOpenApi({ info: { title: "${this.appName}", version: "1.0.0" } })`,
|
|
165
|
-
...this._builderCalls.map((c) => `\t${c}`),
|
|
166
|
-
`\t.withServices((cb) => { ${serviceRegs} })`,
|
|
167
|
-
];
|
|
168
|
-
|
|
169
|
-
if (this._entities.length > 0) {
|
|
170
|
-
const entities = this._entities.map((e) => e.className).join(", ");
|
|
171
|
-
const repos = this._repositories.map((r) => r.className).join(", ");
|
|
172
|
-
const named = this._dataSourceName != null ? `name: "${this._dataSourceName}", ` : "";
|
|
173
|
-
if (this._typeOrmSeed) {
|
|
174
|
-
builderCalls.push(
|
|
175
|
-
`\t.withTypeOrm({ ${named}entities: [${entities}], repositories: [${repos}], seed: async (dataSource) => { ${this._typeOrmSeed} } })`,
|
|
176
|
-
);
|
|
177
|
-
} else {
|
|
178
|
-
builderCalls.push(
|
|
179
|
-
`\t.withTypeOrm({ ${named}entities: [${entities}], repositories: [${repos}] })`,
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
builderCalls.push(`\t.withControllers(HelloController)`);
|
|
185
|
-
|
|
186
|
-
const content = [
|
|
187
|
-
...imports,
|
|
188
|
-
"",
|
|
189
|
-
`scribe.withPinoPretty().and.withPinoRoll({ file: "logs/${this.appName}.log" });`,
|
|
190
|
-
"",
|
|
191
|
-
`const app = await Builder.create({ applicationName: "${this.appName}" })`,
|
|
192
|
-
...builderCalls,
|
|
193
|
-
`\t.build();`,
|
|
194
|
-
"",
|
|
195
|
-
"await app.start({ port: Number(process.env.PORT ?? 3000) });",
|
|
196
|
-
"",
|
|
197
|
-
"export default app;",
|
|
198
|
-
"",
|
|
199
|
-
].join("\n");
|
|
200
|
-
|
|
201
|
-
return [
|
|
202
|
-
{
|
|
203
|
-
filePath: "src/main.ts",
|
|
204
|
-
content,
|
|
205
|
-
},
|
|
206
|
-
];
|
|
207
|
-
}
|
|
208
|
-
}
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { WELL_KNOWN_FILES } from "../../models/const.js";
|
|
2
|
-
import type { KeyedModel, SourceFile } from "../../models/source-file.js";
|
|
3
|
-
import { DEFAULT_WEBAPI_NAME, webapiProjectDir, webapiReleaseEntry } from "../const.js";
|
|
4
|
-
|
|
5
|
-
export type ProductionDockerfileOptions = {
|
|
6
|
-
/** When true, only write `Dockerfile.<name>` (existing workspace — do not replace root Dockerfile). */
|
|
7
|
-
namedOnly?: boolean;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Production Dockerfiles for PaaS (Railway/Fly) and local `docker build`.
|
|
12
|
-
*
|
|
13
|
-
* Context is always the **repo root** (`docker build -f Dockerfile.billing .`).
|
|
14
|
-
* Root `Dockerfile` is the first API (`pnpm start`). Extra or custom-named APIs
|
|
15
|
-
* also get `Dockerfile.<name>` so each service can be deployed on its own.
|
|
16
|
-
*
|
|
17
|
-
* Runtime `node_modules` come from a **prod-deps** stage (`pnpm install --prod`),
|
|
18
|
-
* not the full build install (Forge, TypeScript, Biome, Vitest).
|
|
19
|
-
*/
|
|
20
|
-
export class WebapiProductionDockerfile implements KeyedModel {
|
|
21
|
-
public readonly key = WELL_KNOWN_FILES.PRODUCTION_DOCKERFILE;
|
|
22
|
-
|
|
23
|
-
public constructor(
|
|
24
|
-
private readonly apiNames: readonly string[] = [DEFAULT_WEBAPI_NAME],
|
|
25
|
-
private readonly options: ProductionDockerfileOptions = {},
|
|
26
|
-
) {}
|
|
27
|
-
|
|
28
|
-
public files(): SourceFile[] {
|
|
29
|
-
const names = this.apiNames.length > 0 ? this.apiNames : [DEFAULT_WEBAPI_NAME];
|
|
30
|
-
const first = names[0] ?? DEFAULT_WEBAPI_NAME;
|
|
31
|
-
const files: SourceFile[] = [];
|
|
32
|
-
|
|
33
|
-
if (!this.options.namedOnly) {
|
|
34
|
-
files.push({
|
|
35
|
-
filePath: ".dockerignore",
|
|
36
|
-
content: dockerignoreContent(),
|
|
37
|
-
});
|
|
38
|
-
files.push({
|
|
39
|
-
filePath: "Dockerfile",
|
|
40
|
-
content: renderProductionDockerfile(first, "start"),
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const emitNamed =
|
|
45
|
-
this.options.namedOnly || names.length > 1 || first !== DEFAULT_WEBAPI_NAME;
|
|
46
|
-
if (emitNamed) {
|
|
47
|
-
for (const name of names) {
|
|
48
|
-
files.push({
|
|
49
|
-
filePath: `Dockerfile.${name}`,
|
|
50
|
-
content: renderProductionDockerfile(name, `start:${name}`),
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return files;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function renderProductionDockerfile(apiName: string, startScript: string): string {
|
|
60
|
-
const projectDir = webapiProjectDir(apiName);
|
|
61
|
-
const startCmd =
|
|
62
|
-
startScript === "start"
|
|
63
|
-
? 'CMD ["pnpm", "start"]'
|
|
64
|
-
: `CMD ["pnpm", "run", "${startScript}"]`;
|
|
65
|
-
const startEntry = webapiReleaseEntry(apiName);
|
|
66
|
-
const dockerfileFlag = startScript === "start" ? "" : `.${apiName}`;
|
|
67
|
-
|
|
68
|
-
return `# syntax=docker/dockerfile:1
|
|
69
|
-
# Self-contained production image: install + forge build happen inside docker build.
|
|
70
|
-
# Hosts (Railway/Fly/etc.) clone git and run \`docker build\` — they do not receive CI dist/.
|
|
71
|
-
# Build from the repo root: docker build -f Dockerfile${dockerfileFlag} .
|
|
72
|
-
#
|
|
73
|
-
# Runtime node_modules are a production-only install (prod-deps). The build stage's
|
|
74
|
-
# full install (Forge, TypeScript, Biome, Vitest) is not copied into the run image.
|
|
75
|
-
#
|
|
76
|
-
# Private @decohere/* packages: pass a BuildKit secret named npm_token (npm auth token), e.g.
|
|
77
|
-
# docker build --secret id=npm_token,env=NODE_AUTH_TOKEN .
|
|
78
|
-
# Or set NODE_AUTH_TOKEN as a build-arg on hosts that only support build-args.
|
|
79
|
-
|
|
80
|
-
FROM node:24-bookworm AS build
|
|
81
|
-
WORKDIR /app
|
|
82
|
-
RUN corepack enable && corepack prepare pnpm@10 --activate
|
|
83
|
-
|
|
84
|
-
COPY package.json ./
|
|
85
|
-
COPY pnpm-lock.yaml* ./
|
|
86
|
-
COPY tsconfig.json biome.json ./
|
|
87
|
-
COPY config ./config
|
|
88
|
-
COPY ${projectDir} ./${projectDir}
|
|
89
|
-
|
|
90
|
-
${renderPnpmInstall("all")}
|
|
91
|
-
|
|
92
|
-
RUN pnpm run build
|
|
93
|
-
|
|
94
|
-
FROM node:24-bookworm AS prod-deps
|
|
95
|
-
WORKDIR /app
|
|
96
|
-
ENV NODE_ENV=production
|
|
97
|
-
RUN corepack enable && corepack prepare pnpm@10 --activate
|
|
98
|
-
|
|
99
|
-
COPY package.json ./
|
|
100
|
-
COPY pnpm-lock.yaml* ./
|
|
101
|
-
COPY ${projectDir}/package.json ./${projectDir}/package.json
|
|
102
|
-
|
|
103
|
-
${renderPnpmInstall("prod")}
|
|
104
|
-
|
|
105
|
-
FROM node:24-bookworm-slim AS run
|
|
106
|
-
WORKDIR /app
|
|
107
|
-
ENV NODE_ENV=production
|
|
108
|
-
RUN corepack enable && corepack prepare pnpm@10 --activate
|
|
109
|
-
|
|
110
|
-
COPY --from=prod-deps /app/package.json ./
|
|
111
|
-
COPY --from=prod-deps /app/pnpm-lock.yaml* ./
|
|
112
|
-
COPY --from=build /app/config ./config
|
|
113
|
-
COPY --from=build /app/dist ./dist
|
|
114
|
-
COPY --from=prod-deps /app/node_modules ./node_modules
|
|
115
|
-
COPY --from=prod-deps /app/${projectDir}/package.json ./${projectDir}/package.json
|
|
116
|
-
|
|
117
|
-
EXPOSE 3000
|
|
118
|
-
${startCmd}
|
|
119
|
-
# Entry resolved by package.json "${startScript}" → node ${startEntry}
|
|
120
|
-
`;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/** npm auth + pnpm install. Token is required again in prod-deps (separate stage, no store). */
|
|
124
|
-
function renderPnpmInstall(mode: "all" | "prod"): string {
|
|
125
|
-
const flags = mode === "prod" ? " --prod" : "";
|
|
126
|
-
return `ARG NODE_AUTH_TOKEN=
|
|
127
|
-
RUN --mount=type=secret,id=npm_token,required=false \\
|
|
128
|
-
if [ -f /run/secrets/npm_token ]; then \\
|
|
129
|
-
TOKEN="$(cat /run/secrets/npm_token)"; \\
|
|
130
|
-
else \\
|
|
131
|
-
TOKEN="\${NODE_AUTH_TOKEN}"; \\
|
|
132
|
-
fi; \\
|
|
133
|
-
if [ -n "$TOKEN" ]; then \\
|
|
134
|
-
echo "//registry.npmjs.org/:_authToken=\${TOKEN}" > .npmrc; \\
|
|
135
|
-
fi; \\
|
|
136
|
-
if [ -f pnpm-lock.yaml ]; then \\
|
|
137
|
-
pnpm install --frozen-lockfile${flags}; \\
|
|
138
|
-
else \\
|
|
139
|
-
pnpm install${flags}; \\
|
|
140
|
-
fi; \\
|
|
141
|
-
rm -f .npmrc`;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function dockerignoreContent(): string {
|
|
145
|
-
return [
|
|
146
|
-
"node_modules",
|
|
147
|
-
"dist",
|
|
148
|
-
".git",
|
|
149
|
-
".devcontainer",
|
|
150
|
-
".vscode",
|
|
151
|
-
".github",
|
|
152
|
-
"postman",
|
|
153
|
-
".postman",
|
|
154
|
-
".env",
|
|
155
|
-
".env.*",
|
|
156
|
-
"!.env.example",
|
|
157
|
-
"coverage",
|
|
158
|
-
"**/*.spec.ts",
|
|
159
|
-
"**/*.test.ts",
|
|
160
|
-
"",
|
|
161
|
-
].join("\n");
|
|
162
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { SourceFile, SourceFileProvider } from "../../models/source-file.js";
|
|
2
|
-
|
|
3
|
-
const SAMPLE_CONFIG_CONTENT = `export class SampleConfig {\n\tpublic greeting: string = "";\n}\n`;
|
|
4
|
-
|
|
5
|
-
const SAMPLE_SERVICE_CONTENT = [
|
|
6
|
-
`import { inject, injectable } from "@decohere/crucible/decorators";`,
|
|
7
|
-
`import { ConfigurationServiceToken } from "@decohere/nexus/configuration";`,
|
|
8
|
-
`import type { IConfigurationService } from "@decohere/nexus/configuration";`,
|
|
9
|
-
`import { SampleConfig } from "../sample.config.js";`,
|
|
10
|
-
``,
|
|
11
|
-
`@injectable()`,
|
|
12
|
-
`export class SampleService {`,
|
|
13
|
-
`\tpublic constructor(`,
|
|
14
|
-
`\t\t@inject(ConfigurationServiceToken) private readonly config: IConfigurationService,`,
|
|
15
|
-
`\t) {}`,
|
|
16
|
-
``,
|
|
17
|
-
`\tpublic async getGreeting(): Promise<string> {`,
|
|
18
|
-
`\t\tconst config = await this.config.get(SampleConfig);`,
|
|
19
|
-
`\t\treturn config.greeting;`,
|
|
20
|
-
`\t}`,
|
|
21
|
-
`}`,
|
|
22
|
-
``,
|
|
23
|
-
].join("\n");
|
|
24
|
-
|
|
25
|
-
export class SampleConfigModel implements SourceFileProvider {
|
|
26
|
-
public files(): SourceFile[] {
|
|
27
|
-
return [
|
|
28
|
-
{
|
|
29
|
-
filePath: "src/sample.config.ts",
|
|
30
|
-
content: SAMPLE_CONFIG_CONTENT,
|
|
31
|
-
},
|
|
32
|
-
];
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export class SampleServiceModel implements SourceFileProvider {
|
|
37
|
-
public files(): SourceFile[] {
|
|
38
|
-
return [
|
|
39
|
-
{
|
|
40
|
-
filePath: "src/services/sample.service.ts",
|
|
41
|
-
content: SAMPLE_SERVICE_CONTENT,
|
|
42
|
-
},
|
|
43
|
-
];
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { DockerfileModel } from "../../models/docker-image.model.js";
|
|
2
|
-
import { CREATE_VERSION, DEVCONTAINER_IMAGE_REPOSITORY } from "../../version.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Scaffolded app `.devcontainer/Dockerfile` — thin `FROM` the published base image.
|
|
6
|
-
* Optional feature layers (e.g. postgresql-client) append via `addLayer`.
|
|
7
|
-
*/
|
|
8
|
-
export class WebapiDevContainer extends DockerfileModel {
|
|
9
|
-
public constructor(baseImage = `${DEVCONTAINER_IMAGE_REPOSITORY}:${CREATE_VERSION}`) {
|
|
10
|
-
super(baseImage);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
public files() {
|
|
14
|
-
return [
|
|
15
|
-
{
|
|
16
|
-
filePath: ".devcontainer/Dockerfile",
|
|
17
|
-
content: this.build(),
|
|
18
|
-
},
|
|
19
|
-
];
|
|
20
|
-
}
|
|
21
|
-
}
|