2025-08-26 12:32:21 -07:00
|
|
|
/**
|
|
|
|
|
* Universal File System implementation
|
2025-09-15 14:53:59 -07:00
|
|
|
* Framework-friendly: Trusts that frameworks provide fs polyfills
|
|
|
|
|
* Works in all environments: Browser (via framework), Node.js, Serverless
|
2025-08-26 12:32:21 -07:00
|
|
|
*/
|
|
|
|
|
|
2025-09-15 14:53:59 -07:00
|
|
|
import { isNode } from '../utils/environment.js'
|
2025-08-26 12:32:21 -07:00
|
|
|
|
|
|
|
|
let nodeFs: any = null
|
|
|
|
|
|
|
|
|
|
// Dynamic import for Node.js fs (only in Node.js environment)
|
|
|
|
|
if (isNode()) {
|
|
|
|
|
try {
|
feat: add node: protocol to all Node.js built-in imports for bundler compatibility
- Updated all fs, path, crypto, os, url, util, events, http, https, net, child_process, stream, and zlib imports
- Changed both static imports and dynamic imports to use node: protocol
- This makes Brainy more bundler-friendly by explicitly marking Node.js built-ins
- Prevents bundlers from attempting to polyfill or bundle these modules
- Reduces bundle size for web applications using Brainy
- Improves tree-shaking and dead code elimination
Benefits for external bundlers:
- Clear distinction between Node.js built-ins and external dependencies
- No ambiguity about what needs polyfilling
- Smaller bundles for browser builds
- Better compatibility with modern bundlers (Webpack 5, Vite, Rollup, esbuild)
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-17 14:20:21 -07:00
|
|
|
// Use node: protocol to prevent bundler polyfilling (requires Node 22+)
|
2025-09-17 14:34:24 -07:00
|
|
|
// Import main module and access promises to avoid subpath issues with bundlers
|
|
|
|
|
const fs = await import('node:fs')
|
|
|
|
|
nodeFs = fs.promises
|
2025-08-26 12:32:21 -07:00
|
|
|
} catch {
|
|
|
|
|
// Ignore import errors in non-Node environments
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Universal file operations interface
|
|
|
|
|
*/
|
|
|
|
|
export interface UniversalFS {
|
|
|
|
|
readFile(path: string, encoding?: string): Promise<string>
|
|
|
|
|
writeFile(path: string, data: string, encoding?: string): Promise<void>
|
|
|
|
|
mkdir(path: string, options?: { recursive?: boolean }): Promise<void>
|
|
|
|
|
exists(path: string): Promise<boolean>
|
|
|
|
|
readdir(path: string): Promise<string[]>
|
|
|
|
|
readdir(path: string, options: { withFileTypes: true }): Promise<{ name: string, isDirectory(): boolean, isFile(): boolean }[]>
|
|
|
|
|
unlink(path: string): Promise<void>
|
|
|
|
|
stat(path: string): Promise<{ isFile(): boolean, isDirectory(): boolean }>
|
|
|
|
|
access(path: string, mode?: number): Promise<void>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Node.js implementation using fs/promises
|
|
|
|
|
*/
|
|
|
|
|
class NodeFS implements UniversalFS {
|
|
|
|
|
async readFile(path: string, encoding = 'utf-8'): Promise<string> {
|
|
|
|
|
return await nodeFs.readFile(path, encoding)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async writeFile(path: string, data: string, encoding = 'utf-8'): Promise<void> {
|
|
|
|
|
await nodeFs.writeFile(path, data, encoding)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async mkdir(path: string, options = { recursive: true }): Promise<void> {
|
|
|
|
|
await nodeFs.mkdir(path, options)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async exists(path: string): Promise<boolean> {
|
|
|
|
|
try {
|
|
|
|
|
await nodeFs.access(path)
|
|
|
|
|
return true
|
|
|
|
|
} catch {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async readdir(path: string): Promise<string[]>
|
|
|
|
|
async readdir(path: string, options: { withFileTypes: true }): Promise<{ name: string, isDirectory(): boolean, isFile(): boolean }[]>
|
|
|
|
|
async readdir(path: string, options?: { withFileTypes?: boolean }): Promise<string[] | { name: string, isDirectory(): boolean, isFile(): boolean }[]> {
|
|
|
|
|
if (options?.withFileTypes) {
|
|
|
|
|
return await nodeFs.readdir(path, { withFileTypes: true })
|
|
|
|
|
}
|
|
|
|
|
return await nodeFs.readdir(path)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async unlink(path: string): Promise<void> {
|
|
|
|
|
await nodeFs.unlink(path)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async stat(path: string): Promise<{ isFile(): boolean, isDirectory(): boolean }> {
|
|
|
|
|
const stats = await nodeFs.stat(path)
|
|
|
|
|
return {
|
|
|
|
|
isFile: () => stats.isFile(),
|
|
|
|
|
isDirectory: () => stats.isDirectory()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async access(path: string, mode?: number): Promise<void> {
|
|
|
|
|
await nodeFs.access(path, mode)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-17 16:22:02 -07:00
|
|
|
// Browser-safe no-op implementation
|
|
|
|
|
class BrowserFS implements UniversalFS {
|
|
|
|
|
async readFile(path: string, encoding = 'utf-8'): Promise<string> {
|
|
|
|
|
throw new Error('File system operations not available in browser. Use OPFS, Memory, or S3 storage adapters instead.')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async writeFile(path: string, data: string, encoding = 'utf-8'): Promise<void> {
|
|
|
|
|
throw new Error('File system operations not available in browser. Use OPFS, Memory, or S3 storage adapters instead.')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async mkdir(path: string, options = { recursive: true }): Promise<void> {
|
|
|
|
|
throw new Error('File system operations not available in browser. Use OPFS, Memory, or S3 storage adapters instead.')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async exists(path: string): Promise<boolean> {
|
|
|
|
|
return false // Always return false in browser
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async readdir(path: string): Promise<string[]>
|
|
|
|
|
async readdir(path: string, options: { withFileTypes: true }): Promise<{ name: string, isDirectory(): boolean, isFile(): boolean }[]>
|
|
|
|
|
async readdir(path: string, options?: { withFileTypes?: boolean }): Promise<string[] | { name: string, isDirectory(): boolean, isFile(): boolean }[]> {
|
|
|
|
|
if (options?.withFileTypes) {
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async unlink(path: string): Promise<void> {
|
|
|
|
|
throw new Error('File system operations not available in browser. Use OPFS, Memory, or S3 storage adapters instead.')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async stat(path: string): Promise<{ isFile(): boolean, isDirectory(): boolean }> {
|
|
|
|
|
throw new Error('File system operations not available in browser. Use OPFS, Memory, or S3 storage adapters instead.')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async access(path: string, mode?: number): Promise<void> {
|
|
|
|
|
throw new Error('File system operations not available in browser. Use OPFS, Memory, or S3 storage adapters instead.')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-26 12:32:21 -07:00
|
|
|
// Create the appropriate filesystem implementation
|
|
|
|
|
let fsImpl: UniversalFS
|
|
|
|
|
|
2025-09-15 14:53:59 -07:00
|
|
|
if (isNode() && nodeFs) {
|
2025-08-26 12:32:21 -07:00
|
|
|
fsImpl = new NodeFS()
|
|
|
|
|
} else {
|
2025-09-17 16:22:02 -07:00
|
|
|
// Use browser-safe no-op implementation instead of throwing
|
|
|
|
|
fsImpl = new BrowserFS()
|
2025-08-26 12:32:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Export the filesystem operations
|
|
|
|
|
export const readFile = fsImpl.readFile.bind(fsImpl)
|
|
|
|
|
export const writeFile = fsImpl.writeFile.bind(fsImpl)
|
|
|
|
|
export const mkdir = fsImpl.mkdir.bind(fsImpl)
|
|
|
|
|
export const exists = fsImpl.exists.bind(fsImpl)
|
|
|
|
|
export const readdir = fsImpl.readdir.bind(fsImpl)
|
|
|
|
|
export const unlink = fsImpl.unlink.bind(fsImpl)
|
|
|
|
|
export const stat = fsImpl.stat.bind(fsImpl)
|
|
|
|
|
export const access = fsImpl.access.bind(fsImpl)
|
|
|
|
|
|
|
|
|
|
// Default export with promises namespace compatibility
|
|
|
|
|
export default {
|
|
|
|
|
readFile,
|
|
|
|
|
writeFile,
|
|
|
|
|
mkdir,
|
|
|
|
|
exists,
|
|
|
|
|
readdir,
|
|
|
|
|
unlink,
|
|
|
|
|
stat,
|
|
|
|
|
access
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Named export for fs/promises compatibility
|
|
|
|
|
export const promises = {
|
|
|
|
|
readFile,
|
|
|
|
|
writeFile,
|
|
|
|
|
mkdir,
|
|
|
|
|
exists,
|
|
|
|
|
readdir,
|
|
|
|
|
unlink,
|
|
|
|
|
stat,
|
|
|
|
|
access
|
|
|
|
|
}
|