@supatype/cli 0.1.7 → 0.1.10
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 +125 -122
- package/.turbo/turbo-typecheck.log +1 -1
- package/LICENSE +202 -0
- package/deno.d.ts +1 -1
- package/dist/app/framework.js +1 -1
- package/dist/app/framework.js.map +1 -1
- package/dist/augmentation-generator.d.ts +1 -1
- package/dist/augmentation-generator.js +3 -3
- package/dist/augmentation-generator.js.map +1 -1
- package/dist/binary-cache.d.ts +30 -8
- package/dist/binary-cache.d.ts.map +1 -1
- package/dist/binary-cache.js +47 -35
- package/dist/binary-cache.js.map +1 -1
- package/dist/cache-pins.d.ts +1 -1
- package/dist/cache-pins.js +2 -2
- package/dist/cache-pins.js.map +1 -1
- package/dist/cli-artifacts.d.ts +32 -0
- package/dist/cli-artifacts.d.ts.map +1 -0
- package/dist/cli-artifacts.js +49 -0
- package/dist/cli-artifacts.js.map +1 -0
- package/dist/cli-install-method.d.ts +17 -1
- package/dist/cli-install-method.d.ts.map +1 -1
- package/dist/cli-install-method.js +32 -4
- package/dist/cli-install-method.js.map +1 -1
- package/dist/cli-package-version.d.ts +1 -1
- package/dist/cli-package-version.d.ts.map +1 -1
- package/dist/cli-package-version.js +6 -1
- package/dist/cli-package-version.js.map +1 -1
- package/dist/cli-standalone.d.ts +13 -4
- package/dist/cli-standalone.d.ts.map +1 -1
- package/dist/cli-standalone.js +86 -31
- package/dist/cli-standalone.js.map +1 -1
- package/dist/cli-version-embedded.d.ts +10 -0
- package/dist/cli-version-embedded.d.ts.map +1 -0
- package/dist/cli-version-embedded.js +13 -0
- package/dist/cli-version-embedded.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.js +4 -3
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/admin.js +6 -6
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/adopt.js +1 -1
- package/dist/commands/adopt.js.map +1 -1
- package/dist/commands/app.js +2 -2
- package/dist/commands/app.js.map +1 -1
- package/dist/commands/cache.d.ts +1 -1
- package/dist/commands/cache.js +3 -3
- package/dist/commands/cache.js.map +1 -1
- package/dist/commands/db.d.ts +3 -3
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +12 -11
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/deploy.d.ts +9 -9
- package/dist/commands/deploy.js +12 -12
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts +3 -3
- package/dist/commands/dev.js +17 -17
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.js +3 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/engine.d.ts +1 -1
- package/dist/commands/engine.js +1 -1
- package/dist/commands/functions.d.ts.map +1 -1
- package/dist/commands/functions.js +8 -7
- package/dist/commands/functions.js.map +1 -1
- package/dist/commands/hooks.d.ts +2 -2
- package/dist/commands/hooks.js +4 -4
- package/dist/commands/hooks.js.map +1 -1
- package/dist/commands/init.js +28 -28
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/internal.d.ts.map +1 -1
- package/dist/commands/internal.js +37 -0
- package/dist/commands/internal.js.map +1 -1
- package/dist/commands/login.d.ts +1 -1
- package/dist/commands/login.js +1 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logs.d.ts +1 -1
- package/dist/commands/migrate.js +5 -5
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/pg.d.ts +1 -1
- package/dist/commands/pg.js +1 -1
- package/dist/commands/plugins.js +1 -1
- package/dist/commands/plugins.js.map +1 -1
- package/dist/commands/pull.js +2 -2
- package/dist/commands/pull.js.map +1 -1
- package/dist/commands/push.js +3 -3
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/self-host.d.ts +1 -1
- package/dist/commands/self-host.js +1 -1
- package/dist/commands/self-update.d.ts +2 -8
- package/dist/commands/self-update.d.ts.map +1 -1
- package/dist/commands/self-update.js +20 -12
- package/dist/commands/self-update.js.map +1 -1
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/update.d.ts +1 -1
- package/dist/commands/update.js +1 -1
- package/dist/config.d.ts +23 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +37 -18
- package/dist/config.js.map +1 -1
- package/dist/db-preflight.d.ts +4 -4
- package/dist/db-preflight.js +14 -14
- package/dist/db-preflight.js.map +1 -1
- package/dist/dev-compose.d.ts +3 -3
- package/dist/dev-compose.js +27 -27
- package/dist/dev-compose.js.map +1 -1
- package/dist/dev-log-filter.d.ts +1 -1
- package/dist/dev-log-filter.d.ts.map +1 -1
- package/dist/dev-log-filter.js +1 -1
- package/dist/dev-log-filter.js.map +1 -1
- package/dist/dev-ports.d.ts +2 -2
- package/dist/dev-ports.d.ts.map +1 -1
- package/dist/dev-ports.js +2 -2
- package/dist/dev-ports.js.map +1 -1
- package/dist/dev-session.d.ts +5 -5
- package/dist/dev-session.d.ts.map +1 -1
- package/dist/dev-session.js +6 -6
- package/dist/dev-session.js.map +1 -1
- package/dist/dev-shutdown.d.ts +5 -5
- package/dist/dev-shutdown.d.ts.map +1 -1
- package/dist/dev-shutdown.js +7 -7
- package/dist/dev-shutdown.js.map +1 -1
- package/dist/dev-task-colors.d.ts +1 -1
- package/dist/dev-task-colors.d.ts.map +1 -1
- package/dist/dev-task-colors.js +1 -1
- package/dist/dev-task-colors.js.map +1 -1
- package/dist/docker-postgres.d.ts +5 -5
- package/dist/docker-postgres.d.ts.map +1 -1
- package/dist/docker-postgres.js +5 -5
- package/dist/docker-postgres.js.map +1 -1
- package/dist/docker-runtime.d.ts +2 -2
- package/dist/docker-runtime.d.ts.map +1 -1
- package/dist/docker-runtime.js +5 -5
- package/dist/docker-runtime.js.map +1 -1
- package/dist/engine-client.d.ts +1 -1
- package/dist/engine-client.js +3 -3
- package/dist/engine-client.js.map +1 -1
- package/dist/engine-push-output.js +1 -1
- package/dist/engine-push-output.js.map +1 -1
- package/dist/ensure-binary.d.ts.map +1 -1
- package/dist/ensure-binary.js +17 -0
- package/dist/ensure-binary.js.map +1 -1
- package/dist/ensure-component-binaries.js +1 -1
- package/dist/ensure-component-binaries.js.map +1 -1
- package/dist/env-file.d.ts +2 -2
- package/dist/env-file.d.ts.map +1 -1
- package/dist/env-file.js +1 -1
- package/dist/env-file.js.map +1 -1
- package/dist/field-masking-tier.d.ts +5 -5
- package/dist/field-masking-tier.d.ts.map +1 -1
- package/dist/field-masking-tier.js +6 -6
- package/dist/field-masking-tier.js.map +1 -1
- package/dist/functions-deno-types.d.ts +1 -1
- package/dist/functions-deno-types.d.ts.map +1 -1
- package/dist/functions-deno-types.js +3 -3
- package/dist/functions-deno-types.js.map +1 -1
- package/dist/functions-router-gen.js +1 -1
- package/dist/gitignore.d.ts +2 -2
- package/dist/gitignore.d.ts.map +1 -1
- package/dist/gitignore.js +3 -3
- package/dist/gitignore.js.map +1 -1
- package/dist/hook-upload.d.ts +1 -1
- package/dist/hook-upload.js +3 -3
- package/dist/hooks-generator.d.ts +1 -1
- package/dist/hooks-generator.js +9 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/init-project-detect.d.ts +1 -1
- package/dist/init-project-detect.d.ts.map +1 -1
- package/dist/init-project-detect.js +1 -1
- package/dist/init-project-detect.js.map +1 -1
- package/dist/link.d.ts +1 -1
- package/dist/link.d.ts.map +1 -1
- package/dist/local-secrets.d.ts +5 -5
- package/dist/local-secrets.js +5 -5
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +20 -0
- package/dist/main.js.map +1 -0
- package/dist/model-hooks.d.ts +3 -3
- package/dist/model-hooks.js +4 -4
- package/dist/pgbouncer-userlist.d.ts +1 -1
- package/dist/pgbouncer-userlist.d.ts.map +1 -1
- package/dist/pgbouncer-userlist.js +2 -2
- package/dist/pgbouncer-userlist.js.map +1 -1
- package/dist/postgres-ctl.d.ts +1 -1
- package/dist/postgres-ctl.js +4 -4
- package/dist/postgres-ctl.js.map +1 -1
- package/dist/process-manager.d.ts +2 -2
- package/dist/process-manager.d.ts.map +1 -1
- package/dist/process-manager.js +4 -4
- package/dist/process-manager.js.map +1 -1
- package/dist/project-config.d.ts +12 -12
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +9 -9
- package/dist/project-config.js.map +1 -1
- package/dist/pull-utils.d.ts +2 -2
- package/dist/pull-utils.d.ts.map +1 -1
- package/dist/pull-utils.js +4 -4
- package/dist/pull-utils.js.map +1 -1
- package/dist/realtime-launch.js +1 -1
- package/dist/realtime-launch.js.map +1 -1
- package/dist/release-pins.d.ts +1 -1
- package/dist/release-pins.js +2 -2
- package/dist/release-pins.js.map +1 -1
- package/dist/resolve-api-url.d.ts.map +1 -1
- package/dist/resolve-api-url.js +4 -3
- package/dist/resolve-api-url.js.map +1 -1
- package/dist/resolve-target.d.ts +1 -1
- package/dist/resolve-target.d.ts.map +1 -1
- package/dist/rest-cache-admin.js +2 -2
- package/dist/rest-cache-admin.js.map +1 -1
- package/dist/runtime-routes.d.ts +1 -1
- package/dist/runtime-routes.js +1 -1
- package/dist/schema-ast-v2.d.ts +3 -3
- 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-push-lock.js +3 -3
- package/dist/schema-push-lock.js.map +1 -1
- package/dist/scripts/postinstall.d.ts +1 -1
- package/dist/scripts/postinstall.js +1 -1
- package/dist/self-host-compose.d.ts +5 -5
- package/dist/self-host-compose.d.ts.map +1 -1
- package/dist/self-host-compose.js +24 -24
- package/dist/self-host-compose.js.map +1 -1
- package/dist/service-role-check.js +3 -3
- package/dist/service-role-check.js.map +1 -1
- package/dist/storage-provision.d.ts +1 -1
- package/dist/storage-provision.js +3 -3
- package/dist/storage-provision.js.map +1 -1
- package/dist/studio-dev-server.d.ts +1 -1
- package/dist/studio-dev-server.d.ts.map +1 -1
- package/dist/systemd.d.ts +1 -1
- package/dist/systemd.js +1 -1
- package/dist/target-client.js +1 -1
- package/dist/target-client.js.map +1 -1
- package/dist/tsx-runner.d.ts +12 -0
- package/dist/tsx-runner.d.ts.map +1 -1
- package/dist/tsx-runner.js +36 -2
- package/dist/tsx-runner.js.map +1 -1
- package/dist/type-extractor.js +27 -27
- package/dist/type-extractor.js.map +1 -1
- package/dist/type-resolver.js +1 -1
- package/dist/type-resolver.js.map +1 -1
- package/dist/ui/brand.d.ts +1 -1
- package/dist/ui/brand.js +1 -1
- package/dist/ui/components/LogoWordmark.d.ts +1 -1
- package/dist/ui/components/LogoWordmark.d.ts.map +1 -1
- package/dist/ui/components/LogoWordmark.js +1 -1
- package/dist/ui/components/LogoWordmark.js.map +1 -1
- package/dist/ui/components/SelectField.js +1 -1
- package/dist/ui/components/SelectField.js.map +1 -1
- package/dist/ui/confirm.d.ts +1 -1
- package/dist/ui/confirm.js +1 -1
- package/dist/ui/fatal.d.ts +1 -1
- package/dist/ui/fatal.d.ts.map +1 -1
- package/dist/ui/fatal.js +1 -1
- package/dist/ui/fatal.js.map +1 -1
- package/dist/ui/flows/prompt-fields.js +1 -1
- package/dist/ui/flows/prompt-fields.js.map +1 -1
- package/dist/ui/interactive.d.ts +1 -1
- package/dist/ui/interactive.d.ts.map +1 -1
- package/dist/ui/interactive.js +1 -1
- package/dist/ui/interactive.js.map +1 -1
- package/dist/ui/progress.d.ts +1 -1
- package/dist/ui/progress.js +1 -1
- package/dist/ui/runtime/command-chrome.js +1 -1
- package/dist/ui/runtime/command-chrome.js.map +1 -1
- package/dist/ui/runtime/run-clack-flow.js +1 -1
- package/dist/ui/runtime/run-clack-flow.js.map +1 -1
- package/dist/ui/runtime/stdin-after-ink.js +1 -1
- package/dist/ui/runtime/stdin-after-ink.js.map +1 -1
- package/dist/ui/theme.d.ts +1 -1
- package/dist/ui/theme.js +1 -1
- package/dist/valkey-sidecar.d.ts +1 -1
- package/dist/valkey-sidecar.d.ts.map +1 -1
- package/dist/valkey-sidecar.js +2 -2
- package/dist/valkey-sidecar.js.map +1 -1
- package/package.json +3 -2
- package/scripts/build-release-artifacts.sh +112 -0
- package/scripts/generate-homebrew-formula.mjs +47 -45
- package/scripts/mirror-deno-release.sh +1 -1
- package/src/TYPE-RESOLUTION.md +31 -31
- package/src/app/framework.ts +1 -1
- package/src/augmentation-generator.ts +3 -3
- package/src/binary-cache.ts +51 -36
- package/src/cache-pins.ts +2 -2
- package/src/cli-artifacts.ts +65 -0
- package/src/cli-install-method.ts +35 -4
- package/src/cli-package-version.ts +5 -1
- package/src/cli-standalone.ts +116 -34
- package/src/cli-version-embedded.ts +12 -0
- package/src/cli.ts +1 -1
- package/src/commands/add.ts +4 -3
- package/src/commands/admin.ts +6 -6
- package/src/commands/adopt.ts +1 -1
- package/src/commands/app.ts +2 -2
- package/src/commands/cache.ts +3 -3
- package/src/commands/db.ts +12 -11
- package/src/commands/deploy.ts +12 -12
- package/src/commands/dev.ts +19 -19
- package/src/commands/doctor.ts +3 -3
- package/src/commands/engine.ts +1 -1
- package/src/commands/functions.ts +8 -7
- package/src/commands/hooks.ts +6 -6
- package/src/commands/init.ts +28 -28
- package/src/commands/internal.ts +37 -0
- package/src/commands/login.ts +2 -2
- package/src/commands/logs.ts +1 -1
- package/src/commands/migrate.ts +5 -5
- package/src/commands/pg.ts +1 -1
- package/src/commands/plugins.ts +1 -1
- package/src/commands/pull.ts +2 -2
- package/src/commands/push.ts +3 -3
- package/src/commands/self-host.ts +1 -1
- package/src/commands/self-update.ts +23 -73
- package/src/commands/status.ts +1 -1
- package/src/commands/update.ts +1 -1
- package/src/config.ts +39 -18
- package/src/db-preflight.ts +15 -15
- package/src/dev-compose.ts +27 -27
- package/src/dev-log-filter.ts +1 -1
- package/src/dev-ports.ts +3 -3
- package/src/dev-session.ts +6 -6
- package/src/dev-shutdown.ts +8 -8
- package/src/dev-task-colors.ts +1 -1
- package/src/docker-postgres.ts +6 -6
- package/src/docker-runtime.ts +5 -5
- package/src/engine-client.ts +3 -3
- package/src/engine-push-output.ts +1 -1
- package/src/ensure-binary.ts +20 -0
- package/src/ensure-component-binaries.ts +1 -1
- package/src/env-file.ts +2 -2
- package/src/field-masking-tier.ts +6 -6
- package/src/functions-deno-types.ts +3 -3
- package/src/functions-router-gen.ts +1 -1
- package/src/gitignore.ts +3 -3
- package/src/hook-upload.ts +3 -3
- package/src/hooks-generator.ts +9 -9
- package/src/index.ts +1 -1
- package/src/init-project-detect.ts +1 -1
- package/src/link.ts +1 -1
- package/src/local-secrets.ts +5 -5
- package/src/main.ts +20 -0
- package/src/model-hooks.ts +4 -4
- package/src/pgbouncer-userlist.ts +2 -2
- package/src/postgres-ctl.ts +4 -4
- package/src/process-manager.ts +4 -4
- package/src/project-config.ts +16 -16
- package/src/pull-utils.ts +4 -4
- package/src/realtime-launch.ts +1 -1
- package/src/release-pins.ts +2 -2
- package/src/resolve-api-url.ts +4 -3
- package/src/resolve-target.ts +1 -1
- package/src/rest-cache-admin.ts +2 -2
- package/src/runtime-routes.ts +2 -2
- package/src/schema-ast-v2.ts +4 -4
- package/src/schema-push-lock.ts +3 -3
- package/src/scripts/postinstall.ts +1 -1
- package/src/self-host-compose.ts +25 -25
- package/src/service-role-check.ts +3 -3
- package/src/storage-provision.ts +3 -3
- package/src/studio-dev-server.ts +1 -1
- package/src/systemd.ts +1 -1
- package/src/target-client.ts +1 -1
- package/src/tsx-runner.ts +41 -2
- package/src/type-extractor.ts +27 -27
- package/src/type-resolver.ts +1 -1
- package/src/ui/README.md +1 -1
- package/src/ui/brand.ts +1 -1
- package/src/ui/components/LogoWordmark.tsx +1 -1
- package/src/ui/components/SelectField.tsx +1 -1
- package/src/ui/confirm.ts +1 -1
- package/src/ui/fatal.ts +1 -1
- package/src/ui/flows/prompt-fields.tsx +1 -1
- package/src/ui/interactive.ts +1 -1
- package/src/ui/progress.ts +1 -1
- package/src/ui/runtime/command-chrome.ts +1 -1
- package/src/ui/runtime/run-clack-flow.tsx +1 -1
- package/src/ui/runtime/stdin-after-ink.ts +1 -1
- package/src/ui/theme.ts +1 -1
- package/src/valkey-sidecar.ts +2 -2
- package/tests/access-composition.test.ts +6 -6
- package/tests/admin-studio-membership.test.ts +1 -1
- package/tests/api-schemas.test.ts +1 -1
- package/tests/archive-name-alias.test.ts +1 -1
- package/tests/cli-help.test.ts +8 -2
- package/tests/config-load-failure.test.ts +44 -0
- package/tests/db-preflight.test.ts +1 -1
- package/tests/doctor-render.test.ts +3 -3
- package/tests/doctor-service-role.test.ts +2 -2
- package/tests/engine-push-output.test.ts +1 -1
- package/tests/env-file.test.ts +3 -3
- package/tests/external-database-compose.test.ts +5 -5
- package/tests/external-database-config.test.ts +2 -2
- package/tests/field-masking-tier.test.ts +1 -1
- package/tests/homebrew-formula.test.ts +68 -26
- package/tests/hook-upload.test.ts +1 -1
- package/tests/hooks-generator.test.ts +1 -1
- package/tests/init.test.ts +2 -2
- package/tests/keys.test.ts +8 -2
- package/tests/local-secrets.test.ts +3 -3
- package/tests/model-hooks.test.ts +6 -6
- package/tests/runtime-contract.test.ts +2 -2
- package/tests/service-role-check.test.ts +3 -3
- package/tests/storage-provision.test.ts +1 -1
- package/tests/type-extractor.test.ts +2 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +7 -1
- package/scripts/build-release-bundle.mjs +0 -27
package/src/commands/push.ts
CHANGED
|
@@ -167,7 +167,7 @@ async function pushViaTarget(
|
|
|
167
167
|
await generateTypesLocal(ast, config)
|
|
168
168
|
await provisionLocalStorage(ast, config)
|
|
169
169
|
|
|
170
|
-
// Local Studio only
|
|
170
|
+
// Local Studio only: a cloud/self-host push must not advertise the local
|
|
171
171
|
// gateway URL from config, which may not even be running.
|
|
172
172
|
const baseUrl = (serverBaseUrl(config) ?? "").replace(/\/$/, "")
|
|
173
173
|
if (baseUrl) {
|
|
@@ -191,7 +191,7 @@ async function pushViaTarget(
|
|
|
191
191
|
* into a 503.
|
|
192
192
|
*
|
|
193
193
|
* The handler types are written locally as well. A project pushing to cloud still edits its hooks on
|
|
194
|
-
* this machine, and `hooks/_supatype/hooks.ts` is what makes them typed
|
|
194
|
+
* this machine, and `hooks/_supatype/hooks.ts` is what makes them typed, it is generated, never
|
|
195
195
|
* committed, so a fresh clone that has only ever pushed to cloud would otherwise have no types at all.
|
|
196
196
|
*/
|
|
197
197
|
async function deployHooksToTarget(
|
|
@@ -285,7 +285,7 @@ async function writeLocalAdminConfig(ast: unknown, config: SupatypeProjectConfig
|
|
|
285
285
|
* Stop the push when a declared hook names a function that is not there.
|
|
286
286
|
*
|
|
287
287
|
* A hook is only enforcement if it runs. A typo'd name would extract cleanly, reach the manifest, and
|
|
288
|
-
* then never fire
|
|
288
|
+
* then never fire: so the write it was meant to validate would succeed and look fine. Cheaper to
|
|
289
289
|
* fail here, naming the directory searched.
|
|
290
290
|
*/
|
|
291
291
|
/**
|
|
@@ -1,139 +1,89 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
* supatype self-update — Phase 10.6C8.
|
|
4
|
-
|
|
2
|
+
* supatype self-update
|
|
5
3
|
*
|
|
6
|
-
|
|
7
4
|
* npm-installed CLI: instruct to use `npm update -g`.
|
|
8
|
-
|
|
9
5
|
* Homebrew: instruct to use `brew upgrade supatype`.
|
|
10
|
-
|
|
11
|
-
* Standalone curl|sh installs: replace binary from releases.supatype.com/cli/.
|
|
12
|
-
|
|
6
|
+
* Standalone curl|sh installs: replace the binary from releases.supatype.com/cli/.
|
|
13
7
|
*/
|
|
14
8
|
|
|
15
|
-
|
|
16
|
-
|
|
17
9
|
import type { Command } from "commander"
|
|
18
|
-
|
|
19
10
|
import { copyFileSync, renameSync } from "node:fs"
|
|
20
|
-
|
|
21
|
-
import { detectCliInstallMethod } from "../cli-install-method.js"
|
|
22
|
-
|
|
11
|
+
import { cliPackageVersion } from "../cli-package-version.js"
|
|
12
|
+
import { detectCliInstallMethod, runningBinaryPath } from "../cli-install-method.js"
|
|
23
13
|
import { downloadStandaloneCli, fetchStandaloneCliLatestVersion } from "../cli-standalone.js"
|
|
24
|
-
|
|
25
14
|
import { error, info, plain } from "../ui/messages.js"
|
|
26
15
|
|
|
27
|
-
|
|
28
|
-
|
|
29
16
|
export function registerSelfUpdate(program: Command): void {
|
|
30
|
-
|
|
31
17
|
program
|
|
32
|
-
|
|
33
18
|
.command("self-update")
|
|
34
|
-
|
|
35
19
|
.description("Update the supatype CLI (npm or standalone CDN binary)")
|
|
36
|
-
|
|
37
|
-
.action(async () => {
|
|
38
|
-
|
|
20
|
+
.option("--force", "Reinstall even if the latest version is already installed")
|
|
21
|
+
.action(async (opts: { force?: boolean }) => {
|
|
39
22
|
const method = detectCliInstallMethod()
|
|
40
23
|
|
|
41
|
-
|
|
42
|
-
|
|
43
24
|
if (method === "npm") {
|
|
44
|
-
|
|
45
25
|
plain(
|
|
46
|
-
|
|
47
26
|
"This CLI was installed via npm (or runs from the package workspace).\n" +
|
|
48
|
-
|
|
49
27
|
"Update with:\n\n npm update -g @supatype/cli\n\n" +
|
|
50
|
-
|
|
51
28
|
"To bump engine/server/postgres/deno/realtime pinned in supatype.config.ts, use:\n\n supatype update\n",
|
|
52
|
-
|
|
53
29
|
)
|
|
54
|
-
|
|
55
30
|
return
|
|
56
|
-
|
|
57
31
|
}
|
|
58
32
|
|
|
59
|
-
|
|
60
|
-
|
|
61
33
|
if (method === "homebrew") {
|
|
62
|
-
|
|
63
34
|
plain(
|
|
64
|
-
|
|
65
35
|
"This CLI was installed via Homebrew.\n" +
|
|
66
|
-
|
|
67
36
|
"Update with:\n\n brew update && brew upgrade supatype\n\n" +
|
|
68
|
-
|
|
69
37
|
"To bump component binaries pinned in supatype.config.ts, use:\n\n supatype update\n",
|
|
70
|
-
|
|
71
38
|
)
|
|
72
|
-
|
|
73
39
|
return
|
|
74
|
-
|
|
75
40
|
}
|
|
76
41
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const currentPath =
|
|
80
|
-
|
|
42
|
+
// The binary on disk, not argv[1]: in a compiled binary argv[1] points into the embedded
|
|
43
|
+
// filesystem, so backing it up failed with ENOENT and no update could ever complete.
|
|
44
|
+
const currentPath = runningBinaryPath()
|
|
81
45
|
if (!currentPath) {
|
|
82
|
-
|
|
83
46
|
error("Could not determine current CLI path.")
|
|
84
|
-
|
|
85
47
|
process.exit(1)
|
|
86
|
-
|
|
87
48
|
}
|
|
88
49
|
|
|
89
|
-
|
|
90
|
-
|
|
91
50
|
try {
|
|
92
|
-
|
|
93
51
|
const latest = await fetchStandaloneCliLatestVersion()
|
|
52
|
+
const current = cliPackageVersion()
|
|
53
|
+
|
|
54
|
+
// Without this the command re-downloaded on every run: it compared nothing, so a
|
|
55
|
+
// machine already on the latest version fetched and rewrote the same binary.
|
|
56
|
+
if (latest === current && !opts.force) {
|
|
57
|
+
info(`Already on v${current}, the latest release.`)
|
|
58
|
+
plain("Reinstall anyway with `supatype self-update --force`.")
|
|
59
|
+
return
|
|
60
|
+
}
|
|
94
61
|
|
|
95
|
-
info(
|
|
96
|
-
|
|
62
|
+
info(
|
|
63
|
+
latest === current
|
|
64
|
+
? `Reinstalling v${latest}...`
|
|
65
|
+
: `Downloading supatype CLI v${latest} (current: v${current})...`,
|
|
66
|
+
)
|
|
97
67
|
const downloaded = await downloadStandaloneCli(latest)
|
|
98
68
|
|
|
99
69
|
const backup = `${currentPath}.bak`
|
|
100
|
-
|
|
101
70
|
copyFileSync(currentPath, backup)
|
|
102
|
-
|
|
103
71
|
try {
|
|
104
|
-
|
|
105
72
|
renameSync(downloaded, currentPath)
|
|
106
|
-
|
|
107
73
|
} catch {
|
|
108
|
-
|
|
109
74
|
copyFileSync(downloaded, currentPath)
|
|
110
|
-
|
|
111
75
|
}
|
|
112
76
|
|
|
113
77
|
info(`Updated to v${latest}.`)
|
|
114
|
-
|
|
115
78
|
plain("Run `supatype --version` to verify.")
|
|
116
|
-
|
|
117
79
|
} catch (err) {
|
|
118
|
-
|
|
119
80
|
error(
|
|
120
|
-
|
|
121
81
|
`Standalone CLI self-update failed: ${(err as Error).message}\n\n` +
|
|
122
|
-
|
|
123
82
|
"Install or upgrade via npm:\n npm install -g @supatype/cli\n\n" +
|
|
124
|
-
|
|
125
83
|
"Or Homebrew:\n brew upgrade supatype\n\n" +
|
|
126
|
-
|
|
127
84
|
"Component binaries (engine, server, postgres, deno, realtime):\n supatype update\n",
|
|
128
|
-
|
|
129
85
|
)
|
|
130
|
-
|
|
131
86
|
process.exit(1)
|
|
132
|
-
|
|
133
87
|
}
|
|
134
|
-
|
|
135
88
|
})
|
|
136
|
-
|
|
137
89
|
}
|
|
138
|
-
|
|
139
|
-
|
package/src/commands/status.ts
CHANGED
package/src/commands/update.ts
CHANGED
package/src/config.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs"
|
|
|
2
2
|
import { resolve } from "node:path"
|
|
3
3
|
import { tmpdir } from "node:os"
|
|
4
4
|
import { join } from "node:path"
|
|
5
|
-
import {
|
|
5
|
+
import { importModuleAsJson } from "./tsx-runner.js"
|
|
6
6
|
import { readEnvFile } from "./env-file.js"
|
|
7
7
|
import {
|
|
8
8
|
mergeProjectConfig,
|
|
@@ -72,7 +72,7 @@ export interface AppConfig {
|
|
|
72
72
|
headers?: Record<string, string>
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
/** Identity helper
|
|
75
|
+
/** Identity helper: provides type inference for config files. */
|
|
76
76
|
export function defineConfig(config: SupatypeProjectConfig): SupatypeProjectConfig {
|
|
77
77
|
return config
|
|
78
78
|
}
|
|
@@ -137,7 +137,7 @@ export function loadConfig(cwd: string = process.cwd()): SupatypeProjectConfig {
|
|
|
137
137
|
|
|
138
138
|
const localNorm = normalizeProjectJson(localRaw) as Partial<SupatypeProjectConfig>
|
|
139
139
|
// Re-validated after merging: the local override can reintroduce a combination the base file was
|
|
140
|
-
// rejected for
|
|
140
|
+
// rejected for: `database.provider` beside an inherited `database.external`, say, and merging
|
|
141
141
|
// has no opinion about that.
|
|
142
142
|
return validateProjectConfig(
|
|
143
143
|
mergeProjectConfig(base, localNorm),
|
|
@@ -149,7 +149,7 @@ export function loadConfig(cwd: string = process.cwd()): SupatypeProjectConfig {
|
|
|
149
149
|
* Environment for the child process that imports the config module.
|
|
150
150
|
*
|
|
151
151
|
* `supatype.config.ts` is TypeScript, so the natural way to keep a database password out of version
|
|
152
|
-
* control is `process.env.DATABASE_URL
|
|
152
|
+
* control is `process.env.DATABASE_URL`: which only works if the project's `.env` is visible to the
|
|
153
153
|
* process doing the import. A real environment variable still wins over the file, matching how every
|
|
154
154
|
* other dotenv reader behaves and how Compose itself resolves the same names.
|
|
155
155
|
*/
|
|
@@ -157,6 +157,37 @@ function configLoadEnv(cwd: string): NodeJS.ProcessEnv {
|
|
|
157
157
|
return { ...readEnvFile(cwd), ...process.env }
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
/**
|
|
161
|
+
* A config file exists but could not be evaluated.
|
|
162
|
+
*
|
|
163
|
+
* Distinct from absence on purpose. Six call sites catch config loading so the CLI still works
|
|
164
|
+
* outside a project, and while they treated both cases the same, a config that failed to load
|
|
165
|
+
* looked exactly like no config at all. That is how a standalone binary that could not read any
|
|
166
|
+
* config shipped in v0.1.9 and still appeared to function: `status` printed a stack of stopped
|
|
167
|
+
* services, and `db check` fell back to DATABASE_URL from .env and reported a connection error.
|
|
168
|
+
* Those call sites should swallow absence and re-throw this.
|
|
169
|
+
*/
|
|
170
|
+
export class ConfigLoadError extends Error {
|
|
171
|
+
readonly configPath: string
|
|
172
|
+
|
|
173
|
+
constructor(configPath: string, detail: string) {
|
|
174
|
+
super(`Failed to load ${configPath}:\n${detail}`)
|
|
175
|
+
this.name = "ConfigLoadError"
|
|
176
|
+
this.configPath = configPath
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Re-throw a config that exists but could not be read; swallow anything else.
|
|
182
|
+
*
|
|
183
|
+
* For the call sites that catch config loading so the CLI still works outside a project. They
|
|
184
|
+
* want to ignore absence, not breakage, and treating the two alike is what let a standalone
|
|
185
|
+
* binary that could read no config at all still look like it was working.
|
|
186
|
+
*/
|
|
187
|
+
export function rethrowIfConfigBroken(err: unknown): void {
|
|
188
|
+
if (err instanceof ConfigLoadError) throw err
|
|
189
|
+
}
|
|
190
|
+
|
|
160
191
|
function loadFirstTsConfig(
|
|
161
192
|
cwd: string,
|
|
162
193
|
candidates: string[],
|
|
@@ -166,24 +197,19 @@ function loadFirstTsConfig(
|
|
|
166
197
|
if (!existsSync(configPath)) continue
|
|
167
198
|
|
|
168
199
|
const urlPath = "file:///" + configPath.replace(/\\/g, "/")
|
|
169
|
-
const
|
|
170
|
-
const mod = await import(${JSON.stringify(urlPath)})
|
|
171
|
-
const config = mod.default ?? mod
|
|
172
|
-
process.stdout.write(JSON.stringify(config))
|
|
173
|
-
`
|
|
174
|
-
const result = evalTsSnippet(snippet, { cwd, env: configLoadEnv(cwd) })
|
|
200
|
+
const result = importModuleAsJson(urlPath, { cwd, env: configLoadEnv(cwd) })
|
|
175
201
|
if (result.exitCode === 0) {
|
|
176
202
|
return JSON.parse(result.stdout) as Record<string, unknown>
|
|
177
203
|
}
|
|
178
204
|
|
|
179
205
|
const failure = result.stderr || result.stdout
|
|
180
206
|
if (!shouldStripCliImportOnLoadFailure(failure)) {
|
|
181
|
-
throw new
|
|
207
|
+
throw new ConfigLoadError(candidate, failure)
|
|
182
208
|
}
|
|
183
209
|
|
|
184
210
|
const fallback = loadTsConfigWithoutCliImport(configPath, cwd)
|
|
185
211
|
if (fallback !== null) return fallback
|
|
186
|
-
throw new
|
|
212
|
+
throw new ConfigLoadError(candidate, failure)
|
|
187
213
|
}
|
|
188
214
|
return null
|
|
189
215
|
}
|
|
@@ -217,12 +243,7 @@ function loadTsConfigWithoutCliImport(
|
|
|
217
243
|
writeFileSync(tmpPath, wrapper, "utf8")
|
|
218
244
|
try {
|
|
219
245
|
const urlPath = "file:///" + tmpPath.replace(/\\/g, "/")
|
|
220
|
-
const
|
|
221
|
-
const mod = await import(${JSON.stringify(urlPath)})
|
|
222
|
-
const config = mod.default ?? mod
|
|
223
|
-
process.stdout.write(JSON.stringify(config))
|
|
224
|
-
`
|
|
225
|
-
const result = evalTsSnippet(snippet, { cwd, env: configLoadEnv(cwd) })
|
|
246
|
+
const result = importModuleAsJson(urlPath, { cwd, env: configLoadEnv(cwd) })
|
|
226
247
|
if (result.exitCode !== 0) return null
|
|
227
248
|
return JSON.parse(result.stdout) as Record<string, unknown>
|
|
228
249
|
} finally {
|
package/src/db-preflight.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Written for the bring-your-own-Postgres case: an external database will not look like
|
|
5
5
|
* `supatype/postgres`, and the failures it produces are mostly indirect. The four API roles are
|
|
6
|
-
* the sharpest example
|
|
6
|
+
* the sharpest example: every `GRANT` the engine emits is guarded with
|
|
7
7
|
* `IF EXISTS (SELECT 1 FROM pg_roles …)`, so a missing role is skipped in silence and the symptom
|
|
8
8
|
* is an API that cannot switch roles at all, days later.
|
|
9
9
|
*
|
|
@@ -31,7 +31,7 @@ export interface CheckResult {
|
|
|
31
31
|
/** SQL that resolves it, if SQL can. */
|
|
32
32
|
remedy?: string
|
|
33
33
|
/**
|
|
34
|
-
* True when the remedy cannot run inside a transaction
|
|
34
|
+
* True when the remedy cannot run inside a transaction, server settings, chiefly. `--fix`
|
|
35
35
|
* refuses these and reports them rather than half-applying.
|
|
36
36
|
*/
|
|
37
37
|
remedyNeedsOperator?: boolean
|
|
@@ -117,7 +117,7 @@ const REQUIRED_ROLES = ["anon", "authenticated", "service_role", "authenticator"
|
|
|
117
117
|
* Stands in for the `authenticator` password when the operator has not supplied one.
|
|
118
118
|
*
|
|
119
119
|
* Fine to print for a human to replace; never acceptable to execute. `--fix` refuses when a remedy
|
|
120
|
-
* still contains it, because the decision was that this credential belongs to the operator
|
|
120
|
+
* still contains it, because the decision was that this credential belongs to the operator,
|
|
121
121
|
* Supatype does not invent one for a database it does not own, and creating a role with a
|
|
122
122
|
* placeholder password would be worse than refusing.
|
|
123
123
|
*/
|
|
@@ -225,13 +225,13 @@ export async function runPreflight(
|
|
|
225
225
|
})
|
|
226
226
|
|
|
227
227
|
// `CREATE` on the database, which the check above only asked about when the target schema was
|
|
228
|
-
// missing
|
|
228
|
+
// missing: and `public` always exists, so on a database where the role cannot create schemas that
|
|
229
229
|
// check passed while the stack could not start.
|
|
230
230
|
//
|
|
231
231
|
// Measured on a role with `CONNECT` and `USAGE ON SCHEMA public` but no `CREATE`: storage's
|
|
232
232
|
// bootstrap fails `42501 permission denied for database`, and so does every one of the engine's own
|
|
233
233
|
// schemas. Storage was the least examined service in this plan and this is the whole of what it
|
|
234
|
-
// needs
|
|
234
|
+
// needs: no extensions, no superuser, no replication, just somewhere to put `storage.buckets` and
|
|
235
235
|
// `storage.objects`.
|
|
236
236
|
results.push({
|
|
237
237
|
id: "create-schemas",
|
|
@@ -274,7 +274,7 @@ export async function runPreflight(
|
|
|
274
274
|
// create role" because BYPASSRLS is superuser-only. Better to say so up front.
|
|
275
275
|
(needsSuperuserForRoles(missing, privs.is_super)
|
|
276
276
|
? "\n Note: service_role needs BYPASSRLS, which only a superuser can grant. " +
|
|
277
|
-
`This connection is not a superuser, so --fix cannot create it
|
|
277
|
+
`This connection is not a superuser, so --fix cannot create it, have someone with ` +
|
|
278
278
|
`superuser apply the emitted SQL, or create service_role first.`
|
|
279
279
|
: ""),
|
|
280
280
|
remedy: roleRemedy(missing, opts.authenticatorPassword),
|
|
@@ -333,7 +333,7 @@ export async function runPreflight(
|
|
|
333
333
|
results.push(
|
|
334
334
|
extensionCheck("pgcrypto", installed, available, {
|
|
335
335
|
title: "pgcrypto",
|
|
336
|
-
needs: "UUID primary keys
|
|
336
|
+
needs: "UUID primary keys, the engine emits gen_random_uuid() defaults.",
|
|
337
337
|
whenMissing: "fail",
|
|
338
338
|
}),
|
|
339
339
|
)
|
|
@@ -359,14 +359,14 @@ export async function runPreflight(
|
|
|
359
359
|
detail: installed.has("supatype_mask")
|
|
360
360
|
? "enforced by supatype_mask, the planner rewrite (tier 1)"
|
|
361
361
|
: available.has("supatype_mask")
|
|
362
|
-
? "supatype_mask is available but not installed
|
|
362
|
+
? "supatype_mask is available but not installed, rules will be enforced by generated api views (tier 2)"
|
|
363
363
|
: "enforced by generated api views (tier 2); supatype_mask is not available on this server",
|
|
364
364
|
...(!installed.has("supatype_mask") && {
|
|
365
365
|
impact:
|
|
366
366
|
"Tier 2 puts the masking expression in a view instead of the query planner. It is the same " +
|
|
367
367
|
"behaviour with one exception: an aggregate over a masked column returns the sum of what the " +
|
|
368
|
-
"caller may read, where tier 1 raises. Unreachable through the API
|
|
369
|
-
"aggregates disabled and Supatype never enables them
|
|
368
|
+
"caller may read, where tier 1 raises. Unreachable through the API, PostgREST ships with " +
|
|
369
|
+
"aggregates disabled and Supatype never enables them, so this matters only for direct SQL " +
|
|
370
370
|
"against the api views. PostgREST is pointed at `api` instead of your managed schema, and the " +
|
|
371
371
|
"API roles hold their privileges on the views rather than the tables.",
|
|
372
372
|
...(available.has("supatype_mask") && {
|
|
@@ -413,7 +413,7 @@ export async function runPreflight(
|
|
|
413
413
|
// The definitive realtime check: create the slot realtime would create, then drop it.
|
|
414
414
|
//
|
|
415
415
|
// There is no catalog to consult. `wal2json` is a shared library, not an extension, so it never
|
|
416
|
-
// appears in `pg_available_extensions
|
|
416
|
+
// appears in `pg_available_extensions`: the only way to know whether this database can decode is
|
|
417
417
|
// to ask it to. Cloud SQL does not ship the plugin at all; plenty of managed Postgres will not
|
|
418
418
|
// grant `REPLICATION`. Both produce a stack that looks healthy until the first subscription.
|
|
419
419
|
//
|
|
@@ -448,7 +448,7 @@ export async function runPreflight(
|
|
|
448
448
|
(names.length > 8 ? `, … and ${names.length - 8} more` : ""),
|
|
449
449
|
impact:
|
|
450
450
|
"These are not described by your schema. Model them, or keep them out of the managed " +
|
|
451
|
-
"schema
|
|
451
|
+
"schema: a push only manages what it created.",
|
|
452
452
|
})
|
|
453
453
|
}
|
|
454
454
|
}
|
|
@@ -488,7 +488,7 @@ function extensionCheck(
|
|
|
488
488
|
* SQL to create the missing API roles.
|
|
489
489
|
*
|
|
490
490
|
* `authenticator` needs a password because PostgREST logs in as it; the others never do. The
|
|
491
|
-
* password is the operator's to supply
|
|
491
|
+
* password is the operator's to supply, Supatype does not invent a credential for a database it
|
|
492
492
|
* does not own.
|
|
493
493
|
*/
|
|
494
494
|
function roleRemedy(missing: readonly string[], authenticatorPassword?: string): string {
|
|
@@ -518,7 +518,7 @@ function roleRemedy(missing: readonly string[], authenticatorPassword?: string):
|
|
|
518
518
|
* Whether creating the missing roles needs superuser rather than merely CREATEROLE.
|
|
519
519
|
*
|
|
520
520
|
* `service_role` carries `BYPASSRLS`, and only a superuser may set that attribute. A CREATEROLE
|
|
521
|
-
* operator
|
|
521
|
+
* operator: which is what managed Postgres typically gives you, gets "permission denied to
|
|
522
522
|
* create role" with no indication that the attribute is the reason.
|
|
523
523
|
*/
|
|
524
524
|
function needsSuperuserForRoles(missing: readonly string[], isSuperuser: boolean): boolean {
|
|
@@ -551,7 +551,7 @@ export function operatorRemedies(report: PreflightReport): CheckResult[] {
|
|
|
551
551
|
* Postgres, so any failure rolls the whole thing back and the database is exactly as it was. That
|
|
552
552
|
* is the guarantee worth having when the database belongs to someone else.
|
|
553
553
|
*
|
|
554
|
-
* Server settings are the exception and are never included
|
|
554
|
+
* Server settings are the exception and are never included, `ALTER SYSTEM` cannot run in a
|
|
555
555
|
* transaction, and needs a restart besides. Those are reported for the operator to do.
|
|
556
556
|
*/
|
|
557
557
|
export async function applyRemedies(
|
package/src/dev-compose.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `supatype dev` when `provider: docker
|
|
2
|
+
* `supatype dev` when `provider: docker`, full self-host Compose stack (Kong gateway).
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs"
|
|
@@ -99,10 +99,10 @@ async function resolveDevDbPort(cwd: string): Promise<number> {
|
|
|
99
99
|
/**
|
|
100
100
|
* The database URL to hand a tool, from wherever it happens to run.
|
|
101
101
|
*
|
|
102
|
-
* The compose helpers below describe the `db` *container
|
|
102
|
+
* The compose helpers below describe the `db` *container*, on the host at
|
|
103
103
|
* `SUPATYPE_DEV_DB_PORT`, or in-network at `db:5432`. Neither exists for a project pointed at an
|
|
104
104
|
* external database, and passing one produced "pool timed out while waiting for an open connection"
|
|
105
|
-
|
|
105
|
+
*- a message with nothing in it about the URL being wrong.
|
|
106
106
|
*/
|
|
107
107
|
function projectDatabaseUrl(cwd: string, config: SupatypeProjectConfig, inNetwork = false): string {
|
|
108
108
|
if (usesExternalDatabase(config)) return connectionString(config)
|
|
@@ -193,7 +193,7 @@ async function resolveKongPort(cwd: string): Promise<number> {
|
|
|
193
193
|
* Bring up the compose `db` service and wait for it to answer.
|
|
194
194
|
*
|
|
195
195
|
* A no-op when the project uses an external database: there is no such service, the database is
|
|
196
|
-
* already running, and its readiness is the operator's
|
|
196
|
+
* already running, and its readiness is the operator's, `supatype db check` is what reports on it.
|
|
197
197
|
* Four call sites did this inline, so an external project hit `compose up -d db` against a compose
|
|
198
198
|
* file that deliberately has no `db`.
|
|
199
199
|
*/
|
|
@@ -233,7 +233,7 @@ function upsertDevComposeEnv(
|
|
|
233
233
|
// `POSTGRES_PASSWORD` and `JWT_SECRET` are deliberately absent.
|
|
234
234
|
//
|
|
235
235
|
// They used to be pinned here to published constants on every `dev` run, against the same
|
|
236
|
-
// `.env` a self-host deployment reads
|
|
236
|
+
// `.env` a self-host deployment reads, so a project could not hold its own secrets, and one
|
|
237
237
|
// `dev` after generating them put the published values back with nothing to show it. They are
|
|
238
238
|
// now resolved from `.env` (see `local-secrets.ts`) and written by `init` alone.
|
|
239
239
|
//
|
|
@@ -242,12 +242,12 @@ function upsertDevComposeEnv(
|
|
|
242
242
|
// would desync them from the secret that validates them.
|
|
243
243
|
const updates: Record<string, string> = {
|
|
244
244
|
// The docker dev path renders the *self-host* compose file, where the secrets have no
|
|
245
|
-
// defaults
|
|
245
|
+
// defaults: an unset value is a hard compose error rather than a service quietly starting
|
|
246
246
|
// with a published constant. This guarantees presence without overwriting: only keys
|
|
247
247
|
// genuinely absent from `.env` are filled, and with the value the project has been running
|
|
248
248
|
// with rather than a fresh one.
|
|
249
249
|
...seedMissingLocalSecrets(cwd),
|
|
250
|
-
// Project configuration, seeded not overwritten
|
|
250
|
+
// Project configuration, seeded not overwritten, see seedMissingDatabaseIdentity.
|
|
251
251
|
...seedMissingDatabaseIdentity(cwd),
|
|
252
252
|
ANON_KEY: anonKey,
|
|
253
253
|
SERVICE_ROLE_KEY: serviceRoleKey,
|
|
@@ -264,13 +264,13 @@ function upsertDevComposeEnv(
|
|
|
264
264
|
...(localServerImage !== undefined && { SUPATYPE_SERVER_IMAGE: localServerImage }),
|
|
265
265
|
}
|
|
266
266
|
// Never for an external database: this URL describes the `db` container, which that project does
|
|
267
|
-
// not have. Writing it overwrote the operator's own DATABASE_URL
|
|
268
|
-
// every CLI command reads
|
|
267
|
+
// not have. Writing it overwrote the operator's own DATABASE_URL, the value the whole stack and
|
|
268
|
+
// every CLI command reads, with a DSN pointing at a database that does not exist. Found by
|
|
269
269
|
// rehearsing a push against a real external Postgres, where the compose guard then refused to
|
|
270
270
|
// proceed because .env and the config disagreed. They disagreed because of this line.
|
|
271
271
|
if (devDbPort !== undefined && !usesExternalDatabase(config)) {
|
|
272
272
|
updates.SUPATYPE_DEV_DB_PORT = String(devDbPort)
|
|
273
|
-
// User and database from the project, not hardcoded
|
|
273
|
+
// User and database from the project, not hardcoded, same reason as
|
|
274
274
|
// `seedMissingDatabaseIdentity`: a project not named "supatype" had this URL pointing at a
|
|
275
275
|
// database that does not exist.
|
|
276
276
|
const dbUser = readEnvValue(cwd, "POSTGRES_USER", "supatype_admin")
|
|
@@ -358,7 +358,7 @@ function dumpComposeDbLogs(
|
|
|
358
358
|
maxBuffer: 10 * 1024 * 1024,
|
|
359
359
|
})
|
|
360
360
|
const body = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim()
|
|
361
|
-
const content = body || `(empty
|
|
361
|
+
const content = body || `(empty, docker compose logs db exit ${result.status ?? "?"})\n`
|
|
362
362
|
try {
|
|
363
363
|
writeFileSync(logPath, content)
|
|
364
364
|
} catch {
|
|
@@ -403,7 +403,7 @@ async function waitStorageApiReady(
|
|
|
403
403
|
const body = await res.text()
|
|
404
404
|
const kongUpstreamDown = body.includes("invalid response was received from the upstream server")
|
|
405
405
|
if (!kongUpstreamDown && res.status !== 503) {
|
|
406
|
-
// Non-transient storage response (e.g. 401)
|
|
406
|
+
// Non-transient storage response (e.g. 401), stop waiting.
|
|
407
407
|
return
|
|
408
408
|
}
|
|
409
409
|
} catch {
|
|
@@ -412,7 +412,7 @@ async function waitStorageApiReady(
|
|
|
412
412
|
await new Promise((r) => setTimeout(r, 1000))
|
|
413
413
|
}
|
|
414
414
|
console.warn(
|
|
415
|
-
`[supatype] Storage API at ${url} did not become ready within ${maxSec}s
|
|
415
|
+
`[supatype] Storage API at ${url} did not become ready within ${maxSec}s, bucket provisioning may fail.`,
|
|
416
416
|
)
|
|
417
417
|
}
|
|
418
418
|
|
|
@@ -445,7 +445,7 @@ async function refreshSchemaArtifacts(
|
|
|
445
445
|
await ensureEngine()
|
|
446
446
|
} catch (err) {
|
|
447
447
|
console.warn(
|
|
448
|
-
`[supatype] Host engine unavailable
|
|
448
|
+
`[supatype] Host engine unavailable, admin/types not refreshed: ${(err as Error).message}`,
|
|
449
449
|
)
|
|
450
450
|
return
|
|
451
451
|
}
|
|
@@ -480,7 +480,7 @@ async function refreshSchemaArtifacts(
|
|
|
480
480
|
console.log("[supatype] Admin config written to .supatype/admin-config.json")
|
|
481
481
|
} catch (err) {
|
|
482
482
|
console.warn(
|
|
483
|
-
`[supatype] Admin config generation failed
|
|
483
|
+
`[supatype] Admin config generation failed, Studio may show stale field widgets: ${(err as Error).message}`,
|
|
484
484
|
)
|
|
485
485
|
}
|
|
486
486
|
}
|
|
@@ -498,7 +498,7 @@ async function runComposeSchemaPush(
|
|
|
498
498
|
const supatypeDir = join(cwd, ".supatype")
|
|
499
499
|
mkdirSync(supatypeDir, { recursive: true })
|
|
500
500
|
const astPath = join(supatypeDir, "schema.ast.json")
|
|
501
|
-
// Always materialise on disk
|
|
501
|
+
// Always materialise on disk, schema-engine reads via bind mount; skip must not omit the write.
|
|
502
502
|
writeFileSync(astPath, astJson)
|
|
503
503
|
if (astJson === _lastPushedAst && astJson !== _lastFailedAst) return
|
|
504
504
|
|
|
@@ -506,7 +506,7 @@ async function runComposeSchemaPush(
|
|
|
506
506
|
throw new Error(`Failed to write schema AST at ${astPath}`)
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
-
// Admin + types come from the AST only (no DB)
|
|
509
|
+
// Admin + types come from the AST only (no DB), refresh before push so Studio stays
|
|
510
510
|
// in sync even when migration fails (e.g. bad engine image, lossy column change).
|
|
511
511
|
await refreshSchemaArtifacts(cwd, config, ast)
|
|
512
512
|
|
|
@@ -552,7 +552,7 @@ async function runComposeSchemaPush(
|
|
|
552
552
|
}
|
|
553
553
|
return result
|
|
554
554
|
}
|
|
555
|
-
// B: only hold the advisory lock when realtime is already decoding
|
|
555
|
+
// B: only hold the advisory lock when realtime is already decoding, first-boot
|
|
556
556
|
// push (db only) must not take the lock; that path crashed under lock+DDL in CI.
|
|
557
557
|
const push = composeServiceIsRunning(paths, cwd, composeProject, "realtime")
|
|
558
558
|
? await withComposeSchemaPushLock(paths, cwd, composeProject, runPush)
|
|
@@ -711,7 +711,7 @@ async function runComposeEngineDiff(
|
|
|
711
711
|
|
|
712
712
|
/**
|
|
713
713
|
* `supatype diff` when `provider: docker`. Uses in-compose schema-engine unless
|
|
714
|
-
* `overrides.engine` is set
|
|
714
|
+
* `overrides.engine` is set: then Postgres is published to the host and diff runs
|
|
715
715
|
* through the local engine binary.
|
|
716
716
|
*/
|
|
717
717
|
export async function diffSchemaDocker(cwd: string, config: SupatypeProjectConfig): Promise<DiffResult> {
|
|
@@ -772,7 +772,7 @@ export async function diffSchemaDocker(cwd: string, config: SupatypeProjectConfi
|
|
|
772
772
|
|
|
773
773
|
/**
|
|
774
774
|
* `supatype push` when `provider: docker`. Uses in-compose schema-engine unless
|
|
775
|
-
* `overrides.engine` is set
|
|
775
|
+
* `overrides.engine` is set: then Postgres is published to the host and push runs
|
|
776
776
|
* through the local engine binary (AST v2, contributor builds).
|
|
777
777
|
*/
|
|
778
778
|
export async function pushSchemaDocker(cwd: string, config: SupatypeProjectConfig): Promise<void> {
|
|
@@ -782,7 +782,7 @@ export async function pushSchemaDocker(cwd: string, config: SupatypeProjectConfi
|
|
|
782
782
|
const project = composeProjectName(config.project.name)
|
|
783
783
|
const kongPort = await resolveKongPort(cwd)
|
|
784
784
|
// No dev db port for an external database: `ensureDevDbPort` allocates a host port for the `db`
|
|
785
|
-
// container *and persists a matching DATABASE_URL*, which overwrote the operator's own URL
|
|
785
|
+
// container *and persists a matching DATABASE_URL*, which overwrote the operator's own URL, the
|
|
786
786
|
// one the whole stack and every CLI command reads.
|
|
787
787
|
const devDbPort =
|
|
788
788
|
hasEngineOverride(config) && !usesExternalDatabase(config)
|
|
@@ -798,7 +798,7 @@ export async function pushSchemaDocker(cwd: string, config: SupatypeProjectConfi
|
|
|
798
798
|
const paths = writeSelfHostCompose(cwd, config, { devLocal: true })
|
|
799
799
|
const pushBrand = { intro: "Push schema" }
|
|
800
800
|
|
|
801
|
-
console.log(`[supatype] provider
|
|
801
|
+
console.log(`[supatype] provider docker, applying schema via compose (project ${project})...`)
|
|
802
802
|
await startComposeDatabase(config, paths, cwd, project, pushBrand)
|
|
803
803
|
|
|
804
804
|
const schemaPath = schemaPathFromProject(config, cwd)
|
|
@@ -833,7 +833,7 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
|
|
|
833
833
|
const project = composeProjectName(config.project.name)
|
|
834
834
|
const kongPort = await resolveKongPort(cwd)
|
|
835
835
|
// No dev db port for an external database: `ensureDevDbPort` allocates a host port for the `db`
|
|
836
|
-
// container *and persists a matching DATABASE_URL*, which overwrote the operator's own URL
|
|
836
|
+
// container *and persists a matching DATABASE_URL*, which overwrote the operator's own URL, the
|
|
837
837
|
// one the whole stack and every CLI command reads.
|
|
838
838
|
const devDbPort =
|
|
839
839
|
hasEngineOverride(config) && !usesExternalDatabase(config)
|
|
@@ -850,7 +850,7 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
|
|
|
850
850
|
|
|
851
851
|
ensureDevComposeEnv(cwd, config, anonKey, serviceRoleKey, kongPort, devDbPort, localServerImage)
|
|
852
852
|
|
|
853
|
-
console.log(`[supatype] provider
|
|
853
|
+
console.log(`[supatype] provider docker, starting self-host Compose stack (project ${project}, gateway :${kongPort})...`)
|
|
854
854
|
const paths = writeSelfHostCompose(cwd, config, { devLocal: true })
|
|
855
855
|
if (ensureDevApiConfig(cwd)) {
|
|
856
856
|
console.log("[supatype] API config written to .supatype/api-config.json")
|
|
@@ -898,7 +898,7 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
|
|
|
898
898
|
console.log("[supatype] Bringing up Postgres (compose db)...")
|
|
899
899
|
}
|
|
900
900
|
await startComposeDatabase(config, paths, cwd, project, devBrand, 180_000, endDevSession)
|
|
901
|
-
// Settle before DDL
|
|
901
|
+
// Settle before DDL: pg_isready can pass slightly before the instance is stable.
|
|
902
902
|
await new Promise((r) => setTimeout(r, 3000))
|
|
903
903
|
|
|
904
904
|
// A: apply schema before realtime (and the rest of the stack) starts decoding WAL.
|
|
@@ -1034,7 +1034,7 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
|
|
|
1034
1034
|
shutdownState.studioProc = studioProc
|
|
1035
1035
|
if (studioProc) {
|
|
1036
1036
|
console.log(
|
|
1037
|
-
`[supatype] Studio (overrides.studio)
|
|
1037
|
+
`[supatype] Studio (overrides.studio): live reload proxied at http://localhost:${kongPort}/studio/`,
|
|
1038
1038
|
)
|
|
1039
1039
|
}
|
|
1040
1040
|
}
|
|
@@ -1122,6 +1122,6 @@ function grantAuthSchemaAccess(
|
|
|
1122
1122
|
{ cwd: composeDir, encoding: "utf8", timeout: 10_000 },
|
|
1123
1123
|
)
|
|
1124
1124
|
if (result.status !== 0) {
|
|
1125
|
-
console.warn("[supatype] Could not grant service_role access to auth.users
|
|
1125
|
+
console.warn("[supatype] Could not grant service_role access to auth.users, Studio relation preview may fail.")
|
|
1126
1126
|
}
|
|
1127
1127
|
}
|