@supatype/cli 0.1.0-alpha.9 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/.turbo/turbo-test.log +285 -69
- package/.turbo/turbo-typecheck.log +1 -1
- package/assets/supatype-logo-wordmark.ascii.txt +6 -0
- package/bin/dev-entry.ts +2 -1
- package/dist/app/framework.js +1 -3
- package/dist/app/framework.js.map +1 -1
- package/dist/app/proxy-dev-app.d.ts +14 -0
- package/dist/app/proxy-dev-app.d.ts.map +1 -1
- package/dist/app/proxy-dev-app.js +110 -6
- package/dist/app/proxy-dev-app.js.map +1 -1
- package/dist/app-config.d.ts +10 -0
- package/dist/app-config.d.ts.map +1 -1
- package/dist/app-config.js +72 -0
- package/dist/app-config.js.map +1 -1
- package/dist/assets/supatype-logo-wordmark.ascii.txt +6 -0
- package/dist/binary-cache.d.ts +19 -7
- package/dist/binary-cache.d.ts.map +1 -1
- package/dist/binary-cache.js +92 -46
- package/dist/binary-cache.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +17 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.d.ts +3 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +86 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/admin.d.ts +28 -1
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +297 -149
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/adopt.d.ts +3 -0
- package/dist/commands/adopt.d.ts.map +1 -0
- package/dist/commands/adopt.js +55 -0
- package/dist/commands/adopt.js.map +1 -0
- package/dist/commands/app.d.ts.map +1 -1
- package/dist/commands/app.js +20 -17
- package/dist/commands/app.js.map +1 -1
- package/dist/commands/cache.d.ts.map +1 -1
- package/dist/commands/cache.js +11 -10
- package/dist/commands/cache.js.map +1 -1
- package/dist/commands/cloud.d.ts +4 -9
- package/dist/commands/cloud.d.ts.map +1 -1
- package/dist/commands/cloud.js +75 -125
- package/dist/commands/cloud.js.map +1 -1
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +37 -58
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +140 -96
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +74 -39
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +39 -39
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +78 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/engine.d.ts.map +1 -1
- package/dist/commands/engine.js +5 -4
- package/dist/commands/engine.js.map +1 -1
- package/dist/commands/functions.d.ts.map +1 -1
- package/dist/commands/functions.js +172 -119
- package/dist/commands/functions.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +5 -4
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +35 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +883 -107
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/introspect.d.ts +3 -0
- package/dist/commands/introspect.d.ts.map +1 -0
- package/dist/commands/introspect.js +35 -0
- package/dist/commands/introspect.js.map +1 -0
- package/dist/commands/keys.d.ts +15 -1
- package/dist/commands/keys.d.ts.map +1 -1
- package/dist/commands/keys.js +46 -10
- package/dist/commands/keys.js.map +1 -1
- package/dist/commands/link-helpers.d.ts +15 -0
- package/dist/commands/link-helpers.d.ts.map +1 -0
- package/dist/commands/link-helpers.js +225 -0
- package/dist/commands/link-helpers.js.map +1 -0
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +5 -4
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/migrate-from-v1.d.ts.map +1 -1
- package/dist/commands/migrate-from-v1.js +3 -2
- package/dist/commands/migrate-from-v1.js.map +1 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +119 -26
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/pg.d.ts.map +1 -1
- package/dist/commands/pg.js +11 -12
- package/dist/commands/pg.js.map +1 -1
- package/dist/commands/plugins.d.ts.map +1 -1
- package/dist/commands/plugins.js +55 -46
- package/dist/commands/plugins.js.map +1 -1
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +33 -5
- package/dist/commands/pull.js.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +111 -138
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/seed.d.ts.map +1 -1
- package/dist/commands/seed.js +4 -3
- package/dist/commands/seed.js.map +1 -1
- package/dist/commands/self-host.d.ts +2 -2
- package/dist/commands/self-host.d.ts.map +1 -1
- package/dist/commands/self-host.js +65 -50
- package/dist/commands/self-host.js.map +1 -1
- package/dist/commands/self-update.d.ts.map +1 -1
- package/dist/commands/self-update.js +3 -2
- package/dist/commands/self-update.js.map +1 -1
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +95 -29
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js +3 -2
- package/dist/commands/types.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +54 -21
- package/dist/commands/update.js.map +1 -1
- package/dist/compose-rename.d.ts +10 -0
- package/dist/compose-rename.d.ts.map +1 -0
- package/dist/compose-rename.js +67 -0
- package/dist/compose-rename.js.map +1 -0
- package/dist/config.d.ts +2 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/dev-compose.d.ts +26 -0
- package/dist/dev-compose.d.ts.map +1 -1
- package/dist/dev-compose.js +357 -79
- package/dist/dev-compose.js.map +1 -1
- package/dist/dev-log-bus.d.ts +30 -0
- package/dist/dev-log-bus.d.ts.map +1 -0
- package/dist/dev-log-bus.js +87 -0
- package/dist/dev-log-bus.js.map +1 -0
- package/dist/dev-log-filter.d.ts +10 -0
- package/dist/dev-log-filter.d.ts.map +1 -0
- package/dist/dev-log-filter.js +36 -0
- package/dist/dev-log-filter.js.map +1 -0
- package/dist/dev-logo.d.ts +12 -0
- package/dist/dev-logo.d.ts.map +1 -0
- package/dist/dev-logo.js +56 -0
- package/dist/dev-logo.js.map +1 -0
- package/dist/dev-ports.d.ts +27 -0
- package/dist/dev-ports.d.ts.map +1 -0
- package/dist/dev-ports.js +171 -0
- package/dist/dev-ports.js.map +1 -0
- package/dist/dev-session-lock.d.ts +25 -0
- package/dist/dev-session-lock.d.ts.map +1 -0
- package/dist/dev-session-lock.js +81 -0
- package/dist/dev-session-lock.js.map +1 -0
- package/dist/dev-session.d.ts +26 -0
- package/dist/dev-session.d.ts.map +1 -0
- package/dist/dev-session.js +106 -0
- package/dist/dev-session.js.map +1 -0
- package/dist/dev-shutdown.d.ts +25 -0
- package/dist/dev-shutdown.d.ts.map +1 -0
- package/dist/dev-shutdown.js +114 -0
- package/dist/dev-shutdown.js.map +1 -0
- package/dist/dev-task-colors.d.ts +13 -0
- package/dist/dev-task-colors.d.ts.map +1 -0
- package/dist/dev-task-colors.js +43 -0
- package/dist/dev-task-colors.js.map +1 -0
- package/dist/dev-tui.d.ts +24 -0
- package/dist/dev-tui.d.ts.map +1 -0
- package/dist/dev-tui.js +188 -0
- package/dist/dev-tui.js.map +1 -0
- package/dist/diff-output.d.ts +5 -1
- package/dist/diff-output.d.ts.map +1 -1
- package/dist/diff-output.js +69 -0
- package/dist/diff-output.js.map +1 -1
- package/dist/docker-runtime.d.ts +30 -0
- package/dist/docker-runtime.d.ts.map +1 -0
- package/dist/docker-runtime.js +118 -0
- package/dist/docker-runtime.js.map +1 -0
- package/dist/engine-client.d.ts +10 -1
- package/dist/engine-client.d.ts.map +1 -1
- package/dist/engine-client.js +76 -17
- package/dist/engine-client.js.map +1 -1
- package/dist/engine-push-output.d.ts +17 -0
- package/dist/engine-push-output.d.ts.map +1 -0
- package/dist/engine-push-output.js +64 -0
- package/dist/engine-push-output.js.map +1 -0
- package/dist/ensure-binary.js +2 -2
- package/dist/ensure-binary.js.map +1 -1
- package/dist/env-file.d.ts +5 -0
- package/dist/env-file.d.ts.map +1 -0
- package/dist/env-file.js +33 -0
- package/dist/env-file.js.map +1 -0
- package/dist/gitignore.d.ts +8 -0
- package/dist/gitignore.d.ts.map +1 -0
- package/dist/gitignore.js +41 -0
- package/dist/gitignore.js.map +1 -0
- package/dist/kong-config.d.ts +9 -0
- package/dist/kong-config.d.ts.map +1 -1
- package/dist/kong-config.js +18 -1
- package/dist/kong-config.js.map +1 -1
- package/dist/link.d.ts +66 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +160 -0
- package/dist/link.js.map +1 -0
- package/dist/process-manager.d.ts +8 -0
- package/dist/process-manager.d.ts.map +1 -1
- package/dist/process-manager.js +53 -9
- package/dist/process-manager.js.map +1 -1
- package/dist/project-config.d.ts +30 -3
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +37 -4
- package/dist/project-config.js.map +1 -1
- package/dist/prompts.d.ts +3 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +3 -0
- package/dist/prompts.js.map +1 -0
- package/dist/pull-utils.d.ts +50 -14
- package/dist/pull-utils.d.ts.map +1 -1
- package/dist/pull-utils.js +152 -12
- package/dist/pull-utils.js.map +1 -1
- package/dist/resolve-target.d.ts +86 -0
- package/dist/resolve-target.d.ts.map +1 -0
- package/dist/resolve-target.js +291 -0
- package/dist/resolve-target.js.map +1 -0
- package/dist/restore-system-relation-targets.d.ts +3 -0
- package/dist/restore-system-relation-targets.d.ts.map +1 -0
- package/dist/restore-system-relation-targets.js +45 -0
- package/dist/restore-system-relation-targets.js.map +1 -0
- package/dist/runtime-routes.d.ts.map +1 -1
- package/dist/runtime-routes.js +7 -0
- package/dist/runtime-routes.js.map +1 -1
- package/dist/schema-ast-v2.d.ts +1 -1
- package/dist/schema-ast-v2.d.ts.map +1 -1
- package/dist/schema-ast-v2.js +2 -2
- package/dist/schema-ast-v2.js.map +1 -1
- package/dist/schema-sources.d.ts +40 -0
- package/dist/schema-sources.d.ts.map +1 -0
- package/dist/schema-sources.js +183 -0
- package/dist/schema-sources.js.map +1 -0
- package/dist/scripts/postinstall.js +5 -1
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/self-host-compose.d.ts +37 -1
- package/dist/self-host-compose.d.ts.map +1 -1
- package/dist/self-host-compose.js +234 -43
- package/dist/self-host-compose.js.map +1 -1
- package/dist/storage-provision.d.ts +4 -0
- package/dist/storage-provision.d.ts.map +1 -1
- package/dist/storage-provision.js +24 -2
- package/dist/storage-provision.js.map +1 -1
- package/dist/supatype-eval-1781522769253.d.mts +2 -0
- package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
- package/dist/supatype-eval-1781522769253.mjs +3 -0
- package/dist/supatype-eval-1781522769253.mjs.map +1 -0
- package/dist/systemd.js +2 -2
- package/dist/systemd.js.map +1 -1
- package/dist/target-client.d.ts +10 -0
- package/dist/target-client.d.ts.map +1 -0
- package/dist/target-client.js +22 -0
- package/dist/target-client.js.map +1 -0
- package/dist/type-extractor.d.ts +11 -0
- package/dist/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor.js +95 -8
- package/dist/type-extractor.js.map +1 -1
- package/dist/ui/brand.d.ts +9 -0
- package/dist/ui/brand.d.ts.map +1 -0
- package/dist/ui/brand.js +11 -0
- package/dist/ui/brand.js.map +1 -0
- package/dist/ui/confirm.d.ts +12 -0
- package/dist/ui/confirm.d.ts.map +1 -0
- package/dist/ui/confirm.js +28 -0
- package/dist/ui/confirm.js.map +1 -0
- package/dist/ui/fatal.d.ts +10 -0
- package/dist/ui/fatal.d.ts.map +1 -0
- package/dist/ui/fatal.js +34 -0
- package/dist/ui/fatal.js.map +1 -0
- package/dist/ui/index.d.ts +9 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +9 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/interactive.d.ts +3 -0
- package/dist/ui/interactive.d.ts.map +1 -0
- package/dist/ui/interactive.js +5 -0
- package/dist/ui/interactive.js.map +1 -0
- package/dist/ui/messages.d.ts +10 -0
- package/dist/ui/messages.d.ts.map +1 -0
- package/dist/ui/messages.js +35 -0
- package/dist/ui/messages.js.map +1 -0
- package/dist/ui/next-steps.d.ts +3 -0
- package/dist/ui/next-steps.d.ts.map +1 -0
- package/dist/ui/next-steps.js +10 -0
- package/dist/ui/next-steps.js.map +1 -0
- package/dist/ui/progress.d.ts +5 -0
- package/dist/ui/progress.d.ts.map +1 -0
- package/dist/ui/progress.js +24 -0
- package/dist/ui/progress.js.map +1 -0
- package/dist/ui/prompts.d.ts +14 -0
- package/dist/ui/prompts.d.ts.map +1 -0
- package/dist/ui/prompts.js +34 -0
- package/dist/ui/prompts.js.map +1 -0
- package/package.json +5 -2
- package/src/app/framework.ts +1 -3
- package/src/app/proxy-dev-app.ts +114 -6
- package/src/app-config.ts +80 -0
- package/src/binary-cache.ts +102 -52
- package/src/cli.ts +16 -2
- package/src/commands/add.ts +97 -0
- package/src/commands/admin.ts +381 -190
- package/src/commands/adopt.ts +82 -0
- package/src/commands/app.ts +20 -17
- package/src/commands/cache.ts +11 -10
- package/src/commands/cloud.ts +91 -142
- package/src/commands/db.ts +40 -63
- package/src/commands/deploy.ts +186 -126
- package/src/commands/dev.ts +98 -55
- package/src/commands/diff.ts +52 -43
- package/src/commands/doctor.ts +103 -0
- package/src/commands/engine.ts +5 -4
- package/src/commands/functions.ts +187 -123
- package/src/commands/generate.ts +5 -4
- package/src/commands/init.ts +1087 -104
- package/src/commands/introspect.ts +48 -0
- package/src/commands/keys.ts +56 -14
- package/src/commands/link-helpers.ts +273 -0
- package/src/commands/logs.ts +5 -4
- package/src/commands/migrate-from-v1.ts +3 -2
- package/src/commands/migrate.ts +167 -27
- package/src/commands/pg.ts +13 -18
- package/src/commands/plugins.ts +55 -46
- package/src/commands/pull.ts +38 -9
- package/src/commands/push.ts +148 -175
- package/src/commands/seed.ts +5 -4
- package/src/commands/self-host.ts +85 -54
- package/src/commands/self-update.ts +3 -2
- package/src/commands/status.ts +102 -33
- package/src/commands/types.ts +3 -2
- package/src/commands/update.ts +59 -23
- package/src/compose-rename.ts +76 -0
- package/src/config.ts +2 -1
- package/src/dev-compose.ts +462 -76
- package/src/dev-log-bus.ts +101 -0
- package/src/dev-log-filter.ts +32 -0
- package/src/dev-logo.ts +61 -0
- package/src/dev-ports.ts +212 -0
- package/src/dev-session-lock.ts +101 -0
- package/src/dev-session.ts +130 -0
- package/src/dev-shutdown.ts +147 -0
- package/src/dev-task-colors.ts +47 -0
- package/src/dev-tui.ts +232 -0
- package/src/diff-output.ts +79 -1
- package/src/docker-runtime.ts +151 -0
- package/src/engine-client.ts +81 -17
- package/src/engine-push-output.ts +75 -0
- package/src/ensure-binary.ts +2 -2
- package/src/env-file.ts +37 -0
- package/src/gitignore.ts +48 -0
- package/src/kong-config.ts +24 -1
- package/src/link.ts +243 -0
- package/src/process-manager.ts +66 -10
- package/src/project-config.ts +62 -7
- package/src/prompts.ts +2 -0
- package/src/pull-utils.ts +217 -23
- package/src/resolve-target.ts +419 -0
- package/src/restore-system-relation-targets.ts +45 -0
- package/src/runtime-routes.ts +7 -0
- package/src/schema-ast-v2.ts +2 -1
- package/src/schema-sources.ts +248 -0
- package/src/scripts/postinstall.ts +7 -1
- package/src/self-host-compose.ts +262 -46
- package/src/storage-provision.ts +33 -1
- package/src/supatype-eval-1781522769253.mts +1 -0
- package/src/systemd.ts +2 -2
- package/src/target-client.ts +40 -0
- package/src/type-extractor.ts +124 -11
- package/src/ui/README.md +17 -0
- package/src/ui/brand.ts +12 -0
- package/src/ui/confirm.ts +38 -0
- package/src/ui/fatal.ts +43 -0
- package/src/ui/index.ts +8 -0
- package/src/ui/interactive.ts +4 -0
- package/src/ui/messages.ts +43 -0
- package/src/ui/next-steps.ts +10 -0
- package/src/ui/progress.ts +28 -0
- package/src/ui/prompts.ts +40 -0
- package/tests/admin-ensure.test.ts +59 -0
- package/tests/cli-help.test.ts +27 -2
- package/tests/config.test.ts +29 -2
- package/tests/dev-ports.test.ts +41 -0
- package/tests/dev-session-lock.test.ts +54 -0
- package/tests/dev-ui.test.ts +162 -0
- package/tests/docker-runtime.test.ts +236 -0
- package/tests/engine-push-output.test.ts +67 -0
- package/tests/init.test.ts +197 -18
- package/tests/link.test.ts +148 -0
- package/tests/minisign.test.ts +102 -0
- package/tests/proxy-dev-app.test.ts +45 -1
- package/tests/pull-utils.test.ts +5 -4
- package/tests/runtime-contract.test.ts +186 -2
- package/tests/schema-sources.test.ts +119 -0
- package/tests/storage-provision.test.ts +100 -0
- package/tests/ui-confirm.test.ts +41 -0
- package/tests/ui-messages.test.ts +66 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-task log buffers for `supatype dev` TUI mode.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type DevLogLevel = "log" | "warn" | "error"
|
|
6
|
+
|
|
7
|
+
export interface DevTask {
|
|
8
|
+
id: string
|
|
9
|
+
title: string
|
|
10
|
+
lines: string[]
|
|
11
|
+
unread: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const DEFAULT_MAX_LINES = 5_000
|
|
15
|
+
|
|
16
|
+
export class DevLogBus {
|
|
17
|
+
private readonly tasks = new Map<string, DevTask>()
|
|
18
|
+
private readonly order: string[] = []
|
|
19
|
+
private readonly listeners = new Set<() => void>()
|
|
20
|
+
private focusedTaskId = "stack"
|
|
21
|
+
|
|
22
|
+
constructor(private readonly maxLines = DEFAULT_MAX_LINES) {
|
|
23
|
+
this.registerTask("stack", "supatype")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
registerTask(id: string, title: string): void {
|
|
27
|
+
if (this.tasks.has(id)) return
|
|
28
|
+
this.tasks.set(id, { id, title, lines: [], unread: false })
|
|
29
|
+
this.order.push(id)
|
|
30
|
+
this.notify()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
ensureTask(id: string, title = id): void {
|
|
34
|
+
this.registerTask(id, title)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getTaskOrder(): readonly string[] {
|
|
38
|
+
return this.order
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getTask(id: string): DevTask | undefined {
|
|
42
|
+
return this.tasks.get(id)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getFocusedTaskId(): string {
|
|
46
|
+
return this.focusedTaskId
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
setFocusedTaskId(id: string): void {
|
|
50
|
+
if (!this.tasks.has(id)) return
|
|
51
|
+
this.focusedTaskId = id
|
|
52
|
+
const task = this.tasks.get(id)
|
|
53
|
+
if (task) task.unread = false
|
|
54
|
+
this.notify()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
focusNext(): void {
|
|
58
|
+
const idx = this.order.indexOf(this.focusedTaskId)
|
|
59
|
+
const next = this.order[(idx + 1) % this.order.length]
|
|
60
|
+
if (next) this.setFocusedTaskId(next)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
focusPrevious(): void {
|
|
64
|
+
const idx = this.order.indexOf(this.focusedTaskId)
|
|
65
|
+
const prev = this.order[(idx - 1 + this.order.length) % this.order.length]
|
|
66
|
+
if (prev) this.setFocusedTaskId(prev)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
append(taskId: string, line: string, level: DevLogLevel = "log"): void {
|
|
70
|
+
this.ensureTask(taskId, taskId)
|
|
71
|
+
const task = this.tasks.get(taskId)
|
|
72
|
+
if (!task) return
|
|
73
|
+
|
|
74
|
+
const levelPrefix = level === "warn" ? "⚠ " : level === "error" ? "✗ " : ""
|
|
75
|
+
const parts = line.split(/\r?\n/)
|
|
76
|
+
|
|
77
|
+
for (const part of parts) {
|
|
78
|
+
if (part.length === 0) continue
|
|
79
|
+
const text = taskId === "stack" ? part.replace(/^\[supatype\]\s*/, "") : part
|
|
80
|
+
if (text.length === 0) continue
|
|
81
|
+
task.lines.push(levelPrefix + text)
|
|
82
|
+
if (task.lines.length > this.maxLines) {
|
|
83
|
+
task.lines.splice(0, task.lines.length - this.maxLines)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (taskId !== this.focusedTaskId && parts.some((p) => p.length > 0)) {
|
|
88
|
+
task.unread = true
|
|
89
|
+
}
|
|
90
|
+
this.notify()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
onUpdate(listener: () => void): () => void {
|
|
94
|
+
this.listeners.add(listener)
|
|
95
|
+
return () => this.listeners.delete(listener)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private notify(): void {
|
|
99
|
+
for (const listener of this.listeners) listener()
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Line filters for subprocess output during `supatype dev`.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Strip ANSI SGR sequences for TUI rendering. */
|
|
6
|
+
export function stripAnsi(text: string): string {
|
|
7
|
+
return text.replace(/\x1b\[[0-9;]*m/g, "")
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Drop npm/vite noise from frontend dev servers (proxy app + Studio). */
|
|
11
|
+
export function filterDevSubprocessLine(taskId: string, line: string): boolean {
|
|
12
|
+
if (taskId !== "app" && taskId !== "studio") return true
|
|
13
|
+
const trimmed = stripAnsi(line).trim()
|
|
14
|
+
if (!trimmed) return false
|
|
15
|
+
if (/^>\s+\S/.test(trimmed)) return false
|
|
16
|
+
if (trimmed.includes("Network:") && trimmed.includes("➜")) return false
|
|
17
|
+
return true
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Format console.* arguments the way Node does for a single log line. */
|
|
21
|
+
export function formatConsoleArgs(args: unknown[]): string {
|
|
22
|
+
return args
|
|
23
|
+
.map((arg) => {
|
|
24
|
+
if (typeof arg === "string") return arg
|
|
25
|
+
try {
|
|
26
|
+
return JSON.stringify(arg)
|
|
27
|
+
} catch {
|
|
28
|
+
return String(arg)
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
.join(" ")
|
|
32
|
+
}
|
package/src/dev-logo.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supatype ASCII wordmark for the dev TUI header.
|
|
3
|
+
* Baked from `figlet -f slant supatype` → assets/supatype-logo-wordmark.ascii.txt
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
7
|
+
import { dirname, join } from "node:path"
|
|
8
|
+
import { fileURLToPath } from "node:url"
|
|
9
|
+
import { BOLD, BRAND_COLOR, RESET } from "./ui/brand.js"
|
|
10
|
+
|
|
11
|
+
const EMBEDDED_LOGO_WORDMARK = [
|
|
12
|
+
" __ ",
|
|
13
|
+
" _______ ______ ____ _/ /___ ______ ___ ",
|
|
14
|
+
" / ___/ / / / __ \\/ __ `/ __/ / / / __ \\/ _ \\",
|
|
15
|
+
" (__ ) /_/ / /_/ / /_/ / /_/ /_/ / /_/ / __/",
|
|
16
|
+
"/____/\\__,_/ .___/\\__,_/\\__/\\__, / .___/\\___/ ",
|
|
17
|
+
" /_/ /____/_/ ",
|
|
18
|
+
] as const
|
|
19
|
+
|
|
20
|
+
function readLogoFile(basename: string): readonly string[] | null {
|
|
21
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
22
|
+
const candidates = [
|
|
23
|
+
join(here, "assets", basename),
|
|
24
|
+
join(here, "..", "assets", basename),
|
|
25
|
+
]
|
|
26
|
+
for (const path of candidates) {
|
|
27
|
+
if (!existsSync(path)) continue
|
|
28
|
+
const lines = readFileSync(path, "utf8")
|
|
29
|
+
.split(/\r?\n/)
|
|
30
|
+
.map((line) => (line.endsWith("\r") ? line.slice(0, -1) : line))
|
|
31
|
+
.filter((line) => line.length > 0)
|
|
32
|
+
if (lines.length > 0) return lines
|
|
33
|
+
}
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function loadWordmarkLogoLines(): readonly string[] {
|
|
38
|
+
const fromFile = readLogoFile("supatype-logo-wordmark.ascii.txt")
|
|
39
|
+
if (fromFile) return fromFile
|
|
40
|
+
return [...EMBEDDED_LOGO_WORDMARK]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Figlet slant wordmark (~46×6). */
|
|
44
|
+
export const SUPATYPE_ASCII_LOGO_WORDMARK = loadWordmarkLogoLines()
|
|
45
|
+
|
|
46
|
+
export function pickLogoLines(): readonly string[] {
|
|
47
|
+
return SUPATYPE_ASCII_LOGO_WORDMARK
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function logoRowCount(): number {
|
|
51
|
+
return pickLogoLines().length
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Return logo rows exactly as authored (spaces preserved). */
|
|
55
|
+
export function layoutLogoBlock(lines: readonly string[]): string[] {
|
|
56
|
+
return [...lines]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function colorLogoLines(lines: readonly string[]): string[] {
|
|
60
|
+
return lines.map((line) => `${BRAND_COLOR}${BOLD}${line}${RESET}`)
|
|
61
|
+
}
|
package/src/dev-ports.ts
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kong / Postgres host port resolution for local Docker dev.
|
|
3
|
+
* Persists SUPATYPE_KONG_PORT in `.env` so re-runs are stable, but re-checks
|
|
4
|
+
* availability so multiple projects and port collisions are surfaced clearly.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as p from "@clack/prompts"
|
|
8
|
+
import { COMPOSE_DEV_KONG_PORT } from "./project-config.js"
|
|
9
|
+
import { isPortInUse } from "./postgres-ctl.js"
|
|
10
|
+
import { readEnvInt, upsertEnvFile } from "./env-file.js"
|
|
11
|
+
import { isInteractive } from "./ui/interactive.js"
|
|
12
|
+
import { fatalError } from "./ui/fatal.js"
|
|
13
|
+
|
|
14
|
+
const MIN_PORT = 1024
|
|
15
|
+
const MAX_PORT = 65535
|
|
16
|
+
|
|
17
|
+
export function isValidHostPort(port: number): boolean {
|
|
18
|
+
return Number.isInteger(port) && port >= MIN_PORT && port <= MAX_PORT
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function parseHostPortInput(raw: string): number | null {
|
|
22
|
+
const port = Number(raw.trim())
|
|
23
|
+
return isValidHostPort(port) ? port : null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Next free TCP port on 127.0.0.1 starting at `start`. */
|
|
27
|
+
export async function findNextFreePort(start: number): Promise<number> {
|
|
28
|
+
let port = Math.max(MIN_PORT, start)
|
|
29
|
+
while (port <= MAX_PORT && (await isPortInUse(port))) port++
|
|
30
|
+
if (port > MAX_PORT) {
|
|
31
|
+
throw new Error(`No free local port found in range ${start}–${MAX_PORT}.`)
|
|
32
|
+
}
|
|
33
|
+
return port
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function readPersistedKongPort(cwd: string): number | null {
|
|
37
|
+
return readEnvInt(cwd, "SUPATYPE_KONG_PORT")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function persistKongPort(cwd: string, port: number): void {
|
|
41
|
+
const apiUrl = `http://localhost:${port}`
|
|
42
|
+
upsertEnvFile(cwd, {
|
|
43
|
+
SUPATYPE_KONG_PORT: String(port),
|
|
44
|
+
PUBLIC_SUPATYPE_URL: apiUrl,
|
|
45
|
+
API_EXTERNAL_URL: apiUrl,
|
|
46
|
+
SITE_URL: apiUrl,
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function promptPortConflict(
|
|
51
|
+
cwd: string,
|
|
52
|
+
blockedPort: number,
|
|
53
|
+
reason: "in_use" | "init",
|
|
54
|
+
): Promise<number> {
|
|
55
|
+
const port = await promptPortConflictWithoutPersist(blockedPort, reason)
|
|
56
|
+
persistKongPort(cwd, port)
|
|
57
|
+
return port
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface EnsureKongPortOptions {
|
|
61
|
+
/** When false, fail fast instead of prompting (CI / scripts). */
|
|
62
|
+
interactive?: boolean
|
|
63
|
+
/** init wizard — slightly different copy */
|
|
64
|
+
context?: "dev" | "init"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Resolve the Kong host port for this project directory.
|
|
69
|
+
* - Uses `.env` when set and available.
|
|
70
|
+
* - Prompts (or errors) when the configured port is taken.
|
|
71
|
+
* - Auto-picks the next free port on first run when unset.
|
|
72
|
+
*/
|
|
73
|
+
export async function ensureKongPort(
|
|
74
|
+
cwd: string,
|
|
75
|
+
opts: EnsureKongPortOptions = {},
|
|
76
|
+
): Promise<number> {
|
|
77
|
+
const interactive = opts.interactive ?? isInteractive()
|
|
78
|
+
const context = opts.context ?? "dev"
|
|
79
|
+
const persisted = readPersistedKongPort(cwd)
|
|
80
|
+
|
|
81
|
+
if (persisted !== null) {
|
|
82
|
+
if (!(await isPortInUse(persisted))) return persisted
|
|
83
|
+
|
|
84
|
+
if (!interactive) {
|
|
85
|
+
fatalError(`Port ${persisted} is already in use (SUPATYPE_KONG_PORT in .env).`, [
|
|
86
|
+
"Stop the other service or set a different SUPATYPE_KONG_PORT.",
|
|
87
|
+
"Run `supatype dev` in a terminal to pick a new port interactively.",
|
|
88
|
+
])
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return promptPortConflict(cwd, persisted, "in_use")
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const preferred = COMPOSE_DEV_KONG_PORT
|
|
95
|
+
const port =
|
|
96
|
+
(await isPortInUse(preferred))
|
|
97
|
+
? interactive && context === "init"
|
|
98
|
+
? await promptPortConflict(cwd, preferred, "init")
|
|
99
|
+
: await findNextFreePort(preferred)
|
|
100
|
+
: preferred
|
|
101
|
+
|
|
102
|
+
return port
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Wizard / init — pick a Kong port without writing `.env` (scaffold writes it). */
|
|
106
|
+
export async function promptKongPortChoice(): Promise<number> {
|
|
107
|
+
const freeDefault = (await isPortInUse(COMPOSE_DEV_KONG_PORT))
|
|
108
|
+
? await findNextFreePort(COMPOSE_DEV_KONG_PORT)
|
|
109
|
+
: COMPOSE_DEV_KONG_PORT
|
|
110
|
+
|
|
111
|
+
const value = await p.text({
|
|
112
|
+
message: "Local API gateway port (Kong)",
|
|
113
|
+
defaultValue: String(freeDefault),
|
|
114
|
+
placeholder: String(COMPOSE_DEV_KONG_PORT),
|
|
115
|
+
validate: (raw) => {
|
|
116
|
+
const port = parseHostPortInput(raw ?? "")
|
|
117
|
+
if (!port) return `Enter a port between ${MIN_PORT} and ${MAX_PORT}.`
|
|
118
|
+
return undefined
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
if (p.isCancel(value)) {
|
|
123
|
+
p.cancel("Cancelled.")
|
|
124
|
+
process.exit(0)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const port = parseHostPortInput(value)!
|
|
128
|
+
if (await isPortInUse(port)) {
|
|
129
|
+
return promptPortConflictWithoutPersist(port, "init")
|
|
130
|
+
}
|
|
131
|
+
return port
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function promptPortConflictWithoutPersist(
|
|
135
|
+
blockedPort: number,
|
|
136
|
+
reason: "in_use" | "init",
|
|
137
|
+
): Promise<number> {
|
|
138
|
+
const suggested = await findNextFreePort(blockedPort + 1)
|
|
139
|
+
const headline =
|
|
140
|
+
reason === "init"
|
|
141
|
+
? `Port ${blockedPort} is already in use on this machine.`
|
|
142
|
+
: `Port ${blockedPort} is in use — another Supatype project or service may already be bound to it.`
|
|
143
|
+
|
|
144
|
+
const choice = await p.select<"suggested" | "custom" | "cancel">({
|
|
145
|
+
message: headline,
|
|
146
|
+
options: [
|
|
147
|
+
{
|
|
148
|
+
value: "suggested",
|
|
149
|
+
label: `Use ${suggested} instead`,
|
|
150
|
+
hint: "next available port",
|
|
151
|
+
},
|
|
152
|
+
{ value: "custom", label: "Enter a different port" },
|
|
153
|
+
{ value: "cancel", label: "Cancel" },
|
|
154
|
+
],
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
if (p.isCancel(choice) || choice === "cancel") {
|
|
158
|
+
p.cancel("Cancelled.")
|
|
159
|
+
process.exit(0)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (choice === "suggested") return suggested
|
|
163
|
+
|
|
164
|
+
const custom = await p.text({
|
|
165
|
+
message: "Local API gateway port (Kong)",
|
|
166
|
+
defaultValue: String(suggested),
|
|
167
|
+
validate: (value) => {
|
|
168
|
+
const port = parseHostPortInput(value ?? "")
|
|
169
|
+
if (!port) return `Enter a port between ${MIN_PORT} and ${MAX_PORT}.`
|
|
170
|
+
return undefined
|
|
171
|
+
},
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
if (p.isCancel(custom)) {
|
|
175
|
+
p.cancel("Cancelled.")
|
|
176
|
+
process.exit(0)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const port = parseHostPortInput(custom)
|
|
180
|
+
if (!port) {
|
|
181
|
+
p.cancel("Invalid port.")
|
|
182
|
+
process.exit(1)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (await isPortInUse(port)) {
|
|
186
|
+
fatalError(`Port ${port} is still in use.`, [
|
|
187
|
+
"Pick another port or stop the service using it.",
|
|
188
|
+
"Check Docker Desktop for other Supatype stacks.",
|
|
189
|
+
])
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return port
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const COMPOSE_DEV_DB_PORT = 54329
|
|
196
|
+
|
|
197
|
+
export async function ensureDevDbPort(cwd: string): Promise<number> {
|
|
198
|
+
const persisted = readEnvInt(cwd, "SUPATYPE_DEV_DB_PORT")
|
|
199
|
+
if (persisted !== null) {
|
|
200
|
+
if (!(await isPortInUse(persisted))) return persisted
|
|
201
|
+
const next = await findNextFreePort(persisted + 1)
|
|
202
|
+
upsertEnvFile(cwd, {
|
|
203
|
+
SUPATYPE_DEV_DB_PORT: String(next),
|
|
204
|
+
DATABASE_URL: `postgresql://supatype_admin:postgres@localhost:${next}/supatype?sslmode=disable`,
|
|
205
|
+
})
|
|
206
|
+
return next
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let port = COMPOSE_DEV_DB_PORT
|
|
210
|
+
while (await isPortInUse(port)) port++
|
|
211
|
+
return port
|
|
212
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks an active `supatype dev` session so we can recover when the CLI exits
|
|
3
|
+
* without graceful shutdown (terminal closed, kill signal, etc.).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs"
|
|
7
|
+
import { join, resolve } from "node:path"
|
|
8
|
+
import { spawnSync } from "node:child_process"
|
|
9
|
+
import * as p from "@clack/prompts"
|
|
10
|
+
import { runDockerCompose } from "./self-host-compose.js"
|
|
11
|
+
import { isInteractive } from "./ui/interactive.js"
|
|
12
|
+
import { warn } from "./ui/messages.js"
|
|
13
|
+
|
|
14
|
+
const LOCK_VERSION = 1 as const
|
|
15
|
+
|
|
16
|
+
export interface DevSessionLock {
|
|
17
|
+
version: typeof LOCK_VERSION
|
|
18
|
+
composeProject: string
|
|
19
|
+
projectRef: string
|
|
20
|
+
composePath: string
|
|
21
|
+
kongPort: number
|
|
22
|
+
startedAt: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function devSessionLockPath(cwd: string): string {
|
|
26
|
+
return resolve(cwd, ".supatype/dev-session.json")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function readDevSessionLock(cwd: string): DevSessionLock | null {
|
|
30
|
+
const path = devSessionLockPath(cwd)
|
|
31
|
+
if (!existsSync(path)) return null
|
|
32
|
+
try {
|
|
33
|
+
const data = JSON.parse(readFileSync(path, "utf8")) as DevSessionLock
|
|
34
|
+
if (data.version !== LOCK_VERSION) return null
|
|
35
|
+
return data
|
|
36
|
+
} catch {
|
|
37
|
+
return null
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function writeDevSessionLock(cwd: string, lock: Omit<DevSessionLock, "version">): void {
|
|
42
|
+
const dir = join(cwd, ".supatype")
|
|
43
|
+
mkdirSync(dir, { recursive: true })
|
|
44
|
+
const payload: DevSessionLock = { version: LOCK_VERSION, ...lock }
|
|
45
|
+
writeFileSync(devSessionLockPath(cwd), `${JSON.stringify(payload, null, 2)}\n`, "utf8")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function clearDevSessionLock(cwd: string): void {
|
|
49
|
+
const path = devSessionLockPath(cwd)
|
|
50
|
+
if (existsSync(path)) unlinkSync(path)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function composeStackHasContainers(composeProject: string): boolean {
|
|
54
|
+
const result = spawnSync(
|
|
55
|
+
"docker",
|
|
56
|
+
["ps", "-a", "--filter", `label=com.docker.compose.project=${composeProject}`, "--format", "{{.Names}}"],
|
|
57
|
+
{ encoding: "utf8", shell: process.platform === "win32" },
|
|
58
|
+
)
|
|
59
|
+
return Boolean(result.stdout?.trim())
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* When a previous dev session did not shut down cleanly, offer to stop its stack
|
|
64
|
+
* before starting a new one.
|
|
65
|
+
*/
|
|
66
|
+
export async function recoverStaleDevSession(cwd: string): Promise<void> {
|
|
67
|
+
const lock = readDevSessionLock(cwd)
|
|
68
|
+
if (!lock) return
|
|
69
|
+
if (!composeStackHasContainers(lock.composeProject)) {
|
|
70
|
+
clearDevSessionLock(cwd)
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const message =
|
|
75
|
+
`Previous dev session for "${lock.projectRef}" may not have shut down cleanly ` +
|
|
76
|
+
`(stack "${lock.composeProject}" is still running).`
|
|
77
|
+
|
|
78
|
+
if (!isInteractive()) {
|
|
79
|
+
warn(message)
|
|
80
|
+
warn(`Stop it manually: docker compose -p ${lock.composeProject} down`)
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const stop = await p.confirm({
|
|
85
|
+
message: `${message}\n\nStop the orphaned stack before starting?`,
|
|
86
|
+
initialValue: true,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
if (p.isCancel(stop) || !stop) {
|
|
90
|
+
warn(`Leaving "${lock.composeProject}" running.`)
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const status = runDockerCompose(lock.composePath, ["down"], cwd, lock.composeProject, { quiet: true })
|
|
95
|
+
if (status === 0) {
|
|
96
|
+
clearDevSessionLock(cwd)
|
|
97
|
+
p.log.success(`Stopped orphaned stack "${lock.composeProject}".`)
|
|
98
|
+
} else {
|
|
99
|
+
warn(`Could not stop "${lock.composeProject}" (exit ${status}).`)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `supatype dev` output session — TUI (default) or interleaved stream mode.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ProcessOptions } from "./process-manager.js"
|
|
6
|
+
import { filterDevSubprocessLine, formatConsoleArgs } from "./dev-log-filter.js"
|
|
7
|
+
import { DevLogBus, type DevLogLevel } from "./dev-log-bus.js"
|
|
8
|
+
import { DevTui } from "./dev-tui.js"
|
|
9
|
+
|
|
10
|
+
export type DevUiMode = "tui" | "stream"
|
|
11
|
+
|
|
12
|
+
export function resolveDevUiMode(streamFlag: boolean): DevUiMode {
|
|
13
|
+
if (streamFlag) return "stream"
|
|
14
|
+
if (!process.stdout.isTTY || !process.stdin.isTTY) return "stream"
|
|
15
|
+
return "tui"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let activeSession: DevSession | null = null
|
|
19
|
+
|
|
20
|
+
export function beginDevSession(mode: DevUiMode): DevSession {
|
|
21
|
+
activeSession?.stop()
|
|
22
|
+
activeSession = new DevSession(mode)
|
|
23
|
+
return activeSession
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Enter console capture + TUI. */
|
|
27
|
+
export function startDevSession(): void {
|
|
28
|
+
activeSession?.start()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function endDevSession(): void {
|
|
32
|
+
activeSession?.stop()
|
|
33
|
+
activeSession = null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getActiveDevSession(): DevSession | null {
|
|
37
|
+
return activeSession
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Log to a TUI task stream, or prefixed console output in stream mode. */
|
|
41
|
+
export function appendDevTaskLog(
|
|
42
|
+
taskId: string,
|
|
43
|
+
taskTitle: string,
|
|
44
|
+
line: string,
|
|
45
|
+
level: DevLogLevel = "log",
|
|
46
|
+
): void {
|
|
47
|
+
const session = getActiveDevSession()
|
|
48
|
+
if (session?.isTui()) {
|
|
49
|
+
session.bus.ensureTask(taskId, taskTitle)
|
|
50
|
+
session.bus.append(taskId, line, level)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
const prefix = taskId === "stack" ? "[supatype]" : `[${taskId}]`
|
|
54
|
+
const write = level === "warn" ? console.warn : level === "error" ? console.error : console.log
|
|
55
|
+
write(`${prefix} ${line}`)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class DevSession {
|
|
59
|
+
readonly bus = new DevLogBus()
|
|
60
|
+
readonly mode: DevUiMode
|
|
61
|
+
private tui: DevTui | null = null
|
|
62
|
+
private restoreConsole: (() => void) | null = null
|
|
63
|
+
|
|
64
|
+
constructor(mode: DevUiMode) {
|
|
65
|
+
this.mode = mode
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
isTui(): boolean {
|
|
69
|
+
return this.mode === "tui"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
start(): void {
|
|
73
|
+
if (!this.isTui()) return
|
|
74
|
+
this.restoreConsole = patchConsole(this.bus)
|
|
75
|
+
this.tui = new DevTui(this.bus)
|
|
76
|
+
this.tui.start()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
stop(): void {
|
|
80
|
+
this.tui?.stop()
|
|
81
|
+
this.tui = null
|
|
82
|
+
this.restoreConsole?.()
|
|
83
|
+
this.restoreConsole = null
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function enhanceProcessOptions(label: string, opts: ProcessOptions): ProcessOptions {
|
|
88
|
+
const session = getActiveDevSession()
|
|
89
|
+
const subprocessFilter =
|
|
90
|
+
label === "app" || label === "studio"
|
|
91
|
+
? { shouldLogLine: (line: string) => filterDevSubprocessLine(label, line) }
|
|
92
|
+
: {}
|
|
93
|
+
|
|
94
|
+
if (!session?.isTui()) {
|
|
95
|
+
return { ...opts, ...subprocessFilter }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
session.bus.ensureTask(label, label)
|
|
99
|
+
return {
|
|
100
|
+
...opts,
|
|
101
|
+
...subprocessFilter,
|
|
102
|
+
onLine: (line, stream) => {
|
|
103
|
+
session.bus.append(label, line, stream === "stderr" ? "error" : "log")
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function patchConsole(bus: DevLogBus): () => void {
|
|
109
|
+
const original = {
|
|
110
|
+
log: console.log,
|
|
111
|
+
warn: console.warn,
|
|
112
|
+
error: console.error,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log = (...args: unknown[]) => {
|
|
116
|
+
bus.append("stack", formatConsoleArgs(args), "log")
|
|
117
|
+
}
|
|
118
|
+
console.warn = (...args: unknown[]) => {
|
|
119
|
+
bus.append("stack", formatConsoleArgs(args), "warn")
|
|
120
|
+
}
|
|
121
|
+
console.error = (...args: unknown[]) => {
|
|
122
|
+
bus.append("stack", formatConsoleArgs(args), "error")
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return () => {
|
|
126
|
+
console.log = original.log
|
|
127
|
+
console.warn = original.warn
|
|
128
|
+
console.error = original.error
|
|
129
|
+
}
|
|
130
|
+
}
|