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+)
nodeFs = await import ( 'node: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 )
}
}
// 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-15 14:53:59 -07:00
throw new Error ( 'File system operations not available. Framework bundlers should provide fs polyfills or use storage adapters like OPFS, Memory, or S3.' )
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
}