rura 1.0.2 → 1.0.4

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
@@ -49,38 +49,51 @@ type Ctx = typeof ctx;
49
49
  - Hooks may specify an `order`; omitted values default to `0`.
50
50
 
51
51
  ```ts
52
+ import { createHook, type RuraHook } from "rura";
53
+
52
54
  // With createHook (recommended for convenience)
53
55
  const addOne = createHook<Ctx>("add-one", (ctx) => {
54
56
  ctx.count += 1;
55
57
  }); // order: 0
56
58
 
57
59
  // Manual hook definition (full control)
58
- const stopIfEven: Hook<Ctx, number> = {
60
+ const stopIfEven: RuraHook<Ctx, number> = {
59
61
  name: "stop-if-even",
60
62
  run: (ctx) => {
61
63
  if (ctx.count % 2 === 0) {
62
- return { done: true, output: ctx.count };
64
+ return { early: true, output: ctx.count };
63
65
  }
64
66
  },
65
67
  order: 2, // order: 2
66
68
  };
67
69
  ```
68
70
 
69
- > Rura executes hooks in order and exits early when a hook returns `{ done: true }`.
71
+ - Rura executes hooks in order and exits early when a hook returns `{ early: true, output }`.
72
+
73
+ - When the pipeline does not exit early, `output` is omitted and `early` becomes `false`.
70
74
 
71
75
  #### 3. Run the pipeline
72
76
 
73
77
  ```ts
74
78
  const result = await runRura(ctx, [addOne, stopIfEven]);
75
79
 
76
- console.log(result); // -> 2 (early exit triggered)
80
+ console.log(result);
81
+ // -> {
82
+ // early: true,
83
+ // ctx: { count: 2 },
84
+ // output: 2
85
+ // }
77
86
  ```
78
87
 
88
+ _If you prefer working with a reusable pipeline instance,
89
+ you can use the **Pipeline Builder**, introduced below:_
90
+
79
91
  ---
80
92
 
81
93
  ## Pipeline Builder
82
94
 
83
- **createRura()** - Creates a lightweight, composable pipeline instance.
95
+ **createRura()** - Creates a lightweight, composable pipeline instance
96
+ that can register hooks, merge with other pipelines, and be inspected.
84
97
 
85
98
  ```ts
86
99
  const rura = createRura<Context, Output>();
@@ -125,14 +138,19 @@ const pipeline = pipelineA.merge(pipelineB);
125
138
  // Run it
126
139
  const result = await pipeline.run({ value: 1 });
127
140
 
128
- console.log(result); // -> { value: 9 }
141
+ console.log(result);
142
+ // -> {
143
+ // early: false,
144
+ // ctx: { value: 9 }
145
+ // }
129
146
  ```
130
147
 
131
148
  #### Pipeline Instance Methods
132
149
 
133
- | Method | Description | Parameters | Returns |
134
- | ---------------- | ---------------------------------------------------- | ---------- | ---------------------------- |
135
- | **use(hook)** | Adds a hook to the pipeline. | `hook` | `this` (chainable) |
136
- | **merge(other)** | Merges hooks from another pipeline instance. | `other` | `this` (chainable) |
137
- | **getHooks()** | Returns all registered hooks. | – | `Hook[]` |
138
- | **run(ctx)** | Executes the pipeline **_(equivalent to runRura)_**. | `ctx` | `Promise<Output \| Context>` |
150
+ | Method | Description | Parameters | Returns |
151
+ | ---------------- | ------------------------------------------------------ | ---------- | --------------------- |
152
+ | **use(hook)** | Adds a hook, normalizes its order, and re-sorts hooks. | `hook` | `this` (chainable) |
153
+ | **merge(other)** | Merges hooks from another pipeline and re-sorts them. | `other` | `this` (chainable) |
154
+ | **getHooks()** | Returns a sorted shallow copy of all registered hooks. | – | `RuraHook[]` |
155
+ | **debugHooks()** | Prints a formatted, human-readable hook list. | | `void` |
156
+ | **run(ctx)** | Executes the pipeline (delegates to `runRura`). | `ctx` | `Promise<RuraResult>` |
package/dist/index.cjs CHANGED
@@ -1,40 +1,61 @@
1
1
  'use strict';
2
2
 
3
- // src/is-done.ts
4
- function isDone(r) {
5
- return typeof r === "object" && r !== null && "done" in r;
6
- }
7
-
8
3
  // src/run-rura.ts
9
4
  async function runRura(ctx, hooks) {
10
- hooks.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
11
5
  for (const hook of hooks) {
12
6
  const result = await hook.run(ctx);
13
- if (isDone(result)) {
14
- return result.output;
7
+ const isEarlyReturn = result && result.early === true;
8
+ if (isEarlyReturn) {
9
+ return {
10
+ early: true,
11
+ ctx,
12
+ output: result.output
13
+ };
15
14
  }
16
15
  }
17
- return ctx;
16
+ return {
17
+ early: false,
18
+ ctx
19
+ };
18
20
  }
19
21
 
20
22
  // src/create-rura.ts
21
23
  function createRura(initialHooks = []) {
22
- const hooks = [...initialHooks];
24
+ const hooks = [...initialHooks].map((h) => applyDefaultOrder(h));
25
+ function applyDefaultOrder(h) {
26
+ return { ...h, order: h.order ?? 0 };
27
+ }
28
+ function sortHooks() {
29
+ hooks.sort((a, b) => a.order - b.order);
30
+ }
23
31
  function use(hook) {
24
- hooks.push(hook);
32
+ hooks.push(applyDefaultOrder(hook));
33
+ sortHooks();
25
34
  return api;
26
35
  }
27
36
  function merge(other) {
28
- other.getHooks().forEach((h) => hooks.push(h));
37
+ other.getHooks().forEach((h) => hooks.push(applyDefaultOrder(h)));
38
+ sortHooks();
29
39
  return api;
30
40
  }
31
41
  function getHooks() {
32
- return hooks;
42
+ return [...hooks];
43
+ }
44
+ function debugHooks() {
45
+ console.log(`
46
+ \u{1F4A7} Rura Pipeline (${hooks.length} hooks)`);
47
+ console.log("\u2500\u2500\u2500");
48
+ hooks.forEach(({ name, order }, i) => {
49
+ const paddedOrder = String(order).padStart(4, " ");
50
+ console.log(`${i + 1}. order: ${paddedOrder} | name: ${name}`);
51
+ });
52
+ console.log("\u2500\u2500\u2500\n");
33
53
  }
34
54
  async function run(ctx) {
35
55
  return runRura(ctx, hooks);
36
56
  }
37
- const api = { use, merge, getHooks, run };
57
+ const api = { use, merge, getHooks, debugHooks, run };
58
+ sortHooks();
38
59
  return api;
39
60
  }
40
61
 
package/dist/index.d.cts CHANGED
@@ -1,23 +1,83 @@
1
- type MaybePromise<T> = T | Promise<T>;
2
- interface Done<Output> {
3
- done: true;
4
- output: Output;
5
- }
6
- interface Hook<Context = unknown, Output = unknown> {
1
+ /**
2
+ * The return type of a Rura hook.
3
+ *
4
+ * - Return `void` to continue the pipeline.
5
+ * - Return `{ early: true, output }` to stop the pipeline immediately.
6
+ *
7
+ * Supports both sync and async forms.
8
+ */
9
+ type RuraHookResult<Out> = void | {
10
+ early: true;
11
+ output: Out;
12
+ } | Promise<void | {
13
+ early: true;
14
+ output: Out;
15
+ }>;
16
+ /**
17
+ * A single pipeline hook.
18
+ *
19
+ * Hooks receive the current context and may:
20
+ * - mutate it and continue, or
21
+ * - return `{ early: true, output }` to exit the pipeline early.
22
+ */
23
+ interface RuraHook<Ctx = unknown, Out = unknown> {
24
+ /** Unique name for debugging and inspection. */
7
25
  name: string;
26
+ /** Executes the hook. */
27
+ run(ctx: Ctx): RuraHookResult<Out>;
28
+ /** Optional execution order. Lower values run first. */
8
29
  order?: number;
9
- run(ctx: Context): MaybePromise<void | Done<Output>>;
30
+ }
31
+ /**
32
+ * Final result of a pipeline execution.
33
+ *
34
+ * - If `early` is true, the pipeline stopped before completing all hooks.
35
+ * - The `ctx` field always contains the final context.
36
+ * - The `output` field appears only when early termination occurs.
37
+ */
38
+ interface RuraResult<Ctx, Out> {
39
+ /** Indicates whether the pipeline exited early. */
40
+ early: boolean;
41
+ /** The final context after all executed hooks. */
42
+ ctx: Ctx;
43
+ /** The output value when the pipeline ends early. */
44
+ output?: Out;
10
45
  }
11
46
 
12
- declare function runRura<Context = unknown, Output = unknown>(ctx: Context, hooks: Hook<Context, Output>[]): Promise<Output | Context>;
47
+ /**
48
+ * Executes a list of hooks in order.
49
+ * - Stops early when a hook returns `{ early: true, output }`.
50
+ *
51
+ * @returns A `RuraResult` describing whether the pipeline ended early
52
+ * and the final context/output.
53
+ */
54
+ declare function runRura<Ctx = unknown, Out = unknown>(ctx: Ctx, hooks: RuraHook<Ctx, Out>[]): Promise<RuraResult<Ctx, Out>>;
13
55
 
14
- declare function createRura<Context = unknown, Output = unknown>(initialHooks?: Hook<Context, Output>[]): {
15
- use: (hook: Hook<Context, Output>) => /*elided*/ any;
16
- merge: (other: ReturnType<typeof createRura<Context, Output>>) => /*elided*/ any;
17
- getHooks: () => Hook<Context, Output>[];
18
- run: (ctx: Context) => Promise<Context | Output>;
56
+ /**
57
+ * Creates a reusable pipeline instance.
58
+ * Hooks can be registered, merged, inspected, and executed in order.
59
+ *
60
+ * @param initialHooks - Optional list of hooks to initialize the pipeline with.
61
+ *
62
+ * @returns An API for managing and running the pipeline.
63
+ */
64
+ declare function createRura<Ctx = unknown, Out = unknown>(initialHooks?: RuraHook<Ctx, Out>[]): {
65
+ use: (hook: RuraHook<Ctx, Out>) => /*elided*/ any;
66
+ merge: (other: ReturnType<typeof createRura<Ctx, Out>>) => /*elided*/ any;
67
+ getHooks: () => RuraHook<Ctx, Out>[];
68
+ debugHooks: () => void;
69
+ run: (ctx: Ctx) => Promise<RuraResult<Ctx, Out>>;
19
70
  };
20
71
 
21
- declare function createHook<Context = unknown, Output = unknown>(name: string, run: Hook<Context, Output>["run"], order?: number): Hook<Context, Output>;
72
+ /**
73
+ * Creates a pipeline hook.
74
+ *
75
+ * @param name - Unique name for debugging and inspection.
76
+ * @param run - Hook implementation.
77
+ * @param order - Optional execution order. Lower values run first.
78
+ *
79
+ * @returns A normalized `RuraHook` object.
80
+ */
81
+ declare function createHook<Ctx = unknown, Out = unknown>(name: string, run: RuraHook<Ctx, Out>["run"], order?: number): RuraHook<Ctx, Out>;
22
82
 
23
- export { type Hook, createHook, createRura, runRura };
83
+ export { type RuraHook, type RuraHookResult, type RuraResult, createHook, createRura, runRura };
package/dist/index.d.ts CHANGED
@@ -1,23 +1,83 @@
1
- type MaybePromise<T> = T | Promise<T>;
2
- interface Done<Output> {
3
- done: true;
4
- output: Output;
5
- }
6
- interface Hook<Context = unknown, Output = unknown> {
1
+ /**
2
+ * The return type of a Rura hook.
3
+ *
4
+ * - Return `void` to continue the pipeline.
5
+ * - Return `{ early: true, output }` to stop the pipeline immediately.
6
+ *
7
+ * Supports both sync and async forms.
8
+ */
9
+ type RuraHookResult<Out> = void | {
10
+ early: true;
11
+ output: Out;
12
+ } | Promise<void | {
13
+ early: true;
14
+ output: Out;
15
+ }>;
16
+ /**
17
+ * A single pipeline hook.
18
+ *
19
+ * Hooks receive the current context and may:
20
+ * - mutate it and continue, or
21
+ * - return `{ early: true, output }` to exit the pipeline early.
22
+ */
23
+ interface RuraHook<Ctx = unknown, Out = unknown> {
24
+ /** Unique name for debugging and inspection. */
7
25
  name: string;
26
+ /** Executes the hook. */
27
+ run(ctx: Ctx): RuraHookResult<Out>;
28
+ /** Optional execution order. Lower values run first. */
8
29
  order?: number;
9
- run(ctx: Context): MaybePromise<void | Done<Output>>;
30
+ }
31
+ /**
32
+ * Final result of a pipeline execution.
33
+ *
34
+ * - If `early` is true, the pipeline stopped before completing all hooks.
35
+ * - The `ctx` field always contains the final context.
36
+ * - The `output` field appears only when early termination occurs.
37
+ */
38
+ interface RuraResult<Ctx, Out> {
39
+ /** Indicates whether the pipeline exited early. */
40
+ early: boolean;
41
+ /** The final context after all executed hooks. */
42
+ ctx: Ctx;
43
+ /** The output value when the pipeline ends early. */
44
+ output?: Out;
10
45
  }
11
46
 
12
- declare function runRura<Context = unknown, Output = unknown>(ctx: Context, hooks: Hook<Context, Output>[]): Promise<Output | Context>;
47
+ /**
48
+ * Executes a list of hooks in order.
49
+ * - Stops early when a hook returns `{ early: true, output }`.
50
+ *
51
+ * @returns A `RuraResult` describing whether the pipeline ended early
52
+ * and the final context/output.
53
+ */
54
+ declare function runRura<Ctx = unknown, Out = unknown>(ctx: Ctx, hooks: RuraHook<Ctx, Out>[]): Promise<RuraResult<Ctx, Out>>;
13
55
 
14
- declare function createRura<Context = unknown, Output = unknown>(initialHooks?: Hook<Context, Output>[]): {
15
- use: (hook: Hook<Context, Output>) => /*elided*/ any;
16
- merge: (other: ReturnType<typeof createRura<Context, Output>>) => /*elided*/ any;
17
- getHooks: () => Hook<Context, Output>[];
18
- run: (ctx: Context) => Promise<Context | Output>;
56
+ /**
57
+ * Creates a reusable pipeline instance.
58
+ * Hooks can be registered, merged, inspected, and executed in order.
59
+ *
60
+ * @param initialHooks - Optional list of hooks to initialize the pipeline with.
61
+ *
62
+ * @returns An API for managing and running the pipeline.
63
+ */
64
+ declare function createRura<Ctx = unknown, Out = unknown>(initialHooks?: RuraHook<Ctx, Out>[]): {
65
+ use: (hook: RuraHook<Ctx, Out>) => /*elided*/ any;
66
+ merge: (other: ReturnType<typeof createRura<Ctx, Out>>) => /*elided*/ any;
67
+ getHooks: () => RuraHook<Ctx, Out>[];
68
+ debugHooks: () => void;
69
+ run: (ctx: Ctx) => Promise<RuraResult<Ctx, Out>>;
19
70
  };
20
71
 
21
- declare function createHook<Context = unknown, Output = unknown>(name: string, run: Hook<Context, Output>["run"], order?: number): Hook<Context, Output>;
72
+ /**
73
+ * Creates a pipeline hook.
74
+ *
75
+ * @param name - Unique name for debugging and inspection.
76
+ * @param run - Hook implementation.
77
+ * @param order - Optional execution order. Lower values run first.
78
+ *
79
+ * @returns A normalized `RuraHook` object.
80
+ */
81
+ declare function createHook<Ctx = unknown, Out = unknown>(name: string, run: RuraHook<Ctx, Out>["run"], order?: number): RuraHook<Ctx, Out>;
22
82
 
23
- export { type Hook, createHook, createRura, runRura };
83
+ export { type RuraHook, type RuraHookResult, type RuraResult, createHook, createRura, runRura };
package/dist/index.js CHANGED
@@ -1,38 +1,59 @@
1
- // src/is-done.ts
2
- function isDone(r) {
3
- return typeof r === "object" && r !== null && "done" in r;
4
- }
5
-
6
1
  // src/run-rura.ts
7
2
  async function runRura(ctx, hooks) {
8
- hooks.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
9
3
  for (const hook of hooks) {
10
4
  const result = await hook.run(ctx);
11
- if (isDone(result)) {
12
- return result.output;
5
+ const isEarlyReturn = result && result.early === true;
6
+ if (isEarlyReturn) {
7
+ return {
8
+ early: true,
9
+ ctx,
10
+ output: result.output
11
+ };
13
12
  }
14
13
  }
15
- return ctx;
14
+ return {
15
+ early: false,
16
+ ctx
17
+ };
16
18
  }
17
19
 
18
20
  // src/create-rura.ts
19
21
  function createRura(initialHooks = []) {
20
- const hooks = [...initialHooks];
22
+ const hooks = [...initialHooks].map((h) => applyDefaultOrder(h));
23
+ function applyDefaultOrder(h) {
24
+ return { ...h, order: h.order ?? 0 };
25
+ }
26
+ function sortHooks() {
27
+ hooks.sort((a, b) => a.order - b.order);
28
+ }
21
29
  function use(hook) {
22
- hooks.push(hook);
30
+ hooks.push(applyDefaultOrder(hook));
31
+ sortHooks();
23
32
  return api;
24
33
  }
25
34
  function merge(other) {
26
- other.getHooks().forEach((h) => hooks.push(h));
35
+ other.getHooks().forEach((h) => hooks.push(applyDefaultOrder(h)));
36
+ sortHooks();
27
37
  return api;
28
38
  }
29
39
  function getHooks() {
30
- return hooks;
40
+ return [...hooks];
41
+ }
42
+ function debugHooks() {
43
+ console.log(`
44
+ \u{1F4A7} Rura Pipeline (${hooks.length} hooks)`);
45
+ console.log("\u2500\u2500\u2500");
46
+ hooks.forEach(({ name, order }, i) => {
47
+ const paddedOrder = String(order).padStart(4, " ");
48
+ console.log(`${i + 1}. order: ${paddedOrder} | name: ${name}`);
49
+ });
50
+ console.log("\u2500\u2500\u2500\n");
31
51
  }
32
52
  async function run(ctx) {
33
53
  return runRura(ctx, hooks);
34
54
  }
35
- const api = { use, merge, getHooks, run };
55
+ const api = { use, merge, getHooks, debugHooks, run };
56
+ sortHooks();
36
57
  return api;
37
58
  }
38
59
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rura",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "💧 A pipeline engine that flows like water.",
5
5
  "author": "Yiming Liao",
6
6
  "license": "MIT",