Package Exports
- appstash
- appstash/esm/index.js
- appstash/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (appstash) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
appstash
simple, clean application directory resolution
Application directory resolution for Node.js applications
Simple, clean application directory resolution for Node.js applications.
Installation
npm install appstashFeatures
- 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';
// Get directories for your tool
const dirs = appstash('pgpm');
console.log(dirs.config); // ~/.pgpm/config
console.log(dirs.cache); // ~/.pgpm/cache
console.log(dirs.data); // ~/.pgpm/data
console.log(dirs.logs); // ~/.pgpm/logs
console.log(dirs.tmp); // /tmp/pgpmCreate Directories
import { appstash } from 'appstash';
// Get directories and create them
const dirs = appstash('pgpm', { ensure: true });
// All directories now exist
// dirs.usedFallback will be true if XDG or tmp fallback was usedResolve Paths
import { appstash, resolve } from 'appstash';
const dirs = appstash('pgpm');
// Resolve paths within directories
const configFile = resolve(dirs, 'config', 'settings.json');
// Returns: ~/.pgpm/config/settings.json
const cacheDir = resolve(dirs, 'cache', 'repos', 'my-repo');
// Returns: ~/.pgpm/cache/repos/my-repoManual Ensure
import { appstash, ensure } from 'appstash';
const dirs = appstash('pgpm');
// Create directories later
const result = ensure(dirs);
console.log(result.created); // ['~/.pgpm', '~/.pgpm/config', ...]
console.log(result.usedFallback); // false (or true if fallback was used)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 toos.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 toos.tmpdir())
Returns: AppStashResult
{
root: string; // ~/.<tool>
config: string; // ~/.<tool>/config
cache: string; // ~/.<tool>/cache
data: string; // ~/.<tool>/data
logs: string; // ~/.<tool>/logs
tmp: string; // /tmp/<tool>
usedFallback?: boolean; // true if XDG or tmp fallback was used
}ensure(dirs)
Create directories if they don't exist. Never throws.
Parameters:
dirs(AppStashResult): Directory paths fromappstash()
Returns: EnsureResult
{
created: string[]; // Directories that were created
usedFallback: boolean; // true if XDG or tmp fallback was used
}resolve(dirs, kind, ...parts)
Resolve a path within a specific directory.
Parameters:
dirs(AppStashResult): Directory paths fromappstash()kind('config' | 'cache' | 'data' | 'logs' | 'tmp'): Directory kindparts(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 filesFallback (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/ # LogsFinal 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');
// Write config
fs.writeFileSync(configPath, JSON.stringify({ theme: 'dark' }));
// Read config
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');
// Check if cached
if (fs.existsSync(cacheFile)) {
const cached = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
console.log('Using cached data:', cached);
} else {
// Fetch and cache
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';
// Use a custom base directory
const dirs = appstash('myapp', {
baseDir: '/opt/myapp',
ensure: true
});
console.log(dirs.config); // /opt/myapp/.myapp/configDesign 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
- Clone the repository:
git clone https://github.com/constructive-io/dev-utils.git- Install dependencies:
cd dev-utils
pnpm install
pnpm build- Test the package of interest:
cd packages/<packagename>
pnpm test:watchCredits
🛠 Built by the Constructive team — creators of modular Postgres tooling for secure, composable backends. If you like our work, contribute on GitHub.
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.