@supatype/cli 0.1.2 → 0.1.4

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 (71) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +111 -108
  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/type-extractor.js +44 -0
  29. package/dist/type-extractor.js.map +1 -1
  30. package/dist/ui/dev/DevDashboard.d.ts.map +1 -1
  31. package/dist/ui/dev/DevDashboard.js +11 -4
  32. package/dist/ui/dev/DevDashboard.js.map +1 -1
  33. package/dist/ui/dev/DevReadyPanelView.d.ts +2 -1
  34. package/dist/ui/dev/DevReadyPanelView.d.ts.map +1 -1
  35. package/dist/ui/dev/DevReadyPanelView.js +21 -2
  36. package/dist/ui/dev/DevReadyPanelView.js.map +1 -1
  37. package/dist/ui/flows/FlowApp.d.ts.map +1 -1
  38. package/dist/ui/flows/FlowApp.js +3 -1
  39. package/dist/ui/flows/FlowApp.js.map +1 -1
  40. package/dist/ui/flows/prompt-fields.d.ts.map +1 -1
  41. package/dist/ui/flows/prompt-fields.js +9 -5
  42. package/dist/ui/flows/prompt-fields.js.map +1 -1
  43. package/dist/ui/runtime/run-clack-flow.d.ts.map +1 -1
  44. package/dist/ui/runtime/run-clack-flow.js +15 -3
  45. package/dist/ui/runtime/run-clack-flow.js.map +1 -1
  46. package/dist/ui/runtime/stdin-after-ink.d.ts +3 -0
  47. package/dist/ui/runtime/stdin-after-ink.d.ts.map +1 -0
  48. package/dist/ui/runtime/stdin-after-ink.js +16 -0
  49. package/dist/ui/runtime/stdin-after-ink.js.map +1 -0
  50. package/package.json +1 -1
  51. package/src/commands/admin.ts +135 -26
  52. package/src/commands/init.ts +360 -213
  53. package/src/config.ts +13 -1
  54. package/src/dev-compose.ts +4 -4
  55. package/src/dev-log-bus.ts +5 -3
  56. package/src/dev-ready-panel.ts +17 -0
  57. package/src/init-project-detect.ts +99 -0
  58. package/src/type-extractor.ts +57 -0
  59. package/src/ui/dev/DevDashboard.tsx +14 -4
  60. package/src/ui/dev/DevReadyPanelView.tsx +76 -9
  61. package/src/ui/flows/FlowApp.tsx +3 -1
  62. package/src/ui/flows/prompt-fields.tsx +13 -7
  63. package/src/ui/runtime/run-clack-flow.tsx +16 -3
  64. package/src/ui/runtime/stdin-after-ink.ts +18 -0
  65. package/tests/admin-ensure.test.ts +80 -0
  66. package/tests/config.test.ts +19 -0
  67. package/tests/dev-ready-panel.test.ts +18 -1
  68. package/tests/init-project-detect.test.ts +53 -0
  69. package/tests/stdin-after-ink.test.ts +8 -0
  70. package/tests/type-extractor.test.ts +101 -0
  71. 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.4 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.4 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 (36 tests) 229ms
9
+ ✓ tests/runtime-contract.test.ts (41 tests) 131ms
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,11 @@
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) 95ms
295
+
296
+ ✓ tests/dev-ui.test.ts (21 tests) 108ms
297
+
298
+ [supatype] Shutting down…
295
299
  stdout | tests/docker-runtime.test.ts > requireDockerDaemon > exits when the daemon is unavailable
296
300
  
297
301
  Start the Docker daemon (e.g. sudo systemctl start docker), then try again.
@@ -300,22 +304,21 @@
300
304
 
301
305
  docker: daemon down
302
306
 
303
- ✓ tests/docker-runtime.test.ts (10 tests) 13ms
304
-
305
- ✓ tests/dev-ui.test.ts (21 tests) 102ms
306
-
307
- [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) 17ms
308
+ ✓ tests/config.test.ts (19 tests) 5201ms
309
+ ✓ loads a valid supatype.config.ts (plain export default, no package imports)  515ms
310
+ ✓ normalizes shorthand schema string to { path, pg_schema }  598ms
311
+ ✓ loads a supatype.config.js (plain ESM)  563ms
312
+ ✓ loads config without a versions section (latest by default)  502ms
313
+ ✓ prefers supatype.config.ts over supatype.config.js when both exist  484ms
314
+ ✓ supports optional output field in config  599ms
315
+ ✓ merges supatype.local.config.ts over base  1065ms
316
+ ✓ loads defineConfig import before @supatype/cli is installed (init scaffold)  863ms
317
+ ✓ tests/command-chrome-coverage.test.ts (1 test) 12ms
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
321
+ [supatype] Migrated .supatype/cloud.json → .supatype/link.json (legacy files kept; remove manually when ready).
319
322
  created supatype.config.ts
320
323
  created schema/index.ts
321
324
  created .env
@@ -324,57 +327,11 @@
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
- ✓ 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
334
- ✓ 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
338
- stderr | tests/binary-cache-cloud-overrides.test.ts > isLinkedToCloudProject > is true when .supatype/cloud.json has projectSlug
339
- [supatype] Migrated .supatype/cloud.json → .supatype/link.json (legacy files kept; remove manually when ready).
340
-
341
- ✓ tests/binary-cache-cloud-overrides.test.ts (11 tests) 22ms
342
- ✓ tests/minisign.test.ts (5 tests) 13ms
343
- stdout | tests/storage-provision.test.ts > provisionBuckets > POSTs each bucket to the storage API
344
- [supatype] Storage bucket "photos" ready.
345
-
346
- stdout | tests/storage-provision.test.ts > provisionBuckets > POSTs each bucket to the storage API
347
- [supatype] Storage bucket "avatars" ready.
348
330
 
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
375
- ✓ 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
331
+ ✓ tests/link.test.ts (4 tests) 2930ms
332
+ ✓ resolveTarget returns cloud mode with /api/v1 prefix  1014ms
333
+ ✓ resolveTarget returns self-host mode with /platform/v1 prefix  964ms
334
+ ✓ targetSchemaRollback posts to /platform/v1 when linked  941ms
378
335
  stdout | tests/admin-ensure.test.ts > init admin seed in .env > writes SUPATYPE_ADMIN_* when scaffold options include credentials
379
336
   created package.json
380
337
  created supatype.config.ts
@@ -385,10 +342,55 @@
385
342
  created public/.gitkeep
386
343
  created .gitignore
387
344
 
388
- ✓ tests/admin-ensure.test.ts (3 tests) 145ms
389
- ✓ tests/ui-messages.test.ts (5 tests) 6ms
345
+ ✓ tests/admin-ensure.test.ts (10 tests) 196ms
346
+ ✓ tests/pull-utils.test.ts (49 tests) 14ms
347
+ ✓ tests/schema-sources.test.ts (5 tests) 1185ms
348
+ ✓ buildSchemaSourcesPayload includes manifest metadata  584ms
349
+ ✓ restore overwrites modified local files  555ms
350
+ stderr | tests/binary-cache-cloud-overrides.test.ts > isLinkedToCloudProject > is true when .supatype/cloud.json has projectSlug
351
+ [supatype] Migrated .supatype/cloud.json → .supatype/link.json (legacy files kept; remove manually when ready).
352
+
353
+ ✓ tests/binary-cache-cloud-overrides.test.ts (11 tests) 21ms
354
+ ✓ tests/minisign.test.ts (5 tests) 15ms
355
+ stdout | tests/storage-provision.test.ts > provisionBuckets > POSTs each bucket to the storage API
356
+ [supatype] Storage bucket "photos" ready.
357
+
358
+ stdout | tests/storage-provision.test.ts > provisionBuckets > POSTs each bucket to the storage API
359
+ [supatype] Storage bucket "avatars" ready.
360
+
361
+ ✓ tests/storage-provision.test.ts (6 tests) 14ms
362
+ ✓ tests/cached-artifact-format.test.ts (7 tests) 10ms
363
+ ✓ tests/proxy-dev-app.test.ts (7 tests) 5ms
364
+ ✓ tests/init-dependency-versions.test.ts (4 tests) 40ms
365
+ ✓ tests/keys.test.ts (11 tests) 17122ms
366
+ ✓ exits 0 with --secret  1522ms
367
+ ✓ outputs ANON_KEY= line  1453ms
368
+ ✓ outputs SERVICE_ROLE_KEY= line  1442ms
369
+ ✓ ANON_KEY is a valid three-part JWT  1502ms
370
+ ✓ SERVICE_ROLE_KEY payload has role: service_role  1494ms
371
+ ✓ JWT has exp in the future  1371ms
372
+ ✓ --exp-years changes the expiry window  2798ms
373
+ ✓ reads JWT_SECRET from environment variable  1349ms
374
+ ✓ reads JWT_SECRET from .env file  1454ms
375
+ ✓ exits non-zero when no secret is available  1351ms
376
+ ✓ keys --help shows --secret and --exp-years options  1380ms
377
+ ✓ tests/augmentation-generator.test.ts (3 tests) 11ms
378
+ ✓ tests/type-resolver.test.ts (2 tests) 18ms
379
+ ✓ tests/engine-push-output.test.ts (7 tests) 5ms
380
+ ✓ tests/command-chrome-wrap.test.ts (3 tests) 8ms
381
+ ✓ tests/tsx-runner.test.ts (7 tests) 2945ms
382
+ ✓ evaluates a simple expression and returns stdout  425ms
383
+ ✓ evaluates TypeScript syntax (type annotations)  342ms
384
+ ✓ can serialize a JSON object to stdout  368ms
385
+ ✓ returns non-zero exit code on syntax error  437ms
386
+ ✓ returns non-zero exit code on runtime error  357ms
387
+ ✓ captures stderr separately from stdout  417ms
388
+ ✓ runs a .ts file and returns its stdout  593ms
389
+ ✓ tests/ensure-binary.test.ts (2 tests) 11ms
390
+ ✓ tests/init-project-detect.test.ts (4 tests) 12ms
391
+ ✓ tests/ui-messages.test.ts (5 tests) 8ms
390
392
  ✓ tests/required-host-components.test.ts (4 tests) 7ms
391
- ✓ tests/normalize-admin-config.test.ts (1 test) 6ms
393
+ ✓ tests/normalize-admin-config.test.ts (1 test) 5ms
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,11 +401,10 @@
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/dev-session-lock.test.ts (2 tests) 6ms
405
+ ✓ tests/ui-confirm.test.ts (3 tests) 8ms
404
406
  ✓ tests/docker-postgres.test.ts (5 tests) 5ms
405
- ✓ tests/binary-download-lock.test.ts (2 tests) 7ms
406
- ✓ tests/dev-ports.test.ts (3 tests) 6ms
407
+ ✓ tests/binary-download-lock.test.ts (2 tests) 8ms
407
408
  stdout | tests/app-command.test.ts > supatype app add --static > creates the static directory when missing
408
409
   created package.json
409
410
  created supatype.config.ts
@@ -414,41 +415,43 @@
414
415
  created public/.gitkeep
415
416
  created .gitignore
416
417
 
417
- ✓ tests/seed-discover.test.ts (2 tests) 20ms
418
- ✓ 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
418
+ ✓ tests/dev-ports.test.ts (3 tests) 7ms
419
+ ✓ tests/cli-help.test.ts (16 tests) 22108ms
420
+ ✓ --help lists all top-level commands  1433ms
421
+ ✓ --version prints package.json version  1339ms
422
+ ✓ self-update --help describes the command  1410ms
423
+ ✓ init --help describes the init command  1366ms
424
+ ✓ push --help describes the push command and --yes flag  1476ms
425
+ ✓ diff --help describes the diff command  1376ms
426
+ ✓ pull --help describes scaffold pull command  1374ms
427
+ ✓ doctor --help describes drift report command  1246ms
428
+ ✓ introspect --help describes database introspection command  1412ms
429
+ ✓ adopt --help describes adoption ceremony command  1344ms
430
+ ✓ reset --help shows --yes flag  1291ms
431
+ ✓ app --help describes the app command with add/remove subcommands  1455ms
432
+ ✓ app add --help shows --static and --port options  1401ms
433
+ ✓ self-host --help shows compose only (legacy commands hidden)  1380ms
434
+ ✓ self-host compose --help shows compose subcommands  1467ms
435
+ ✓ unknown command exits non-zero  1331ms
436
+ ✓ tests/dev-ready-panel.test.ts (2 tests) 4ms
437
+ ✓ tests/studio-admin-roles.test.ts (3 tests) 4ms
438
+ ✓ tests/release-pins.test.ts (2 tests) 4ms
439
+ ✓ tests/seed-discover.test.ts (2 tests) 13ms
440
+ ✓ tests/app-command.test.ts (2 tests) 2875ms
441
+ ✓ sets app.mode=static and static_dir in supatype.config.ts  1454ms
442
+ ✓ creates the static directory when missing  1419ms
437
443
  ✓ 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
444
  ✓ tests/rest-cache-admin.test.ts (1 test) 48ms
442
- ✓ tests/resolve-api-url.test.ts (2 tests) 4ms
443
445
  ✓ tests/init-package-json.test.ts (1 test) 4ms
444
- ✓ tests/migrate-from-v1.test.ts (1 test) 11ms
446
+ ✓ tests/resolve-api-url.test.ts (2 tests) 4ms
445
447
  ✓ tests/pg-spawn-env.test.ts (2 tests) 5ms
446
- ✓ 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
449
-
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)
448
+ ✓ tests/migrate-from-v1.test.ts (1 test) 18ms
449
+ ✓ tests/cli-package-version.test.ts (1 test) 3ms
450
+ ✓ tests/postgres-archive-tag.test.ts (1 test) 2ms
451
+ ✓ tests/stdin-after-ink.test.ts (1 test) 3ms
452
+
453
+  Test Files  48 passed (48)
454
+  Tests  364 passed (364)
455
+  Start at  07:44:14
456
+  Duration  28.86s (transform 3.82s, setup 0ms, import 21.76s, tests 55.51s, 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.4 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());