summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-16 19:50:15 -0400
committerGitHub <[email protected]>2026-04-16 23:50:15 +0000
commit715786bbf96304f617c5f2a48ed49fe6101c90ef (patch)
treec33f73c2b8764a63bce867465b37756fddc370cf /packages
parent218eca7c2bc95355f594c0fe50853326c86c469f (diff)
downloadopencode-715786bbf96304f617c5f2a48ed49fe6101c90ef.tar.gz
opencode-715786bbf96304f617c5f2a48ed49fe6101c90ef.zip
refactor: unwrap FileTime namespace + self-reexport (#22966)
Diffstat (limited to 'packages')
-rw-r--r--packages/opencode/src/file/time.ts208
1 files changed, 104 insertions, 104 deletions
diff --git a/packages/opencode/src/file/time.ts b/packages/opencode/src/file/time.ts
index 327eadbef..cc26682d5 100644
--- a/packages/opencode/src/file/time.ts
+++ b/packages/opencode/src/file/time.ts
@@ -5,109 +5,109 @@ import { Flag } from "@/flag/flag"
import type { SessionID } from "@/session/schema"
import { Log } from "../util"
-export namespace FileTime {
- const log = Log.create({ service: "file.time" })
-
- export type Stamp = {
- readonly read: Date
- readonly mtime: number | undefined
- readonly size: number | undefined
- }
-
- const session = (reads: Map<SessionID, Map<string, Stamp>>, sessionID: SessionID) => {
- const value = reads.get(sessionID)
- if (value) return value
-
- const next = new Map<string, Stamp>()
- reads.set(sessionID, next)
- return next
- }
-
- interface State {
- reads: Map<SessionID, Map<string, Stamp>>
- locks: Map<string, Semaphore.Semaphore>
- }
-
- export interface Interface {
- readonly read: (sessionID: SessionID, file: string) => Effect.Effect<void>
- readonly get: (sessionID: SessionID, file: string) => Effect.Effect<Date | undefined>
- readonly assert: (sessionID: SessionID, filepath: string) => Effect.Effect<void>
- readonly withLock: <T>(filepath: string, fn: () => Effect.Effect<T>) => Effect.Effect<T>
- }
-
- export class Service extends Context.Service<Service, Interface>()("@opencode/FileTime") {}
-
- export const layer = Layer.effect(
- Service,
- Effect.gen(function* () {
- const fsys = yield* AppFileSystem.Service
- const disableCheck = yield* Flag.OPENCODE_DISABLE_FILETIME_CHECK
-
- const stamp = Effect.fnUntraced(function* (file: string) {
- const info = yield* fsys.stat(file).pipe(Effect.catch(() => Effect.void))
- return {
- read: yield* DateTime.nowAsDate,
- mtime: info ? Option.getOrUndefined(info.mtime)?.getTime() : undefined,
- size: info ? Number(info.size) : undefined,
- }
- })
- const state = yield* InstanceState.make<State>(
- Effect.fn("FileTime.state")(() =>
- Effect.succeed({
- reads: new Map<SessionID, Map<string, Stamp>>(),
- locks: new Map<string, Semaphore.Semaphore>(),
- }),
- ),
- )
+const log = Log.create({ service: "file.time" })
+
+export type Stamp = {
+ readonly read: Date
+ readonly mtime: number | undefined
+ readonly size: number | undefined
+}
+
+const session = (reads: Map<SessionID, Map<string, Stamp>>, sessionID: SessionID) => {
+ const value = reads.get(sessionID)
+ if (value) return value
+
+ const next = new Map<string, Stamp>()
+ reads.set(sessionID, next)
+ return next
+}
+
+interface State {
+ reads: Map<SessionID, Map<string, Stamp>>
+ locks: Map<string, Semaphore.Semaphore>
+}
- const getLock = Effect.fn("FileTime.lock")(function* (filepath: string) {
- filepath = AppFileSystem.normalizePath(filepath)
- const locks = (yield* InstanceState.get(state)).locks
- const lock = locks.get(filepath)
- if (lock) return lock
-
- const next = Semaphore.makeUnsafe(1)
- locks.set(filepath, next)
- return next
- })
-
- const read = Effect.fn("FileTime.read")(function* (sessionID: SessionID, file: string) {
- file = AppFileSystem.normalizePath(file)
- const reads = (yield* InstanceState.get(state)).reads
- log.info("read", { sessionID, file })
- session(reads, sessionID).set(file, yield* stamp(file))
- })
-
- const get = Effect.fn("FileTime.get")(function* (sessionID: SessionID, file: string) {
- file = AppFileSystem.normalizePath(file)
- const reads = (yield* InstanceState.get(state)).reads
- return reads.get(sessionID)?.get(file)?.read
- })
-
- const assert = Effect.fn("FileTime.assert")(function* (sessionID: SessionID, filepath: string) {
- if (disableCheck) return
- filepath = AppFileSystem.normalizePath(filepath)
-
- const reads = (yield* InstanceState.get(state)).reads
- const time = reads.get(sessionID)?.get(filepath)
- if (!time) throw new Error(`You must read file ${filepath} before overwriting it. Use the Read tool first`)
-
- const next = yield* stamp(filepath)
- const changed = next.mtime !== time.mtime || next.size !== time.size
- if (!changed) return
-
- throw new Error(
- `File ${filepath} has been modified since it was last read.\nLast modification: ${new Date(next.mtime ?? next.read.getTime()).toISOString()}\nLast read: ${time.read.toISOString()}\n\nPlease read the file again before modifying it.`,
- )
- })
-
- const withLock = Effect.fn("FileTime.withLock")(function* <T>(filepath: string, fn: () => Effect.Effect<T>) {
- return yield* fn().pipe((yield* getLock(filepath)).withPermits(1))
- })
-
- return Service.of({ read, get, assert, withLock })
- }),
- ).pipe(Layer.orDie)
-
- export const defaultLayer = layer.pipe(Layer.provide(AppFileSystem.defaultLayer))
+export interface Interface {
+ readonly read: (sessionID: SessionID, file: string) => Effect.Effect<void>
+ readonly get: (sessionID: SessionID, file: string) => Effect.Effect<Date | undefined>
+ readonly assert: (sessionID: SessionID, filepath: string) => Effect.Effect<void>
+ readonly withLock: <T>(filepath: string, fn: () => Effect.Effect<T>) => Effect.Effect<T>
}
+
+export class Service extends Context.Service<Service, Interface>()("@opencode/FileTime") {}
+
+export const layer = Layer.effect(
+ Service,
+ Effect.gen(function* () {
+ const fsys = yield* AppFileSystem.Service
+ const disableCheck = yield* Flag.OPENCODE_DISABLE_FILETIME_CHECK
+
+ const stamp = Effect.fnUntraced(function* (file: string) {
+ const info = yield* fsys.stat(file).pipe(Effect.catch(() => Effect.void))
+ return {
+ read: yield* DateTime.nowAsDate,
+ mtime: info ? Option.getOrUndefined(info.mtime)?.getTime() : undefined,
+ size: info ? Number(info.size) : undefined,
+ }
+ })
+ const state = yield* InstanceState.make<State>(
+ Effect.fn("FileTime.state")(() =>
+ Effect.succeed({
+ reads: new Map<SessionID, Map<string, Stamp>>(),
+ locks: new Map<string, Semaphore.Semaphore>(),
+ }),
+ ),
+ )
+
+ const getLock = Effect.fn("FileTime.lock")(function* (filepath: string) {
+ filepath = AppFileSystem.normalizePath(filepath)
+ const locks = (yield* InstanceState.get(state)).locks
+ const lock = locks.get(filepath)
+ if (lock) return lock
+
+ const next = Semaphore.makeUnsafe(1)
+ locks.set(filepath, next)
+ return next
+ })
+
+ const read = Effect.fn("FileTime.read")(function* (sessionID: SessionID, file: string) {
+ file = AppFileSystem.normalizePath(file)
+ const reads = (yield* InstanceState.get(state)).reads
+ log.info("read", { sessionID, file })
+ session(reads, sessionID).set(file, yield* stamp(file))
+ })
+
+ const get = Effect.fn("FileTime.get")(function* (sessionID: SessionID, file: string) {
+ file = AppFileSystem.normalizePath(file)
+ const reads = (yield* InstanceState.get(state)).reads
+ return reads.get(sessionID)?.get(file)?.read
+ })
+
+ const assert = Effect.fn("FileTime.assert")(function* (sessionID: SessionID, filepath: string) {
+ if (disableCheck) return
+ filepath = AppFileSystem.normalizePath(filepath)
+
+ const reads = (yield* InstanceState.get(state)).reads
+ const time = reads.get(sessionID)?.get(filepath)
+ if (!time) throw new Error(`You must read file ${filepath} before overwriting it. Use the Read tool first`)
+
+ const next = yield* stamp(filepath)
+ const changed = next.mtime !== time.mtime || next.size !== time.size
+ if (!changed) return
+
+ throw new Error(
+ `File ${filepath} has been modified since it was last read.\nLast modification: ${new Date(next.mtime ?? next.read.getTime()).toISOString()}\nLast read: ${time.read.toISOString()}\n\nPlease read the file again before modifying it.`,
+ )
+ })
+
+ const withLock = Effect.fn("FileTime.withLock")(function* <T>(filepath: string, fn: () => Effect.Effect<T>) {
+ return yield* fn().pipe((yield* getLock(filepath)).withPermits(1))
+ })
+
+ return Service.of({ read, get, assert, withLock })
+ }),
+).pipe(Layer.orDie)
+
+export const defaultLayer = layer.pipe(Layer.provide(AppFileSystem.defaultLayer))
+
+export * as FileTime from "./time"