diff --git a/.changeset/lemon-boxes-dream.md b/.changeset/lemon-boxes-dream.md new file mode 100644 index 0000000000..ebdcafb90b --- /dev/null +++ b/.changeset/lemon-boxes-dream.md @@ -0,0 +1,7 @@ +--- +"@lix-js/client": minor +"@lix-js/server": minor +"@lix-js/fs": minor +--- + +release for lazy checkout diff --git a/lix/source-code/client/src/helpers.ts b/lix/source-code/client/src/helpers.ts index cfa86fc568..e34e9c1fe0 100644 --- a/lix/source-code/client/src/helpers.ts +++ b/lix/source-code/client/src/helpers.ts @@ -8,8 +8,13 @@ type Args = { nodeishFs: NodeishFilesystem verbose?: boolean description?: string - intercept?: (args: { prop: keyof NodeishFilesystem; execute: () => any }) => any + intercept?: (args: { + prop: keyof NodeishFilesystem + execute: () => any + argumentsList: any[] + }) => any } + export const withLazyFetching = ({ nodeishFs, verbose = false, @@ -31,6 +36,7 @@ export const withLazyFetching = ({ ? intercept({ prop, argumentsList, execute } as { prop: keyof typeof nodeishFs execute: () => any + argumentsList: any[] }) : execute() }, diff --git a/lix/source-code/fs/src/implementations.test.ts b/lix/source-code/fs/src/implementations.test.ts index 05257d635e..4b9f2fcb4f 100644 --- a/lix/source-code/fs/src/implementations.test.ts +++ b/lix/source-code/fs/src/implementations.test.ts @@ -267,6 +267,41 @@ const runFsTestSuite = async ( ]) }) + test.skipIf(isNodeFs)("placeholders", async () => { + const placeholderPath = `/placeholders/subdir` + + await fs.mkdir(placeholderPath, { recursive: true }) + + await fs.writeFile(`${placeholderPath}/file`, "") + + fs._createPlaceholder(placeholderPath + "/test") + + expect(fs._isPlaceholder(placeholderPath + "/test")).toBe(true) + expect(fs._isPlaceholder(placeholderPath + "/noexists")).toBe(false) + expect(fs._isPlaceholder(placeholderPath + "/file")).toBe(false) + + const dirents = await fs.readdir(`/placeholders/subdir`) + + expect(dirents).toStrictEqual(["file", "test"]) + + await expect(async () => await fs.readFile(`${placeholderPath}/test`)).rejects.toThrow( + /EPLACEHOLDER/ + ) + + await fs.rm("/placeholders", { recursive: true }) + + const finalDirents = await fs.readdir(`/`) + + expect(finalDirents).toStrictEqual([ + "home", + "file2", + "file3", + "file1.link", + "file3.link", + "user1.link", + ]) + }) + test("unlink", async () => { await fs.unlink(`${tempDir}/user1.link`) await fs.unlink(`${tempDir}/file1.link`) diff --git a/lix/source-code/fs/src/memoryFs.ts b/lix/source-code/fs/src/memoryFs.ts index a06cf8a1c8..e9c8948f55 100644 --- a/lix/source-code/fs/src/memoryFs.ts +++ b/lix/source-code/fs/src/memoryFs.ts @@ -2,11 +2,11 @@ import type { NodeishFilesystem, NodeishStats, FileChangeInfo } from "./NodeishF import { FilesystemError } from "./errors/FilesystemError.js" import { normalPath, getBasename, getDirname } from "./utilities/helpers.js" -type Inode = Uint8Array | Set +type Inode = Uint8Array | Set | { placeholder: true } export type Snapshot = { fsMap: { - [key: string]: string[] | string + [key: string]: string[] | string | { placeholder: true } } fsStats: { [key: string]: { @@ -148,6 +148,31 @@ export function createNodeishMemoryFs(): NodeishFilesystem { return { _state: state, + + _createPlaceholder: function ( + path: Parameters[0], + options?: Parameters[2] + ) { + path = normalPath(path) + const dirName = getDirname(path) + const baseName = getBasename(path) + const parentDir: Inode | undefined = state.fsMap.get(dirName) + if (!(parentDir instanceof Set)) throw new FilesystemError("ENOENT", path, "writeFile") + parentDir.add(baseName) + newStatEntry(path, state.fsStats, 0, options?.mode ?? 0o644) + state.fsMap.set(path, { placeholder: true }) + }, + + _isPlaceholder: function (path: Parameters[0]) { + path = normalPath(path) + const entry = state.fsMap.get(path) + + if (entry && "placeholder" in entry) { + return true + } + return false + }, + writeFile: async function ( path: Parameters[0], data: Parameters[1], @@ -194,6 +219,7 @@ export function createNodeishMemoryFs(): NodeishFilesystem { if (file instanceof Set) throw new FilesystemError("EISDIR", path, "readFile") if (file === undefined) throw new FilesystemError("ENOENT", path, "readFile") + if ("placeholder" in file) throw new FilesystemError("EPLACEHOLDER", path, "readFile") if (!(options?.encoding || typeof options === "string")) return file return decoder.decode(file) @@ -257,8 +283,9 @@ export function createNodeishMemoryFs(): NodeishFilesystem { throw new FilesystemError("ENOENT", path, "rm") if (parentDir instanceof Uint8Array) throw new FilesystemError("ENOTDIR", path, "rm") + if ("placeholder" in parentDir) throw new FilesystemError("EPLACEHOLDER", path, "readFile") - if (target instanceof Uint8Array) { + if (target instanceof Uint8Array || "placeholder" in target) { parentDir.delete(baseName) state.fsStats.delete(path) state.fsMap.delete(path) @@ -392,6 +419,10 @@ export function createNodeishMemoryFs(): NodeishFilesystem { if (parentDir instanceof Uint8Array || target instanceof Uint8Array) throw new FilesystemError("ENOTDIR", path, "rmdir") + if ("placeholder" in parentDir || "placeholder" in target) { + throw new FilesystemError("EPLACEHOLDER", path, "readFile") + } + if (target.size) throw new FilesystemError("ENOTEMPTY", path, "rmdir") parentDir.delete(baseName) @@ -427,6 +458,9 @@ export function createNodeishMemoryFs(): NodeishFilesystem { if (targetInode !== undefined) { state.fsMap.set(path, targetInode) } + if ("placeholder" in parentDir) { + throw new FilesystemError("EPLACEHOLDER", path, "readFile") + } parentDir.add(getBasename(path)) newStatEntry(path, state.fsStats, 2, 0o777, target) @@ -449,6 +483,10 @@ export function createNodeishMemoryFs(): NodeishFilesystem { throw new FilesystemError("EISDIR", path, "unlink") } + if ("placeholder" in parentDir || "placeholder" in target) { + throw new FilesystemError("EPLACEHOLDER", path, "readFile") + } + parentDir.delete(getBasename(path)) state.fsStats.delete(path) state.fsMap.delete(path) diff --git a/lix/source-code/server/package.json b/lix/source-code/server/package.json index 06ebaecc04..2b7bf6c582 100644 --- a/lix/source-code/server/package.json +++ b/lix/source-code/server/package.json @@ -1,6 +1,7 @@ { "name": "@lix-js/server", "type": "module", + "version": "0.0.1", "private": true, "exports": { ".": "./dist/index.js" @@ -39,6 +40,5 @@ "typescript": "5.2.2", "vitest": "0.34.3" }, - "license": "Apache-2.0", - "version": null + "license": "Apache-2.0" }