| ... | @@ -179,18 +179,53 @@ export const layer = Layer.effect( | ... | @@ -179,18 +179,53 @@ export const layer = Layer.effect( |
| 179 | }, | 179 | }, |
| 180 | ); | 180 | ); |
| 181 | | 181 | |
| 182 | const loadLocalStatus = Effect.fn("VcsStatusBroadcaster.loadLocalStatus")(function* ( | 182 | const updateCachedStatus = Effect.fn("VcsStatusBroadcaster.updateCachedStatus")(function* ( |
| 183 | cwd: string, | 183 | cwd: string, |
| | 184 | local: VcsStatusLocalResult, |
| | 185 | remote: VcsStatusRemoteResult | null, |
| | 186 | options?: { publish?: boolean }, |
| 184 | ) { | 187 | ) { |
| 185 | const local = yield* workflow.localStatus({ cwd }); | 188 | const nextLocal = { |
| 186 | return yield* updateCachedLocalStatus(cwd, local); | 189 | fingerprint: fingerprintStatusPart(local), |
| | 190 | value: local, |
| | 191 | } satisfies CachedValue<VcsStatusLocalResult>; |
| | 192 | const nextRemote = { |
| | 193 | fingerprint: fingerprintStatusPart(remote), |
| | 194 | value: remote, |
| | 195 | } satisfies CachedValue<VcsStatusRemoteResult | null>; |
| | 196 | const shouldPublish = yield* Ref.modify(cacheRef, (cache) => { |
| | 197 | const previous = cache.get(cwd) ?? { local: null, remote: null }; |
| | 198 | const nextCache = new Map(cache); |
| | 199 | nextCache.set(cwd, { |
| | 200 | local: nextLocal, |
| | 201 | remote: nextRemote, |
| | 202 | }); |
| | 203 | return [ |
| | 204 | previous.local?.fingerprint !== nextLocal.fingerprint || |
| | 205 | previous.remote?.fingerprint !== nextRemote.fingerprint, |
| | 206 | nextCache, |
| | 207 | ] as const; |
| | 208 | }); |
| | 209 | |
| | 210 | if (options?.publish && shouldPublish) { |
| | 211 | yield* PubSub.publish(changesPubSub, { |
| | 212 | cwd, |
| | 213 | event: { |
| | 214 | _tag: "snapshot", |
| | 215 | local, |
| | 216 | remote, |
| | 217 | }, |
| | 218 | }); |
| | 219 | } |
| | 220 | |
| | 221 | return mergeGitStatusParts(local, remote); |
| 187 | }); | 222 | }); |
| 188 | | 223 | |
| 189 | const loadRemoteStatus = Effect.fn("VcsStatusBroadcaster.loadRemoteStatus")(function* ( | 224 | const loadLocalStatus = Effect.fn("VcsStatusBroadcaster.loadLocalStatus")(function* ( |
| 190 | cwd: string, | 225 | cwd: string, |
| 191 | ) { | 226 | ) { |
| 192 | const remote = yield* workflow.remoteStatus({ cwd }); | 227 | const local = yield* workflow.localStatus({ cwd }); |
| 193 | return yield* updateCachedRemoteStatus(cwd, remote); | 228 | return yield* updateCachedLocalStatus(cwd, local); |
| 194 | }); | 229 | }); |
| 195 | | 230 | |
| 196 | const getOrLoadLocalStatus = Effect.fn("VcsStatusBroadcaster.getOrLoadLocalStatus")(function* ( | 231 | const getOrLoadLocalStatus = Effect.fn("VcsStatusBroadcaster.getOrLoadLocalStatus")(function* ( |
| ... | @@ -203,36 +238,39 @@ export const layer = Layer.effect( | ... | @@ -203,36 +238,39 @@ export const layer = Layer.effect( |
| 203 | return yield* loadLocalStatus(cwd); | 238 | return yield* loadLocalStatus(cwd); |
| 204 | }); | 239 | }); |
| 205 | | 240 | |
| 206 | const getOrLoadRemoteStatus = Effect.fn("VcsStatusBroadcaster.getOrLoadRemoteStatus")( | | |
| 207 | function* (cwd: string) { | | |
| 208 | const cached = yield* getCachedStatus(cwd); | | |
| 209 | if (cached?.remote) { | | |
| 210 | return cached.remote.value; | | |
| 211 | } | | |
| 212 | return yield* loadRemoteStatus(cwd); | | |
| 213 | }, | | |
| 214 | ); | | |
| 215 | | | |
| 216 | const withFileSystem = Effect.provideService(FileSystem.FileSystem, fs); | 241 | const withFileSystem = Effect.provideService(FileSystem.FileSystem, fs); |
| 217 | | 242 | |
| 218 | const getStatus: VcsStatusBroadcasterShape["getStatus"] = Effect.fn( | 243 | const getStatus: VcsStatusBroadcasterShape["getStatus"] = Effect.fn( |
| 219 | "VcsStatusBroadcaster.getStatus", | 244 | "VcsStatusBroadcaster.getStatus", |
| 220 | )(function* (input) { | 245 | )(function* (input) { |
| 221 | const cwd = yield* withFileSystem(normalizeCwd(input.cwd)); | 246 | const cwd = yield* withFileSystem(normalizeCwd(input.cwd)); |
| 222 | const [local, remote] = yield* Effect.all([ | 247 | const cached = yield* getCachedStatus(cwd); |
| 223 | getOrLoadLocalStatus(cwd), | 248 | if (cached?.local && cached.remote) { |
| 224 | getOrLoadRemoteStatus(cwd), | 249 | return mergeGitStatusParts(cached.local.value, cached.remote.value); |
| 225 | ]); | 250 | } |
| 226 | return mergeGitStatusParts(local, remote); | 251 | const [local, remote] = yield* Effect.all( |
| | 252 | [ |
| | 253 | cached?.local ? Effect.succeed(cached.local.value) : workflow.localStatus({ cwd }), |
| | 254 | cached?.remote ? Effect.succeed(cached.remote.value) : workflow.remoteStatus({ cwd }), |
| | 255 | ], |
| | 256 | { concurrency: "unbounded" }, |
| | 257 | ); |
| | 258 | return yield* updateCachedStatus(cwd, local, remote); |
| 227 | }); | 259 | }); |
| 228 | | 260 | |
| | 261 | const refreshLocalStatusCore = Effect.fn("VcsStatusBroadcaster.refreshLocalStatusCore")( |
| | 262 | function* (cwd: string) { |
| | 263 | yield* workflow.invalidateLocalStatus(cwd); |
| | 264 | const local = yield* workflow.localStatus({ cwd }); |
| | 265 | return yield* updateCachedLocalStatus(cwd, local, { publish: true }); |
| | 266 | }, |
| | 267 | ); |
| | 268 | |
| 229 | const refreshLocalStatus: VcsStatusBroadcasterShape["refreshLocalStatus"] = Effect.fn( | 269 | const refreshLocalStatus: VcsStatusBroadcasterShape["refreshLocalStatus"] = Effect.fn( |
| 230 | "VcsStatusBroadcaster.refreshLocalStatus", | 270 | "VcsStatusBroadcaster.refreshLocalStatus", |
| 231 | )(function* (rawCwd) { | 271 | )(function* (rawCwd) { |
| 232 | const cwd = yield* withFileSystem(normalizeCwd(rawCwd)); | 272 | const cwd = yield* withFileSystem(normalizeCwd(rawCwd)); |
| 233 | yield* workflow.invalidateLocalStatus(cwd); | 273 | return yield* refreshLocalStatusCore(cwd); |
| 234 | const local = yield* workflow.localStatus({ cwd }); | | |
| 235 | return yield* updateCachedLocalStatus(cwd, local, { publish: true }); | | |
| 236 | }); | 274 | }); |
| 237 | | 275 | |
| 238 | const refreshRemoteStatus = Effect.fn("VcsStatusBroadcaster.refreshRemoteStatus")(function* ( | 276 | const refreshRemoteStatus = Effect.fn("VcsStatusBroadcaster.refreshRemoteStatus")(function* ( |
| ... | @@ -247,11 +285,15 @@ export const layer = Layer.effect( | ... | @@ -247,11 +285,15 @@ export const layer = Layer.effect( |
| 247 | "VcsStatusBroadcaster.refreshStatus", | 285 | "VcsStatusBroadcaster.refreshStatus", |
| 248 | )(function* (rawCwd) { | 286 | )(function* (rawCwd) { |
| 249 | const cwd = yield* withFileSystem(normalizeCwd(rawCwd)); | 287 | const cwd = yield* withFileSystem(normalizeCwd(rawCwd)); |
| 250 | const [local, remote] = yield* Effect.all([ | 288 | yield* Effect.all( |
| 251 | refreshLocalStatus(cwd), | 289 | [workflow.invalidateLocalStatus(cwd), workflow.invalidateRemoteStatus(cwd)], |
| 252 | refreshRemoteStatus(cwd), | 290 | { concurrency: "unbounded", discard: true }, |
| 253 | ]); | 291 | ); |
| 254 | return mergeGitStatusParts(local, remote); | 292 | const [local, remote] = yield* Effect.all( |
| | 293 | [workflow.localStatus({ cwd }), workflow.remoteStatus({ cwd })], |
| | 294 | { concurrency: "unbounded" }, |
| | 295 | ); |
| | 296 | return yield* updateCachedStatus(cwd, local, remote, { publish: true }); |
| 255 | }); | 297 | }); |
| 256 | | 298 | |
| 257 | const makeRemoteRefreshLoop = ( | 299 | const makeRemoteRefreshLoop = ( |