diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts index 81a9a903ce4c..eb9a8069b17d 100644 --- a/packages/core/src/session.ts +++ b/packages/core/src/session.ts @@ -366,9 +366,15 @@ const layer = Layer.effect( if (recorded) return recorded const parent = input.parentID ? yield* store.get(input.parentID) : undefined if (input.parentID && parent === undefined) return yield* new NotFoundError({ sessionID: input.parentID }) - const location = parent?.location ?? input.location - if (location === undefined) + const requestedLocation = parent?.location ?? input.location + if (requestedLocation === undefined) return yield* Effect.die(new Error("Session.create requires either location or an existing parentID")) + const location = parent + ? parent.location + : Location.Ref.make({ + ...requestedLocation, + directory: AbsolutePath.make(yield* fs.resolve(requestedLocation.directory)), + }) const project = yield* projects.resolve(location.directory) yield* persistProject(project) const projected = yield* bus @@ -474,7 +480,8 @@ const layer = Layer.effect( const order = direction === "previous" ? (requestedOrder === "asc" ? "desc" : "asc") : requestedOrder const sortColumn = SessionTable.time_updated const conditions: SQL[] = [] - if ("directory" in input) conditions.push(eq(SessionTable.directory, input.directory)) + if ("directory" in input) + conditions.push(eq(SessionTable.directory, AbsolutePath.make(yield* fs.resolve(input.directory)))) if (input.workspaceID) conditions.push(eq(SessionTable.workspace_id, input.workspaceID)) if ("project" in input) conditions.push(eq(SessionTable.project_id, input.project)) if ("project" in input && input.subpath !== undefined) conditions.push(eq(SessionTable.path, input.subpath)) @@ -775,7 +782,7 @@ const layer = Layer.effect( const value = input.directory.trim() const expanded = value === "~" ? global.home : value.startsWith("~/") ? path.join(global.home, value.slice(2)) : value - const directory = AbsolutePath.make(path.resolve(current.location.directory, expanded)) + const directory = AbsolutePath.make(yield* fs.resolve(path.resolve(current.location.directory, expanded))) const info = yield* fs.stat(directory).pipe(Effect.orElseSucceed(() => undefined)) if (!info) return yield* new DestinationNotFoundError({ directory }) if (info.type !== "Directory") return yield* new DestinationNotDirectoryError({ directory }) diff --git a/packages/core/test/session-create.test.ts b/packages/core/test/session-create.test.ts index 69de3a547e45..9d1c23d56588 100644 --- a/packages/core/test/session-create.test.ts +++ b/packages/core/test/session-create.test.ts @@ -83,6 +83,27 @@ function withTmp(f: (directory: string) => Effect.Effect) { } describe("Session.create", () => { + liveIt.live("uses on-disk macOS casing for creation and directory listing", () => + withTmp((directory) => + Effect.gen(function* () { + if (process.platform !== "darwin") return + const session = yield* Session.Service + const actual = path.join(directory, "Academic", "Project") + yield* Effect.promise(() => fs.mkdir(actual, { recursive: true })) + const requested = path.join(directory, "academic", "project") + if (!(yield* Effect.promise(() => fs.access(requested).then(() => true, () => false)))) return + + const created = yield* session.create({ + location: Location.Ref.make({ directory: AbsolutePath.make(requested) }), + }) + + expect(created.location.directory).toBe(AbsolutePath.make(actual)) + expect((yield* session.list({ directory: AbsolutePath.make(requested) })).data).toEqual([created]) + expect((yield* session.list({ directory: AbsolutePath.make(actual) })).data).toEqual([created]) + }), + ), + ) + liveIt.live("follows the directory's project identity established after creation", () => withTmp((directory) => Effect.gen(function* () { diff --git a/packages/util/src/fs-util.ts b/packages/util/src/fs-util.ts index b82a5d8e66ce..afcaf431b809 100644 --- a/packages/util/src/fs-util.ts +++ b/packages/util/src/fs-util.ts @@ -103,6 +103,7 @@ export namespace FSUtil { const resolve = Effect.fn("FileSystem.resolve")(function* (input: string) { const resolved = path.resolve(windowsPath(input)) return yield* fs.realPath(resolved).pipe( + Effect.map((real) => (process.platform === "darwin" ? realpathSync.native(resolved) : real)), Effect.catchReason("PlatformError", "NotFound", () => Effect.succeed(resolved)), Effect.orDie, )