Skip to content

feat: define core types for virtual file system#13

Open
AliiiBenn wants to merge 14 commits intomainfrom
feat/define-types
Open

feat: define core types for virtual file system#13
AliiiBenn wants to merge 14 commits intomainfrom
feat/define-types

Conversation

@AliiiBenn
Copy link
Copy Markdown
Member

Summary

  • Define core TypeScript types for virtual file system: File, Directory, FileSystemNode, FileSystemItem
  • Add FileSystem interface defining the VFS API contract
  • Implement type guards: isFile(), isDirectory()
  • Add comprehensive features documentation
  • Refactor interfaces to types for better type inference
  • Organize tests in dedicated tests folder

Test plan

  • Run pnpm test to verify all tests pass
  • Run pnpm lint to check code style
  • Run pnpm typecheck to verify TypeScript types

🤖 Generated with Claude Code

AliiiBenn and others added 7 commits March 5, 2026 11:38
- Add FileSystemNode, File, Directory interfaces
- Add FileSystem interface with CRUD operations
- Add isFile and isDirectory type guards
- Add tests for type guards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use string (ISO 8601) instead of Date for serialization
- Add SizeInBytes type alias for clarity
- Add JSDoc for FileSystem interface

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use Date objects for createdAt/modifiedAt
- Fix children type to FileSystemItem[] for type safety
- Update type guards to accept FileSystemNode
- Change rm() return type to void
- Add JSDoc comments to all exports

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add comprehensive documentation for all VFS package features including:
- Core data structures (File, Directory, FileSystemNode)
- Maybe and Result monads
- Helper functions (file, folder, type guards)
- Path utilities
- Core VFS API operations
- Lock system
- File system events
- Shell module

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Virtual file system interface.
* This defines the API contract for implementing a virtual file system.
*/
export interface FileSystem {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FileSystem interface has methods that return void or nullable types, but according to the documentation, these should return Result types for proper error handling. Consider aligning with the documented API that uses Result monads.

@@ -0,0 +1,63 @@
export type SizeInBytes = number;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a branded type for stronger type safety, e.g., type SizeInBytes = number & { __brand: 'SizeInBytes' }. This prevents accidental mixing of numeric values.

root: Directory;
exists(path: string): boolean;
get(path: string): FileSystemItem | null;
mkdir(path: string): Directory;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FileSystem interface specifies mkdir, touch, and rm operations but these are not yet implemented. Consider either removing these methods or documenting them as planned/coming soon in the interface.

/**
* File type representing a file in the virtual file system.
*/
export type File = FileSystemNode & {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The size property is defined as SizeInBytes but there's no enforcement that it matches content.length. Consider either computing it dynamically or adding a runtime validation.

@marty-action
Copy link
Copy Markdown

marty-action bot commented Mar 9, 2026

Summary

This PR introduces core TypeScript types for a virtual file system (VFS) implementation, including file/directory types, a FileSystem interface, and type guard functions.

Critical Issues

  1. Inconsistent Error Handling: The FileSystem interface uses void returns and nullable types, but the existing documentation (docs/features/README.md) specifies using Result monads for error handling. This creates an inconsistency between the documented API and the implemented interface.

  2. Incomplete Interface: Methods like mkdir, touch, rm are declared in the interface but no implementation exists in this PR. Consider either implementing them or marking them as planned.

Recommendations

  1. Branded Types: Consider using branded types (e.g., type SizeInBytes = number and { __brand: SizeInBytes }) to prevent accidental mixing of numeric values.

  2. Size Consistency: The size property in File should either be computed from content.length or validated to match it at runtime.

  3. Path Type: Consider adding a specific Path type instead of using string for all paths to improve type safety.

  4. Test Coverage: Consider adding tests for edge cases like empty strings, special characters in filenames, and boundary conditions.

Positive Notes

  • Good use of discriminated union types (FileSystemItem = File | Directory) for type safety
  • Type guards (isFile, isDirectory) are correctly implemented and tested
  • Clean separation between base types (FileSystemNode) and specialized types
  • Well-documented with JSDoc comments
  • Tests are well-structured and cover basic functionality

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@marty-action
Copy link
Copy Markdown

marty-action bot commented Mar 9, 2026

Code Review

Inline Comments

packages/vfs/src/types.ts:1
SizeInBytes is defined but not exported. Consider whether users need access to this base type.

packages/vfs/src/types.ts:6-12
FileSystemNode base type is not exported. If users need to work with the common properties of files and directories, they should have access to this type.

packages/vfs/src/types.ts:40-49
The FileSystem interface looks good, but there are some considerations:

  • The path parameter in methods should document expected format (e.g., absolute vs relative paths)
  • Consider adding return types for methods that could fail (e.g., read() returning null is good, but mkdir() could also fail if parent doesn't exist)
  • The rm() method has void return but doesn't indicate if it throws on non-existent paths

packages/vfs/src/types.ts:54-56 & 61-63
Type guards are well implemented. However, they check !item.isDirectory which relies on the boolean being correct. Consider adding an additional check for safety in case the type is incomplete.

Copy link
Copy Markdown

@marty-action marty-action bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Summary

The PR introduces core TypeScript types for a virtual file system with good structure and type safety. The implementation uses discriminated unions effectively and provides useful type guards.

Critical Issues

  1. Missing exports: SizeInBytes, FileSystemNode, and FileSystemItem are not exported from index.ts. Users may need these base types to work with the VFS.
  2. No path format specification: The FileSystem interface methods accept path: string but don't specify whether paths should be absolute, relative, or any particular format.
  3. Error handling not defined: Methods like mkdir(), rm(), write() don't indicate their error behavior - do they throw on failure?

Recommendations

  1. Add JSDoc documentation to FileSystem interface methods explaining path format expectations and error conditions.
  2. Consider adding a FileSystemError type or enum for consistent error handling across implementations.
  3. The size property in File could benefit from being optional or having validation - file size should be non-negative.
  4. Consider adding a readdir() method to FileSystem interface for listing directory contents.

Positive Notes

  • Well-structured type hierarchy with proper use of TypeScript features
  • Good JSDoc documentation on most types
  • Type guards are correctly implemented using type predicates
  • Tests cover the type guards comprehensively
  • Clean separation between File and Directory using discriminated union pattern

Update documentation to reflect the actual code structure using
intersection types for File and Directory extending FileSystemNode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@@ -0,0 +1,63 @@
export type SizeInBytes = number;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SizeInBytes type is just a type alias to number, which doesn't provide meaningful type safety. Consider using a branded type to prevent mixing with other numbers:

type SizeInBytes = number & { readonly brand: unique symbol };

mkdir(path: string): Directory;
touch(path: string, content?: string): File;
rm(path: string): void;
read(path: string): string | null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The read method returns string | null, which cannot distinguish between "file not found" and "empty file content". Consider using the Result monad as documented in docs/README.md for consistent error handling across all methods.

* Virtual file system interface.
* This defines the API contract for implementing a virtual file system.
*/
export interface FileSystem {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation in docs/README.md shows a more comprehensive API using Result monads with discriminated union error types (e.g., { type: 'not_found'; path: string }). There's a mismatch between the documented API and this interface. Consider aligning them or updating the documentation.

export type File = FileSystemNode & {
isDirectory: false;
content: string;
size: SizeInBytes;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no mechanism to ensure size stays in sync with content.length. Consider adding a helper function or making size a getter that computes the size dynamically:</```typescript
get size(): SizeInBytes {
return new TextEncoder().encode(this.content).length;
}

@@ -0,0 +1,63 @@
export type SizeInBytes = number;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary: Good foundation for the VFS core types with well-structured type hierarchy and type guards. The types are well-documented with JSDoc comments.

Critical Issues:

  • API inconsistency between types.ts and the documented API in docs/README.md - the interface uses nullable returns while docs show Result monads
  • SizeInBytes is just a type alias providing no real type safety

Recommendations:

  • Align the FileSystem interface with the documented Result-based error handling
  • Consider using branded types for SizeInBytes to prevent arithmetic errors
  • Add size synchronization mechanism or make it a computed getter
  • Add path validation types (the docs mention security against traversal attacks but no validation types exist)

Positive Notes:

  • Clean type hierarchy with proper intersection types (FileSystemNode & {...})
  • Type guards isFile and isDirectory are correctly implemented
  • Good use of discriminated unions for isDirectory field
  • Tests cover the type guards adequately

AliiiBenn and others added 2 commits March 9, 2026 14:00
These have been extracted to an external @nesalia/fp package.
Added note referencing the external package.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Virtual file system interface.
* This defines the API contract for implementing a virtual file system.
*/
export interface FileSystem {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FileSystem interface defines a useful API contract, but there's a mismatch between what's declared here and what's documented in docs/README.md. The documentation shows methods like readFile, writeFile, mkdir, rm, copy, move, readDir, rename, lock, unlock, watch, etc., but the interface only has: exists, get, mkdir, touch, rm, read, write.

This is a critical issue because:

  1. The documentation describes an API that doesn't match the interface
  2. The actual implementation isn't provided in this PR (this is just type definitions)

Either the interface needs to be expanded to match the docs, or the docs need to be updated to reflect the actual interface.

@@ -0,0 +1,500 @@
# VFS Package Features
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: The documentation in docs/README.md references features that don't exist in the codebase yet (helper functions like file(), folder(), path utilities, lock system, file system events, shell module). The docs read as if all these features are already implemented, but only the core types exist.

Recommendation: Either:

  1. Remove the documentation for unimplemented features, or
  2. Add a prominent note at the top indicating this is a feature roadmap/backlog

*/
export type Directory = FileSystemNode & {
isDirectory: true;
children: FileSystemItem[];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The children type is FileSystemItem[] which creates a circular reference in the type definition. While this works in TypeScript, it could cause issues with certain tools (like some JSON schema generators). Consider if this needs special handling or documentation about the circular reference.

@marty-action
Copy link
Copy Markdown

marty-action bot commented Mar 9, 2026

Summary

This PR introduces core TypeScript types for a virtual file system (File, Directory, FileSystemNode, FileSystemItem) along with a FileSystem interface and type guards. The implementation is clean and follows TypeScript best practices.

Critical Issues

  1. Documentation/Interface Mismatch: The FileSystem interface (lines 40-49) only defines 8 methods (root, exists, get, mkdir, touch, rm, read, write), but docs/README.md documents many more (like readFile, writeFile, copy, move, rename, lock, unlock, watch). This creates confusion about the actual API contract.

  2. Premature Documentation: The docs/README.md documents features that don't exist yet (helper functions file()/folder(), path utilities, lock system, events, shell module). The docs read as if all features are implemented, but only core types exist.

Recommendations

  1. Sync interface with docs: Either expand the FileSystem interface to match documented methods, or update the docs to reflect only what's implemented.

  2. Scope the documentation: Either remove unimplemented feature docs or add a clear note that this is a roadmap/backlog document.

  3. Consider a separate package.json for docs: Since the docs are in a top-level docs/ folder but describe a package, consider whether they should be published with the package or kept as internal planning docs.

Positive Notes

  • Clean type design using intersection types for File and Directory extending FileSystemNode
  • Proper TypeScript type guards with correct predicate syntax (item is File)
  • Good JSDoc comments explaining each type's purpose
  • Tests are well-written and cover the type guards (4 tests, all passing)
  • All CI checks pass: pnpm test, pnpm lint, pnpm typecheck all succeed
  • Uses types instead of interfaces for better type inference (as mentioned in PR summary)

- Add installation section
- Remove redundant isDirectory from File type
- Change children to Record for O(1) lookup
- Remove path from FileSystemNode (derived from tree)
- Fix API consistency (all operations return Result)
- Fix timestamps to use Date consistently
- Clarify Lock permissions with defaults
- Add Planned Features section
- Update Design Principles

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
### FileSystemNode

```typescript
type FileSystemNode = {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a significant mismatch between the code and the documentation. The documentation claims:

  • path is NOT stored in FileSystemNode (derived from tree structure)
  • children is Record<string, FileSystemItem> for O(1) lookup
  • Helper functions file() and folder() exist

But the actual code has:

  • path: string in FileSystemNode (line 8)
  • children: FileSystemItem[] (line 28)
  • No file() or folder() helper functions exported

* Virtual file system interface.
* This defines the API contract for implementing a virtual file system.
*/
export interface FileSystem {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FileSystem interface methods return different types inconsistently:

  • mkdir() returns Directory (not Result)
  • touch() returns File (not Result)
  • rm() returns void (not Result)
  • read() returns string | null (not Result)
  • write() returns File (not Result)

But the documentation claims "All operations use the @deessejs/core Result monad for error handling". Either update the code to use Result types or update the documentation to reflect the actual API.

export interface FileSystem {
root: Directory;
exists(path: string): boolean;
get(path: string): FileSystemItem | null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The get() method returns FileSystemItem | null which requires null checking. Consider using Maybe<FileSystemItem> from @deessejs/core for consistency with the documented error handling pattern.

*/
export type Directory = FileSystemNode & {
isDirectory: true;
children: FileSystemItem[];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The children type is an array (FileSystemItem[]), but the documentation (line 72) states it should be Record<string, FileSystemItem> for O(1) lookup. Consider using Record for better performance.

/**
* File type representing a file in the virtual file system.
*/
export type File = FileSystemNode & {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The isDirectory: false here is redundant since FileSystemNode already has isDirectory: boolean. However, this pattern allows the type guard to work properly, so it's a necessary TypeScript pattern for discrimination. Consider adding a comment explaining this for future maintainers.

@@ -0,0 +1,58 @@
import { describe, it, expect } from "vitest";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests only cover type guards (isFile, isDirectory). No tests exist for the FileSystem interface since it's only an interface definition at this point. Consider adding tests when the implementation is added.

Copy link
Copy Markdown

@marty-action marty-action bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This PR defines core TypeScript types for a virtual file system including File, Directory, FileSystemNode, FileSystemItem, and a FileSystem interface. It also includes type guards (isFile, isDirectory) and comprehensive documentation.

Critical Issues

  1. Documentation Mismatch: The docs/README.md claims features that don't exist in the code:

    • path is NOT stored (derived from tree) - but code has path: string
    • children is Record<string, FileSystemItem> - but code has FileSystemItem[]
    • Helper functions file() and folder() don't exist
    • FileSystem methods should return Result monads but they return raw values
  2. FileSystem Interface Inconsistency: Methods return different types inconsistently - some return raw values, some return null, contradicting the documented error handling pattern using Result monads.

Recommendations

  1. Update the code to match the documentation (add helper functions, use Record for children, return Result types), OR

  2. Update the documentation to match the current code (remove mentions of Result monads, helper functions, Record children)

  3. Missing Test Coverage: No tests for the FileSystem interface (which is expected since it's only an interface at this stage)

  4. API Design Decision: Consider whether get() should return Maybe<FileSystemItem> instead of FileSystemItem | null for consistency with the monad-based error handling.

Positive Notes

  • Good use of intersection types for File and Directory extending FileSystemNode
  • Type guards are properly implemented with type predicates (item is File)
  • JSDoc comments are present and helpful
  • Test coverage for type guards is good with both positive and negative cases
  • Separation of concerns is clear (types, exports, tests organized properly)
  • Good use of the SizeInBytes type alias for semantic clarity

Test Plan Verification

Since the build tools (turbo, pnpm) are not available in this environment, I couldn't run pnpm test, pnpm lint, or pnpm typecheck. Please ensure these pass locally before merging.

- Add Safety & Policies section:
  - Overwrite policy (fail/replace/merge)
  - Lock inheritance (bubble-up checking)
  - Circular reference prevention
  - Memory management
- Add binary support (Uint8Array) to File type
- Add mkdir recursive option
- Add OverwriteMode to copy/move operations
- Add ShellResult type with stdout/stderr/exitCode
- Add parent_locked error type
- Update Design Principles with Fail by Default and Safety First
- Add more planned features (JSON export, async, streaming)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
### FileSystemNode

```typescript
type FileSystemNode = {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation shows path as "derived from the tree structure, not stored in each node" but the actual type definition includes path: string as a required property. This is a mismatch that should be resolved.

### Directory

```typescript
type Directory = FileSystemNode & {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs show Record<string, FileSystemItem> for children but the actual implementation uses FileSystemItem[] (array). These should be consistent.

### File

```typescript
type File = FileSystemNode & {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs mention Uint8Array support for binary files, but the current implementation only supports content: string. Either update the type or update the documentation.

get(path: string): FileSystemItem | null;
mkdir(path: string): Directory;
touch(path: string, content?: string): File;
rm(path: string): void;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rm method returns void which doesn't allow for error handling. Consider returning a type that can indicate success/failure (e.g., boolean or a Result type) for better error handling.

- Fix lock inheritance description (Inherited Protection)
- Remove unimplemented redirection in Shell example
- Clarify OverwriteMode merge behavior
- Add readText/readBinary/read methods for binary support
- Add atomic operations to planned features

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
*/
export type FileSystemNode = {
name: string;
path: string;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding validation or documenting the expected path format. Should paths be absolute (e.g., /home/user/file.txt) or relative?

*/
export type Directory = FileSystemNode & {
isDirectory: true;
children: FileSystemItem[];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using ReadonlyArray<FileSystemItem> to prevent mutation, or add documentation about immutability expectations.

root: Directory;
exists(path: string): boolean;
get(path: string): FileSystemItem | null;
mkdir(path: string): Directory;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider clarifying behavior: Does this create parent directories (like mkdir -p) or require all parents to exist?

get(path: string): FileSystemItem | null;
mkdir(path: string): Directory;
touch(path: string, content?: string): File;
rm(path: string): void;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider clarifying: Does this support recursive deletion for directories? What happens if the path doesn't exist - throw error or no-op?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant