appstash
Simple, clean application directory resolution for Node.js applications.
Installation
npm install appstash
Features
- Simple API: Just one function to get all your app directories
- Clean structure:
~/.<tool>/{config,cache,data,logs} + /tmp/<tool>
- Graceful fallback: XDG directories → tmp if home fails
- No throws: Always returns valid paths, never throws errors
- TypeScript: Full type definitions included
- Zero dependencies: Pure Node.js implementation
Usage
Basic Usage
import { appstash } from 'appstash';
const dirs = appstash('pgpm');
console.log(dirs.config);
console.log(dirs.cache);
console.log(dirs.data);
console.log(dirs.logs);
console.log(dirs.tmp);
Create Directories
import { appstash } from 'appstash';
const dirs = appstash('pgpm', { ensure: true });
Resolve Paths
import { appstash, resolve } from 'appstash';
const dirs = appstash('pgpm');
const configFile = resolve(dirs, 'config', 'settings.json');
const cacheDir = resolve(dirs, 'cache', 'repos', 'my-repo');
Manual Ensure
import { appstash, ensure } from 'appstash';
const dirs = appstash('pgpm');
const result = ensure(dirs);
console.log(result.created);
console.log(result.usedFallback);
API
appstash(tool, options?)
Get application directories for a tool.
Parameters:
tool (string): Tool name (e.g., 'pgpm', 'lql')
options (object, optional):
baseDir (string): Base directory (defaults to os.homedir())
useXdgFallback (boolean): Use XDG fallback if home fails (default: true)
ensure (boolean): Automatically create directories (default: false)
tmpRoot (string): Root for temp directory (defaults to os.tmpdir())
Returns: AppStashResult
{
root: string;
config: string;
cache: string;
data: string;
logs: string;
tmp: string;
usedFallback?: boolean;
}
ensure(dirs)
Create directories if they don't exist. Never throws.
Parameters:
dirs (AppStashResult): Directory paths from appstash()
Returns: EnsureResult
{
created: string[];
usedFallback: boolean;
}
resolve(dirs, kind, ...parts)
Resolve a path within a specific directory.
Parameters:
dirs (AppStashResult): Directory paths from appstash()
kind ('config' | 'cache' | 'data' | 'logs' | 'tmp'): Directory kind
parts (string[]): Path parts to join
Returns: string - Resolved path
Directory Structure
Primary (POSIX-style)
~/.<tool>/
├── config/ # Configuration files
├── cache/ # Cached data
├── data/ # Application data
└── logs/ # Log files
/tmp/<tool>/ # Temporary files
Fallback (XDG)
If home directory is unavailable or creation fails, falls back to XDG:
~/.config/<tool>/ # Config
~/.cache/<tool>/ # Cache
~/.local/share/<tool>/ # Data
~/.local/state/<tool>/logs/ # Logs
Final Fallback (tmp)
If XDG also fails, falls back to system temp:
/tmp/<tool>/
├── config/
├── cache/
├── data/
└── logs/
Examples
Configuration File
import { appstash, resolve } from 'appstash';
import fs from 'fs';
const dirs = appstash('myapp', { ensure: true });
const configPath = resolve(dirs, 'config', 'settings.json');
fs.writeFileSync(configPath, JSON.stringify({ theme: 'dark' }));
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
Cache Management
import { appstash, resolve } from 'appstash';
import fs from 'fs';
const dirs = appstash('myapp', { ensure: true });
const cacheFile = resolve(dirs, 'cache', 'data.json');
if (fs.existsSync(cacheFile)) {
const cached = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
console.log('Using cached data:', cached);
} else {
const data = await fetchData();
fs.writeFileSync(cacheFile, JSON.stringify(data));
}
Logging
import { appstash, resolve } from 'appstash';
import fs from 'fs';
const dirs = appstash('myapp', { ensure: true });
const logFile = resolve(dirs, 'logs', 'app.log');
function log(message: string) {
const timestamp = new Date().toISOString();
fs.appendFileSync(logFile, `[${timestamp}] ${message}\n`);
}
log('Application started');
Custom Base Directory
import { appstash } from 'appstash';
const dirs = appstash('myapp', {
baseDir: '/opt/myapp',
ensure: true
});
console.log(dirs.config);
Design Philosophy
- Simple: One function, clear structure
- Clean: No pollution of exports, minimal API surface
- Graceful: Never throws, always returns valid paths
- Fallback: XDG only as absolute fallback, not primary
- Focused: Just directory resolution, no state management
Development
Setup
git clone https://github.com/hyperweb-io/dev-utils.git
cd dev-utils
pnpm install
pnpm build
- Test the package of interest:
cd packages/<packagename>
pnpm test:watch
Credits
Built for developers, with developers.
👉 https://launchql.com | https://hyperweb.io
Disclaimer
AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.