Buckets:
| export * as FileMutation from "./file-mutation" | |
| import { Context, Effect, Layer, Schema } from "effect" | |
| import { dirname } from "path" | |
| import { KeyedMutex } from "./effect/keyed-mutex" | |
| import { FSUtil } from "./fs-util" | |
| export interface Target { | |
| readonly canonical: string | |
| readonly resource: string | |
| } | |
| export interface WriteInput { | |
| readonly target: Target | |
| readonly content: string | Uint8Array | |
| } | |
| export interface TextWriteInput { | |
| readonly target: Target | |
| readonly content: string | |
| } | |
| export interface ConditionalWriteInput extends WriteInput { | |
| readonly expected: Uint8Array | |
| } | |
| export interface RemoveInput { | |
| readonly target: Target | |
| } | |
| export class StaleContentError extends Schema.TaggedErrorClass<StaleContentError>()("FileMutation.StaleContentError", { | |
| path: Schema.String, | |
| }) {} | |
| export class TargetExistsError extends Schema.TaggedErrorClass<TargetExistsError>()("FileMutation.TargetExistsError", { | |
| path: Schema.String, | |
| }) {} | |
| export interface WriteResult { | |
| readonly operation: "write" | |
| readonly target: string | |
| readonly resource: string | |
| readonly existed: boolean | |
| } | |
| export interface RemoveResult { | |
| readonly operation: "remove" | |
| readonly target: string | |
| readonly resource: string | |
| readonly existed: boolean | |
| } | |
| export interface Interface { | |
| /** Create without replacing an existing target. */ | |
| readonly create: (input: WriteInput) => Effect.Effect<WriteResult, TargetExistsError | FSUtil.Error> | |
| readonly write: (input: WriteInput) => Effect.Effect<WriteResult, FSUtil.Error> | |
| /** Write text while retaining an existing UTF-8 BOM and emitting at most one BOM. */ | |
| readonly writeTextPreservingBom: (input: TextWriteInput) => Effect.Effect<WriteResult, FSUtil.Error> | |
| /** Commit only if an existing target still has the expected bytes. */ | |
| readonly writeIfUnchanged: ( | |
| input: ConditionalWriteInput, | |
| ) => Effect.Effect<WriteResult, StaleContentError | FSUtil.Error> | |
| readonly remove: (input: RemoveInput) => Effect.Effect<RemoveResult, FSUtil.Error> | |
| } | |
| export class Service extends Context.Service<Service, Interface>()("@opencode/v2/FileMutation") {} | |
| /** | |
| * Serialize file changes by canonical target. Conditional writes compare and | |
| * write under the same process-local lock so cooperating OpenCode mutations do | |
| * not overwrite changes made from the same stale content. | |
| */ | |
| export const layer = Layer.effect( | |
| Service, | |
| Effect.gen(function* () { | |
| const fs = yield* FSUtil.Service | |
| const locks = KeyedMutex.makeUnsafe<string>() | |
| const withTargetLock = | |
| (target: Target) => | |
| <A, E, R>(effect: Effect.Effect<A, E, R>) => | |
| locks.withLock(target.canonical)(Effect.uninterruptible(effect)) | |
| const writeResult = (target: Target, existed: boolean): WriteResult => ({ | |
| operation: "write", | |
| target: target.canonical, | |
| resource: target.resource, | |
| existed, | |
| }) | |
| const removeResult = (target: Target, existed: boolean): RemoveResult => ({ | |
| operation: "remove", | |
| target: target.canonical, | |
| resource: target.resource, | |
| existed, | |
| }) | |
| const write = Effect.fn("FileMutation.write")((input: WriteInput) => | |
| withTargetLock(input.target)( | |
| Effect.gen(function* () { | |
| const existed = yield* fs.exists(input.target.canonical) | |
| yield* fs.writeWithDirs(input.target.canonical, input.content) | |
| return writeResult(input.target, existed) | |
| }), | |
| ), | |
| ) | |
| const writeTextPreservingBom = Effect.fn("FileMutation.writeTextPreservingBom")((input: TextWriteInput) => | |
| withTargetLock(input.target)( | |
| Effect.gen(function* () { | |
| const next = splitBom(input.content) | |
| const current = yield* fs | |
| .readFile(input.target.canonical) | |
| .pipe(Effect.catchReason("PlatformError", "NotFound", () => Effect.succeed(undefined))) | |
| yield* fs.writeWithDirs( | |
| input.target.canonical, | |
| joinBom(next.text, Boolean(current && hasUtf8Bom(current)) || next.bom), | |
| ) | |
| return writeResult(input.target, current !== undefined) | |
| }), | |
| ), | |
| ) | |
| const create = Effect.fn("FileMutation.create")((input: WriteInput) => | |
| withTargetLock(input.target)( | |
| Effect.gen(function* () { | |
| const write = | |
| typeof input.content === "string" | |
| ? fs.writeFileString(input.target.canonical, input.content, { flag: "wx" }) | |
| : fs.writeFile(input.target.canonical, input.content, { flag: "wx" }) | |
| yield* write.pipe( | |
| Effect.catchReason("PlatformError", "NotFound", () => | |
| fs.ensureDir(dirname(input.target.canonical)).pipe(Effect.andThen(write)), | |
| ), | |
| Effect.catchReason("PlatformError", "AlreadyExists", () => | |
| Effect.fail(new TargetExistsError({ path: input.target.canonical })), | |
| ), | |
| ) | |
| return writeResult(input.target, false) | |
| }), | |
| ), | |
| ) | |
| const writeIfUnchanged = Effect.fn("FileMutation.writeIfUnchanged")((input: ConditionalWriteInput) => | |
| withTargetLock(input.target)( | |
| Effect.gen(function* () { | |
| const current = yield* fs.readFile(input.target.canonical) | |
| if (!sameBytes(current, input.expected)) { | |
| return yield* new StaleContentError({ path: input.target.canonical }) | |
| } | |
| yield* typeof input.content === "string" | |
| ? fs.writeFileString(input.target.canonical, input.content) | |
| : fs.writeFile(input.target.canonical, input.content) | |
| return writeResult(input.target, true) | |
| }), | |
| ), | |
| ) | |
| const remove = Effect.fn("FileMutation.remove")((input: RemoveInput) => | |
| withTargetLock(input.target)( | |
| Effect.gen(function* () { | |
| const existed = yield* fs.remove(input.target.canonical).pipe( | |
| Effect.as(true), | |
| Effect.catchReason("PlatformError", "NotFound", () => Effect.succeed(false)), | |
| ) | |
| return removeResult(input.target, existed) | |
| }), | |
| ), | |
| ) | |
| return Service.of({ create, write, writeTextPreservingBom, writeIfUnchanged, remove }) | |
| }), | |
| ) | |
| function splitBom(text: string) { | |
| const stripped = text.replace(/^\uFEFF+/, "") | |
| return { bom: stripped.length !== text.length, text: stripped } | |
| } | |
| function joinBom(text: string, bom: boolean) { | |
| const stripped = splitBom(text).text | |
| return bom ? `\uFEFF${stripped}` : stripped | |
| } | |
| function hasUtf8Bom(content: Uint8Array) { | |
| return content[0] === 0xef && content[1] === 0xbb && content[2] === 0xbf | |
| } | |
| function sameBytes(left: Uint8Array, right: Uint8Array) { | |
| if (left.length !== right.length) return false | |
| return left.every((byte, index) => byte === right[index]) | |
| } | |
| export const locationLayer = layer | |
| /** | |
| * Deferred until the corresponding V2 integrations exist. | |
| */ | |
| // TODO: Add formatter integration after V2 formatter runtime exists. | |
| // TODO: Publish watcher/file-edit events after V2 watcher integration exists. | |
| // TODO: Add snapshots / undo after V2 snapshot design exists. | |
| // TODO: Notify LSP and collect diagnostics after V2 LSP runtime exists. | |
| // TODO: Design multi-file transactions / rollback if apply_patch needs atomic edits. | |
| // Until then, edits are sequential and report partial application. | |
| // TODO: Define crash recovery and idempotency for side effects between Tool.Called and durable settlement. | |
Xet Storage Details
- Size:
- 7.35 kB
- Xet hash:
- a712a49d3e11b3f52aa9efd273dd458cd083d9b1db6315465c98589939148750
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.