@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.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +111 -108
- package/.turbo/turbo-typecheck.log +1 -1
- package/dist/commands/admin.d.ts +12 -0
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +97 -23
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +287 -182
- package/dist/commands/init.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.js +3 -3
- package/dist/dev-compose.js.map +1 -1
- package/dist/dev-log-bus.d.ts +1 -1
- package/dist/dev-log-bus.d.ts.map +1 -1
- package/dist/dev-log-bus.js +5 -3
- package/dist/dev-log-bus.js.map +1 -1
- package/dist/dev-ready-panel.d.ts +2 -0
- package/dist/dev-ready-panel.d.ts.map +1 -1
- package/dist/dev-ready-panel.js +18 -0
- package/dist/dev-ready-panel.js.map +1 -1
- 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/type-extractor.js +44 -0
- package/dist/type-extractor.js.map +1 -1
- package/dist/ui/dev/DevDashboard.d.ts.map +1 -1
- package/dist/ui/dev/DevDashboard.js +11 -4
- package/dist/ui/dev/DevDashboard.js.map +1 -1
- package/dist/ui/dev/DevReadyPanelView.d.ts +2 -1
- package/dist/ui/dev/DevReadyPanelView.d.ts.map +1 -1
- package/dist/ui/dev/DevReadyPanelView.js +21 -2
- package/dist/ui/dev/DevReadyPanelView.js.map +1 -1
- package/dist/ui/flows/FlowApp.d.ts.map +1 -1
- package/dist/ui/flows/FlowApp.js +3 -1
- package/dist/ui/flows/FlowApp.js.map +1 -1
- package/dist/ui/flows/prompt-fields.d.ts.map +1 -1
- package/dist/ui/flows/prompt-fields.js +9 -5
- package/dist/ui/flows/prompt-fields.js.map +1 -1
- package/dist/ui/runtime/run-clack-flow.d.ts.map +1 -1
- package/dist/ui/runtime/run-clack-flow.js +15 -3
- package/dist/ui/runtime/run-clack-flow.js.map +1 -1
- 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/package.json +1 -1
- package/src/commands/admin.ts +135 -26
- package/src/commands/init.ts +360 -213
- package/src/config.ts +13 -1
- package/src/dev-compose.ts +4 -4
- package/src/dev-log-bus.ts +5 -3
- package/src/dev-ready-panel.ts +17 -0
- package/src/init-project-detect.ts +99 -0
- package/src/type-extractor.ts +57 -0
- package/src/ui/dev/DevDashboard.tsx +14 -4
- package/src/ui/dev/DevReadyPanelView.tsx +76 -9
- package/src/ui/flows/FlowApp.tsx +3 -1
- package/src/ui/flows/prompt-fields.tsx +13 -7
- package/src/ui/runtime/run-clack-flow.tsx +16 -3
- package/src/ui/runtime/stdin-after-ink.ts +18 -0
- package/tests/admin-ensure.test.ts +80 -0
- package/tests/config.test.ts +19 -0
- package/tests/dev-ready-panel.test.ts +18 -1
- package/tests/init-project-detect.test.ts +53 -0
- package/tests/stdin-after-ink.test.ts +8 -0
- package/tests/type-extractor.test.ts +101 -0
- 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)
|