瀏覽代碼

refactor: normalize AccountRepo to canonical Effect service pattern (#22991)

Kit Langton 4 月之前
父節點
當前提交
9c87a144e8

+ 2 - 2
packages/opencode/src/account/account.ts

@@ -181,10 +181,10 @@ export interface Interface {
 
 export class Service extends Context.Service<Service, Interface>()("@opencode/Account") {}
 
-export const layer: Layer.Layer<Service, never, AccountRepo | HttpClient.HttpClient> = Layer.effect(
+export const layer: Layer.Layer<Service, never, AccountRepo.Service | HttpClient.HttpClient> = Layer.effect(
   Service,
   Effect.gen(function* () {
-    const repo = yield* AccountRepo
+    const repo = yield* AccountRepo.Service
     const http = yield* HttpClient.HttpClient
     const httpRead = withTransientReadRetry(http)
     const httpOk = HttpClient.filterStatusOk(http)

+ 140 - 140
packages/opencode/src/account/repo.ts

@@ -13,154 +13,154 @@ type DbTransactionCallback<A> = Parameters<typeof Database.transaction<A>>[0]
 
 const ACCOUNT_STATE_ID = 1
 
-export namespace AccountRepo {
-  export interface Service {
-    readonly active: () => Effect.Effect<Option.Option<Info>, AccountRepoError>
-    readonly list: () => Effect.Effect<Info[], AccountRepoError>
-    readonly remove: (accountID: AccountID) => Effect.Effect<void, AccountRepoError>
-    readonly use: (accountID: AccountID, orgID: Option.Option<OrgID>) => Effect.Effect<void, AccountRepoError>
-    readonly getRow: (accountID: AccountID) => Effect.Effect<Option.Option<AccountRow>, AccountRepoError>
-    readonly persistToken: (input: {
-      accountID: AccountID
-      accessToken: AccessToken
-      refreshToken: RefreshToken
-      expiry: Option.Option<number>
-    }) => Effect.Effect<void, AccountRepoError>
-    readonly persistAccount: (input: {
-      id: AccountID
-      email: string
-      url: string
-      accessToken: AccessToken
-      refreshToken: RefreshToken
-      expiry: number
-      orgID: Option.Option<OrgID>
-    }) => Effect.Effect<void, AccountRepoError>
-  }
+export interface Interface {
+  readonly active: () => Effect.Effect<Option.Option<Info>, AccountRepoError>
+  readonly list: () => Effect.Effect<Info[], AccountRepoError>
+  readonly remove: (accountID: AccountID) => Effect.Effect<void, AccountRepoError>
+  readonly use: (accountID: AccountID, orgID: Option.Option<OrgID>) => Effect.Effect<void, AccountRepoError>
+  readonly getRow: (accountID: AccountID) => Effect.Effect<Option.Option<AccountRow>, AccountRepoError>
+  readonly persistToken: (input: {
+    accountID: AccountID
+    accessToken: AccessToken
+    refreshToken: RefreshToken
+    expiry: Option.Option<number>
+  }) => Effect.Effect<void, AccountRepoError>
+  readonly persistAccount: (input: {
+    id: AccountID
+    email: string
+    url: string
+    accessToken: AccessToken
+    refreshToken: RefreshToken
+    expiry: number
+    orgID: Option.Option<OrgID>
+  }) => Effect.Effect<void, AccountRepoError>
 }
 
-export class AccountRepo extends Context.Service<AccountRepo, AccountRepo.Service>()("@opencode/AccountRepo") {
-  static readonly layer: Layer.Layer<AccountRepo> = Layer.effect(
-    AccountRepo,
-    Effect.gen(function* () {
-      const decode = Schema.decodeUnknownSync(Info)
+export class Service extends Context.Service<Service, Interface>()("@opencode/AccountRepo") {}
 
-      const query = <A>(f: DbTransactionCallback<A>) =>
-        Effect.try({
-          try: () => Database.use(f),
-          catch: (cause) => new AccountRepoError({ message: "Database operation failed", cause }),
-        })
+export const layer: Layer.Layer<Service> = Layer.effect(
+  Service,
+  Effect.gen(function* () {
+    const decode = Schema.decodeUnknownSync(Info)
 
-      const tx = <A>(f: DbTransactionCallback<A>) =>
-        Effect.try({
-          try: () => Database.transaction(f),
-          catch: (cause) => new AccountRepoError({ message: "Database operation failed", cause }),
-        })
+    const query = <A>(f: DbTransactionCallback<A>) =>
+      Effect.try({
+        try: () => Database.use(f),
+        catch: (cause) => new AccountRepoError({ message: "Database operation failed", cause }),
+      })
 
-      const current = (db: DbClient) => {
-        const state = db.select().from(AccountStateTable).where(eq(AccountStateTable.id, ACCOUNT_STATE_ID)).get()
-        if (!state?.active_account_id) return
-        const account = db.select().from(AccountTable).where(eq(AccountTable.id, state.active_account_id)).get()
-        if (!account) return
-        return { ...account, active_org_id: state.active_org_id ?? null }
-      }
-
-      const state = (db: DbClient, accountID: AccountID, orgID: Option.Option<OrgID>) => {
-        const id = Option.getOrNull(orgID)
-        return db
-          .insert(AccountStateTable)
-          .values({ id: ACCOUNT_STATE_ID, active_account_id: accountID, active_org_id: id })
-          .onConflictDoUpdate({
-            target: AccountStateTable.id,
-            set: { active_account_id: accountID, active_org_id: id },
-          })
+    const tx = <A>(f: DbTransactionCallback<A>) =>
+      Effect.try({
+        try: () => Database.transaction(f),
+        catch: (cause) => new AccountRepoError({ message: "Database operation failed", cause }),
+      })
+
+    const current = (db: DbClient) => {
+      const state = db.select().from(AccountStateTable).where(eq(AccountStateTable.id, ACCOUNT_STATE_ID)).get()
+      if (!state?.active_account_id) return
+      const account = db.select().from(AccountTable).where(eq(AccountTable.id, state.active_account_id)).get()
+      if (!account) return
+      return { ...account, active_org_id: state.active_org_id ?? null }
+    }
+
+    const state = (db: DbClient, accountID: AccountID, orgID: Option.Option<OrgID>) => {
+      const id = Option.getOrNull(orgID)
+      return db
+        .insert(AccountStateTable)
+        .values({ id: ACCOUNT_STATE_ID, active_account_id: accountID, active_org_id: id })
+        .onConflictDoUpdate({
+          target: AccountStateTable.id,
+          set: { active_account_id: accountID, active_org_id: id },
+        })
+        .run()
+    }
+
+    const active = Effect.fn("AccountRepo.active")(() =>
+      query((db) => current(db)).pipe(Effect.map((row) => (row ? Option.some(decode(row)) : Option.none()))),
+    )
+
+    const list = Effect.fn("AccountRepo.list")(() =>
+      query((db) =>
+        db
+          .select()
+          .from(AccountTable)
+          .all()
+          .map((row: AccountRow) => decode({ ...row, active_org_id: null })),
+      ),
+    )
+
+    const remove = Effect.fn("AccountRepo.remove")((accountID: AccountID) =>
+      tx((db) => {
+        db.update(AccountStateTable)
+          .set({ active_account_id: null, active_org_id: null })
+          .where(eq(AccountStateTable.active_account_id, accountID))
           .run()
-      }
-
-      const active = Effect.fn("AccountRepo.active")(() =>
-        query((db) => current(db)).pipe(Effect.map((row) => (row ? Option.some(decode(row)) : Option.none()))),
-      )
-
-      const list = Effect.fn("AccountRepo.list")(() =>
-        query((db) =>
-          db
-            .select()
-            .from(AccountTable)
-            .all()
-            .map((row: AccountRow) => decode({ ...row, active_org_id: null })),
-        ),
-      )
-
-      const remove = Effect.fn("AccountRepo.remove")((accountID: AccountID) =>
-        tx((db) => {
-          db.update(AccountStateTable)
-            .set({ active_account_id: null, active_org_id: null })
-            .where(eq(AccountStateTable.active_account_id, accountID))
-            .run()
-          db.delete(AccountTable).where(eq(AccountTable.id, accountID)).run()
-        }).pipe(Effect.asVoid),
-      )
-
-      const use = Effect.fn("AccountRepo.use")((accountID: AccountID, orgID: Option.Option<OrgID>) =>
-        query((db) => state(db, accountID, orgID)).pipe(Effect.asVoid),
-      )
-
-      const getRow = Effect.fn("AccountRepo.getRow")((accountID: AccountID) =>
-        query((db) => db.select().from(AccountTable).where(eq(AccountTable.id, accountID)).get()).pipe(
-          Effect.map(Option.fromNullishOr),
-        ),
-      )
-
-      const persistToken = Effect.fn("AccountRepo.persistToken")((input) =>
-        query((db) =>
-          db
-            .update(AccountTable)
-            .set({
-              access_token: input.accessToken,
-              refresh_token: input.refreshToken,
-              token_expiry: Option.getOrNull(input.expiry),
-            })
-            .where(eq(AccountTable.id, input.accountID))
-            .run(),
-        ).pipe(Effect.asVoid),
-      )
-
-      const persistAccount = Effect.fn("AccountRepo.persistAccount")((input) =>
-        tx((db) => {
-          const url = normalizeServerUrl(input.url)
-
-          db.insert(AccountTable)
-            .values({
-              id: input.id,
+        db.delete(AccountTable).where(eq(AccountTable.id, accountID)).run()
+      }).pipe(Effect.asVoid),
+    )
+
+    const use = Effect.fn("AccountRepo.use")((accountID: AccountID, orgID: Option.Option<OrgID>) =>
+      query((db) => state(db, accountID, orgID)).pipe(Effect.asVoid),
+    )
+
+    const getRow = Effect.fn("AccountRepo.getRow")((accountID: AccountID) =>
+      query((db) => db.select().from(AccountTable).where(eq(AccountTable.id, accountID)).get()).pipe(
+        Effect.map(Option.fromNullishOr),
+      ),
+    )
+
+    const persistToken = Effect.fn("AccountRepo.persistToken")((input) =>
+      query((db) =>
+        db
+          .update(AccountTable)
+          .set({
+            access_token: input.accessToken,
+            refresh_token: input.refreshToken,
+            token_expiry: Option.getOrNull(input.expiry),
+          })
+          .where(eq(AccountTable.id, input.accountID))
+          .run(),
+      ).pipe(Effect.asVoid),
+    )
+
+    const persistAccount = Effect.fn("AccountRepo.persistAccount")((input) =>
+      tx((db) => {
+        const url = normalizeServerUrl(input.url)
+
+        db.insert(AccountTable)
+          .values({
+            id: input.id,
+            email: input.email,
+            url,
+            access_token: input.accessToken,
+            refresh_token: input.refreshToken,
+            token_expiry: input.expiry,
+          })
+          .onConflictDoUpdate({
+            target: AccountTable.id,
+            set: {
               email: input.email,
               url,
               access_token: input.accessToken,
               refresh_token: input.refreshToken,
               token_expiry: input.expiry,
-            })
-            .onConflictDoUpdate({
-              target: AccountTable.id,
-              set: {
-                email: input.email,
-                url,
-                access_token: input.accessToken,
-                refresh_token: input.refreshToken,
-                token_expiry: input.expiry,
-              },
-            })
-            .run()
-          void state(db, input.id, input.orgID)
-        }).pipe(Effect.asVoid),
-      )
-
-      return AccountRepo.of({
-        active,
-        list,
-        remove,
-        use,
-        getRow,
-        persistToken,
-        persistAccount,
-      })
-    }),
-  )
-}
+            },
+          })
+          .run()
+        void state(db, input.id, input.orgID)
+      }).pipe(Effect.asVoid),
+    )
+
+    return Service.of({
+      active,
+      list,
+      remove,
+      use,
+      getRow,
+      persistToken,
+      persistAccount,
+    })
+  }),
+)
+
+export * as AccountRepo from "./repo"

+ 39 - 39
packages/opencode/test/account/repo.test.ts

@@ -18,14 +18,14 @@ const it = testEffect(Layer.merge(AccountRepo.layer, truncate))
 
 it.live("list returns empty when no accounts exist", () =>
   Effect.gen(function* () {
-    const accounts = yield* AccountRepo.use((r) => r.list())
+    const accounts = yield* AccountRepo.Service.use((r) => r.list())
     expect(accounts).toEqual([])
   }),
 )
 
 it.live("active returns none when no accounts exist", () =>
   Effect.gen(function* () {
-    const active = yield* AccountRepo.use((r) => r.active())
+    const active = yield* AccountRepo.Service.use((r) => r.active())
     expect(Option.isNone(active)).toBe(true)
   }),
 )
@@ -33,7 +33,7 @@ it.live("active returns none when no accounts exist", () =>
 it.live("persistAccount inserts and getRow retrieves", () =>
   Effect.gen(function* () {
     const id = AccountID.make("user-1")
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id,
         email: "test@example.com",
@@ -45,13 +45,13 @@ it.live("persistAccount inserts and getRow retrieves", () =>
       }),
     )
 
-    const row = yield* AccountRepo.use((r) => r.getRow(id))
+    const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
     expect(Option.isSome(row)).toBe(true)
     const value = Option.getOrThrow(row)
     expect(value.id).toBe(AccountID.make("user-1"))
     expect(value.email).toBe("test@example.com")
 
-    const active = yield* AccountRepo.use((r) => r.active())
+    const active = yield* AccountRepo.Service.use((r) => r.active())
     expect(Option.getOrThrow(active).active_org_id).toBe(OrgID.make("org-1"))
   }),
 )
@@ -60,7 +60,7 @@ it.live("persistAccount normalizes trailing slashes in stored server URLs", () =
   Effect.gen(function* () {
     const id = AccountID.make("user-1")
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id,
         email: "test@example.com",
@@ -72,9 +72,9 @@ it.live("persistAccount normalizes trailing slashes in stored server URLs", () =
       }),
     )
 
-    const row = yield* AccountRepo.use((r) => r.getRow(id))
-    const active = yield* AccountRepo.use((r) => r.active())
-    const list = yield* AccountRepo.use((r) => r.list())
+    const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
+    const active = yield* AccountRepo.Service.use((r) => r.active())
+    const list = yield* AccountRepo.Service.use((r) => r.list())
 
     expect(Option.getOrThrow(row).url).toBe("https://control.example.com")
     expect(Option.getOrThrow(active).url).toBe("https://control.example.com")
@@ -87,7 +87,7 @@ it.live("persistAccount sets the active account and org", () =>
     const id1 = AccountID.make("user-1")
     const id2 = AccountID.make("user-2")
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id: id1,
         email: "first@example.com",
@@ -99,7 +99,7 @@ it.live("persistAccount sets the active account and org", () =>
       }),
     )
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id: id2,
         email: "second@example.com",
@@ -112,7 +112,7 @@ it.live("persistAccount sets the active account and org", () =>
     )
 
     // Last persisted account is active with its org
-    const active = yield* AccountRepo.use((r) => r.active())
+    const active = yield* AccountRepo.Service.use((r) => r.active())
     expect(Option.isSome(active)).toBe(true)
     expect(Option.getOrThrow(active).id).toBe(AccountID.make("user-2"))
     expect(Option.getOrThrow(active).active_org_id).toBe(OrgID.make("org-2"))
@@ -124,7 +124,7 @@ it.live("list returns all accounts", () =>
     const id1 = AccountID.make("user-1")
     const id2 = AccountID.make("user-2")
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id: id1,
         email: "a@example.com",
@@ -136,7 +136,7 @@ it.live("list returns all accounts", () =>
       }),
     )
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id: id2,
         email: "b@example.com",
@@ -148,7 +148,7 @@ it.live("list returns all accounts", () =>
       }),
     )
 
-    const accounts = yield* AccountRepo.use((r) => r.list())
+    const accounts = yield* AccountRepo.Service.use((r) => r.list())
     expect(accounts.length).toBe(2)
     expect(accounts.map((a) => a.email).sort()).toEqual(["a@example.com", "b@example.com"])
   }),
@@ -158,7 +158,7 @@ it.live("remove deletes an account", () =>
   Effect.gen(function* () {
     const id = AccountID.make("user-1")
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id,
         email: "test@example.com",
@@ -170,9 +170,9 @@ it.live("remove deletes an account", () =>
       }),
     )
 
-    yield* AccountRepo.use((r) => r.remove(id))
+    yield* AccountRepo.Service.use((r) => r.remove(id))
 
-    const row = yield* AccountRepo.use((r) => r.getRow(id))
+    const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
     expect(Option.isNone(row)).toBe(true)
   }),
 )
@@ -182,7 +182,7 @@ it.live("use stores the selected org and marks the account active", () =>
     const id1 = AccountID.make("user-1")
     const id2 = AccountID.make("user-2")
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id: id1,
         email: "first@example.com",
@@ -194,7 +194,7 @@ it.live("use stores the selected org and marks the account active", () =>
       }),
     )
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id: id2,
         email: "second@example.com",
@@ -206,13 +206,13 @@ it.live("use stores the selected org and marks the account active", () =>
       }),
     )
 
-    yield* AccountRepo.use((r) => r.use(id1, Option.some(OrgID.make("org-99"))))
-    const active1 = yield* AccountRepo.use((r) => r.active())
+    yield* AccountRepo.Service.use((r) => r.use(id1, Option.some(OrgID.make("org-99"))))
+    const active1 = yield* AccountRepo.Service.use((r) => r.active())
     expect(Option.getOrThrow(active1).id).toBe(id1)
     expect(Option.getOrThrow(active1).active_org_id).toBe(OrgID.make("org-99"))
 
-    yield* AccountRepo.use((r) => r.use(id1, Option.none()))
-    const active2 = yield* AccountRepo.use((r) => r.active())
+    yield* AccountRepo.Service.use((r) => r.use(id1, Option.none()))
+    const active2 = yield* AccountRepo.Service.use((r) => r.active())
     expect(Option.getOrThrow(active2).active_org_id).toBeNull()
   }),
 )
@@ -221,7 +221,7 @@ it.live("persistToken updates token fields", () =>
   Effect.gen(function* () {
     const id = AccountID.make("user-1")
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id,
         email: "test@example.com",
@@ -234,7 +234,7 @@ it.live("persistToken updates token fields", () =>
     )
 
     const expiry = Date.now() + 7200_000
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistToken({
         accountID: id,
         accessToken: AccessToken.make("new_token"),
@@ -243,7 +243,7 @@ it.live("persistToken updates token fields", () =>
       }),
     )
 
-    const row = yield* AccountRepo.use((r) => r.getRow(id))
+    const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
     const value = Option.getOrThrow(row)
     expect(value.access_token).toBe(AccessToken.make("new_token"))
     expect(value.refresh_token).toBe(RefreshToken.make("new_refresh"))
@@ -255,7 +255,7 @@ it.live("persistToken with no expiry sets token_expiry to null", () =>
   Effect.gen(function* () {
     const id = AccountID.make("user-1")
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id,
         email: "test@example.com",
@@ -267,7 +267,7 @@ it.live("persistToken with no expiry sets token_expiry to null", () =>
       }),
     )
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistToken({
         accountID: id,
         accessToken: AccessToken.make("new_token"),
@@ -276,7 +276,7 @@ it.live("persistToken with no expiry sets token_expiry to null", () =>
       }),
     )
 
-    const row = yield* AccountRepo.use((r) => r.getRow(id))
+    const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
     expect(Option.getOrThrow(row).token_expiry).toBeNull()
   }),
 )
@@ -285,7 +285,7 @@ it.live("persistAccount upserts on conflict", () =>
   Effect.gen(function* () {
     const id = AccountID.make("user-1")
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id,
         email: "test@example.com",
@@ -297,7 +297,7 @@ it.live("persistAccount upserts on conflict", () =>
       }),
     )
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id,
         email: "test@example.com",
@@ -309,14 +309,14 @@ it.live("persistAccount upserts on conflict", () =>
       }),
     )
 
-    const accounts = yield* AccountRepo.use((r) => r.list())
+    const accounts = yield* AccountRepo.Service.use((r) => r.list())
     expect(accounts.length).toBe(1)
 
-    const row = yield* AccountRepo.use((r) => r.getRow(id))
+    const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
     const value = Option.getOrThrow(row)
     expect(value.access_token).toBe(AccessToken.make("at_v2"))
 
-    const active = yield* AccountRepo.use((r) => r.active())
+    const active = yield* AccountRepo.Service.use((r) => r.active())
     expect(Option.getOrThrow(active).active_org_id).toBe(OrgID.make("org-2"))
   }),
 )
@@ -325,7 +325,7 @@ it.live("remove clears active state when deleting the active account", () =>
   Effect.gen(function* () {
     const id = AccountID.make("user-1")
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id,
         email: "test@example.com",
@@ -337,16 +337,16 @@ it.live("remove clears active state when deleting the active account", () =>
       }),
     )
 
-    yield* AccountRepo.use((r) => r.remove(id))
+    yield* AccountRepo.Service.use((r) => r.remove(id))
 
-    const active = yield* AccountRepo.use((r) => r.active())
+    const active = yield* AccountRepo.Service.use((r) => r.active())
     expect(Option.isNone(active)).toBe(true)
   }),
 )
 
 it.live("getRow returns none for nonexistent account", () =>
   Effect.gen(function* () {
-    const row = yield* AccountRepo.use((r) => r.getRow(AccountID.make("nope")))
+    const row = yield* AccountRepo.Service.use((r) => r.getRow(AccountID.make("nope")))
     expect(Option.isNone(row)).toBe(true)
   }),
 )

+ 10 - 10
packages/opencode/test/account/service.test.ts

@@ -122,7 +122,7 @@ it.live("login maps transport failures to account transport errors", () =>
 
 it.live("orgsByAccount groups orgs per account", () =>
   Effect.gen(function* () {
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id: AccountID.make("user-1"),
         email: "one@example.com",
@@ -134,7 +134,7 @@ it.live("orgsByAccount groups orgs per account", () =>
       }),
     )
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id: AccountID.make("user-2"),
         email: "two@example.com",
@@ -177,7 +177,7 @@ it.live("token refresh persists the new token", () =>
   Effect.gen(function* () {
     const id = AccountID.make("user-1")
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id,
         email: "user@example.com",
@@ -206,7 +206,7 @@ it.live("token refresh persists the new token", () =>
     expect(Option.getOrThrow(token)).toBeDefined()
     expect(String(Option.getOrThrow(token))).toBe("at_new")
 
-    const row = yield* AccountRepo.use((r) => r.getRow(id))
+    const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
     const value = Option.getOrThrow(row)
     expect(value.access_token).toBe(AccessToken.make("at_new"))
     expect(value.refresh_token).toBe(RefreshToken.make("rt_new"))
@@ -218,7 +218,7 @@ it.live("token refreshes before expiry when inside the eager refresh window", ()
   Effect.gen(function* () {
     const id = AccountID.make("user-1")
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id,
         email: "user@example.com",
@@ -251,7 +251,7 @@ it.live("token refreshes before expiry when inside the eager refresh window", ()
     expect(String(Option.getOrThrow(token))).toBe("at_new")
     expect(refreshCalls).toBe(1)
 
-    const row = yield* AccountRepo.use((r) => r.getRow(id))
+    const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
     const value = Option.getOrThrow(row)
     expect(value.access_token).toBe(AccessToken.make("at_new"))
     expect(value.refresh_token).toBe(RefreshToken.make("rt_new"))
@@ -262,7 +262,7 @@ it.live("concurrent config and token requests coalesce token refresh", () =>
   Effect.gen(function* () {
     const id = AccountID.make("user-1")
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id,
         email: "user@example.com",
@@ -315,7 +315,7 @@ it.live("concurrent config and token requests coalesce token refresh", () =>
     expect(String(Option.getOrThrow(token))).toBe("at_new")
     expect(refreshCalls).toBe(1)
 
-    const row = yield* AccountRepo.use((r) => r.getRow(id))
+    const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
     const value = Option.getOrThrow(row)
     expect(value.access_token).toBe(AccessToken.make("at_new"))
     expect(value.refresh_token).toBe(RefreshToken.make("rt_new"))
@@ -326,7 +326,7 @@ it.live("config sends the selected org header", () =>
   Effect.gen(function* () {
     const id = AccountID.make("user-1")
 
-    yield* AccountRepo.use((r) =>
+    yield* AccountRepo.Service.use((r) =>
       r.persistAccount({
         id,
         email: "user@example.com",
@@ -388,7 +388,7 @@ it.live("poll stores the account and first org on success", () =>
       expect(res.email).toBe("user@example.com")
     }
 
-    const active = yield* AccountRepo.use((r) => r.active())
+    const active = yield* AccountRepo.Service.use((r) => r.active())
     expect(Option.getOrThrow(active)).toEqual(
       expect.objectContaining({
         id: "user-1",

+ 1 - 1
packages/opencode/test/share/share-next.test.ts

@@ -72,7 +72,7 @@ const share = (id: SessionID) =>
   Database.use((db) => db.select().from(SessionShareTable).where(eq(SessionShareTable.session_id, id)).get())
 
 const seed = (url: string, org?: string) =>
-  AccountRepo.use((repo) =>
+  AccountRepo.Service.use((repo) =>
     repo.persistAccount({
       id: AccountID.make("account-1"),
       email: "user@example.com",