@supatype/cli 0.1.7 → 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 +128 -128
- package/.turbo/turbo-typecheck.log +1 -1
- package/LICENSE +202 -0
- package/deno.d.ts +1 -1
- package/dist/app/framework.js +1 -1
- package/dist/app/framework.js.map +1 -1
- package/dist/augmentation-generator.d.ts +1 -1
- package/dist/augmentation-generator.js +3 -3
- package/dist/augmentation-generator.js.map +1 -1
- package/dist/binary-cache.d.ts +30 -8
- package/dist/binary-cache.d.ts.map +1 -1
- package/dist/binary-cache.js +47 -35
- package/dist/binary-cache.js.map +1 -1
- package/dist/cache-pins.d.ts +1 -1
- package/dist/cache-pins.js +2 -2
- package/dist/cache-pins.js.map +1 -1
- package/dist/cli-artifacts.d.ts +32 -0
- package/dist/cli-artifacts.d.ts.map +1 -0
- package/dist/cli-artifacts.js +49 -0
- package/dist/cli-artifacts.js.map +1 -0
- package/dist/cli-install-method.d.ts +9 -1
- package/dist/cli-install-method.d.ts.map +1 -1
- package/dist/cli-install-method.js +22 -4
- package/dist/cli-install-method.js.map +1 -1
- package/dist/cli-package-version.d.ts +1 -1
- package/dist/cli-package-version.d.ts.map +1 -1
- package/dist/cli-package-version.js +6 -1
- package/dist/cli-package-version.js.map +1 -1
- package/dist/cli-standalone.d.ts +13 -4
- package/dist/cli-standalone.d.ts.map +1 -1
- package/dist/cli-standalone.js +86 -31
- package/dist/cli-standalone.js.map +1 -1
- package/dist/cli-version-embedded.d.ts +10 -0
- package/dist/cli-version-embedded.d.ts.map +1 -0
- package/dist/cli-version-embedded.js +13 -0
- package/dist/cli-version-embedded.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.js +1 -1
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/admin.js +6 -6
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/adopt.js +1 -1
- package/dist/commands/adopt.js.map +1 -1
- package/dist/commands/app.js +2 -2
- package/dist/commands/app.js.map +1 -1
- package/dist/commands/cache.d.ts +1 -1
- package/dist/commands/cache.js +3 -3
- package/dist/commands/cache.js.map +1 -1
- package/dist/commands/db.d.ts +3 -3
- package/dist/commands/db.js +9 -9
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/deploy.d.ts +9 -9
- package/dist/commands/deploy.js +12 -12
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts +3 -3
- package/dist/commands/dev.js +17 -17
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.js +3 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/engine.d.ts +1 -1
- package/dist/commands/engine.js +1 -1
- package/dist/commands/functions.js +5 -5
- package/dist/commands/functions.js.map +1 -1
- package/dist/commands/hooks.d.ts +2 -2
- package/dist/commands/hooks.js +4 -4
- package/dist/commands/hooks.js.map +1 -1
- package/dist/commands/init.js +28 -28
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/login.d.ts +1 -1
- package/dist/commands/login.js +1 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logs.d.ts +1 -1
- package/dist/commands/migrate.js +5 -5
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/pg.d.ts +1 -1
- package/dist/commands/pg.js +1 -1
- package/dist/commands/plugins.js +1 -1
- package/dist/commands/plugins.js.map +1 -1
- package/dist/commands/pull.js +2 -2
- package/dist/commands/pull.js.map +1 -1
- package/dist/commands/push.js +3 -3
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/self-host.d.ts +1 -1
- package/dist/commands/self-host.js +1 -1
- package/dist/commands/self-update.d.ts +2 -8
- package/dist/commands/self-update.d.ts.map +1 -1
- package/dist/commands/self-update.js +20 -12
- package/dist/commands/self-update.js.map +1 -1
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/update.d.ts +1 -1
- package/dist/commands/update.js +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/db-preflight.d.ts +4 -4
- package/dist/db-preflight.js +14 -14
- package/dist/db-preflight.js.map +1 -1
- package/dist/dev-compose.d.ts +3 -3
- package/dist/dev-compose.js +27 -27
- package/dist/dev-compose.js.map +1 -1
- package/dist/dev-log-filter.d.ts +1 -1
- package/dist/dev-log-filter.d.ts.map +1 -1
- package/dist/dev-log-filter.js +1 -1
- package/dist/dev-log-filter.js.map +1 -1
- package/dist/dev-ports.d.ts +2 -2
- package/dist/dev-ports.d.ts.map +1 -1
- package/dist/dev-ports.js +2 -2
- package/dist/dev-ports.js.map +1 -1
- package/dist/dev-session.d.ts +5 -5
- package/dist/dev-session.d.ts.map +1 -1
- package/dist/dev-session.js +6 -6
- package/dist/dev-session.js.map +1 -1
- package/dist/dev-shutdown.d.ts +5 -5
- package/dist/dev-shutdown.d.ts.map +1 -1
- package/dist/dev-shutdown.js +7 -7
- package/dist/dev-shutdown.js.map +1 -1
- package/dist/dev-task-colors.d.ts +1 -1
- package/dist/dev-task-colors.d.ts.map +1 -1
- package/dist/dev-task-colors.js +1 -1
- package/dist/dev-task-colors.js.map +1 -1
- package/dist/docker-postgres.d.ts +5 -5
- package/dist/docker-postgres.d.ts.map +1 -1
- package/dist/docker-postgres.js +5 -5
- package/dist/docker-postgres.js.map +1 -1
- package/dist/docker-runtime.d.ts +2 -2
- package/dist/docker-runtime.d.ts.map +1 -1
- package/dist/docker-runtime.js +5 -5
- package/dist/docker-runtime.js.map +1 -1
- package/dist/engine-client.d.ts +1 -1
- package/dist/engine-client.js +3 -3
- package/dist/engine-client.js.map +1 -1
- package/dist/engine-push-output.js +1 -1
- package/dist/engine-push-output.js.map +1 -1
- package/dist/ensure-binary.d.ts.map +1 -1
- package/dist/ensure-binary.js +17 -0
- package/dist/ensure-binary.js.map +1 -1
- package/dist/ensure-component-binaries.js +1 -1
- package/dist/ensure-component-binaries.js.map +1 -1
- package/dist/env-file.d.ts +2 -2
- package/dist/env-file.d.ts.map +1 -1
- package/dist/env-file.js +1 -1
- package/dist/env-file.js.map +1 -1
- package/dist/field-masking-tier.d.ts +5 -5
- package/dist/field-masking-tier.d.ts.map +1 -1
- package/dist/field-masking-tier.js +6 -6
- package/dist/field-masking-tier.js.map +1 -1
- package/dist/functions-deno-types.d.ts +1 -1
- package/dist/functions-deno-types.d.ts.map +1 -1
- package/dist/functions-deno-types.js +3 -3
- package/dist/functions-deno-types.js.map +1 -1
- package/dist/functions-router-gen.js +1 -1
- package/dist/gitignore.d.ts +2 -2
- package/dist/gitignore.d.ts.map +1 -1
- package/dist/gitignore.js +3 -3
- package/dist/gitignore.js.map +1 -1
- package/dist/hook-upload.d.ts +1 -1
- package/dist/hook-upload.js +3 -3
- package/dist/hooks-generator.d.ts +1 -1
- package/dist/hooks-generator.js +9 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/init-project-detect.d.ts +1 -1
- package/dist/init-project-detect.d.ts.map +1 -1
- package/dist/init-project-detect.js +1 -1
- package/dist/init-project-detect.js.map +1 -1
- package/dist/link.d.ts +1 -1
- package/dist/link.d.ts.map +1 -1
- package/dist/local-secrets.d.ts +5 -5
- package/dist/local-secrets.js +5 -5
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +20 -0
- package/dist/main.js.map +1 -0
- package/dist/model-hooks.d.ts +3 -3
- package/dist/model-hooks.js +4 -4
- package/dist/pgbouncer-userlist.d.ts +1 -1
- package/dist/pgbouncer-userlist.d.ts.map +1 -1
- package/dist/pgbouncer-userlist.js +2 -2
- package/dist/pgbouncer-userlist.js.map +1 -1
- package/dist/postgres-ctl.d.ts +1 -1
- package/dist/postgres-ctl.js +4 -4
- package/dist/postgres-ctl.js.map +1 -1
- package/dist/process-manager.d.ts +2 -2
- package/dist/process-manager.d.ts.map +1 -1
- package/dist/process-manager.js +4 -4
- package/dist/process-manager.js.map +1 -1
- package/dist/project-config.d.ts +12 -12
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +9 -9
- package/dist/project-config.js.map +1 -1
- package/dist/pull-utils.d.ts +2 -2
- package/dist/pull-utils.d.ts.map +1 -1
- package/dist/pull-utils.js +4 -4
- package/dist/pull-utils.js.map +1 -1
- package/dist/realtime-launch.js +1 -1
- package/dist/realtime-launch.js.map +1 -1
- package/dist/release-pins.d.ts +1 -1
- package/dist/release-pins.js +2 -2
- package/dist/release-pins.js.map +1 -1
- package/dist/resolve-api-url.js +1 -1
- package/dist/resolve-api-url.js.map +1 -1
- package/dist/resolve-target.d.ts +1 -1
- package/dist/resolve-target.d.ts.map +1 -1
- package/dist/rest-cache-admin.js +2 -2
- package/dist/rest-cache-admin.js.map +1 -1
- package/dist/runtime-routes.d.ts +1 -1
- package/dist/runtime-routes.js +1 -1
- package/dist/schema-ast-v2.d.ts +3 -3
- package/dist/schema-ast-v2.d.ts.map +1 -1
- package/dist/schema-ast-v2.js +2 -2
- package/dist/schema-ast-v2.js.map +1 -1
- package/dist/schema-push-lock.js +3 -3
- package/dist/schema-push-lock.js.map +1 -1
- package/dist/scripts/postinstall.d.ts +1 -1
- package/dist/scripts/postinstall.js +1 -1
- package/dist/self-host-compose.d.ts +5 -5
- package/dist/self-host-compose.d.ts.map +1 -1
- package/dist/self-host-compose.js +24 -24
- package/dist/self-host-compose.js.map +1 -1
- package/dist/service-role-check.js +3 -3
- package/dist/service-role-check.js.map +1 -1
- package/dist/storage-provision.d.ts +1 -1
- package/dist/storage-provision.js +3 -3
- package/dist/storage-provision.js.map +1 -1
- package/dist/studio-dev-server.d.ts +1 -1
- package/dist/studio-dev-server.d.ts.map +1 -1
- package/dist/systemd.d.ts +1 -1
- package/dist/systemd.js +1 -1
- package/dist/target-client.js +1 -1
- package/dist/target-client.js.map +1 -1
- package/dist/tsx-runner.js +1 -1
- package/dist/tsx-runner.js.map +1 -1
- package/dist/type-extractor.js +27 -27
- package/dist/type-extractor.js.map +1 -1
- package/dist/type-resolver.js +1 -1
- package/dist/type-resolver.js.map +1 -1
- package/dist/ui/brand.d.ts +1 -1
- package/dist/ui/brand.js +1 -1
- package/dist/ui/components/LogoWordmark.d.ts +1 -1
- package/dist/ui/components/LogoWordmark.d.ts.map +1 -1
- package/dist/ui/components/LogoWordmark.js +1 -1
- package/dist/ui/components/LogoWordmark.js.map +1 -1
- package/dist/ui/components/SelectField.js +1 -1
- package/dist/ui/components/SelectField.js.map +1 -1
- package/dist/ui/confirm.d.ts +1 -1
- package/dist/ui/confirm.js +1 -1
- package/dist/ui/fatal.d.ts +1 -1
- package/dist/ui/fatal.d.ts.map +1 -1
- package/dist/ui/fatal.js +1 -1
- package/dist/ui/fatal.js.map +1 -1
- package/dist/ui/flows/prompt-fields.js +1 -1
- package/dist/ui/flows/prompt-fields.js.map +1 -1
- package/dist/ui/interactive.d.ts +1 -1
- package/dist/ui/interactive.d.ts.map +1 -1
- package/dist/ui/interactive.js +1 -1
- package/dist/ui/interactive.js.map +1 -1
- package/dist/ui/progress.d.ts +1 -1
- package/dist/ui/progress.js +1 -1
- package/dist/ui/runtime/command-chrome.js +1 -1
- package/dist/ui/runtime/command-chrome.js.map +1 -1
- package/dist/ui/runtime/run-clack-flow.js +1 -1
- package/dist/ui/runtime/run-clack-flow.js.map +1 -1
- package/dist/ui/runtime/stdin-after-ink.js +1 -1
- package/dist/ui/runtime/stdin-after-ink.js.map +1 -1
- package/dist/ui/theme.d.ts +1 -1
- package/dist/ui/theme.js +1 -1
- package/dist/valkey-sidecar.d.ts +1 -1
- package/dist/valkey-sidecar.d.ts.map +1 -1
- package/dist/valkey-sidecar.js +2 -2
- package/dist/valkey-sidecar.js.map +1 -1
- package/package.json +3 -2
- package/scripts/build-release-artifacts.sh +112 -0
- package/scripts/generate-homebrew-formula.mjs +47 -45
- package/scripts/mirror-deno-release.sh +1 -1
- package/src/TYPE-RESOLUTION.md +31 -31
- package/src/app/framework.ts +1 -1
- package/src/augmentation-generator.ts +3 -3
- package/src/binary-cache.ts +51 -36
- package/src/cache-pins.ts +2 -2
- package/src/cli-artifacts.ts +65 -0
- package/src/cli-install-method.ts +24 -4
- package/src/cli-package-version.ts +5 -1
- package/src/cli-standalone.ts +116 -34
- package/src/cli-version-embedded.ts +12 -0
- package/src/cli.ts +1 -1
- package/src/commands/add.ts +1 -1
- package/src/commands/admin.ts +6 -6
- package/src/commands/adopt.ts +1 -1
- package/src/commands/app.ts +2 -2
- package/src/commands/cache.ts +3 -3
- package/src/commands/db.ts +9 -9
- package/src/commands/deploy.ts +12 -12
- package/src/commands/dev.ts +19 -19
- package/src/commands/doctor.ts +3 -3
- package/src/commands/engine.ts +1 -1
- package/src/commands/functions.ts +5 -5
- package/src/commands/hooks.ts +6 -6
- package/src/commands/init.ts +28 -28
- package/src/commands/login.ts +2 -2
- package/src/commands/logs.ts +1 -1
- package/src/commands/migrate.ts +5 -5
- package/src/commands/pg.ts +1 -1
- package/src/commands/plugins.ts +1 -1
- package/src/commands/pull.ts +2 -2
- package/src/commands/push.ts +3 -3
- package/src/commands/self-host.ts +1 -1
- package/src/commands/self-update.ts +23 -73
- package/src/commands/status.ts +1 -1
- package/src/commands/update.ts +1 -1
- package/src/config.ts +3 -3
- package/src/db-preflight.ts +15 -15
- package/src/dev-compose.ts +27 -27
- package/src/dev-log-filter.ts +1 -1
- package/src/dev-ports.ts +3 -3
- package/src/dev-session.ts +6 -6
- package/src/dev-shutdown.ts +8 -8
- package/src/dev-task-colors.ts +1 -1
- package/src/docker-postgres.ts +6 -6
- package/src/docker-runtime.ts +5 -5
- package/src/engine-client.ts +3 -3
- package/src/engine-push-output.ts +1 -1
- package/src/ensure-binary.ts +20 -0
- package/src/ensure-component-binaries.ts +1 -1
- package/src/env-file.ts +2 -2
- package/src/field-masking-tier.ts +6 -6
- package/src/functions-deno-types.ts +3 -3
- package/src/functions-router-gen.ts +1 -1
- package/src/gitignore.ts +3 -3
- package/src/hook-upload.ts +3 -3
- package/src/hooks-generator.ts +9 -9
- package/src/index.ts +1 -1
- package/src/init-project-detect.ts +1 -1
- package/src/link.ts +1 -1
- package/src/local-secrets.ts +5 -5
- package/src/main.ts +20 -0
- package/src/model-hooks.ts +4 -4
- package/src/pgbouncer-userlist.ts +2 -2
- package/src/postgres-ctl.ts +4 -4
- package/src/process-manager.ts +4 -4
- package/src/project-config.ts +16 -16
- package/src/pull-utils.ts +4 -4
- package/src/realtime-launch.ts +1 -1
- package/src/release-pins.ts +2 -2
- package/src/resolve-api-url.ts +1 -1
- package/src/resolve-target.ts +1 -1
- package/src/rest-cache-admin.ts +2 -2
- package/src/runtime-routes.ts +2 -2
- package/src/schema-ast-v2.ts +4 -4
- package/src/schema-push-lock.ts +3 -3
- package/src/scripts/postinstall.ts +1 -1
- package/src/self-host-compose.ts +25 -25
- package/src/service-role-check.ts +3 -3
- package/src/storage-provision.ts +3 -3
- package/src/studio-dev-server.ts +1 -1
- package/src/systemd.ts +1 -1
- package/src/target-client.ts +1 -1
- package/src/tsx-runner.ts +1 -1
- package/src/type-extractor.ts +27 -27
- package/src/type-resolver.ts +1 -1
- package/src/ui/README.md +1 -1
- package/src/ui/brand.ts +1 -1
- package/src/ui/components/LogoWordmark.tsx +1 -1
- package/src/ui/components/SelectField.tsx +1 -1
- package/src/ui/confirm.ts +1 -1
- package/src/ui/fatal.ts +1 -1
- package/src/ui/flows/prompt-fields.tsx +1 -1
- package/src/ui/interactive.ts +1 -1
- package/src/ui/progress.ts +1 -1
- package/src/ui/runtime/command-chrome.ts +1 -1
- package/src/ui/runtime/run-clack-flow.tsx +1 -1
- package/src/ui/runtime/stdin-after-ink.ts +1 -1
- package/src/ui/theme.ts +1 -1
- package/src/valkey-sidecar.ts +2 -2
- package/tests/access-composition.test.ts +6 -6
- package/tests/admin-studio-membership.test.ts +1 -1
- package/tests/api-schemas.test.ts +1 -1
- package/tests/archive-name-alias.test.ts +1 -1
- package/tests/cli-help.test.ts +8 -2
- package/tests/db-preflight.test.ts +1 -1
- package/tests/doctor-render.test.ts +3 -3
- package/tests/doctor-service-role.test.ts +2 -2
- package/tests/engine-push-output.test.ts +1 -1
- package/tests/env-file.test.ts +3 -3
- package/tests/external-database-compose.test.ts +5 -5
- package/tests/external-database-config.test.ts +2 -2
- package/tests/field-masking-tier.test.ts +1 -1
- package/tests/homebrew-formula.test.ts +68 -26
- package/tests/hook-upload.test.ts +1 -1
- package/tests/hooks-generator.test.ts +1 -1
- package/tests/init.test.ts +2 -2
- package/tests/keys.test.ts +8 -2
- package/tests/local-secrets.test.ts +3 -3
- package/tests/model-hooks.test.ts +6 -6
- package/tests/runtime-contract.test.ts +2 -2
- package/tests/service-role-check.test.ts +3 -3
- package/tests/storage-provision.test.ts +1 -1
- package/tests/type-extractor.test.ts +2 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +7 -1
- package/scripts/build-release-bundle.mjs +0 -27
package/src/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
|
@@ -10,7 +10,7 @@ import { join } from "node:path"
|
|
|
10
10
|
* `SUPATYPE_REALTIME_IMAGE` from `.env` and could not override `SUPATYPE_SERVER_IMAGE`, because the
|
|
11
11
|
* latter is derivable from a pin and the former is not. Same file, same shape, different rules.
|
|
12
12
|
*/
|
|
13
|
-
export const MANAGED_MARKER = "# supatype:managed
|
|
13
|
+
export const MANAGED_MARKER = "# supatype:managed, value from versions in supatype.config.ts"
|
|
14
14
|
|
|
15
15
|
interface EnvLine {
|
|
16
16
|
/** `KEY` for an assignment, undefined for comments and blanks. */
|
|
@@ -47,7 +47,7 @@ function parseLines(text: string): EnvLine[] {
|
|
|
47
47
|
|
|
48
48
|
export interface UpsertOptions {
|
|
49
49
|
/**
|
|
50
|
-
* Keys to delete
|
|
50
|
+
* Keys to delete: but only where the CLI wrote them (see [`MANAGED_MARKER`]). A hand-written value
|
|
51
51
|
* is left alone, because the operator putting an image tag in `.env` is the documented way to run a
|
|
52
52
|
* local build and deleting it silently sent them back to `:latest`.
|
|
53
53
|
*/
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Which mechanism will enforce this project's per-column rules
|
|
2
|
+
* Which mechanism will enforce this project's per-column rules, decided offline.
|
|
3
3
|
*
|
|
4
4
|
* The engine makes the same decision at push time by asking the database whether `supatype_mask` is
|
|
5
5
|
* installed. The CLI has to answer earlier: it writes `PGRST_DB_SCHEMA` when it generates compose,
|
|
6
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
|
|
7
|
+
* `shared_preload_libraries` and a compiled library on the host, so exactly one path ships it,
|
|
8
8
|
* `supatype/postgres`. Everything else gets the view tier:
|
|
9
9
|
*
|
|
10
10
|
* | Config | Ships the extension | Tier |
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*
|
|
17
17
|
* Deliberately **not** "is the database external": a native `supatype dev` has no extension either,
|
|
18
18
|
* and treating it as tier 1 would point PostgREST at a schema whose tables the API roles no longer
|
|
19
|
-
* hold privileges on
|
|
19
|
+
* hold privileges on: every request denied.
|
|
20
20
|
*
|
|
21
21
|
* The one case this cannot call is a self-managed Postgres where the operator compiled and installed
|
|
22
22
|
* the extension themselves and points at it with `database.external`. They set `schema.api_schemas`
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
import { existsSync } from "node:fs"
|
|
27
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
|
|
28
|
+
// it for a lazy import and every command that generates compose would have thrown at runtime, caught
|
|
29
29
|
// by the tests that run the built binary rather than the source.
|
|
30
30
|
import { loadSchemaAst } from "./config.js"
|
|
31
31
|
export { nativeMaskLibraryPresent } from "./postgres-ctl.js"
|
|
@@ -77,7 +77,7 @@ export function schemaHasFieldRules(ast: unknown): boolean {
|
|
|
77
77
|
export function fieldMaskingTier(
|
|
78
78
|
config: SupatypeProjectConfig,
|
|
79
79
|
ast: unknown,
|
|
80
|
-
/** Set on the native path once the Postgres install is known
|
|
80
|
+
/** Set on the native path once the Postgres install is known, see [`nativeMaskLibraryPresent`]. */
|
|
81
81
|
maskLibraryPresent?: boolean,
|
|
82
82
|
): FieldMaskingTier {
|
|
83
83
|
if (!schemaHasFieldRules(ast)) return "none"
|
|
@@ -88,7 +88,7 @@ export function fieldMaskingTier(
|
|
|
88
88
|
/**
|
|
89
89
|
* The tier for a project on disk, loading its schema to see whether any column is masked.
|
|
90
90
|
*
|
|
91
|
-
* A schema that fails to load falls back to `"none"
|
|
91
|
+
* A schema that fails to load falls back to `"none"`, the default exposed-schema list. Deciding the
|
|
92
92
|
* tier is the wrong moment to report a syntax error, and `push`/`dev` do it properly moments later
|
|
93
93
|
* with the file and line.
|
|
94
94
|
*/
|
|
@@ -23,7 +23,7 @@ export function functionsDenoAmbientSource(): string {
|
|
|
23
23
|
return `/**
|
|
24
24
|
* ${MARKER}.
|
|
25
25
|
* Runtime is Deno; this file covers APIs edge functions commonly use.
|
|
26
|
-
* Do not add \`@types/deno\` to the app package.json
|
|
26
|
+
* Do not add \`@types/deno\` to the app package.json, it leaks into the
|
|
27
27
|
* whole project when root tsconfig has no \`"types"\` field.
|
|
28
28
|
*
|
|
29
29
|
* Optional: for the full Deno API, install the Deno VS Code/Cursor extension
|
|
@@ -75,7 +75,7 @@ declare namespace Deno {
|
|
|
75
75
|
`
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
/** `functions/tsconfig.json
|
|
78
|
+
/** `functions/tsconfig.json`: owns function files so the app config can exclude them. */
|
|
79
79
|
export function functionsTsConfigSource(): string {
|
|
80
80
|
return `{
|
|
81
81
|
"compilerOptions": {
|
|
@@ -166,7 +166,7 @@ function ensureRootTsconfigExcludesFunctions(
|
|
|
166
166
|
writeFileSync(path, `${JSON.stringify(asJson, null, 2)}\n`, "utf8")
|
|
167
167
|
return "updated"
|
|
168
168
|
} catch {
|
|
169
|
-
// JSONC / trailing commas
|
|
169
|
+
// JSONC / trailing commas, try a conservative string splice into an existing exclude array.
|
|
170
170
|
if (/"exclude"\s*:\s*\[/.test(raw)) {
|
|
171
171
|
const next = raw.replace(/("exclude"\s*:\s*\[)/, `$1\n "functions",`)
|
|
172
172
|
if (next !== raw) {
|
|
@@ -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>> = {
|
package/src/gitignore.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from "node:fs"
|
|
2
2
|
import { resolve } from "node:path"
|
|
3
3
|
|
|
4
|
-
export const SUPATYPE_GITIGNORE_MARKER = "# Supatype
|
|
4
|
+
export const SUPATYPE_GITIGNORE_MARKER = "# Supatype, local runtime (contains secrets in link.json)"
|
|
5
5
|
export const SUPATYPE_GITIGNORE_BLOCK = `${SUPATYPE_GITIGNORE_MARKER}
|
|
6
6
|
.env
|
|
7
7
|
.supatype/
|
|
@@ -34,7 +34,7 @@ export function ensureSupatypeGitignore(cwd: string, opts?: { silent?: boolean }
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
if (!opts?.silent) {
|
|
37
|
-
console.warn("Added .supatype/ to .gitignore (link.json contains secrets
|
|
37
|
+
console.warn("Added .supatype/ to .gitignore (link.json contains secrets, never commit).")
|
|
38
38
|
}
|
|
39
39
|
return true
|
|
40
40
|
}
|
|
@@ -42,7 +42,7 @@ export function ensureSupatypeGitignore(cwd: string, opts?: { silent?: boolean }
|
|
|
42
42
|
export function warnIfLinkNotGitignored(cwd: string): void {
|
|
43
43
|
if (isSupatypeGitignored(cwd)) return
|
|
44
44
|
console.warn(
|
|
45
|
-
"\n⚠ Warning: .supatype/ is not in .gitignore
|
|
45
|
+
"\n⚠ Warning: .supatype/ is not in .gitignore, link.json contains tokens and must not be committed.",
|
|
46
46
|
)
|
|
47
47
|
console.warn(" Run with link --fix-gitignore to append the Supatype block.\n")
|
|
48
48
|
}
|
package/src/hook-upload.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Package a project's hooks for a managed stack.
|
|
3
3
|
*
|
|
4
4
|
* Self-hosted stacks need none of this: the worker mounts the project directory, so `hooks/` is already
|
|
5
|
-
* where it looks. A managed project has no directory
|
|
5
|
+
* where it looks. A managed project has no directory, its worker reads a ConfigMap, whose keys may only
|
|
6
6
|
* contain alphanumerics, `-`, `_` and `.`. So the tree is flattened here, where it can be checked and
|
|
7
7
|
* explained, rather than in the deploy route where a dropped file would be silent.
|
|
8
8
|
*/
|
|
@@ -28,7 +28,7 @@ export interface HookUpload {
|
|
|
28
28
|
* Rewrite the one import a hook handler is generated to make.
|
|
29
29
|
*
|
|
30
30
|
* `../_supatype/hooks.ts` is a path that cannot survive flattening, and it is the specifier our own
|
|
31
|
-
* scaffold writes
|
|
31
|
+
* scaffold writes: so it is rewritten rather than refused. Any *other* relative import is refused
|
|
32
32
|
* below: it would resolve to nothing on the worker, and a hook that fails to import is a hooked table
|
|
33
33
|
* whose writes all start failing.
|
|
34
34
|
*/
|
|
@@ -55,7 +55,7 @@ function unsupportedImports(source: string): string[] {
|
|
|
55
55
|
* holding the service-role key with nothing able to call it. Uploading it buys nothing and widens what
|
|
56
56
|
* runs with that credential.
|
|
57
57
|
*
|
|
58
|
-
* Throws with a message meant for a terminal when a handler cannot be flattened faithfully
|
|
58
|
+
* Throws with a message meant for a terminal when a handler cannot be flattened faithfully, the
|
|
59
59
|
* alternative is a deploy that succeeds and a hook that fails to import, which reads to the caller as
|
|
60
60
|
* the table's writes being broken for no visible reason.
|
|
61
61
|
*/
|
package/src/hooks-generator.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generate `functions/_supatype/hooks.ts
|
|
2
|
+
* Generate `functions/_supatype/hooks.ts`: the typed handler surface for model hooks.
|
|
3
3
|
*
|
|
4
4
|
* Generated into the project rather than shipped as a package, for two reasons: a Deno function
|
|
5
5
|
* resolves a relative `.ts` import with no npm involved, and the types then move in lockstep with the
|
|
@@ -40,7 +40,7 @@ function hookedModels(ast: unknown): HookedModel[] {
|
|
|
40
40
|
* The module source, or `null` when no model declares a hook.
|
|
41
41
|
*
|
|
42
42
|
* Returning null rather than an empty module keeps a `_supatype/hooks.ts` from appearing in projects
|
|
43
|
-
* that have no hooks
|
|
43
|
+
* that have no hooks, a generated file nobody imports is a file somebody eventually edits.
|
|
44
44
|
*/
|
|
45
45
|
export function generateHooksModule(ast: unknown): string | null {
|
|
46
46
|
const models = hookedModels(ast)
|
|
@@ -56,7 +56,7 @@ export function generateHooksModule(ast: unknown): string | null {
|
|
|
56
56
|
)
|
|
57
57
|
.join("\n")
|
|
58
58
|
|
|
59
|
-
return `// Generated by supatype CLI
|
|
59
|
+
return `// Generated by supatype CLI, do not edit manually.
|
|
60
60
|
//
|
|
61
61
|
// Typed handlers for the models that declare \`hooks\`. Import from a function beside it:
|
|
62
62
|
//
|
|
@@ -103,7 +103,7 @@ interface HookBase {
|
|
|
103
103
|
/**
|
|
104
104
|
* The rows this request is about to overwrite, fetched only if you ask.
|
|
105
105
|
*
|
|
106
|
-
* Resolved by the server from the in-flight request's own filter, as the caller's own role
|
|
106
|
+
* Resolved by the server from the in-flight request's own filter, as the caller's own role, so it
|
|
107
107
|
* cannot show you rows the caller could not read, and you cannot widen the filter. Capped:
|
|
108
108
|
* \`truncated\` is true when there were more, in which case acting on what you got means acting on a
|
|
109
109
|
* prefix you did not choose.
|
|
@@ -131,7 +131,7 @@ export type BeforeDeleteContext<T extends HookedTable> = HookBase & {
|
|
|
131
131
|
|
|
132
132
|
export type AfterChangeContext<T extends HookedTable> = HookBase & {
|
|
133
133
|
readonly operation: "insert" | "update"
|
|
134
|
-
/** Written rows
|
|
134
|
+
/** Written rows: present only when the caller sent \`Prefer: return=representation\`. */
|
|
135
135
|
readonly rows?: HookTables[T]["Row"][]
|
|
136
136
|
}
|
|
137
137
|
|
|
@@ -201,7 +201,7 @@ interface WireContext {
|
|
|
201
201
|
*
|
|
202
202
|
* The outcome travels as the **HTTP status**: 200 for proceed, 4xx for a rejection. That keeps the
|
|
203
203
|
* contract in the transport rather than in a second envelope inside the body, so a hook written by
|
|
204
|
-
* hand
|
|
204
|
+
* hand: or in another language, needs to know nothing about this file.
|
|
205
205
|
*/
|
|
206
206
|
export function hook(handler: AnyHandler): (req: Request) => Promise<Response> {
|
|
207
207
|
return async (req: Request): Promise<Response> => {
|
|
@@ -263,7 +263,7 @@ export function hook(handler: AnyHandler): (req: Request) => Promise<Response> {
|
|
|
263
263
|
*
|
|
264
264
|
* The worker takes **one default export per function directory**, so \`hook(single)\` is one hook per
|
|
265
265
|
* directory. That is fine for a single moderation rule and tedious for a model whose whole lifecycle
|
|
266
|
-
* you want in one place
|
|
266
|
+
* you want in one place, hence this. Point every event at the same function name in the schema:
|
|
267
267
|
*
|
|
268
268
|
* hooks: { beforeChange: "post-hooks", afterChange: "post-hooks" }
|
|
269
269
|
*
|
|
@@ -284,7 +284,7 @@ export function hooks(handlers: Record<string, AnyHandler | undefined>): (req: R
|
|
|
284
284
|
* The stack's address from this worker's point of view.
|
|
285
285
|
*
|
|
286
286
|
* \`SUPATYPE_INTERNAL_URL\` in Compose is the in-network gateway; \`SUPATYPE_URL\` is the same value
|
|
287
|
-
* there. Never a public or host URL
|
|
287
|
+
* there. Never a public or host URL, \`localhost\` inside this container is this container.
|
|
288
288
|
*/
|
|
289
289
|
function stackBaseUrl(): string {
|
|
290
290
|
const base =
|
|
@@ -330,7 +330,7 @@ function json(body: unknown, status: number): Response {
|
|
|
330
330
|
* Replace cross-package type imports the client types use but a hook cannot resolve.
|
|
331
331
|
*
|
|
332
332
|
* `RichText` normally generates `import("@supatype/types/lexical").SerializedEditorState`. That works
|
|
333
|
-
* in an app, where the package is installed
|
|
333
|
+
* in an app, where the package is installed, but this file lives in `functions/`, is imported by a
|
|
334
334
|
* Deno handler, and has no node_modules to reach into. Left alone, any hooked model with a rich-text
|
|
335
335
|
* column emitted a module that failed to compile in the user's editor. Caught by compiling the
|
|
336
336
|
* generated file in a test rather than by a reader reporting it.
|
package/src/index.ts
CHANGED
|
@@ -62,7 +62,7 @@ function detectStaticDir(cwd: string): string {
|
|
|
62
62
|
return "./public"
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
/** Inspect `cwd` before init prompts
|
|
65
|
+
/** Inspect `cwd` before init prompts, Vite, static dir, existing Supatype config. */
|
|
66
66
|
export function detectProjectSetup(cwd: string): DetectedProjectSetup {
|
|
67
67
|
let entries: string[] = []
|
|
68
68
|
try {
|
package/src/link.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface ProjectLink {
|
|
|
23
23
|
defaultEnvironment: string
|
|
24
24
|
/** Access token (cloud user JWT) or self-host SERVICE_ROLE_KEY. */
|
|
25
25
|
token?: string
|
|
26
|
-
/** Cloud GoTrue refresh token
|
|
26
|
+
/** Cloud GoTrue refresh token, used to renew short-lived access JWTs. */
|
|
27
27
|
refreshToken?: string
|
|
28
28
|
orgId?: string | undefined
|
|
29
29
|
cloudApiUrl?: string
|
package/src/local-secrets.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The secrets the local stack runs with.
|
|
3
3
|
*
|
|
4
4
|
* **Read from `.env`, never pinned back into it.** These used to be hardcoded constants that
|
|
5
|
-
* `supatype dev` wrote to `.env` on every run
|
|
5
|
+
* `supatype dev` wrote to `.env` on every run, which meant a project could not hold its own
|
|
6
6
|
* secrets: whatever `supatype init` or the developer put there was replaced by the published
|
|
7
7
|
* defaults on the next `dev`, and the same `.env` then went to a server. Resolving instead of
|
|
8
8
|
* pinning is what lets a generated secret survive to production.
|
|
@@ -16,7 +16,7 @@ import { hasEnvValue, readEnvValue } from "./env-file.js"
|
|
|
16
16
|
/**
|
|
17
17
|
* Fallback JWT signing secret for a project that has none.
|
|
18
18
|
*
|
|
19
|
-
* Published, therefore worthless as a secret
|
|
19
|
+
* Published, therefore worthless as a secret, anyone can mint a token for a stack still using
|
|
20
20
|
* it. That is precisely why `init` generates one instead of relying on this.
|
|
21
21
|
*/
|
|
22
22
|
export const FALLBACK_JWT_SECRET = "super-secret-jwt-token-with-at-least-32-characters-long"
|
|
@@ -52,7 +52,7 @@ export function devAuthenticatorPassword(cwd: string): string {
|
|
|
52
52
|
*
|
|
53
53
|
* The compose template requires these with `${VAR:?}` rather than defaulting them, because a
|
|
54
54
|
* default is how a published constant ends up signing a real deployment's tokens. That only
|
|
55
|
-
* works if something guarantees presence
|
|
55
|
+
* works if something guarantees presence, this does, for any project that predates
|
|
56
56
|
* `supatype init` writing them.
|
|
57
57
|
*
|
|
58
58
|
* **Fills with the fallback, does not generate.** Preserving the value such a project has been
|
|
@@ -72,11 +72,11 @@ export function seedMissingLocalSecrets(cwd: string): Record<string, string> {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
* The database identity the local stack connects with
|
|
75
|
+
* The database identity the local stack connects with, seeded, never overwritten.
|
|
76
76
|
*
|
|
77
77
|
* `POSTGRES_DB` in particular is *project configuration*: `supatype init` sets it to the
|
|
78
78
|
* project name, and the running container's database was created from it. Rewriting it to a
|
|
79
|
-
* default afterwards points every tool at a database that does not exist
|
|
79
|
+
* default afterwards points every tool at a database that does not exist, observed as
|
|
80
80
|
* `supatype push` failing with `FATAL: database "supatype" does not exist` on a project called
|
|
81
81
|
* something else, because the push path rewrote `.env` after `self-host compose up` had already
|
|
82
82
|
* created the database under its real name.
|
package/src/main.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry point for the standalone binary.
|
|
3
|
+
*
|
|
4
|
+
* `bin/supatype.js` stays the npm entry: it loads the CLI with a dynamic import, which Bun
|
|
5
|
+
* cannot follow when compiling. Given `import("../dist/cli.js")` it emits a binary that
|
|
6
|
+
* bundles one module and then fails at runtime with
|
|
7
|
+
* `Cannot find module '../dist/cli.js' from '/$bunfs/root/supatype'`. The import below is
|
|
8
|
+
* static, so the whole graph is embedded.
|
|
9
|
+
*/
|
|
10
|
+
import { run } from "./cli.js"
|
|
11
|
+
import { reportCliFatal } from "./ui/fatal.js"
|
|
12
|
+
|
|
13
|
+
// reportCliFatal rather than console.error: it is what run() already uses for a failed
|
|
14
|
+
// command, and it prints a stack under SUPATYPE_DEBUG=1. Open-coding the message here would
|
|
15
|
+
// make a crash report from a curl | bash user less useful than one from an npm user. This
|
|
16
|
+
// only fires for a throw during command registration, since run() catches everything after.
|
|
17
|
+
run().catch((err: unknown) => {
|
|
18
|
+
reportCliFatal(err)
|
|
19
|
+
process.exit(1)
|
|
20
|
+
})
|
package/src/model-hooks.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Validate the lifecycle hooks a schema declares against the functions that exist.
|
|
3
3
|
*
|
|
4
4
|
* A hook names a function directory. If the name is wrong, the honest failure is at push time with
|
|
5
|
-
* the path we searched
|
|
5
|
+
* the path we searched, not at runtime, where the symptom is a hook that never fires and a write
|
|
6
6
|
* that quietly succeeds unvalidated. That silence is the whole reason this check exists.
|
|
7
7
|
*
|
|
8
8
|
* Kept out of the extractor on purpose: resolving the functions directory needs the project config,
|
|
@@ -119,7 +119,7 @@ export interface ManifestHookEntry {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
* The hook map for `.supatype/manifest.json`, keyed by **table name
|
|
122
|
+
* The hook map for `.supatype/manifest.json`, keyed by **table name**, because that is what the
|
|
123
123
|
* server matches a request path against, not the model name.
|
|
124
124
|
*
|
|
125
125
|
* Defaults are resolved here rather than in the server: one place decides that a `before*` hook
|
|
@@ -172,7 +172,7 @@ export const DEFAULT_HOOK_TIMEOUT_MS = 2000
|
|
|
172
172
|
*
|
|
173
173
|
* **Only updates a manifest that is already there.** Creating one from scratch here would be a
|
|
174
174
|
* hazard: `functions_enabled` is a plain bool on the server's side, so a manifest carrying only
|
|
175
|
-
* hooks would read as functions *disabled
|
|
175
|
+
* hooks would read as functions *disabled*, the exact defect this repo fixed a commit ago, arriving
|
|
176
176
|
* by a different door. The compose path owns creation; this owns one key.
|
|
177
177
|
*
|
|
178
178
|
* Returns true when the file was rewritten.
|
|
@@ -216,7 +216,7 @@ export interface HooksReport {
|
|
|
216
216
|
/**
|
|
217
217
|
* What `supatype doctor` needs to answer "will my hooks actually run?".
|
|
218
218
|
*
|
|
219
|
-
* Local facts only
|
|
219
|
+
* Local facts only: the schema, the functions on disk, and the manifest the server reads. No probe
|
|
220
220
|
* of a running worker, so the answer is the same whether or not the stack is up, and a report that
|
|
221
221
|
* needs a stack is a report nobody runs before deploying.
|
|
222
222
|
*
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createHash } from "node:crypto"
|
|
2
2
|
|
|
3
|
-
/** PgBouncer md5 auth: "md5" + md5(password + username)
|
|
3
|
+
/** PgBouncer md5 auth: "md5" + md5(password + username), same as supatype-cloud / self-host. */
|
|
4
4
|
export function pgbouncerMd5Hash(password: string, username: string): string {
|
|
5
5
|
return "md5" + createHash("md5").update(password + username).digest("hex")
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
/** Two roles used by local compose: superuser pools (GoTrue, engine) and PostgREST. */
|
|
9
9
|
export function pgbouncerUserlistContent(pgPassword: string): string {
|
|
10
|
-
return `# PgBouncer md5 userlist
|
|
10
|
+
return `# PgBouncer md5 userlist, matches supatype-cloud transaction pool (auth_type = md5).
|
|
11
11
|
# Regenerated by supatype dev --local / init from POSTGRES_PASSWORD.
|
|
12
12
|
"supatype_admin" "${pgbouncerMd5Hash(pgPassword, "supatype_admin")}"
|
|
13
13
|
"authenticator" "${pgbouncerMd5Hash(pgPassword, "authenticator")}"
|
package/src/postgres-ctl.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* postgres-ctl
|
|
2
|
+
* postgres-ctl: wrappers around pg_ctl, initdb, and pg_isready for managing
|
|
3
3
|
* a native Postgres installation.
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -95,7 +95,7 @@ export function start(opts: PgOptions): void {
|
|
|
95
95
|
const bin = pgBin(opts.pgBinDir, "pg_ctl")
|
|
96
96
|
const logPath = opts.logPath ?? join(opts.dataDir, "postgres.log")
|
|
97
97
|
|
|
98
|
-
// `supatype_mask` is a planner hook, so it has to be preloaded
|
|
98
|
+
// `supatype_mask` is a planner hook, so it has to be preloaded, `CREATE EXTENSION` alone does
|
|
99
99
|
// nothing without it. Only when the archive actually carries the library: an install downloaded
|
|
100
100
|
// before it was bundled would fail to start with "could not access file".
|
|
101
101
|
//
|
|
@@ -131,7 +131,7 @@ export function stop(opts: PgOptions): void {
|
|
|
131
131
|
encoding: "utf8",
|
|
132
132
|
env: pgSpawnEnv(opts.pgBinDir),
|
|
133
133
|
})
|
|
134
|
-
// Ignore exit code
|
|
134
|
+
// Ignore exit code: Postgres may already be stopped.
|
|
135
135
|
void result
|
|
136
136
|
}
|
|
137
137
|
|
|
@@ -175,7 +175,7 @@ export async function isPortInUse(port: number): Promise<boolean> {
|
|
|
175
175
|
return new Promise((resolve) => {
|
|
176
176
|
const server = createServer()
|
|
177
177
|
server.once("error", (err: NodeJS.ErrnoException) => {
|
|
178
|
-
// EADDRINUSE
|
|
178
|
+
// EADDRINUSE: something is listening. EACCES, Windows excluded/reserved port range.
|
|
179
179
|
resolve(err.code === "EADDRINUSE" || err.code === "EACCES")
|
|
180
180
|
})
|
|
181
181
|
server.once("listening", () => {
|
package/src/process-manager.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ProcessManager
|
|
2
|
+
* ProcessManager: spawn a child process, write its PID, stream logs with a
|
|
3
3
|
* colored prefix, and restart on crash with exponential backoff.
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -73,7 +73,7 @@ export class ProcessManager {
|
|
|
73
73
|
this.backoffMs = this.opts.initialBackoffMs
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
/** Start the process. Returns immediately
|
|
76
|
+
/** Start the process. Returns immediately, the process runs in the background. */
|
|
77
77
|
start(): void {
|
|
78
78
|
this.stopped = false
|
|
79
79
|
this.spawn()
|
|
@@ -201,7 +201,7 @@ export class ProcessManager {
|
|
|
201
201
|
mkdirSync(this.opts.pidDir, { recursive: true })
|
|
202
202
|
writeFileSync(join(this.opts.pidDir, `${this.opts.label}.pid`), String(pid))
|
|
203
203
|
} catch {
|
|
204
|
-
// Non-fatal
|
|
204
|
+
// Non-fatal: PID tracking is best-effort.
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
|
|
@@ -209,7 +209,7 @@ export class ProcessManager {
|
|
|
209
209
|
try {
|
|
210
210
|
await unlink(join(this.opts.pidDir, `${this.opts.label}.pid`))
|
|
211
211
|
} catch {
|
|
212
|
-
// Ignore
|
|
212
|
+
// Ignore: file may already be gone.
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
}
|
package/src/project-config.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface SupatypeProjectConfig {
|
|
|
21
21
|
root?: string
|
|
22
22
|
}
|
|
23
23
|
project: {
|
|
24
|
-
/** Project name
|
|
24
|
+
/** Project name: used for per-project state dirs and logging. */
|
|
25
25
|
name: string
|
|
26
26
|
/** Cloud project reference (set by `supatype link`). */
|
|
27
27
|
ref?: string
|
|
@@ -32,7 +32,7 @@ export interface SupatypeProjectConfig {
|
|
|
32
32
|
* "native" = supatype manages a native Postgres binary (downloaded from CDN).
|
|
33
33
|
* "docker" = supatype runs supatype/postgres via Docker (includes all extensions).
|
|
34
34
|
*
|
|
35
|
-
* Omitted when `external` is set
|
|
35
|
+
* Omitted when `external` is set, there is no backend for Supatype to choose.
|
|
36
36
|
*/
|
|
37
37
|
provider?: "native" | "docker"
|
|
38
38
|
/**
|
|
@@ -50,7 +50,7 @@ export interface SupatypeProjectConfig {
|
|
|
50
50
|
* Postgres URL for every service in the stack.
|
|
51
51
|
*
|
|
52
52
|
* The role in it owns the schema and runs migrations. PostgREST connects as `authenticator`
|
|
53
|
-
* separately
|
|
53
|
+
* separately: see `supatype db check`, which reports what this database is missing.
|
|
54
54
|
*/
|
|
55
55
|
url: string
|
|
56
56
|
/**
|
|
@@ -215,12 +215,12 @@ export interface SupatypeProjectConfig {
|
|
|
215
215
|
/**
|
|
216
216
|
* Schemas the REST API exposes, in order (`PGRST_DB_SCHEMA`).
|
|
217
217
|
*
|
|
218
|
-
* Defaults to `pg_schema` plus the ones the stack needs for itself
|
|
219
|
-
* views, `graphql_public`, `auth
|
|
218
|
+
* Defaults to `pg_schema` plus the ones the stack needs for itself, `supatype` for Studio's
|
|
219
|
+
* views, `graphql_public`, `auth`: so setting `pg_schema` alone does the sensible thing.
|
|
220
220
|
*
|
|
221
221
|
* It used to be a hardcoded literal, which meant choosing a non-`public` `pg_schema` gave you
|
|
222
222
|
* a correct push and an API that answered `PGRST106` for everything: the engine had moved and
|
|
223
|
-
* PostgREST had not been told. State this explicitly when you need a different set
|
|
223
|
+
* PostgREST had not been told. State this explicitly when you need a different set, an extra
|
|
224
224
|
* schema of your own, or to stop exposing one.
|
|
225
225
|
*/
|
|
226
226
|
api_schemas?: readonly string[]
|
|
@@ -238,7 +238,7 @@ export interface SupatypeProjectConfig {
|
|
|
238
238
|
*
|
|
239
239
|
* **Model hooks are not listed here and do not need to be.** A hook is procedural: only the API
|
|
240
240
|
* server calls it, around a write the caller was already permitted to make, and the gateway
|
|
241
|
-
* refuses its route from outside
|
|
241
|
+
* refuses its route from outside, so there is no attacker to withhold it from, and the trust is
|
|
242
242
|
* the same a trigger already has.
|
|
243
243
|
*/
|
|
244
244
|
serviceRole?: readonly string[]
|
|
@@ -430,7 +430,7 @@ function validateExternalDatabase(cfg: Record<string, unknown>, filename: string
|
|
|
430
430
|
const url = (external as Record<string, unknown>)["url"]
|
|
431
431
|
if (typeof url !== "string" || url.trim().length === 0) {
|
|
432
432
|
throw new Error(
|
|
433
|
-
`${filename}: database.external.url is required
|
|
433
|
+
`${filename}: database.external.url is required, the Postgres URL every service connects to.\n` +
|
|
434
434
|
"Reading it from the environment keeps the password out of version control:\n" +
|
|
435
435
|
" database: { external: { url: process.env.DATABASE_URL! } }\n" +
|
|
436
436
|
"The project's .env is loaded before the config module, so DATABASE_URL there is enough.",
|
|
@@ -450,7 +450,7 @@ function validateExternalDatabase(cfg: Record<string, unknown>, filename: string
|
|
|
450
450
|
if (database["provider"] !== undefined) {
|
|
451
451
|
throw new Error(
|
|
452
452
|
`${filename}: database.provider ("${String(database["provider"])}") and database.external ` +
|
|
453
|
-
"cannot both be set
|
|
453
|
+
"cannot both be set, provider chooses a Postgres for Supatype to run, external says one " +
|
|
454
454
|
"already exists.\n" +
|
|
455
455
|
"Remove database.provider. The runtime stack is still chosen by the top-level `provider`.",
|
|
456
456
|
)
|
|
@@ -459,7 +459,7 @@ function validateExternalDatabase(cfg: Record<string, unknown>, filename: string
|
|
|
459
459
|
const server = cfg["server"] as Record<string, unknown> | undefined
|
|
460
460
|
if (server?.["mode"] === "managed") {
|
|
461
461
|
throw new Error(
|
|
462
|
-
`${filename}: database.external is not supported with server.mode "managed"
|
|
462
|
+
`${filename}: database.external is not supported with server.mode "managed", on the cloud ` +
|
|
463
463
|
"path the database is part of what is being provided.\n" +
|
|
464
464
|
"Use an external database with a self-hosted stack (server.mode \"dev\" or \"standalone\").",
|
|
465
465
|
)
|
|
@@ -469,7 +469,7 @@ function validateExternalDatabase(cfg: Record<string, unknown>, filename: string
|
|
|
469
469
|
if (typeof connection === "string" && connection.trim() !== url.trim()) {
|
|
470
470
|
throw new Error(
|
|
471
471
|
`${filename}: connection and database.external.url are both set and disagree.\n` +
|
|
472
|
-
"database.external.url is what the whole stack uses, CLI commands included
|
|
472
|
+
"database.external.url is what the whole stack uses, CLI commands included, remove " +
|
|
473
473
|
"`connection`.",
|
|
474
474
|
)
|
|
475
475
|
}
|
|
@@ -496,11 +496,11 @@ export function functionsPathCandidatesFromProject(cfg: SupatypeProjectConfig, c
|
|
|
496
496
|
}
|
|
497
497
|
|
|
498
498
|
/**
|
|
499
|
-
* Directory holding **model hooks
|
|
499
|
+
* Directory holding **model hooks**, procedural handlers the API calls around a write.
|
|
500
500
|
*
|
|
501
501
|
* Separate from `functions/` because the two have different trust models: a function is a public
|
|
502
502
|
* endpoint anyone with the anon key may invoke, while a hook is only ever called by the server. One
|
|
503
|
-
* worker serves both, and the gateway refuses the hook namespace from outside
|
|
503
|
+
* worker serves both, and the gateway refuses the hook namespace from outside, so keeping them in
|
|
504
504
|
* separate directories is what makes that boundary structural rather than a list to maintain.
|
|
505
505
|
*/
|
|
506
506
|
export function hooksPathFromProject(cfg: SupatypeProjectConfig, cwd: string): string {
|
|
@@ -565,7 +565,7 @@ export function resolveRuntimeProvider(cfg: SupatypeProjectConfig): "native" | "
|
|
|
565
565
|
*
|
|
566
566
|
* Resolved here rather than in the compose template so there is one definition of the format.
|
|
567
567
|
*
|
|
568
|
-
* This does **not** check that the names exist
|
|
568
|
+
* This does **not** check that the names exist, it cannot, since it has only the config. An earlier
|
|
569
569
|
* version of this comment claimed a typo was "visible in one place rather than silently granting
|
|
570
570
|
* nothing", which was false: nothing read the list except the two callers that turn it into an env var.
|
|
571
571
|
* `checkServiceRoleRoutes` in `service-role-check.ts` is what actually resolves the names, and `push`
|
|
@@ -614,7 +614,7 @@ export function externalDatabaseUrl(cfg: SupatypeProjectConfig): string | undefi
|
|
|
614
614
|
* Whether realtime should run.
|
|
615
615
|
*
|
|
616
616
|
* `false` only when stated. An external database that cannot support logical replication is detected
|
|
617
|
-
* rather than declared
|
|
617
|
+
* rather than declared: the capability record is what Studio and `doctor` read, so an operator who
|
|
618
618
|
* has not thought about it gets a truthful answer instead of a silent default.
|
|
619
619
|
*/
|
|
620
620
|
export function realtimeEnabled(cfg: SupatypeProjectConfig): boolean {
|
|
@@ -643,7 +643,7 @@ export const STACK_API_SCHEMAS = ["supatype", "graphql_public", "auth"] as const
|
|
|
643
643
|
* while PostgREST kept serving `public`, so every request answered `PGRST106` and nothing in the
|
|
644
644
|
* output mentioned the setting that caused it.
|
|
645
645
|
*
|
|
646
|
-
* `api_schemas` replaces the whole list when stated
|
|
646
|
+
* `api_schemas` replaces the whole list when stated, including the stack schemas, so dropping
|
|
647
647
|
* `supatype` from it is a supported way to stop exposing Studio's views. Order is preserved and
|
|
648
648
|
* duplicates removed: PostgREST serves the first entry as the default profile, so the managed
|
|
649
649
|
* schema has to lead.
|