fix(opencode): skip share sync for unshared sessions (#23159)

This commit is contained in:
Kit Langton 2026-04-17 16:15:24 -04:00 committed by GitHub
commit 984f5ed6eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -38,8 +38,9 @@ const ShareSchema = Schema.Struct({
export type Share = typeof ShareSchema.Type export type Share = typeof ShareSchema.Type
type State = { type State = {
queue: Map<string, { data: Map<string, Data> }> queue: Map<SessionID, Map<string, Data>>
scope: Scope.Closeable scope: Scope.Closeable
shared: Map<SessionID, Share | null>
} }
type Data = type Data =
@ -118,17 +119,20 @@ export const layer = Layer.effect(
function sync(sessionID: SessionID, data: Data[]): Effect.Effect<void> { function sync(sessionID: SessionID, data: Data[]): Effect.Effect<void> {
return Effect.gen(function* () { return Effect.gen(function* () {
if (disabled) return if (disabled) return
const share = yield* getCached(sessionID)
if (!share) return
const s = yield* InstanceState.get(state) const s = yield* InstanceState.get(state)
const existing = s.queue.get(sessionID) const existing = s.queue.get(sessionID)
if (existing) { if (existing) {
for (const item of data) { for (const item of data) {
existing.data.set(key(item), item) existing.set(key(item), item)
} }
return return
} }
const next = new Map(data.map((item) => [key(item), item])) const next = new Map(data.map((item) => [key(item), item]))
s.queue.set(sessionID, { data: next }) s.queue.set(sessionID, next)
yield* flush(sessionID).pipe( yield* flush(sessionID).pipe(
Effect.delay(1000), Effect.delay(1000),
Effect.catchCause((cause) => Effect.catchCause((cause) =>
@ -143,13 +147,14 @@ export const layer = Layer.effect(
const state: InstanceState.InstanceState<State> = yield* InstanceState.make<State>( const state: InstanceState.InstanceState<State> = yield* InstanceState.make<State>(
Effect.fn("ShareNext.state")(function* (_ctx) { Effect.fn("ShareNext.state")(function* (_ctx) {
const cache: State = { queue: new Map(), scope: yield* Scope.make() } const cache: State = { queue: new Map(), scope: yield* Scope.make(), shared: new Map() }
yield* Effect.addFinalizer(() => yield* Effect.addFinalizer(() =>
Scope.close(cache.scope, Exit.void).pipe( Scope.close(cache.scope, Exit.void).pipe(
Effect.andThen( Effect.andThen(
Effect.sync(() => { Effect.sync(() => {
cache.queue.clear() cache.queue.clear()
cache.shared.clear()
}), }),
), ),
), ),
@ -227,6 +232,18 @@ export const layer = Layer.effect(
return { id: row.id, secret: row.secret, url: row.url } satisfies Share return { id: row.id, secret: row.secret, url: row.url } satisfies Share
}) })
const getCached = Effect.fnUntraced(function* (sessionID: SessionID) {
const s = yield* InstanceState.get(state)
if (s.shared.has(sessionID)) {
const cached = s.shared.get(sessionID)
return cached === null ? undefined : cached
}
const share = yield* get(sessionID)
s.shared.set(sessionID, share ?? null)
return share
})
const flush = Effect.fn("ShareNext.flush")(function* (sessionID: SessionID) { const flush = Effect.fn("ShareNext.flush")(function* (sessionID: SessionID) {
if (disabled) return if (disabled) return
const s = yield* InstanceState.get(state) const s = yield* InstanceState.get(state)
@ -235,13 +252,13 @@ export const layer = Layer.effect(
s.queue.delete(sessionID) s.queue.delete(sessionID)
const share = yield* get(sessionID) const share = yield* getCached(sessionID)
if (!share) return if (!share) return
const req = yield* request() const req = yield* request()
const res = yield* HttpClientRequest.post(`${req.baseUrl}${req.api.sync(share.id)}`).pipe( const res = yield* HttpClientRequest.post(`${req.baseUrl}${req.api.sync(share.id)}`).pipe(
HttpClientRequest.setHeaders(req.headers), HttpClientRequest.setHeaders(req.headers),
HttpClientRequest.bodyJson({ secret: share.secret, data: Array.from(queued.data.values()) }), HttpClientRequest.bodyJson({ secret: share.secret, data: Array.from(queued.values()) }),
Effect.flatMap((r) => http.execute(r)), Effect.flatMap((r) => http.execute(r)),
) )
@ -307,6 +324,7 @@ export const layer = Layer.effect(
.run(), .run(),
) )
const s = yield* InstanceState.get(state) const s = yield* InstanceState.get(state)
s.shared.set(sessionID, result)
yield* full(sessionID).pipe( yield* full(sessionID).pipe(
Effect.catchCause((cause) => Effect.catchCause((cause) =>
Effect.sync(() => { Effect.sync(() => {
@ -321,8 +339,13 @@ export const layer = Layer.effect(
const remove = Effect.fn("ShareNext.remove")(function* (sessionID: SessionID) { const remove = Effect.fn("ShareNext.remove")(function* (sessionID: SessionID) {
if (disabled) return if (disabled) return
log.info("removing share", { sessionID }) log.info("removing share", { sessionID })
const share = yield* get(sessionID) const s = yield* InstanceState.get(state)
if (!share) return const share = yield* getCached(sessionID)
if (!share) {
s.shared.delete(sessionID)
s.queue.delete(sessionID)
return
}
const req = yield* request() const req = yield* request()
yield* HttpClientRequest.delete(`${req.baseUrl}${req.api.remove(share.id)}`).pipe( yield* HttpClientRequest.delete(`${req.baseUrl}${req.api.remove(share.id)}`).pipe(
@ -332,6 +355,8 @@ export const layer = Layer.effect(
) )
yield* db((db) => db.delete(SessionShareTable).where(eq(SessionShareTable.session_id, sessionID)).run()) yield* db((db) => db.delete(SessionShareTable).where(eq(SessionShareTable.session_id, sessionID)).run())
s.shared.delete(sessionID)
s.queue.delete(sessionID)
}) })
return Service.of({ init, url, request, create, remove }) return Service.of({ init, url, request, create, remove })