UCD.js Docs
FS BridgeSpecification

Overview

Complete specification for the FileSystemBridge interface

FS Bridge Specification

This document describes the complete specification for the FileSystemBridge interface, including all operations, capabilities, hooks, and types.

FileSystemBridge Interface

The FileSystemBridge interface is the core contract that all bridge implementations must satisfy. It extends the file system operations and adds metadata, capabilities tracking, and hooks.

interface FileSystemBridge extends FileSystemBridgeOperations {
  /**
   * The capabilities of this file system bridge.
   */
  optionalCapabilities: HasOptionalCapabilityMap;

  /**
   * Metadata about this file system bridge.
   */
  meta: FileSystemBridgeMetadata;

  /**
   * Hook system for listening to file system events.
   */
  hook: HookableCore<FileSystemBridgeHooks>["hook"];
}

Required Operations

All bridges must implement these three operations:

read

Reads the contents of a file as a string.

read(path: string): Promise<string>

Parameters:

  • path (string): The path to the file to read

Returns: Promise that resolves to the file contents as a string

Example:

const content = await bridge.read("file.txt");

exists

Checks if a file or directory exists.

exists(path: string): Promise<boolean>

Parameters:

  • path (string): The path to check for existence

Returns: Promise that resolves to true if the path exists, false otherwise

Example:

const exists = await bridge.exists("file.txt");
if (exists) {
  // File exists
}

listdir

Lists the contents of a directory.

listdir(path: string, recursive?: boolean): Promise<FSEntry[]>

Parameters:

  • path (string): The path to the directory to list
  • recursive (boolean, optional): If true, lists files in subdirectories as well. Defaults to false.

Returns: Promise that resolves to an array of FSEntry objects

Example:

// List top-level contents
const entries = await bridge.listdir("directory");

// List recursively
const allEntries = await bridge.listdir("directory", true);

Optional Operations

Bridges may optionally implement these operations. If not implemented, attempting to use them will throw a BridgeUnsupportedOperation error.

write

Writes data to a file.

write(path: string, data: string | Uint8Array, encoding?: BufferEncoding): Promise<void>

Parameters:

  • path (string): The path to the file to write
  • data (string | Uint8Array): The data to write to the file
  • encoding (BufferEncoding, optional): Encoding for string data. Defaults to 'utf8'.

Returns: Promise that resolves when the write operation is complete

Example:

await bridge.write("file.txt", "Hello World");
await bridge.write("file.bin", new Uint8Array([1, 2, 3]));

mkdir

Creates a directory.

mkdir(path: string): Promise<void>

Parameters:

  • path (string): The path of the directory to create

Returns: Promise that resolves when the directory is created

Example:

await bridge.mkdir("new/directory");

rm

Removes a file or directory.

rm(path: string, options?: FileSystemBridgeRmOptions): Promise<void>

Parameters:

  • path (string): The path to remove
  • options (FileSystemBridgeRmOptions, optional): Configuration for removal
    • recursive (boolean): If true, removes directories and their contents recursively
    • force (boolean): If true, ignores errors if the path doesn't exist

Returns: Promise that resolves when the removal is complete

Example:

// Remove a file
await bridge.rm("file.txt");

// Remove a directory recursively
await bridge.rm("directory", { recursive: true, force: true });

Capabilities System

Bridges automatically detect which optional operations they support. For complete documentation on checking and asserting capabilities, see the Capabilities documentation.

Hooks System

Bridges provide a hook system for listening to file system events. The hook property on a bridge instance allows you to register listeners for operation events.

interface FileSystemBridge {
  // ...
  hook: HookableCore<FileSystemBridgeHooks>["hook"];
}

All bridges support hooks for monitoring, debugging, and logging. For complete documentation on available hooks, payload types, and usage examples, see the Hooks documentation.

FileSystemBridgeObject

When defining a custom bridge, you provide a FileSystemBridgeObject:

interface FileSystemBridgeObject<
  TOptionsSchema extends z.ZodType = z.ZodNever,
  TState extends Record<string, unknown> = Record<string, unknown>,
> {
  /**
   * Metadata about the file system bridge
   */
  meta: FileSystemBridgeMetadata;

  /**
   * Zod schema for validating bridge options
   */
  optionsSchema?: TOptionsSchema;

  /**
   * Optional state object for the file system bridge
   */
  state?: TState;

  /**
   * Setup function that receives options and state
   * and returns the filesystem operations implementation
   */
  setup: FileSystemBridgeSetupFn<TOptionsSchema, TState>;
}

Metadata

interface FileSystemBridgeMetadata {
  name: string;
  description: string;
}

Setup Function

The setup function receives a context object and returns the operations:

type FileSystemBridgeSetupFn<
  TOptionsSchema extends z.ZodType,
  TState extends Record<string, unknown> = Record<string, unknown>,
> = (ctx: {
  options: z.infer<TOptionsSchema>;
  state: TState;
  resolveSafePath: ResolveSafePathFn;
}) => FileSystemBridgeOperations;

The resolveSafePath function helps prevent path traversal attacks by resolving paths relative to a base path safely.

defineFileSystemBridge

The defineFileSystemBridge function creates a bridge factory from a FileSystemBridgeObject:

function defineFileSystemBridge<
  TOptionsSchema extends z.ZodType = z.ZodNever,
  TState extends Record<string, unknown> = Record<string, unknown>,
>(
  fsBridge: FileSystemBridgeObject<TOptionsSchema, TState>,
): FileSystemBridgeFactory<TOptionsSchema>

Example:

const MyBridge = defineFileSystemBridge({
  meta: {
    name: "My Bridge",
    description: "Custom bridge implementation"
  },
  optionsSchema: z.object({
    basePath: z.string()
  }),
  setup({ options, resolveSafePath }) {
    return {
      async read(path) {
        const resolved = resolveSafePath(options.basePath, path);
        // ... implementation
      },
      // ... other operations
    };
  }
});

// Create an instance
const bridge = MyBridge({ basePath: "/path/to/base" });

FSEntry Type

The listdir operation returns an array of FSEntry objects:

type FSEntry =
  | {
      type: "file";
      name: string;
      path: string;
    }
  | {
      type: "directory";
      name: string;
      path: string;
      children: FSEntry[];
    };

Example:

const entries = await bridge.listdir("directory");
for (const entry of entries) {
  if (entry.type === "file") {
    console.log(`File: ${entry.name} at ${entry.path}`);
  } else {
    console.log(`Directory: ${entry.name} with ${entry.children.length} children`);
  }
}

Error Handling

The bridge system defines several error types for different error scenarios. For complete documentation on error types and error handling patterns, see the Errors documentation.

Type Safety

The bridge system is fully type-safe. TypeScript will:

  • Infer optional capabilities based on what operations are implemented
  • Narrow types when using hasCapability or assertCapability
  • Validate options using Zod schemas
  • Provide autocomplete for all operations and hooks

Best Practices

  1. Always use resolveSafePath: When implementing custom bridges, use the provided resolveSafePath function to prevent path traversal attacks.

  2. Check capabilities before use: Use hasCapability or assertCapability before calling optional operations.

  3. Handle errors gracefully: Bridge operations may throw errors. Always wrap calls in try-catch blocks or handle errors appropriately.

  4. Use hooks for debugging: The hook system is excellent for monitoring bridge operations during development.

  5. Validate options: Always provide an optionsSchema when defining bridges to ensure type-safe configuration.

On this page