@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
package/src/type-extractor.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs"
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs"
|
|
2
|
+
import { createHash } from "node:crypto"
|
|
3
|
+
import { dirname, isAbsolute, relative, resolve } from "node:path"
|
|
3
4
|
import ts from "typescript"
|
|
4
5
|
import {
|
|
5
6
|
applyImportRename,
|
|
@@ -103,7 +104,7 @@ export function extractSchemaAstFromTypes(
|
|
|
103
104
|
)
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
const { tableName, access, options } = parseModelMeta(
|
|
107
|
+
const { tableName, access, options, indexes } = parseModelMeta(
|
|
107
108
|
metaArg,
|
|
108
109
|
sourceFile,
|
|
109
110
|
stmt.name.text,
|
|
@@ -112,7 +113,7 @@ export function extractSchemaAstFromTypes(
|
|
|
112
113
|
)
|
|
113
114
|
|
|
114
115
|
models.push(
|
|
115
|
-
emitModel(stmt.name.text, fields, options, tableName, access),
|
|
116
|
+
emitModel(stmt.name.text, fields, options, tableName, access, indexes),
|
|
116
117
|
)
|
|
117
118
|
}
|
|
118
119
|
}
|
|
@@ -143,9 +144,51 @@ export function extractSchemaAstFromTypes(
|
|
|
143
144
|
})
|
|
144
145
|
}
|
|
145
146
|
|
|
146
|
-
|
|
147
|
+
export interface SchemaSourceFile {
|
|
148
|
+
relativePath: string
|
|
149
|
+
absolutePath: string
|
|
150
|
+
sha256: string
|
|
151
|
+
bytes: number
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface SchemaSourceGraph {
|
|
155
|
+
entryPoint: string
|
|
156
|
+
files: SchemaSourceFile[]
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function collectSchemaSourcePaths(entryAbsPath: string, projectRoot: string): SchemaSourceGraph {
|
|
160
|
+
const root = resolve(projectRoot)
|
|
161
|
+
const entryReal = realpathSync(entryAbsPath)
|
|
162
|
+
const entryPoint = relative(root, entryReal).replace(/\\/g, "/")
|
|
163
|
+
if (entryPoint.startsWith("..")) {
|
|
164
|
+
throw new Error(`Schema entry must be under project root: ${entryAbsPath}`)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const absolutePaths = walkSchemaSourceAbsPaths(entryReal)
|
|
168
|
+
const files: SchemaSourceFile[] = []
|
|
169
|
+
|
|
170
|
+
for (const abs of absolutePaths) {
|
|
171
|
+
const real = realpathSync(abs)
|
|
172
|
+
const rel = relative(root, real).replace(/\\/g, "/")
|
|
173
|
+
if (rel.startsWith("..")) {
|
|
174
|
+
throw new Error(`Schema source escapes project root: ${abs}`)
|
|
175
|
+
}
|
|
176
|
+
const content = readFileSync(real)
|
|
177
|
+
files.push({
|
|
178
|
+
relativePath: rel,
|
|
179
|
+
absolutePath: real,
|
|
180
|
+
sha256: createHash("sha256").update(content).digest("hex"),
|
|
181
|
+
bytes: content.length,
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
files.sort((a, b) => a.relativePath.localeCompare(b.relativePath))
|
|
186
|
+
return { entryPoint, files }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function walkSchemaSourceAbsPaths(entryPath: string): string[] {
|
|
147
190
|
const visited = new Set<string>()
|
|
148
|
-
const
|
|
191
|
+
const paths: string[] = []
|
|
149
192
|
const queue: string[] = [entryPath]
|
|
150
193
|
|
|
151
194
|
while (queue.length > 0) {
|
|
@@ -153,12 +196,11 @@ function loadSchemaSourceFiles(entryPath: string): ts.SourceFile[] {
|
|
|
153
196
|
if (!currentPath) continue
|
|
154
197
|
if (visited.has(currentPath)) continue
|
|
155
198
|
visited.add(currentPath)
|
|
156
|
-
|
|
157
199
|
if (!existsSync(currentPath)) continue
|
|
200
|
+
paths.push(currentPath)
|
|
201
|
+
|
|
158
202
|
const sourceText = readFileSync(currentPath, "utf8")
|
|
159
203
|
const sourceFile = ts.createSourceFile(currentPath, sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)
|
|
160
|
-
sourceFiles.push(sourceFile)
|
|
161
|
-
|
|
162
204
|
const baseDir = dirname(currentPath)
|
|
163
205
|
for (const stmt of sourceFile.statements) {
|
|
164
206
|
let specifier: string | undefined
|
|
@@ -178,7 +220,14 @@ function loadSchemaSourceFiles(entryPath: string): ts.SourceFile[] {
|
|
|
178
220
|
}
|
|
179
221
|
}
|
|
180
222
|
|
|
181
|
-
return
|
|
223
|
+
return paths
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function loadSchemaSourceFiles(entryPath: string): ts.SourceFile[] {
|
|
227
|
+
return walkSchemaSourceAbsPaths(entryPath).map((abs) => {
|
|
228
|
+
const sourceText = readFileSync(abs, "utf8")
|
|
229
|
+
return ts.createSourceFile(abs, sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)
|
|
230
|
+
})
|
|
182
231
|
}
|
|
183
232
|
|
|
184
233
|
function resolveTypeModulePath(fromDir: string, specifier: string): string | null {
|
|
@@ -1372,7 +1421,12 @@ function parseModelMeta(
|
|
|
1372
1421
|
modelName: string,
|
|
1373
1422
|
fieldsArg: ts.TypeNode,
|
|
1374
1423
|
fields: Record<string, FieldAstV2>,
|
|
1375
|
-
): {
|
|
1424
|
+
): {
|
|
1425
|
+
tableName: string
|
|
1426
|
+
access: Record<string, unknown>
|
|
1427
|
+
options: Record<string, unknown>
|
|
1428
|
+
indexes: unknown[]
|
|
1429
|
+
} {
|
|
1376
1430
|
const literal = parseMetaLiteral(metaArg, sourceFile)
|
|
1377
1431
|
const singleton = literal.singleton === true
|
|
1378
1432
|
const tableName =
|
|
@@ -1397,7 +1451,66 @@ function parseModelMeta(
|
|
|
1397
1451
|
tableName,
|
|
1398
1452
|
access: parseModelAccess(metaArg, sourceFile),
|
|
1399
1453
|
options,
|
|
1454
|
+
indexes: parseModelIndexes(metaArg, sourceFile, fields),
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
function parseModelIndexes(
|
|
1459
|
+
metaArg: ts.TypeNode | undefined,
|
|
1460
|
+
sourceFile: ts.SourceFile,
|
|
1461
|
+
fields: Record<string, FieldAstV2>,
|
|
1462
|
+
): unknown[] {
|
|
1463
|
+
if (!metaArg || !ts.isTypeLiteralNode(metaArg)) return []
|
|
1464
|
+
|
|
1465
|
+
const indexesProp = metaArg.members.find(
|
|
1466
|
+
(member) => ts.isPropertySignature(member) && getPropertyName(member.name) === "indexes",
|
|
1467
|
+
)
|
|
1468
|
+
if (
|
|
1469
|
+
!indexesProp ||
|
|
1470
|
+
!ts.isPropertySignature(indexesProp) ||
|
|
1471
|
+
!indexesProp.type ||
|
|
1472
|
+
!ts.isTupleTypeNode(indexesProp.type)
|
|
1473
|
+
) {
|
|
1474
|
+
return []
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
const indexes: unknown[] = []
|
|
1478
|
+
for (const element of indexesProp.type.elements) {
|
|
1479
|
+
if (!ts.isTypeLiteralNode(element)) continue
|
|
1480
|
+
const indexDef: Record<string, unknown> = { using: "btree" }
|
|
1481
|
+
for (const member of element.members) {
|
|
1482
|
+
if (!ts.isPropertySignature(member) || !member.type) continue
|
|
1483
|
+
const key = getPropertyName(member.name)
|
|
1484
|
+
if (!key) continue
|
|
1485
|
+
if (key === "name" && ts.isLiteralTypeNode(member.type) && ts.isStringLiteral(member.type.literal)) {
|
|
1486
|
+
indexDef.name = member.type.literal.text
|
|
1487
|
+
} else if (key === "unique" && isBooleanLiteralType(member.type, true)) {
|
|
1488
|
+
indexDef.unique = true
|
|
1489
|
+
} else if (key === "fields" && ts.isTupleTypeNode(member.type)) {
|
|
1490
|
+
indexDef.fields = member.type.elements
|
|
1491
|
+
.map((fieldNode) => {
|
|
1492
|
+
if (!ts.isLiteralTypeNode(fieldNode) || !ts.isStringLiteral(fieldNode.literal)) return null
|
|
1493
|
+
return resolveIndexFieldName(fieldNode.literal.text, fields)
|
|
1494
|
+
})
|
|
1495
|
+
.filter((field): field is string => field !== null)
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
if (Array.isArray(indexDef.fields) && indexDef.fields.length > 0) {
|
|
1499
|
+
indexes.push(indexDef)
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
return indexes
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
function resolveIndexFieldName(fieldName: string, fields: Record<string, FieldAstV2>): string | null {
|
|
1506
|
+
if (fields[fieldName] !== undefined) {
|
|
1507
|
+
const field = fields[fieldName]
|
|
1508
|
+
if (field.kind === "relation" && field.annotations?.db?.foreignKey) {
|
|
1509
|
+
return field.annotations.db.foreignKey as string
|
|
1510
|
+
}
|
|
1511
|
+
return fieldName
|
|
1400
1512
|
}
|
|
1513
|
+
return fieldName
|
|
1401
1514
|
}
|
|
1402
1515
|
|
|
1403
1516
|
function parseModelAccess(metaArg: ts.TypeNode | undefined, sourceFile: ts.SourceFile): Record<string, unknown> {
|
package/src/ui/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# CLI terminal UI (`packages/cli/src/ui/`)
|
|
2
|
+
|
|
3
|
+
Shared output layer for `supatype` commands (not `supatype dev` TUI).
|
|
4
|
+
|
|
5
|
+
| Module | Use for |
|
|
6
|
+
|--------|---------|
|
|
7
|
+
| `messages.ts` | `info` / `warn` / `error` (Clack log in TTY; `[supatype]` in CI), `file`, `step`, `plain` |
|
|
8
|
+
| `fatal.ts` | `fatalError`, `reportCliFatal` — branded fatals + global catch helper |
|
|
9
|
+
| `confirm.ts` | Yes/no prompts (Clack in TTY; respects `--yes` / non-TTY) |
|
|
10
|
+
| `progress.ts` | `withSpinner` for long async work |
|
|
11
|
+
| `prompts.ts` | Logo, Clack wizard helpers, `promptText` |
|
|
12
|
+
| `next-steps.ts` | Bulleted follow-up commands |
|
|
13
|
+
| `brand.ts` | ANSI colours + logo styling (shared with dev TUI) |
|
|
14
|
+
|
|
15
|
+
**`supatype dev`** keeps its own alt-screen TUI (`dev-tui.ts`). Do not route dev logs through Clack.
|
|
16
|
+
|
|
17
|
+
**Convention:** interactive multi-step flows use Clack (`init`, `add`, `link`); one-shot commands use `info` + optional `withSpinner`.
|
package/src/ui/brand.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared ANSI styling for Supatype CLI output and the dev TUI.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const RESET = "\x1b[0m"
|
|
6
|
+
export const DIM = "\x1b[2m"
|
|
7
|
+
export const BOLD = "\x1b[1m"
|
|
8
|
+
export const BRAND_COLOR = "\x1b[35m"
|
|
9
|
+
|
|
10
|
+
export function brandStyle(text: string): string {
|
|
11
|
+
return `${BRAND_COLOR}${BOLD}${text}${RESET}`
|
|
12
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as p from "@clack/prompts"
|
|
2
|
+
import { isInteractive } from "./interactive.js"
|
|
3
|
+
import { plain } from "./messages.js"
|
|
4
|
+
|
|
5
|
+
export interface ConfirmOptions {
|
|
6
|
+
default?: boolean
|
|
7
|
+
/** When non-interactive, return this instead of `default` (e.g. require --yes). */
|
|
8
|
+
nonInteractive?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Yes/no confirmation — Clack in TTY, plain prompt or default otherwise.
|
|
13
|
+
*/
|
|
14
|
+
export async function confirm(message: string, opts: ConfirmOptions = {}): Promise<boolean> {
|
|
15
|
+
const fallback = opts.nonInteractive ?? opts.default ?? false
|
|
16
|
+
|
|
17
|
+
if (!isInteractive()) {
|
|
18
|
+
if (opts.nonInteractive !== undefined) return opts.nonInteractive
|
|
19
|
+
return fallback
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const value = await p.confirm({
|
|
23
|
+
message,
|
|
24
|
+
initialValue: opts.default ?? false,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
if (p.isCancel(value)) {
|
|
28
|
+
p.cancel("Cancelled.")
|
|
29
|
+
process.exit(0)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return value
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Legacy-style y/N prompt text for non-TTY logs when skipping confirm. */
|
|
36
|
+
export function logSkippedConfirm(reason: string): void {
|
|
37
|
+
plain(`${reason} (use --yes to skip confirmation)`)
|
|
38
|
+
}
|
package/src/ui/fatal.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as p from "@clack/prompts"
|
|
2
|
+
import type { DockerBrandOptions } from "../docker-runtime.js"
|
|
3
|
+
import { isInteractive } from "./interactive.js"
|
|
4
|
+
import { error, plain } from "./messages.js"
|
|
5
|
+
import { clack, printLogo } from "./prompts.js"
|
|
6
|
+
|
|
7
|
+
export type FatalOptions = {
|
|
8
|
+
brand?: DockerBrandOptions
|
|
9
|
+
exitCode?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Print a fatal headline + optional hint block, then exit. */
|
|
13
|
+
export function fatalError(message: string, hints: string[] = [], opts?: FatalOptions): never {
|
|
14
|
+
if (opts?.brand && isInteractive()) {
|
|
15
|
+
printLogo()
|
|
16
|
+
clack.intro(opts.brand.intro)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
error(message)
|
|
20
|
+
|
|
21
|
+
if (hints.length > 0) {
|
|
22
|
+
if (isInteractive()) {
|
|
23
|
+
p.note(hints.join("\n\n"))
|
|
24
|
+
} else {
|
|
25
|
+
for (const hint of hints) {
|
|
26
|
+
plain()
|
|
27
|
+
plain(` ${hint}`)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
process.exit(opts?.exitCode ?? 1)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Top-level CLI catch — user-facing message without branding. */
|
|
36
|
+
export function reportCliFatal(err: unknown): void {
|
|
37
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
38
|
+
error(message)
|
|
39
|
+
if (process.env["SUPATYPE_DEBUG"] === "1" && err instanceof Error && err.stack) {
|
|
40
|
+
plain()
|
|
41
|
+
plain(err.stack)
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/ui/index.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as p from "@clack/prompts"
|
|
2
|
+
import { isInteractive } from "./interactive.js"
|
|
3
|
+
|
|
4
|
+
export const SUPATYPE_PREFIX = "[supatype]"
|
|
5
|
+
|
|
6
|
+
export function info(message: string): void {
|
|
7
|
+
if (isInteractive()) {
|
|
8
|
+
p.log.info(message)
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
console.log(`${SUPATYPE_PREFIX} ${message}`)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function warn(message: string): void {
|
|
15
|
+
if (isInteractive()) {
|
|
16
|
+
p.log.warn(message)
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
console.warn(`${SUPATYPE_PREFIX} ${message}`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function error(message: string): void {
|
|
23
|
+
if (isInteractive()) {
|
|
24
|
+
p.log.error(message)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
console.error(`${SUPATYPE_PREFIX} ${message}`)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Unprefixed line (tables, diff output, blank lines). */
|
|
31
|
+
export function plain(message = ""): void {
|
|
32
|
+
console.log(message)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function step(title: string): void {
|
|
36
|
+
console.log(`\n${title}`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type FileAction = "created" | "updated" | "skipped" | "removed" | "wrote"
|
|
40
|
+
|
|
41
|
+
export function file(action: FileAction, rel: string): void {
|
|
42
|
+
console.log(` ${action} ${rel}`)
|
|
43
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as p from "@clack/prompts"
|
|
2
|
+
import { isInteractive } from "./interactive.js"
|
|
3
|
+
import { info } from "./messages.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Run an async task with a Clack spinner (TTY) or a plain status line (CI/pipes).
|
|
7
|
+
*/
|
|
8
|
+
export async function withSpinner<T>(
|
|
9
|
+
message: string,
|
|
10
|
+
task: () => Promise<T>,
|
|
11
|
+
doneMessage?: string,
|
|
12
|
+
): Promise<T> {
|
|
13
|
+
if (!isInteractive()) {
|
|
14
|
+
info(`${message}...`)
|
|
15
|
+
return task()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const spinner = p.spinner()
|
|
19
|
+
spinner.start(message)
|
|
20
|
+
try {
|
|
21
|
+
const result = await task()
|
|
22
|
+
spinner.stop(doneMessage ?? message)
|
|
23
|
+
return result
|
|
24
|
+
} catch (err) {
|
|
25
|
+
spinner.stop("Failed")
|
|
26
|
+
throw err
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as p from "@clack/prompts"
|
|
2
|
+
import { SUPATYPE_ASCII_LOGO_WORDMARK, colorLogoLines } from "../dev-logo.js"
|
|
3
|
+
import { plain } from "./messages.js"
|
|
4
|
+
import { isInteractive } from "./interactive.js"
|
|
5
|
+
|
|
6
|
+
/** Print the coloured Supatype ASCII wordmark at the top of an interactive command. */
|
|
7
|
+
export function printLogo(): void {
|
|
8
|
+
plain()
|
|
9
|
+
plain(colorLogoLines([...SUPATYPE_ASCII_LOGO_WORDMARK]).join("\n"))
|
|
10
|
+
plain()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Unwrap a clack prompt result, exiting cleanly when the user cancels (Ctrl-C).
|
|
15
|
+
*/
|
|
16
|
+
export function ensureNotCancelled<T>(value: T | symbol, cancelMessage = "Cancelled."): T {
|
|
17
|
+
if (p.isCancel(value)) {
|
|
18
|
+
p.cancel(cancelMessage)
|
|
19
|
+
process.exit(0)
|
|
20
|
+
}
|
|
21
|
+
return value as T
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Single-line text input via Clack (TTY only). */
|
|
25
|
+
export async function promptText(
|
|
26
|
+
message: string,
|
|
27
|
+
opts?: { defaultValue?: string; placeholder?: string },
|
|
28
|
+
): Promise<string> {
|
|
29
|
+
if (!isInteractive()) {
|
|
30
|
+
throw new Error(`Cannot prompt for "${message}" in non-interactive mode.`)
|
|
31
|
+
}
|
|
32
|
+
const value = await p.text({
|
|
33
|
+
message,
|
|
34
|
+
...(opts?.defaultValue !== undefined ? { defaultValue: opts.defaultValue } : {}),
|
|
35
|
+
...(opts?.placeholder !== undefined ? { placeholder: opts.placeholder } : {}),
|
|
36
|
+
})
|
|
37
|
+
return ensureNotCancelled(value).trim()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export { p as clack }
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest"
|
|
2
|
+
import {
|
|
3
|
+
ADMIN_EMAIL_ENV,
|
|
4
|
+
ADMIN_PASSWORD_ENV,
|
|
5
|
+
clearAdminSeedPassword,
|
|
6
|
+
hashPasswordForAuth,
|
|
7
|
+
} from "../src/commands/admin.js"
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
|
9
|
+
import { join } from "node:path"
|
|
10
|
+
import { tmpdir } from "node:os"
|
|
11
|
+
import { scaffold, defaultScaffoldOptions } from "../src/commands/init.js"
|
|
12
|
+
|
|
13
|
+
describe("hashPasswordForAuth", () => {
|
|
14
|
+
it("produces a bcrypt hash", async () => {
|
|
15
|
+
const hash = await hashPasswordForAuth("test-password-123")
|
|
16
|
+
expect(hash.startsWith("$2")).toBe(true)
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
describe("clearAdminSeedPassword", () => {
|
|
21
|
+
it("removes SUPATYPE_ADMIN_PASSWORD from .env", () => {
|
|
22
|
+
const dir = join(tmpdir(), `supatype-admin-seed-${Date.now()}`)
|
|
23
|
+
mkdirSync(dir, { recursive: true })
|
|
24
|
+
writeFileSync(
|
|
25
|
+
join(dir, ".env"),
|
|
26
|
+
`${ADMIN_EMAIL_ENV}=admin@example.com\n${ADMIN_PASSWORD_ENV}=secret123\nJWT_SECRET=x\n`,
|
|
27
|
+
"utf8",
|
|
28
|
+
)
|
|
29
|
+
try {
|
|
30
|
+
clearAdminSeedPassword(dir)
|
|
31
|
+
const content = readFileSync(join(dir, ".env"), "utf8")
|
|
32
|
+
expect(content).toContain(`${ADMIN_EMAIL_ENV}=admin@example.com`)
|
|
33
|
+
expect(content).not.toContain(ADMIN_PASSWORD_ENV)
|
|
34
|
+
expect(content).toContain("JWT_SECRET=x")
|
|
35
|
+
} finally {
|
|
36
|
+
rmSync(dir, { recursive: true, force: true })
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe("init admin seed in .env", () => {
|
|
42
|
+
it("writes SUPATYPE_ADMIN_* when scaffold options include credentials", () => {
|
|
43
|
+
const dir = join(tmpdir(), `supatype-init-admin-${Date.now()}`)
|
|
44
|
+
mkdirSync(dir, { recursive: true })
|
|
45
|
+
try {
|
|
46
|
+
scaffold(dir, {
|
|
47
|
+
...defaultScaffoldOptions("seeded-app"),
|
|
48
|
+
adminEmail: "admin@example.com",
|
|
49
|
+
adminPassword: "password123",
|
|
50
|
+
})
|
|
51
|
+
const env = readFileSync(join(dir, ".env"), "utf8")
|
|
52
|
+
expect(env).toContain("SUPATYPE_ADMIN_EMAIL=admin@example.com")
|
|
53
|
+
expect(env).toContain("SUPATYPE_ADMIN_PASSWORD=password123")
|
|
54
|
+
expect(existsSync(join(dir, ".env"))).toBe(true)
|
|
55
|
+
} finally {
|
|
56
|
+
rmSync(dir, { recursive: true, force: true })
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
})
|
package/tests/cli-help.test.ts
CHANGED
|
@@ -33,6 +33,9 @@ describe("CLI binary (requires built dist/)", () => {
|
|
|
33
33
|
"push",
|
|
34
34
|
"diff",
|
|
35
35
|
"pull",
|
|
36
|
+
"doctor",
|
|
37
|
+
"introspect",
|
|
38
|
+
"adopt",
|
|
36
39
|
"generate",
|
|
37
40
|
"migrate",
|
|
38
41
|
"rollback",
|
|
@@ -79,10 +82,32 @@ describe("CLI binary (requires built dist/)", () => {
|
|
|
79
82
|
expect(stdout).toContain("dry run")
|
|
80
83
|
})
|
|
81
84
|
|
|
82
|
-
it("pull --help describes
|
|
85
|
+
it("pull --help describes scaffold pull command", () => {
|
|
83
86
|
const { stdout, exitCode } = runCli(["pull", "--help"])
|
|
84
87
|
expect(exitCode).toBe(0)
|
|
85
|
-
expect(stdout).toContain("
|
|
88
|
+
expect(stdout).toContain("Scaffold")
|
|
89
|
+
expect(stdout).toContain("--dry-run")
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it("doctor --help describes drift report command", () => {
|
|
93
|
+
const { stdout, exitCode } = runCli(["doctor", "--help"])
|
|
94
|
+
expect(exitCode).toBe(0)
|
|
95
|
+
expect(stdout).toContain("drift")
|
|
96
|
+
expect(stdout).toContain("--strict")
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it("introspect --help describes database introspection command", () => {
|
|
100
|
+
const { stdout, exitCode } = runCli(["introspect", "--help"])
|
|
101
|
+
expect(exitCode).toBe(0)
|
|
102
|
+
expect(stdout).toContain("Introspect")
|
|
103
|
+
expect(stdout).toContain("--json")
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it("adopt --help describes adoption ceremony command", () => {
|
|
107
|
+
const { stdout, exitCode } = runCli(["adopt", "--help"])
|
|
108
|
+
expect(exitCode).toBe(0)
|
|
109
|
+
expect(stdout).toContain("adopt")
|
|
110
|
+
expect(stdout).toContain("--yes")
|
|
86
111
|
})
|
|
87
112
|
|
|
88
113
|
it("reset --help shows --yes flag", () => {
|
package/tests/config.test.ts
CHANGED
|
@@ -101,7 +101,7 @@ describe("loadConfig()", () => {
|
|
|
101
101
|
expect(cfg.connection).toBe("postgresql://localhost/jsdb")
|
|
102
102
|
})
|
|
103
103
|
|
|
104
|
-
it("
|
|
104
|
+
it("loads config without a versions section (latest by default)", () => {
|
|
105
105
|
writeFileSync(
|
|
106
106
|
join(tmpDir, "supatype.config.ts"),
|
|
107
107
|
`export default {
|
|
@@ -112,7 +112,8 @@ describe("loadConfig()", () => {
|
|
|
112
112
|
schema: { path: "./schema/index.ts" },
|
|
113
113
|
}`,
|
|
114
114
|
)
|
|
115
|
-
|
|
115
|
+
const cfg = loadConfig(tmpDir)
|
|
116
|
+
expect(cfg.versions).toBeUndefined()
|
|
116
117
|
})
|
|
117
118
|
|
|
118
119
|
it("prefers supatype.config.ts over supatype.config.js when both exist", () => {
|
|
@@ -248,4 +249,30 @@ describe("mergeProjectConfig()", () => {
|
|
|
248
249
|
expect(merged.app.start).toBe("dev:site")
|
|
249
250
|
expect(merged.app.static_dir).toBe("./dist")
|
|
250
251
|
})
|
|
252
|
+
|
|
253
|
+
it("preserves base environments when a local override sets only server.mode", () => {
|
|
254
|
+
const base = defineConfig({
|
|
255
|
+
...minimalProject("p"),
|
|
256
|
+
server: { mode: "standalone", domain: "api.example.com" },
|
|
257
|
+
environments: { default: "production" },
|
|
258
|
+
})
|
|
259
|
+
const merged = mergeProjectConfig(base, { server: { mode: "dev" } })
|
|
260
|
+
expect(merged.server.mode).toBe("dev")
|
|
261
|
+
expect(merged.environments?.default).toBe("production")
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
it("deep-merges environments.branchDefaults across base and override", () => {
|
|
265
|
+
const base = defineConfig({
|
|
266
|
+
...minimalProject("p"),
|
|
267
|
+
environments: { default: "production", branchDefaults: { main: "production" } },
|
|
268
|
+
})
|
|
269
|
+
const merged = mergeProjectConfig(base, {
|
|
270
|
+
environments: { branchDefaults: { staging: "preview" } },
|
|
271
|
+
})
|
|
272
|
+
expect(merged.environments?.default).toBe("production")
|
|
273
|
+
expect(merged.environments?.branchDefaults).toEqual({
|
|
274
|
+
main: "production",
|
|
275
|
+
staging: "preview",
|
|
276
|
+
})
|
|
277
|
+
})
|
|
251
278
|
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"
|
|
2
|
+
import { mkdtempSync, writeFileSync } from "node:fs"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
import { tmpdir } from "node:os"
|
|
5
|
+
import {
|
|
6
|
+
findNextFreePort,
|
|
7
|
+
isValidHostPort,
|
|
8
|
+
parseHostPortInput,
|
|
9
|
+
readPersistedKongPort,
|
|
10
|
+
} from "../src/dev-ports.js"
|
|
11
|
+
import { isPortInUse } from "../src/postgres-ctl.js"
|
|
12
|
+
|
|
13
|
+
vi.mock("../src/postgres-ctl.js", () => ({
|
|
14
|
+
isPortInUse: vi.fn(),
|
|
15
|
+
}))
|
|
16
|
+
|
|
17
|
+
const isPortInUseMock = vi.mocked(isPortInUse)
|
|
18
|
+
|
|
19
|
+
describe("dev-ports", () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
isPortInUseMock.mockReset()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it("validates host ports", () => {
|
|
25
|
+
expect(isValidHostPort(18473)).toBe(true)
|
|
26
|
+
expect(isValidHostPort(80)).toBe(false)
|
|
27
|
+
expect(parseHostPortInput("18473")).toBe(18473)
|
|
28
|
+
expect(parseHostPortInput("nope")).toBeNull()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it("reads persisted Kong port from .env", () => {
|
|
32
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-ports-"))
|
|
33
|
+
writeFileSync(join(dir, ".env"), "SUPATYPE_KONG_PORT=18474\n", "utf8")
|
|
34
|
+
expect(readPersistedKongPort(dir)).toBe(18474)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("findNextFreePort skips taken ports", async () => {
|
|
38
|
+
isPortInUseMock.mockImplementation(async (port) => port === 18473 || port === 18474)
|
|
39
|
+
await expect(findNextFreePort(18473)).resolves.toBe(18475)
|
|
40
|
+
})
|
|
41
|
+
})
|