@supatype/cli 0.1.2 → 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.
Files changed (67) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +108 -105
  3. package/.turbo/turbo-typecheck.log +1 -1
  4. package/dist/commands/admin.d.ts +12 -0
  5. package/dist/commands/admin.d.ts.map +1 -1
  6. package/dist/commands/admin.js +97 -23
  7. package/dist/commands/admin.js.map +1 -1
  8. package/dist/commands/init.d.ts.map +1 -1
  9. package/dist/commands/init.js +287 -182
  10. package/dist/commands/init.js.map +1 -1
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +12 -1
  13. package/dist/config.js.map +1 -1
  14. package/dist/dev-compose.js +3 -3
  15. package/dist/dev-compose.js.map +1 -1
  16. package/dist/dev-log-bus.d.ts +1 -1
  17. package/dist/dev-log-bus.d.ts.map +1 -1
  18. package/dist/dev-log-bus.js +5 -3
  19. package/dist/dev-log-bus.js.map +1 -1
  20. package/dist/dev-ready-panel.d.ts +2 -0
  21. package/dist/dev-ready-panel.d.ts.map +1 -1
  22. package/dist/dev-ready-panel.js +18 -0
  23. package/dist/dev-ready-panel.js.map +1 -1
  24. package/dist/init-project-detect.d.ts +14 -0
  25. package/dist/init-project-detect.d.ts.map +1 -0
  26. package/dist/init-project-detect.js +89 -0
  27. package/dist/init-project-detect.js.map +1 -0
  28. package/dist/ui/dev/DevDashboard.d.ts.map +1 -1
  29. package/dist/ui/dev/DevDashboard.js +11 -4
  30. package/dist/ui/dev/DevDashboard.js.map +1 -1
  31. package/dist/ui/dev/DevReadyPanelView.d.ts +2 -1
  32. package/dist/ui/dev/DevReadyPanelView.d.ts.map +1 -1
  33. package/dist/ui/dev/DevReadyPanelView.js +21 -2
  34. package/dist/ui/dev/DevReadyPanelView.js.map +1 -1
  35. package/dist/ui/flows/FlowApp.d.ts.map +1 -1
  36. package/dist/ui/flows/FlowApp.js +3 -1
  37. package/dist/ui/flows/FlowApp.js.map +1 -1
  38. package/dist/ui/flows/prompt-fields.d.ts.map +1 -1
  39. package/dist/ui/flows/prompt-fields.js +9 -5
  40. package/dist/ui/flows/prompt-fields.js.map +1 -1
  41. package/dist/ui/runtime/run-clack-flow.d.ts.map +1 -1
  42. package/dist/ui/runtime/run-clack-flow.js +15 -3
  43. package/dist/ui/runtime/run-clack-flow.js.map +1 -1
  44. package/dist/ui/runtime/stdin-after-ink.d.ts +3 -0
  45. package/dist/ui/runtime/stdin-after-ink.d.ts.map +1 -0
  46. package/dist/ui/runtime/stdin-after-ink.js +16 -0
  47. package/dist/ui/runtime/stdin-after-ink.js.map +1 -0
  48. package/package.json +1 -1
  49. package/src/commands/admin.ts +135 -26
  50. package/src/commands/init.ts +360 -213
  51. package/src/config.ts +13 -1
  52. package/src/dev-compose.ts +4 -4
  53. package/src/dev-log-bus.ts +5 -3
  54. package/src/dev-ready-panel.ts +17 -0
  55. package/src/init-project-detect.ts +99 -0
  56. package/src/ui/dev/DevDashboard.tsx +14 -4
  57. package/src/ui/dev/DevReadyPanelView.tsx +76 -9
  58. package/src/ui/flows/FlowApp.tsx +3 -1
  59. package/src/ui/flows/prompt-fields.tsx +13 -7
  60. package/src/ui/runtime/run-clack-flow.tsx +16 -3
  61. package/src/ui/runtime/stdin-after-ink.ts +18 -0
  62. package/tests/admin-ensure.test.ts +80 -0
  63. package/tests/config.test.ts +19 -0
  64. package/tests/dev-ready-panel.test.ts +18 -1
  65. package/tests/init-project-detect.test.ts +53 -0
  66. package/tests/stdin-after-ink.test.ts +8 -0
  67. package/tsconfig.tsbuildinfo +1 -1
@@ -1,4 +1,4 @@
1
1
 
2
- > @supatype/cli@0.1.2 build /home/runner/work/supatype/supatype/packages/cli
2
+ > @supatype/cli@0.1.3 build /home/runner/work/supatype/supatype/packages/cli
3
3
  > 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')"
4
4
 
@@ -1,12 +1,12 @@
1
1
 
2
- > @supatype/cli@0.1.2 test /home/runner/work/supatype/supatype/packages/cli
2
+ > @supatype/cli@0.1.3 test /home/runner/work/supatype/supatype/packages/cli
3
3
  > vitest run
4
4
 
5
5
 
6
6
   RUN  v4.0.18 /home/runner/work/supatype/supatype/packages/cli
7
7
 
8
- ✓ tests/type-extractor.test.ts (34 tests) 174ms
9
- ✓ tests/runtime-contract.test.ts (41 tests) 157ms
8
+ ✓ tests/type-extractor.test.ts (34 tests) 211ms
9
+ ✓ tests/runtime-contract.test.ts (41 tests) 141ms
10
10
  stdout | tests/init.test.ts > scaffold() > creates all expected files
11
11
   created package.json
12
12
  created supatype.config.ts
@@ -291,7 +291,9 @@
291
291
  created public/.gitkeep
292
292
  created .gitignore
293
293
 
294
- ✓ tests/init.test.ts (26 tests) 70ms
294
+ ✓ tests/init.test.ts (26 tests) 74ms
295
+
296
+ ✓ tests/dev-ui.test.ts (21 tests) 106ms
295
297
  stdout | tests/docker-runtime.test.ts > requireDockerDaemon > exits when the daemon is unavailable
296
298
  
297
299
  Start the Docker daemon (e.g. sudo systemctl start docker), then try again.
@@ -300,95 +302,95 @@
300
302
 
301
303
  docker: daemon down
302
304
 
303
- ✓ tests/docker-runtime.test.ts (10 tests) 13ms
304
-
305
- ✓ tests/dev-ui.test.ts (21 tests) 102ms
306
305
 
307
306
  [supatype] Shutting down…
308
- ✓ tests/config.test.ts (18 tests) 4407ms
309
- ✓ loads a valid supatype.config.ts (plain export default, no package imports)  600ms
310
- ✓ normalizes shorthand schema string to { path, pg_schema }  582ms
311
- ✓ loads a supatype.config.js (plain ESM)  579ms
312
- ✓ loads config without a versions section (latest by default)  503ms
313
- ✓ prefers supatype.config.ts over supatype.config.js when both exist  555ms
314
- ✓ supports optional output field in config  559ms
315
- ✓ merges supatype.local.config.ts over base  1016ms
316
- ✓ tests/command-chrome-coverage.test.ts (1 test) 17ms
307
+ ✓ tests/docker-runtime.test.ts (10 tests) 13ms
308
+ ✓ tests/config.test.ts (19 tests) 5120ms
309
+ ✓ loads a valid supatype.config.ts (plain export default, no package imports)  591ms
310
+ ✓ normalizes shorthand schema string to { path, pg_schema }  533ms
311
+ ✓ loads a supatype.config.js (plain ESM)  533ms
312
+ ✓ loads config without a versions section (latest by default)  451ms
313
+ ✓ prefers supatype.config.ts over supatype.config.js when both exist  491ms
314
+ ✓ supports optional output field in config  552ms
315
+ ✓ merges supatype.local.config.ts over base  1019ms
316
+ ✓ loads defineConfig import before @supatype/cli is installed (init scaffold)  938ms
317
+ ✓ tests/command-chrome-coverage.test.ts (1 test) 14ms
317
318
  stdout | tests/link.test.ts > link model > migrates cloud.json into link.json
318
319
   created package.json
320
+ stderr | tests/link.test.ts > link model > migrates cloud.json into link.json
319
321
  created supatype.config.ts
320
322
  created schema/index.ts
323
+ [supatype] Migrated .supatype/cloud.json → .supatype/link.json (legacy files kept; remove manually when ready).
321
324
  created .env
322
325
  created seed.ts
323
326
  created seeds/.gitkeep
324
327
  created public/.gitkeep
325
328
  created .gitignore
326
329
 
327
- stderr | tests/link.test.ts > link model > migrates cloud.json into link.json
328
- [supatype] Migrated .supatype/cloud.json → .supatype/link.json (legacy files kept; remove manually when ready).
329
330
 
330
- ✓ tests/link.test.ts (4 tests) 2778ms
331
- ✓ resolveTarget returns cloud mode with /api/v1 prefix  895ms
332
- ✓ resolveTarget returns self-host mode with /platform/v1 prefix  894ms
333
- ✓ targetSchemaRollback posts to /platform/v1 when linked  981ms
331
+ ✓ tests/link.test.ts (4 tests) 2602ms
332
+ ✓ resolveTarget returns cloud mode with /api/v1 prefix  909ms
333
+ ✓ resolveTarget returns self-host mode with /platform/v1 prefix  849ms
334
+ ✓ targetSchemaRollback posts to /platform/v1 when linked  834ms
335
+ stdout | tests/admin-ensure.test.ts > init admin seed in .env > writes SUPATYPE_ADMIN_* when scaffold options include credentials
336
+  created package.json
337
+ created supatype.config.ts
338
+ created schema/index.ts
339
+ created .env
340
+ created seed.ts
341
+ created seeds/.gitkeep
342
+ created public/.gitkeep
343
+ created .gitignore
344
+
345
+ ✓ tests/admin-ensure.test.ts (10 tests) 158ms
334
346
  ✓ tests/pull-utils.test.ts (49 tests) 11ms
335
- ✓ tests/schema-sources.test.ts (5 tests) 1117ms
336
- ✓ buildSchemaSourcesPayload includes manifest metadata  524ms
337
- ✓ restore overwrites modified local files  543ms
347
+ ✓ tests/schema-sources.test.ts (5 tests) 1160ms
348
+ ✓ buildSchemaSourcesPayload includes manifest metadata  503ms
349
+ ✓ restore overwrites modified local files  608ms
338
350
  stderr | tests/binary-cache-cloud-overrides.test.ts > isLinkedToCloudProject > is true when .supatype/cloud.json has projectSlug
339
351
  [supatype] Migrated .supatype/cloud.json → .supatype/link.json (legacy files kept; remove manually when ready).
340
352
 
341
- ✓ tests/binary-cache-cloud-overrides.test.ts (11 tests) 22ms
342
- ✓ tests/minisign.test.ts (5 tests) 13ms
353
+ ✓ tests/binary-cache-cloud-overrides.test.ts (11 tests) 19ms
354
+ ✓ tests/minisign.test.ts (5 tests) 14ms
343
355
  stdout | tests/storage-provision.test.ts > provisionBuckets > POSTs each bucket to the storage API
344
356
  [supatype] Storage bucket "photos" ready.
345
357
 
346
358
  stdout | tests/storage-provision.test.ts > provisionBuckets > POSTs each bucket to the storage API
347
359
  [supatype] Storage bucket "avatars" ready.
348
360
 
349
- ✓ tests/storage-provision.test.ts (6 tests) 9ms
350
- ✓ tests/cached-artifact-format.test.ts (7 tests) 13ms
351
- ✓ tests/proxy-dev-app.test.ts (7 tests) 6ms
352
- ✓ tests/init-dependency-versions.test.ts (4 tests) 35ms
353
- ✓ tests/tsx-runner.test.ts (7 tests) 2856ms
354
- ✓ evaluates a simple expression and returns stdout  351ms
355
- ✓ evaluates TypeScript syntax (type annotations)  369ms
356
- ✓ can serialize a JSON object to stdout  399ms
357
- ✓ returns non-zero exit code on syntax error  393ms
358
- ✓ returns non-zero exit code on runtime error  387ms
359
- ✓ captures stderr separately from stdout  402ms
360
- ✓ runs a .ts file and returns its stdout  551ms
361
- ✓ tests/augmentation-generator.test.ts (3 tests) 12ms
362
- ✓ tests/keys.test.ts (11 tests) 16973ms
363
- ✓ exits 0 with --secret  1455ms
364
- ✓ outputs ANON_KEY= line  1401ms
365
- ✓ outputs SERVICE_ROLE_KEY= line  1425ms
366
- ✓ ANON_KEY is a valid three-part JWT  1343ms
367
- ✓ SERVICE_ROLE_KEY payload has role: service_role  1477ms
368
- ✓ JWT has exp in the future  1476ms
369
- ✓ --exp-years changes the expiry window  2796ms
370
- ✓ reads JWT_SECRET from environment variable  1383ms
371
- ✓ reads JWT_SECRET from .env file  1398ms
372
- ✓ exits non-zero when no secret is available  1399ms
373
- ✓ keys --help shows --secret and --exp-years options  1415ms
374
- ✓ tests/engine-push-output.test.ts (7 tests) 7ms
361
+ ✓ tests/storage-provision.test.ts (6 tests) 11ms
362
+ ✓ tests/cached-artifact-format.test.ts (7 tests) 24ms
363
+ ✓ tests/proxy-dev-app.test.ts (7 tests) 5ms
364
+ ✓ tests/init-dependency-versions.test.ts (4 tests) 39ms
365
+ ✓ tests/keys.test.ts (11 tests) 16241ms
366
+ ✓ exits 0 with --secret  1443ms
367
+ ✓ outputs ANON_KEY= line  1398ms
368
+ ✓ outputs SERVICE_ROLE_KEY= line  1314ms
369
+ ✓ ANON_KEY is a valid three-part JWT  1369ms
370
+ ✓ SERVICE_ROLE_KEY payload has role: service_role  1397ms
371
+ ✓ JWT has exp in the future  1411ms
372
+ ✓ --exp-years changes the expiry window  2752ms
373
+ ✓ reads JWT_SECRET from environment variable  1240ms
374
+ ✓ reads JWT_SECRET from .env file  1296ms
375
+ ✓ exits non-zero when no secret is available  1318ms
376
+ ✓ keys --help shows --secret and --exp-years options  1299ms
377
+ ✓ tests/augmentation-generator.test.ts (3 tests) 17ms
375
378
  ✓ tests/type-resolver.test.ts (2 tests) 16ms
376
- ✓ tests/command-chrome-wrap.test.ts (3 tests) 7ms
377
- ✓ tests/ensure-binary.test.ts (2 tests) 10ms
378
- stdout | tests/admin-ensure.test.ts > init admin seed in .env > writes SUPATYPE_ADMIN_* when scaffold options include credentials
379
-  created package.json
380
- created supatype.config.ts
381
- created schema/index.ts
382
- created .env
383
- created seed.ts
384
- created seeds/.gitkeep
385
- created public/.gitkeep
386
- created .gitignore
387
-
388
- ✓ tests/admin-ensure.test.ts (3 tests) 145ms
389
- ✓ tests/ui-messages.test.ts (5 tests) 6ms
390
- ✓ tests/required-host-components.test.ts (4 tests) 7ms
391
- ✓ tests/normalize-admin-config.test.ts (1 test) 6ms
379
+ ✓ tests/engine-push-output.test.ts (7 tests) 6ms
380
+ ✓ tests/tsx-runner.test.ts (7 tests) 2720ms
381
+ ✓ evaluates a simple expression and returns stdout  400ms
382
+ ✓ evaluates TypeScript syntax (type annotations)  368ms
383
+ ✓ can serialize a JSON object to stdout  337ms
384
+ ✓ returns non-zero exit code on syntax error  342ms
385
+ ✓ returns non-zero exit code on runtime error  387ms
386
+ ✓ captures stderr separately from stdout  342ms
387
+ ✓ runs a .ts file and returns its stdout  541ms
388
+ ✓ tests/command-chrome-wrap.test.ts (3 tests) 6ms
389
+ ✓ tests/init-project-detect.test.ts (4 tests) 9ms
390
+ ✓ tests/ensure-binary.test.ts (2 tests) 12ms
391
+ ✓ tests/ui-messages.test.ts (5 tests) 7ms
392
+ ✓ tests/required-host-components.test.ts (4 tests) 5ms
393
+ ✓ tests/normalize-admin-config.test.ts (1 test) 7ms
392
394
  stdout | tests/app-command.test.ts > supatype app add --static > sets app.mode=static and static_dir in supatype.config.ts
393
395
   created package.json
394
396
  created supatype.config.ts
@@ -399,9 +401,9 @@
399
401
  created public/.gitkeep
400
402
  created .gitignore
401
403
 
402
- ✓ tests/dev-session-lock.test.ts (2 tests) 5ms
403
- ✓ tests/ui-confirm.test.ts (3 tests) 9ms
404
- ✓ tests/docker-postgres.test.ts (5 tests) 5ms
404
+ ✓ tests/dev-session-lock.test.ts (2 tests) 4ms
405
+ ✓ tests/ui-confirm.test.ts (3 tests) 8ms
406
+ ✓ tests/docker-postgres.test.ts (5 tests) 4ms
405
407
  ✓ tests/binary-download-lock.test.ts (2 tests) 7ms
406
408
  ✓ tests/dev-ports.test.ts (3 tests) 6ms
407
409
  stdout | tests/app-command.test.ts > supatype app add --static > creates the static directory when missing
@@ -414,41 +416,42 @@
414
416
  created public/.gitkeep
415
417
  created .gitignore
416
418
 
417
- ✓ tests/seed-discover.test.ts (2 tests) 20ms
419
+ ✓ tests/dev-ready-panel.test.ts (2 tests) 4ms
420
+ ✓ tests/cli-help.test.ts (16 tests) 21614ms
421
+ ✓ --help lists all top-level commands  1439ms
422
+ ✓ --version prints package.json version  1284ms
423
+ ✓ self-update --help describes the command  1331ms
424
+ ✓ init --help describes the init command  1443ms
425
+ ✓ push --help describes the push command and --yes flag  1272ms
426
+ ✓ diff --help describes the diff command  1335ms
427
+ ✓ pull --help describes scaffold pull command  1398ms
428
+ ✓ doctor --help describes drift report command  1280ms
429
+ ✓ introspect --help describes database introspection command  1370ms
430
+ ✓ adopt --help describes adoption ceremony command  1316ms
431
+ ✓ reset --help shows --yes flag  1428ms
432
+ ✓ app --help describes the app command with add/remove subcommands  1330ms
433
+ ✓ app add --help shows --static and --port options  1395ms
434
+ ✓ self-host --help shows compose only (legacy commands hidden)  1328ms
435
+ ✓ self-host compose --help shows compose subcommands  1274ms
436
+ ✓ unknown command exits non-zero  1383ms
418
437
  ✓ tests/studio-admin-roles.test.ts (3 tests) 5ms
419
- ✓ tests/cli-help.test.ts (16 tests) 22078ms
420
- ✓ --help lists all top-level commands  1455ms
421
- ✓ --version prints package.json version  1304ms
422
- ✓ self-update --help describes the command  1444ms
423
- ✓ init --help describes the init command  1357ms
424
- ✓ push --help describes the push command and --yes flag  1477ms
425
- ✓ diff --help describes the diff command  1369ms
426
- ✓ pull --help describes scaffold pull command  1401ms
427
- ✓ doctor --help describes drift report command  1293ms
428
- ✓ introspect --help describes database introspection command  1298ms
429
- ✓ adopt --help describes adoption ceremony command  1462ms
430
- ✓ reset --help shows --yes flag  1360ms
431
- ✓ app --help describes the app command with add/remove subcommands  1388ms
432
- ✓ app add --help shows --static and --port options  1371ms
433
- ✓ self-host --help shows compose only (legacy commands hidden)  1315ms
434
- ✓ self-host compose --help shows compose subcommands  1354ms
435
- ✓ unknown command exits non-zero  1427ms
436
- ✓ tests/release-pins.test.ts (2 tests) 5ms
437
- ✓ tests/ensure-dev-api-config.test.ts (1 test) 5ms
438
- ✓ tests/app-command.test.ts (2 tests) 2894ms
439
- ✓ sets app.mode=static and static_dir in supatype.config.ts  1541ms
440
- ✓ creates the static directory when missing  1350ms
441
- ✓ tests/rest-cache-admin.test.ts (1 test) 48ms
438
+ ✓ tests/release-pins.test.ts (2 tests) 4ms
439
+ ✓ tests/ensure-dev-api-config.test.ts (1 test) 4ms
440
+ ✓ tests/app-command.test.ts (2 tests) 2786ms
441
+ ✓ sets app.mode=static and static_dir in supatype.config.ts  1429ms
442
+ ✓ creates the static directory when missing  1355ms
443
+ ✓ tests/seed-discover.test.ts (2 tests) 20ms
444
+ ✓ tests/rest-cache-admin.test.ts (1 test) 44ms
445
+ ✓ tests/init-package-json.test.ts (1 test) 3ms
442
446
  ✓ tests/resolve-api-url.test.ts (2 tests) 4ms
443
- ✓ tests/init-package-json.test.ts (1 test) 4ms
444
- ✓ tests/migrate-from-v1.test.ts (1 test) 11ms
445
- ✓ tests/pg-spawn-env.test.ts (2 tests) 5ms
447
+ ✓ tests/pg-spawn-env.test.ts (2 tests) 4ms
448
+ ✓ tests/migrate-from-v1.test.ts (1 test) 17ms
446
449
  ✓ tests/cli-package-version.test.ts (1 test) 4ms
447
- ✓ tests/postgres-archive-tag.test.ts (1 test) 3ms
448
- ✓ tests/dev-ready-panel.test.ts (1 test) 2ms
450
+ ✓ tests/postgres-archive-tag.test.ts (1 test) 2ms
451
+ ✓ tests/stdin-after-ink.test.ts (1 test) 3ms
449
452
 
450
-  Test Files  46 passed (46)
451
-  Tests  348 passed (348)
452
-  Start at  11:55:52
453
-  Duration  28.26s (transform 4.10s, setup 0ms, import 21.64s, tests 54.11s, environment 9ms)
453
+  Test Files  48 passed (48)
454
+  Tests  362 passed (362)
455
+  Start at  23:33:06
456
+  Duration  27.66s (transform 4.07s, setup 0ms, import 21.16s, tests 53.31s, environment 9ms)
454
457
 
@@ -1,4 +1,4 @@
1
1
 
2
- > @supatype/cli@0.1.2 typecheck /home/runner/work/supatype/supatype/packages/cli
2
+ > @supatype/cli@0.1.3 typecheck /home/runner/work/supatype/supatype/packages/cli
3
3
  > tsc --project tsconfig.json --noEmit && tsc --project tests/tsconfig.json --noEmit
4
4
 
@@ -1,7 +1,16 @@
1
1
  import type { Command } from "commander";
2
+ import type { QueryResult } from "pg";
2
3
  import { type SupatypeProjectConfig } from "../project-config.js";
3
4
  export declare const ADMIN_EMAIL_ENV = "SUPATYPE_ADMIN_EMAIL";
4
5
  export declare const ADMIN_PASSWORD_ENV = "SUPATYPE_ADMIN_PASSWORD";
6
+ /** GoTrue scopes users to the nil instance id in single-tenant/self-host mode. */
7
+ export declare const GOTRUE_NIL_INSTANCE_ID = "00000000-0000-0000-0000-000000000000";
8
+ /** Audience claim used by GoTrue when looking up users (matches GOTRUE_JWT_AUD in compose). */
9
+ export declare function gotrueJwtAud(cwd: string): string;
10
+ type DbQuery = (sql: string, params?: unknown[]) => Promise<QueryResult>;
11
+ type AuthConfirmedColumn = "email_confirmed_at" | "confirmed_at";
12
+ /** Prefer GoTrue's column when migrated; fall back to postgres init `confirmed_at`. */
13
+ export declare function resolveAuthConfirmedAtColumn(query: DbQuery): Promise<AuthConfirmedColumn>;
5
14
  export interface EnsureFirstAdminOptions {
6
15
  email?: string;
7
16
  password?: string;
@@ -28,4 +37,7 @@ export declare function ensureFirstAdminUser(connection: string, options?: Ensur
28
37
  export declare function ensureFirstAdminUserForProject(cwd: string, config: SupatypeProjectConfig, options?: EnsureFirstAdminOptions): Promise<void>;
29
38
  export declare function clearAdminSeedPassword(cwd: string): void;
30
39
  export declare function hashPasswordForAuth(password: string): Promise<string>;
40
+ /** Postgres password for compose `exec psql` (db is not published to the host). */
41
+ export declare function composePostgresPassword(cwd: string): string;
42
+ export {};
31
43
  //# sourceMappingURL=admin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/commands/admin.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAOxC,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAA;AAW7B,eAAO,MAAM,eAAe,yBAAyB,CAAA;AACrD,eAAO,MAAM,kBAAkB,4BAA4B,CAAA;AAI3D,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;CACnD;AAID,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkKpD;AAED,2CAA2C;AAC3C,eAAO,MAAM,oBAAoB,6BAAuB,CAAA;AAExD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;GAGG;AACH,wBAAsB,8BAA8B,CAClD,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CA0Bf;AAwFD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAExD;AAED,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE3E"}
1
+ {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/commands/admin.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKxC,OAAO,KAAK,EAAQ,WAAW,EAAE,MAAM,IAAI,CAAA;AAE3C,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAA;AAa7B,eAAO,MAAM,eAAe,yBAAyB,CAAA;AACrD,eAAO,MAAM,kBAAkB,4BAA4B,CAAA;AAI3D,kFAAkF;AAClF,eAAO,MAAM,sBAAsB,yCAAyC,CAAA;AAE5E,+FAA+F;AAC/F,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,KAAK,OAAO,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,WAAW,CAAC,CAAA;AACxE,KAAK,mBAAmB,GAAG,oBAAoB,GAAG,cAAc,CAAA;AAEhE,uFAAuF;AACvF,wBAAsB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAc/F;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;CACnD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+KpD;AAED,2CAA2C;AAC3C,eAAO,MAAM,oBAAoB,6BAAuB,CAAA;AAExD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;GAGG;AACH,wBAAsB,8BAA8B,CAClD,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CA2Bf;AA2FD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAExD;AAED,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE3E;AAsHD,mFAAmF;AACnF,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3D"}
@@ -10,6 +10,8 @@ import { loadConfig } from "../config.js";
10
10
  import { connectionString, resolveRuntimeProvider, } from "../project-config.js";
11
11
  import { readEnvValue, upsertEnvFile } from "../env-file.js";
12
12
  import { hasEngineOverride } from "../binary-cache.js";
13
+ import { readDevSessionLock } from "../dev-session-lock.js";
14
+ import { composeProjectName } from "../self-host-compose.js";
13
15
  import { confirm as uiConfirm } from "../ui/confirm.js";
14
16
  import { error, info, plain } from "../ui/messages.js";
15
17
  import { promptPassword, promptText } from "../ui/prompts.js";
@@ -18,6 +20,27 @@ import { getActiveDevSession, } from "../dev-session.js";
18
20
  export const ADMIN_EMAIL_ENV = "SUPATYPE_ADMIN_EMAIL";
19
21
  export const ADMIN_PASSWORD_ENV = "SUPATYPE_ADMIN_PASSWORD";
20
22
  const BCRYPT_ROUNDS = 10;
23
+ /** GoTrue scopes users to the nil instance id in single-tenant/self-host mode. */
24
+ export const GOTRUE_NIL_INSTANCE_ID = "00000000-0000-0000-0000-000000000000";
25
+ /** Audience claim used by GoTrue when looking up users (matches GOTRUE_JWT_AUD in compose). */
26
+ export function gotrueJwtAud(cwd) {
27
+ return readEnvValue(cwd, "GOTRUE_JWT_AUD", "authenticated");
28
+ }
29
+ /** Prefer GoTrue's column when migrated; fall back to postgres init `confirmed_at`. */
30
+ export async function resolveAuthConfirmedAtColumn(query) {
31
+ const result = await query(`SELECT column_name
32
+ FROM information_schema.columns
33
+ WHERE table_schema = 'auth'
34
+ AND table_name = 'users'
35
+ AND column_name IN ('email_confirmed_at', 'confirmed_at')
36
+ ORDER BY CASE column_name WHEN 'email_confirmed_at' THEN 0 ELSE 1 END
37
+ LIMIT 1`);
38
+ const row = result.rows[0];
39
+ const name = row?.column_name ?? row?.value;
40
+ if (name === "email_confirmed_at" || name === "confirmed_at")
41
+ return name;
42
+ return "confirmed_at";
43
+ }
21
44
  export function registerAdmin(program) {
22
45
  const adminCmd = program
23
46
  .command("admin")
@@ -45,11 +68,22 @@ export function registerAdmin(program) {
45
68
  }
46
69
  const role = opts.role;
47
70
  info(`Creating admin user: ${email} (role: ${role})...`);
48
- const pg = await importPg();
49
- const pool = new pg.Pool({ connectionString: connection, max: 2 });
71
+ const compose = opts.connection === undefined ? resolveDockerComposeContext(cwd, config) : null;
50
72
  try {
51
- await ensureAuthUsersTable(pool);
52
- await createAdminUser(pool, email, password, role);
73
+ if (compose) {
74
+ await createAdminUser((sql, params) => composeExecQuery(cwd, compose, sql, params), email, password, role, { cwd });
75
+ }
76
+ else {
77
+ const pg = await importPg();
78
+ const pool = new pg.Pool({ connectionString: connection, max: 2 });
79
+ try {
80
+ await ensureAuthUsersTable(pool);
81
+ await createAdminUser(pool, email, password, role, { cwd });
82
+ }
83
+ finally {
84
+ await pool.end();
85
+ }
86
+ }
53
87
  info("This user can now log in to the admin panel at /admin");
54
88
  }
55
89
  catch (err) {
@@ -57,9 +91,6 @@ export function registerAdmin(program) {
57
91
  error(`Failed to create admin user: ${message}`);
58
92
  process.exit(1);
59
93
  }
60
- finally {
61
- await pool.end();
62
- }
63
94
  });
64
95
  adminCmd
65
96
  .command("set-role")
@@ -170,8 +201,9 @@ export async function ensureFirstAdminUserForProject(cwd, config, options = {})
170
201
  try {
171
202
  await ensureFirstAdminWithQuery((sql, params) => composeExecQuery(root, merged.compose, sql, params), merged);
172
203
  }
173
- catch {
174
- // Non-fatal
204
+ catch (err) {
205
+ const message = err instanceof Error ? err.message : String(err);
206
+ error(`Could not ensure first admin user: ${message}`);
175
207
  }
176
208
  return;
177
209
  }
@@ -196,7 +228,10 @@ async function ensureFirstAdminWithQuery(query, options) {
196
228
  }
197
229
  const role = options.role ?? "admin";
198
230
  try {
199
- await createAdminUser(query, credentials.email, credentials.password, role, { quiet: true });
231
+ await createAdminUser(query, credentials.email, credentials.password, role, {
232
+ quiet: true,
233
+ cwd,
234
+ });
200
235
  }
201
236
  catch (err) {
202
237
  const message = err instanceof Error ? err.message : String(err);
@@ -260,9 +295,12 @@ async function createAdminUser(db, email, password, role, opts = {}) {
260
295
  ? (sql, params) => db.query(sql, params)
261
296
  : db;
262
297
  const normalized = email.toLowerCase();
263
- const existing = await query(`SELECT id FROM auth.users WHERE email = $1`, [
264
- normalized,
265
- ]);
298
+ const aud = opts.aud ?? (opts.cwd ? gotrueJwtAud(opts.cwd) : "authenticated");
299
+ const existing = await query(`SELECT id FROM auth.users
300
+ WHERE instance_id = $1::uuid
301
+ AND LOWER(email) = $2
302
+ AND aud = $3
303
+ AND is_sso_user = false`, [GOTRUE_NIL_INSTANCE_ID, normalized, aud]);
266
304
  if (existing.rows.length > 0) {
267
305
  throw new Error(`User with email "${email}" already exists.`);
268
306
  }
@@ -273,15 +311,24 @@ async function createAdminUser(db, email, password, role, opts = {}) {
273
311
  providers: ["email"],
274
312
  });
275
313
  const userMetadata = JSON.stringify({});
314
+ const confirmedCol = await resolveAuthConfirmedAtColumn(query);
276
315
  const result = await query(`INSERT INTO auth.users (
277
- id, email, encrypted_password, role, aud,
316
+ instance_id, id, aud, role, email, encrypted_password,
278
317
  raw_app_meta_data, raw_user_meta_data,
279
- email_confirmed_at, created_at, updated_at
318
+ ${confirmedCol},
319
+ confirmation_token, recovery_token,
320
+ email_change_token_new, email_change, email_change_token_current,
321
+ phone_change, phone_change_token, reauthentication_token,
322
+ is_sso_user, is_anonymous,
323
+ created_at, updated_at
280
324
  ) VALUES (
281
- gen_random_uuid(), $1, $2, 'authenticated', 'authenticated',
282
- $3::jsonb, $4::jsonb,
283
- now(), now(), now()
284
- ) RETURNING id, email`, [normalized, passwordHash, appMetadata, userMetadata]);
325
+ $1::uuid, gen_random_uuid(), $2, 'authenticated', $3, $4,
326
+ $5::jsonb, $6::jsonb,
327
+ now(),
328
+ '', '', '', '', '', '', '', '',
329
+ false, false,
330
+ now(), now()
331
+ ) RETURNING id, email`, [GOTRUE_NIL_INSTANCE_ID, aud, normalized, passwordHash, appMetadata, userMetadata]);
285
332
  const user = result.rows[0];
286
333
  if (!opts.quiet) {
287
334
  info("Admin user created successfully.");
@@ -297,7 +344,7 @@ async function ensureAuthUsersTable(pool) {
297
344
 
298
345
  CREATE TABLE IF NOT EXISTS auth.users (
299
346
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
300
- instance_id UUID,
347
+ instance_id UUID DEFAULT '00000000-0000-0000-0000-000000000000'::uuid,
301
348
  aud TEXT DEFAULT 'authenticated',
302
349
  role TEXT DEFAULT 'authenticated',
303
350
  email TEXT UNIQUE,
@@ -310,7 +357,13 @@ async function ensureAuthUsersTable(pool) {
310
357
  confirmation_token TEXT DEFAULT '',
311
358
  recovery_token TEXT DEFAULT '',
312
359
  email_change_token_new TEXT DEFAULT '',
313
- email_change TEXT DEFAULT ''
360
+ email_change_token_current TEXT DEFAULT '',
361
+ email_change TEXT DEFAULT '',
362
+ phone_change TEXT DEFAULT '',
363
+ phone_change_token TEXT DEFAULT '',
364
+ reauthentication_token TEXT DEFAULT '',
365
+ is_sso_user BOOLEAN NOT NULL DEFAULT false,
366
+ is_anonymous BOOLEAN NOT NULL DEFAULT false
314
367
  );
315
368
  `);
316
369
  }
@@ -328,9 +381,30 @@ async function hasAdminUsers(query) {
328
381
  const count = adminCount.rows[0]?.count ?? 0;
329
382
  return count > 0;
330
383
  }
384
+ /** Postgres password for compose `exec psql` (db is not published to the host). */
385
+ export function composePostgresPassword(cwd) {
386
+ return readEnvValue(cwd, "POSTGRES_PASSWORD", "postgres");
387
+ }
388
+ function resolveDockerComposeContext(cwd, config) {
389
+ if (resolveRuntimeProvider(config) !== "docker" || hasEngineOverride(config)) {
390
+ return null;
391
+ }
392
+ const session = readDevSessionLock(cwd);
393
+ if (session?.composeProject && session.composePath && existsSync(session.composePath)) {
394
+ return { project: session.composeProject, composePath: session.composePath };
395
+ }
396
+ const composePath = resolve(cwd, ".supatype/self-host/docker-compose.yml");
397
+ if (!existsSync(composePath))
398
+ return null;
399
+ return {
400
+ project: composeProjectName(config.project?.name ?? "project"),
401
+ composePath,
402
+ };
403
+ }
331
404
  function composeExecQuery(cwd, compose, sql, params = []) {
332
405
  const db = readEnvValue(cwd, "POSTGRES_DB", "supatype");
333
406
  const user = readEnvValue(cwd, "POSTGRES_USER", "supatype_admin");
407
+ const pgPassword = composePostgresPassword(cwd);
334
408
  const envFile = join(cwd, ".env");
335
409
  const composeDir = dirname(compose.composePath);
336
410
  const args = [
@@ -345,7 +419,7 @@ function composeExecQuery(cwd, compose, sql, params = []) {
345
419
  if (existsSync(envFile))
346
420
  args.push("--env-file", envFile);
347
421
  if (params.length === 0) {
348
- args.push("exec", "-T", "db", "psql", "-U", user, "-d", db, "-tAc", sql);
422
+ args.push("exec", "-T", "-e", `PGPASSWORD=${pgPassword}`, "db", "psql", "-U", user, "-d", db, "-tAc", sql);
349
423
  const result = spawnSync("docker", args, { cwd: composeDir, encoding: "utf8" });
350
424
  if (result.status !== 0) {
351
425
  throw new Error((result.stderr ?? result.stdout ?? "compose psql failed").trim());
@@ -368,7 +442,7 @@ function composeExecQuery(cwd, compose, sql, params = []) {
368
442
  fields: [],
369
443
  });
370
444
  }
371
- args.push("exec", "-T", "db", "psql", "-U", user, "-d", db, "-v", "ON_ERROR_STOP=1", "-c", interpolateSql(sql, params));
445
+ args.push("exec", "-T", "-e", `PGPASSWORD=${pgPassword}`, "db", "psql", "-U", user, "-d", db, "-v", "ON_ERROR_STOP=1", "-c", interpolateSql(sql, params));
372
446
  const result = spawnSync("docker", args, { cwd: composeDir, encoding: "utf8" });
373
447
  if (result.status !== 0) {
374
448
  throw new Error((result.stderr ?? result.stdout ?? "compose psql failed").trim());