refactor(flags): route sync workspaces through runtime flags (#27336)
This commit is contained in:
@@ -7,11 +7,11 @@ import type { InstanceContext } from "@/project/instance"
|
|||||||
import { EventSequenceTable, EventTable } from "./event.sql"
|
import { EventSequenceTable, EventTable } from "./event.sql"
|
||||||
import type { WorkspaceID } from "@/control-plane/schema"
|
import type { WorkspaceID } from "@/control-plane/schema"
|
||||||
import { EventID } from "./schema"
|
import { EventID } from "./schema"
|
||||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
|
||||||
import { Context, Effect, Layer, Schema as EffectSchema } from "effect"
|
import { Context, Effect, Layer, Schema as EffectSchema } from "effect"
|
||||||
import type { DeepMutable } from "@opencode-ai/core/schema"
|
import type { DeepMutable } from "@opencode-ai/core/schema"
|
||||||
import { serviceUse } from "@/effect/service-use"
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { InstanceState } from "@/effect/instance-state"
|
import { InstanceState } from "@/effect/instance-state"
|
||||||
|
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||||
|
|
||||||
// Keep `Event["data"]` mutable because projectors mutate the persisted shape
|
// Keep `Event["data"]` mutable because projectors mutate the persisted shape
|
||||||
// when writing to the database. Bus payloads (`Properties`) stay readonly —
|
// when writing to the database. Bus payloads (`Properties`) stay readonly —
|
||||||
@@ -69,6 +69,8 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Sy
|
|||||||
|
|
||||||
export const layer = Layer.effect(Service)(
|
export const layer = Layer.effect(Service)(
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
const flags = yield* RuntimeFlags.Service
|
||||||
|
|
||||||
const replay: Interface["replay"] = Effect.fn("SyncEvent.replay")(function* (event, options) {
|
const replay: Interface["replay"] = Effect.fn("SyncEvent.replay")(function* (event, options) {
|
||||||
const def = registry.get(event.type)
|
const def = registry.get(event.type)
|
||||||
if (!def) {
|
if (!def) {
|
||||||
@@ -104,7 +106,7 @@ export const layer = Layer.effect(Service)(
|
|||||||
workspace: yield* InstanceState.workspaceID,
|
workspace: yield* InstanceState.workspaceID,
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
process(def, event, { publish, context, ownerID: options?.ownerID })
|
process(def, event, { publish, context, ownerID: options?.ownerID, experimentalWorkspaces: flags.experimentalWorkspaces })
|
||||||
})
|
})
|
||||||
|
|
||||||
const replayAll: Interface["replayAll"] = Effect.fn("SyncEvent.replayAll")(function* (events, options) {
|
const replayAll: Interface["replayAll"] = Effect.fn("SyncEvent.replayAll")(function* (events, options) {
|
||||||
@@ -160,7 +162,7 @@ export const layer = Layer.effect(Service)(
|
|||||||
const seq = row?.seq != null ? row.seq + 1 : 0
|
const seq = row?.seq != null ? row.seq + 1 : 0
|
||||||
|
|
||||||
const event = { id, seq, aggregateID: agg, data }
|
const event = { id, seq, aggregateID: agg, data }
|
||||||
process(def, event, { publish, context })
|
process(def, event, { publish, context, experimentalWorkspaces: flags.experimentalWorkspaces })
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
behavior: "immediate",
|
behavior: "immediate",
|
||||||
@@ -197,7 +199,7 @@ export const layer = Layer.effect(Service)(
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const defaultLayer = layer
|
export const defaultLayer = layer.pipe(Layer.provide(RuntimeFlags.defaultLayer))
|
||||||
|
|
||||||
export const use = serviceUse(Service)
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
@@ -279,7 +281,7 @@ export function project<Def extends Definition>(
|
|||||||
function process<Def extends Definition>(
|
function process<Def extends Definition>(
|
||||||
def: Def,
|
def: Def,
|
||||||
event: Event<Def>,
|
event: Event<Def>,
|
||||||
options: { publish: boolean; context?: PublishContext; ownerID?: string },
|
options: { publish: boolean; context?: PublishContext; ownerID?: string; experimentalWorkspaces: boolean },
|
||||||
) {
|
) {
|
||||||
if (projectors == null) {
|
if (projectors == null) {
|
||||||
throw new Error("No projectors available. Call `SyncEvent.init` to install projectors")
|
throw new Error("No projectors available. Call `SyncEvent.init` to install projectors")
|
||||||
@@ -293,7 +295,7 @@ function process<Def extends Definition>(
|
|||||||
Database.transaction((tx) => {
|
Database.transaction((tx) => {
|
||||||
projector(tx, event.data, event)
|
projector(tx, event.data, event)
|
||||||
|
|
||||||
if (Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) {
|
if (options.experimentalWorkspaces) {
|
||||||
tx.insert(EventSequenceTable)
|
tx.insert(EventSequenceTable)
|
||||||
.values({
|
.values({
|
||||||
aggregate_id: event.aggregateID,
|
aggregate_id: event.aggregateID,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, beforeEach, afterEach, afterAll } from "bun:test"
|
import { describe, expect, beforeEach, afterAll } from "bun:test"
|
||||||
import { provideTmpdirInstance } from "../fixture/fixture"
|
import { provideTmpdirInstance } from "../fixture/fixture"
|
||||||
import { Effect, Layer, Schema } from "effect"
|
import { Effect, Layer, Schema } from "effect"
|
||||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||||
@@ -7,21 +7,19 @@ import { SyncEvent } from "../../src/sync"
|
|||||||
import { Database, eq } from "@/storage/db"
|
import { Database, eq } from "@/storage/db"
|
||||||
import { EventSequenceTable, EventTable } from "../../src/sync/event.sql"
|
import { EventSequenceTable, EventTable } from "../../src/sync/event.sql"
|
||||||
import { MessageID } from "../../src/session/schema"
|
import { MessageID } from "../../src/session/schema"
|
||||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
|
||||||
import { initProjectors } from "../../src/server/projectors"
|
import { initProjectors } from "../../src/server/projectors"
|
||||||
import { testEffect } from "../lib/effect"
|
import { testEffect } from "../lib/effect"
|
||||||
|
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||||
|
|
||||||
const original = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
|
const it = testEffect(
|
||||||
const it = testEffect(Layer.mergeAll(SyncEvent.defaultLayer, CrossSpawnSpawner.defaultLayer))
|
Layer.mergeAll(
|
||||||
|
SyncEvent.layer.pipe(Layer.provide(RuntimeFlags.layer({ experimentalWorkspaces: true }))),
|
||||||
|
CrossSpawnSpawner.defaultLayer,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
Database.close()
|
Database.close()
|
||||||
|
|
||||||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = original
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("SyncEvent", () => {
|
describe("SyncEvent", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user