Skip to content

Commit 702bb62

Browse files
authored
Merge pull request #7 from hyperweb-io/feat/modules
Feat/modules
2 parents 6a73ca8 + 20e89df commit 702bb62

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+7563
-5031
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,21 @@ jobs:
4646
fail-fast: false
4747
matrix:
4848
package:
49-
- komoji
49+
- appstash
5050
- fetch-api-client
5151
- find-pkg
5252
- http-errors
5353
- jsonld-tools
54+
- komoji
5455
- makage
5556
- nested-obj
5657
- node-api-client
5758
- schema-sdk
5859
- schema-typescript
5960
- strfy-js
6061
- yanse
62+
- inquirerer
63+
- create-gen-app
6164

6265
steps:
6366
- name: Checkout code

packages/appstash/README.md

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
# appstash
2+
3+
Simple, clean application directory resolution for Node.js applications.
4+
5+
## Installation
6+
7+
```bash
8+
npm install appstash
9+
# or
10+
pnpm add appstash
11+
```
12+
13+
## Features
14+
15+
- **Simple API**: Just one function to get all your app directories
16+
- **Clean structure**: `~/.<tool>/{config,cache,data,logs}` + `/tmp/<tool>`
17+
- **Graceful fallback**: XDG directories → tmp if home fails
18+
- **No throws**: Always returns valid paths, never throws errors
19+
- **TypeScript**: Full type definitions included
20+
- **Zero dependencies**: Pure Node.js implementation
21+
22+
## Usage
23+
24+
### Basic Usage
25+
26+
```typescript
27+
import { appstash } from 'appstash';
28+
29+
// Get directories for your tool
30+
const dirs = appstash('pgpm');
31+
32+
console.log(dirs.config); // ~/.pgpm/config
33+
console.log(dirs.cache); // ~/.pgpm/cache
34+
console.log(dirs.data); // ~/.pgpm/data
35+
console.log(dirs.logs); // ~/.pgpm/logs
36+
console.log(dirs.tmp); // /tmp/pgpm
37+
```
38+
39+
### Create Directories
40+
41+
```typescript
42+
import { appstash } from 'appstash';
43+
44+
// Get directories and create them
45+
const dirs = appstash('pgpm', { ensure: true });
46+
47+
// All directories now exist
48+
// dirs.usedFallback will be true if XDG or tmp fallback was used
49+
```
50+
51+
### Resolve Paths
52+
53+
```typescript
54+
import { appstash, resolve } from 'appstash';
55+
56+
const dirs = appstash('pgpm');
57+
58+
// Resolve paths within directories
59+
const configFile = resolve(dirs, 'config', 'settings.json');
60+
// Returns: ~/.pgpm/config/settings.json
61+
62+
const cacheDir = resolve(dirs, 'cache', 'repos', 'my-repo');
63+
// Returns: ~/.pgpm/cache/repos/my-repo
64+
```
65+
66+
### Manual Ensure
67+
68+
```typescript
69+
import { appstash, ensure } from 'appstash';
70+
71+
const dirs = appstash('pgpm');
72+
73+
// Create directories later
74+
const result = ensure(dirs);
75+
76+
console.log(result.created); // ['~/.pgpm', '~/.pgpm/config', ...]
77+
console.log(result.usedFallback); // false (or true if fallback was used)
78+
```
79+
80+
## API
81+
82+
### `appstash(tool, options?)`
83+
84+
Get application directories for a tool.
85+
86+
**Parameters:**
87+
- `tool` (string): Tool name (e.g., 'pgpm', 'lql')
88+
- `options` (object, optional):
89+
- `baseDir` (string): Base directory (defaults to `os.homedir()`)
90+
- `useXdgFallback` (boolean): Use XDG fallback if home fails (default: `true`)
91+
- `ensure` (boolean): Automatically create directories (default: `false`)
92+
- `tmpRoot` (string): Root for temp directory (defaults to `os.tmpdir()`)
93+
94+
**Returns:** `AppStashResult`
95+
```typescript
96+
{
97+
root: string; // ~/.<tool>
98+
config: string; // ~/.<tool>/config
99+
cache: string; // ~/.<tool>/cache
100+
data: string; // ~/.<tool>/data
101+
logs: string; // ~/.<tool>/logs
102+
tmp: string; // /tmp/<tool>
103+
usedFallback?: boolean; // true if XDG or tmp fallback was used
104+
}
105+
```
106+
107+
### `ensure(dirs)`
108+
109+
Create directories if they don't exist. Never throws.
110+
111+
**Parameters:**
112+
- `dirs` (AppStashResult): Directory paths from `appstash()`
113+
114+
**Returns:** `EnsureResult`
115+
```typescript
116+
{
117+
created: string[]; // Directories that were created
118+
usedFallback: boolean; // true if XDG or tmp fallback was used
119+
}
120+
```
121+
122+
### `resolve(dirs, kind, ...parts)`
123+
124+
Resolve a path within a specific directory.
125+
126+
**Parameters:**
127+
- `dirs` (AppStashResult): Directory paths from `appstash()`
128+
- `kind` ('config' | 'cache' | 'data' | 'logs' | 'tmp'): Directory kind
129+
- `parts` (string[]): Path parts to join
130+
131+
**Returns:** `string` - Resolved path
132+
133+
## Directory Structure
134+
135+
### Primary (POSIX-style)
136+
137+
```
138+
~/.<tool>/
139+
├── config/ # Configuration files
140+
├── cache/ # Cached data
141+
├── data/ # Application data
142+
└── logs/ # Log files
143+
144+
/tmp/<tool>/ # Temporary files
145+
```
146+
147+
### Fallback (XDG)
148+
149+
If home directory is unavailable or creation fails, falls back to XDG:
150+
151+
```
152+
~/.config/<tool>/ # Config
153+
~/.cache/<tool>/ # Cache
154+
~/.local/share/<tool>/ # Data
155+
~/.local/state/<tool>/logs/ # Logs
156+
```
157+
158+
### Final Fallback (tmp)
159+
160+
If XDG also fails, falls back to system temp:
161+
162+
```
163+
/tmp/<tool>/
164+
├── config/
165+
├── cache/
166+
├── data/
167+
└── logs/
168+
```
169+
170+
## Examples
171+
172+
### Configuration File
173+
174+
```typescript
175+
import { appstash, resolve } from 'appstash';
176+
import fs from 'fs';
177+
178+
const dirs = appstash('myapp', { ensure: true });
179+
const configPath = resolve(dirs, 'config', 'settings.json');
180+
181+
// Write config
182+
fs.writeFileSync(configPath, JSON.stringify({ theme: 'dark' }));
183+
184+
// Read config
185+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
186+
```
187+
188+
### Cache Management
189+
190+
```typescript
191+
import { appstash, resolve } from 'appstash';
192+
import fs from 'fs';
193+
194+
const dirs = appstash('myapp', { ensure: true });
195+
const cacheFile = resolve(dirs, 'cache', 'data.json');
196+
197+
// Check if cached
198+
if (fs.existsSync(cacheFile)) {
199+
const cached = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
200+
console.log('Using cached data:', cached);
201+
} else {
202+
// Fetch and cache
203+
const data = await fetchData();
204+
fs.writeFileSync(cacheFile, JSON.stringify(data));
205+
}
206+
```
207+
208+
### Logging
209+
210+
```typescript
211+
import { appstash, resolve } from 'appstash';
212+
import fs from 'fs';
213+
214+
const dirs = appstash('myapp', { ensure: true });
215+
const logFile = resolve(dirs, 'logs', 'app.log');
216+
217+
function log(message: string) {
218+
const timestamp = new Date().toISOString();
219+
fs.appendFileSync(logFile, `[${timestamp}] ${message}\n`);
220+
}
221+
222+
log('Application started');
223+
```
224+
225+
### Custom Base Directory
226+
227+
```typescript
228+
import { appstash } from 'appstash';
229+
230+
// Use a custom base directory
231+
const dirs = appstash('myapp', {
232+
baseDir: '/opt/myapp',
233+
ensure: true
234+
});
235+
236+
console.log(dirs.config); // /opt/myapp/.myapp/config
237+
```
238+
239+
## Design Philosophy
240+
241+
- **Simple**: One function, clear structure
242+
- **Clean**: No pollution of exports, minimal API surface
243+
- **Graceful**: Never throws, always returns valid paths
244+
- **Fallback**: XDG only as absolute fallback, not primary
245+
- **Focused**: Just directory resolution, no state management
246+
247+
## License
248+
249+
MIT
250+
251+
## Contributing
252+
253+
See the main [hyperweb-io/dev-utils repository](https://github.com/hyperweb-io/dev-utils) for contribution guidelines.

0 commit comments

Comments
 (0)