@supatype/cli 0.1.1 → 0.1.3
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 +126 -99
- package/.turbo/turbo-typecheck.log +2 -2
- package/dist/binary-cache.d.ts +2 -0
- package/dist/binary-cache.d.ts.map +1 -1
- package/dist/binary-cache.js +36 -0
- package/dist/binary-cache.js.map +1 -1
- package/dist/binary-download-lock.d.ts +12 -0
- package/dist/binary-download-lock.d.ts.map +1 -0
- package/dist/binary-download-lock.js +106 -0
- package/dist/binary-download-lock.js.map +1 -0
- package/dist/cli-package-version.d.ts +3 -0
- package/dist/cli-package-version.d.ts.map +1 -0
- package/dist/cli-package-version.js +15 -0
- package/dist/cli-package-version.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +5 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.js +25 -19
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/admin.d.ts +12 -0
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +116 -26
- package/dist/commands/admin.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 +98 -5
- package/dist/commands/cache.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +26 -22
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.d.ts +3 -4
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +296 -121
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/link-helpers.d.ts.map +1 -1
- package/dist/commands/link-helpers.js +26 -25
- package/dist/commands/link-helpers.js.map +1 -1
- package/dist/compose-local-server-image.d.ts +13 -0
- package/dist/compose-local-server-image.d.ts.map +1 -0
- package/dist/compose-local-server-image.js +57 -0
- package/dist/compose-local-server-image.js.map +1 -0
- package/dist/compose-rename.d.ts.map +1 -1
- package/dist/compose-rename.js +5 -8
- package/dist/compose-rename.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -1
- package/dist/config.js.map +1 -1
- package/dist/dev-compose.d.ts.map +1 -1
- package/dist/dev-compose.js +113 -56
- package/dist/dev-compose.js.map +1 -1
- package/dist/dev-log-bus.d.ts +5 -0
- package/dist/dev-log-bus.d.ts.map +1 -1
- package/dist/dev-log-bus.js +16 -0
- package/dist/dev-log-bus.js.map +1 -1
- package/dist/dev-log-filter.d.ts +4 -0
- package/dist/dev-log-filter.d.ts.map +1 -1
- package/dist/dev-log-filter.js +52 -0
- package/dist/dev-log-filter.js.map +1 -1
- package/dist/dev-ports.d.ts.map +1 -1
- package/dist/dev-ports.js +19 -12
- package/dist/dev-ports.js.map +1 -1
- package/dist/dev-ready-panel.d.ts +21 -0
- package/dist/dev-ready-panel.d.ts.map +1 -0
- package/dist/dev-ready-panel.js +67 -0
- package/dist/dev-ready-panel.js.map +1 -0
- package/dist/dev-session-lock.d.ts.map +1 -1
- package/dist/dev-session-lock.js +5 -8
- package/dist/dev-session-lock.js.map +1 -1
- package/dist/dev-session.d.ts +18 -5
- package/dist/dev-session.d.ts.map +1 -1
- package/dist/dev-session.js +91 -18
- package/dist/dev-session.js.map +1 -1
- package/dist/dev-shutdown.d.ts +2 -0
- package/dist/dev-shutdown.d.ts.map +1 -1
- package/dist/dev-shutdown.js +29 -15
- package/dist/dev-shutdown.js.map +1 -1
- package/dist/dev-task-colors.d.ts +3 -1
- package/dist/dev-task-colors.d.ts.map +1 -1
- package/dist/dev-task-colors.js +23 -3
- package/dist/dev-task-colors.js.map +1 -1
- package/dist/docker-runtime.js +1 -1
- package/dist/docker-runtime.js.map +1 -1
- package/dist/engine-push-output.d.ts +1 -0
- package/dist/engine-push-output.d.ts.map +1 -1
- package/dist/engine-push-output.js +4 -1
- package/dist/engine-push-output.js.map +1 -1
- package/dist/ensure-binary.d.ts.map +1 -1
- package/dist/ensure-binary.js +14 -2
- package/dist/ensure-binary.js.map +1 -1
- package/dist/ensure-component-binaries.d.ts +17 -0
- package/dist/ensure-component-binaries.d.ts.map +1 -0
- package/dist/ensure-component-binaries.js +45 -0
- package/dist/ensure-component-binaries.js.map +1 -0
- package/dist/ensure-dev-api-config.d.ts +3 -0
- package/dist/ensure-dev-api-config.d.ts.map +1 -0
- package/dist/ensure-dev-api-config.js +17 -0
- package/dist/ensure-dev-api-config.js.map +1 -0
- package/dist/init-dependency-versions.d.ts +13 -0
- package/dist/init-dependency-versions.d.ts.map +1 -0
- package/dist/init-dependency-versions.js +53 -0
- package/dist/init-dependency-versions.js.map +1 -0
- package/dist/init-package-json.d.ts +21 -0
- package/dist/init-package-json.d.ts.map +1 -0
- package/dist/init-package-json.js +46 -0
- package/dist/init-package-json.js.map +1 -0
- package/dist/init-project-detect.d.ts +14 -0
- package/dist/init-project-detect.d.ts.map +1 -0
- package/dist/init-project-detect.js +89 -0
- package/dist/init-project-detect.js.map +1 -0
- package/dist/postgres-ctl.d.ts.map +1 -1
- package/dist/postgres-ctl.js +2 -1
- package/dist/postgres-ctl.js.map +1 -1
- package/dist/required-host-components.d.ts +11 -0
- package/dist/required-host-components.d.ts.map +1 -0
- package/dist/required-host-components.js +25 -0
- package/dist/required-host-components.js.map +1 -0
- package/dist/resolve-api-url.d.ts +6 -0
- package/dist/resolve-api-url.d.ts.map +1 -0
- package/dist/resolve-api-url.js +39 -0
- package/dist/resolve-api-url.js.map +1 -0
- package/dist/rest-cache-admin.d.ts +33 -0
- package/dist/rest-cache-admin.d.ts.map +1 -0
- package/dist/rest-cache-admin.js +96 -0
- package/dist/rest-cache-admin.js.map +1 -0
- package/dist/scripts/postinstall.d.ts +4 -6
- package/dist/scripts/postinstall.d.ts.map +1 -1
- package/dist/scripts/postinstall.js +8 -41
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/self-host-compose.d.ts.map +1 -1
- package/dist/self-host-compose.js +23 -13
- package/dist/self-host-compose.js.map +1 -1
- package/dist/studio-dev-server.d.ts +4 -2
- package/dist/studio-dev-server.d.ts.map +1 -1
- package/dist/studio-dev-server.js +3 -2
- package/dist/studio-dev-server.js.map +1 -1
- package/dist/ui/brand.d.ts +2 -5
- package/dist/ui/brand.d.ts.map +1 -1
- package/dist/ui/brand.js +2 -7
- package/dist/ui/brand.js.map +1 -1
- package/dist/ui/clack-api.d.ts +44 -0
- package/dist/ui/clack-api.d.ts.map +1 -0
- package/dist/ui/clack-api.js +76 -0
- package/dist/ui/clack-api.js.map +1 -0
- package/dist/ui/clack.d.ts +14 -0
- package/dist/ui/clack.d.ts.map +1 -0
- package/dist/ui/clack.js +147 -0
- package/dist/ui/clack.js.map +1 -0
- package/dist/ui/components/FlowSpinner.d.ts +5 -0
- package/dist/ui/components/FlowSpinner.d.ts.map +1 -0
- package/dist/ui/components/FlowSpinner.js +8 -0
- package/dist/ui/components/FlowSpinner.js.map +1 -0
- package/dist/ui/components/LogoWordmark.d.ts +9 -0
- package/dist/ui/components/LogoWordmark.d.ts.map +1 -0
- package/dist/ui/components/LogoWordmark.js +17 -0
- package/dist/ui/components/LogoWordmark.js.map +1 -0
- package/dist/ui/components/PromptPanel.d.ts +5 -0
- package/dist/ui/components/PromptPanel.d.ts.map +1 -0
- package/dist/ui/components/PromptPanel.js +7 -0
- package/dist/ui/components/PromptPanel.js.map +1 -0
- package/dist/ui/components/SelectField.d.ts +11 -0
- package/dist/ui/components/SelectField.d.ts.map +1 -0
- package/dist/ui/components/SelectField.js +19 -0
- package/dist/ui/components/SelectField.js.map +1 -0
- package/dist/ui/confirm.d.ts +2 -1
- package/dist/ui/confirm.d.ts.map +1 -1
- package/dist/ui/confirm.js +4 -4
- package/dist/ui/confirm.js.map +1 -1
- package/dist/ui/dev/DevDashboard.d.ts +8 -0
- package/dist/ui/dev/DevDashboard.d.ts.map +1 -0
- package/dist/ui/dev/DevDashboard.js +158 -0
- package/dist/ui/dev/DevDashboard.js.map +1 -0
- package/dist/ui/dev/DevReadyPanelView.d.ts +8 -0
- package/dist/ui/dev/DevReadyPanelView.d.ts.map +1 -0
- package/dist/ui/dev/DevReadyPanelView.js +48 -0
- package/dist/ui/dev/DevReadyPanelView.js.map +1 -0
- package/dist/ui/fatal.d.ts.map +1 -1
- package/dist/ui/fatal.js +5 -11
- package/dist/ui/fatal.js.map +1 -1
- package/dist/ui/flows/FlowApp.d.ts +8 -0
- package/dist/ui/flows/FlowApp.d.ts.map +1 -0
- package/dist/ui/flows/FlowApp.js +44 -0
- package/dist/ui/flows/FlowApp.js.map +1 -0
- package/dist/ui/flows/prompt-fields.d.ts +48 -0
- package/dist/ui/flows/prompt-fields.d.ts.map +1 -0
- package/dist/ui/flows/prompt-fields.js +92 -0
- package/dist/ui/flows/prompt-fields.js.map +1 -0
- package/dist/ui/flows/types.d.ts +41 -0
- package/dist/ui/flows/types.d.ts.map +1 -0
- package/dist/ui/flows/types.js +2 -0
- package/dist/ui/flows/types.js.map +1 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/ui/messages.d.ts +1 -0
- package/dist/ui/messages.d.ts.map +1 -1
- package/dist/ui/messages.js +48 -8
- package/dist/ui/messages.js.map +1 -1
- package/dist/ui/progress.d.ts +5 -1
- package/dist/ui/progress.d.ts.map +1 -1
- package/dist/ui/progress.js +12 -3
- package/dist/ui/progress.js.map +1 -1
- package/dist/ui/prompts.d.ts +7 -5
- package/dist/ui/prompts.d.ts.map +1 -1
- package/dist/ui/prompts.js +15 -8
- package/dist/ui/prompts.js.map +1 -1
- package/dist/ui/runtime/cancel.d.ts +4 -0
- package/dist/ui/runtime/cancel.d.ts.map +1 -0
- package/dist/ui/runtime/cancel.js +6 -0
- package/dist/ui/runtime/cancel.js.map +1 -0
- package/dist/ui/runtime/command-chrome.d.ts +17 -0
- package/dist/ui/runtime/command-chrome.d.ts.map +1 -0
- package/dist/ui/runtime/command-chrome.js +123 -0
- package/dist/ui/runtime/command-chrome.js.map +1 -0
- package/dist/ui/runtime/dev-prompt-queue.d.ts +30 -0
- package/dist/ui/runtime/dev-prompt-queue.d.ts.map +1 -0
- package/dist/ui/runtime/dev-prompt-queue.js +57 -0
- package/dist/ui/runtime/dev-prompt-queue.js.map +1 -0
- package/dist/ui/runtime/flow-session.d.ts +4 -0
- package/dist/ui/runtime/flow-session.d.ts.map +1 -0
- package/dist/ui/runtime/flow-session.js +8 -0
- package/dist/ui/runtime/flow-session.js.map +1 -0
- package/dist/ui/runtime/run-clack-flow.d.ts +5 -0
- package/dist/ui/runtime/run-clack-flow.d.ts.map +1 -0
- package/dist/ui/runtime/run-clack-flow.js +46 -0
- package/dist/ui/runtime/run-clack-flow.js.map +1 -0
- package/dist/ui/runtime/stdin-after-ink.d.ts +3 -0
- package/dist/ui/runtime/stdin-after-ink.d.ts.map +1 -0
- package/dist/ui/runtime/stdin-after-ink.js +16 -0
- package/dist/ui/runtime/stdin-after-ink.js.map +1 -0
- package/dist/ui/theme.d.ts +24 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +34 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/valkey-sidecar.d.ts +17 -0
- package/dist/valkey-sidecar.d.ts.map +1 -0
- package/dist/valkey-sidecar.js +94 -0
- package/dist/valkey-sidecar.js.map +1 -0
- package/package.json +9 -3
- package/src/binary-cache.ts +61 -0
- package/src/binary-download-lock.ts +124 -0
- package/src/cli-package-version.ts +15 -0
- package/src/cli.ts +6 -2
- package/src/commands/add.ts +26 -19
- package/src/commands/admin.ts +156 -29
- package/src/commands/cache.ts +105 -5
- package/src/commands/dev.ts +26 -21
- package/src/commands/init.ts +473 -247
- package/src/commands/link-helpers.ts +30 -29
- package/src/compose-local-server-image.ts +68 -0
- package/src/compose-rename.ts +5 -8
- package/src/config.ts +13 -1
- package/src/dev-compose.ts +139 -54
- package/src/dev-log-bus.ts +20 -0
- package/src/dev-log-filter.ts +42 -0
- package/src/dev-ports.ts +22 -11
- package/src/dev-ready-panel.ts +80 -0
- package/src/dev-session-lock.ts +5 -8
- package/src/dev-session.ts +98 -18
- package/src/dev-shutdown.ts +29 -17
- package/src/dev-task-colors.ts +22 -3
- package/src/docker-runtime.ts +1 -1
- package/src/engine-push-output.ts +5 -1
- package/src/ensure-binary.ts +21 -1
- package/src/ensure-component-binaries.ts +71 -0
- package/src/ensure-dev-api-config.ts +18 -0
- package/src/init-dependency-versions.ts +69 -0
- package/src/init-package-json.ts +63 -0
- package/src/init-project-detect.ts +99 -0
- package/src/postgres-ctl.ts +2 -1
- package/src/required-host-components.ts +37 -0
- package/src/resolve-api-url.ts +43 -0
- package/src/rest-cache-admin.ts +135 -0
- package/src/scripts/postinstall.ts +9 -46
- package/src/self-host-compose.ts +22 -12
- package/src/studio-dev-server.ts +5 -4
- package/src/ui/README.md +13 -9
- package/src/ui/brand.ts +2 -8
- package/src/ui/clack-api.ts +123 -0
- package/src/ui/clack.ts +153 -0
- package/src/ui/components/FlowSpinner.tsx +15 -0
- package/src/ui/components/LogoWordmark.tsx +36 -0
- package/src/ui/components/PromptPanel.tsx +17 -0
- package/src/ui/components/SelectField.tsx +58 -0
- package/src/ui/confirm.ts +5 -4
- package/src/ui/dev/DevDashboard.tsx +223 -0
- package/src/ui/dev/DevReadyPanelView.tsx +134 -0
- package/src/ui/fatal.ts +5 -10
- package/src/ui/flows/FlowApp.tsx +69 -0
- package/src/ui/flows/prompt-fields.tsx +198 -0
- package/src/ui/flows/types.ts +50 -0
- package/src/ui/index.ts +1 -1
- package/src/ui/messages.ts +50 -8
- package/src/ui/progress.ts +14 -3
- package/src/ui/prompts.ts +17 -9
- package/src/ui/runtime/cancel.ts +6 -0
- package/src/ui/runtime/command-chrome.ts +147 -0
- package/src/ui/runtime/dev-prompt-queue.ts +84 -0
- package/src/ui/runtime/flow-session.ts +11 -0
- package/src/ui/runtime/run-clack-flow.tsx +65 -0
- package/src/ui/runtime/stdin-after-ink.ts +18 -0
- package/src/ui/theme.ts +41 -0
- package/src/valkey-sidecar.ts +126 -0
- package/tests/admin-ensure.test.ts +80 -0
- package/tests/binary-cache-cloud-overrides.test.ts +2 -2
- package/tests/binary-download-lock.test.ts +41 -0
- package/tests/cli-help.test.ts +7 -4
- package/tests/cli-package-version.test.ts +15 -0
- package/tests/command-chrome-coverage.test.ts +149 -0
- package/tests/command-chrome-wrap.test.ts +51 -0
- package/tests/config.test.ts +27 -4
- package/tests/dev-ready-panel.test.ts +37 -0
- package/tests/dev-ui.test.ts +42 -2
- package/tests/docker-runtime.test.ts +9 -20
- package/tests/ensure-dev-api-config.test.ts +22 -0
- package/tests/init-dependency-versions.test.ts +62 -0
- package/tests/init-package-json.test.ts +18 -0
- package/tests/init-project-detect.test.ts +53 -0
- package/tests/init.test.ts +30 -3
- package/tests/required-host-components.test.ts +51 -0
- package/tests/resolve-api-url.test.ts +23 -0
- package/tests/rest-cache-admin.test.ts +28 -0
- package/tests/runtime-contract.test.ts +12 -3
- package/tests/stdin-after-ink.test.ts +8 -0
- package/tests/tsconfig.json +8 -2
- package/tests/ui-confirm.test.ts +13 -9
- package/tests/ui-messages.test.ts +4 -18
- package/tsconfig.json +1 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +3 -0
- package/dist/dev-tui.d.ts +0 -24
- package/dist/dev-tui.d.ts.map +0 -1
- package/dist/dev-tui.js +0 -188
- package/dist/dev-tui.js.map +0 -1
- package/src/dev-tui.ts +0 -232
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"valkey-sidecar.js","sourceRoot":"","sources":["../src/valkey-sidecar.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supatype/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Supatype CLI — schema push, introspect, generate",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,8 +19,12 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@clack/prompts": "^1.6.0",
|
|
23
22
|
"bcryptjs": "^3.0.2",
|
|
23
|
+
"ink": "^5.2.1",
|
|
24
|
+
"ink-select-input": "^6.2.0",
|
|
25
|
+
"ink-spinner": "^5.0.0",
|
|
26
|
+
"ink-text-input": "^6.0.0",
|
|
27
|
+
"react": "^18.3.1",
|
|
24
28
|
"commander": "^12.1.0",
|
|
25
29
|
"pg": "^8.13.0",
|
|
26
30
|
"ts-morph": "^28.0.0",
|
|
@@ -29,6 +33,7 @@
|
|
|
29
33
|
},
|
|
30
34
|
"devDependencies": {
|
|
31
35
|
"@types/bcryptjs": "^2.4.6",
|
|
36
|
+
"@types/react": "^18.3.18",
|
|
32
37
|
"@types/node": "^22",
|
|
33
38
|
"@types/pg": "^8",
|
|
34
39
|
"vitest": "^4.0.18"
|
|
@@ -36,7 +41,8 @@
|
|
|
36
41
|
"scripts": {
|
|
37
42
|
"build": "tsc --project tsconfig.json && node --input-type=module -e \"import{cpSync,mkdirSync}from'node:fs';const a='dist/assets';mkdirSync(a,{recursive:true});cpSync('assets/supatype-logo-wordmark.ascii.txt',a+'/supatype-logo-wordmark.ascii.txt')\"",
|
|
38
43
|
"dev": "tsc --project tsconfig.json --watch",
|
|
39
|
-
"typecheck": "tsc --project tsconfig.json --noEmit",
|
|
44
|
+
"typecheck": "tsc --project tsconfig.json --noEmit && tsc --project tests/tsconfig.json --noEmit",
|
|
45
|
+
"typecheck:tests": "tsc --project tests/tsconfig.json --noEmit",
|
|
40
46
|
"postinstall": "node --input-type=module --eval \"import{existsSync}from'node:fs';import{resolve}from'node:path';import{pathToFileURL}from'node:url';const f=resolve(process.cwd(),'dist/scripts/postinstall.js');if(existsSync(f))await import(pathToFileURL(f).href)\"",
|
|
41
47
|
"test": "vitest run",
|
|
42
48
|
"clean": "rm -rf dist *.tsbuildinfo"
|
package/src/binary-cache.ts
CHANGED
|
@@ -37,6 +37,12 @@ import { basename, join, resolve, isAbsolute } from "node:path"
|
|
|
37
37
|
import type { SupatypeProjectConfig } from "./project-config.js"
|
|
38
38
|
import { loadProjectLink, migrateLegacyLinkFiles } from "./link.js"
|
|
39
39
|
import { releasePublicKey } from "./release-public-key.js"
|
|
40
|
+
import {
|
|
41
|
+
isDownloadInProgress,
|
|
42
|
+
releaseDownloadLock,
|
|
43
|
+
tryAcquireDownloadLock,
|
|
44
|
+
waitForComponentDownload,
|
|
45
|
+
} from "./binary-download-lock.js"
|
|
40
46
|
|
|
41
47
|
/**
|
|
42
48
|
* Set `versions.{engine|server|postgres|deno}: VERSION_PIN_LOCAL` to mean “use `overrides.*` only”
|
|
@@ -167,6 +173,16 @@ export function cachedBinaryPath(component: Component, version: string, platform
|
|
|
167
173
|
return join(cachePath(component, version), binaryName(component, version, platform))
|
|
168
174
|
}
|
|
169
175
|
|
|
176
|
+
/** True when the platform binary for `version` is present and passes format checks. */
|
|
177
|
+
export function isCachedBinaryReady(
|
|
178
|
+
component: Component,
|
|
179
|
+
version: string,
|
|
180
|
+
platform: PlatformId = currentPlatform(),
|
|
181
|
+
): boolean {
|
|
182
|
+
const destPath = cachedBinaryPath(component, version, platform)
|
|
183
|
+
return existsSync(destPath) && cachedArtifactLooksValid(component, destPath)
|
|
184
|
+
}
|
|
185
|
+
|
|
170
186
|
function binaryName(component: Component, version: string, platform: PlatformId): string {
|
|
171
187
|
const win = platform.os === "windows"
|
|
172
188
|
switch (component) {
|
|
@@ -299,6 +315,45 @@ export async function ensureCachedBinary(
|
|
|
299
315
|
return download(component, version, platform)
|
|
300
316
|
}
|
|
301
317
|
|
|
318
|
+
async function acquireDownloadSlot(
|
|
319
|
+
component: Component,
|
|
320
|
+
version: string,
|
|
321
|
+
platform: PlatformId,
|
|
322
|
+
): Promise<void> {
|
|
323
|
+
const isReady = () => isCachedBinaryReady(component, version, platform)
|
|
324
|
+
|
|
325
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
326
|
+
if (isReady()) return
|
|
327
|
+
|
|
328
|
+
if (tryAcquireDownloadLock(component, version)) return
|
|
329
|
+
|
|
330
|
+
if (isDownloadInProgress(component, version)) {
|
|
331
|
+
console.log(
|
|
332
|
+
`[supatype] ${component} v${version} is downloading in another process — waiting...`,
|
|
333
|
+
)
|
|
334
|
+
const outcome = await waitForComponentDownload(component, version, isReady, (c) => {
|
|
335
|
+
console.log(`[supatype] Still waiting for ${c} download...`)
|
|
336
|
+
})
|
|
337
|
+
if (outcome === "ready") return
|
|
338
|
+
if (outcome === "timeout") {
|
|
339
|
+
throw new Error(
|
|
340
|
+
`Timed out waiting for ${component} v${version} download. Run: supatype update`,
|
|
341
|
+
)
|
|
342
|
+
}
|
|
343
|
+
console.warn(
|
|
344
|
+
`[supatype] ${component} v${version} download did not finish in the other process — retrying.`,
|
|
345
|
+
)
|
|
346
|
+
continue
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (tryAcquireDownloadLock(component, version)) return
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
throw new Error(
|
|
353
|
+
`Could not acquire download lock for ${component} v${version}. Run: supatype update`,
|
|
354
|
+
)
|
|
355
|
+
}
|
|
356
|
+
|
|
302
357
|
export async function download(
|
|
303
358
|
component: Component,
|
|
304
359
|
version: string,
|
|
@@ -327,6 +382,11 @@ export async function download(
|
|
|
327
382
|
unlinkSync(destPath)
|
|
328
383
|
}
|
|
329
384
|
|
|
385
|
+
await acquireDownloadSlot(component, version, platform)
|
|
386
|
+
if (isCachedBinaryReady(component, version, platform)) {
|
|
387
|
+
return destPath
|
|
388
|
+
}
|
|
389
|
+
|
|
330
390
|
const binaryUrl = `${CDN_BASE}${CDN_PATHS[component](version, platform)}`
|
|
331
391
|
const checksumsUrl = `${CDN_BASE}${checksumsDirPath(component, version)}`
|
|
332
392
|
const minisigUrl = `${checksumsUrl}.minisig`
|
|
@@ -361,6 +421,7 @@ export async function download(
|
|
|
361
421
|
`Failed to download ${component} v${version} from ${CDN_BASE}: ${(err as Error).message}`,
|
|
362
422
|
)
|
|
363
423
|
} finally {
|
|
424
|
+
releaseDownloadLock(component, version)
|
|
364
425
|
if (existsSync(tmpPath)) {
|
|
365
426
|
try { unlinkSync(tmpPath) } catch { /* ignore */ }
|
|
366
427
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coordinates concurrent component-binary downloads (postinstall, init, dev).
|
|
3
|
+
* A second CLI process waits for an in-flight download instead of racing the CDN.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs"
|
|
7
|
+
import { join } from "node:path"
|
|
8
|
+
import { cachePath, type Component } from "./binary-cache.js"
|
|
9
|
+
|
|
10
|
+
const LOCK_VERSION = 1 as const
|
|
11
|
+
const STALE_MS = 30 * 60 * 1000
|
|
12
|
+
const POLL_MS = 500
|
|
13
|
+
const WAIT_NOTICE_MS = 3000
|
|
14
|
+
|
|
15
|
+
interface DownloadLockPayload {
|
|
16
|
+
version: typeof LOCK_VERSION
|
|
17
|
+
pid: number
|
|
18
|
+
component: Component
|
|
19
|
+
semver: string
|
|
20
|
+
startedAt: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function lockPath(component: Component, version: string): string {
|
|
24
|
+
return join(cachePath(component, version), ".downloading")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function sleep(ms: number): Promise<void> {
|
|
28
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isPidAlive(pid: number): boolean {
|
|
32
|
+
try {
|
|
33
|
+
process.kill(pid, 0)
|
|
34
|
+
return true
|
|
35
|
+
} catch {
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function readLock(component: Component, version: string): DownloadLockPayload | null {
|
|
41
|
+
const path = lockPath(component, version)
|
|
42
|
+
if (!existsSync(path)) return null
|
|
43
|
+
try {
|
|
44
|
+
const data = JSON.parse(readFileSync(path, "utf8")) as DownloadLockPayload
|
|
45
|
+
if (data.version !== LOCK_VERSION) return null
|
|
46
|
+
return data
|
|
47
|
+
} catch {
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isLockStale(lock: DownloadLockPayload): boolean {
|
|
53
|
+
if (!isPidAlive(lock.pid)) return true
|
|
54
|
+
const age = Date.now() - Date.parse(lock.startedAt)
|
|
55
|
+
return Number.isNaN(age) || age > STALE_MS
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function clearDownloadLock(component: Component, version: string): void {
|
|
59
|
+
const path = lockPath(component, version)
|
|
60
|
+
if (existsSync(path)) unlinkSync(path)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Atomically claim the download lock for this process, or return false if another live holder exists. */
|
|
64
|
+
export function tryAcquireDownloadLock(component: Component, version: string): boolean {
|
|
65
|
+
mkdirSync(cachePath(component, version), { recursive: true })
|
|
66
|
+
const existing = readLock(component, version)
|
|
67
|
+
if (existing && !isLockStale(existing)) return false
|
|
68
|
+
if (existing) clearDownloadLock(component, version)
|
|
69
|
+
|
|
70
|
+
const payload: DownloadLockPayload = {
|
|
71
|
+
version: LOCK_VERSION,
|
|
72
|
+
pid: process.pid,
|
|
73
|
+
component,
|
|
74
|
+
semver: version,
|
|
75
|
+
startedAt: new Date().toISOString(),
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
writeFileSync(lockPath(component, version), JSON.stringify(payload), { flag: "wx" })
|
|
79
|
+
return true
|
|
80
|
+
} catch {
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function releaseDownloadLock(component: Component, version: string): void {
|
|
86
|
+
const lock = readLock(component, version)
|
|
87
|
+
if (!lock || lock.pid !== process.pid) return
|
|
88
|
+
clearDownloadLock(component, version)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function isDownloadInProgress(component: Component, version: string): boolean {
|
|
92
|
+
const lock = readLock(component, version)
|
|
93
|
+
return lock !== null && !isLockStale(lock)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function waitForComponentDownload(
|
|
97
|
+
component: Component,
|
|
98
|
+
version: string,
|
|
99
|
+
isReady: () => boolean,
|
|
100
|
+
onWait?: (component: Component) => void,
|
|
101
|
+
): Promise<"ready" | "failed" | "timeout"> {
|
|
102
|
+
const deadline = Date.now() + STALE_MS
|
|
103
|
+
let lastNotice = 0
|
|
104
|
+
|
|
105
|
+
while (Date.now() < deadline) {
|
|
106
|
+
if (isReady()) return "ready"
|
|
107
|
+
|
|
108
|
+
const lock = readLock(component, version)
|
|
109
|
+
if (!lock || isLockStale(lock)) {
|
|
110
|
+
if (isReady()) return "ready"
|
|
111
|
+
return "failed"
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const now = Date.now()
|
|
115
|
+
if (onWait && now - lastNotice >= WAIT_NOTICE_MS) {
|
|
116
|
+
onWait(component)
|
|
117
|
+
lastNotice = now
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await sleep(POLL_MS)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return isReady() ? "ready" : "timeout"
|
|
124
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs"
|
|
2
|
+
import { dirname, join } from "node:path"
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
4
|
+
|
|
5
|
+
const CLI_PACKAGE_JSON = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json")
|
|
6
|
+
|
|
7
|
+
/** Installed @supatype/cli version from package.json (used by --version and init scaffolding). */
|
|
8
|
+
export function cliPackageVersion(): string {
|
|
9
|
+
try {
|
|
10
|
+
const pkg = JSON.parse(readFileSync(CLI_PACKAGE_JSON, "utf8")) as { version?: string }
|
|
11
|
+
return pkg.version ?? "0.0.0"
|
|
12
|
+
} catch {
|
|
13
|
+
return "0.0.0"
|
|
14
|
+
}
|
|
15
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -29,13 +29,15 @@ import { registerPlugins } from "./commands/plugins.js"
|
|
|
29
29
|
import { registerTypes } from "./commands/types.js"
|
|
30
30
|
import { registerMigrateFromV1 } from "./commands/migrate-from-v1.js"
|
|
31
31
|
import { registerSelfUpdate } from "./commands/self-update.js"
|
|
32
|
+
import { cliPackageVersion } from "./cli-package-version.js"
|
|
32
33
|
import { reportCliFatal } from "./ui/fatal.js"
|
|
34
|
+
import { wrapProgramActionsWithChrome } from "./ui/runtime/command-chrome.js"
|
|
33
35
|
|
|
34
36
|
export async function run(): Promise<void> {
|
|
35
37
|
const program = new Command()
|
|
36
38
|
.name("supatype")
|
|
37
|
-
.description("Supatype —
|
|
38
|
-
.version(
|
|
39
|
+
.description("Supatype — type-first platform for PostgreSQL")
|
|
40
|
+
.version(cliPackageVersion())
|
|
39
41
|
|
|
40
42
|
registerInit(program)
|
|
41
43
|
registerDev(program)
|
|
@@ -68,6 +70,8 @@ export async function run(): Promise<void> {
|
|
|
68
70
|
registerTypes(program)
|
|
69
71
|
registerMigrateFromV1(program)
|
|
70
72
|
|
|
73
|
+
wrapProgramActionsWithChrome(program)
|
|
74
|
+
|
|
71
75
|
try {
|
|
72
76
|
await program.parseAsync(process.argv)
|
|
73
77
|
} catch (err) {
|
package/src/commands/add.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Command } from "commander"
|
|
2
|
-
import
|
|
2
|
+
import { p, runClackFlow } from "../ui/clack.js"
|
|
3
3
|
import { loadConfig } from "../config.js"
|
|
4
4
|
import { selfHostTlsEnabled } from "../project-config.js"
|
|
5
5
|
import { updateServerConfigInProject } from "../app-config.js"
|
|
6
|
-
import { ensureNotCancelled
|
|
6
|
+
import { ensureNotCancelled } from "../ui/prompts.js"
|
|
7
7
|
import { file, error, info, plain } from "../ui/messages.js"
|
|
8
8
|
import { nextSteps } from "../ui/next-steps.js"
|
|
9
9
|
|
|
@@ -49,26 +49,33 @@ async function addDomain(domainArg?: string, emailArg?: string): Promise<void> {
|
|
|
49
49
|
const cwd = process.cwd()
|
|
50
50
|
const interactive = !domainArg?.trim() || !emailArg?.trim()
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const domain = await promptDomain(domainArg)
|
|
58
|
-
const email = await promptEmail(emailArg)
|
|
52
|
+
const run = async (): Promise<void> => {
|
|
53
|
+
const domain = await promptDomain(domainArg)
|
|
54
|
+
const email = await promptEmail(emailArg)
|
|
59
55
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
try {
|
|
57
|
+
const configPath = updateServerConfigInProject(cwd, { domain, tlsEmail: email })
|
|
58
|
+
if (interactive) {
|
|
59
|
+
p.outro(`Updated ${configPath}`)
|
|
60
|
+
} else {
|
|
61
|
+
file("updated", configPath)
|
|
62
|
+
}
|
|
63
|
+
printDomainNextSteps(cwd, domain)
|
|
64
|
+
} catch (err) {
|
|
65
|
+
error((err as Error).message)
|
|
66
|
+
process.exit(1)
|
|
66
67
|
}
|
|
67
|
-
printDomainNextSteps(cwd, domain)
|
|
68
|
-
} catch (err) {
|
|
69
|
-
error((err as Error).message)
|
|
70
|
-
process.exit(1)
|
|
71
68
|
}
|
|
69
|
+
|
|
70
|
+
if (interactive) {
|
|
71
|
+
await runClackFlow(async () => {
|
|
72
|
+
p.intro("Add a custom domain")
|
|
73
|
+
await run()
|
|
74
|
+
})
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
await run()
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
function printDomainNextSteps(cwd: string, domain: string): void {
|
package/src/commands/admin.ts
CHANGED
|
@@ -17,16 +17,49 @@ import {
|
|
|
17
17
|
} from "../project-config.js"
|
|
18
18
|
import { readEnvValue, upsertEnvFile } from "../env-file.js"
|
|
19
19
|
import { hasEngineOverride } from "../binary-cache.js"
|
|
20
|
+
import { readDevSessionLock } from "../dev-session-lock.js"
|
|
21
|
+
import { composeProjectName } from "../self-host-compose.js"
|
|
20
22
|
import { confirm as uiConfirm } from "../ui/confirm.js"
|
|
21
23
|
import { error, info, plain } from "../ui/messages.js"
|
|
22
|
-
import { promptText } from "../ui/prompts.js"
|
|
24
|
+
import { promptPassword, promptText } from "../ui/prompts.js"
|
|
23
25
|
import { isInteractive } from "../ui/interactive.js"
|
|
26
|
+
import {
|
|
27
|
+
getActiveDevSession,
|
|
28
|
+
} from "../dev-session.js"
|
|
24
29
|
|
|
25
30
|
export const ADMIN_EMAIL_ENV = "SUPATYPE_ADMIN_EMAIL"
|
|
26
31
|
export const ADMIN_PASSWORD_ENV = "SUPATYPE_ADMIN_PASSWORD"
|
|
27
32
|
|
|
28
33
|
const BCRYPT_ROUNDS = 10
|
|
29
34
|
|
|
35
|
+
/** GoTrue scopes users to the nil instance id in single-tenant/self-host mode. */
|
|
36
|
+
export const GOTRUE_NIL_INSTANCE_ID = "00000000-0000-0000-0000-000000000000"
|
|
37
|
+
|
|
38
|
+
/** Audience claim used by GoTrue when looking up users (matches GOTRUE_JWT_AUD in compose). */
|
|
39
|
+
export function gotrueJwtAud(cwd: string): string {
|
|
40
|
+
return readEnvValue(cwd, "GOTRUE_JWT_AUD", "authenticated")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type DbQuery = (sql: string, params?: unknown[]) => Promise<QueryResult>
|
|
44
|
+
type AuthConfirmedColumn = "email_confirmed_at" | "confirmed_at"
|
|
45
|
+
|
|
46
|
+
/** Prefer GoTrue's column when migrated; fall back to postgres init `confirmed_at`. */
|
|
47
|
+
export async function resolveAuthConfirmedAtColumn(query: DbQuery): Promise<AuthConfirmedColumn> {
|
|
48
|
+
const result = await query(
|
|
49
|
+
`SELECT column_name
|
|
50
|
+
FROM information_schema.columns
|
|
51
|
+
WHERE table_schema = 'auth'
|
|
52
|
+
AND table_name = 'users'
|
|
53
|
+
AND column_name IN ('email_confirmed_at', 'confirmed_at')
|
|
54
|
+
ORDER BY CASE column_name WHEN 'email_confirmed_at' THEN 0 ELSE 1 END
|
|
55
|
+
LIMIT 1`,
|
|
56
|
+
)
|
|
57
|
+
const row = result.rows[0] as { column_name?: string; value?: string } | undefined
|
|
58
|
+
const name = row?.column_name ?? row?.value
|
|
59
|
+
if (name === "email_confirmed_at" || name === "confirmed_at") return name
|
|
60
|
+
return "confirmed_at"
|
|
61
|
+
}
|
|
62
|
+
|
|
30
63
|
export interface EnsureFirstAdminOptions {
|
|
31
64
|
email?: string
|
|
32
65
|
password?: string
|
|
@@ -36,8 +69,6 @@ export interface EnsureFirstAdminOptions {
|
|
|
36
69
|
compose?: { project: string; composePath: string }
|
|
37
70
|
}
|
|
38
71
|
|
|
39
|
-
type DbQuery = (sql: string, params?: unknown[]) => Promise<QueryResult>
|
|
40
|
-
|
|
41
72
|
export function registerAdmin(program: Command): void {
|
|
42
73
|
const adminCmd = program
|
|
43
74
|
.command("admin")
|
|
@@ -78,20 +109,33 @@ export function registerAdmin(program: Command): void {
|
|
|
78
109
|
|
|
79
110
|
info(`Creating admin user: ${email} (role: ${role})...`)
|
|
80
111
|
|
|
81
|
-
const
|
|
82
|
-
|
|
112
|
+
const compose =
|
|
113
|
+
opts.connection === undefined ? resolveDockerComposeContext(cwd, config) : null
|
|
83
114
|
|
|
84
115
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
116
|
+
if (compose) {
|
|
117
|
+
await createAdminUser(
|
|
118
|
+
(sql, params) => composeExecQuery(cwd, compose, sql, params),
|
|
119
|
+
email,
|
|
120
|
+
password,
|
|
121
|
+
role,
|
|
122
|
+
{ cwd },
|
|
123
|
+
)
|
|
124
|
+
} else {
|
|
125
|
+
const pg = await importPg()
|
|
126
|
+
const pool = new pg.Pool({ connectionString: connection, max: 2 })
|
|
127
|
+
try {
|
|
128
|
+
await ensureAuthUsersTable(pool)
|
|
129
|
+
await createAdminUser(pool, email, password, role, { cwd })
|
|
130
|
+
} finally {
|
|
131
|
+
await pool.end()
|
|
132
|
+
}
|
|
133
|
+
}
|
|
87
134
|
info("This user can now log in to the admin panel at /admin")
|
|
88
135
|
} catch (err) {
|
|
89
|
-
const message =
|
|
90
|
-
err instanceof Error ? err.message : "Unknown error"
|
|
136
|
+
const message = err instanceof Error ? err.message : "Unknown error"
|
|
91
137
|
error(`Failed to create admin user: ${message}`)
|
|
92
138
|
process.exit(1)
|
|
93
|
-
} finally {
|
|
94
|
-
await pool.end()
|
|
95
139
|
}
|
|
96
140
|
},
|
|
97
141
|
)
|
|
@@ -249,8 +293,9 @@ export async function ensureFirstAdminUserForProject(
|
|
|
249
293
|
(sql, params) => composeExecQuery(root, merged.compose!, sql, params),
|
|
250
294
|
merged,
|
|
251
295
|
)
|
|
252
|
-
} catch {
|
|
253
|
-
|
|
296
|
+
} catch (err) {
|
|
297
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
298
|
+
error(`Could not ensure first admin user: ${message}`)
|
|
254
299
|
}
|
|
255
300
|
return
|
|
256
301
|
}
|
|
@@ -284,9 +329,27 @@ async function ensureFirstAdminWithQuery(
|
|
|
284
329
|
}
|
|
285
330
|
|
|
286
331
|
const role = options.role ?? "admin"
|
|
287
|
-
|
|
332
|
+
try {
|
|
333
|
+
await createAdminUser(query, credentials.email, credentials.password, role, {
|
|
334
|
+
quiet: true,
|
|
335
|
+
cwd,
|
|
336
|
+
})
|
|
337
|
+
} catch (err) {
|
|
338
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
339
|
+
error(`Failed to create admin user: ${message}`)
|
|
340
|
+
return
|
|
341
|
+
}
|
|
288
342
|
clearAdminSeedPassword(cwd)
|
|
289
|
-
|
|
343
|
+
logFirstAdminCreated(credentials.email, role)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function logFirstAdminCreated(email: string, role: string): void {
|
|
347
|
+
if (getActiveDevSession()?.isTui()) {
|
|
348
|
+
console.log(`Admin user "${email}" created (role: ${role}).`)
|
|
349
|
+
console.log("Log in at /admin after starting the dev server.")
|
|
350
|
+
return
|
|
351
|
+
}
|
|
352
|
+
info(`Admin user "${email}" created (role: ${role}).`)
|
|
290
353
|
info("Log in at /admin after starting the dev server.")
|
|
291
354
|
}
|
|
292
355
|
|
|
@@ -325,7 +388,7 @@ async function resolveAdminCredentials(
|
|
|
325
388
|
return null
|
|
326
389
|
}
|
|
327
390
|
|
|
328
|
-
const password = await
|
|
391
|
+
const password = await promptPassword("Admin password (min 8 chars)")
|
|
329
392
|
if (!password || password.length < 8) {
|
|
330
393
|
info("Password too short. Skipping admin user creation.")
|
|
331
394
|
return null
|
|
@@ -347,7 +410,7 @@ async function createAdminUser(
|
|
|
347
410
|
email: string,
|
|
348
411
|
password: string,
|
|
349
412
|
role: string,
|
|
350
|
-
opts: { quiet?: boolean } = {},
|
|
413
|
+
opts: { quiet?: boolean; aud?: string; cwd?: string } = {},
|
|
351
414
|
): Promise<{ id: string; email: string }> {
|
|
352
415
|
const query: DbQuery =
|
|
353
416
|
typeof (db as Pool).query === "function"
|
|
@@ -355,9 +418,15 @@ async function createAdminUser(
|
|
|
355
418
|
: (db as DbQuery)
|
|
356
419
|
|
|
357
420
|
const normalized = email.toLowerCase()
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
421
|
+
const aud = opts.aud ?? (opts.cwd ? gotrueJwtAud(opts.cwd) : "authenticated")
|
|
422
|
+
const existing = await query(
|
|
423
|
+
`SELECT id FROM auth.users
|
|
424
|
+
WHERE instance_id = $1::uuid
|
|
425
|
+
AND LOWER(email) = $2
|
|
426
|
+
AND aud = $3
|
|
427
|
+
AND is_sso_user = false`,
|
|
428
|
+
[GOTRUE_NIL_INSTANCE_ID, normalized, aud],
|
|
429
|
+
)
|
|
361
430
|
if (existing.rows.length > 0) {
|
|
362
431
|
throw new Error(`User with email "${email}" already exists.`)
|
|
363
432
|
}
|
|
@@ -369,18 +438,27 @@ async function createAdminUser(
|
|
|
369
438
|
providers: ["email"],
|
|
370
439
|
})
|
|
371
440
|
const userMetadata = JSON.stringify({})
|
|
441
|
+
const confirmedCol = await resolveAuthConfirmedAtColumn(query)
|
|
372
442
|
|
|
373
443
|
const result = await query(
|
|
374
444
|
`INSERT INTO auth.users (
|
|
375
|
-
|
|
445
|
+
instance_id, id, aud, role, email, encrypted_password,
|
|
376
446
|
raw_app_meta_data, raw_user_meta_data,
|
|
377
|
-
|
|
447
|
+
${confirmedCol},
|
|
448
|
+
confirmation_token, recovery_token,
|
|
449
|
+
email_change_token_new, email_change, email_change_token_current,
|
|
450
|
+
phone_change, phone_change_token, reauthentication_token,
|
|
451
|
+
is_sso_user, is_anonymous,
|
|
452
|
+
created_at, updated_at
|
|
378
453
|
) VALUES (
|
|
379
|
-
$1, $2, 'authenticated',
|
|
380
|
-
$
|
|
381
|
-
now(),
|
|
454
|
+
$1::uuid, gen_random_uuid(), $2, 'authenticated', $3, $4,
|
|
455
|
+
$5::jsonb, $6::jsonb,
|
|
456
|
+
now(),
|
|
457
|
+
'', '', '', '', '', '', '', '',
|
|
458
|
+
false, false,
|
|
459
|
+
now(), now()
|
|
382
460
|
) RETURNING id, email`,
|
|
383
|
-
[normalized, passwordHash, appMetadata, userMetadata],
|
|
461
|
+
[GOTRUE_NIL_INSTANCE_ID, aud, normalized, passwordHash, appMetadata, userMetadata],
|
|
384
462
|
)
|
|
385
463
|
|
|
386
464
|
const user = result.rows[0] as { id: string; email: string }
|
|
@@ -399,7 +477,7 @@ async function ensureAuthUsersTable(pool: Pool): Promise<void> {
|
|
|
399
477
|
|
|
400
478
|
CREATE TABLE IF NOT EXISTS auth.users (
|
|
401
479
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
402
|
-
instance_id UUID,
|
|
480
|
+
instance_id UUID DEFAULT '00000000-0000-0000-0000-000000000000'::uuid,
|
|
403
481
|
aud TEXT DEFAULT 'authenticated',
|
|
404
482
|
role TEXT DEFAULT 'authenticated',
|
|
405
483
|
email TEXT UNIQUE,
|
|
@@ -412,7 +490,13 @@ async function ensureAuthUsersTable(pool: Pool): Promise<void> {
|
|
|
412
490
|
confirmation_token TEXT DEFAULT '',
|
|
413
491
|
recovery_token TEXT DEFAULT '',
|
|
414
492
|
email_change_token_new TEXT DEFAULT '',
|
|
415
|
-
|
|
493
|
+
email_change_token_current TEXT DEFAULT '',
|
|
494
|
+
email_change TEXT DEFAULT '',
|
|
495
|
+
phone_change TEXT DEFAULT '',
|
|
496
|
+
phone_change_token TEXT DEFAULT '',
|
|
497
|
+
reauthentication_token TEXT DEFAULT '',
|
|
498
|
+
is_sso_user BOOLEAN NOT NULL DEFAULT false,
|
|
499
|
+
is_anonymous BOOLEAN NOT NULL DEFAULT false
|
|
416
500
|
);
|
|
417
501
|
`)
|
|
418
502
|
}
|
|
@@ -437,6 +521,33 @@ async function hasAdminUsers(query: DbQuery): Promise<boolean> {
|
|
|
437
521
|
return count > 0
|
|
438
522
|
}
|
|
439
523
|
|
|
524
|
+
/** Postgres password for compose `exec psql` (db is not published to the host). */
|
|
525
|
+
export function composePostgresPassword(cwd: string): string {
|
|
526
|
+
return readEnvValue(cwd, "POSTGRES_PASSWORD", "postgres")
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function resolveDockerComposeContext(
|
|
530
|
+
cwd: string,
|
|
531
|
+
config: SupatypeProjectConfig,
|
|
532
|
+
): { project: string; composePath: string } | null {
|
|
533
|
+
if (resolveRuntimeProvider(config) !== "docker" || hasEngineOverride(config)) {
|
|
534
|
+
return null
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const session = readDevSessionLock(cwd)
|
|
538
|
+
if (session?.composeProject && session.composePath && existsSync(session.composePath)) {
|
|
539
|
+
return { project: session.composeProject, composePath: session.composePath }
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const composePath = resolve(cwd, ".supatype/self-host/docker-compose.yml")
|
|
543
|
+
if (!existsSync(composePath)) return null
|
|
544
|
+
|
|
545
|
+
return {
|
|
546
|
+
project: composeProjectName(config.project?.name ?? "project"),
|
|
547
|
+
composePath,
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
440
551
|
function composeExecQuery(
|
|
441
552
|
cwd: string,
|
|
442
553
|
compose: { project: string; composePath: string },
|
|
@@ -445,6 +556,7 @@ function composeExecQuery(
|
|
|
445
556
|
): Promise<QueryResult> {
|
|
446
557
|
const db = readEnvValue(cwd, "POSTGRES_DB", "supatype")
|
|
447
558
|
const user = readEnvValue(cwd, "POSTGRES_USER", "supatype_admin")
|
|
559
|
+
const pgPassword = composePostgresPassword(cwd)
|
|
448
560
|
const envFile = join(cwd, ".env")
|
|
449
561
|
const composeDir = dirname(compose.composePath)
|
|
450
562
|
const args = [
|
|
@@ -459,7 +571,20 @@ function composeExecQuery(
|
|
|
459
571
|
if (existsSync(envFile)) args.push("--env-file", envFile)
|
|
460
572
|
|
|
461
573
|
if (params.length === 0) {
|
|
462
|
-
args.push(
|
|
574
|
+
args.push(
|
|
575
|
+
"exec",
|
|
576
|
+
"-T",
|
|
577
|
+
"-e",
|
|
578
|
+
`PGPASSWORD=${pgPassword}`,
|
|
579
|
+
"db",
|
|
580
|
+
"psql",
|
|
581
|
+
"-U",
|
|
582
|
+
user,
|
|
583
|
+
"-d",
|
|
584
|
+
db,
|
|
585
|
+
"-tAc",
|
|
586
|
+
sql,
|
|
587
|
+
)
|
|
463
588
|
const result = spawnSync("docker", args, { cwd: composeDir, encoding: "utf8" })
|
|
464
589
|
if (result.status !== 0) {
|
|
465
590
|
throw new Error((result.stderr ?? result.stdout ?? "compose psql failed").trim())
|
|
@@ -486,6 +611,8 @@ function composeExecQuery(
|
|
|
486
611
|
args.push(
|
|
487
612
|
"exec",
|
|
488
613
|
"-T",
|
|
614
|
+
"-e",
|
|
615
|
+
`PGPASSWORD=${pgPassword}`,
|
|
489
616
|
"db",
|
|
490
617
|
"psql",
|
|
491
618
|
"-U",
|