Skip to content

Latest commit

 

History

History
260 lines (191 loc) · 6.33 KB

File metadata and controls

260 lines (191 loc) · 6.33 KB

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 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';

// 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/pgpm

Create 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 used

Resolve 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-repo

Manual 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 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;        // ~/.<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 from appstash()

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 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');

// 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/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