bunosh 0.5.6 → 0.5.8

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
@@ -187,7 +187,7 @@ Bunoshfile.api.js # bunosh api:deploy, bunosh api:test
187
187
  Built-in tasks are available via `global.bunosh`:
188
188
 
189
189
  ```javascript
190
- const { exec, shell, fetch, writeToFile, copyFile, task } = global.bunosh;
190
+ const { exec, shell, fetch, writeToFile, copyFile, task, assert } = global.bunosh;
191
191
  ```
192
192
 
193
193
  > Global variables are used instead of imports so bunosh works with the single-executable on any platform.
@@ -195,6 +195,7 @@ const { exec, shell, fetch, writeToFile, copyFile, task } = global.bunosh;
195
195
  * Async tasks: `exec`, `shell`, `fetch`
196
196
  * Sync tasks: `writeToFile`, `copyFile`
197
197
  * Task wrapper: `task`
198
+ * Precondition guard: `assert`
198
199
 
199
200
  Each task returns a `TaskResult` object:
200
201
 
@@ -273,6 +274,30 @@ For details see the [Bun shell](https://bun.sh/docs/runtime/shell) reference.
273
274
  | `exec` | Single command execution | spawn process | Node.js + Bun, platform dependent |
274
275
  | `shell` | Cross-platform shell commands | Bun shell | Bun only, cross-platform |
275
276
 
277
+ ### `assert`
278
+
279
+ Guard a command on a precondition. If the condition is falsy, `assert` prints a red failure line and records a failed task — the run continues, and the process exits with code 1 at the end:
280
+
281
+ ```javascript
282
+ export async function deploy() {
283
+ assert(process.env.TOKEN, 'TOKEN must be set');
284
+ assert(await Bun.file('dist/bundle.js').exists(), 'bundle not built');
285
+ await shell`./scripts/deploy.sh`;
286
+ }
287
+ ```
288
+
289
+ With `task.stopOnFailures()` enabled, a failed `assert` exits immediately at that line:
290
+
291
+ ```javascript
292
+ export async function strict() {
293
+ task.stopOnFailures();
294
+ assert(process.env.TOKEN, 'TOKEN must be set');
295
+ await shell`./scripts/deploy.sh`;
296
+ }
297
+ ```
298
+
299
+ `assert` does not throw a JavaScript exception in default mode — it records the failure and execution continues. Use `task.stopOnFailures()` (or `return` after the `assert`) when you need a hard stop.
300
+
276
301
  ### `fetch`
277
302
 
278
303
  Wraps the fetch API as a task:
package/index.js CHANGED
@@ -5,10 +5,11 @@ import fetch from "./src/tasks/fetch.js";
5
5
  import writeToFile from "./src/tasks/writeToFile.js";
6
6
  import copyFile from "./src/tasks/copyFile.js";
7
7
  import ai from "./src/tasks/ai.js";
8
+ import assert from "./src/tasks/assert.js";
8
9
  import { ask, yell, say } from "./src/io.js";
9
10
  import { task, tryTask, stopOnFail, ignoreFail, stopOnFailures, ignoreFailures, silence, prints, silent, TaskResult } from "./src/task.js";
10
11
 
11
- export { exec, shell, fetch, writeToFile, copyFile, ai, ask, yell, say, task, tryTask, stopOnFail, ignoreFail, stopOnFailures, ignoreFailures, silence, prints, silent, TaskResult };
12
+ export { exec, shell, fetch, writeToFile, copyFile, ai, assert, ask, yell, say, task, tryTask, stopOnFail, ignoreFail, stopOnFailures, ignoreFailures, silence, prints, silent, TaskResult };
12
13
 
13
14
  export function buildCmd(cmd) {
14
15
  return function (args) {
@@ -26,6 +27,7 @@ global.bunosh = {
26
27
  writeToFile,
27
28
  copyFile,
28
29
  ai,
30
+ assert,
29
31
  stopOnFail,
30
32
  ignoreFail,
31
33
  task,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunosh",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "Task runner that turns JavaScript functions into CLI commands. Runs on Bun and Node.js.",
5
5
  "type": "module",
6
6
  "module": "index.js",
package/src/task.js CHANGED
@@ -47,6 +47,22 @@ export function getIgnoreFailuresMode() {
47
47
  return ignoreFailuresMode;
48
48
  }
49
49
 
50
+ export function isStopOnFailuresMode() {
51
+ return stopOnFailuresMode;
52
+ }
53
+
54
+ export function isTestEnv() {
55
+ const commandArgs = process.argv.slice(2);
56
+ return process.env.NODE_ENV === 'test' ||
57
+ commandArgs.some(arg => {
58
+ const lower = arg.toLowerCase();
59
+ return lower.includes('vitest') ||
60
+ lower.includes('jest') ||
61
+ lower === '--test' ||
62
+ lower.startsWith('test:');
63
+ });
64
+ }
65
+
50
66
  export function silence() {
51
67
  globalSilenceMode = true;
52
68
  }
@@ -222,24 +238,12 @@ export async function task(name, fn, isSilent = false) {
222
238
  printer.error(name, err);
223
239
  runningTasks.delete(taskInfo.id);
224
240
 
225
- // Don't exit during testing
226
- const commandArgs = process.argv.slice(2);
227
- const isTestEnvironment = process.env.NODE_ENV === 'test' ||
228
- typeof Bun?.jest !== 'undefined' ||
229
- commandArgs.some(arg => {
230
- const lowerArg = arg.toLowerCase();
231
- return lowerArg.includes('vitest') ||
232
- lowerArg.includes('jest') ||
233
- lowerArg === '--test' ||
234
- lowerArg.startsWith('test:');
235
- });
236
-
237
- // Exit immediately if stopOnFailures mode is enabled
241
+ const isTestEnvironment = isTestEnv();
242
+
238
243
  if (stopOnFailuresMode && !isTestEnvironment) {
239
244
  process.exit(1);
240
245
  }
241
-
242
- // Also exit if stopFailToggle is enabled (legacy behavior)
246
+
243
247
  if (stopFailToggle && !isTestEnvironment) {
244
248
  process.exit(1);
245
249
  }
@@ -0,0 +1,23 @@
1
+ import { createTaskInfo, finishTaskInfo, getCurrentTaskId, runningTasks, isStopOnFailuresMode, isTestEnv } from '../task.js';
2
+ import Printer from '../printer.js';
3
+
4
+ export default function assert(condition, message = 'Assertion failed') {
5
+ const currentTaskId = getCurrentTaskId();
6
+ const parent = currentTaskId ? runningTasks.get(currentTaskId) : null;
7
+ const isParentSilent = parent?.isSilent || false;
8
+
9
+ const taskInfo = createTaskInfo(message, currentTaskId, isParentSilent);
10
+ const printer = new Printer('assert', taskInfo.id);
11
+
12
+ if (condition) {
13
+ printer.finish(message);
14
+ finishTaskInfo(taskInfo, true, null, message);
15
+ return;
16
+ }
17
+
18
+ const error = new Error(message);
19
+ printer.error(message, error);
20
+ finishTaskInfo(taskInfo, false, error, message);
21
+
22
+ if (isStopOnFailuresMode() && !isTestEnv()) process.exit(1);
23
+ }
package/types.d.ts CHANGED
@@ -34,8 +34,9 @@ declare global {
34
34
  copyFile(src: string, dst: string): void;
35
35
  stopOnFail(enable?: boolean): void;
36
36
  ignoreFail(enable?: boolean): void;
37
- buildCmd(cmd: string): (args: string) => Promise<any>;
37
+ buildCmd(cmd: string): (args: string) => Promise<any>;
38
38
  task(name: string | Function, fn?: Function): Promise<any>;
39
+ assert(condition: any, message?: string): asserts condition;
39
40
  };
40
41
  }
41
42
  }