@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 CHANGED
@@ -5,13 +5,11 @@ TypeScript utilities - logger, currency, offensive programming, file operations,
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- # Using JSR (recommended)
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
- - **Offensive Programming**: Assert, must, check, panic utilities
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
@@ -13,4 +13,5 @@ export * from './platform/dir.js';
13
13
  export * from './platform/path.js';
14
14
  export * from './platform/project.js';
15
15
  export * from './platform/kev.js';
16
+ export * from './platform/xdg.js';
16
17
  //# sourceMappingURL=index.d.ts.map
@@ -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 - uses runtime.exit() so it adapts to environment
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,0EAA0E;AAC1E,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"}
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"}
@@ -3,58 +3,55 @@
3
3
  *
4
4
  * "A confused program SHOULD scream" - John Carmack
5
5
  *
6
- * These utilities are the ANTITHESIS of defensive programming.
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
- * Defensive programming: try/catch, error recovery, graceful degradation, silent failures
9
- * Offensive programming: FAIL LOUD, FAIL FAST, NO RECOVERY, CRASH EARLY
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
- * We don't handle errors - we make them catastrophic.
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
- * This approach makes bugs IMPOSSIBLE to ignore:
16
- * - Wrong assumptions? CRASH
17
- * - Invalid state? CRASH
18
- * - "Impossible" error? CRASH
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
- * The only acceptable response to confusion is to scream and die.
25
+ * // after: 1 line
26
+ * return must(() => readFileSync(path, 'utf-8'))
21
27
  */
22
28
  /**
23
- * Assert exits with error if condition is false.
24
- * Use for validating preconditions and invariants.
29
+ * Distinct error class for offensive programming failures.
30
+ * Distinguishes bugs from runtime errors at catch boundaries.
25
31
  *
26
32
  * @example
27
- * function sendPacket(data: Buffer, port: number) {
28
- * assert(data.length > 0, 'empty packet')
29
- * assert(port > 0 && port < 65536, 'invalid port:', port)
30
- * // Now safe to proceed
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
- * @example
39
- * const data = must(() => JSON.parse(staticJsonString))
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 function must<T>(fn: () => T): T;
42
+ export declare class Panic extends Error {
43
+ constructor(message: string);
44
+ }
43
45
  /**
44
- * Check exits cleanly with formatted error message if error is not null/undefined.
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
- * try {
49
- * const data = await fs.readFile(userFile)
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 check(err: any, ...messages: string[]): void;
52
+ export declare function assert(condition: boolean, ...msg: any[]): asserts condition;
55
53
  /**
56
- * Panic immediately exits with error message.
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
@@ -1 +1 @@
1
- {"version":3,"file":"offensive.d.ts","sourceRoot":"","sources":["../src/offensive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAKH;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,CAM3E;AAED;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAOtC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAS3D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,CAI1C;AAED,eAAO,MAAM,SAAS;;;;;CAKrB,CAAA"}
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
- * These utilities are the ANTITHESIS of defensive programming.
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
- * Defensive programming: try/catch, error recovery, graceful degradation, silent failures
9
- * Offensive programming: FAIL LOUD, FAIL FAST, NO RECOVERY, CRASH EARLY
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
- * We don't handle errors - we make them catastrophic.
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
- * This approach makes bugs IMPOSSIBLE to ignore:
16
- * - Wrong assumptions? CRASH
17
- * - Invalid state? CRASH
18
- * - "Impossible" error? CRASH
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
- * The only acceptable response to confusion is to scream and die.
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
- * Assert exits with error if condition is false.
26
- * Use for validating preconditions and invariants.
29
+ * Distinct error class for offensive programming failures.
30
+ * Distinguishes bugs from runtime errors at catch boundaries.
27
31
  *
28
32
  * @example
29
- * function sendPacket(data: Buffer, port: number) {
30
- * assert(data.length > 0, 'empty packet')
31
- * assert(port > 0 && port < 65536, 'invalid port:', port)
32
- * // Now safe to proceed
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 function assert(condition, ...msg) {
36
- if (!condition) {
37
- const message = msg.length > 0 ? msg.join(' ') : 'assertion failed';
38
- log.error(message);
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
- * Must unwraps a value that may throw and exits if error occurs.
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
- * const data = must(() => JSON.parse(staticJsonString))
48
- * const regex = must(() => new RegExp('^\\d+$'))
52
+ * assert(data.length > 0, 'empty packet')
53
+ * assert(port > 0 && port < 65536, 'invalid port:', port)
49
54
  */
50
- export function must(fn) {
51
- try {
52
- return fn();
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
- * Check exits cleanly with formatted error message if error is not null/undefined.
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
- * try {
65
- * const data = await fs.readFile(userFile)
66
- * } catch (err) {
67
- * check(err) // exits with error message
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 check(err, ...messages) {
71
- if (err) {
72
- if (messages.length > 0) {
73
- log.error(messages.join(' '));
74
- }
75
- else {
76
- log.error(err);
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
- runtime.exit(1);
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
- * Panic immediately exits with error message.
83
- * Use when the program reaches an impossible state.
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
- * switch (state) {
87
- * case 'ready': handleReady(); break
88
- * case 'loading': handleLoading(); break
89
- * default: panic('impossible state:', state)
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 panic(...msg) {
93
- const message = msg.length > 0 ? msg.join(' ') : 'panic';
94
- log.error(message);
95
- return runtime.exit(1);
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
@@ -1 +1 @@
1
- {"version":3,"file":"offensive.js","sourceRoot":"","sources":["../src/offensive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,MAAM,CAAC,SAAkB,EAAE,GAAG,GAAU;IACtD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAA;QACnE,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,IAAI,CAAI,EAAW;IACjC,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,CAAA;IACb,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAChB,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAU,CAAA;IACjC,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,KAAK,CAAC,GAAQ,EAAE,GAAG,QAAkB;IACnD,IAAI,GAAG,EAAE,CAAC;QACR,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QAC/B,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAChB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,KAAK,CAAC,GAAG,GAAU;IACjC,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;IACxD,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAClB,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACxB,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,MAAM;IACN,IAAI;IACJ,KAAK;IACL,KAAK;CACN,CAAA"}
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"}
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Directory operations that exit on error (offensive programming style)
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) and exit on error
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, exit on error
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, exit on error
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;IAQ1B;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAU7B;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQ1B;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAS5B;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAKnC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAYhC;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAYjC;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAS/B;AAED,eAAO,MAAM,GAAG,QAAe,CAAA"}
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"}
@@ -1,20 +1,15 @@
1
1
  import { mkdirSync, existsSync, statSync, rmSync, readdirSync } from 'fs';
2
- import { check } from '../offensive.js';
2
+ import { must } from '../offensive.js';
3
3
  import { path } from './path.js';
4
4
  /**
5
- * Directory operations that exit on error (offensive programming style)
5
+ * Directory operations that throw on error (offensive programming style)
6
6
  */
7
7
  class DirOps {
8
8
  /**
9
- * Create a directory (including parents) and exit on error
9
+ * Create a directory (including parents). Throws on error.
10
10
  */
11
11
  create(path) {
12
- try {
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, exit on error
29
+ * Remove a directory and all its contents. Throws on error.
35
30
  */
36
31
  remove(path) {
37
- try {
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, exit on error
35
+ * List all entries in a directory. Throws on error.
46
36
  */
47
37
  list(path) {
48
- try {
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
- try {
68
- const entries = readdirSync(path, { withFileTypes: true });
69
- return entries
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
- try {
83
- const entries = readdirSync(path, { withFileTypes: true });
84
- return entries
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
- try {
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();
@@ -1 +1 @@
1
- {"version":3,"file":"dir.js","sourceRoot":"","sources":["../../src/platform/dir.ts"],"names":[],"mappings":"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"}
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"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * File operations that exit on error (offensive programming style)
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 and exit on error
12
- * Relative paths (./file.txt) are resolved from the calling module
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 and exit on error
18
- * Relative paths (./file.txt) are resolved from the calling module
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 and exit on error
28
- * Relative paths (./file.txt) are resolved from the calling module
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 and exit on error
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;;;;OAIG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAkB9B;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM;IAkCtG;;;;OAIG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAiBpD;;;;OAIG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAmBjC;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQ1B;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAU1B;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAS1B;AAED,eAAO,MAAM,IAAI,SAAgB,CAAA"}
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"}
@@ -1,8 +1,8 @@
1
1
  import { readFileSync, writeFileSync, existsSync, unlinkSync, statSync } from 'fs';
2
- import { check } from '../offensive.js';
2
+ import { must } from '../offensive.js';
3
3
  import { path } from './path.js';
4
4
  /**
5
- * File operations that exit on error (offensive programming style)
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 and exit on error
62
- * Relative paths (./file.txt) are resolved from the calling module
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
- try {
67
- // Auto-detect caller for relative paths
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 and exit on error
85
- * Relative paths (./file.txt) are resolved from the calling module
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
- try {
94
- // Determine if second param is encoding or URL
95
- let callerUrl;
96
- let actualEncoding = 'utf-8';
97
- if (encodingOrUrl) {
98
- if (encodingOrUrl.startsWith('file://') || encodingOrUrl.includes('/')) {
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 if (!path.isAbsolute(filePath)) {
116
- resolvedPath = path.resolve(filePath);
94
+ else {
95
+ actualEncoding = encodingOrUrl;
117
96
  }
118
- return readFileSync(resolvedPath, actualEncoding);
119
97
  }
120
- catch (err) {
121
- check(err);
122
- throw err;
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 and exit on error
127
- * Relative paths (./file.txt) are resolved from the calling module
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
- try {
132
- // Auto-detect caller for relative paths
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
- // Auto-detect caller for relative paths
155
- let resolvedPath = filePath;
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(resolvedPath);
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 and exit on error
132
+ * Remove a file. Throws on error.
174
133
  */
175
134
  remove(path) {
176
- try {
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
- try {
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
- try {
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":"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"}
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.10",
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
  }