@supatype/cli 0.1.1 → 0.1.3
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +126 -99
- package/.turbo/turbo-typecheck.log +2 -2
- package/dist/binary-cache.d.ts +2 -0
- package/dist/binary-cache.d.ts.map +1 -1
- package/dist/binary-cache.js +36 -0
- package/dist/binary-cache.js.map +1 -1
- package/dist/binary-download-lock.d.ts +12 -0
- package/dist/binary-download-lock.d.ts.map +1 -0
- package/dist/binary-download-lock.js +106 -0
- package/dist/binary-download-lock.js.map +1 -0
- package/dist/cli-package-version.d.ts +3 -0
- package/dist/cli-package-version.d.ts.map +1 -0
- package/dist/cli-package-version.js +15 -0
- package/dist/cli-package-version.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +5 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.js +25 -19
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/admin.d.ts +12 -0
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +116 -26
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/cache.d.ts +1 -1
- package/dist/commands/cache.d.ts.map +1 -1
- package/dist/commands/cache.js +98 -5
- package/dist/commands/cache.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +26 -22
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.d.ts +3 -4
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +296 -121
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/link-helpers.d.ts.map +1 -1
- package/dist/commands/link-helpers.js +26 -25
- package/dist/commands/link-helpers.js.map +1 -1
- package/dist/compose-local-server-image.d.ts +13 -0
- package/dist/compose-local-server-image.d.ts.map +1 -0
- package/dist/compose-local-server-image.js +57 -0
- package/dist/compose-local-server-image.js.map +1 -0
- package/dist/compose-rename.d.ts.map +1 -1
- package/dist/compose-rename.js +5 -8
- package/dist/compose-rename.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -1
- package/dist/config.js.map +1 -1
- package/dist/dev-compose.d.ts.map +1 -1
- package/dist/dev-compose.js +113 -56
- package/dist/dev-compose.js.map +1 -1
- package/dist/dev-log-bus.d.ts +5 -0
- package/dist/dev-log-bus.d.ts.map +1 -1
- package/dist/dev-log-bus.js +16 -0
- package/dist/dev-log-bus.js.map +1 -1
- package/dist/dev-log-filter.d.ts +4 -0
- package/dist/dev-log-filter.d.ts.map +1 -1
- package/dist/dev-log-filter.js +52 -0
- package/dist/dev-log-filter.js.map +1 -1
- package/dist/dev-ports.d.ts.map +1 -1
- package/dist/dev-ports.js +19 -12
- package/dist/dev-ports.js.map +1 -1
- package/dist/dev-ready-panel.d.ts +21 -0
- package/dist/dev-ready-panel.d.ts.map +1 -0
- package/dist/dev-ready-panel.js +67 -0
- package/dist/dev-ready-panel.js.map +1 -0
- package/dist/dev-session-lock.d.ts.map +1 -1
- package/dist/dev-session-lock.js +5 -8
- package/dist/dev-session-lock.js.map +1 -1
- package/dist/dev-session.d.ts +18 -5
- package/dist/dev-session.d.ts.map +1 -1
- package/dist/dev-session.js +91 -18
- package/dist/dev-session.js.map +1 -1
- package/dist/dev-shutdown.d.ts +2 -0
- package/dist/dev-shutdown.d.ts.map +1 -1
- package/dist/dev-shutdown.js +29 -15
- package/dist/dev-shutdown.js.map +1 -1
- package/dist/dev-task-colors.d.ts +3 -1
- package/dist/dev-task-colors.d.ts.map +1 -1
- package/dist/dev-task-colors.js +23 -3
- package/dist/dev-task-colors.js.map +1 -1
- package/dist/docker-runtime.js +1 -1
- package/dist/docker-runtime.js.map +1 -1
- package/dist/engine-push-output.d.ts +1 -0
- package/dist/engine-push-output.d.ts.map +1 -1
- package/dist/engine-push-output.js +4 -1
- package/dist/engine-push-output.js.map +1 -1
- package/dist/ensure-binary.d.ts.map +1 -1
- package/dist/ensure-binary.js +14 -2
- package/dist/ensure-binary.js.map +1 -1
- package/dist/ensure-component-binaries.d.ts +17 -0
- package/dist/ensure-component-binaries.d.ts.map +1 -0
- package/dist/ensure-component-binaries.js +45 -0
- package/dist/ensure-component-binaries.js.map +1 -0
- package/dist/ensure-dev-api-config.d.ts +3 -0
- package/dist/ensure-dev-api-config.d.ts.map +1 -0
- package/dist/ensure-dev-api-config.js +17 -0
- package/dist/ensure-dev-api-config.js.map +1 -0
- package/dist/init-dependency-versions.d.ts +13 -0
- package/dist/init-dependency-versions.d.ts.map +1 -0
- package/dist/init-dependency-versions.js +53 -0
- package/dist/init-dependency-versions.js.map +1 -0
- package/dist/init-package-json.d.ts +21 -0
- package/dist/init-package-json.d.ts.map +1 -0
- package/dist/init-package-json.js +46 -0
- package/dist/init-package-json.js.map +1 -0
- package/dist/init-project-detect.d.ts +14 -0
- package/dist/init-project-detect.d.ts.map +1 -0
- package/dist/init-project-detect.js +89 -0
- package/dist/init-project-detect.js.map +1 -0
- package/dist/postgres-ctl.d.ts.map +1 -1
- package/dist/postgres-ctl.js +2 -1
- package/dist/postgres-ctl.js.map +1 -1
- package/dist/required-host-components.d.ts +11 -0
- package/dist/required-host-components.d.ts.map +1 -0
- package/dist/required-host-components.js +25 -0
- package/dist/required-host-components.js.map +1 -0
- package/dist/resolve-api-url.d.ts +6 -0
- package/dist/resolve-api-url.d.ts.map +1 -0
- package/dist/resolve-api-url.js +39 -0
- package/dist/resolve-api-url.js.map +1 -0
- package/dist/rest-cache-admin.d.ts +33 -0
- package/dist/rest-cache-admin.d.ts.map +1 -0
- package/dist/rest-cache-admin.js +96 -0
- package/dist/rest-cache-admin.js.map +1 -0
- package/dist/scripts/postinstall.d.ts +4 -6
- package/dist/scripts/postinstall.d.ts.map +1 -1
- package/dist/scripts/postinstall.js +8 -41
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/self-host-compose.d.ts.map +1 -1
- package/dist/self-host-compose.js +23 -13
- package/dist/self-host-compose.js.map +1 -1
- package/dist/studio-dev-server.d.ts +4 -2
- package/dist/studio-dev-server.d.ts.map +1 -1
- package/dist/studio-dev-server.js +3 -2
- package/dist/studio-dev-server.js.map +1 -1
- package/dist/ui/brand.d.ts +2 -5
- package/dist/ui/brand.d.ts.map +1 -1
- package/dist/ui/brand.js +2 -7
- package/dist/ui/brand.js.map +1 -1
- package/dist/ui/clack-api.d.ts +44 -0
- package/dist/ui/clack-api.d.ts.map +1 -0
- package/dist/ui/clack-api.js +76 -0
- package/dist/ui/clack-api.js.map +1 -0
- package/dist/ui/clack.d.ts +14 -0
- package/dist/ui/clack.d.ts.map +1 -0
- package/dist/ui/clack.js +147 -0
- package/dist/ui/clack.js.map +1 -0
- package/dist/ui/components/FlowSpinner.d.ts +5 -0
- package/dist/ui/components/FlowSpinner.d.ts.map +1 -0
- package/dist/ui/components/FlowSpinner.js +8 -0
- package/dist/ui/components/FlowSpinner.js.map +1 -0
- package/dist/ui/components/LogoWordmark.d.ts +9 -0
- package/dist/ui/components/LogoWordmark.d.ts.map +1 -0
- package/dist/ui/components/LogoWordmark.js +17 -0
- package/dist/ui/components/LogoWordmark.js.map +1 -0
- package/dist/ui/components/PromptPanel.d.ts +5 -0
- package/dist/ui/components/PromptPanel.d.ts.map +1 -0
- package/dist/ui/components/PromptPanel.js +7 -0
- package/dist/ui/components/PromptPanel.js.map +1 -0
- package/dist/ui/components/SelectField.d.ts +11 -0
- package/dist/ui/components/SelectField.d.ts.map +1 -0
- package/dist/ui/components/SelectField.js +19 -0
- package/dist/ui/components/SelectField.js.map +1 -0
- package/dist/ui/confirm.d.ts +2 -1
- package/dist/ui/confirm.d.ts.map +1 -1
- package/dist/ui/confirm.js +4 -4
- package/dist/ui/confirm.js.map +1 -1
- package/dist/ui/dev/DevDashboard.d.ts +8 -0
- package/dist/ui/dev/DevDashboard.d.ts.map +1 -0
- package/dist/ui/dev/DevDashboard.js +158 -0
- package/dist/ui/dev/DevDashboard.js.map +1 -0
- package/dist/ui/dev/DevReadyPanelView.d.ts +8 -0
- package/dist/ui/dev/DevReadyPanelView.d.ts.map +1 -0
- package/dist/ui/dev/DevReadyPanelView.js +48 -0
- package/dist/ui/dev/DevReadyPanelView.js.map +1 -0
- package/dist/ui/fatal.d.ts.map +1 -1
- package/dist/ui/fatal.js +5 -11
- package/dist/ui/fatal.js.map +1 -1
- package/dist/ui/flows/FlowApp.d.ts +8 -0
- package/dist/ui/flows/FlowApp.d.ts.map +1 -0
- package/dist/ui/flows/FlowApp.js +44 -0
- package/dist/ui/flows/FlowApp.js.map +1 -0
- package/dist/ui/flows/prompt-fields.d.ts +48 -0
- package/dist/ui/flows/prompt-fields.d.ts.map +1 -0
- package/dist/ui/flows/prompt-fields.js +92 -0
- package/dist/ui/flows/prompt-fields.js.map +1 -0
- package/dist/ui/flows/types.d.ts +41 -0
- package/dist/ui/flows/types.d.ts.map +1 -0
- package/dist/ui/flows/types.js +2 -0
- package/dist/ui/flows/types.js.map +1 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/ui/messages.d.ts +1 -0
- package/dist/ui/messages.d.ts.map +1 -1
- package/dist/ui/messages.js +48 -8
- package/dist/ui/messages.js.map +1 -1
- package/dist/ui/progress.d.ts +5 -1
- package/dist/ui/progress.d.ts.map +1 -1
- package/dist/ui/progress.js +12 -3
- package/dist/ui/progress.js.map +1 -1
- package/dist/ui/prompts.d.ts +7 -5
- package/dist/ui/prompts.d.ts.map +1 -1
- package/dist/ui/prompts.js +15 -8
- package/dist/ui/prompts.js.map +1 -1
- package/dist/ui/runtime/cancel.d.ts +4 -0
- package/dist/ui/runtime/cancel.d.ts.map +1 -0
- package/dist/ui/runtime/cancel.js +6 -0
- package/dist/ui/runtime/cancel.js.map +1 -0
- package/dist/ui/runtime/command-chrome.d.ts +17 -0
- package/dist/ui/runtime/command-chrome.d.ts.map +1 -0
- package/dist/ui/runtime/command-chrome.js +123 -0
- package/dist/ui/runtime/command-chrome.js.map +1 -0
- package/dist/ui/runtime/dev-prompt-queue.d.ts +30 -0
- package/dist/ui/runtime/dev-prompt-queue.d.ts.map +1 -0
- package/dist/ui/runtime/dev-prompt-queue.js +57 -0
- package/dist/ui/runtime/dev-prompt-queue.js.map +1 -0
- package/dist/ui/runtime/flow-session.d.ts +4 -0
- package/dist/ui/runtime/flow-session.d.ts.map +1 -0
- package/dist/ui/runtime/flow-session.js +8 -0
- package/dist/ui/runtime/flow-session.js.map +1 -0
- package/dist/ui/runtime/run-clack-flow.d.ts +5 -0
- package/dist/ui/runtime/run-clack-flow.d.ts.map +1 -0
- package/dist/ui/runtime/run-clack-flow.js +46 -0
- package/dist/ui/runtime/run-clack-flow.js.map +1 -0
- package/dist/ui/runtime/stdin-after-ink.d.ts +3 -0
- package/dist/ui/runtime/stdin-after-ink.d.ts.map +1 -0
- package/dist/ui/runtime/stdin-after-ink.js +16 -0
- package/dist/ui/runtime/stdin-after-ink.js.map +1 -0
- package/dist/ui/theme.d.ts +24 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +34 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/valkey-sidecar.d.ts +17 -0
- package/dist/valkey-sidecar.d.ts.map +1 -0
- package/dist/valkey-sidecar.js +94 -0
- package/dist/valkey-sidecar.js.map +1 -0
- package/package.json +9 -3
- package/src/binary-cache.ts +61 -0
- package/src/binary-download-lock.ts +124 -0
- package/src/cli-package-version.ts +15 -0
- package/src/cli.ts +6 -2
- package/src/commands/add.ts +26 -19
- package/src/commands/admin.ts +156 -29
- package/src/commands/cache.ts +105 -5
- package/src/commands/dev.ts +26 -21
- package/src/commands/init.ts +473 -247
- package/src/commands/link-helpers.ts +30 -29
- package/src/compose-local-server-image.ts +68 -0
- package/src/compose-rename.ts +5 -8
- package/src/config.ts +13 -1
- package/src/dev-compose.ts +139 -54
- package/src/dev-log-bus.ts +20 -0
- package/src/dev-log-filter.ts +42 -0
- package/src/dev-ports.ts +22 -11
- package/src/dev-ready-panel.ts +80 -0
- package/src/dev-session-lock.ts +5 -8
- package/src/dev-session.ts +98 -18
- package/src/dev-shutdown.ts +29 -17
- package/src/dev-task-colors.ts +22 -3
- package/src/docker-runtime.ts +1 -1
- package/src/engine-push-output.ts +5 -1
- package/src/ensure-binary.ts +21 -1
- package/src/ensure-component-binaries.ts +71 -0
- package/src/ensure-dev-api-config.ts +18 -0
- package/src/init-dependency-versions.ts +69 -0
- package/src/init-package-json.ts +63 -0
- package/src/init-project-detect.ts +99 -0
- package/src/postgres-ctl.ts +2 -1
- package/src/required-host-components.ts +37 -0
- package/src/resolve-api-url.ts +43 -0
- package/src/rest-cache-admin.ts +135 -0
- package/src/scripts/postinstall.ts +9 -46
- package/src/self-host-compose.ts +22 -12
- package/src/studio-dev-server.ts +5 -4
- package/src/ui/README.md +13 -9
- package/src/ui/brand.ts +2 -8
- package/src/ui/clack-api.ts +123 -0
- package/src/ui/clack.ts +153 -0
- package/src/ui/components/FlowSpinner.tsx +15 -0
- package/src/ui/components/LogoWordmark.tsx +36 -0
- package/src/ui/components/PromptPanel.tsx +17 -0
- package/src/ui/components/SelectField.tsx +58 -0
- package/src/ui/confirm.ts +5 -4
- package/src/ui/dev/DevDashboard.tsx +223 -0
- package/src/ui/dev/DevReadyPanelView.tsx +134 -0
- package/src/ui/fatal.ts +5 -10
- package/src/ui/flows/FlowApp.tsx +69 -0
- package/src/ui/flows/prompt-fields.tsx +198 -0
- package/src/ui/flows/types.ts +50 -0
- package/src/ui/index.ts +1 -1
- package/src/ui/messages.ts +50 -8
- package/src/ui/progress.ts +14 -3
- package/src/ui/prompts.ts +17 -9
- package/src/ui/runtime/cancel.ts +6 -0
- package/src/ui/runtime/command-chrome.ts +147 -0
- package/src/ui/runtime/dev-prompt-queue.ts +84 -0
- package/src/ui/runtime/flow-session.ts +11 -0
- package/src/ui/runtime/run-clack-flow.tsx +65 -0
- package/src/ui/runtime/stdin-after-ink.ts +18 -0
- package/src/ui/theme.ts +41 -0
- package/src/valkey-sidecar.ts +126 -0
- package/tests/admin-ensure.test.ts +80 -0
- package/tests/binary-cache-cloud-overrides.test.ts +2 -2
- package/tests/binary-download-lock.test.ts +41 -0
- package/tests/cli-help.test.ts +7 -4
- package/tests/cli-package-version.test.ts +15 -0
- package/tests/command-chrome-coverage.test.ts +149 -0
- package/tests/command-chrome-wrap.test.ts +51 -0
- package/tests/config.test.ts +27 -4
- package/tests/dev-ready-panel.test.ts +37 -0
- package/tests/dev-ui.test.ts +42 -2
- package/tests/docker-runtime.test.ts +9 -20
- package/tests/ensure-dev-api-config.test.ts +22 -0
- package/tests/init-dependency-versions.test.ts +62 -0
- package/tests/init-package-json.test.ts +18 -0
- package/tests/init-project-detect.test.ts +53 -0
- package/tests/init.test.ts +30 -3
- package/tests/required-host-components.test.ts +51 -0
- package/tests/resolve-api-url.test.ts +23 -0
- package/tests/rest-cache-admin.test.ts +28 -0
- package/tests/runtime-contract.test.ts +12 -3
- package/tests/stdin-after-ink.test.ts +8 -0
- package/tests/tsconfig.json +8 -2
- package/tests/ui-confirm.test.ts +13 -9
- package/tests/ui-messages.test.ts +4 -18
- package/tsconfig.json +1 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +3 -0
- package/dist/dev-tui.d.ts +0 -24
- package/dist/dev-tui.d.ts.map +0 -1
- package/dist/dev-tui.js +0 -188
- package/dist/dev-tui.js.map +0 -1
- package/src/dev-tui.ts +0 -232
package/src/commands/init.ts
CHANGED
|
@@ -1,36 +1,26 @@
|
|
|
1
1
|
import type { Command } from "commander"
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
|
|
3
|
-
import { resolve, join,
|
|
4
|
-
import { fileURLToPath } from "node:url"
|
|
3
|
+
import { resolve, join, basename } from "node:path"
|
|
5
4
|
import { spawnSync } from "node:child_process"
|
|
6
|
-
import
|
|
7
|
-
import { ensureNotCancelled
|
|
5
|
+
import { p, runClackFlow } from "../ui/clack.js"
|
|
6
|
+
import { ensureNotCancelled } from "../ui/prompts.js"
|
|
8
7
|
import { generateAndWriteKeys } from "./keys.js"
|
|
9
8
|
import { file, error, info, plain, warn } from "../ui/messages.js"
|
|
10
9
|
import { nextSteps } from "../ui/next-steps.js"
|
|
11
10
|
import { probeDockerDaemon, reportDockerUnavailable } from "../docker-runtime.js"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
function cliPackageVersion(): string {
|
|
27
|
-
try {
|
|
28
|
-
const pkg = JSON.parse(readFileSync(CLI_PACKAGE_JSON, "utf8")) as { version?: string }
|
|
29
|
-
return pkg.version ?? "0.1.0"
|
|
30
|
-
} catch {
|
|
31
|
-
return "0.1.0"
|
|
32
|
-
}
|
|
33
|
-
}
|
|
11
|
+
import { loadConfig } from "../config.js"
|
|
12
|
+
import {
|
|
13
|
+
ensureComponentBinaries,
|
|
14
|
+
reportComponentBinaryFailures,
|
|
15
|
+
} from "../ensure-component-binaries.js"
|
|
16
|
+
import { mergeSupatypePackageJson } from "../init-package-json.js"
|
|
17
|
+
import { cliPackageVersion } from "../cli-package-version.js"
|
|
18
|
+
import {
|
|
19
|
+
initDependencyVersionsFallback,
|
|
20
|
+
resolveInitDependencyVersions,
|
|
21
|
+
type InitDependencyVersions,
|
|
22
|
+
} from "../init-dependency-versions.js"
|
|
23
|
+
import { detectProjectSetup, type DetectedProjectSetup } from "../init-project-detect.js"
|
|
34
24
|
|
|
35
25
|
// ─── Options model ─────────────────────────────────────────────────────────--
|
|
36
26
|
|
|
@@ -166,8 +156,14 @@ export function registerInit(program: Command): void {
|
|
|
166
156
|
|
|
167
157
|
let result: WizardResult
|
|
168
158
|
if (interactive) {
|
|
169
|
-
|
|
170
|
-
|
|
159
|
+
await runInteractiveInit({
|
|
160
|
+
name,
|
|
161
|
+
dir,
|
|
162
|
+
defaultName,
|
|
163
|
+
modeTarget,
|
|
164
|
+
opts,
|
|
165
|
+
})
|
|
166
|
+
return
|
|
171
167
|
} else {
|
|
172
168
|
result = {
|
|
173
169
|
...defaultScaffoldOptions(defaultName, modeTarget),
|
|
@@ -200,220 +196,346 @@ export function registerInit(program: Command): void {
|
|
|
200
196
|
|
|
201
197
|
if (name) mkdirSync(dir, { recursive: true })
|
|
202
198
|
|
|
203
|
-
|
|
199
|
+
const deps = await resolveInitDependencyVersions()
|
|
200
|
+
const runningCli = cliPackageVersion()
|
|
201
|
+
if (deps.cli !== runningCli || deps.types !== runningCli) {
|
|
202
|
+
info(
|
|
203
|
+
`Using published npm versions: @supatype/cli ^${deps.cli}, @supatype/types ^${deps.types}`,
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
scaffold(dir, result, deps)
|
|
204
208
|
|
|
205
|
-
|
|
209
|
+
const installOk = doInstall ? runInstall(dir, result.packageManager) : false
|
|
206
210
|
const keysGenerated = doKeys ? writeKeys(dir) : false
|
|
211
|
+
const binariesReady =
|
|
212
|
+
doInstall && installOk ? await ensureInitBinaries(dir) : false
|
|
213
|
+
|
|
214
|
+
if (doInstall && !installOk) {
|
|
215
|
+
error(
|
|
216
|
+
`Dependency install failed. Run "${result.packageManager} install" in the project directory, then "supatype update".`,
|
|
217
|
+
)
|
|
218
|
+
process.exit(1)
|
|
219
|
+
}
|
|
220
|
+
if (doKeys && !keysGenerated) {
|
|
221
|
+
error("Could not generate API keys. Check .env has JWT_SECRET, then run `supatype keys`.")
|
|
222
|
+
process.exit(1)
|
|
223
|
+
}
|
|
224
|
+
if (doInstall && installOk && !binariesReady) {
|
|
225
|
+
error("Component binaries could not be downloaded. Run `supatype update` in the project directory.")
|
|
226
|
+
process.exit(1)
|
|
227
|
+
}
|
|
207
228
|
|
|
208
229
|
warnDockerUnavailableForProvider(result.provider)
|
|
209
230
|
|
|
210
231
|
printNextSteps({
|
|
211
232
|
name,
|
|
212
233
|
result,
|
|
213
|
-
installed: doInstall,
|
|
234
|
+
installed: doInstall && installOk,
|
|
214
235
|
keysGenerated,
|
|
236
|
+
binariesReady,
|
|
215
237
|
})
|
|
216
238
|
})
|
|
217
239
|
}
|
|
218
240
|
|
|
219
|
-
|
|
241
|
+
interface InteractiveInitArgs {
|
|
242
|
+
name: string | undefined
|
|
243
|
+
dir: string
|
|
244
|
+
defaultName: string
|
|
245
|
+
modeTarget: ProductionTarget
|
|
246
|
+
opts: InitCliOptions
|
|
247
|
+
}
|
|
220
248
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
249
|
+
/** Interactive init — one Ink session for wizard + scaffold + install + finish. */
|
|
250
|
+
async function runInteractiveInit(args: InteractiveInitArgs): Promise<void> {
|
|
251
|
+
const { name, dir, defaultName, modeTarget, opts } = args
|
|
252
|
+
|
|
253
|
+
await runClackFlow(async () => {
|
|
254
|
+
p.intro("Create a new Supatype project")
|
|
255
|
+
const detected = detectProjectSetup(dir)
|
|
256
|
+
if (detected.hasExistingFiles && detected.summaryLines.length > 0) {
|
|
257
|
+
p.note(
|
|
258
|
+
["Existing project detected:", ...detected.summaryLines.map((line) => ` • ${line}`)].join(
|
|
259
|
+
"\n",
|
|
260
|
+
),
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
let result = await collectWizardAnswers(defaultName, modeTarget, dir, detected)
|
|
264
|
+
|
|
265
|
+
if (opts.admin === false) {
|
|
266
|
+
delete result.adminEmail
|
|
267
|
+
delete result.adminPassword
|
|
268
|
+
} else if (opts.adminEmail && opts.adminPassword) {
|
|
269
|
+
result.adminEmail = opts.adminEmail
|
|
270
|
+
result.adminPassword = opts.adminPassword
|
|
271
|
+
}
|
|
226
272
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
message: "Project name",
|
|
230
|
-
defaultValue: defaultName,
|
|
231
|
-
placeholder: defaultName,
|
|
232
|
-
}),
|
|
233
|
-
).trim() || defaultName
|
|
273
|
+
const doInstall = opts.install !== false && result.install
|
|
274
|
+
const doKeys = opts.keys !== false && result.generateKeys
|
|
234
275
|
|
|
235
|
-
|
|
236
|
-
await p.select<PackageManager>({
|
|
237
|
-
message: "Package manager",
|
|
238
|
-
initialValue: detectInvokingPackageManager(),
|
|
239
|
-
options: [
|
|
240
|
-
{ value: "npm", label: "npm" },
|
|
241
|
-
{ value: "pnpm", label: "pnpm" },
|
|
242
|
-
{ value: "yarn", label: "yarn" },
|
|
243
|
-
{ value: "bun", label: "bun" },
|
|
244
|
-
],
|
|
245
|
-
}),
|
|
246
|
-
)
|
|
276
|
+
if (name) mkdirSync(dir, { recursive: true })
|
|
247
277
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
{ value: "later", label: "Decide later", hint: "local development only for now" },
|
|
256
|
-
],
|
|
257
|
-
}),
|
|
258
|
-
)
|
|
278
|
+
const deps = await resolveInitDependencyVersions()
|
|
279
|
+
const runningCli = cliPackageVersion()
|
|
280
|
+
if (deps.cli !== runningCli || deps.types !== runningCli) {
|
|
281
|
+
info(
|
|
282
|
+
`Using published npm versions: @supatype/cli ^${deps.cli}, @supatype/types ^${deps.types}`,
|
|
283
|
+
)
|
|
284
|
+
}
|
|
259
285
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if (domain) {
|
|
271
|
-
tlsEmail =
|
|
272
|
-
ensureNotCancelled(
|
|
273
|
-
await p.text({
|
|
274
|
-
message: "Email for Let's Encrypt (HTTPS) certificates",
|
|
275
|
-
placeholder: "you@example.com",
|
|
276
|
-
defaultValue: "",
|
|
277
|
-
}),
|
|
278
|
-
).trim() || undefined
|
|
286
|
+
const setupSpinner = p.spinner()
|
|
287
|
+
setupSpinner.start("Setting up your project...")
|
|
288
|
+
|
|
289
|
+
scaffold(dir, result, deps)
|
|
290
|
+
|
|
291
|
+
let installOk = !doInstall
|
|
292
|
+
if (doInstall) {
|
|
293
|
+
setupSpinner.start(`Installing dependencies with ${result.packageManager}...`)
|
|
294
|
+
installOk = runInstall(dir, result.packageManager)
|
|
295
|
+
setupSpinner.stop(installOk ? "Dependencies installed." : "Dependency install failed.")
|
|
279
296
|
}
|
|
280
|
-
}
|
|
281
297
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
{ value: "native", label: "Native", hint: "host Postgres + server binaries, no Docker" },
|
|
289
|
-
],
|
|
290
|
-
}),
|
|
291
|
-
)
|
|
298
|
+
let keysGenerated = !doKeys
|
|
299
|
+
if (doKeys) {
|
|
300
|
+
setupSpinner.start("Generating API keys...")
|
|
301
|
+
keysGenerated = writeKeys(dir)
|
|
302
|
+
setupSpinner.stop(keysGenerated ? "API keys generated." : "Could not generate API keys.")
|
|
303
|
+
}
|
|
292
304
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
305
|
+
let binariesReady = false
|
|
306
|
+
if (doInstall && installOk) {
|
|
307
|
+
setupSpinner.start("Preparing component binaries...")
|
|
308
|
+
binariesReady = await ensureInitBinaries(dir)
|
|
309
|
+
setupSpinner.stop(binariesReady ? "Component binaries ready." : "Some binaries are missing.")
|
|
310
|
+
}
|
|
298
311
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
|
|
312
|
+
if (doInstall && !installOk) {
|
|
313
|
+
error(
|
|
314
|
+
`Dependency install failed. Run "${result.packageManager} install" in the project directory, then "supatype update".`,
|
|
315
|
+
)
|
|
316
|
+
process.exit(1)
|
|
317
|
+
}
|
|
318
|
+
if (doKeys && !keysGenerated) {
|
|
319
|
+
error("Could not generate API keys. Check .env has JWT_SECRET, then run `supatype keys`.")
|
|
320
|
+
process.exit(1)
|
|
321
|
+
}
|
|
322
|
+
if (doInstall && installOk && !binariesReady) {
|
|
323
|
+
error("Component binaries could not be downloaded. Run `supatype update` in the project directory.")
|
|
324
|
+
process.exit(1)
|
|
325
|
+
}
|
|
306
326
|
|
|
307
|
-
|
|
327
|
+
warnDockerUnavailableForProvider(result.provider)
|
|
308
328
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
],
|
|
319
|
-
}),
|
|
320
|
-
)
|
|
329
|
+
p.outro(`Supatype project ready${name ? ` in ${name}/` : ""}.`)
|
|
330
|
+
printNextSteps({
|
|
331
|
+
name,
|
|
332
|
+
result,
|
|
333
|
+
installed: doInstall && installOk,
|
|
334
|
+
keysGenerated,
|
|
335
|
+
binariesReady,
|
|
336
|
+
})
|
|
337
|
+
})
|
|
321
338
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
message: "Local storage (for development)?",
|
|
325
|
-
initialValue: "local",
|
|
326
|
-
options: STORAGE_PROVIDER_OPTIONS,
|
|
327
|
-
}),
|
|
328
|
-
)
|
|
339
|
+
process.exit(0)
|
|
340
|
+
}
|
|
329
341
|
|
|
330
|
-
|
|
331
|
-
await p.select<StorageProvider>({
|
|
332
|
-
message: "Production storage?",
|
|
333
|
-
initialValue: "local",
|
|
334
|
-
options: STORAGE_PROVIDER_OPTIONS,
|
|
335
|
-
}),
|
|
336
|
-
)
|
|
342
|
+
// ─── Wizard ──────────────────────────────────────────────────────────────────
|
|
337
343
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
+
async function collectWizardAnswers(
|
|
345
|
+
defaultName: string,
|
|
346
|
+
defaultTarget: ProductionTarget,
|
|
347
|
+
dir: string,
|
|
348
|
+
detected: DetectedProjectSetup,
|
|
349
|
+
): Promise<WizardResult> {
|
|
350
|
+
const projectName = ensureNotCancelled(
|
|
351
|
+
await p.text({
|
|
352
|
+
message: "Project name",
|
|
353
|
+
defaultValue: defaultName,
|
|
354
|
+
placeholder: defaultName,
|
|
355
|
+
}),
|
|
356
|
+
).trim() || defaultName
|
|
357
|
+
|
|
358
|
+
const admin = await promptAdminUser()
|
|
359
|
+
|
|
360
|
+
const packageManager = ensureNotCancelled(
|
|
361
|
+
await p.select<PackageManager>({
|
|
362
|
+
message: "Package manager",
|
|
363
|
+
initialValue: detectInvokingPackageManager(),
|
|
364
|
+
options: [
|
|
365
|
+
{ value: "npm", label: "npm" },
|
|
366
|
+
{ value: "pnpm", label: "pnpm" },
|
|
367
|
+
{ value: "yarn", label: "yarn" },
|
|
368
|
+
{ value: "bun", label: "bun" },
|
|
369
|
+
],
|
|
370
|
+
}),
|
|
371
|
+
)
|
|
344
372
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
373
|
+
const productionTarget = ensureNotCancelled(
|
|
374
|
+
await p.select<ProductionTarget>({
|
|
375
|
+
message: "Where will this run in production?",
|
|
376
|
+
initialValue: defaultTarget,
|
|
377
|
+
options: [
|
|
378
|
+
{ value: "cloud", label: "Supatype Cloud", hint: "managed; deploy via supatype link" },
|
|
379
|
+
{ value: "self-host", label: "Self-host", hint: "your own server with TLS" },
|
|
380
|
+
{ value: "later", label: "Decide later", hint: "local development only for now" },
|
|
381
|
+
],
|
|
382
|
+
}),
|
|
383
|
+
)
|
|
351
384
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
385
|
+
let domain: string | undefined
|
|
386
|
+
let tlsEmail: string | undefined
|
|
387
|
+
if (productionTarget === "self-host") {
|
|
388
|
+
domain = ensureNotCancelled(
|
|
389
|
+
await p.text({
|
|
390
|
+
message: "Production domain for ACME TLS (optional, can set later)",
|
|
391
|
+
placeholder: "api.example.com",
|
|
392
|
+
defaultValue: "",
|
|
393
|
+
}),
|
|
394
|
+
).trim()
|
|
395
|
+
if (domain) {
|
|
396
|
+
tlsEmail =
|
|
397
|
+
ensureNotCancelled(
|
|
398
|
+
await p.text({
|
|
399
|
+
message: "Email for Let's Encrypt (HTTPS) certificates",
|
|
400
|
+
placeholder: "you@example.com",
|
|
401
|
+
defaultValue: "",
|
|
402
|
+
}),
|
|
403
|
+
).trim() || undefined
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const provider = ensureNotCancelled(
|
|
408
|
+
await p.select<ScaffoldOptions["provider"]>({
|
|
409
|
+
message: "How should Postgres and the server run for local development?",
|
|
410
|
+
initialValue: "docker",
|
|
411
|
+
options: [
|
|
412
|
+
{ value: "docker", label: "Docker", hint: "Docker Compose stack (recommended)" },
|
|
413
|
+
{ value: "native", label: "Native", hint: "host Postgres + server binaries, no Docker" },
|
|
414
|
+
],
|
|
415
|
+
}),
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
let kongPort: number | undefined
|
|
419
|
+
if (provider === "docker") {
|
|
420
|
+
const { promptKongPortChoice } = await import("../dev-ports.js")
|
|
421
|
+
kongPort = await promptKongPortChoice()
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const defaultSchemaPath = existsSync(join(dir, "schema/index.ts"))
|
|
425
|
+
? "schema/index.ts"
|
|
426
|
+
: "schema/index.ts"
|
|
427
|
+
const schemaPath = ensureNotCancelled(
|
|
428
|
+
await p.text({
|
|
429
|
+
message: "Where should your schema live?",
|
|
430
|
+
defaultValue: defaultSchemaPath,
|
|
431
|
+
placeholder: "schema/index.ts",
|
|
432
|
+
}),
|
|
433
|
+
).trim() || defaultSchemaPath
|
|
434
|
+
|
|
435
|
+
const app = await promptApp(detected, productionTarget)
|
|
436
|
+
|
|
437
|
+
const email = ensureNotCancelled(
|
|
438
|
+
await p.select<ScaffoldOptions["email"]>({
|
|
439
|
+
message: "Email provider",
|
|
440
|
+
initialValue: "console",
|
|
441
|
+
options: [
|
|
442
|
+
{ value: "console", label: "console", hint: "log emails to the terminal (dev)" },
|
|
443
|
+
{ value: "smtp", label: "SMTP" },
|
|
444
|
+
{ value: "resend", label: "Resend" },
|
|
445
|
+
{ value: "ses", label: "Amazon SES" },
|
|
446
|
+
],
|
|
447
|
+
}),
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
const storageLocal = ensureNotCancelled(
|
|
451
|
+
await p.select<StorageProvider>({
|
|
452
|
+
message: "Local storage (for development)?",
|
|
453
|
+
initialValue: "local",
|
|
454
|
+
options: STORAGE_PROVIDER_OPTIONS,
|
|
455
|
+
}),
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
const storageProduction = ensureNotCancelled(
|
|
459
|
+
await p.select<StorageProvider>({
|
|
460
|
+
message: "Production storage?",
|
|
461
|
+
initialValue: "local",
|
|
462
|
+
options: STORAGE_PROVIDER_OPTIONS,
|
|
463
|
+
}),
|
|
464
|
+
)
|
|
358
465
|
|
|
359
|
-
|
|
360
|
-
|
|
466
|
+
const helloFunction = ensureNotCancelled(
|
|
467
|
+
await p.confirm({
|
|
468
|
+
message: "Create a hello-world edge function?",
|
|
469
|
+
initialValue: false,
|
|
470
|
+
}),
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
projectName,
|
|
475
|
+
provider,
|
|
476
|
+
...(kongPort !== undefined ? { kongPort } : {}),
|
|
477
|
+
productionTarget,
|
|
478
|
+
...(domain !== undefined ? { domain } : {}),
|
|
479
|
+
...(tlsEmail !== undefined ? { tlsEmail } : {}),
|
|
480
|
+
schemaPath,
|
|
481
|
+
app,
|
|
482
|
+
email,
|
|
483
|
+
storageLocal,
|
|
484
|
+
storageProduction,
|
|
485
|
+
helloFunction,
|
|
486
|
+
packageManager,
|
|
487
|
+
install: true,
|
|
488
|
+
generateKeys: true,
|
|
489
|
+
...admin,
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
async function promptAdminUser(): Promise<{ adminEmail?: string; adminPassword?: string }> {
|
|
361
494
|
const createAdmin = ensureNotCancelled(
|
|
362
495
|
await p.confirm({
|
|
363
496
|
message: "Create an admin user for /admin?",
|
|
364
497
|
initialValue: true,
|
|
365
498
|
}),
|
|
366
499
|
)
|
|
367
|
-
if (createAdmin) {
|
|
368
|
-
adminEmail =
|
|
369
|
-
ensureNotCancelled(
|
|
370
|
-
await p.text({
|
|
371
|
-
message: "Admin email",
|
|
372
|
-
placeholder: "you@example.com",
|
|
373
|
-
defaultValue: "",
|
|
374
|
-
}),
|
|
375
|
-
).trim() || undefined
|
|
376
|
-
if (adminEmail) {
|
|
377
|
-
adminPassword =
|
|
378
|
-
ensureNotCancelled(
|
|
379
|
-
await p.text({
|
|
380
|
-
message: "Admin password (min 8 chars)",
|
|
381
|
-
defaultValue: "",
|
|
382
|
-
}),
|
|
383
|
-
).trim() || undefined
|
|
384
|
-
if (adminPassword && adminPassword.length < 8) {
|
|
385
|
-
adminPassword = undefined
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
500
|
+
if (!createAdmin) return {}
|
|
389
501
|
|
|
390
|
-
p.
|
|
502
|
+
p.note("First sign-in at /admin — created automatically on supatype dev or push.")
|
|
391
503
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
generateKeys,
|
|
408
|
-
...(adminEmail && adminPassword ? { adminEmail, adminPassword } : {}),
|
|
504
|
+
const adminEmail = ensureNotCancelled(
|
|
505
|
+
await p.text({
|
|
506
|
+
message: "Admin email",
|
|
507
|
+
placeholder: "you@example.com",
|
|
508
|
+
validate: (value) => (value.trim() ? undefined : "Email is required"),
|
|
509
|
+
}),
|
|
510
|
+
).trim()
|
|
511
|
+
|
|
512
|
+
let adminPassword = ""
|
|
513
|
+
while (true) {
|
|
514
|
+
adminPassword = ensureNotCancelled(
|
|
515
|
+
await p.password({ message: "Admin password (min 8 characters)" }),
|
|
516
|
+
).trim()
|
|
517
|
+
if (adminPassword.length >= 8) break
|
|
518
|
+
p.log.warn("Password must be at least 8 characters.")
|
|
409
519
|
}
|
|
520
|
+
|
|
521
|
+
return { adminEmail, adminPassword }
|
|
410
522
|
}
|
|
411
523
|
|
|
412
|
-
async function promptApp(
|
|
524
|
+
async function promptApp(
|
|
525
|
+
detected: DetectedProjectSetup,
|
|
526
|
+
productionTarget: ProductionTarget,
|
|
527
|
+
): Promise<ScaffoldAppOptions> {
|
|
528
|
+
const initialMode: ScaffoldAppOptions["mode"] =
|
|
529
|
+
detected.hasVite && productionTarget !== "later"
|
|
530
|
+
? "static"
|
|
531
|
+
: detected.hasVite
|
|
532
|
+
? "proxy"
|
|
533
|
+
: "none"
|
|
534
|
+
|
|
413
535
|
const mode = ensureNotCancelled(
|
|
414
536
|
await p.select<ScaffoldAppOptions["mode"]>({
|
|
415
537
|
message: "Host a frontend app at /?",
|
|
416
|
-
initialValue:
|
|
538
|
+
initialValue: initialMode,
|
|
417
539
|
options: [
|
|
418
540
|
{ value: "none", label: "No app", hint: "API only" },
|
|
419
541
|
{ value: "static", label: "Static site", hint: "serve a built directory" },
|
|
@@ -426,11 +548,11 @@ async function promptApp(): Promise<ScaffoldAppOptions> {
|
|
|
426
548
|
const staticDir = ensureNotCancelled(
|
|
427
549
|
await p.text({
|
|
428
550
|
message: "Directory to serve",
|
|
429
|
-
defaultValue:
|
|
430
|
-
placeholder:
|
|
551
|
+
defaultValue: detected.staticDir,
|
|
552
|
+
placeholder: detected.staticDir,
|
|
431
553
|
}),
|
|
432
|
-
).trim() ||
|
|
433
|
-
const viteDevUrl = await promptViteDevUrl()
|
|
554
|
+
).trim() || detected.staticDir
|
|
555
|
+
const viteDevUrl = await promptViteDevUrl(detected, productionTarget)
|
|
434
556
|
return { mode, staticDir, ...(viteDevUrl ? { viteDevUrl } : {}) }
|
|
435
557
|
}
|
|
436
558
|
|
|
@@ -438,40 +560,49 @@ async function promptApp(): Promise<ScaffoldAppOptions> {
|
|
|
438
560
|
const upstream = ensureNotCancelled(
|
|
439
561
|
await p.text({
|
|
440
562
|
message: "URL of your running dev server",
|
|
441
|
-
defaultValue: "http://localhost:3000",
|
|
442
|
-
placeholder: "http://localhost:3000",
|
|
563
|
+
defaultValue: detected.hasVite ? detected.viteDevUrl : "http://localhost:3000",
|
|
564
|
+
placeholder: detected.hasVite ? detected.viteDevUrl : "http://localhost:3000",
|
|
443
565
|
}),
|
|
444
|
-
).trim() || "http://localhost:3000"
|
|
566
|
+
).trim() || (detected.hasVite ? detected.viteDevUrl : "http://localhost:3000")
|
|
445
567
|
const start = ensureNotCancelled(
|
|
446
568
|
await p.text({
|
|
447
569
|
message: "package.json script that starts your dev server",
|
|
448
|
-
defaultValue: "dev",
|
|
449
|
-
placeholder: "dev",
|
|
570
|
+
defaultValue: detected.hasVite ? "vite" : "dev",
|
|
571
|
+
placeholder: detected.hasVite ? "vite" : "dev",
|
|
450
572
|
}),
|
|
451
|
-
).trim() || "dev"
|
|
452
|
-
const viteDevUrl = await promptViteDevUrl()
|
|
573
|
+
).trim() || (detected.hasVite ? "vite" : "dev")
|
|
574
|
+
const viteDevUrl = await promptViteDevUrl(detected, productionTarget)
|
|
453
575
|
return { mode, upstream, start, ...(viteDevUrl ? { viteDevUrl } : {}) }
|
|
454
576
|
}
|
|
455
577
|
|
|
456
578
|
return { mode: "none" }
|
|
457
579
|
}
|
|
458
580
|
|
|
459
|
-
async function promptViteDevUrl(
|
|
581
|
+
async function promptViteDevUrl(
|
|
582
|
+
detected: DetectedProjectSetup,
|
|
583
|
+
productionTarget: ProductionTarget,
|
|
584
|
+
): Promise<string | undefined> {
|
|
585
|
+
const defaultYes = detected.hasVite || productionTarget !== "later"
|
|
460
586
|
const useVite = ensureNotCancelled(
|
|
461
587
|
await p.confirm({
|
|
462
|
-
message:
|
|
463
|
-
|
|
588
|
+
message: detected.hasVite
|
|
589
|
+
? "Use Vite for local development?"
|
|
590
|
+
: "Develop locally with Vite (live reload)?",
|
|
591
|
+
initialValue: defaultYes,
|
|
464
592
|
}),
|
|
465
593
|
)
|
|
466
594
|
if (!useVite) return undefined
|
|
595
|
+
|
|
596
|
+
p.note(`Press Enter for ${detected.viteDevUrl}, or type a custom URL.`)
|
|
597
|
+
|
|
467
598
|
return (
|
|
468
599
|
ensureNotCancelled(
|
|
469
600
|
await p.text({
|
|
470
601
|
message: "Vite dev server URL",
|
|
471
|
-
defaultValue:
|
|
472
|
-
placeholder:
|
|
602
|
+
defaultValue: detected.viteDevUrl,
|
|
603
|
+
placeholder: detected.viteDevUrl,
|
|
473
604
|
}),
|
|
474
|
-
).trim() ||
|
|
605
|
+
).trim() || detected.viteDevUrl
|
|
475
606
|
)
|
|
476
607
|
}
|
|
477
608
|
|
|
@@ -485,15 +616,36 @@ function detectInvokingPackageManager(): PackageManager {
|
|
|
485
616
|
return "npm"
|
|
486
617
|
}
|
|
487
618
|
|
|
488
|
-
function runInstall(dir: string, pm: PackageManager):
|
|
489
|
-
info(`Installing dependencies with ${pm}...`)
|
|
619
|
+
function runInstall(dir: string, pm: PackageManager): boolean {
|
|
490
620
|
const res = spawnSync(pm, ["install"], {
|
|
491
621
|
cwd: dir,
|
|
492
622
|
stdio: "inherit",
|
|
493
623
|
shell: process.platform === "win32",
|
|
494
624
|
})
|
|
495
625
|
if (res.status !== 0 || res.error) {
|
|
496
|
-
|
|
626
|
+
return false
|
|
627
|
+
}
|
|
628
|
+
return true
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
async function ensureInitBinaries(dir: string): Promise<boolean> {
|
|
632
|
+
const previousCwd = process.cwd()
|
|
633
|
+
try {
|
|
634
|
+
process.chdir(dir)
|
|
635
|
+
const config = loadConfig(dir)
|
|
636
|
+
const result = await ensureComponentBinaries(config, dir)
|
|
637
|
+
if (!result.ok) {
|
|
638
|
+
reportComponentBinaryFailures(result.failures)
|
|
639
|
+
return false
|
|
640
|
+
}
|
|
641
|
+
return true
|
|
642
|
+
} catch (err) {
|
|
643
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
644
|
+
warn(`Could not verify component binaries: ${message}`)
|
|
645
|
+
warn("Run manually from your project directory: supatype update")
|
|
646
|
+
return false
|
|
647
|
+
} finally {
|
|
648
|
+
process.chdir(previousCwd)
|
|
497
649
|
}
|
|
498
650
|
}
|
|
499
651
|
|
|
@@ -508,7 +660,11 @@ function writeKeys(dir: string): boolean {
|
|
|
508
660
|
|
|
509
661
|
// ─── Scaffold ──────────────────────────────────────────────────────────────--
|
|
510
662
|
|
|
511
|
-
function scaffold(
|
|
663
|
+
export function scaffold(
|
|
664
|
+
dir: string,
|
|
665
|
+
optsOrName: ScaffoldOptions | string,
|
|
666
|
+
deps: InitDependencyVersions = initDependencyVersionsFallback(),
|
|
667
|
+
): void {
|
|
512
668
|
const opts =
|
|
513
669
|
typeof optsOrName === "string" ? defaultScaffoldOptions(optsOrName) : optsOrName
|
|
514
670
|
const write = (rel: string, content: string) => {
|
|
@@ -520,9 +676,17 @@ function scaffold(dir: string, optsOrName: ScaffoldOptions | string): void {
|
|
|
520
676
|
|
|
521
677
|
const pkgPath = join(dir, "package.json")
|
|
522
678
|
if (!existsSync(pkgPath)) {
|
|
523
|
-
write("package.json", packageJsonTemplate(opts,
|
|
679
|
+
write("package.json", packageJsonTemplate(opts, deps))
|
|
524
680
|
} else {
|
|
525
|
-
|
|
681
|
+
try {
|
|
682
|
+
const existing = JSON.parse(readFileSync(pkgPath, "utf8")) as Record<string, unknown>
|
|
683
|
+
const merged = mergeSupatypePackageJson(existing, opts, deps)
|
|
684
|
+
writeFileSync(pkgPath, `${JSON.stringify(merged, null, 2)}\n`, "utf8")
|
|
685
|
+
file("updated", "package.json (added Supatype dependencies)")
|
|
686
|
+
} catch {
|
|
687
|
+
warn("Could not merge into package.json — add @supatype/cli and @supatype/types manually.")
|
|
688
|
+
file("skipped", "package.json (invalid JSON)")
|
|
689
|
+
}
|
|
526
690
|
}
|
|
527
691
|
|
|
528
692
|
write("supatype.config.ts", tsConfigTemplate(opts))
|
|
@@ -533,7 +697,7 @@ function scaffold(dir: string, optsOrName: ScaffoldOptions | string): void {
|
|
|
533
697
|
write(".env", envTemplate(opts))
|
|
534
698
|
write("seed.ts", seedTemplate(opts.projectName))
|
|
535
699
|
write("seeds/.gitkeep", "")
|
|
536
|
-
scaffoldAppAssets(opts, write)
|
|
700
|
+
scaffoldAppAssets(dir, opts, write)
|
|
537
701
|
|
|
538
702
|
if (opts.helloFunction) scaffoldHelloFunction(dir, write)
|
|
539
703
|
|
|
@@ -557,40 +721,63 @@ function staticDirRelative(staticDir?: string): string {
|
|
|
557
721
|
}
|
|
558
722
|
|
|
559
723
|
function scaffoldAppAssets(
|
|
724
|
+
dir: string,
|
|
560
725
|
opts: ScaffoldOptions,
|
|
561
726
|
write: (rel: string, content: string) => void,
|
|
562
727
|
): void {
|
|
728
|
+
const writeUnlessExists = (rel: string, content: string) => {
|
|
729
|
+
const full = join(dir, rel)
|
|
730
|
+
if (existsSync(full)) {
|
|
731
|
+
file("skipped", `${rel} (already exists)`)
|
|
732
|
+
return
|
|
733
|
+
}
|
|
734
|
+
write(rel, content)
|
|
735
|
+
}
|
|
736
|
+
|
|
563
737
|
const holding = holdingPageTemplate(opts.projectName)
|
|
564
738
|
const hasVite = Boolean(opts.app.viteDevUrl)
|
|
565
739
|
|
|
566
740
|
if (opts.app.mode === "static") {
|
|
567
741
|
const staticRel = staticDirRelative(opts.app.staticDir)
|
|
568
|
-
|
|
569
|
-
|
|
742
|
+
if (opts.app.viteDevUrl && opts.productionTarget !== "later") {
|
|
743
|
+
writeUnlessExists("dist/index.html", holding)
|
|
744
|
+
scaffoldVite(dir, opts, writeUnlessExists, holding)
|
|
745
|
+
return
|
|
746
|
+
}
|
|
747
|
+
writeUnlessExists(`${staticRel}/index.html`, holding)
|
|
748
|
+
if (opts.app.viteDevUrl) scaffoldVite(dir, opts, writeUnlessExists, holding)
|
|
570
749
|
return
|
|
571
750
|
}
|
|
572
751
|
|
|
573
752
|
if (opts.app.mode === "proxy" && opts.productionTarget !== "later") {
|
|
574
|
-
|
|
575
|
-
if (hasVite) scaffoldVite(opts,
|
|
753
|
+
writeUnlessExists("dist/index.html", holding)
|
|
754
|
+
if (hasVite) scaffoldVite(dir, opts, writeUnlessExists, holding)
|
|
576
755
|
return
|
|
577
756
|
}
|
|
578
757
|
|
|
579
758
|
if (opts.app.mode === "proxy" && hasVite) {
|
|
580
|
-
scaffoldVite(opts,
|
|
759
|
+
scaffoldVite(dir, opts, writeUnlessExists, holding)
|
|
581
760
|
return
|
|
582
761
|
}
|
|
583
762
|
|
|
584
|
-
|
|
763
|
+
writeUnlessExists("public/.gitkeep", "")
|
|
585
764
|
}
|
|
586
765
|
|
|
587
766
|
function scaffoldVite(
|
|
767
|
+
dir: string,
|
|
588
768
|
opts: ScaffoldOptions,
|
|
589
769
|
write: (rel: string, content: string) => void,
|
|
590
770
|
holding: string,
|
|
591
771
|
): void {
|
|
592
772
|
if (!opts.app.viteDevUrl) return
|
|
593
773
|
write("index.html", holding)
|
|
774
|
+
const viteConfigRel = ["vite.config.ts", "vite.config.js", "vite.config.mjs", "vite.config.cjs"].find(
|
|
775
|
+
(name) => existsSync(join(dir, name)),
|
|
776
|
+
)
|
|
777
|
+
if (viteConfigRel) {
|
|
778
|
+
file("skipped", `${viteConfigRel} (already exists)`)
|
|
779
|
+
return
|
|
780
|
+
}
|
|
594
781
|
write("vite.config.ts", viteConfigTemplate(opts.app.viteDevUrl))
|
|
595
782
|
}
|
|
596
783
|
|
|
@@ -609,7 +796,7 @@ function scaffoldHelloFunction(
|
|
|
609
796
|
|
|
610
797
|
// ─── Templates ───────────────────────────────────────────────────────────────
|
|
611
798
|
|
|
612
|
-
function packageJsonTemplate(opts: ScaffoldOptions,
|
|
799
|
+
function packageJsonTemplate(opts: ScaffoldOptions, deps: InitDependencyVersions): string {
|
|
613
800
|
const scripts: string[] = [
|
|
614
801
|
` "dev": "supatype dev"`,
|
|
615
802
|
` "push": "supatype push"`,
|
|
@@ -633,8 +820,8 @@ function packageJsonTemplate(opts: ScaffoldOptions, cliVersion: string): string
|
|
|
633
820
|
${scripts.join(",\n")}
|
|
634
821
|
},
|
|
635
822
|
"dependencies": {
|
|
636
|
-
"@supatype/cli": "^${
|
|
637
|
-
"@supatype/types": "^${
|
|
823
|
+
"@supatype/cli": "^${deps.cli}",
|
|
824
|
+
"@supatype/types": "^${deps.types}"
|
|
638
825
|
},
|
|
639
826
|
"devDependencies": {
|
|
640
827
|
${devDeps.join(",\n")}
|
|
@@ -701,20 +888,49 @@ function localConfigTemplate(app: ScaffoldAppOptions): string {
|
|
|
701
888
|
`const localConfig: Partial<SupatypeConfig> = {`,
|
|
702
889
|
` server: { mode: "dev" },`,
|
|
703
890
|
]
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
lines.push(
|
|
707
|
-
lines.push(` upstream: "${app.upstream ?? "http://localhost:3000"}",`)
|
|
708
|
-
lines.push(` start: "${app.start ?? "dev"}",`)
|
|
709
|
-
if (app.viteDevUrl) lines.push(` vite_dev_url: "${app.viteDevUrl}",`)
|
|
710
|
-
lines.push(` },`)
|
|
891
|
+
const localApp = localDevAppConfigLines(app)
|
|
892
|
+
if (localApp.length > 0) {
|
|
893
|
+
lines.push(...localApp)
|
|
711
894
|
}
|
|
712
895
|
lines.push(`}`, ``, `export default localConfig`, ``)
|
|
713
896
|
return lines.join("\n")
|
|
714
897
|
}
|
|
715
898
|
|
|
899
|
+
/** App block for supatype.local.config.ts (proxy + Vite during local dev). */
|
|
900
|
+
function localDevAppConfigLines(app: ScaffoldAppOptions): string[] {
|
|
901
|
+
if (app.mode === "proxy") {
|
|
902
|
+
return [
|
|
903
|
+
` app: {`,
|
|
904
|
+
` mode: "proxy",`,
|
|
905
|
+
` upstream: "${app.upstream ?? "http://localhost:3000"}",`,
|
|
906
|
+
` start: "${app.start ?? "dev"}",`,
|
|
907
|
+
...(app.viteDevUrl ? [` vite_dev_url: "${app.viteDevUrl}",`] : []),
|
|
908
|
+
` },`,
|
|
909
|
+
]
|
|
910
|
+
}
|
|
911
|
+
if (app.viteDevUrl) {
|
|
912
|
+
return [
|
|
913
|
+
` app: {`,
|
|
914
|
+
` mode: "proxy",`,
|
|
915
|
+
` upstream: "${app.viteDevUrl}",`,
|
|
916
|
+
` start: "vite",`,
|
|
917
|
+
` vite_dev_url: "${app.viteDevUrl}",`,
|
|
918
|
+
` },`,
|
|
919
|
+
]
|
|
920
|
+
}
|
|
921
|
+
return []
|
|
922
|
+
}
|
|
923
|
+
|
|
716
924
|
function appConfigLines(app: ScaffoldAppOptions, productionTarget: ProductionTarget): string[] {
|
|
717
925
|
if (app.mode === "static") {
|
|
926
|
+
if (productionTarget !== "later" && app.viteDevUrl) {
|
|
927
|
+
return [
|
|
928
|
+
` app: {`,
|
|
929
|
+
` mode: "static",`,
|
|
930
|
+
` static_dir: "./dist", // production build output`,
|
|
931
|
+
` },`,
|
|
932
|
+
]
|
|
933
|
+
}
|
|
718
934
|
const out = [
|
|
719
935
|
` app: {`,
|
|
720
936
|
` mode: "static",`,
|
|
@@ -774,7 +990,7 @@ function holdingPageTemplate(projectName: string): string {
|
|
|
774
990
|
<meta charset="UTF-8" />
|
|
775
991
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
776
992
|
<title>${name} — Supatype</title>
|
|
777
|
-
<meta name="description" content="A Supatype project. Define your types — we generate your
|
|
993
|
+
<meta name="description" content="A Supatype project. Define your types — we generate your platform." />
|
|
778
994
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
779
995
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
780
996
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
|
@@ -909,6 +1125,8 @@ export default defineConfig({
|
|
|
909
1125
|
host: "127.0.0.1",
|
|
910
1126
|
port: ${port},
|
|
911
1127
|
strictPort: true,
|
|
1128
|
+
// Docker Compose app proxy reaches the host as host.docker.internal.
|
|
1129
|
+
allowedHosts: ["127.0.0.1", "localhost", "host.docker.internal"],
|
|
912
1130
|
},
|
|
913
1131
|
})
|
|
914
1132
|
`
|
|
@@ -1185,27 +1403,35 @@ function printNextSteps(args: {
|
|
|
1185
1403
|
result: WizardResult
|
|
1186
1404
|
installed: boolean
|
|
1187
1405
|
keysGenerated: boolean
|
|
1406
|
+
binariesReady: boolean
|
|
1188
1407
|
}): void {
|
|
1189
|
-
const { name, result, installed, keysGenerated } = args
|
|
1408
|
+
const { name, result, installed, keysGenerated, binariesReady } = args
|
|
1409
|
+
const setupComplete = installed && keysGenerated && binariesReady
|
|
1190
1410
|
const steps: string[] = []
|
|
1191
1411
|
if (name) steps.push(`cd ${name}`)
|
|
1192
|
-
if (!installed) steps.push(`${result.packageManager} install`)
|
|
1193
|
-
if (!keysGenerated) steps.push("supatype keys")
|
|
1194
1412
|
const kongHint =
|
|
1195
1413
|
result.provider === "docker" && result.kongPort !== undefined
|
|
1196
1414
|
? `supatype dev # Docker Compose stack (Kong :${result.kongPort})`
|
|
1197
1415
|
: "supatype dev # Docker Compose stack (Kong :18473)"
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1416
|
+
if (setupComplete) {
|
|
1417
|
+
steps.push(kongHint)
|
|
1418
|
+
steps.push("supatype push # apply schema + generate types")
|
|
1419
|
+
if (result.adminEmail) {
|
|
1420
|
+
steps.push(" # first admin user is created on dev or push")
|
|
1421
|
+
}
|
|
1422
|
+
} else {
|
|
1423
|
+
if (!installed) steps.push(`${result.packageManager} install`)
|
|
1424
|
+
if (!binariesReady) steps.push("supatype update # download component binaries")
|
|
1425
|
+
if (!keysGenerated) steps.push("supatype keys")
|
|
1426
|
+
steps.push(kongHint)
|
|
1427
|
+
steps.push("supatype push # apply schema + generate types")
|
|
1202
1428
|
}
|
|
1203
1429
|
if (result.helloFunction) {
|
|
1204
1430
|
steps.push("supatype functions serve # run edge functions locally")
|
|
1205
1431
|
}
|
|
1206
1432
|
|
|
1207
1433
|
info(`Supatype project ready${name ? ` in ${name}/` : ""}.`)
|
|
1208
|
-
nextSteps("Next steps:", steps)
|
|
1434
|
+
nextSteps(setupComplete ? "Next steps:" : "Finish setup:", steps)
|
|
1209
1435
|
|
|
1210
1436
|
if (result.app.mode === "none") {
|
|
1211
1437
|
nextSteps("Static frontend (self-host):", [
|