@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest"
|
|
2
|
-
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
|
2
|
+
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
|
3
3
|
import { join } from "node:path"
|
|
4
4
|
import { tmpdir } from "node:os"
|
|
5
5
|
import { runtimeRouteSpec } from "../src/runtime-routes.js"
|
|
@@ -89,6 +89,25 @@ describe("runtime contract", () => {
|
|
|
89
89
|
expect(first).toBe(second)
|
|
90
90
|
})
|
|
91
91
|
|
|
92
|
+
it("self-host compose exposes the managed schema over REST, not a hardcoded public", () => {
|
|
93
|
+
// The literal here was the reason a non-default pg_schema pushed correctly and then answered
|
|
94
|
+
// PGRST106 on every request: the engine had moved and PostgREST was never told.
|
|
95
|
+
const compose = renderSelfHostCompose({
|
|
96
|
+
...baseConfig,
|
|
97
|
+
schema: { pg_schema: "app" },
|
|
98
|
+
})
|
|
99
|
+
expect(compose).toContain('PGRST_DB_SCHEMA: "app, supatype, graphql_public, auth"')
|
|
100
|
+
expect(compose).not.toContain('PGRST_DB_SCHEMA: "public,')
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it("self-host compose honours an explicit api_schemas list verbatim", () => {
|
|
104
|
+
const compose = renderSelfHostCompose({
|
|
105
|
+
...baseConfig,
|
|
106
|
+
schema: { pg_schema: "app", api_schemas: ["app", "reporting"] },
|
|
107
|
+
})
|
|
108
|
+
expect(compose).toContain('PGRST_DB_SCHEMA: "app, reporting"')
|
|
109
|
+
})
|
|
110
|
+
|
|
92
111
|
it("self-host compose does not inject a synthetic app-proxy service", () => {
|
|
93
112
|
const compose = renderSelfHostCompose({ ...baseConfig, app: { mode: "proxy", upstream: "http://app:3000" } })
|
|
94
113
|
expect(compose).not.toContain("ghcr.io/supatype/app-proxy")
|
|
@@ -109,6 +128,49 @@ describe("runtime contract", () => {
|
|
|
109
128
|
expect(compose).toContain("supatype/schema-engine:latest")
|
|
110
129
|
})
|
|
111
130
|
|
|
131
|
+
// PostgREST must connect as `authenticator`, never as POSTGRES_USER. POSTGRES_USER is
|
|
132
|
+
// `supatype_admin`, a superuser, and a superuser session may SET ROLE to any role in the
|
|
133
|
+
// cluster: so a request whose JWT named one got it. Verified against a live stack: a
|
|
134
|
+
// token with `role: "supatype_admin"` returned every row of an RLS-protected table.
|
|
135
|
+
it("self-host compose connects PostgREST as authenticator, not the superuser", () => {
|
|
136
|
+
const compose = renderSelfHostCompose(baseConfig)
|
|
137
|
+
expect(compose).toContain("PGRST_DB_URI: postgresql://authenticator:")
|
|
138
|
+
expect(compose).not.toMatch(/PGRST_DB_URI:.*POSTGRES_USER/)
|
|
139
|
+
expect(compose).not.toMatch(/PGRST_DB_URI:.*supatype_admin/)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
// A separate credential, so rotating the operator's POSTGRES_PASSWORD cannot take the REST
|
|
143
|
+
// API down with it. Unset is a hard compose error rather than an empty password.
|
|
144
|
+
it("gives PostgREST its own credential, not POSTGRES_PASSWORD", () => {
|
|
145
|
+
const compose = renderSelfHostCompose(baseConfig)
|
|
146
|
+
expect(compose).toMatch(/PGRST_DB_URI:.*\$\{AUTHENTICATOR_PASSWORD:\?/)
|
|
147
|
+
expect(compose).not.toMatch(/PGRST_DB_URI:.*POSTGRES_PASSWORD/)
|
|
148
|
+
expect(compose).toMatch(/AUTHENTICATOR_PASSWORD: \$\{AUTHENTICATOR_PASSWORD:\?/)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
// `supatype dev --provider docker` renders *this* file, so a `${VAR:?}` with no default
|
|
152
|
+
// takes local dev down before anything starts, `docker compose` refuses to interpolate and
|
|
153
|
+
// exits 1. Adding one therefore means wiring it into `upsertDevComposeEnv` as well. This
|
|
154
|
+
// list is the reminder; if it fails, do that rather than just updating the list.
|
|
155
|
+
it("requires only variables the dev path also sets", () => {
|
|
156
|
+
const compose = renderSelfHostCompose(baseConfig)
|
|
157
|
+
const required = [...compose.matchAll(/\$\{([A-Z0-9_]+):\?/g)].map((m) => m[1])
|
|
158
|
+
expect([...new Set(required)].sort()).toEqual([
|
|
159
|
+
"AUTHENTICATOR_PASSWORD",
|
|
160
|
+
"JWT_SECRET",
|
|
161
|
+
"POSTGRES_PASSWORD",
|
|
162
|
+
])
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
// The whole point of requiring them: a service must never fall back to a secret published in
|
|
166
|
+
// this repository. That default used to reach PostgREST, storage and GoTrue.
|
|
167
|
+
it("never defaults a secret to a published constant", () => {
|
|
168
|
+
const compose = renderSelfHostCompose(baseConfig)
|
|
169
|
+
expect(compose).not.toContain("super-secret-jwt-token")
|
|
170
|
+
expect(compose).not.toMatch(/\$\{JWT_SECRET:-/)
|
|
171
|
+
expect(compose).not.toMatch(/\$\{POSTGRES_PASSWORD:-/)
|
|
172
|
+
})
|
|
173
|
+
|
|
112
174
|
it("composeDockerImageEnv maps pinned versions to SUPATYPE_*_IMAGE", () => {
|
|
113
175
|
expect(composeDockerImageEnv(baseConfig)).toEqual({
|
|
114
176
|
SUPATYPE_ENGINE_IMAGE: "supatype/schema-engine:v0.4.2",
|
|
@@ -251,6 +313,55 @@ describe("runtime contract", () => {
|
|
|
251
313
|
}
|
|
252
314
|
})
|
|
253
315
|
|
|
316
|
+
it("manifest switches functions on, because the compose file always runs a worker", () => {
|
|
317
|
+
// The generated compose file starts a `functions-worker` and hands the server
|
|
318
|
+
// SUPATYPE_FUNCTIONS_WORKER_URL, while the manifest beside it said functions were disabled. The
|
|
319
|
+
// server reads the manifest, so every function 404'd in a stack that was running a worker for
|
|
320
|
+
// them. Docker `supatype dev` renders this same file, so one flag broke both paths.
|
|
321
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-fn-manifest-"))
|
|
322
|
+
try {
|
|
323
|
+
writeSelfHostCompose(dir, baseConfig)
|
|
324
|
+
const compose = readFileSync(join(dir, ".supatype", "self-host", "docker-compose.yml"), "utf8")
|
|
325
|
+
expect(compose).toContain(" functions-worker:")
|
|
326
|
+
expect(compose).toContain("SUPATYPE_FUNCTIONS_WORKER_URL: http://functions-worker:8001")
|
|
327
|
+
|
|
328
|
+
const manifest = JSON.parse(
|
|
329
|
+
readFileSync(join(dir, ".supatype", "manifest.json"), "utf8"),
|
|
330
|
+
) as Record<string, unknown>
|
|
331
|
+
expect(manifest["functions_enabled"]).toBe(true)
|
|
332
|
+
expect(manifest["functions_worker_url"]).toBe("http://functions-worker:8001")
|
|
333
|
+
} finally {
|
|
334
|
+
rmSync(dir, { recursive: true, force: true })
|
|
335
|
+
}
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
it("repairs a manifest written by an older CLI without touching its other keys", () => {
|
|
339
|
+
// Regenerating the compose file is the obvious fix to try, so it has to work: without the
|
|
340
|
+
// repair, only brand-new projects get functions and an existing stack stays broken.
|
|
341
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-fn-repair-"))
|
|
342
|
+
try {
|
|
343
|
+
mkdirSync(join(dir, ".supatype"), { recursive: true })
|
|
344
|
+
writeFileSync(
|
|
345
|
+
join(dir, ".supatype", "manifest.json"),
|
|
346
|
+
JSON.stringify({ schema: "tenant_7", postgrest_url: "http://pg:3000", functions_enabled: false }),
|
|
347
|
+
"utf8",
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
writeSelfHostCompose(dir, baseConfig)
|
|
351
|
+
|
|
352
|
+
const manifest = JSON.parse(
|
|
353
|
+
readFileSync(join(dir, ".supatype", "manifest.json"), "utf8"),
|
|
354
|
+
) as Record<string, unknown>
|
|
355
|
+
expect(manifest["functions_enabled"]).toBe(true)
|
|
356
|
+
expect(manifest["functions_worker_url"]).toBe("http://functions-worker:8001")
|
|
357
|
+
// Values `push` put there survive: the schema is not "public" and must stay as found.
|
|
358
|
+
expect(manifest["schema"]).toBe("tenant_7")
|
|
359
|
+
expect(manifest["postgrest_url"]).toBe("http://pg:3000")
|
|
360
|
+
} finally {
|
|
361
|
+
rmSync(dir, { recursive: true, force: true })
|
|
362
|
+
}
|
|
363
|
+
})
|
|
364
|
+
|
|
254
365
|
it("devLocal compose enables STUDIO_OPEN_DEV on supatype-server", () => {
|
|
255
366
|
const compose = renderSelfHostCompose(baseConfig, process.cwd(), { devLocal: true })
|
|
256
367
|
expect(compose).toContain('STUDIO_OPEN_DEV: "1"')
|
|
@@ -276,6 +387,7 @@ describe("runtime contract", () => {
|
|
|
276
387
|
expect(compose).toContain("\n functions-worker:\n")
|
|
277
388
|
expect(compose).toContain("SUPATYPE_FUNCTIONS_WORKER_URL: http://functions-worker:8001")
|
|
278
389
|
expect(compose).toContain("SUPATYPE_FUNCTIONS_ROOT: /project/functions")
|
|
390
|
+
expect(compose).toContain("SUPATYPE_URL: http://kong:8000")
|
|
279
391
|
expect(compose).not.toContain("deploy/functions")
|
|
280
392
|
expect(compose).not.toContain("supatype-functions")
|
|
281
393
|
})
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs"
|
|
2
|
+
import { tmpdir } from "node:os"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
import { describe, expect, it } from "vitest"
|
|
5
|
+
|
|
6
|
+
import { checkServiceRoleRoutes, serviceRoleProblemLines } from "../src/service-role-check.js"
|
|
7
|
+
import type { SupatypeProjectConfig } from "../src/project-config.js"
|
|
8
|
+
import { DENO_RELEASE_PIN } from "../src/release-pins.js"
|
|
9
|
+
|
|
10
|
+
function config(serviceRole?: string[]): SupatypeProjectConfig {
|
|
11
|
+
return {
|
|
12
|
+
project: { name: "p" },
|
|
13
|
+
database: { provider: "docker" },
|
|
14
|
+
server: { mode: "dev" },
|
|
15
|
+
app: { mode: "none" },
|
|
16
|
+
versions: { engine: "0.4.2", server: "0.1.0", postgres: "17.2", deno: DENO_RELEASE_PIN },
|
|
17
|
+
...(serviceRole !== undefined && { functions: { serviceRole } }),
|
|
18
|
+
} as SupatypeProjectConfig
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** A project whose functions/ holds the named directories, each with an index.ts. */
|
|
22
|
+
function projectWith(names: string[], bareFiles: string[] = []): string {
|
|
23
|
+
const cwd = mkdtempSync(join(tmpdir(), "supatype-svcrole-check-"))
|
|
24
|
+
const dir = join(cwd, "functions")
|
|
25
|
+
mkdirSync(dir, { recursive: true })
|
|
26
|
+
for (const name of names) {
|
|
27
|
+
mkdirSync(join(dir, name), { recursive: true })
|
|
28
|
+
writeFileSync(join(dir, name, "index.ts"), "export default () => new Response()\n", "utf8")
|
|
29
|
+
}
|
|
30
|
+
for (const file of bareFiles) {
|
|
31
|
+
writeFileSync(join(dir, `${file}.ts`), "export default () => new Response()\n", "utf8")
|
|
32
|
+
}
|
|
33
|
+
return cwd
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe("checkServiceRoleRoutes", () => {
|
|
37
|
+
it("says nothing when a project grants nothing", () => {
|
|
38
|
+
const problems = checkServiceRoleRoutes(config(), projectWith([]))
|
|
39
|
+
expect(problems.errors).toEqual([])
|
|
40
|
+
expect(problems.warnings).toEqual([])
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it("accepts a name that matches a function directory", () => {
|
|
44
|
+
const problems = checkServiceRoleRoutes(config(["send-email"]), projectWith(["send-email"]))
|
|
45
|
+
expect(problems.errors).toEqual([])
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it("accepts a bare <name>.ts function, which the worker also serves", () => {
|
|
49
|
+
const problems = checkServiceRoleRoutes(config(["audit"]), projectWith([], ["audit"]))
|
|
50
|
+
expect(problems.errors).toEqual([])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it("reports a typo, which would otherwise grant nothing in silence", () => {
|
|
54
|
+
// The failure this exists for: the grant fails closed, so the function reads no key at runtime in a
|
|
55
|
+
// deploy that reported success.
|
|
56
|
+
const problems = checkServiceRoleRoutes(config(["send-emial"]), projectWith(["send-email"]))
|
|
57
|
+
expect(problems.errors).toHaveLength(1)
|
|
58
|
+
expect(problems.errors[0]).toContain("send-emial")
|
|
59
|
+
// Structural too, so `doctor` can mark the entry without parsing the printed line back apart.
|
|
60
|
+
expect(problems.missing).toEqual(["send-emial"])
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it("reports a name whose function was renamed away", () => {
|
|
64
|
+
const problems = checkServiceRoleRoutes(config(["send-email"]), projectWith(["mailer"]))
|
|
65
|
+
expect(problems.errors).toHaveLength(1)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it("warns rather than fails on a hook entry", () => {
|
|
69
|
+
// It was the documented form once, and the worker grants hooks whatever the list says, so it is
|
|
70
|
+
// harmless, and worth saying because a reader would assume the line is what grants.
|
|
71
|
+
const problems = checkServiceRoleRoutes(config(["hooks/moderate-post"]), projectWith([]))
|
|
72
|
+
expect(problems.errors).toEqual([])
|
|
73
|
+
expect(problems.warnings).toHaveLength(1)
|
|
74
|
+
expect(problems.warnings[0]).toContain("not needed")
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it("ignores an index-less directory, matching how the worker discovers routes", () => {
|
|
78
|
+
const cwd = projectWith([])
|
|
79
|
+
mkdirSync(join(cwd, "functions", "half-written"), { recursive: true })
|
|
80
|
+
const problems = checkServiceRoleRoutes(config(["half-written"]), cwd)
|
|
81
|
+
expect(problems.errors).toHaveLength(1)
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe("serviceRoleProblemLines", () => {
|
|
86
|
+
it("is empty when nothing is wrong", () => {
|
|
87
|
+
expect(serviceRoleProblemLines({ errors: [], missing: [], warnings: [], available: ["a"] })).toEqual([])
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it("lists what could have been named instead", () => {
|
|
91
|
+
const lines = serviceRoleProblemLines({
|
|
92
|
+
errors: [' "x": no functions/x/index.ts'],
|
|
93
|
+
missing: ["x"],
|
|
94
|
+
warnings: [],
|
|
95
|
+
available: ["audit", "send-email"],
|
|
96
|
+
})
|
|
97
|
+
expect(lines.join("\n")).toContain("Functions found: audit, send-email")
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it("says so when there are no functions at all", () => {
|
|
101
|
+
const lines = serviceRoleProblemLines({
|
|
102
|
+
errors: [' "x": nope'],
|
|
103
|
+
missing: ["x"],
|
|
104
|
+
warnings: [],
|
|
105
|
+
available: [],
|
|
106
|
+
})
|
|
107
|
+
expect(lines.join("\n")).toContain("No functions found")
|
|
108
|
+
})
|
|
109
|
+
})
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs"
|
|
2
|
+
import { tmpdir } from "node:os"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
import { describe, expect, it } from "vitest"
|
|
5
|
+
|
|
6
|
+
import { serviceRoleRoutesFor } from "../src/commands/functions.js"
|
|
7
|
+
import { DENO_RELEASE_PIN } from "../src/release-pins.js"
|
|
8
|
+
|
|
9
|
+
/** A config the loader accepts, plus whatever the test is actually about. */
|
|
10
|
+
function projectWith(extra: Record<string, unknown>): string {
|
|
11
|
+
const cwd = mkdtempSync(join(tmpdir(), "supatype-svcrole-"))
|
|
12
|
+
mkdirSync(cwd, { recursive: true })
|
|
13
|
+
const body = {
|
|
14
|
+
project: { name: "p" },
|
|
15
|
+
database: { provider: "docker" },
|
|
16
|
+
server: { mode: "dev" },
|
|
17
|
+
app: { mode: "none" },
|
|
18
|
+
versions: { engine: "0.4.2", server: "0.1.0", postgres: "17.2", deno: DENO_RELEASE_PIN },
|
|
19
|
+
schema: { path: "./schema/index.ts" },
|
|
20
|
+
...extra,
|
|
21
|
+
}
|
|
22
|
+
writeFileSync(join(cwd, "supatype.config.ts"), `export default ${JSON.stringify(body)}\n`, "utf8")
|
|
23
|
+
return cwd
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe("serviceRoleRoutesFor", () => {
|
|
27
|
+
it("reads the declared list", () => {
|
|
28
|
+
const cwd = projectWith({ functions: { serviceRole: ["send-email"] } })
|
|
29
|
+
expect(serviceRoleRoutesFor(cwd)).toEqual(["send-email"])
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("is an empty list when a project declares none", () => {
|
|
33
|
+
// Distinct from the case below: this project *has* said something, and it said nobody. The platform
|
|
34
|
+
// stores that as a real revocation.
|
|
35
|
+
expect(serviceRoleRoutesFor(projectWith({}))).toEqual([])
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it("is undefined when the config cannot be read at all", () => {
|
|
39
|
+
// Not []: the platform replaces its stored list wholesale whenever the field is present, so an
|
|
40
|
+
// unreadable config sending [] would revoke every grant the project has.
|
|
41
|
+
expect(serviceRoleRoutesFor(mkdtempSync(join(tmpdir(), "supatype-svcrole-none-")))).toBeUndefined()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it("sends what the project declared, hook entries included", () => {
|
|
45
|
+
// Filtering hook routes out is the platform's job, not this one's: a hook is granted the key by the
|
|
46
|
+
// worker anyway, and dropping the entry here would hide a mistake in the config rather than making
|
|
47
|
+
// it harmless. Sorted, so an unchanged config produces an unchanged request.
|
|
48
|
+
const cwd = projectWith({ functions: { serviceRole: ["send-email", "hooks/moderate-post"] } })
|
|
49
|
+
expect(serviceRoleRoutesFor(cwd)).toEqual(["hooks/moderate-post", "send-email"])
|
|
50
|
+
})
|
|
51
|
+
})
|
|
@@ -80,7 +80,7 @@ describe("provisionBuckets", () => {
|
|
|
80
80
|
|
|
81
81
|
await provisionBuckets("http://localhost:54321/storage/v1", "key", [{ id: "photos", public: false }])
|
|
82
82
|
|
|
83
|
-
expect(warn).toHaveBeenCalledWith('[storage] Storage API unreachable
|
|
83
|
+
expect(warn).toHaveBeenCalledWith('[storage] Storage API unreachable, skipped bucket "photos"')
|
|
84
84
|
})
|
|
85
85
|
})
|
|
86
86
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, expect, it, vi, afterEach } from "vitest"
|
|
2
|
+
import { targetFetch, TargetApiError } from "../src/target-client.js"
|
|
3
|
+
|
|
4
|
+
describe("targetFetch cloud auth refresh", () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
vi.unstubAllGlobals()
|
|
7
|
+
vi.restoreAllMocks()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it("refreshes once on TOKEN_EXPIRED then retries", async () => {
|
|
11
|
+
const fetchMock = vi
|
|
12
|
+
.fn()
|
|
13
|
+
.mockResolvedValueOnce({
|
|
14
|
+
ok: false,
|
|
15
|
+
status: 401,
|
|
16
|
+
json: async () => ({ message: "Token expired", code: "TOKEN_EXPIRED" }),
|
|
17
|
+
})
|
|
18
|
+
.mockResolvedValueOnce({
|
|
19
|
+
ok: true,
|
|
20
|
+
status: 200,
|
|
21
|
+
json: async () => ({
|
|
22
|
+
access_token: "new-access",
|
|
23
|
+
refresh_token: "new-refresh",
|
|
24
|
+
expires_in: 3600,
|
|
25
|
+
}),
|
|
26
|
+
})
|
|
27
|
+
.mockResolvedValueOnce({
|
|
28
|
+
ok: true,
|
|
29
|
+
status: 200,
|
|
30
|
+
json: async () => ({ data: { ok: true } }),
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
vi.stubGlobal("fetch", fetchMock)
|
|
34
|
+
|
|
35
|
+
const onRefreshed = vi.fn()
|
|
36
|
+
const result = await targetFetch<{ ok: boolean }>("http://api.test", "/api/v1", {
|
|
37
|
+
method: "GET",
|
|
38
|
+
path: "/projects/x/status",
|
|
39
|
+
token: "old-access",
|
|
40
|
+
authRefresh: {
|
|
41
|
+
cloudApiUrl: "http://api.test",
|
|
42
|
+
refreshToken: "old-refresh",
|
|
43
|
+
onRefreshed,
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
expect(result).toEqual({ ok: true })
|
|
48
|
+
expect(onRefreshed).toHaveBeenCalledWith({
|
|
49
|
+
accessToken: "new-access",
|
|
50
|
+
refreshToken: "new-refresh",
|
|
51
|
+
})
|
|
52
|
+
expect(fetchMock).toHaveBeenCalledTimes(3)
|
|
53
|
+
const retryAuth = (fetchMock.mock.calls[2]![1] as RequestInit).headers as Record<string, string>
|
|
54
|
+
expect(retryAuth.Authorization).toBe("Bearer new-access")
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it("points to supatype login when expired without refresh token", async () => {
|
|
58
|
+
vi.stubGlobal(
|
|
59
|
+
"fetch",
|
|
60
|
+
vi.fn().mockResolvedValue({
|
|
61
|
+
ok: false,
|
|
62
|
+
status: 401,
|
|
63
|
+
json: async () => ({ message: "Token expired", code: "TOKEN_EXPIRED" }),
|
|
64
|
+
}),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
await expect(
|
|
68
|
+
targetFetch("http://api.test", "/api/v1", {
|
|
69
|
+
method: "GET",
|
|
70
|
+
path: "/projects/x/status",
|
|
71
|
+
token: "old",
|
|
72
|
+
}),
|
|
73
|
+
).rejects.toThrow(/supatype login/)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it("throws TargetApiError with status", async () => {
|
|
77
|
+
vi.stubGlobal(
|
|
78
|
+
"fetch",
|
|
79
|
+
vi.fn().mockResolvedValue({
|
|
80
|
+
ok: false,
|
|
81
|
+
status: 500,
|
|
82
|
+
json: async () => ({ message: "boom" }),
|
|
83
|
+
}),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
await expect(
|
|
87
|
+
targetFetch("http://api.test", "/api/v1", {
|
|
88
|
+
method: "GET",
|
|
89
|
+
path: "/x",
|
|
90
|
+
token: "t",
|
|
91
|
+
}),
|
|
92
|
+
).rejects.toMatchObject({ name: "TargetApiError", status: 500 } satisfies Partial<TargetApiError>)
|
|
93
|
+
})
|
|
94
|
+
})
|
|
@@ -1135,4 +1135,191 @@ export type User = Model<{ id: UUID; email: NonNullable<string> }>
|
|
|
1135
1135
|
|
|
1136
1136
|
expect(() => extractSchemaAstFromTypes(schemaPath, dir)).toThrow(/Unknown Supatype type "NonNullable"/)
|
|
1137
1137
|
})
|
|
1138
|
+
// Regression: access factored into a shared type alias silently produced NO
|
|
1139
|
+
// rules, so every model using the pattern was published without RLS.
|
|
1140
|
+
it("resolves model access from a shared type alias in another file", () => {
|
|
1141
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-access-alias-"))
|
|
1142
|
+
dirs.push(dir)
|
|
1143
|
+
writeFileSync(
|
|
1144
|
+
join(dir, "shared.ts"),
|
|
1145
|
+
`
|
|
1146
|
+
import type { Public, Role } from "@supatype/types"
|
|
1147
|
+
|
|
1148
|
+
export type CmsPublicReadAdminWrite = {
|
|
1149
|
+
read: Public
|
|
1150
|
+
create: Role<"admin">
|
|
1151
|
+
update: Role<"admin">
|
|
1152
|
+
delete: Role<"admin">
|
|
1153
|
+
}
|
|
1154
|
+
`,
|
|
1155
|
+
"utf8",
|
|
1156
|
+
)
|
|
1157
|
+
const schemaPath = join(dir, "schema.ts")
|
|
1158
|
+
writeFileSync(
|
|
1159
|
+
schemaPath,
|
|
1160
|
+
`
|
|
1161
|
+
import type { Model, UUID } from "@supatype/types"
|
|
1162
|
+
import type { CmsPublicReadAdminWrite } from "./shared.js"
|
|
1163
|
+
|
|
1164
|
+
export type Article = Model<{ id: UUID; title: string }, {
|
|
1165
|
+
tableName: "articles"
|
|
1166
|
+
access: CmsPublicReadAdminWrite
|
|
1167
|
+
}>
|
|
1168
|
+
`,
|
|
1169
|
+
"utf8",
|
|
1170
|
+
)
|
|
1171
|
+
|
|
1172
|
+
const ast = extractSchemaAstFromTypes(schemaPath, dir)
|
|
1173
|
+
const access = modelAccess(ast?.models.find((m) => m.name === "Article"))
|
|
1174
|
+
expect(access).toEqual({
|
|
1175
|
+
read: { type: "public" },
|
|
1176
|
+
create: { type: "role", roles: ["admin"] },
|
|
1177
|
+
update: { type: "role", roles: ["admin"] },
|
|
1178
|
+
delete: { type: "role", roles: ["admin"] },
|
|
1179
|
+
})
|
|
1180
|
+
})
|
|
1181
|
+
|
|
1182
|
+
it("resolves model access from an intersection of aliases", () => {
|
|
1183
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-access-intersection-"))
|
|
1184
|
+
dirs.push(dir)
|
|
1185
|
+
const schemaPath = join(dir, "schema.ts")
|
|
1186
|
+
writeFileSync(
|
|
1187
|
+
schemaPath,
|
|
1188
|
+
`
|
|
1189
|
+
import type { Model, UUID, Public, Role } from "@supatype/types"
|
|
1190
|
+
|
|
1191
|
+
type PublicRead = { read: Public }
|
|
1192
|
+
type AdminWrite = { create: Role<"admin">; delete: Role<"admin"> }
|
|
1193
|
+
|
|
1194
|
+
export type Article = Model<{ id: UUID; title: string }, {
|
|
1195
|
+
access: PublicRead & AdminWrite
|
|
1196
|
+
}>
|
|
1197
|
+
`,
|
|
1198
|
+
"utf8",
|
|
1199
|
+
)
|
|
1200
|
+
|
|
1201
|
+
const ast = extractSchemaAstFromTypes(schemaPath, dir)
|
|
1202
|
+
const access = modelAccess(ast?.models.find((m) => m.name === "Article"))
|
|
1203
|
+
expect(Object.keys(access).sort()).toEqual(["create", "delete", "read"])
|
|
1204
|
+
})
|
|
1205
|
+
|
|
1206
|
+
// The dangerous case: `const … as const` cannot express access (the rule names
|
|
1207
|
+
// are types), and silently ignoring it published an unprotected table.
|
|
1208
|
+
it("throws when model access cannot be resolved instead of dropping the rules", () => {
|
|
1209
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-access-unresolvable-"))
|
|
1210
|
+
dirs.push(dir)
|
|
1211
|
+
const schemaPath = join(dir, "schema.ts")
|
|
1212
|
+
writeFileSync(
|
|
1213
|
+
schemaPath,
|
|
1214
|
+
`
|
|
1215
|
+
import type { Model, UUID } from "@supatype/types"
|
|
1216
|
+
|
|
1217
|
+
export type Article = Model<{ id: UUID; title: string }, {
|
|
1218
|
+
access: typeof someRuntimeValue
|
|
1219
|
+
}>
|
|
1220
|
+
`,
|
|
1221
|
+
"utf8",
|
|
1222
|
+
)
|
|
1223
|
+
|
|
1224
|
+
expect(() => extractSchemaAstFromTypes(schemaPath, dir)).toThrow(
|
|
1225
|
+
/Model "Article": could not resolve its `access` rules/,
|
|
1226
|
+
)
|
|
1227
|
+
})
|
|
1228
|
+
|
|
1229
|
+
it("carries Decimal precision and scale through to the AST", () => {
|
|
1230
|
+
// `Decimal<10, 2>` used to extract as an unbounded NUMERIC: the engine renders `NUMERIC(p, s)`
|
|
1231
|
+
// from these two facts, and nothing read the type arguments. A money column silently lost its
|
|
1232
|
+
// constraint, which no error surfaced and no test caught.
|
|
1233
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-decimal-"))
|
|
1234
|
+
dirs.push(dir)
|
|
1235
|
+
const schemaPath = join(dir, "schema.ts")
|
|
1236
|
+
writeFileSync(
|
|
1237
|
+
schemaPath,
|
|
1238
|
+
`
|
|
1239
|
+
import type { Decimal, Model, UUID } from "@supatype/types"
|
|
1240
|
+
|
|
1241
|
+
export type Invoice = Model<{
|
|
1242
|
+
id: UUID
|
|
1243
|
+
total: Decimal<10, 2>
|
|
1244
|
+
rate: Decimal<5, 4>
|
|
1245
|
+
}>
|
|
1246
|
+
`,
|
|
1247
|
+
"utf8",
|
|
1248
|
+
)
|
|
1249
|
+
|
|
1250
|
+
const ast = extractSchemaAstFromTypes(schemaPath, dir)
|
|
1251
|
+
const invoice = ast?.models.find((m) => m.name === "Invoice")
|
|
1252
|
+
expect(invoice?.fields["total"]).toMatchObject({ kind: "decimal", precision: 10, scale: 2 })
|
|
1253
|
+
expect(invoice?.fields["rate"]).toMatchObject({ kind: "decimal", precision: 5, scale: 4 })
|
|
1254
|
+
})
|
|
1255
|
+
|
|
1256
|
+
it("extracts Code and Currency as JSONB carrying their declared shape", () => {
|
|
1257
|
+
// Both were exported from `@supatype/types` and rejected by the extractor, so a schema using
|
|
1258
|
+
// either failed the push with "unknown type". Each carries two values, a language with its
|
|
1259
|
+
// source, an amount with its currency, so JSONB is the column that does not drop one.
|
|
1260
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-code-currency-"))
|
|
1261
|
+
dirs.push(dir)
|
|
1262
|
+
const schemaPath = join(dir, "schema.ts")
|
|
1263
|
+
writeFileSync(
|
|
1264
|
+
schemaPath,
|
|
1265
|
+
`
|
|
1266
|
+
import type { Code, Currency, Model, UUID } from "@supatype/types"
|
|
1267
|
+
|
|
1268
|
+
export type Snippet = Model<{
|
|
1269
|
+
id: UUID
|
|
1270
|
+
body: Code<"sql">
|
|
1271
|
+
anyLang: Code
|
|
1272
|
+
price: Currency<"USD">
|
|
1273
|
+
anyCurrency: Currency
|
|
1274
|
+
}>
|
|
1275
|
+
`,
|
|
1276
|
+
"utf8",
|
|
1277
|
+
)
|
|
1278
|
+
|
|
1279
|
+
const ast = extractSchemaAstFromTypes(schemaPath, dir)
|
|
1280
|
+
const snippet = ast?.models.find((m) => m.name === "Snippet")
|
|
1281
|
+
|
|
1282
|
+
expect(snippet?.fields["body"]).toMatchObject({
|
|
1283
|
+
kind: "json",
|
|
1284
|
+
annotations: { db: { pgType: "JSONB" } },
|
|
1285
|
+
tsType: '{ lang: "sql"; source: string }',
|
|
1286
|
+
})
|
|
1287
|
+
expect(snippet?.fields["price"]).toMatchObject({
|
|
1288
|
+
kind: "json",
|
|
1289
|
+
annotations: { db: { pgType: "JSONB" } },
|
|
1290
|
+
tsType: '{ amount: string; code: "USD" }',
|
|
1291
|
+
})
|
|
1292
|
+
|
|
1293
|
+
// Without a literal argument the currency is not known statically, so the row carries it.
|
|
1294
|
+
expect(snippet?.fields["anyLang"]).toMatchObject({ tsType: "{ lang: string; source: string }" })
|
|
1295
|
+
expect(snippet?.fields["anyCurrency"]).toMatchObject({ tsType: "{ amount: string; code: string }" })
|
|
1296
|
+
|
|
1297
|
+
// Both are JSONB, so the column kind cannot tell Studio which editor to show. The `editor`
|
|
1298
|
+
// annotation is what separates a code snippet from a money amount from a raw JSON blob.
|
|
1299
|
+
expect(snippet?.fields["body"]).toMatchObject({ annotations: { platform: { editor: "code" } } })
|
|
1300
|
+
expect(snippet?.fields["price"]).toMatchObject({
|
|
1301
|
+
annotations: { platform: { editor: "currency" } },
|
|
1302
|
+
})
|
|
1303
|
+
})
|
|
1304
|
+
|
|
1305
|
+
it("throws when bucket access cannot be resolved", () => {
|
|
1306
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-bucket-access-"))
|
|
1307
|
+
dirs.push(dir)
|
|
1308
|
+
const schemaPath = join(dir, "schema.ts")
|
|
1309
|
+
writeFileSync(
|
|
1310
|
+
schemaPath,
|
|
1311
|
+
`
|
|
1312
|
+
import type { Bucket } from "@supatype/types"
|
|
1313
|
+
|
|
1314
|
+
export type Media = Bucket<"media", {
|
|
1315
|
+
access: typeof someRuntimeValue
|
|
1316
|
+
}>
|
|
1317
|
+
`,
|
|
1318
|
+
"utf8",
|
|
1319
|
+
)
|
|
1320
|
+
|
|
1321
|
+
expect(() => extractSchemaAstFromTypes(schemaPath, dir)).toThrow(
|
|
1322
|
+
/Bucket "media": could not resolve its `access` rules/,
|
|
1323
|
+
)
|
|
1324
|
+
})
|
|
1138
1325
|
})
|