@@ -10,6 +10,9 @@ import { BusEvent } from "@/bus/bus-event"
1010import { GlobalBus } from "@/bus/global"
1111import { which } from "../util/which"
1212import { ProjectID } from "./schema"
13+ import { Bus } from "@/bus"
14+ import { Command } from "@/command"
15+ import { InstanceState } from "@/effect/instance-state"
1316import { Effect , Layer , Path , Scope , Context , Stream , Types , Schema } from "effect"
1417import { ChildProcess , ChildProcessSpawner } from "effect/unstable/process"
1518import { NodePath } from "@effect/platform-node"
@@ -108,6 +111,12 @@ export type UpdatePayload = Types.DeepMutable<Schema.Schema.Type<typeof UpdatePa
108111// ---------------------------------------------------------------------------
109112
110113export interface Interface {
114+ /**
115+ * Per-instance setup. Subscribes to the `/init` slash command for the
116+ * current instance and stamps the project's initialized timestamp when it
117+ * fires. Subscription lifetime is tied to the per-instance state scope.
118+ */
119+ readonly init : ( ) => Effect . Effect < void >
111120 readonly fromDirectory : ( directory : string ) => Effect . Effect < { project : Info ; sandbox : string } >
112121 readonly discover : ( input : Info ) => Effect . Effect < void >
113122 readonly list : ( ) => Effect . Effect < Info [ ] >
@@ -127,13 +136,14 @@ type GitResult = { code: number; text: string; stderr: string }
127136export const layer : Layer . Layer <
128137 Service ,
129138 never ,
130- AppFileSystem . Service | Path . Path | ChildProcessSpawner . ChildProcessSpawner
139+ AppFileSystem . Service | Path . Path | ChildProcessSpawner . ChildProcessSpawner | Bus . Service
131140> = Layer . effect (
132141 Service ,
133142 Effect . gen ( function * ( ) {
134143 const fs = yield * AppFileSystem . Service
135144 const pathSvc = yield * Path . Path
136145 const spawner = yield * ChildProcessSpawner . ChildProcessSpawner
146+ const bus = yield * Bus . Service
137147
138148 const git = Effect . fnUntraced (
139149 function * ( args : string [ ] , opts ?: { cwd ?: string } ) {
@@ -417,6 +427,21 @@ export const layer: Layer.Layer<
417427 )
418428 } )
419429
430+ const initState = yield * InstanceState . make (
431+ Effect . fn ( "Project.initState" ) ( function * ( ctx ) {
432+ yield * bus . subscribe ( Command . Event . Executed ) . pipe (
433+ Stream . runForEach ( ( payload ) =>
434+ payload . properties . name === Command . Default . INIT ? setInitialized ( ctx . project . id ) : Effect . void ,
435+ ) ,
436+ Effect . forkScoped ,
437+ )
438+ } ) ,
439+ )
440+
441+ const init = Effect . fn ( "Project.init" ) ( function * ( ) {
442+ yield * InstanceState . get ( initState )
443+ } )
444+
420445 const sandboxes = Effect . fn ( "Project.sandboxes" ) ( function * ( id : ProjectID ) {
421446 const row = yield * db ( ( d ) => d . select ( ) . from ( ProjectTable ) . where ( eq ( ProjectTable . id , id ) ) . get ( ) )
422447 if ( ! row ) return [ ]
@@ -466,6 +491,7 @@ export const layer: Layer.Layer<
466491 } )
467492
468493 return Service . of ( {
494+ init,
469495 fromDirectory,
470496 discover,
471497 list,
@@ -481,6 +507,7 @@ export const layer: Layer.Layer<
481507)
482508
483509export const defaultLayer = layer . pipe (
510+ Layer . provide ( Bus . defaultLayer ) ,
484511 Layer . provide ( CrossSpawnSpawner . defaultLayer ) ,
485512 Layer . provide ( AppFileSystem . defaultLayer ) ,
486513 Layer . provide ( NodePath . layer ) ,
0 commit comments