@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,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest"
2
- import { devReadyPanelRowCount } from "../src/dev-ready-panel.js"
2
+ import { devReadyPanelCompactRowCount, devReadyPanelRowCount } from "../src/dev-ready-panel.js"
3
3
 
4
4
  describe("devReadyPanelRowCount", () => {
5
5
  it("accounts for border, keys block, and hints", () => {
@@ -18,3 +18,20 @@ describe("devReadyPanelRowCount", () => {
18
18
  expect(rows).toBe(10)
19
19
  })
20
20
  })
21
+
22
+ describe("devReadyPanelCompactRowCount", () => {
23
+ it("uses a shorter layout than the full panel", () => {
24
+ const panel = {
25
+ title: "Services running",
26
+ links: [
27
+ { label: "API", url: "http://localhost:1" },
28
+ { label: "REST", url: "http://localhost:1/rest/v1/" },
29
+ { label: "Studio", url: "http://localhost:1/studio/" },
30
+ ],
31
+ hints: ["Demo data: pnpm seed"],
32
+ anonKey: "anon-jwt",
33
+ serviceRoleKey: "svc-jwt",
34
+ }
35
+ expect(devReadyPanelCompactRowCount(panel)).toBeLessThan(devReadyPanelRowCount(panel))
36
+ })
37
+ })
@@ -0,0 +1,53 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest"
2
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs"
3
+ import { join } from "node:path"
4
+ import { tmpdir } from "node:os"
5
+ import { detectProjectSetup } from "../src/init-project-detect.js"
6
+
7
+ let dir: string
8
+
9
+ beforeEach(() => {
10
+ dir = join(tmpdir(), `supatype-detect-${Date.now()}`)
11
+ mkdirSync(dir, { recursive: true })
12
+ })
13
+
14
+ afterEach(() => {
15
+ rmSync(dir, { recursive: true, force: true })
16
+ })
17
+
18
+ describe("detectProjectSetup()", () => {
19
+ it("returns empty summary for an empty directory", () => {
20
+ const detected = detectProjectSetup(dir)
21
+ expect(detected.hasExistingFiles).toBe(false)
22
+ expect(detected.hasVite).toBe(false)
23
+ expect(detected.summaryLines).toEqual([])
24
+ })
25
+
26
+ it("detects Vite config and port", () => {
27
+ writeFileSync(
28
+ join(dir, "vite.config.ts"),
29
+ `export default { server: { port: 5174 } }`,
30
+ "utf8",
31
+ )
32
+ const detected = detectProjectSetup(dir)
33
+ expect(detected.hasVite).toBe(true)
34
+ expect(detected.hasViteConfig).toBe(true)
35
+ expect(detected.viteDevUrl).toBe("http://127.0.0.1:5174")
36
+ expect(detected.summaryLines.some((line) => line.includes("Vite config"))).toBe(true)
37
+ })
38
+
39
+ it("detects static dist directory", () => {
40
+ mkdirSync(join(dir, "dist"), { recursive: true })
41
+ writeFileSync(join(dir, "dist", "index.html"), "<html></html>", "utf8")
42
+ const detected = detectProjectSetup(dir)
43
+ expect(detected.staticDir).toBe("./dist")
44
+ expect(detected.summaryLines.some((line) => line.includes("./dist"))).toBe(true)
45
+ })
46
+
47
+ it("detects existing supatype.config.ts", () => {
48
+ writeFileSync(join(dir, "supatype.config.ts"), "export default {}", "utf8")
49
+ const detected = detectProjectSetup(dir)
50
+ expect(detected.hasSupatypeConfig).toBe(true)
51
+ expect(detected.summaryLines).toContain("supatype.config already present")
52
+ })
53
+ })
@@ -0,0 +1,8 @@
1
+ import { describe, it, expect } from "vitest"
2
+ import { restoreStdinAfterInk } from "../src/ui/runtime/stdin-after-ink.js"
3
+
4
+ describe("restoreStdinAfterInk()", () => {
5
+ it("does not throw when stdin is not a TTY", () => {
6
+ expect(() => restoreStdinAfterInk()).not.toThrow()
7
+ })
8
+ })
@@ -581,6 +581,107 @@ export type Post = Model<WithTimestamps<{
581
581
  expect(post?.options.singleton).toBeUndefined()
582
582
  })
583
583
 
584
+ it("extracts models that use intersection `} & Timestamps` with relational fields", () => {
585
+ const dir = mkdtempSync(join(tmpdir(), "supatype-intersection-timestamps-"))
586
+ dirs.push(dir)
587
+ const schemaPath = join(dir, "schema.ts")
588
+ writeFileSync(
589
+ schemaPath,
590
+ `
591
+ import type {
592
+ Model, LoggedIn, Owner, Public, RelatedTo, Unique, Optional, MaxLength, Timestamps, UUID,
593
+ } from "@supatype/types"
594
+
595
+ export type Profile = Model<{
596
+ id: UUID
597
+ display_name: string
598
+ }, {
599
+ access: { read: LoggedIn; create: Owner<"id">; update: Owner<"id">; delete: Owner<"id"> }
600
+ }>
601
+
602
+ export type Room = Model<{
603
+ id: UUID
604
+ name: Unique<string>
605
+ topic: Optional<string>
606
+ created_by: RelatedTo<Profile, { required: true }>
607
+ } & Timestamps, {
608
+ access: { read: Public; create: LoggedIn; update: Owner<"created_by_id">; delete: Owner<"created_by_id"> }
609
+ }>
610
+
611
+ export type Message = Model<{
612
+ id: UUID
613
+ room: RelatedTo<Room, { required: true, onDelete: "cascade" }>
614
+ author: RelatedTo<Profile, { required: true }>
615
+ body: MaxLength<string, 2000>
616
+ } & Timestamps, {
617
+ access: { read: Public; create: LoggedIn; update: Owner<"author_id">; delete: Owner<"author_id"> }
618
+ indexes: [{ fields: ["room_id", "created_at"] }]
619
+ }>
620
+ `,
621
+ "utf8",
622
+ )
623
+
624
+ const ast = extractSchemaAstFromTypes(schemaPath, dir)
625
+ expect(ast).not.toBeNull()
626
+ expect(ast?.models).toHaveLength(3)
627
+
628
+ const room = ast?.models.find((m) => m.name === "Room")
629
+ const message = ast?.models.find((m) => m.name === "Message")
630
+
631
+ expect(room).toBeDefined()
632
+ expect(tableName(room)).toBe("room")
633
+ expect(room?.options.timestamps).toBe(true)
634
+ expect(room?.fields["created_by"]).toMatchObject({
635
+ kind: "relation",
636
+ cardinality: "belongsTo",
637
+ target: "Profile",
638
+ annotations: { db: { foreignKey: "created_by_id" } },
639
+ })
640
+ expect(room?.fields["created_at"]).toMatchObject({ kind: "datetime" })
641
+ expect(room?.fields["updated_at"]).toMatchObject({ kind: "datetime" })
642
+
643
+ expect(message).toBeDefined()
644
+ expect(tableName(message)).toBe("message")
645
+ expect(message?.options.timestamps).toBe(true)
646
+ expect(message?.fields["room"]).toMatchObject({
647
+ kind: "relation",
648
+ cardinality: "belongsTo",
649
+ target: "Room",
650
+ annotations: { db: { foreignKey: "room_id" } },
651
+ })
652
+ expect(message?.fields["author"]).toMatchObject({
653
+ kind: "relation",
654
+ cardinality: "belongsTo",
655
+ target: "Profile",
656
+ })
657
+ })
658
+
659
+ it("extracts models with `} & SoftDelete` intersection", () => {
660
+ const dir = mkdtempSync(join(tmpdir(), "supatype-intersection-softdelete-"))
661
+ dirs.push(dir)
662
+ const schemaPath = join(dir, "schema.ts")
663
+ writeFileSync(
664
+ schemaPath,
665
+ `
666
+ import type { Model, UUID, SoftDelete, Public, LoggedIn } from "@supatype/types"
667
+
668
+ export type Post = Model<{
669
+ id: UUID
670
+ title: string
671
+ } & SoftDelete, {
672
+ access: { read: Public; create: LoggedIn }
673
+ }>
674
+ `,
675
+ "utf8",
676
+ )
677
+
678
+ const ast = extractSchemaAstFromTypes(schemaPath, dir)
679
+ const post = ast?.models.find((m) => m.name === "Post")
680
+ expect(post).toBeDefined()
681
+ expect(post?.fields["deleted_at"]).toMatchObject({ kind: "datetime", required: false })
682
+ expect(post?.options.softDelete).toBe(true)
683
+ })
684
+
584
685
  it("extracts LocaleConfig into schema AST locales", () => {
585
686
  const dir = mkdtempSync(join(tmpdir(), "supatype-types-locale-config-"))
586
687
  dirs.push(dir)