@@ -8,46 +8,52 @@ import { BusEvent } from "@/bus/bus-event"
88import { GlobalBus } from "@/bus/global"
99import { which } from "../util/which"
1010import { ProjectID } from "./schema"
11- import { Effect , Layer , Path , Scope , Context , Stream } from "effect"
11+ import { Effect , Layer , Path , Scope , Context , Stream , Types , Schema } from "effect"
1212import { ChildProcess , ChildProcessSpawner } from "effect/unstable/process"
1313import { NodePath } from "@effect/platform-node"
1414import { AppFileSystem } from "@opencode-ai/shared/filesystem"
1515import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner"
16+ import { zod } from "@/util/effect-zod"
17+ import { withStatics } from "@/util/schema"
1618
1719const log = Log . create ( { service : "project" } )
1820
19- export const Info = z
20- . object ( {
21- id : ProjectID . zod ,
22- worktree : z . string ( ) ,
23- vcs : z . literal ( "git" ) . optional ( ) ,
24- name : z . string ( ) . optional ( ) ,
25- icon : z
26- . object ( {
27- url : z . string ( ) . optional ( ) ,
28- override : z . string ( ) . optional ( ) ,
29- color : z . string ( ) . optional ( ) ,
30- } )
31- . optional ( ) ,
32- commands : z
33- . object ( {
34- start : z . string ( ) . optional ( ) . describe ( "Startup script to run when creating a new workspace (worktree)" ) ,
35- } )
36- . optional ( ) ,
37- time : z . object ( {
38- created : z . number ( ) ,
39- updated : z . number ( ) ,
40- initialized : z . number ( ) . optional ( ) ,
41- } ) ,
42- sandboxes : z . array ( z . string ( ) ) ,
43- } )
44- . meta ( {
45- ref : "Project" ,
46- } )
47- export type Info = z . infer < typeof Info >
21+ const ProjectVcs = Schema . Literal ( "git" )
22+
23+ const ProjectIcon = Schema . Struct ( {
24+ url : Schema . optional ( Schema . String ) ,
25+ override : Schema . optional ( Schema . String ) ,
26+ color : Schema . optional ( Schema . String ) ,
27+ } )
28+
29+ const ProjectCommands = Schema . Struct ( {
30+ start : Schema . optional (
31+ Schema . String . annotate ( { description : "Startup script to run when creating a new workspace (worktree)" } ) ,
32+ ) ,
33+ } )
34+
35+ const ProjectTime = Schema . Struct ( {
36+ created : Schema . Number ,
37+ updated : Schema . Number ,
38+ initialized : Schema . optional ( Schema . Number ) ,
39+ } )
40+
41+ export const Info = Schema . Struct ( {
42+ id : ProjectID ,
43+ worktree : Schema . String ,
44+ vcs : Schema . optional ( ProjectVcs ) ,
45+ name : Schema . optional ( Schema . String ) ,
46+ icon : Schema . optional ( ProjectIcon ) ,
47+ commands : Schema . optional ( ProjectCommands ) ,
48+ time : ProjectTime ,
49+ sandboxes : Schema . Array ( Schema . String ) ,
50+ } )
51+ . annotate ( { identifier : "Project" } )
52+ . pipe ( withStatics ( ( s ) => ( { zod : zod ( s ) } ) ) )
53+ export type Info = Types . DeepMutable < Schema . Schema . Type < typeof Info > >
4854
4955export const Event = {
50- Updated : BusEvent . define ( "project.updated" , Info ) ,
56+ Updated : BusEvent . define ( "project.updated" , Info . zod ) ,
5157}
5258
5359type Row = typeof ProjectTable . $inferSelect
@@ -58,7 +64,7 @@ export function fromRow(row: Row): Info {
5864 return {
5965 id : row . id ,
6066 worktree : row . worktree ,
61- vcs : row . vcs ? Info . shape . vcs . parse ( row . vcs ) : undefined ,
67+ vcs : row . vcs ? Schema . decodeUnknownSync ( ProjectVcs ) ( row . vcs ) : undefined ,
6268 name : row . name ?? undefined ,
6369 icon,
6470 time : {
@@ -74,8 +80,8 @@ export function fromRow(row: Row): Info {
7480export const UpdateInput = z . object ( {
7581 projectID : ProjectID . zod ,
7682 name : z . string ( ) . optional ( ) ,
77- icon : Info . shape . icon . optional ( ) ,
78- commands : Info . shape . commands . optional ( ) ,
83+ icon : zod ( ProjectIcon ) . optional ( ) ,
84+ commands : zod ( ProjectCommands ) . optional ( ) ,
7985} )
8086export type UpdateInput = z . infer < typeof UpdateInput >
8187
@@ -139,7 +145,7 @@ export const layer: Layer.Layer<
139145 } ) ,
140146 )
141147
142- const fakeVcs = Info . shape . vcs . parse ( Flag . OPENCODE_FAKE_VCS )
148+ const fakeVcs = Schema . decodeUnknownSync ( Schema . optional ( ProjectVcs ) ) ( Flag . OPENCODE_FAKE_VCS )
143149
144150 const resolveGitPath = ( cwd : string , name : string ) => {
145151 if ( ! name ) return cwd
0 commit comments