@adriangalilea/utils 0.0.10 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/offensive.d.ts +61 -39
- package/dist/offensive.d.ts.map +1 -1
- package/dist/offensive.js +72 -64
- package/dist/offensive.js.map +1 -1
- package/dist/platform/dir.d.ts +7 -7
- package/dist/platform/dir.d.ts.map +1 -1
- package/dist/platform/dir.js +18 -55
- package/dist/platform/dir.js.map +1 -1
- package/dist/platform/file.d.ts +11 -15
- package/dist/platform/file.d.ts.map +1 -1
- package/dist/platform/file.js +48 -108
- package/dist/platform/file.js.map +1 -1
- package/dist/platform/xdg.d.ts +8 -0
- package/dist/platform/xdg.d.ts.map +1 -0
- package/dist/platform/xdg.js +12 -0
- package/dist/platform/xdg.js.map +1 -0
- package/package.json +14 -13
package/README.md
CHANGED
|
@@ -5,13 +5,11 @@ TypeScript utilities - logger, currency, offensive programming, file operations,
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
9
|
-
pnpm dlx jsr add @adriangalilea/utils
|
|
10
|
-
|
|
11
|
-
# or with npm
|
|
12
|
-
npx jsr add @adriangalilea/utils
|
|
8
|
+
pnpm add @adriangalilea/utils
|
|
13
9
|
```
|
|
14
10
|
|
|
11
|
+
Also available on [JSR](https://jsr.io/@adriangalilea/utils) but the JSR publish pipeline is not automated — versions may lag behind npm.
|
|
12
|
+
|
|
15
13
|
## Usage
|
|
16
14
|
|
|
17
15
|
### Logger
|
|
@@ -79,21 +77,123 @@ currency.percentToBasisPoints(1) // 100
|
|
|
79
77
|
currency.formatBasisPoints(50) // "50 bps"
|
|
80
78
|
```
|
|
81
79
|
|
|
80
|
+
### Format
|
|
81
|
+
|
|
82
|
+
Number and currency formatting utilities:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { format } from '@adriangalilea/utils/format'
|
|
86
|
+
|
|
87
|
+
// Number formatting
|
|
88
|
+
format.number(1234.567, 2) // "1234.57"
|
|
89
|
+
format.withCommas(1234567) // "1,234,567"
|
|
90
|
+
format.withCommas(1234.567, 2) // "1,234.57"
|
|
91
|
+
|
|
92
|
+
// Compact notation
|
|
93
|
+
format.compact(1234567) // "1.2M"
|
|
94
|
+
format.compact(1234) // "1.2K"
|
|
95
|
+
|
|
96
|
+
// Currency formatting
|
|
97
|
+
format.usd(1234.56) // "$1,234.56"
|
|
98
|
+
format.btc(0.00123456) // "0.001235 ₿"
|
|
99
|
+
format.eth(1.23456789) // "1.234568 Ξ"
|
|
100
|
+
format.auto(100, 'EUR') // "€100.00"
|
|
101
|
+
|
|
102
|
+
// Percentages
|
|
103
|
+
format.percentage(12.3456) // "12.3%"
|
|
104
|
+
format.percentage(0.05) // "0.05%"
|
|
105
|
+
format.percentage(123.456) // "123%"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Offensive Programming
|
|
109
|
+
|
|
110
|
+
Fail loud, fail fast. All primitives throw `Panic` — an uncaught `Panic` crashes the process with a full stack trace. Zero dependencies, works identically in Node, Deno, Bun, and browsers.
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { assert, panic, must, unwrap, Panic } from '@adriangalilea/utils'
|
|
114
|
+
|
|
115
|
+
// Assert invariants — narrows types
|
|
116
|
+
assert(port > 0 && port < 65536, 'invalid port:', port)
|
|
117
|
+
|
|
118
|
+
// Impossible state
|
|
119
|
+
switch (state) {
|
|
120
|
+
case 'ready': handleReady(); break
|
|
121
|
+
default: panic('impossible state:', state)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Unwrap operations that shouldn't fail (sync + async)
|
|
125
|
+
const data = must(() => JSON.parse(staticJsonString))
|
|
126
|
+
const file = must(() => readFileSync(path))
|
|
127
|
+
const resp = await must(() => fetch(url))
|
|
128
|
+
|
|
129
|
+
// Unwrap nullable values — type narrows T | null | undefined → T in one expression
|
|
130
|
+
// (assert needs two statements, unwrap does it inline)
|
|
131
|
+
const user = unwrap(db.findUser(id), 'user not found:', id)
|
|
132
|
+
const el = unwrap(document.getElementById('app'))
|
|
133
|
+
|
|
134
|
+
// must() replaces try/catch boilerplate:
|
|
135
|
+
// try { return readFileSync(path, 'utf-8') }
|
|
136
|
+
// catch (err) { check(err) }
|
|
137
|
+
// becomes:
|
|
138
|
+
return must(() => readFileSync(path, 'utf-8'))
|
|
139
|
+
|
|
140
|
+
// Panic is a distinct error class — distinguishes bugs from runtime errors
|
|
141
|
+
// In a server: let Panics crash, handle everything else
|
|
142
|
+
app.use((err, req, res, next) => {
|
|
143
|
+
if (err instanceof Panic) throw err // bug, re-throw, let it crash
|
|
144
|
+
res.status(500).json({ error: 'internal error' })
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// In tests: assert that code panics
|
|
148
|
+
expect(() => assert(false, 'boom')).toThrow(Panic)
|
|
149
|
+
```
|
|
150
|
+
|
|
82
151
|
## Features
|
|
83
152
|
|
|
84
153
|
- **Logger**: Next.js-style colored console output with symbols
|
|
85
|
-
- **Currency**:
|
|
154
|
+
- **Currency**:
|
|
86
155
|
- 13,750+ crypto symbols from CoinGecko (auto-updatable)
|
|
87
156
|
- Alternative ticker support (XBT→BTC, wrapped tokens, etc.)
|
|
88
157
|
- Optimal decimal calculations
|
|
89
158
|
- Percentage and basis point utilities
|
|
90
159
|
- Fiat and stablecoin detection
|
|
91
|
-
- **
|
|
160
|
+
- **Format**: Number and currency formatting with compact notation
|
|
161
|
+
- **Offensive Programming**: assert, panic, must, unwrap — all throw `Panic` with full stack traces
|
|
92
162
|
- **File Operations**: Read, write with automatic path resolution
|
|
93
163
|
- **Directory Operations**: Create, list, walk directories
|
|
94
164
|
- **KEV**: Redis-style environment variable management with monorepo support
|
|
165
|
+
- **XDG**: XDG Base Directory paths — reads env vars set by [xdg-dirs](https://github.com/adriangalilea/xdg-dirs), falls back to spec defaults
|
|
95
166
|
- **Project Discovery**: Find project/monorepo roots, detect JS/TS projects
|
|
96
167
|
|
|
168
|
+
### XDG Base Directories
|
|
169
|
+
|
|
170
|
+
XDG paths that respect env vars from [xdg-dirs](https://github.com/adriangalilea/xdg-dirs) with spec-compliant fallbacks:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { xdg, dir } from '@adriangalilea/utils'
|
|
174
|
+
|
|
175
|
+
xdg.state('notify') // ~/.local/state/notify
|
|
176
|
+
xdg.state('notify', 'watchers.json') // ~/.local/state/notify/watchers.json
|
|
177
|
+
xdg.config('myapp') // ~/.config/myapp
|
|
178
|
+
xdg.data('myapp') // ~/.local/share/myapp
|
|
179
|
+
xdg.cache('myapp') // ~/.cache/myapp
|
|
180
|
+
xdg.runtime('myapp') // $XDG_RUNTIME_DIR/myapp
|
|
181
|
+
|
|
182
|
+
// Ensure the directory exists before writing
|
|
183
|
+
dir.create(xdg.state('notify'))
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Release
|
|
187
|
+
|
|
188
|
+
Bump version in `package.json` (and `jsr.json`), push to `main`. CI handles everything:
|
|
189
|
+
|
|
190
|
+
1. Type-check, lint, build
|
|
191
|
+
2. Publish to npm via [OIDC trusted publishing](https://docs.npmjs.com/generating-provenance-statements) (no tokens — GitHub Actions proves identity directly to npm)
|
|
192
|
+
3. Create git tag `vX.Y.Z`
|
|
193
|
+
4. Generate changelog via [git-cliff](https://github.com/orhun/git-cliff) and create GitHub release
|
|
194
|
+
|
|
195
|
+
JSR publishing is not automated — see `jsr.json` TODO.
|
|
196
|
+
|
|
97
197
|
## License
|
|
98
198
|
|
|
99
199
|
MIT
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAGvD,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,+BAA+B,CAAA;AAG7C,cAAc,gBAAgB,CAAA;AAI9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,mBAAmB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAGvD,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,+BAA+B,CAAA;AAG7C,cAAc,gBAAgB,CAAA;AAI9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ export { runtime } from './runtime.js';
|
|
|
8
8
|
export * from './universal/log.js';
|
|
9
9
|
export * from './universal/format.js';
|
|
10
10
|
export * from './universal/currency/index.js';
|
|
11
|
-
// Offensive programming -
|
|
11
|
+
// Offensive programming - pure throws, zero dependencies
|
|
12
12
|
export * from './offensive.js';
|
|
13
13
|
// Platform-specific utilities
|
|
14
14
|
// These will throw helpful errors in browser environment when used
|
|
@@ -17,4 +17,5 @@ export * from './platform/dir.js';
|
|
|
17
17
|
export * from './platform/path.js';
|
|
18
18
|
export * from './platform/project.js';
|
|
19
19
|
export * from './platform/kev.js';
|
|
20
|
+
export * from './platform/xdg.js';
|
|
20
21
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,8CAA8C;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAGtC,wCAAwC;AACxC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,+BAA+B,CAAA;AAE7C,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,8CAA8C;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAGtC,wCAAwC;AACxC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,+BAA+B,CAAA;AAE7C,yDAAyD;AACzD,cAAc,gBAAgB,CAAA;AAE9B,8BAA8B;AAC9B,mEAAmE;AACnE,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA"}
|
package/dist/offensive.d.ts
CHANGED
|
@@ -3,58 +3,55 @@
|
|
|
3
3
|
*
|
|
4
4
|
* "A confused program SHOULD scream" - John Carmack
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Throw, always. An uncaught Panic crashes the process with a full stack trace.
|
|
7
|
+
* Zero dependencies. Works identically in Node, Deno, Bun, and browsers.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
9
|
+
* Four primitives:
|
|
10
|
+
* assert(cond, ...msg) - invariant checking, narrows types
|
|
11
|
+
* panic(...msg) - impossible state reached
|
|
12
|
+
* must(() => expr) - unwrap-or-die for operations (sync + async)
|
|
13
|
+
* unwrap(value, ...msg) - unwrap nullable values
|
|
10
14
|
*
|
|
11
|
-
*
|
|
12
|
-
* We don't recover - we crash.
|
|
13
|
-
* We don't validate and continue - we assert and panic.
|
|
15
|
+
* must() replaces the old try/catch/check pattern:
|
|
14
16
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* // before: 5 lines
|
|
18
|
+
* try {
|
|
19
|
+
* const data = readFileSync(path, 'utf-8')
|
|
20
|
+
* return data
|
|
21
|
+
* } catch (err) {
|
|
22
|
+
* check(err)
|
|
23
|
+
* }
|
|
19
24
|
*
|
|
20
|
-
*
|
|
25
|
+
* // after: 1 line
|
|
26
|
+
* return must(() => readFileSync(path, 'utf-8'))
|
|
21
27
|
*/
|
|
22
28
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
29
|
+
* Distinct error class for offensive programming failures.
|
|
30
|
+
* Distinguishes bugs from runtime errors at catch boundaries.
|
|
25
31
|
*
|
|
26
32
|
* @example
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* }
|
|
32
|
-
*/
|
|
33
|
-
export declare function assert(condition: boolean, ...msg: any[]): asserts condition;
|
|
34
|
-
/**
|
|
35
|
-
* Must unwraps a value that may throw and exits if error occurs.
|
|
36
|
-
* Use for operations that should never fail in correct code.
|
|
33
|
+
* // In a server — let Panics crash, handle everything else
|
|
34
|
+
* app.use((err, req, res, next) => {
|
|
35
|
+
* if (err instanceof Panic) throw err // bug, re-throw, let it crash
|
|
36
|
+
* res.status(500).json({ error: 'internal error' })
|
|
37
|
+
* })
|
|
37
38
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* const regex = must(() => new RegExp('^\\d+$'))
|
|
39
|
+
* // In tests — assert that code panics
|
|
40
|
+
* expect(() => assert(false, 'boom')).toThrow(Panic)
|
|
41
41
|
*/
|
|
42
|
-
export declare
|
|
42
|
+
export declare class Panic extends Error {
|
|
43
|
+
constructor(message: string);
|
|
44
|
+
}
|
|
43
45
|
/**
|
|
44
|
-
*
|
|
45
|
-
* Use for expected errors: file not found, network issues, permissions, etc.
|
|
46
|
+
* Assert throws if condition is false. Narrows types via `asserts condition`.
|
|
46
47
|
*
|
|
47
48
|
* @example
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* } catch (err) {
|
|
51
|
-
* check(err) // exits with error message
|
|
52
|
-
* }
|
|
49
|
+
* assert(data.length > 0, 'empty packet')
|
|
50
|
+
* assert(port > 0 && port < 65536, 'invalid port:', port)
|
|
53
51
|
*/
|
|
54
|
-
export declare function
|
|
52
|
+
export declare function assert(condition: boolean, ...msg: any[]): asserts condition;
|
|
55
53
|
/**
|
|
56
|
-
* Panic immediately
|
|
57
|
-
* Use when the program reaches an impossible state.
|
|
54
|
+
* Panic throws immediately. Use when the program reaches an impossible state.
|
|
58
55
|
*
|
|
59
56
|
* @example
|
|
60
57
|
* switch (state) {
|
|
@@ -64,10 +61,35 @@ export declare function check(err: any, ...messages: string[]): void;
|
|
|
64
61
|
* }
|
|
65
62
|
*/
|
|
66
63
|
export declare function panic(...msg: any[]): never;
|
|
64
|
+
/**
|
|
65
|
+
* Must unwraps an operation that should never fail. Handles sync and async.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* const data = must(() => JSON.parse(staticJsonString))
|
|
69
|
+
* const file = await must(() => fs.promises.readFile(path))
|
|
70
|
+
* const buf = must(() => readFileSync(path))
|
|
71
|
+
*/
|
|
72
|
+
export declare function must<T>(fn: () => Promise<T>): Promise<T>;
|
|
73
|
+
export declare function must<T>(fn: () => T): T;
|
|
74
|
+
/**
|
|
75
|
+
* Unwrap a nullable value or throw. Like Rust's .unwrap()/.expect().
|
|
76
|
+
* Returns T from T | null | undefined with type narrowing in one expression.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* // assert needs two statements:
|
|
80
|
+
* const user = db.findUser(id)
|
|
81
|
+
* assert(user !== null, 'user not found:', id)
|
|
82
|
+
*
|
|
83
|
+
* // unwrap does it inline:
|
|
84
|
+
* const user = unwrap(db.findUser(id), 'user not found:', id)
|
|
85
|
+
* const el = unwrap(document.getElementById('app'))
|
|
86
|
+
*/
|
|
87
|
+
export declare function unwrap<T>(value: T | null | undefined, ...msg: any[]): T;
|
|
67
88
|
export declare const offensive: {
|
|
89
|
+
Panic: typeof Panic;
|
|
68
90
|
assert: typeof assert;
|
|
69
|
-
must: typeof must;
|
|
70
|
-
check: typeof check;
|
|
71
91
|
panic: typeof panic;
|
|
92
|
+
must: typeof must;
|
|
93
|
+
unwrap: typeof unwrap;
|
|
72
94
|
};
|
|
73
95
|
//# sourceMappingURL=offensive.d.ts.map
|
package/dist/offensive.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"offensive.d.ts","sourceRoot":"","sources":["../src/offensive.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"offensive.d.ts","sourceRoot":"","sources":["../src/offensive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;;;;;;;;;;;GAaG;AACH,qBAAa,KAAM,SAAQ,KAAK;gBAClB,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,CAE3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,CAE1C;AAED;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;AACzD,wBAAgB,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAA;AAevC;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAGvE;AAED,eAAO,MAAM,SAAS;;;;;;CAMrB,CAAA"}
|
package/dist/offensive.js
CHANGED
|
@@ -3,101 +3,109 @@
|
|
|
3
3
|
*
|
|
4
4
|
* "A confused program SHOULD scream" - John Carmack
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Throw, always. An uncaught Panic crashes the process with a full stack trace.
|
|
7
|
+
* Zero dependencies. Works identically in Node, Deno, Bun, and browsers.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
9
|
+
* Four primitives:
|
|
10
|
+
* assert(cond, ...msg) - invariant checking, narrows types
|
|
11
|
+
* panic(...msg) - impossible state reached
|
|
12
|
+
* must(() => expr) - unwrap-or-die for operations (sync + async)
|
|
13
|
+
* unwrap(value, ...msg) - unwrap nullable values
|
|
10
14
|
*
|
|
11
|
-
*
|
|
12
|
-
* We don't recover - we crash.
|
|
13
|
-
* We don't validate and continue - we assert and panic.
|
|
15
|
+
* must() replaces the old try/catch/check pattern:
|
|
14
16
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* // before: 5 lines
|
|
18
|
+
* try {
|
|
19
|
+
* const data = readFileSync(path, 'utf-8')
|
|
20
|
+
* return data
|
|
21
|
+
* } catch (err) {
|
|
22
|
+
* check(err)
|
|
23
|
+
* }
|
|
19
24
|
*
|
|
20
|
-
*
|
|
25
|
+
* // after: 1 line
|
|
26
|
+
* return must(() => readFileSync(path, 'utf-8'))
|
|
21
27
|
*/
|
|
22
|
-
import { log } from './universal/log.js';
|
|
23
|
-
import { runtime } from './runtime.js';
|
|
24
28
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
29
|
+
* Distinct error class for offensive programming failures.
|
|
30
|
+
* Distinguishes bugs from runtime errors at catch boundaries.
|
|
27
31
|
*
|
|
28
32
|
* @example
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* }
|
|
33
|
+
* // In a server — let Panics crash, handle everything else
|
|
34
|
+
* app.use((err, req, res, next) => {
|
|
35
|
+
* if (err instanceof Panic) throw err // bug, re-throw, let it crash
|
|
36
|
+
* res.status(500).json({ error: 'internal error' })
|
|
37
|
+
* })
|
|
38
|
+
*
|
|
39
|
+
* // In tests — assert that code panics
|
|
40
|
+
* expect(() => assert(false, 'boom')).toThrow(Panic)
|
|
34
41
|
*/
|
|
35
|
-
export
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
runtime.exit(1);
|
|
42
|
+
export class Panic extends Error {
|
|
43
|
+
constructor(message) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = 'Panic';
|
|
40
46
|
}
|
|
41
47
|
}
|
|
42
48
|
/**
|
|
43
|
-
*
|
|
44
|
-
* Use for operations that should never fail in correct code.
|
|
49
|
+
* Assert throws if condition is false. Narrows types via `asserts condition`.
|
|
45
50
|
*
|
|
46
51
|
* @example
|
|
47
|
-
*
|
|
48
|
-
*
|
|
52
|
+
* assert(data.length > 0, 'empty packet')
|
|
53
|
+
* assert(port > 0 && port < 65536, 'invalid port:', port)
|
|
49
54
|
*/
|
|
50
|
-
export function
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
log.error(error);
|
|
56
|
-
return runtime.exit(1);
|
|
57
|
-
}
|
|
55
|
+
export function assert(condition, ...msg) {
|
|
56
|
+
if (!condition)
|
|
57
|
+
throw new Panic(msg.join(' ') || 'assertion failed');
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
|
-
*
|
|
61
|
-
* Use for expected errors: file not found, network issues, permissions, etc.
|
|
60
|
+
* Panic throws immediately. Use when the program reaches an impossible state.
|
|
62
61
|
*
|
|
63
62
|
* @example
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
63
|
+
* switch (state) {
|
|
64
|
+
* case 'ready': handleReady(); break
|
|
65
|
+
* case 'loading': handleLoading(); break
|
|
66
|
+
* default: panic('impossible state:', state)
|
|
68
67
|
* }
|
|
69
68
|
*/
|
|
70
|
-
export function
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
export function panic(...msg) {
|
|
70
|
+
throw new Panic(msg.join(' ') || 'panic');
|
|
71
|
+
}
|
|
72
|
+
export function must(fn) {
|
|
73
|
+
try {
|
|
74
|
+
const result = fn();
|
|
75
|
+
if (result instanceof Promise) {
|
|
76
|
+
return result.catch(e => {
|
|
77
|
+
throw new Panic(e instanceof Error ? e.message : String(e));
|
|
78
|
+
});
|
|
77
79
|
}
|
|
78
|
-
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
throw new Panic(e instanceof Error ? e.message : String(e));
|
|
79
84
|
}
|
|
80
85
|
}
|
|
81
86
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
87
|
+
* Unwrap a nullable value or throw. Like Rust's .unwrap()/.expect().
|
|
88
|
+
* Returns T from T | null | undefined with type narrowing in one expression.
|
|
84
89
|
*
|
|
85
90
|
* @example
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
+
* // assert needs two statements:
|
|
92
|
+
* const user = db.findUser(id)
|
|
93
|
+
* assert(user !== null, 'user not found:', id)
|
|
94
|
+
*
|
|
95
|
+
* // unwrap does it inline:
|
|
96
|
+
* const user = unwrap(db.findUser(id), 'user not found:', id)
|
|
97
|
+
* const el = unwrap(document.getElementById('app'))
|
|
91
98
|
*/
|
|
92
|
-
export function
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return
|
|
99
|
+
export function unwrap(value, ...msg) {
|
|
100
|
+
if (value == null)
|
|
101
|
+
throw new Panic(msg.join(' ') || `unwrap: got ${value}`);
|
|
102
|
+
return value;
|
|
96
103
|
}
|
|
97
104
|
export const offensive = {
|
|
105
|
+
Panic,
|
|
98
106
|
assert,
|
|
99
|
-
must,
|
|
100
|
-
check,
|
|
101
107
|
panic,
|
|
108
|
+
must,
|
|
109
|
+
unwrap,
|
|
102
110
|
};
|
|
103
111
|
//# sourceMappingURL=offensive.js.map
|
package/dist/offensive.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"offensive.js","sourceRoot":"","sources":["../src/offensive.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"offensive.js","sourceRoot":"","sources":["../src/offensive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,KAAM,SAAQ,KAAK;IAC9B,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,OAAO,CAAA;IACrB,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,MAAM,CAAC,SAAkB,EAAE,GAAG,GAAU;IACtD,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,CAAA;AACtE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,KAAK,CAAC,GAAG,GAAU;IACjC,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,CAAA;AAC3C,CAAC;AAYD,MAAM,UAAU,IAAI,CAAI,EAAwB;IAC9C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,EAAE,EAAE,CAAA;QACnB,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;YAC9B,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;gBACtB,MAAM,IAAI,KAAK,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;YAC7D,CAAC,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;IAC7D,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,MAAM,CAAI,KAA2B,EAAE,GAAG,GAAU;IAClE,IAAI,KAAK,IAAI,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,eAAe,KAAK,EAAE,CAAC,CAAA;IAC3E,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,KAAK;IACL,MAAM;IACN,KAAK;IACL,IAAI;IACJ,MAAM;CACP,CAAA"}
|
package/dist/platform/dir.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Directory operations that
|
|
2
|
+
* Directory operations that throw on error (offensive programming style)
|
|
3
3
|
*/
|
|
4
4
|
declare class DirOps {
|
|
5
5
|
/**
|
|
6
|
-
* Create a directory (including parents)
|
|
6
|
+
* Create a directory (including parents). Throws on error.
|
|
7
7
|
*/
|
|
8
8
|
create(path: string): void;
|
|
9
9
|
/**
|
|
@@ -11,11 +11,11 @@ declare class DirOps {
|
|
|
11
11
|
*/
|
|
12
12
|
exists(path: string): boolean;
|
|
13
13
|
/**
|
|
14
|
-
* Remove a directory and all its contents
|
|
14
|
+
* Remove a directory and all its contents. Throws on error.
|
|
15
15
|
*/
|
|
16
16
|
remove(path: string): void;
|
|
17
17
|
/**
|
|
18
|
-
* List all entries in a directory
|
|
18
|
+
* List all entries in a directory. Throws on error.
|
|
19
19
|
*/
|
|
20
20
|
list(path: string): string[];
|
|
21
21
|
/**
|
|
@@ -23,15 +23,15 @@ declare class DirOps {
|
|
|
23
23
|
*/
|
|
24
24
|
listFull(dirPath: string): string[];
|
|
25
25
|
/**
|
|
26
|
-
* List only subdirectories
|
|
26
|
+
* List only subdirectories. Throws on error.
|
|
27
27
|
*/
|
|
28
28
|
listDirs(path: string): string[];
|
|
29
29
|
/**
|
|
30
|
-
* List only files (not directories)
|
|
30
|
+
* List only files (not directories). Throws on error.
|
|
31
31
|
*/
|
|
32
32
|
listFiles(path: string): string[];
|
|
33
33
|
/**
|
|
34
|
-
* Check if directory is empty
|
|
34
|
+
* Check if directory is empty. Throws on error.
|
|
35
35
|
*/
|
|
36
36
|
isEmpty(path: string): boolean;
|
|
37
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dir.d.ts","sourceRoot":"","sources":["../../src/platform/dir.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,cAAM,MAAM;IACV;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"dir.d.ts","sourceRoot":"","sources":["../../src/platform/dir.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,cAAM,MAAM;IACV;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1B;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAU7B;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1B;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAI5B;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAKnC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAMhC;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAMjC;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAG/B;AAED,eAAO,MAAM,GAAG,QAAe,CAAA"}
|
package/dist/platform/dir.js
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import { mkdirSync, existsSync, statSync, rmSync, readdirSync } from 'fs';
|
|
2
|
-
import {
|
|
2
|
+
import { must } from '../offensive.js';
|
|
3
3
|
import { path } from './path.js';
|
|
4
4
|
/**
|
|
5
|
-
* Directory operations that
|
|
5
|
+
* Directory operations that throw on error (offensive programming style)
|
|
6
6
|
*/
|
|
7
7
|
class DirOps {
|
|
8
8
|
/**
|
|
9
|
-
* Create a directory (including parents)
|
|
9
|
+
* Create a directory (including parents). Throws on error.
|
|
10
10
|
*/
|
|
11
11
|
create(path) {
|
|
12
|
-
|
|
13
|
-
mkdirSync(path, { recursive: true, mode: 0o755 });
|
|
14
|
-
}
|
|
15
|
-
catch (err) {
|
|
16
|
-
check(err);
|
|
17
|
-
}
|
|
12
|
+
must(() => mkdirSync(path, { recursive: true, mode: 0o755 }));
|
|
18
13
|
}
|
|
19
14
|
/**
|
|
20
15
|
* Check if directory exists
|
|
@@ -31,27 +26,16 @@ class DirOps {
|
|
|
31
26
|
}
|
|
32
27
|
}
|
|
33
28
|
/**
|
|
34
|
-
* Remove a directory and all its contents
|
|
29
|
+
* Remove a directory and all its contents. Throws on error.
|
|
35
30
|
*/
|
|
36
31
|
remove(path) {
|
|
37
|
-
|
|
38
|
-
rmSync(path, { recursive: true, force: true });
|
|
39
|
-
}
|
|
40
|
-
catch (err) {
|
|
41
|
-
check(err);
|
|
42
|
-
}
|
|
32
|
+
must(() => rmSync(path, { recursive: true, force: true }));
|
|
43
33
|
}
|
|
44
34
|
/**
|
|
45
|
-
* List all entries in a directory
|
|
35
|
+
* List all entries in a directory. Throws on error.
|
|
46
36
|
*/
|
|
47
37
|
list(path) {
|
|
48
|
-
|
|
49
|
-
return readdirSync(path);
|
|
50
|
-
}
|
|
51
|
-
catch (err) {
|
|
52
|
-
check(err);
|
|
53
|
-
throw err;
|
|
54
|
-
}
|
|
38
|
+
return must(() => readdirSync(path));
|
|
55
39
|
}
|
|
56
40
|
/**
|
|
57
41
|
* List full paths of all entries in a directory
|
|
@@ -61,47 +45,26 @@ class DirOps {
|
|
|
61
45
|
return names.map(name => path.join(dirPath, name));
|
|
62
46
|
}
|
|
63
47
|
/**
|
|
64
|
-
* List only subdirectories
|
|
48
|
+
* List only subdirectories. Throws on error.
|
|
65
49
|
*/
|
|
66
50
|
listDirs(path) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
.filter(entry => entry.isDirectory())
|
|
71
|
-
.map(entry => entry.name);
|
|
72
|
-
}
|
|
73
|
-
catch (err) {
|
|
74
|
-
check(err);
|
|
75
|
-
throw err;
|
|
76
|
-
}
|
|
51
|
+
return must(() => readdirSync(path, { withFileTypes: true }))
|
|
52
|
+
.filter(entry => entry.isDirectory())
|
|
53
|
+
.map(entry => entry.name);
|
|
77
54
|
}
|
|
78
55
|
/**
|
|
79
|
-
* List only files (not directories)
|
|
56
|
+
* List only files (not directories). Throws on error.
|
|
80
57
|
*/
|
|
81
58
|
listFiles(path) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
.filter(entry => entry.isFile())
|
|
86
|
-
.map(entry => entry.name);
|
|
87
|
-
}
|
|
88
|
-
catch (err) {
|
|
89
|
-
check(err);
|
|
90
|
-
throw err;
|
|
91
|
-
}
|
|
59
|
+
return must(() => readdirSync(path, { withFileTypes: true }))
|
|
60
|
+
.filter(entry => entry.isFile())
|
|
61
|
+
.map(entry => entry.name);
|
|
92
62
|
}
|
|
93
63
|
/**
|
|
94
|
-
* Check if directory is empty
|
|
64
|
+
* Check if directory is empty. Throws on error.
|
|
95
65
|
*/
|
|
96
66
|
isEmpty(path) {
|
|
97
|
-
|
|
98
|
-
const entries = readdirSync(path);
|
|
99
|
-
return entries.length === 0;
|
|
100
|
-
}
|
|
101
|
-
catch (err) {
|
|
102
|
-
check(err);
|
|
103
|
-
throw err;
|
|
104
|
-
}
|
|
67
|
+
return must(() => readdirSync(path)).length === 0;
|
|
105
68
|
}
|
|
106
69
|
}
|
|
107
70
|
export const dir = new DirOps();
|
package/dist/platform/dir.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dir.js","sourceRoot":"","sources":["../../src/platform/dir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,IAAI,CAAA;AACzE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"dir.js","sourceRoot":"","sources":["../../src/platform/dir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,IAAI,CAAA;AACzE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC;;GAEG;AACH,MAAM,MAAM;IACV;;OAEG;IACH,MAAM,CAAC,IAAY;QACjB,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;IAC/D,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,IAAY;QACjB,IAAI,CAAC;YACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;gBAAE,OAAO,KAAK,CAAA;YACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;YAC5B,OAAO,KAAK,CAAC,WAAW,EAAE,CAAA;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,IAAY;QACjB,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IAC5D,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,IAAY;QACf,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAA;IACtC,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,OAAe;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAChC,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAA;IACpD,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,IAAY;QACnB,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;aAC1D,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;aACpC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC7B,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,IAAY;QACpB,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;aAC1D,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;aAC/B,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC7B,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,IAAY;QAClB,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAA;IACnD,CAAC;CACF;AAED,MAAM,CAAC,MAAM,GAAG,GAAG,IAAI,MAAM,EAAE,CAAA"}
|
package/dist/platform/file.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* File operations that
|
|
2
|
+
* File operations that throw on error (offensive programming style)
|
|
3
3
|
*/
|
|
4
4
|
declare class FileOps {
|
|
5
5
|
/**
|
|
@@ -7,16 +7,15 @@ declare class FileOps {
|
|
|
7
7
|
* This allows us to resolve relative paths from the calling module
|
|
8
8
|
*/
|
|
9
9
|
getCallerUrl(): string | undefined;
|
|
10
|
+
private resolvePath;
|
|
10
11
|
/**
|
|
11
|
-
* Read a file
|
|
12
|
-
* Relative paths
|
|
13
|
-
* Absolute paths are used as-is
|
|
12
|
+
* Read a file as Buffer. Throws on error.
|
|
13
|
+
* Relative paths resolved from calling module.
|
|
14
14
|
*/
|
|
15
15
|
read(filePath: string): Buffer;
|
|
16
16
|
/**
|
|
17
|
-
* Read a file as string
|
|
18
|
-
* Relative paths
|
|
19
|
-
* Absolute paths are used as-is
|
|
17
|
+
* Read a file as string. Throws on error.
|
|
18
|
+
* Relative paths resolved from calling module.
|
|
20
19
|
*
|
|
21
20
|
* @param filePath - Path to the file
|
|
22
21
|
* @param encodingOrUrl - Optional: encoding (e.g. 'utf-8') or import.meta.url for explicit resolution
|
|
@@ -24,27 +23,24 @@ declare class FileOps {
|
|
|
24
23
|
*/
|
|
25
24
|
readText(filePath: string, encodingOrUrl?: BufferEncoding | string, encoding?: BufferEncoding): string;
|
|
26
25
|
/**
|
|
27
|
-
* Write data to a file
|
|
28
|
-
* Relative paths
|
|
29
|
-
* Absolute paths are used as-is
|
|
26
|
+
* Write data to a file. Throws on error.
|
|
27
|
+
* Relative paths resolved from calling module.
|
|
30
28
|
*/
|
|
31
29
|
write(filePath: string, data: string | Buffer): void;
|
|
32
30
|
/**
|
|
33
31
|
* Check if file exists (not a directory)
|
|
34
|
-
* Relative paths (./file.txt) are resolved from the calling module
|
|
35
|
-
* Absolute paths are used as-is
|
|
36
32
|
*/
|
|
37
33
|
exists(filePath: string): boolean;
|
|
38
34
|
/**
|
|
39
|
-
* Remove a file
|
|
35
|
+
* Remove a file. Throws on error.
|
|
40
36
|
*/
|
|
41
37
|
remove(path: string): void;
|
|
42
38
|
/**
|
|
43
|
-
* Get file size in bytes
|
|
39
|
+
* Get file size in bytes. Throws on error.
|
|
44
40
|
*/
|
|
45
41
|
size(path: string): number;
|
|
46
42
|
/**
|
|
47
|
-
* Get file modification time
|
|
43
|
+
* Get file modification time. Throws on error.
|
|
48
44
|
*/
|
|
49
45
|
mtime(path: string): Date;
|
|
50
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/platform/file.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,cAAM,OAAO;IACX;;;OAGG;IACH,YAAY,IAAI,MAAM,GAAG,SAAS;IA2DlC
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/platform/file.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,cAAM,OAAO;IACX;;;OAGG;IACH,YAAY,IAAI,MAAM,GAAG,SAAS;IA2DlC,OAAO,CAAC,WAAW;IAWnB;;;OAGG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAK9B;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM;IAwBtG;;;OAGG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKpD;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAWjC;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1B;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI1B;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAG1B;AAED,eAAO,MAAM,IAAI,SAAgB,CAAA"}
|
package/dist/platform/file.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, existsSync, unlinkSync, statSync } from 'fs';
|
|
2
|
-
import {
|
|
2
|
+
import { must } from '../offensive.js';
|
|
3
3
|
import { path } from './path.js';
|
|
4
4
|
/**
|
|
5
|
-
* File operations that
|
|
5
|
+
* File operations that throw on error (offensive programming style)
|
|
6
6
|
*/
|
|
7
7
|
class FileOps {
|
|
8
8
|
/**
|
|
@@ -18,7 +18,7 @@ class FileOps {
|
|
|
18
18
|
// Stack looks like:
|
|
19
19
|
// Error
|
|
20
20
|
// at FileOps.getCallerUrl (file:///path/to/file.ts:line:col)
|
|
21
|
-
// at FileOps.readText (file:///path/to/file.ts:line:col)
|
|
21
|
+
// at FileOps.readText (file:///path/to/file.ts:line:col)
|
|
22
22
|
// at caller (file:///path/to/caller.ts:line:col) <- we want this
|
|
23
23
|
const lines = stack.split('\n');
|
|
24
24
|
// Try multiple regex patterns for different environments
|
|
@@ -57,112 +57,71 @@ class FileOps {
|
|
|
57
57
|
}
|
|
58
58
|
return undefined;
|
|
59
59
|
}
|
|
60
|
+
resolvePath(filePath) {
|
|
61
|
+
if (filePath.startsWith('./') || filePath.startsWith('../')) {
|
|
62
|
+
const callerUrl = this.getCallerUrl();
|
|
63
|
+
return path.resolve(filePath, callerUrl);
|
|
64
|
+
}
|
|
65
|
+
if (!path.isAbsolute(filePath)) {
|
|
66
|
+
return path.resolve(filePath);
|
|
67
|
+
}
|
|
68
|
+
return filePath;
|
|
69
|
+
}
|
|
60
70
|
/**
|
|
61
|
-
* Read a file
|
|
62
|
-
* Relative paths
|
|
63
|
-
* Absolute paths are used as-is
|
|
71
|
+
* Read a file as Buffer. Throws on error.
|
|
72
|
+
* Relative paths resolved from calling module.
|
|
64
73
|
*/
|
|
65
74
|
read(filePath) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
let resolvedPath = filePath;
|
|
69
|
-
if (filePath.startsWith('./') || filePath.startsWith('../')) {
|
|
70
|
-
const callerUrl = this.getCallerUrl();
|
|
71
|
-
resolvedPath = path.resolve(filePath, callerUrl);
|
|
72
|
-
}
|
|
73
|
-
else if (!path.isAbsolute(filePath)) {
|
|
74
|
-
resolvedPath = path.resolve(filePath);
|
|
75
|
-
}
|
|
76
|
-
return readFileSync(resolvedPath);
|
|
77
|
-
}
|
|
78
|
-
catch (err) {
|
|
79
|
-
check(err);
|
|
80
|
-
throw err; // TypeScript needs this even though check exits
|
|
81
|
-
}
|
|
75
|
+
const resolved = this.resolvePath(filePath);
|
|
76
|
+
return must(() => readFileSync(resolved));
|
|
82
77
|
}
|
|
83
78
|
/**
|
|
84
|
-
* Read a file as string
|
|
85
|
-
* Relative paths
|
|
86
|
-
* Absolute paths are used as-is
|
|
79
|
+
* Read a file as string. Throws on error.
|
|
80
|
+
* Relative paths resolved from calling module.
|
|
87
81
|
*
|
|
88
82
|
* @param filePath - Path to the file
|
|
89
83
|
* @param encodingOrUrl - Optional: encoding (e.g. 'utf-8') or import.meta.url for explicit resolution
|
|
90
84
|
* @param encoding - Optional: encoding when second param is import.meta.url
|
|
91
85
|
*/
|
|
92
86
|
readText(filePath, encodingOrUrl, encoding) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// It's a URL/path
|
|
100
|
-
callerUrl = encodingOrUrl;
|
|
101
|
-
actualEncoding = encoding || 'utf-8';
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
// It's an encoding
|
|
105
|
-
actualEncoding = encodingOrUrl;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
// Auto-detect caller for relative paths
|
|
109
|
-
let resolvedPath = filePath;
|
|
110
|
-
if (filePath.startsWith('./') || filePath.startsWith('../')) {
|
|
111
|
-
// Use explicit URL if provided, otherwise detect from stack
|
|
112
|
-
const url = callerUrl || this.getCallerUrl();
|
|
113
|
-
resolvedPath = path.resolve(filePath, url);
|
|
87
|
+
let callerUrl;
|
|
88
|
+
let actualEncoding = 'utf-8';
|
|
89
|
+
if (encodingOrUrl) {
|
|
90
|
+
if (encodingOrUrl.startsWith('file://') || encodingOrUrl.includes('/')) {
|
|
91
|
+
callerUrl = encodingOrUrl;
|
|
92
|
+
actualEncoding = encoding || 'utf-8';
|
|
114
93
|
}
|
|
115
|
-
else
|
|
116
|
-
|
|
94
|
+
else {
|
|
95
|
+
actualEncoding = encodingOrUrl;
|
|
117
96
|
}
|
|
118
|
-
return readFileSync(resolvedPath, actualEncoding);
|
|
119
97
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
98
|
+
let resolvedPath = filePath;
|
|
99
|
+
if (filePath.startsWith('./') || filePath.startsWith('../')) {
|
|
100
|
+
const url = callerUrl || this.getCallerUrl();
|
|
101
|
+
resolvedPath = path.resolve(filePath, url);
|
|
123
102
|
}
|
|
103
|
+
else if (!path.isAbsolute(filePath)) {
|
|
104
|
+
resolvedPath = path.resolve(filePath);
|
|
105
|
+
}
|
|
106
|
+
return must(() => readFileSync(resolvedPath, actualEncoding));
|
|
124
107
|
}
|
|
125
108
|
/**
|
|
126
|
-
* Write data to a file
|
|
127
|
-
* Relative paths
|
|
128
|
-
* Absolute paths are used as-is
|
|
109
|
+
* Write data to a file. Throws on error.
|
|
110
|
+
* Relative paths resolved from calling module.
|
|
129
111
|
*/
|
|
130
112
|
write(filePath, data) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
let resolvedPath = filePath;
|
|
134
|
-
if (filePath.startsWith('./') || filePath.startsWith('../')) {
|
|
135
|
-
const callerUrl = this.getCallerUrl();
|
|
136
|
-
resolvedPath = path.resolve(filePath, callerUrl);
|
|
137
|
-
}
|
|
138
|
-
else if (!path.isAbsolute(filePath)) {
|
|
139
|
-
resolvedPath = path.resolve(filePath);
|
|
140
|
-
}
|
|
141
|
-
writeFileSync(resolvedPath, data, { mode: 0o644 });
|
|
142
|
-
}
|
|
143
|
-
catch (err) {
|
|
144
|
-
check(err);
|
|
145
|
-
}
|
|
113
|
+
const resolved = this.resolvePath(filePath);
|
|
114
|
+
must(() => writeFileSync(resolved, data, { mode: 0o644 }));
|
|
146
115
|
}
|
|
147
116
|
/**
|
|
148
117
|
* Check if file exists (not a directory)
|
|
149
|
-
* Relative paths (./file.txt) are resolved from the calling module
|
|
150
|
-
* Absolute paths are used as-is
|
|
151
118
|
*/
|
|
152
119
|
exists(filePath) {
|
|
153
120
|
try {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (filePath.startsWith('./') || filePath.startsWith('../')) {
|
|
157
|
-
const callerUrl = this.getCallerUrl();
|
|
158
|
-
resolvedPath = path.resolve(filePath, callerUrl);
|
|
159
|
-
}
|
|
160
|
-
else if (!path.isAbsolute(filePath)) {
|
|
161
|
-
resolvedPath = path.resolve(filePath);
|
|
162
|
-
}
|
|
163
|
-
if (!existsSync(resolvedPath))
|
|
121
|
+
const resolved = this.resolvePath(filePath);
|
|
122
|
+
if (!existsSync(resolved))
|
|
164
123
|
return false;
|
|
165
|
-
const stats = statSync(
|
|
124
|
+
const stats = statSync(resolved);
|
|
166
125
|
return stats.isFile();
|
|
167
126
|
}
|
|
168
127
|
catch {
|
|
@@ -170,41 +129,22 @@ class FileOps {
|
|
|
170
129
|
}
|
|
171
130
|
}
|
|
172
131
|
/**
|
|
173
|
-
* Remove a file
|
|
132
|
+
* Remove a file. Throws on error.
|
|
174
133
|
*/
|
|
175
134
|
remove(path) {
|
|
176
|
-
|
|
177
|
-
unlinkSync(path);
|
|
178
|
-
}
|
|
179
|
-
catch (err) {
|
|
180
|
-
check(err);
|
|
181
|
-
}
|
|
135
|
+
must(() => unlinkSync(path));
|
|
182
136
|
}
|
|
183
137
|
/**
|
|
184
|
-
* Get file size in bytes
|
|
138
|
+
* Get file size in bytes. Throws on error.
|
|
185
139
|
*/
|
|
186
140
|
size(path) {
|
|
187
|
-
|
|
188
|
-
const stats = statSync(path);
|
|
189
|
-
return stats.size;
|
|
190
|
-
}
|
|
191
|
-
catch (err) {
|
|
192
|
-
check(err);
|
|
193
|
-
throw err;
|
|
194
|
-
}
|
|
141
|
+
return must(() => statSync(path)).size;
|
|
195
142
|
}
|
|
196
143
|
/**
|
|
197
|
-
* Get file modification time
|
|
144
|
+
* Get file modification time. Throws on error.
|
|
198
145
|
*/
|
|
199
146
|
mtime(path) {
|
|
200
|
-
|
|
201
|
-
const stats = statSync(path);
|
|
202
|
-
return stats.mtime;
|
|
203
|
-
}
|
|
204
|
-
catch (err) {
|
|
205
|
-
check(err);
|
|
206
|
-
throw err;
|
|
207
|
-
}
|
|
147
|
+
return must(() => statSync(path)).mtime;
|
|
208
148
|
}
|
|
209
149
|
}
|
|
210
150
|
export const file = new FileOps();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/platform/file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;AAClF,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/platform/file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const xdg: {
|
|
2
|
+
config: (...segments: string[]) => string;
|
|
3
|
+
data: (...segments: string[]) => string;
|
|
4
|
+
state: (...segments: string[]) => string;
|
|
5
|
+
cache: (...segments: string[]) => string;
|
|
6
|
+
runtime: (...segments: string[]) => string;
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=xdg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xdg.d.ts","sourceRoot":"","sources":["../../src/platform/xdg.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,GAAG;0BACS,MAAM,EAAE;wBACR,MAAM,EAAE;yBACR,MAAM,EAAE;yBACR,MAAM,EAAE;2BACR,MAAM,EAAE;CAChC,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const home = homedir();
|
|
4
|
+
const env = (key, fallback) => process.env[key] || fallback;
|
|
5
|
+
export const xdg = {
|
|
6
|
+
config: (...segments) => join(env('XDG_CONFIG_HOME', join(home, '.config')), ...segments),
|
|
7
|
+
data: (...segments) => join(env('XDG_DATA_HOME', join(home, '.local', 'share')), ...segments),
|
|
8
|
+
state: (...segments) => join(env('XDG_STATE_HOME', join(home, '.local', 'state')), ...segments),
|
|
9
|
+
cache: (...segments) => join(env('XDG_CACHE_HOME', join(home, '.cache')), ...segments),
|
|
10
|
+
runtime: (...segments) => join(env('XDG_RUNTIME_DIR', join(home, '.local', 'run')), ...segments),
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=xdg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xdg.js","sourceRoot":"","sources":["../../src/platform/xdg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAA;AACtB,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,QAAgB,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAA;AAE3E,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,MAAM,EAAG,CAAC,GAAG,QAAkB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC;IACpG,IAAI,EAAK,CAAC,GAAG,QAAkB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC;IAC1G,KAAK,EAAI,CAAC,GAAG,QAAkB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC;IAC3G,KAAK,EAAI,CAAC,GAAG,QAAkB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC;IAClG,OAAO,EAAE,CAAC,GAAG,QAAkB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC;CAC3G,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adriangalilea/utils",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "TypeScript utilities - logger, currency, formatter, and more",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -32,6 +32,18 @@
|
|
|
32
32
|
"files": [
|
|
33
33
|
"dist"
|
|
34
34
|
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc",
|
|
37
|
+
"dev": "tsx src/index.ts",
|
|
38
|
+
"test": "echo 'Tests not configured yet'",
|
|
39
|
+
"test:runtime": "tsx tests/runtime-test.ts",
|
|
40
|
+
"test:browser": "open tests/browser-test.html",
|
|
41
|
+
"example": "tsx tests/example.ts",
|
|
42
|
+
"update-crypto": "tsx src/universal/currency/download-crypto-list.ts",
|
|
43
|
+
"type-check": "tsc --noEmit",
|
|
44
|
+
"lint": "echo 'No linter configured yet'",
|
|
45
|
+
"prepublishOnly": "pnpm run build"
|
|
46
|
+
},
|
|
35
47
|
"keywords": [
|
|
36
48
|
"typescript",
|
|
37
49
|
"utils",
|
|
@@ -60,16 +72,5 @@
|
|
|
60
72
|
"bugs": {
|
|
61
73
|
"url": "https://github.com/adriangalilea/ts-utils/issues"
|
|
62
74
|
},
|
|
63
|
-
"homepage": "https://github.com/adriangalilea/ts-utils#readme"
|
|
64
|
-
"scripts": {
|
|
65
|
-
"build": "tsc",
|
|
66
|
-
"dev": "tsx src/index.ts",
|
|
67
|
-
"test": "echo 'Tests not configured yet'",
|
|
68
|
-
"test:runtime": "tsx tests/runtime-test.ts",
|
|
69
|
-
"test:browser": "open tests/browser-test.html",
|
|
70
|
-
"example": "tsx tests/example.ts",
|
|
71
|
-
"update-crypto": "tsx src/universal/currency/download-crypto-list.ts",
|
|
72
|
-
"type-check": "tsc --noEmit",
|
|
73
|
-
"lint": "echo 'No linter configured yet'"
|
|
74
|
-
}
|
|
75
|
+
"homepage": "https://github.com/adriangalilea/ts-utils#readme"
|
|
75
76
|
}
|