@supatype/cli 0.1.6 → 0.1.9
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 +152 -102
- package/.turbo/turbo-typecheck.log +1 -1
- package/LICENSE +202 -0
- package/deno.d.ts +52 -0
- package/dist/app/framework.js +1 -1
- package/dist/app/framework.js.map +1 -1
- package/dist/augmentation-generator.d.ts +22 -0
- package/dist/augmentation-generator.d.ts.map +1 -1
- package/dist/augmentation-generator.js +19 -5
- package/dist/augmentation-generator.js.map +1 -1
- package/dist/binary-cache.d.ts +42 -6
- package/dist/binary-cache.d.ts.map +1 -1
- package/dist/binary-cache.js +76 -44
- package/dist/binary-cache.js.map +1 -1
- package/dist/cache-clean.d.ts +15 -0
- package/dist/cache-clean.d.ts.map +1 -0
- package/dist/cache-clean.js +76 -0
- package/dist/cache-clean.js.map +1 -0
- package/dist/cache-pins.d.ts +51 -0
- package/dist/cache-pins.d.ts.map +1 -0
- package/dist/cache-pins.js +184 -0
- package/dist/cache-pins.js.map +1 -0
- 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 +27 -0
- package/dist/cli-install-method.d.ts.map +1 -0
- package/dist/cli-install-method.js +78 -0
- package/dist/cli-install-method.js.map +1 -0
- 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.d.ts.map +1 -1
- package/dist/cli.js +7 -1
- package/dist/cli.js.map +1 -1
- package/dist/cloud-credentials.d.ts +34 -0
- package/dist/cloud-credentials.d.ts.map +1 -0
- package/dist/cloud-credentials.js +139 -0
- package/dist/cloud-credentials.js.map +1 -0
- package/dist/commands/add.js +1 -1
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/admin.d.ts +11 -0
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +123 -21
- 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.d.ts.map +1 -1
- package/dist/commands/cache.js +69 -73
- package/dist/commands/cache.js.map +1 -1
- package/dist/commands/cloud.d.ts.map +1 -1
- package/dist/commands/cloud.js +32 -8
- package/dist/commands/cloud.js.map +1 -1
- package/dist/commands/db.d.ts +3 -2
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +149 -3
- 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.d.ts.map +1 -1
- package/dist/commands/dev.js +63 -23
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts +28 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +60 -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 +7 -0
- package/dist/commands/functions.d.ts.map +1 -1
- package/dist/commands/functions.js +45 -11
- package/dist/commands/functions.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +5 -1
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/hooks.d.ts +12 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +118 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +64 -30
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/internal.d.ts +6 -0
- package/dist/commands/internal.d.ts.map +1 -0
- package/dist/commands/internal.js +22 -0
- package/dist/commands/internal.js.map +1 -0
- package/dist/commands/link-helpers.d.ts.map +1 -1
- package/dist/commands/link-helpers.js +8 -2
- package/dist/commands/link-helpers.js.map +1 -1
- package/dist/commands/login.d.ts +7 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +110 -0
- package/dist/commands/login.js.map +1 -0
- 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.d.ts.map +1 -1
- package/dist/commands/push.js +110 -7
- 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 +3 -2
- package/dist/commands/self-update.d.ts.map +1 -1
- package/dist/commands/self-update.js +31 -16
- 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 +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +19 -4
- package/dist/config.js.map +1 -1
- package/dist/db-preflight.d.ts +78 -0
- package/dist/db-preflight.d.ts.map +1 -0
- package/dist/db-preflight.js +452 -0
- package/dist/db-preflight.js.map +1 -0
- package/dist/dev-compose.d.ts +4 -4
- package/dist/dev-compose.d.ts.map +1 -1
- package/dist/dev-compose.js +143 -88
- 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 +40 -2
- package/dist/env-file.d.ts.map +1 -1
- package/dist/env-file.js +132 -11
- package/dist/env-file.js.map +1 -1
- package/dist/field-masking-tier.d.ts +50 -0
- package/dist/field-masking-tier.d.ts.map +1 -0
- package/dist/field-masking-tier.js +91 -0
- package/dist/field-masking-tier.js.map +1 -0
- package/dist/functions-deno-types.d.ts +24 -0
- package/dist/functions-deno-types.d.ts.map +1 -0
- package/dist/functions-deno-types.js +155 -0
- package/dist/functions-deno-types.js.map +1 -0
- 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 +30 -0
- package/dist/hook-upload.d.ts.map +1 -0
- package/dist/hook-upload.js +86 -0
- package/dist/hook-upload.js.map +1 -0
- package/dist/hooks-generator.d.ts +8 -0
- package/dist/hooks-generator.d.ts.map +1 -0
- package/dist/hooks-generator.js +332 -0
- package/dist/hooks-generator.js.map +1 -0
- 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 +4 -0
- package/dist/link.d.ts.map +1 -1
- package/dist/link.js +2 -0
- package/dist/link.js.map +1 -1
- package/dist/local-secrets.d.ts +71 -0
- package/dist/local-secrets.d.ts.map +1 -0
- package/dist/local-secrets.js +100 -0
- package/dist/local-secrets.js.map +1 -0
- 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 +73 -0
- package/dist/model-hooks.d.ts.map +1 -0
- package/dist/model-hooks.js +211 -0
- package/dist/model-hooks.js.map +1 -0
- 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 +12 -1
- package/dist/postgres-ctl.d.ts.map +1 -1
- package/dist/postgres-ctl.js +28 -5
- 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 +122 -2
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +150 -1
- 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.js +1 -1
- package/dist/resolve-api-url.js.map +1 -1
- package/dist/resolve-target.d.ts +4 -0
- package/dist/resolve-target.d.ts.map +1 -1
- package/dist/resolve-target.js +69 -73
- package/dist/resolve-target.js.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 +5 -4
- package/dist/schema-ast-v2.d.ts.map +1 -1
- package/dist/schema-ast-v2.js +5 -3
- package/dist/schema-ast-v2.js.map +1 -1
- package/dist/schema-push-lock.d.ts.map +1 -1
- package/dist/schema-push-lock.js +43 -16
- 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 +26 -2
- package/dist/self-host-compose.d.ts.map +1 -1
- package/dist/self-host-compose.js +305 -73
- package/dist/self-host-compose.js.map +1 -1
- package/dist/service-role-check.d.ts +21 -0
- package/dist/service-role-check.d.ts.map +1 -0
- package/dist/service-role-check.js +85 -0
- package/dist/service-role-check.js.map +1 -0
- 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.d.ts +17 -0
- package/dist/target-client.d.ts.map +1 -1
- package/dist/target-client.js +69 -17
- package/dist/target-client.js.map +1 -1
- package/dist/tsx-runner.js +1 -1
- package/dist/tsx-runner.js.map +1 -1
- package/dist/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor.js +738 -34
- 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 +2 -2
- 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.d.ts.map +1 -1
- package/dist/ui/runtime/stdin-after-ink.js +4 -2
- 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 +6 -2
- package/scripts/build-release-artifacts.sh +112 -0
- package/scripts/generate-homebrew-formula.mjs +123 -0
- 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 +19 -6
- package/src/binary-cache.ts +94 -47
- package/src/cache-clean.ts +81 -0
- package/src/cache-pins.ts +243 -0
- package/src/cli-artifacts.ts +65 -0
- package/src/cli-install-method.ts +87 -0
- 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 +7 -1
- package/src/cloud-credentials.ts +183 -0
- package/src/commands/add.ts +1 -1
- package/src/commands/admin.ts +142 -27
- package/src/commands/adopt.ts +1 -1
- package/src/commands/app.ts +2 -2
- package/src/commands/cache.ts +92 -77
- package/src/commands/cloud.ts +32 -8
- package/src/commands/db.ts +164 -3
- package/src/commands/deploy.ts +12 -12
- package/src/commands/dev.ts +72 -23
- package/src/commands/doctor.ts +69 -3
- package/src/commands/engine.ts +1 -1
- package/src/commands/functions.ts +47 -10
- package/src/commands/generate.ts +5 -1
- package/src/commands/hooks.ts +138 -0
- package/src/commands/init.ts +65 -31
- package/src/commands/internal.ts +23 -0
- package/src/commands/link-helpers.ts +9 -2
- package/src/commands/login.ts +127 -0
- 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 +124 -7
- package/src/commands/self-host.ts +1 -1
- package/src/commands/self-update.ts +41 -19
- package/src/commands/status.ts +1 -1
- package/src/commands/update.ts +1 -1
- package/src/config.ts +23 -4
- package/src/db-preflight.ts +575 -0
- package/src/dev-compose.ts +158 -89
- 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 +157 -11
- package/src/field-masking-tier.ts +107 -0
- package/src/functions-deno-types.ts +188 -0
- package/src/functions-router-gen.ts +1 -1
- package/src/gitignore.ts +3 -3
- package/src/hook-upload.ts +107 -0
- package/src/hooks-generator.ts +352 -0
- package/src/index.ts +1 -1
- package/src/init-project-detect.ts +1 -1
- package/src/link.ts +7 -0
- package/src/local-secrets.ts +104 -0
- package/src/main.ts +20 -0
- package/src/model-hooks.ts +260 -0
- package/src/pgbouncer-userlist.ts +2 -2
- package/src/postgres-ctl.ts +32 -5
- package/src/process-manager.ts +4 -4
- package/src/project-config.ts +237 -3
- 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 +1 -1
- package/src/resolve-target.ts +113 -68
- package/src/rest-cache-admin.ts +2 -2
- package/src/runtime-routes.ts +2 -2
- package/src/schema-ast-v2.ts +8 -5
- package/src/schema-push-lock.ts +56 -17
- package/src/scripts/postinstall.ts +1 -1
- package/src/self-host-compose.ts +348 -72
- package/src/service-role-check.ts +111 -0
- 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 +105 -18
- package/src/tsx-runner.ts +1 -1
- package/src/type-extractor.ts +885 -30
- 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 +2 -2
- package/src/ui/runtime/run-clack-flow.tsx +1 -1
- package/src/ui/runtime/stdin-after-ink.ts +4 -2
- package/src/ui/theme.ts +1 -1
- package/src/valkey-sidecar.ts +2 -2
- package/tests/access-composition.test.ts +667 -0
- package/tests/admin-studio-membership.test.ts +121 -0
- package/tests/api-schemas.test.ts +83 -0
- package/tests/archive-name-alias.test.ts +42 -0
- package/tests/augmentation-generator.test.ts +28 -0
- package/tests/cache-clean.test.ts +206 -0
- package/tests/cli-help.test.ts +11 -2
- package/tests/cli-install-method.test.ts +66 -0
- package/tests/command-chrome-coverage.test.ts +31 -26
- package/tests/command-chrome-wrap.test.ts +1 -0
- package/tests/db-preflight.test.ts +72 -0
- package/tests/doctor-render.test.ts +62 -0
- package/tests/doctor-service-role.test.ts +55 -0
- package/tests/engine-push-output.test.ts +1 -1
- package/tests/env-file.test.ts +121 -0
- package/tests/external-database-compose.test.ts +184 -0
- package/tests/external-database-config.test.ts +154 -0
- package/tests/field-masking-tier.test.ts +184 -0
- package/tests/functions-deno-types.test.ts +197 -0
- package/tests/homebrew-formula.test.ts +100 -0
- package/tests/hook-upload.test.ts +125 -0
- package/tests/hooks-generator.test.ts +212 -0
- package/tests/init.test.ts +5 -2
- package/tests/keys.test.ts +8 -2
- package/tests/local-secrets.test.ts +92 -0
- package/tests/model-hooks.test.ts +322 -0
- package/tests/runtime-contract.test.ts +113 -1
- package/tests/service-role-check.test.ts +109 -0
- package/tests/service-role-routes.test.ts +51 -0
- package/tests/storage-provision.test.ts +1 -1
- package/tests/target-client-refresh.test.ts +94 -0
- package/tests/type-extractor.test.ts +187 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +7 -1
- package/scripts/build-release-bundle.mjs +0 -27
- package/tests/engine-distribution.test.ts +0 -418
package/src/docker-postgres.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* docker-postgres
|
|
2
|
+
* docker-postgres: manage a supatype/postgres Docker container for local dev.
|
|
3
3
|
*
|
|
4
4
|
* Used by `supatype dev` when database.provider = "docker".
|
|
5
5
|
* The container is named supatype-{projectName} and persists data in a
|
|
@@ -11,7 +11,7 @@ import { spawnSync } from "node:child_process"
|
|
|
11
11
|
export interface DockerPgOptions {
|
|
12
12
|
/** Docker image to run. Defaults to supatype/postgres:latest. */
|
|
13
13
|
image: string
|
|
14
|
-
/** Project name
|
|
14
|
+
/** Project name: used to derive the container and volume names. */
|
|
15
15
|
projectName: string
|
|
16
16
|
/** Host port to bind to container's 5432. */
|
|
17
17
|
port: number
|
|
@@ -85,7 +85,7 @@ export function dockerPgStart(opts: DockerPgOptions): void {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
-
* Stop the container (fast
|
|
88
|
+
* Stop the container (fast, does not remove it or the data volume).
|
|
89
89
|
* Safe to call even if the container is not running.
|
|
90
90
|
*/
|
|
91
91
|
export function dockerPgStop(projectName: string): void {
|
|
@@ -120,7 +120,7 @@ export function dockerPgPostInitServing(logs: string): boolean {
|
|
|
120
120
|
|
|
121
121
|
const initDone = logs.lastIndexOf("PostgreSQL init process complete")
|
|
122
122
|
if (initDone === -1) {
|
|
123
|
-
// Reused data volume
|
|
123
|
+
// Reused data volume: this run did not re-run entrypoint init.
|
|
124
124
|
return true
|
|
125
125
|
}
|
|
126
126
|
return initDone < lastReady
|
|
@@ -240,7 +240,7 @@ export function dockerDbUrl(projectName: string, port: number, password = DEFAUL
|
|
|
240
240
|
/**
|
|
241
241
|
* DB URL for processes sharing the Postgres container network namespace
|
|
242
242
|
* (supatype-server migrate in a one-shot container). Uses loopback inside the
|
|
243
|
-
* container where pg_hba grants trust
|
|
243
|
+
* container where pg_hba grants trust, avoids host-published-port SCRAM/SSL issues.
|
|
244
244
|
*/
|
|
245
245
|
export function dockerPgLoopbackDbUrl(projectName: string, password = DEFAULT_DEV_PASSWORD): string {
|
|
246
246
|
return `postgres://${PG_USER}:${password}@127.0.0.1:5432/${projectName}?sslmode=disable`
|
|
@@ -254,7 +254,7 @@ export const DEFAULT_SERVER_DOCKER_IMAGE = "supatype/server:latest"
|
|
|
254
254
|
|
|
255
255
|
/**
|
|
256
256
|
* Run `supatype-server migrate` on the Postgres container network (loopback trust).
|
|
257
|
-
* Used on Windows + database.provider docker
|
|
257
|
+
* Used on Windows + database.provider docker, host-published :5432 breaks libpq TLS there.
|
|
258
258
|
*/
|
|
259
259
|
export function runGotrueMigrationsViaDocker(
|
|
260
260
|
pgContainerName: string,
|
package/src/docker-runtime.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Docker daemon availability
|
|
2
|
+
* Docker daemon availability: used before `docker compose` and other docker CLI calls.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { spawnSync } from "node:child_process"
|
|
@@ -41,7 +41,7 @@ export function probeDockerDaemon(): DockerDaemonProbe {
|
|
|
41
41
|
const versionStderr = (version.stderr ?? "").trim()
|
|
42
42
|
const versionDetail = `${versionStderr}${version.stdout ?? ""}`.trim()
|
|
43
43
|
|
|
44
|
-
// No client version
|
|
44
|
+
// No client version: CLI missing or broken.
|
|
45
45
|
if (!clientVersion) {
|
|
46
46
|
return {
|
|
47
47
|
ok: false,
|
|
@@ -51,7 +51,7 @@ export function probeDockerDaemon(): DockerDaemonProbe {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// Client is present but daemon refused (paused/stopped). `docker info` can hang
|
|
54
|
-
// while paused on Docker Desktop
|
|
54
|
+
// while paused on Docker Desktop, use the version stderr and skip info.
|
|
55
55
|
if (version.status !== 0 && versionStderr) {
|
|
56
56
|
return { ok: false, reason: "daemon_unavailable", detail: versionStderr }
|
|
57
57
|
}
|
|
@@ -104,7 +104,7 @@ function dockerUnavailableHints(probe: Extract<DockerDaemonProbe, { ok: false }>
|
|
|
104
104
|
return hints
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
/** @deprecated Tests only
|
|
107
|
+
/** @deprecated Tests only: use `reportDockerUnavailable`. */
|
|
108
108
|
export function formatDockerUnavailableMessage(
|
|
109
109
|
probe: Extract<DockerDaemonProbe, { ok: false }>,
|
|
110
110
|
): string {
|
|
@@ -142,7 +142,7 @@ export function reportDockerUnavailable(
|
|
|
142
142
|
export function requireDockerDaemon(opts?: DockerReportOptions): void {
|
|
143
143
|
const probe = probeDockerDaemon()
|
|
144
144
|
if (probe.ok) return
|
|
145
|
-
// Dev TUI patches console
|
|
145
|
+
// Dev TUI patches console, restore stderr before printing a fatal message.
|
|
146
146
|
if (getActiveDevSession()) {
|
|
147
147
|
endDevSession()
|
|
148
148
|
}
|
package/src/engine-client.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine-client.ts
|
|
2
|
+
* engine-client.ts: subprocess-based engine invocation.
|
|
3
3
|
*
|
|
4
4
|
* Replaces the former HTTP-based engine client (Docker container API).
|
|
5
5
|
* All callers use the same interface; only the transport changed.
|
|
@@ -99,7 +99,7 @@ async function getEngineBin(): Promise<string> {
|
|
|
99
99
|
// Otherwise (no valid project config) fall through to default cache scan.
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
// No config found
|
|
102
|
+
// No config found: scan the cache for any available engine binary.
|
|
103
103
|
const platform = currentPlatform()
|
|
104
104
|
const engineCacheDir = join(homedir(), ".supatype", "cache", "engine")
|
|
105
105
|
try {
|
|
@@ -208,7 +208,7 @@ export async function engineRequest<T = unknown>(
|
|
|
208
208
|
try {
|
|
209
209
|
return JSON.parse(result.stdout) as T
|
|
210
210
|
} catch {
|
|
211
|
-
// Non-JSON stdout
|
|
211
|
+
// Non-JSON stdout: return as message.
|
|
212
212
|
return { message: result.stdout.trim() } as T
|
|
213
213
|
}
|
|
214
214
|
}
|
|
@@ -43,7 +43,7 @@ export function parseEnginePushOutput(output: string): EnginePushResult | null {
|
|
|
43
43
|
export function formatEnginePushMessage(result: EnginePushResult): string {
|
|
44
44
|
if (result.status === "up_to_date") {
|
|
45
45
|
if (result.admin_refreshed) {
|
|
46
|
-
return "Schema up to date
|
|
46
|
+
return "Schema up to date, Studio metadata synced."
|
|
47
47
|
}
|
|
48
48
|
return "Schema up to date."
|
|
49
49
|
}
|
package/src/ensure-binary.ts
CHANGED
|
@@ -11,8 +11,26 @@ import {
|
|
|
11
11
|
type Component,
|
|
12
12
|
} from "./binary-cache.js"
|
|
13
13
|
import { isDownloadInProgress, waitForComponentDownload } from "./binary-download-lock.js"
|
|
14
|
+
import { currentLibc } from "./cli-artifacts.js"
|
|
14
15
|
import type { SupatypeProjectConfig } from "./project-config.js"
|
|
15
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Components are published for glibc only, so on Alpine the CLI itself runs (it has a musl
|
|
19
|
+
* build) while everything it downloads cannot start. Said once, before the first download,
|
|
20
|
+
* because the alternative is a dynamic loader error naming a file the user never asked for.
|
|
21
|
+
*/
|
|
22
|
+
let warnedAboutMusl = false
|
|
23
|
+
function warnIfMusl(component: Component): void {
|
|
24
|
+
if (warnedAboutMusl || currentLibc() !== "musl") return
|
|
25
|
+
warnedAboutMusl = true
|
|
26
|
+
console.warn(
|
|
27
|
+
`[supatype] This is a musl system (Alpine). Component binaries such as ${component} are ` +
|
|
28
|
+
"published for glibc only, so they may fail to start here.\n" +
|
|
29
|
+
"[supatype] Use a glibc image (for example node:22-bookworm) for local stacks, or run " +
|
|
30
|
+
"against a remote target with `supatype link`.",
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
16
34
|
export async function ensureBinary(
|
|
17
35
|
component: Component,
|
|
18
36
|
config: SupatypeProjectConfig,
|
|
@@ -26,6 +44,8 @@ export async function ensureBinary(
|
|
|
26
44
|
}
|
|
27
45
|
}
|
|
28
46
|
|
|
47
|
+
warnIfMusl(component)
|
|
48
|
+
|
|
29
49
|
const version = await resolveVersionFor(component, config)
|
|
30
50
|
const platform = currentPlatform()
|
|
31
51
|
|
|
@@ -37,7 +37,7 @@ export async function ensureComponentBinaries(
|
|
|
37
37
|
const list = describeRequiredHostComponents(components)
|
|
38
38
|
if (provider === "docker") {
|
|
39
39
|
info(
|
|
40
|
-
`Preparing host CLI tools (${list})
|
|
40
|
+
`Preparing host CLI tools (${list}), Compose still runs Postgres and the API stack.`,
|
|
41
41
|
)
|
|
42
42
|
} else {
|
|
43
43
|
info(`Preparing local runtime components (${list})…`)
|
package/src/env-file.ts
CHANGED
|
@@ -1,23 +1,162 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from "node:fs"
|
|
2
2
|
import { join } from "node:path"
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Marks a value the CLI wrote from `versions` in `supatype.config.ts`.
|
|
6
|
+
*
|
|
7
|
+
* Provenance, so that removing a pin from config can clean up the stale image reference it left
|
|
8
|
+
* behind *without* deleting a value the operator wrote by hand. Before this, the image keys derived
|
|
9
|
+
* from config were removed unconditionally on every compose run: an operator could override
|
|
10
|
+
* `SUPATYPE_REALTIME_IMAGE` from `.env` and could not override `SUPATYPE_SERVER_IMAGE`, because the
|
|
11
|
+
* latter is derivable from a pin and the former is not. Same file, same shape, different rules.
|
|
12
|
+
*/
|
|
13
|
+
export const MANAGED_MARKER = "# supatype:managed, value from versions in supatype.config.ts"
|
|
14
|
+
|
|
15
|
+
interface EnvLine {
|
|
16
|
+
/** `KEY` for an assignment, undefined for comments and blanks. */
|
|
17
|
+
key?: string
|
|
18
|
+
text: string
|
|
19
|
+
/** True when the preceding line is the managed marker. */
|
|
20
|
+
managed: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parseLines(text: string): EnvLine[] {
|
|
24
|
+
const out: EnvLine[] = []
|
|
25
|
+
if (text === "") return out
|
|
26
|
+
let markerPending = false
|
|
27
|
+
// A file ending in a newline yields a final empty element; keeping it would put a blank line
|
|
28
|
+
// between the existing content and anything appended.
|
|
29
|
+
const raws = text.split(/\r?\n/)
|
|
30
|
+
while (raws.length > 0 && raws[raws.length - 1] === "") raws.pop()
|
|
31
|
+
for (const raw of raws) {
|
|
32
|
+
const trimmed = raw.trim()
|
|
33
|
+
if (trimmed === MANAGED_MARKER) {
|
|
34
|
+
markerPending = true
|
|
35
|
+
out.push({ text: raw, managed: false })
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
const eq = raw.indexOf("=")
|
|
39
|
+
const key = eq > 0 ? raw.slice(0, eq).trim() : undefined
|
|
40
|
+
const isAssignment = key !== undefined && /^[A-Za-z_][A-Za-z0-9_]*$/.test(key)
|
|
41
|
+
out.push({ ...(isAssignment && { key }), text: raw, managed: isAssignment && markerPending })
|
|
42
|
+
if (!isAssignment && trimmed !== "") markerPending = false
|
|
43
|
+
else if (isAssignment) markerPending = false
|
|
44
|
+
}
|
|
45
|
+
return out
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface UpsertOptions {
|
|
49
|
+
/**
|
|
50
|
+
* Keys to delete: but only where the CLI wrote them (see [`MANAGED_MARKER`]). A hand-written value
|
|
51
|
+
* is left alone, because the operator putting an image tag in `.env` is the documented way to run a
|
|
52
|
+
* local build and deleting it silently sent them back to `:latest`.
|
|
53
|
+
*/
|
|
54
|
+
removeManaged?: readonly string[]
|
|
55
|
+
/** Keys in `updates` that come from config and may later be cleaned up. Written with the marker. */
|
|
56
|
+
managed?: readonly string[]
|
|
57
|
+
/** Keys to delete outright, whoever wrote them. For secrets being rotated out. */
|
|
58
|
+
remove?: readonly string[]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Merge key/value updates into a project `.env`.
|
|
63
|
+
*
|
|
64
|
+
* Comments, blank lines and ordering are preserved: this file is edited by hand, and the previous
|
|
65
|
+
* implementation rebuilt it from the assignments alone, so every comment an operator had written
|
|
66
|
+
* disappeared the first time they ran anything that touched `.env`.
|
|
67
|
+
*/
|
|
5
68
|
export function upsertEnvFile(
|
|
6
69
|
cwd: string,
|
|
7
70
|
updates: Record<string, string>,
|
|
8
|
-
|
|
71
|
+
options: UpsertOptions | readonly string[] = {},
|
|
9
72
|
): void {
|
|
73
|
+
// Historically the third argument was a plain list of keys to remove unconditionally.
|
|
74
|
+
const opts: UpsertOptions = Array.isArray(options)
|
|
75
|
+
? { remove: options as readonly string[] }
|
|
76
|
+
: (options as UpsertOptions)
|
|
77
|
+
const removeManaged = new Set(opts.removeManaged ?? [])
|
|
78
|
+
const removeAlways = new Set(opts.remove ?? [])
|
|
79
|
+
const managed = new Set(opts.managed ?? [])
|
|
80
|
+
|
|
10
81
|
const envPath = join(cwd, ".env")
|
|
11
82
|
const existing = existsSync(envPath) ? readFileSync(envPath, "utf8") : ""
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
83
|
+
const lines = parseLines(existing)
|
|
84
|
+
|
|
85
|
+
const written = new Set<string>()
|
|
86
|
+
const kept: EnvLine[] = []
|
|
87
|
+
|
|
88
|
+
for (let i = 0; i < lines.length; i++) {
|
|
89
|
+
const line = lines[i]!
|
|
90
|
+
const key = line.key
|
|
91
|
+
|
|
92
|
+
if (key === undefined) {
|
|
93
|
+
kept.push(line)
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (removeAlways.has(key) || (removeManaged.has(key) && line.managed)) {
|
|
98
|
+
// Drop the marker that introduced it, so no orphan comment is left behind.
|
|
99
|
+
if (line.managed && kept[kept.length - 1]?.text.trim() === MANAGED_MARKER) kept.pop()
|
|
100
|
+
continue
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (key in updates) {
|
|
104
|
+
const wantsMarker = managed.has(key)
|
|
105
|
+
if (wantsMarker && kept[kept.length - 1]?.text.trim() !== MANAGED_MARKER) {
|
|
106
|
+
kept.push({ text: MANAGED_MARKER, managed: false })
|
|
107
|
+
}
|
|
108
|
+
if (!wantsMarker && line.managed && kept[kept.length - 1]?.text.trim() === MANAGED_MARKER) {
|
|
109
|
+
kept.pop()
|
|
110
|
+
}
|
|
111
|
+
kept.push({ key, text: `${key}=${updates[key]}`, managed: wantsMarker })
|
|
112
|
+
written.add(key)
|
|
113
|
+
continue
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
kept.push(line)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const appended: string[] = []
|
|
120
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
121
|
+
if (written.has(key)) continue
|
|
122
|
+
if (managed.has(key)) appended.push(MANAGED_MARKER)
|
|
123
|
+
appended.push(`${key}=${value}`)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const body = [...kept.map((l) => l.text), ...appended].join("\n")
|
|
127
|
+
writeFileSync(envPath, `${body.replace(/\n+$/, "")}\n`, "utf8")
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Parse a project `.env` into key/value pairs.
|
|
132
|
+
*
|
|
133
|
+
* Deliberately small: `KEY=value`, `#` comments, one optional layer of surrounding quotes. No
|
|
134
|
+
* interpolation and no `export` handling, because Compose's own parser does not do those either and
|
|
135
|
+
* a `.env` that means two different things depending on who reads it is worse than a limited one.
|
|
136
|
+
*/
|
|
137
|
+
export function readEnvFile(cwd: string): Record<string, string> {
|
|
138
|
+
const envPath = join(cwd, ".env")
|
|
139
|
+
if (!existsSync(envPath)) return {}
|
|
140
|
+
|
|
141
|
+
const out: Record<string, string> = {}
|
|
142
|
+
for (const line of readFileSync(envPath, "utf8").split(/\r?\n/)) {
|
|
143
|
+
const trimmed = line.trim()
|
|
144
|
+
if (trimmed.length === 0 || trimmed.startsWith("#")) continue
|
|
145
|
+
const eq = trimmed.indexOf("=")
|
|
146
|
+
if (eq <= 0) continue
|
|
147
|
+
const key = trimmed.slice(0, eq).trim()
|
|
148
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue
|
|
149
|
+
let value = trimmed.slice(eq + 1).trim()
|
|
150
|
+
if (
|
|
151
|
+
value.length >= 2 &&
|
|
152
|
+
((value.startsWith('"') && value.endsWith('"')) ||
|
|
153
|
+
(value.startsWith("'") && value.endsWith("'")))
|
|
154
|
+
) {
|
|
155
|
+
value = value.slice(1, -1)
|
|
156
|
+
}
|
|
157
|
+
out[key] = value
|
|
158
|
+
}
|
|
159
|
+
return out
|
|
21
160
|
}
|
|
22
161
|
|
|
23
162
|
export function readEnvValue(cwd: string, key: string, fallback: string): string {
|
|
@@ -29,6 +168,13 @@ export function readEnvValue(cwd: string, key: string, fallback: string): string
|
|
|
29
168
|
return fallback
|
|
30
169
|
}
|
|
31
170
|
|
|
171
|
+
/** Whether `.env` carries a non-empty value for `key`. Distinguishes absent from defaulted. */
|
|
172
|
+
export function hasEnvValue(cwd: string, key: string): boolean {
|
|
173
|
+
const envPath = join(cwd, ".env")
|
|
174
|
+
if (!existsSync(envPath)) return false
|
|
175
|
+
return new RegExp(`^${key}=(.+)$`, "m").test(readFileSync(envPath, "utf8"))
|
|
176
|
+
}
|
|
177
|
+
|
|
32
178
|
export function readEnvInt(cwd: string, key: string): number | null {
|
|
33
179
|
const raw = readEnvValue(cwd, key, "")
|
|
34
180
|
if (!raw) return null
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which mechanism will enforce this project's per-column rules, decided offline.
|
|
3
|
+
*
|
|
4
|
+
* The engine makes the same decision at push time by asking the database whether `supatype_mask` is
|
|
5
|
+
* installed. The CLI has to answer earlier: it writes `PGRST_DB_SCHEMA` when it generates compose,
|
|
6
|
+
* before any database necessarily exists. It can, because the extension needs
|
|
7
|
+
* `shared_preload_libraries` and a compiled library on the host, so exactly one path ships it,
|
|
8
|
+
* `supatype/postgres`. Everything else gets the view tier:
|
|
9
|
+
*
|
|
10
|
+
* | Config | Ships the extension | Tier |
|
|
11
|
+
* | -------------------------------------------- | ------------------- | --------- |
|
|
12
|
+
* | `database.provider: "docker"` | yes | extension |
|
|
13
|
+
* | `provider: native` | no | views |
|
|
14
|
+
* | `database.external` | no | views |
|
|
15
|
+
* | `provider: docker` + `database.image` override | no | views |
|
|
16
|
+
*
|
|
17
|
+
* Deliberately **not** "is the database external": a native `supatype dev` has no extension either,
|
|
18
|
+
* and treating it as tier 1 would point PostgREST at a schema whose tables the API roles no longer
|
|
19
|
+
* hold privileges on: every request denied.
|
|
20
|
+
*
|
|
21
|
+
* The one case this cannot call is a self-managed Postgres where the operator compiled and installed
|
|
22
|
+
* the extension themselves and points at it with `database.external`. They set `schema.api_schemas`
|
|
23
|
+
* explicitly; `supatype db check` reports the mismatch either way.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { existsSync } from "node:fs"
|
|
27
|
+
// Statically imported: these packages are ESM, where `require` does not exist. The first attempt used
|
|
28
|
+
// it for a lazy import and every command that generates compose would have thrown at runtime, caught
|
|
29
|
+
// by the tests that run the built binary rather than the source.
|
|
30
|
+
import { loadSchemaAst } from "./config.js"
|
|
31
|
+
export { nativeMaskLibraryPresent } from "./postgres-ctl.js"
|
|
32
|
+
import type { SupatypeProjectConfig } from "./project-config.js"
|
|
33
|
+
import {
|
|
34
|
+
resolveRuntimeProvider,
|
|
35
|
+
schemaPathFromProject,
|
|
36
|
+
usesExternalDatabase,
|
|
37
|
+
} from "./project-config.js"
|
|
38
|
+
|
|
39
|
+
export type FieldMaskingTier = "none" | "extension" | "views"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
/** Postgres image this project would run, when it runs one at all. */
|
|
43
|
+
function postgresImage(config: SupatypeProjectConfig): string | undefined {
|
|
44
|
+
if (usesExternalDatabase(config)) return undefined
|
|
45
|
+
if (config.database.provider !== "docker" && resolveRuntimeProvider(config) !== "docker") {
|
|
46
|
+
return undefined
|
|
47
|
+
}
|
|
48
|
+
return config.database.image?.trim() || "supatype/postgres:latest"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** True when the Postgres this project runs is one that carries `supatype_mask`. */
|
|
52
|
+
export function imageShipsMaskExtension(config: SupatypeProjectConfig): boolean {
|
|
53
|
+
const image = postgresImage(config)
|
|
54
|
+
return image !== undefined && /^supatype\/postgres(:|$)/.test(image)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Whether any model masks a column.
|
|
59
|
+
*
|
|
60
|
+
* Read from the extracted AST rather than the config, because that is where field rules live. Shaped
|
|
61
|
+
* loosely on purpose: the CLI's AST type carries `platform.access` as an opaque record, and this only
|
|
62
|
+
* needs to know whether a `fields` map is present and non-empty.
|
|
63
|
+
*/
|
|
64
|
+
export function schemaHasFieldRules(ast: unknown): boolean {
|
|
65
|
+
const models = (ast as { models?: unknown[] } | null)?.models
|
|
66
|
+
if (!Array.isArray(models)) return false
|
|
67
|
+
return models.some((model) => {
|
|
68
|
+
const access = (
|
|
69
|
+
model as { annotations?: { platform?: { access?: Record<string, unknown> } } } | null
|
|
70
|
+
)?.annotations?.platform?.access
|
|
71
|
+
const fields = access?.["fields"]
|
|
72
|
+
return typeof fields === "object" && fields !== null && Object.keys(fields).length > 0
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** The tier this project's next push will use. */
|
|
77
|
+
export function fieldMaskingTier(
|
|
78
|
+
config: SupatypeProjectConfig,
|
|
79
|
+
ast: unknown,
|
|
80
|
+
/** Set on the native path once the Postgres install is known, see [`nativeMaskLibraryPresent`]. */
|
|
81
|
+
maskLibraryPresent?: boolean,
|
|
82
|
+
): FieldMaskingTier {
|
|
83
|
+
if (!schemaHasFieldRules(ast)) return "none"
|
|
84
|
+
if (maskLibraryPresent === true) return "extension"
|
|
85
|
+
return imageShipsMaskExtension(config) ? "extension" : "views"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The tier for a project on disk, loading its schema to see whether any column is masked.
|
|
90
|
+
*
|
|
91
|
+
* A schema that fails to load falls back to `"none"`, the default exposed-schema list. Deciding the
|
|
92
|
+
* tier is the wrong moment to report a syntax error, and `push`/`dev` do it properly moments later
|
|
93
|
+
* with the file and line.
|
|
94
|
+
*/
|
|
95
|
+
export function fieldMaskingTierFromProject(
|
|
96
|
+
cwd: string,
|
|
97
|
+
config: SupatypeProjectConfig,
|
|
98
|
+
maskLibraryPresent?: boolean,
|
|
99
|
+
): FieldMaskingTier {
|
|
100
|
+
try {
|
|
101
|
+
const schemaPath = schemaPathFromProject(config, cwd)
|
|
102
|
+
if (!existsSync(schemaPath)) return "none"
|
|
103
|
+
return fieldMaskingTier(config, loadSchemaAst(schemaPath, cwd), maskLibraryPresent)
|
|
104
|
+
} catch {
|
|
105
|
+
return "none"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scaffold Deno IDE types for edge functions so tsserver / Cursor stop
|
|
3
|
+
* reporting "Cannot find name 'Deno'" under the project root tsconfig.
|
|
4
|
+
*
|
|
5
|
+
* We ship a curated ambient declaration (not `@types/deno` at the app root):
|
|
6
|
+
* installing `@types/deno` as a project dependency auto-includes it in the
|
|
7
|
+
* whole app when root `tsconfig` omits `"types"`, which pollutes React/Next.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
existsSync,
|
|
12
|
+
mkdirSync,
|
|
13
|
+
readFileSync,
|
|
14
|
+
writeFileSync,
|
|
15
|
+
} from "node:fs"
|
|
16
|
+
import { join, relative } from "node:path"
|
|
17
|
+
import ts from "typescript"
|
|
18
|
+
|
|
19
|
+
const MARKER = "Supatype edge functions (IDE / tsserver)"
|
|
20
|
+
|
|
21
|
+
/** Ambient Deno types written into `functions/deno.d.ts`. */
|
|
22
|
+
export function functionsDenoAmbientSource(): string {
|
|
23
|
+
return `/**
|
|
24
|
+
* ${MARKER}.
|
|
25
|
+
* Runtime is Deno; this file covers APIs edge functions commonly use.
|
|
26
|
+
* Do not add \`@types/deno\` to the app package.json, it leaks into the
|
|
27
|
+
* whole project when root tsconfig has no \`"types"\` field.
|
|
28
|
+
*
|
|
29
|
+
* Optional: for the full Deno API, install the Deno VS Code/Cursor extension
|
|
30
|
+
* and open \`functions/\` as a Deno-enabled workspace folder.
|
|
31
|
+
*/
|
|
32
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
33
|
+
|
|
34
|
+
interface DenoEnv {
|
|
35
|
+
/** Returns the value of the environment variable, or \`undefined\` if unset. */
|
|
36
|
+
get(key: string): string | undefined
|
|
37
|
+
set(key: string, value: string): void
|
|
38
|
+
delete(key: string): void
|
|
39
|
+
has(key: string): boolean
|
|
40
|
+
toObject(): { [key: string]: string }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
declare namespace Deno {
|
|
44
|
+
const env: DenoEnv
|
|
45
|
+
|
|
46
|
+
namespace errors {
|
|
47
|
+
class NotFound extends Error {}
|
|
48
|
+
class PermissionDenied extends Error {}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface DirEntry {
|
|
52
|
+
name: string
|
|
53
|
+
isFile: boolean
|
|
54
|
+
isDirectory: boolean
|
|
55
|
+
isSymlink: boolean
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface FileInfo {
|
|
59
|
+
isFile: boolean
|
|
60
|
+
isDirectory: boolean
|
|
61
|
+
isSymlink: boolean
|
|
62
|
+
size: number
|
|
63
|
+
mtime: Date | null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readDir(path: string): AsyncIterable<DirEntry>
|
|
67
|
+
function readTextFile(path: string): Promise<string>
|
|
68
|
+
function stat(path: string): Promise<FileInfo>
|
|
69
|
+
|
|
70
|
+
function serve(
|
|
71
|
+
options: { port: number; hostname?: string; onListen?: (params: { hostname: string; port: number }) => void },
|
|
72
|
+
handler: (req: Request) => Response | Promise<Response>,
|
|
73
|
+
): void
|
|
74
|
+
}
|
|
75
|
+
`
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** `functions/tsconfig.json`: owns function files so the app config can exclude them. */
|
|
79
|
+
export function functionsTsConfigSource(): string {
|
|
80
|
+
return `{
|
|
81
|
+
"compilerOptions": {
|
|
82
|
+
"target": "ES2022",
|
|
83
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
84
|
+
"strict": true,
|
|
85
|
+
"noEmit": true,
|
|
86
|
+
"module": "ESNext",
|
|
87
|
+
"moduleResolution": "bundler",
|
|
88
|
+
"isolatedModules": true,
|
|
89
|
+
"skipLibCheck": true,
|
|
90
|
+
"allowImportingTsExtensions": true,
|
|
91
|
+
"types": []
|
|
92
|
+
},
|
|
93
|
+
"include": ["./**/*.ts", "./deno.d.ts"]
|
|
94
|
+
}
|
|
95
|
+
`
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export type FunctionsDenoTypesResult = {
|
|
99
|
+
wroteDenoDts: boolean
|
|
100
|
+
wroteTsconfig: boolean
|
|
101
|
+
rootExclude: "updated" | "already" | "missing" | "manual"
|
|
102
|
+
hints: string[]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Ensure `functions/deno.d.ts` + `functions/tsconfig.json` exist, and try to
|
|
107
|
+
* exclude `functions` from the project-root `tsconfig.json`.
|
|
108
|
+
*/
|
|
109
|
+
export function ensureFunctionsDenoTypes(
|
|
110
|
+
projectRoot: string,
|
|
111
|
+
functionsDir: string,
|
|
112
|
+
): FunctionsDenoTypesResult {
|
|
113
|
+
mkdirSync(functionsDir, { recursive: true })
|
|
114
|
+
|
|
115
|
+
const denoDtsPath = join(functionsDir, "deno.d.ts")
|
|
116
|
+
const tsconfigPath = join(functionsDir, "tsconfig.json")
|
|
117
|
+
|
|
118
|
+
let wroteDenoDts = false
|
|
119
|
+
if (!existsSync(denoDtsPath)) {
|
|
120
|
+
writeFileSync(denoDtsPath, functionsDenoAmbientSource(), "utf8")
|
|
121
|
+
wroteDenoDts = true
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let wroteTsconfig = false
|
|
125
|
+
if (!existsSync(tsconfigPath)) {
|
|
126
|
+
writeFileSync(tsconfigPath, functionsTsConfigSource(), "utf8")
|
|
127
|
+
wroteTsconfig = true
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const rootExclude = ensureRootTsconfigExcludesFunctions(projectRoot)
|
|
131
|
+
const hints: string[] = []
|
|
132
|
+
const fnRel = toPosix(relative(projectRoot, functionsDir)) || "functions"
|
|
133
|
+
|
|
134
|
+
if (rootExclude === "manual") {
|
|
135
|
+
hints.push(
|
|
136
|
+
`Add "functions" to the root tsconfig.json "exclude" array so the app checker skips ${fnRel}/.`,
|
|
137
|
+
)
|
|
138
|
+
} else if (rootExclude === "missing") {
|
|
139
|
+
hints.push(
|
|
140
|
+
`If you add a root tsconfig.json, exclude "${fnRel}" (or "functions") so app typecheck stays separate.`,
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return { wroteDenoDts, wroteTsconfig, rootExclude, hints }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function ensureRootTsconfigExcludesFunctions(
|
|
148
|
+
projectRoot: string,
|
|
149
|
+
): FunctionsDenoTypesResult["rootExclude"] {
|
|
150
|
+
const path = join(projectRoot, "tsconfig.json")
|
|
151
|
+
if (!existsSync(path)) return "missing"
|
|
152
|
+
|
|
153
|
+
const raw = readFileSync(path, "utf8")
|
|
154
|
+
const parsed = ts.parseConfigFileTextToJson(path, raw)
|
|
155
|
+
if (parsed.error || parsed.config === undefined || typeof parsed.config !== "object") {
|
|
156
|
+
return "manual"
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const cfg = parsed.config as { exclude?: unknown }
|
|
160
|
+
const exclude = Array.isArray(cfg.exclude) ? cfg.exclude.map(String) : []
|
|
161
|
+
if (exclude.some(isFunctionsExcludeEntry)) return "already"
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const asJson = JSON.parse(raw) as { exclude?: string[] }
|
|
165
|
+
asJson.exclude = [...(Array.isArray(asJson.exclude) ? asJson.exclude : []), "functions"]
|
|
166
|
+
writeFileSync(path, `${JSON.stringify(asJson, null, 2)}\n`, "utf8")
|
|
167
|
+
return "updated"
|
|
168
|
+
} catch {
|
|
169
|
+
// JSONC / trailing commas, try a conservative string splice into an existing exclude array.
|
|
170
|
+
if (/"exclude"\s*:\s*\[/.test(raw)) {
|
|
171
|
+
const next = raw.replace(/("exclude"\s*:\s*\[)/, `$1\n "functions",`)
|
|
172
|
+
if (next !== raw) {
|
|
173
|
+
writeFileSync(path, next, "utf8")
|
|
174
|
+
return "updated"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return "manual"
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function isFunctionsExcludeEntry(entry: string): boolean {
|
|
182
|
+
const n = entry.replace(/\\/g, "/").replace(/^\.\//, "")
|
|
183
|
+
return n === "functions" || n === "functions/**" || n.startsWith("functions/")
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function toPosix(path: string): string {
|
|
187
|
+
return path.replace(/\\/g, "/")
|
|
188
|
+
}
|
|
@@ -58,7 +58,7 @@ export function generateFunctionsRouterSource(
|
|
|
58
58
|
)
|
|
59
59
|
const routes = fns.map((fn, i) => ` "${fn.name}": handler_${i},`)
|
|
60
60
|
|
|
61
|
-
return `// Auto-generated
|
|
61
|
+
return `// Auto-generated: do not edit (regenerated by supatype dev / functions serve)
|
|
62
62
|
${imports.join("\n")}
|
|
63
63
|
|
|
64
64
|
const handlers: Record<string, (req: Request) => Response | Promise<Response>> = {
|