fullnative 0.1.0 → 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/CHANGELOG.md CHANGED
@@ -18,6 +18,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
18
18
  - `ProcessError` class: structured error with command, args, kind, exitCode, signal, stderr, cause.
19
19
  - `Shell` session: run, $ tagged template, pipe, chain, ifOk, ifFail, bg, jobs registry, killAll, cd, set/unset, alias/unalias, history.
20
20
  - `Job` class: name, autoRestart, restartCount, onRestart, kill, wait, result, full LiveProcess forwarding.
21
- - `env` module: load (with `${VAR}` interpolation via `util.parseEnv`), get, require.
21
+ - `env` module: load (with `${VAR}` interpolation via `util.parseEnv`), get, requireEnv.
22
22
  - 150 tests covering all modules.
23
23
  - JSDoc in Spanish across all public APIs.
package/README.md CHANGED
@@ -1,105 +1,409 @@
1
1
  # fullnative
2
2
 
3
- TypeScript toolchain for Node.js scripting — files, folders, processes, and shell sessions with a clean, object-oriented API.
3
+ TypeScript toolchain for Node.js — files, folders, processes, shell sessions, and environment variables with a clean, object-oriented API for stateful resources and functional utilities for the rest.
4
+
5
+ [![npm](https://img.shields.io/npm/v/fullnative.svg)](https://www.npmjs.com/package/fullnative)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
4
7
 
5
8
  ## Why
6
9
 
7
10
  Node's built-in modules are powerful but verbose. `fullnative` wraps them in simple objects that are pleasant to use in scripts, CLIs, and build tools — no callbacks, no boilerplate, just objects.
8
11
 
12
+ - **Zero dependencies** — uses only Node.js built-ins (`fs`, `child_process`, `crypto`, `util.parseEnv`)
13
+ - **Fully typed** — ships with `.d.ts` declarations for every module
14
+ - **Object-oriented for stateful resources** (`File`, `Folder`, `Process`, `Shell` are classes you instantiate and chain), **functional utilities for the rest** (`env` module is plain functions)
15
+ - **Node.js >= 20.12** — takes advantage of native `util.parseEnv` for `.env` parsing
16
+ - **ESM only** — ships as `"type": "module"` with `import`/`export`. No CJS support.
17
+
9
18
  ## Install
10
19
 
11
20
  ```bash
21
+ npm install fullnative
22
+ # or
12
23
  pnpm add fullnative
24
+ # or
25
+ yarn add fullnative
13
26
  ```
14
27
 
15
28
  ## Quick start
16
29
 
17
30
  ```ts
18
- import { File, Folder, Process, Shell } from "fullnative";
19
- ```
31
+ import { File, Folder, Process, Shell, load } from "fullnative";
20
32
 
21
- ### File
33
+ // Load .env variables with ${VAR} interpolation
34
+ await load();
22
35
 
23
- ```ts
36
+ // Work with files
24
37
  const config = new File("./config.json");
25
-
26
38
  await config.writeJson({ port: 3000 });
27
- const { port } = await config.readJson<{ port: number }>();
28
39
 
29
- await config.replace("v1.0.0", "v2.0.0");
30
- const hash = await config.hash("sha256");
31
- await config.copyTo("./backup/config.json");
40
+ // Run commands
41
+ const proc = new Process();
42
+ const result = await proc.run("git", "log", "--oneline");
43
+ console.log(result.stdout);
32
44
  ```
33
45
 
34
- ### Folder
46
+ ---
47
+
48
+ ## Table of Contents
49
+
50
+ - [File](#file)
51
+ - [Folder](#folder)
52
+ - [Process](#process)
53
+ - [Command](#command-immutable-builder)
54
+ - [LiveProcess](#liveprocess-interactive-handle)
55
+ - [Result](#result-finished-command)
56
+ - [ProcessError](#processerror)
57
+ - [Shell](#shell)
58
+ - [Job](#job)
59
+ - [env](#env)
60
+ - [API Reference](#api-reference)
61
+ - [Requirements](#requirements)
62
+ - [Roadmap](#roadmap)
63
+
64
+ ---
65
+
66
+ ## File
67
+
68
+ Represents a single file on disk. All operations are lazy — they read/write only when invoked.
35
69
 
36
70
  ```ts
71
+ import { File } from "fullnative";
72
+
73
+ const file = new File("./data.txt");
74
+
75
+ // Read & write
76
+ await file.write("hello world");
77
+ const text = await file.read(); // "hello world"
78
+
79
+ // JSON
80
+ const cfg = new File("./config.json");
81
+ await cfg.writeJson({ port: 3000, host: "0.0.0.0" });
82
+ const { port, host } = await cfg.readJson<{ port: number; host: string }>();
83
+
84
+ // Append & prepend
85
+ await file.append("\nsecond line");
86
+ await file.prepend("first line\n");
87
+
88
+ // Replace (first match) and replaceMany (multiple in sequence)
89
+ await file.replace("old", "new");
90
+ await file.replaceMany([
91
+ { search: "v1", replacement: "v2" },
92
+ { search: "alpha", replacement: "beta" },
93
+ ]);
94
+
95
+ // Hash & compare
96
+ const hash = await file.hash("sha256");
97
+ const same = await file.equals(new File("./other.txt"));
98
+ const matches = await file.contentEquals("exact content");
99
+
100
+ // Copy returns a NEW File; move/rename mutate this.path and return this
101
+ const backup = await file.copyTo("./backup/data.txt"); // new File
102
+ await file.moveTo("./archive/data.txt"); // same instance, path updated
103
+ await file.rename("renamed.txt"); // same instance, path updated
104
+
105
+ // Streams
106
+ const readable = file.readStream(); // Readable stream
107
+ const writable = file.writeStream(); // Writable stream
108
+
109
+ // Metadata
110
+ const size = await file.size(); // bytes
111
+ const empty = await file.isEmpty(); // true if 0 bytes
112
+ const exists = await file.exists();
113
+ const stat = await file.stat(); // fs.Stats
114
+
115
+ // Delete
116
+ await file.delete(); // returns true if existed
117
+ ```
118
+
119
+ **Key behaviors:**
120
+ - `write()`, `append()`, `prepend()` create parent directories automatically.
121
+ - `copyTo()` / `copyInto()` return a **new** `File` instance.
122
+ - `moveTo()` / `moveInto()` / `rename()` mutate `this.path` and return `this` for chaining.
123
+
124
+ ---
125
+
126
+ ## Folder
127
+
128
+ Represents a directory. Supports navigation, recursive listing, streaming walks, copying, moving, and file tree visualization.
129
+
130
+ ```ts
131
+ import { Folder } from "fullnative";
132
+
37
133
  const project = new Folder("./my-app");
38
- await project.ensure();
39
134
 
135
+ // Lifecycle
136
+ await project.ensure(); // create if missing
137
+ await project.create(); // mkdir -p
138
+ await project.clear(); // empty contents
139
+ await project.delete(true); // recursive delete
140
+
141
+ // Navigation (returns references, doesn't check existence)
142
+ const entry: File = project.file("index.ts");
143
+ const sub: Folder = project.dir("src");
144
+ await project.hasFile("package.json"); // true/false
145
+ await project.hasDir("node_modules"); // true/false
146
+
147
+ // Listing (direct children only)
148
+ const items = await project.list(); // (File | Folder)[]
149
+ const files = await project.listFiles(); // File[]
150
+ const dirs = await project.listDirs(); // Folder[]
40
151
  const tsFiles = await project.listByExt("ts");
41
- const entry = await project.find("index.ts");
42
- const tree = await project.tree(); // ASCII tree visualization
43
- await project.copyTo("./backup/my-app");
152
+
153
+ // Recursive walk (returns arrays)
154
+ const all = await project.walk(); // everything, recursively
155
+ const allFiles = await project.walkFiles();
156
+ const allDirs = await project.walkDirs();
157
+
158
+ // Streaming walk (async generators — memory-efficient for large trees)
159
+ for await (const item of project.walkIter()) {
160
+ console.log(item.path);
161
+ }
162
+ for await (const f of project.walkFilesIter()) {
163
+ console.log(f.name);
164
+ }
165
+
166
+ // Search
167
+ const found = await project.find("index.ts"); // File | undefined
168
+ const subdir = await project.findDir("components"); // Folder | undefined
169
+ const matched = await project.matchFiles(/\.test\.ts$/); // File[]
170
+
171
+ // Create inside
172
+ const newFile = await project.createFile("note.txt", "hello");
173
+ const newDir = await project.createDir("utils");
174
+
175
+ // Copy & move (same semantics as File)
176
+ const copy = await project.copyTo("./backup/my-app"); // new Folder
177
+ await project.moveTo("./archive/my-app"); // same instance
178
+ await project.rename("renamed-app"); // same instance
179
+
180
+ // Tree visualization
181
+ const tree = await project.tree();
182
+ console.log(tree);
183
+ // └── my-app/
184
+ // ├── index.ts
185
+ // ├── src/
186
+ // │ └── utils.ts
187
+ // └── package.json
44
188
  ```
45
189
 
46
- ### Process
190
+ ---
191
+
192
+ ## Process
193
+
194
+ Facade for executing native commands with a comfortable API.
47
195
 
48
196
  ```ts
197
+ import { Process } from "fullnative";
198
+
49
199
  const proc = new Process();
50
200
 
201
+ // Simple execution
51
202
  const result = await proc.run("git", "log", "--oneline");
52
- console.log(result.stdout);
53
- console.log(result.ok); // true if exitCode === 0
54
- console.log(result.lines); // stdout split into lines
55
-
56
- // Builder API
57
- const out = await proc
58
- .cmd("git")
59
- .withArgs("rev-parse", "--abbrev-ref", "HEAD")
60
- .in("/my/repo")
61
- .output();
62
-
63
- // Interactive
64
- const node = proc.spawn("node", "-i");
65
- node.sendLine("1 + 1");
66
- node.endInput();
67
- const res = await node.wait();
203
+ console.log(result.stdout); // captured output
204
+ console.log(result.ok); // true if exitCode === 0
205
+ console.log(result.exitCode); // 0
206
+ console.log(result.lines); // stdout split into lines (empty lines filtered)
207
+ console.log(result.durationMs); // execution time in ms
208
+
209
+ // Get stdout only (trimmed)
210
+ const branch = await proc.output("git", "rev-parse", "--abbrev-ref", "HEAD");
211
+
212
+ // Shell-interpreted execution
213
+ const r = await proc.shell("echo hello && ls -la");
68
214
 
69
215
  // Check if a binary exists
70
216
  if (await proc.exists("docker")) {
71
217
  await proc.run("docker", "compose", "up", "-d");
72
218
  }
219
+
220
+ // Resolve binary path
221
+ const nodePath = await proc.which("node"); // "/usr/local/bin/node" | null
222
+ ```
223
+
224
+ ### Command (immutable builder)
225
+
226
+ Build a command fluently before executing it. Each method returns a **new** `Command`.
227
+
228
+ ```ts
229
+ const cmd = proc
230
+ .cmd("npm")
231
+ .withArgs("test", "--coverage")
232
+ .in("/my/project") // set cwd
233
+ .withEnv({ CI: "true" }) // merge env vars
234
+ .withTimeout(30000) // kill after 30s
235
+ .withInput("stdin data\n") // pipe to stdin
236
+ .throwOnError(); // reject on non-zero exit
237
+
238
+ const result = await cmd.run(); // execute, returns Result
239
+ const out = await cmd.output(); // stdout.trim()
240
+ const handle = cmd.spawn(); // returns LiveProcess
241
+ ```
242
+
243
+ ### LiveProcess (interactive handle)
244
+
245
+ A running process you can interact with in real time.
246
+
247
+ ```ts
248
+ const repl = proc.spawn("node", "-i");
249
+
250
+ // Send input
251
+ repl.sendLine("1 + 1");
252
+ repl.sendLine("process.exit()");
253
+ repl.endInput();
254
+
255
+ // Read output
256
+ repl.onStdout((chunk) => process.stdout.write(chunk));
257
+ repl.onStderr((chunk) => process.stderr.write(chunk));
258
+ repl.onOutput((chunk) => console.log(chunk.toString())); // stdout + stderr combined
259
+
260
+ // Lifecycle
261
+ console.log(repl.running); // true
262
+ console.log(repl.pid); // number
263
+ console.log(repl.elapsed); // ms since start (live)
264
+
265
+ const result = await repl.wait(); // blocks until exit, returns Result
266
+ console.log(repl.stopped); // true if killed (vs natural exit)
267
+ console.log(repl.exitCode); // 0
268
+ console.log(repl.signal); // null | "SIGTERM" | ...
269
+
270
+ // Kill
271
+ repl.kill("SIGTERM");
272
+ repl.forceKill(); // SIGKILL
73
273
  ```
74
274
 
75
- ### Shell
275
+ ### Result (finished command)
276
+
277
+ Immutable result of a completed command.
76
278
 
77
279
  ```ts
280
+ const result = await proc.run("node", "-e", "console.log(JSON.stringify({ok:true}))");
281
+
282
+ result.stdout; // '{"ok":true}\n'
283
+ result.stderr; // ''
284
+ result.exitCode; // 0
285
+ result.ok; // true
286
+ result.failed; // false
287
+ result.output; // stdout + stderr, trimmed
288
+ result.lines; // ["{\"ok\":true}"]
289
+ result.durationMs; // 12
290
+ result.json(); // { ok: true } — parses stdout as JSON
291
+ result.json<{ ok: boolean }>(); // typed
292
+
293
+ // Throw on failure
294
+ result.throwIfFailed(); // throws ProcessError if exitCode !== 0, returns this if ok
295
+ ```
296
+
297
+ ---
298
+
299
+ ## ProcessError
300
+
301
+ Structured error thrown by `throwIfFailed()`, `throwOnError()`, and `wait()` on spawn failure.
302
+
303
+ ```ts
304
+ import { Process, ProcessError } from "fullnative";
305
+
306
+ const proc = new Process();
307
+
308
+ try {
309
+ const result = await proc.run("npm", "run", "build");
310
+ result.throwIfFailed();
311
+ } catch (err) {
312
+ if (err instanceof ProcessError) {
313
+ err.command; // "npm"
314
+ err.args; // ["run", "build"]
315
+ err.kind; // "exit" (non-zero exit) | "spawn" (failed to start)
316
+ err.exitCode; // 1 (or null on spawn failure)
317
+ err.signal; // null | "SIGTERM" | ...
318
+ err.stderr; // captured stderr output
319
+ err.cause; // original Error (on spawn failure)
320
+ }
321
+ }
322
+ ```
323
+
324
+ The `kind` field distinguishes between:
325
+ - `"exit"` — the process started and exited with a non-zero code.
326
+ - `"spawn"` — the process couldn't even start (e.g. ENOENT, permission denied).
327
+
328
+ ---
329
+
330
+ ## Shell
331
+
332
+ A shell session with state (cwd, env, aliases, history) that delegates to `Process` internally.
333
+
334
+ ```ts
335
+ import { Shell } from "fullnative";
336
+
78
337
  const sh = new Shell({ cwd: "/my/project" });
79
338
 
80
339
  // Run shell scripts
81
- await sh.run("npm install && npm run build");
340
+ const result = await sh.run("npm install && npm run build");
341
+ console.log(result.ok);
342
+
343
+ // Change directory
344
+ sh.cd("./src");
345
+ console.log(sh.cwd); // "/my/project/src"
346
+
347
+ // Environment variables
348
+ sh.set("NODE_ENV", "production");
349
+ sh.unset("NODE_ENV");
350
+
351
+ // Aliases
352
+ sh.alias("deploy", "npm run deploy");
353
+ const r = await sh.run("deploy"); // runs "npm run deploy"
354
+ sh.unalias("deploy");
355
+
356
+ // History
357
+ console.log(sh.history); // [{ command, startedAt, durationMs, exitCode, ok }]
358
+ console.log(sh.lastCommand()); // HistoryItem | undefined
359
+ sh.clearHistory();
360
+ ```
361
+
362
+ #### `$` — Tagged template with safe quoting
82
363
 
83
- // Tagged template with safe quoting
364
+ Interpolates values with automatic shell quoting. Strings with special characters are escaped; arrays expand to separate arguments.
365
+
366
+ ```ts
84
367
  const branch = "main";
368
+ const files = ["a.ts", "b.ts"];
369
+
85
370
  await sh.$`git push origin ${branch}`;
371
+ await sh.$`echo ${files}`; // echo a.ts b.ts
372
+ await sh.$`echo ${42}`; // echo 42
373
+ await sh.$`echo ${"a b'c"}`; // echo 'a b'\''c' — safely quoted
374
+ ```
375
+
376
+ #### `pipe()` — Pipelines
86
377
 
87
- // Pipeline
88
- const errors = await sh.pipe("cat log.txt", "grep ERROR", "wc -l");
378
+ Pipe output between commands, left to right.
89
379
 
90
- // Chain (stop on error)
380
+ ```ts
381
+ const result = await sh.pipe("cat log.txt", "grep ERROR", "wc -l");
382
+ console.log(result.stdout.trim()); // error count
383
+ ```
384
+
385
+ #### `chain()` — Sequential execution (stop on error)
386
+
387
+ ```ts
91
388
  const results = await sh.chain("npm run lint", "npm run build", "npm test");
92
389
  const allOk = results.every((r) => r.ok);
93
-
94
- // Conditionals
95
- await sh.ifOk("npm test", "npm run deploy");
96
390
  ```
97
391
 
98
- ### Background jobs
392
+ #### `ifOk()` / `ifFail()` — Conditionals
99
393
 
100
394
  ```ts
101
- const sh = new Shell();
395
+ // Run second command only if first succeeds
396
+ const r1 = await sh.ifOk("npm test", "npm run deploy");
102
397
 
398
+ // Run second command only if first fails
399
+ const r2 = await sh.ifFail("npm run build", "echo build failed, running fallback");
400
+ ```
401
+
402
+ #### `bg()` — Background jobs
403
+
404
+ Launch long-running processes with naming, auto-restart, and a job registry.
405
+
406
+ ```ts
103
407
  const dev = sh.bg("npm run dev", { name: "dev" });
104
408
  dev.onOutput((chunk) => process.stdout.write(chunk));
105
409
 
@@ -107,55 +411,134 @@ const server = sh.bg("npm run serve", {
107
411
  name: "server",
108
412
  autoRestart: true,
109
413
  });
110
- server.onRestart((job) => console.log(`Restarted (${job.restartCount})`));
414
+ server.onRestart((job) => console.log(`Restarted (${job.restartCount}x)`));
415
+
416
+ // Inspect running jobs
417
+ console.log(sh.activeJobs.map((j) => j.name)); // ["dev", "server"]
418
+ console.log(sh.jobs.get("dev")); // Job | undefined
419
+ console.log(sh.job("server")); // Job | undefined
111
420
 
112
- // Introspect
113
- console.log(sh.activeJobs.map((j) => j.name));
421
+ // Wait for a job to finish
422
+ const result = await dev.result();
114
423
 
115
- // Clean up everything
424
+ // Kill all background jobs
116
425
  sh.killAll();
117
426
  ```
118
427
 
119
- ### Error handling
428
+ ### Job
429
+
430
+ A background process managed by a `Shell` session.
120
431
 
121
432
  ```ts
122
- import { ProcessError } from "fullnative";
433
+ const job = sh.bg("npm run dev", { name: "dev", autoRestart: true });
434
+
435
+ job.name; // "dev"
436
+ job.script; // "npm run dev"
437
+ job.autoRestart; // true
438
+ job.restartCount; // 0 (increments on each restart)
439
+ job.pid; // number
440
+ job.running; // boolean
441
+ job.ended; // boolean
442
+ job.stopped; // true if killed (vs natural exit)
443
+ job.elapsed; // ms since start (live)
444
+ job.command; // resolved command
445
+ job.exitCode; // number | null
446
+ job.stdin; // Writable stream
447
+ job.stdout; // Readable stream
448
+ job.stderr; // Readable stream
449
+
450
+ // Interaction
451
+ job.write("input data\n");
452
+ job.sendLine("command");
453
+ job.endInput();
454
+
455
+ // Callbacks
456
+ job.onStdout((chunk) => console.log(chunk.toString()));
457
+ job.onStderr((chunk) => console.error(chunk.toString()));
458
+ job.onOutput((chunk) => console.log(chunk.toString()));
459
+ job.onExit((result) => console.log("exited", result.exitCode));
460
+ job.onRestart((j) => console.log("restarted", j.restartCount));
461
+
462
+ // Lifecycle
463
+ const result = await job.wait(); // blocks until exit
464
+ await job.result(); // same as wait(), returns Result
465
+ job.kill("SIGTERM");
466
+ job.forceKill();
467
+ ```
123
468
 
124
- try {
125
- await proc.run("npm", "run", "build").then((r) => r.throwIfFailed());
126
- } catch (err) {
127
- if (err instanceof ProcessError) {
128
- console.log(err.command); // "npm"
129
- console.log(err.kind); // "exit" | "spawn"
130
- console.log(err.exitCode); // 1
131
- console.log(err.stderr); // build error output
132
- }
133
- }
469
+ ---
470
+
471
+ ## env
472
+
473
+ Load `.env` files with `${VAR}` interpolation using Node's native `util.parseEnv`.
474
+
475
+ ```ts
476
+ import { load, get, requireEnv } from "fullnative";
477
+
478
+ // Load .env (default path: ".env")
479
+ await load();
480
+ await load("./.env.production");
481
+
482
+ // Get a variable
483
+ const port = get("PORT"); // string | undefined
484
+ const host = get("HOST", "localhost"); // string (fallback)
485
+ const key = requireEnv("API_KEY"); // string — throws if missing
134
486
  ```
135
487
 
136
- ## API
488
+ **Behavior:**
489
+ - Parses with `util.parseEnv` (handles quotes, comments, multiline values).
490
+ - Interpolates `${VAR}` references across multiple passes (resolves chains like `A=${B}`, `B=${C}`, `C=value`).
491
+ - Circular references (`A=${B}`, `B=${A}`) are cut off after 5 passes — no infinite loops.
492
+ - Variables already set in `process.env` are **never overwritten** — the real environment always wins.
493
+ - `requireEnv("KEY")` throws `Error` with the key name if the variable is missing.
494
+ - **`load()` rejects if the file doesn't exist** — wrap in try/catch if you want optional loading. There is no silent mode.
495
+
496
+ ---
137
497
 
138
- | Class | Description |
498
+ ## API Reference
499
+
500
+ | Class / Function | Description |
139
501
  |---|---|
140
- | `File` | File operations: read, write, JSON, hash, streams, copy, move |
141
- | `Folder` | Directory operations: list, walk, tree, find, matchFiles, watch, copy, move |
142
- | `Process` | Execute native commands: run, spawn, shell, exists, which |
502
+ | `File` | File operations: read, write, JSON, hash, streams, copy, move, rename, replace, permissions, truncate, touch |
503
+ | `Folder` | Directory operations: list, walk, walkIter, walkFilesIter, tree, find, matchFiles, watch, copy, move, rename |
504
+ | `Process` | Execute native commands: run, output, shell, spawn, spawnScript, exists, which |
143
505
  | `Command` | Immutable builder: withArgs, in, withEnv, withTimeout, withInput, throwOnError |
144
- | `LiveProcess` | Running process: stdin/stdout/stderr, kill, wait, onOutput, elapsed, stopped |
145
- | `Result` | Finished command: stdout, stderr, json, lines, ok, throwIfFailed |
146
- | `Shell` | Shell session: run, $, pipe, chain, ifOk, ifFail, bg, jobs, killAll |
147
- | `Job` | Background process: name, autoRestart, onRestart, kill, wait |
148
- | `ProcessError` | Structured error: command, kind, exitCode, signal, stderr, cause |
506
+ | `LiveProcess` | Running process: stdin/stdout/stderr, kill, forceKill, wait, onOutput, elapsed, stopped |
507
+ | `Result` | Finished command: stdout, stderr, output, lines, json, ok, failed, throwIfFailed |
508
+ | `ProcessError` | Structured error: command, args, kind, exitCode, signal, stderr, cause |
509
+ | `Shell` | Shell session: run, $, pipe, chain, ifOk, ifFail, bg, cd, set/unset, alias, history, killAll |
510
+ | `Job` | Background process: name, autoRestart, restartCount, onRestart, kill, wait, result |
511
+ | `load` | Load `.env` file with `${VAR}` interpolation — rejects if file missing |
512
+ | `get` | Get env var with optional fallback |
513
+ | `requireEnv` | Get env var or throw if missing |
514
+
515
+ ## Requirements
516
+
517
+ - **Node.js >= 20.12** (requires `util.parseEnv`)
518
+ - **ESM only** — no CJS support. Use `import`/`export` in your project.
519
+ - **Zero runtime dependencies**
149
520
 
150
521
  ## Development
151
522
 
152
523
  ```bash
153
524
  pnpm install
154
- pnpm test # run tests
525
+ pnpm test # run 150 tests
155
526
  pnpm run typecheck # type check
156
527
  pnpm run build # compile to dist/
157
528
  ```
158
529
 
530
+ ## Roadmap
531
+
532
+ Planned for future releases:
533
+
534
+ - **`sleep(ms)`** — promise-based delay
535
+ - **`waitFor(fn, opts)`** — poll until a condition is met
536
+ - **`retry(fn, opts)`** — retry with backoff strategies
537
+ - **`timeout(promise, ms)`** — race a promise against a timer
538
+ - **`onShutdown(fn)`** — register graceful shutdown handlers (SIGINT/SIGTERM)
539
+ - **`tempDir()`** — create and auto-cleanup a temporary directory
540
+ - **Dual CJS/ESM support** — if there's demand from legacy projects
541
+
159
542
  ## License
160
543
 
161
- MIT
544
+ MIT © [Zeltri](https://github.com/zeltri)
@@ -26,9 +26,10 @@ export declare function get(key: string): string | undefined;
26
26
  export declare function get(key: string, fallback: string): string;
27
27
  /**
28
28
  * Devuelve el valor de una env var, o lanza `Error` si no existe.
29
+ * Renombrado desde `require` para evitar colisión con la función global de CJS.
29
30
  * @param key Nombre de la variable
30
31
  * @returns El valor
31
32
  * @throws {Error} Si la variable no existe, con un mensaje que incluye el nombre de la key
32
33
  */
33
- export declare function require(key: string): string;
34
+ export declare function requireEnv(key: string): string;
34
35
  //# sourceMappingURL=env.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../src/core/env/env.ts"],"names":[],"mappings":"AA4FA;;;;;;;;;;;GAWG;AACH,wBAAsB,IAAI,CAAC,OAAO,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAY1D;AAED;;;;GAIG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAErD;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;AAgB3D;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM3C"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../src/core/env/env.ts"],"names":[],"mappings":"AA4FA;;;;;;;;;;;GAWG;AACH,wBAAsB,IAAI,CAAC,OAAO,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAY1D;AAED;;;;GAIG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAErD;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;AAgB3D;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM9C"}
@@ -120,11 +120,12 @@ export function get(key, fallback) {
120
120
  }
121
121
  /**
122
122
  * Devuelve el valor de una env var, o lanza `Error` si no existe.
123
+ * Renombrado desde `require` para evitar colisión con la función global de CJS.
123
124
  * @param key Nombre de la variable
124
125
  * @returns El valor
125
126
  * @throws {Error} Si la variable no existe, con un mensaje que incluye el nombre de la key
126
127
  */
127
- export function require(key) {
128
+ export function requireEnv(key) {
128
129
  const value = process.env[key];
129
130
  if (value === undefined) {
130
131
  throw new Error(`Missing required environment variable: ${key}`);
@@ -1 +1 @@
1
- {"version":3,"file":"env.js","sourceRoot":"","sources":["../../../src/core/env/env.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../../src/core/env/env.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,2 @@
1
- /**
2
- * Punto de entrada del módulo de variables de entorno.
3
- *
4
- * Reexporta la API pública para cargar, leer y exigir variables de entorno:
5
- * - `load(path?)`: carga un archivo `.env` y lo mergea a `process.env`.
6
- * - `get(key)` / `get(key, fallback)`: lee una variable con o sin valor por defecto.
7
- * - `require(key)`: lee una variable o lanza si no existe.
8
- */
9
- export { load, get, require } from "./env.js";
1
+ export { load, get, requireEnv } from "./env.js";
10
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/env/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/env/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC"}
@@ -1,10 +1,2 @@
1
- /**
2
- * Punto de entrada del módulo de variables de entorno.
3
- *
4
- * Reexporta la API pública para cargar, leer y exigir variables de entorno:
5
- * - `load(path?)`: carga un archivo `.env` y lo mergea a `process.env`.
6
- * - `get(key)` / `get(key, fallback)`: lee una variable con o sin valor por defecto.
7
- * - `require(key)`: lee una variable o lanza si no existe.
8
- */
9
- export { load, get, require } from "./env.js";
1
+ export { load, get, requireEnv } from "./env.js";
10
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/env/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/env/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC"}
@@ -2,5 +2,5 @@ export { File } from "./file/File.js";
2
2
  export { Folder } from "./file/Folder.js";
3
3
  export { Process, Command, LiveProcess, Result, ProcessError, type ProcessOptions, } from "./process/index.js";
4
4
  export { Shell, Job, type ShellConfig, type HistoryItem, type JobOptions, } from "./shell/index.js";
5
- export { load, get, require } from "./env/index.js";
5
+ export { load, get, requireEnv } from "./env/index.js";
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EACL,OAAO,EACP,OAAO,EACP,WAAW,EACX,MAAM,EACN,YAAY,EACZ,KAAK,cAAc,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,KAAK,EACL,GAAG,EACH,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,UAAU,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EACL,OAAO,EACP,OAAO,EACP,WAAW,EACX,MAAM,EACN,YAAY,EACZ,KAAK,cAAc,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,KAAK,EACL,GAAG,EACH,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,UAAU,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC"}
@@ -2,5 +2,5 @@ export { File } from "./file/File.js";
2
2
  export { Folder } from "./file/Folder.js";
3
3
  export { Process, Command, LiveProcess, Result, ProcessError, } from "./process/index.js";
4
4
  export { Shell, Job, } from "./shell/index.js";
5
- export { load, get, require } from "./env/index.js";
5
+ export { load, get, requireEnv } from "./env/index.js";
6
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EACL,OAAO,EACP,OAAO,EACP,WAAW,EACX,MAAM,EACN,YAAY,GAEb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,KAAK,EACL,GAAG,GAIJ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EACL,OAAO,EACP,OAAO,EACP,WAAW,EACX,MAAM,EACN,YAAY,GAEb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,KAAK,EACL,GAAG,GAIJ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullnative",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "TypeScript toolchain for Node.js — files, folders, processes, shell sessions, and env loading with a clean, object-oriented API.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -61,4 +61,4 @@
61
61
  "engines": {
62
62
  "node": ">=20.12.0"
63
63
  }
64
- }
64
+ }