rura 1.0.8 → 1.1.1
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 +38 -118
- package/dist/index.cjs +75 -63
- package/dist/index.d.ts +211 -159
- package/dist/index.js +70 -64
- package/package.json +17 -21
- package/dist/index.d.cts +0 -159
package/README.md
CHANGED
|
@@ -2,16 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
built for clarity and stability.
|
|
5
|
+
The hook pipeline
|
|
7
6
|
|
|
8
7
|
</div>
|
|
9
8
|
|
|
10
9
|
<div align="center">
|
|
11
10
|
|
|
12
11
|
[](https://www.npmjs.com/package/rura)
|
|
13
|
-
[](https://bundlephobia.com/package/rura)
|
|
14
|
-
[](https://coveralls.io/github/yiming-liao/rura?branch=main)
|
|
15
12
|
[](https://www.typescriptlang.org/)
|
|
16
13
|
[](LICENSE)
|
|
17
14
|
|
|
@@ -19,158 +16,81 @@ built for clarity and stability.
|
|
|
19
16
|
|
|
20
17
|
## Features
|
|
21
18
|
|
|
22
|
-
- **Deterministic**
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
19
|
+
- **Deterministic** — Strict hook ordering with early termination.
|
|
20
|
+
- **Type-safe** — Fully inferred context and output.
|
|
21
|
+
- **Composable** — Minimal and framework-agnostic.
|
|
25
22
|
|
|
26
23
|
## Installation
|
|
27
24
|
|
|
28
25
|
```bash
|
|
29
|
-
# npm
|
|
30
26
|
npm install rura
|
|
31
|
-
#
|
|
27
|
+
# or
|
|
32
28
|
yarn add rura
|
|
33
|
-
#
|
|
29
|
+
# or
|
|
34
30
|
pnpm add rura
|
|
35
31
|
```
|
|
36
32
|
|
|
37
|
-
##
|
|
38
|
-
|
|
39
|
-
#### 1. Define the initial context
|
|
33
|
+
## Example
|
|
40
34
|
|
|
41
35
|
```ts
|
|
42
|
-
|
|
43
|
-
type Ctx = typeof ctx;
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
#### 2. Create your hooks
|
|
47
|
-
|
|
48
|
-
- Hooks can be created with `rura.createHook`, or defined manually using the `RuraHook` type.
|
|
49
|
-
- Hooks may specify an `order`; omitted values default to `0`.
|
|
36
|
+
import { rura } from "rura";
|
|
50
37
|
|
|
51
|
-
|
|
52
|
-
|
|
38
|
+
// Shared pipeline context.
|
|
39
|
+
type Ctx = { count: number };
|
|
53
40
|
|
|
54
|
-
//
|
|
41
|
+
// Synchronous hook.
|
|
55
42
|
const addOne = rura.createHook<Ctx>("add-one", (ctx) => {
|
|
56
43
|
ctx.count += 1;
|
|
57
|
-
});
|
|
44
|
+
});
|
|
58
45
|
|
|
59
|
-
//
|
|
60
|
-
const stopIfEven
|
|
61
|
-
|
|
62
|
-
|
|
46
|
+
// Synchronous hook (Early-terminating).
|
|
47
|
+
const stopIfEven = rura.createHook<Ctx, number>(
|
|
48
|
+
"stop-if-even",
|
|
49
|
+
(ctx) => {
|
|
63
50
|
if (ctx.count % 2 === 0) {
|
|
64
51
|
return { early: true, output: ctx.count };
|
|
65
52
|
}
|
|
66
53
|
},
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
- Rura executes hooks in order and exits early when a hook returns `{ early: true, output }`.
|
|
54
|
+
2, // optional execution order
|
|
55
|
+
);
|
|
72
56
|
|
|
73
|
-
|
|
57
|
+
// Deterministic execution.
|
|
58
|
+
const result = rura.run({ count: 1 }, [addOne, stopIfEven, addOne]);
|
|
59
|
+
```
|
|
74
60
|
|
|
75
|
-
|
|
61
|
+
Asynchronous variant:
|
|
76
62
|
|
|
77
63
|
```ts
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
console.log(result);
|
|
81
|
-
// -> {
|
|
82
|
-
// early: true,
|
|
83
|
-
// ctx: { count: 2 },
|
|
84
|
-
// output: 2
|
|
85
|
-
// }
|
|
64
|
+
rura.createHookAsync(...)
|
|
65
|
+
rura.runAsync(...)
|
|
86
66
|
```
|
|
87
67
|
|
|
88
|
-
> Note: If your hooks are asynchronous,
|
|
89
|
-
> use `rura.createHookAsync()` and run them through `rura.runAsync()` accordingly.
|
|
90
|
-
|
|
91
68
|
---
|
|
92
69
|
|
|
93
70
|
## Pipeline Builder
|
|
94
71
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
**rura.createPipeline()** - Creates a lightweight, composable pipeline instance
|
|
98
|
-
that can register hooks, merge with other pipelines, and be inspected.
|
|
72
|
+
Reusable pipeline instance:
|
|
99
73
|
|
|
100
74
|
```ts
|
|
101
|
-
|
|
75
|
+
const pipeline = rura.createPipeline<Ctx>();
|
|
102
76
|
|
|
103
|
-
|
|
77
|
+
pipeline.use(hookA).use(hookB);
|
|
104
78
|
|
|
105
|
-
|
|
106
|
-
pipeline
|
|
107
|
-
.use(hookA) // chainable
|
|
108
|
-
.use(hookB)
|
|
109
|
-
.use(hookC);
|
|
79
|
+
const result = pipeline.run({ count: 1 });
|
|
110
80
|
```
|
|
111
81
|
|
|
112
|
-
|
|
82
|
+
Asynchronous variant:
|
|
113
83
|
|
|
114
84
|
```ts
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// Create a reusable pipeline instance
|
|
118
|
-
const pipelineA = rura.createPipeline<Ctx>();
|
|
119
|
-
|
|
120
|
-
// Register a hook using `use()`
|
|
121
|
-
pipelineA.use({
|
|
122
|
-
name: "add-two",
|
|
123
|
-
run: (ctx) => {
|
|
124
|
-
ctx.value += 2;
|
|
125
|
-
},
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
// Create another pipeline (preloaded with hooks)
|
|
129
|
-
const pipelineB = rura.createPipeline<Ctx>([
|
|
130
|
-
{
|
|
131
|
-
name: "multiply-three",
|
|
132
|
-
run: (ctx) => {
|
|
133
|
-
ctx.value *= 3;
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
]);
|
|
137
|
-
|
|
138
|
-
// Merge pipelines into a single combined pipeline
|
|
139
|
-
const pipeline = pipelineA.merge(pipelineB);
|
|
140
|
-
|
|
141
|
-
// Execute the pipeline
|
|
142
|
-
const result = await pipeline.run({ value: 1 });
|
|
143
|
-
|
|
144
|
-
console.log(result);
|
|
145
|
-
// -> {
|
|
146
|
-
// early: false,
|
|
147
|
-
// ctx: { value: 9 }
|
|
148
|
-
// }
|
|
85
|
+
const pipeline = rura.createPipelineAsync<Ctx>();
|
|
86
|
+
await pipeline.run(ctx);
|
|
149
87
|
```
|
|
150
88
|
|
|
151
|
-
|
|
152
|
-
> be sure to use `rura.createPipelineAsync()`, which awaits each hook in order.
|
|
153
|
-
|
|
154
|
-
#### Pipeline Instance Methods
|
|
155
|
-
|
|
156
|
-
`rura.createPipeline()` — Synchronous Pipeline
|
|
157
|
-
|
|
158
|
-
| Method | Description | Parameters | Returns |
|
|
159
|
-
| ---------------- | ----------------------------------------------------------------- | ---------- | ------------------ |
|
|
160
|
-
| **use(hook)** | Adds a hook, normalizes its order, and re-sorts hooks. | `hook` | `this` (chainable) |
|
|
161
|
-
| **merge(other)** | Merges hooks from another pipeline and re-sorts them. | `other` | `this` (chainable) |
|
|
162
|
-
| **getHooks()** | Returns a sorted shallow copy of all registered hooks. | – | `RuraHook[]` |
|
|
163
|
-
| **debugHooks()** | Prints a formatted, human-readable hook list. | – | `void` |
|
|
164
|
-
| **run(ctx)** | Executes the pipeline synchronously. (delegates to `rura.run()`). | `ctx` | `RuraResult` |
|
|
165
|
-
|
|
166
|
-
<br/>
|
|
167
|
-
|
|
168
|
-
`rura.createPipelineAsync()` — Asynchronous Pipeline
|
|
89
|
+
#### Pipeline Instance API
|
|
169
90
|
|
|
170
|
-
| Method
|
|
171
|
-
|
|
|
172
|
-
| **use
|
|
173
|
-
| **
|
|
174
|
-
| **
|
|
175
|
-
| **
|
|
176
|
-
| **run(ctx)** | Executes the pipeline asynchronously. (delegates to `rura.runAsync()`). | `ctx` | `Promise<RuraResult>` |
|
|
91
|
+
| Method | Description | Returns |
|
|
92
|
+
| ------------ | ------------------------------------------- | ------------------------ |
|
|
93
|
+
| **use** | Registers a hook and re-sorts the pipeline. | `this` (chainable) |
|
|
94
|
+
| **getHooks** | Returns a shallow copy of sorted hooks. | `Hook[]` |
|
|
95
|
+
| **logHooks** | Logs hook order and execution type. | `void` |
|
|
96
|
+
| **run** | Executes the pipeline. | `RuraResult` / `Promise` |
|
package/dist/index.cjs
CHANGED
|
@@ -1,87 +1,95 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// src/hooks/internal.ts
|
|
4
|
+
var ASYNC_HOOK = /* @__PURE__ */ Symbol("rura.async");
|
|
5
|
+
|
|
3
6
|
// src/hooks/create-hook.ts
|
|
4
7
|
function createHook(name, run2, order) {
|
|
5
|
-
|
|
8
|
+
const hook = {
|
|
9
|
+
name,
|
|
10
|
+
run: run2,
|
|
11
|
+
...order !== void 0 ? { order } : {}
|
|
12
|
+
};
|
|
13
|
+
return hook;
|
|
6
14
|
}
|
|
7
15
|
function createHookAsync(name, run2, order) {
|
|
8
|
-
|
|
16
|
+
const hook = {
|
|
17
|
+
name,
|
|
18
|
+
run: run2,
|
|
19
|
+
...order !== void 0 ? { order } : {},
|
|
20
|
+
[ASYNC_HOOK]: true
|
|
21
|
+
};
|
|
22
|
+
return hook;
|
|
9
23
|
}
|
|
10
24
|
|
|
11
25
|
// src/hooks/utils/is-async-hook.ts
|
|
12
26
|
function isAsyncHook(hook) {
|
|
13
|
-
return hook
|
|
27
|
+
return hook[ASYNC_HOOK] === true;
|
|
14
28
|
}
|
|
15
29
|
|
|
16
|
-
// src/pipeline/create/utils/
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
${
|
|
21
|
-
|
|
22
|
-
const label = name ? ` <${name}>` : "";
|
|
23
|
-
console.log(`
|
|
24
|
-
\u{1F30A} Rura Pipeline${label} (${hooks.length} hooks)`);
|
|
30
|
+
// src/pipeline/create/utils/assert-sync-hook.ts
|
|
31
|
+
function assertSyncHook(hook) {
|
|
32
|
+
if (isAsyncHook(hook)) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Async hook "${hook.name}" is not allowed in sync pipeline.`
|
|
35
|
+
);
|
|
25
36
|
}
|
|
26
|
-
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/pipeline/create/utils/log-pipeline-hooks.ts
|
|
40
|
+
function logPipelineHooks(hooks, name = "Rura") {
|
|
41
|
+
const count = hooks.length;
|
|
42
|
+
const suffix = count === 1 ? "hook" : "hooks";
|
|
43
|
+
console.log(`${name} (${count} ${suffix})`);
|
|
44
|
+
console.log("\u2500\u2500\u2500");
|
|
27
45
|
hooks.forEach((hook, i) => {
|
|
28
46
|
const kind = isAsyncHook(hook) ? "async" : "sync";
|
|
47
|
+
const order = hook.order ?? "-";
|
|
29
48
|
console.log(
|
|
30
|
-
`${i + 1}. order
|
|
49
|
+
`${i + 1}. order: ${String(order).padEnd(4)} | ${hook.name} (${kind})`
|
|
31
50
|
);
|
|
32
51
|
});
|
|
33
|
-
console.log(
|
|
34
|
-
|
|
35
|
-
};
|
|
52
|
+
console.log("\u2500\u2500\u2500");
|
|
53
|
+
}
|
|
36
54
|
|
|
37
55
|
// src/pipeline/create/create-pipeline-base.ts
|
|
38
56
|
function createPipelineBase(initialHooks, runFn, options) {
|
|
57
|
+
const isSync = options.mode === "sync";
|
|
58
|
+
if (isSync) for (const hook of initialHooks) assertSyncHook(hook);
|
|
39
59
|
const name = options.name;
|
|
40
|
-
const hooks = initialHooks
|
|
41
|
-
function applyDefaultOrder(h) {
|
|
42
|
-
return { ...h, order: h.order ?? 0 };
|
|
43
|
-
}
|
|
60
|
+
const hooks = [...initialHooks];
|
|
44
61
|
function sortHooks() {
|
|
45
|
-
hooks.sort((a, b) =>
|
|
62
|
+
hooks.sort((a, b) => {
|
|
63
|
+
const ao = a.order ?? Number.POSITIVE_INFINITY;
|
|
64
|
+
const bo = b.order ?? Number.POSITIVE_INFINITY;
|
|
65
|
+
return ao - bo;
|
|
66
|
+
});
|
|
46
67
|
}
|
|
47
|
-
function use(
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
hooks.push(applyDefaultOrder(h));
|
|
52
|
-
sortHooks();
|
|
53
|
-
return api;
|
|
54
|
-
}
|
|
55
|
-
function merge(other) {
|
|
56
|
-
other.getHooks().forEach((h) => hooks.push(applyDefaultOrder(h)));
|
|
68
|
+
function use(hook) {
|
|
69
|
+
if (isSync) assertSyncHook(hook);
|
|
70
|
+
hooks.push(hook);
|
|
57
71
|
sortHooks();
|
|
58
|
-
return
|
|
72
|
+
return pipeline;
|
|
59
73
|
}
|
|
60
74
|
function getHooks() {
|
|
61
75
|
return [...hooks];
|
|
62
76
|
}
|
|
63
|
-
function
|
|
64
|
-
|
|
77
|
+
function logHooks() {
|
|
78
|
+
logPipelineHooks(hooks, name);
|
|
65
79
|
}
|
|
66
80
|
function run2(ctx) {
|
|
67
|
-
return runFn(ctx, hooks);
|
|
81
|
+
return runFn(ctx, [...hooks]);
|
|
68
82
|
}
|
|
69
|
-
const
|
|
83
|
+
const pipeline = { use, getHooks, logHooks, run: run2 };
|
|
70
84
|
sortHooks();
|
|
71
|
-
return
|
|
85
|
+
return pipeline;
|
|
72
86
|
}
|
|
73
87
|
|
|
74
88
|
// src/pipeline/run/run.ts
|
|
75
89
|
function run(ctx, hooks) {
|
|
76
90
|
for (const hook of hooks) {
|
|
77
|
-
if (isAsyncHook(hook)) {
|
|
78
|
-
throw new Error(
|
|
79
|
-
`Async hook "${hook.name}" detected in run(). Use runAsync() instead.`
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
91
|
const result = hook.run(ctx);
|
|
83
|
-
|
|
84
|
-
if (isEarlyReturn) {
|
|
92
|
+
if (result?.early === true) {
|
|
85
93
|
return {
|
|
86
94
|
early: true,
|
|
87
95
|
ctx,
|
|
@@ -95,20 +103,11 @@ function run(ctx, hooks) {
|
|
|
95
103
|
};
|
|
96
104
|
}
|
|
97
105
|
|
|
98
|
-
// src/pipeline/create/create-pipeline.ts
|
|
99
|
-
function createPipeline(hooks = []) {
|
|
100
|
-
return createPipelineBase(
|
|
101
|
-
hooks,
|
|
102
|
-
run,
|
|
103
|
-
{ mode: "sync" }
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
106
|
// src/pipeline/run/run-async.ts
|
|
108
107
|
async function runAsync(ctx, hooks) {
|
|
109
108
|
for (const hook of hooks) {
|
|
110
109
|
const result = await hook.run(ctx);
|
|
111
|
-
if (result
|
|
110
|
+
if (result?.early === true) {
|
|
112
111
|
return {
|
|
113
112
|
early: true,
|
|
114
113
|
ctx,
|
|
@@ -122,6 +121,15 @@ async function runAsync(ctx, hooks) {
|
|
|
122
121
|
};
|
|
123
122
|
}
|
|
124
123
|
|
|
124
|
+
// src/pipeline/create/create-pipeline.ts
|
|
125
|
+
function createPipeline(hooks = []) {
|
|
126
|
+
return createPipelineBase(
|
|
127
|
+
hooks,
|
|
128
|
+
run,
|
|
129
|
+
{ mode: "sync" }
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
125
133
|
// src/pipeline/create/create-pipeline-async.ts
|
|
126
134
|
function createPipelineAsync(hooks = []) {
|
|
127
135
|
return createPipelineBase(
|
|
@@ -132,18 +140,22 @@ function createPipelineAsync(hooks = []) {
|
|
|
132
140
|
}
|
|
133
141
|
|
|
134
142
|
// src/rura.ts
|
|
135
|
-
var rura = {
|
|
136
|
-
//
|
|
143
|
+
var rura = Object.freeze({
|
|
144
|
+
// Hook creation
|
|
137
145
|
createHook,
|
|
138
146
|
createHookAsync,
|
|
139
|
-
//
|
|
147
|
+
// Direct execution
|
|
140
148
|
run,
|
|
141
149
|
runAsync,
|
|
142
|
-
//
|
|
150
|
+
// Pipeline construction
|
|
143
151
|
createPipeline,
|
|
144
|
-
createPipelineAsync
|
|
145
|
-
|
|
146
|
-
isAsyncHook
|
|
147
|
-
};
|
|
152
|
+
createPipelineAsync
|
|
153
|
+
});
|
|
148
154
|
|
|
155
|
+
exports.createHook = createHook;
|
|
156
|
+
exports.createHookAsync = createHookAsync;
|
|
157
|
+
exports.createPipeline = createPipeline;
|
|
158
|
+
exports.createPipelineAsync = createPipelineAsync;
|
|
159
|
+
exports.run = run;
|
|
160
|
+
exports.runAsync = runAsync;
|
|
149
161
|
exports.rura = rura;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,159 +1,211 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*
|
|
29
|
-
* - Executes
|
|
30
|
-
* -
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
*
|
|
49
|
-
* -
|
|
50
|
-
* -
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
*
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* -
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* @
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* -
|
|
109
|
-
* -
|
|
110
|
-
* -
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Rura — The hook pipeline
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates a synchronous hook.
|
|
9
|
+
*
|
|
10
|
+
* The hook executes without awaiting.
|
|
11
|
+
*
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
export declare function createHook<Ctx = unknown, Out = unknown>(name: string, run: RuraHookSync<Ctx, Out>["run"], order?: number): RuraHookSync<Ctx, Out>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates an asynchronous hook.
|
|
18
|
+
*
|
|
19
|
+
* The hook executes via `await`.
|
|
20
|
+
*
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
export declare function createHookAsync<Ctx = unknown, Out = unknown>(name: string, run: RuraHookAsync<Ctx, Out>["run"], order?: number): RuraHookAsync<Ctx, Out>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates a synchronous Rura pipeline.
|
|
27
|
+
*
|
|
28
|
+
* The returned pipeline:
|
|
29
|
+
* - Executes hooks sequentially using a synchronous strategy.
|
|
30
|
+
* - Rejects asynchronous hooks at registration time.
|
|
31
|
+
* - Preserves deterministic ordering based on `order`.
|
|
32
|
+
*
|
|
33
|
+
* This variant guarantees that `run()` returns immediately
|
|
34
|
+
* without producing a Promise.
|
|
35
|
+
*
|
|
36
|
+
* @public
|
|
37
|
+
*/
|
|
38
|
+
export declare function createPipeline<Ctx = unknown, Out = unknown>(hooks?: RuraHookSync<Ctx, Out>[]): {
|
|
39
|
+
use: (hook: RuraHookSync<Ctx, Out>) => /*elided*/ any;
|
|
40
|
+
getHooks: () => RuraHookSync<Ctx, Out>[];
|
|
41
|
+
logHooks: () => void;
|
|
42
|
+
run: (ctx: Ctx) => RuraResult<Ctx, Out>;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates an asynchronous Rura pipeline.
|
|
47
|
+
*
|
|
48
|
+
* The returned pipeline:
|
|
49
|
+
* - Executes hooks sequentially using an async strategy.
|
|
50
|
+
* - Accepts any RuraHook, including sync and async variants.
|
|
51
|
+
* - Preserves deterministic ordering based on `order`.
|
|
52
|
+
*
|
|
53
|
+
* This variant guarantees that `run()` returns a Promise.
|
|
54
|
+
*
|
|
55
|
+
* @public
|
|
56
|
+
*/
|
|
57
|
+
export declare function createPipelineAsync<Ctx = unknown, Out = unknown>(hooks?: RuraHook<Ctx, Out>[]): {
|
|
58
|
+
use: (hook: RuraHook<Ctx, Out>) => /*elided*/ any;
|
|
59
|
+
getHooks: () => RuraHook<Ctx, Out>[];
|
|
60
|
+
logHooks: () => void;
|
|
61
|
+
run: (ctx: Ctx) => Promise<RuraResult<Ctx, Out>>;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Executes a synchronous Rura pipeline.
|
|
66
|
+
*
|
|
67
|
+
* Hooks are executed sequentially in the given order.
|
|
68
|
+
*
|
|
69
|
+
* Execution contract:
|
|
70
|
+
* - Each hook receives the same `ctx` reference.
|
|
71
|
+
* - If a hook returns `{ early: true, output }`,
|
|
72
|
+
* execution stops immediately and that result is returned.
|
|
73
|
+
* - If no hook triggers early termination, a normal result
|
|
74
|
+
* `{ early: false, ctx }` is returned.
|
|
75
|
+
*
|
|
76
|
+
* This function is strictly synchronous:
|
|
77
|
+
* - It does not produce a Promise.
|
|
78
|
+
* - Only `RuraHookSync` hooks are allowed.
|
|
79
|
+
*
|
|
80
|
+
* @public
|
|
81
|
+
*/
|
|
82
|
+
export declare function run<Ctx = unknown, Out = unknown>(ctx: Ctx, hooks: RuraHookSync<Ctx, Out>[]): RuraResult<Ctx, Out>;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Executes an asynchronous Rura pipeline.
|
|
86
|
+
*
|
|
87
|
+
* Hooks are awaited sequentially in the provided order.
|
|
88
|
+
*
|
|
89
|
+
* Execution contract:
|
|
90
|
+
* - Each hook receives the same `ctx` reference.
|
|
91
|
+
* - Hooks may be synchronous or asynchronous.
|
|
92
|
+
* - If a hook resolves to `{ early: true, output }`,
|
|
93
|
+
* execution stops immediately and that result is returned.
|
|
94
|
+
* - If no hook triggers early termination, a normal result
|
|
95
|
+
* `{ early: false, ctx }` is returned.
|
|
96
|
+
*
|
|
97
|
+
* This function always returns a Promise.
|
|
98
|
+
*
|
|
99
|
+
* @public
|
|
100
|
+
*/
|
|
101
|
+
export declare function runAsync<Ctx = unknown, Out = unknown>(ctx: Ctx, hooks: RuraHook<Ctx, Out>[]): Promise<RuraResult<Ctx, Out>>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Rura public API namespace.
|
|
105
|
+
*
|
|
106
|
+
* Provides a cohesive entry point for:
|
|
107
|
+
*
|
|
108
|
+
* - Hook factories (`createHook`, `createHookAsync`)
|
|
109
|
+
* - Direct executors (`run`, `runAsync`)
|
|
110
|
+
* - Pipeline constructors (`createPipeline`, `createPipelineAsync`)
|
|
111
|
+
*
|
|
112
|
+
* This object contains no additional logic —
|
|
113
|
+
* it simply exposes the stable, public surface of Rura.
|
|
114
|
+
*
|
|
115
|
+
* The namespace is frozen to prevent runtime mutation.
|
|
116
|
+
*
|
|
117
|
+
* @public
|
|
118
|
+
*/
|
|
119
|
+
export declare const rura: Readonly<{
|
|
120
|
+
createHook: typeof createHook;
|
|
121
|
+
createHookAsync: typeof createHookAsync;
|
|
122
|
+
run: typeof run;
|
|
123
|
+
runAsync: typeof runAsync;
|
|
124
|
+
createPipeline: typeof createPipeline;
|
|
125
|
+
createPipelineAsync: typeof createPipelineAsync;
|
|
126
|
+
}>;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Union of all hook variants supported by the Rura pipeline.
|
|
130
|
+
*
|
|
131
|
+
* @public
|
|
132
|
+
*/
|
|
133
|
+
export declare type RuraHook<Ctx = unknown, Out = unknown> = RuraHookSync<Ctx, Out> | RuraHookAsync<Ctx, Out>;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Asynchronous hook.
|
|
137
|
+
*
|
|
138
|
+
* Executed via `await`.
|
|
139
|
+
* The provided `ctx` may be mutated.
|
|
140
|
+
* Resolving with an early signal terminates the pipeline.
|
|
141
|
+
*
|
|
142
|
+
* @public
|
|
143
|
+
*/
|
|
144
|
+
export declare interface RuraHookAsync<Ctx, Out> extends RuraHookBase {
|
|
145
|
+
run(ctx: Ctx): Promise<void | {
|
|
146
|
+
early: true;
|
|
147
|
+
output: Out;
|
|
148
|
+
}>;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Base contract for all Rura hooks.
|
|
153
|
+
*
|
|
154
|
+
* A hook is an ordered execution unit within a pipeline.
|
|
155
|
+
* The `name` uniquely identifies the hook.
|
|
156
|
+
* The optional `order` controls execution priority (lower values run first).
|
|
157
|
+
*
|
|
158
|
+
* @public
|
|
159
|
+
*/
|
|
160
|
+
export declare interface RuraHookBase {
|
|
161
|
+
/** Unique hook identifier. */
|
|
162
|
+
name: string;
|
|
163
|
+
/** Execution priority. Lower values run earlier. */
|
|
164
|
+
order?: number;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Synchronous hook.
|
|
169
|
+
*
|
|
170
|
+
* Executed without awaiting.
|
|
171
|
+
* The provided `ctx` may be mutated.
|
|
172
|
+
* Returning an early signal terminates the pipeline.
|
|
173
|
+
*
|
|
174
|
+
* @public
|
|
175
|
+
*/
|
|
176
|
+
export declare interface RuraHookSync<Ctx, Out> extends RuraHookBase {
|
|
177
|
+
run(ctx: Ctx): void | {
|
|
178
|
+
early: true;
|
|
179
|
+
output: Out;
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Result of a Rura pipeline execution.
|
|
185
|
+
*
|
|
186
|
+
* This type represents the final outcome of running a pipeline,
|
|
187
|
+
* either through `run` or `runAsync`.
|
|
188
|
+
*
|
|
189
|
+
* Execution semantics:
|
|
190
|
+
* - `ctx` always refers to the same context object that was
|
|
191
|
+
* passed into the pipeline. Hooks may mutate it in place.
|
|
192
|
+
* - When `early` is `true`, execution stopped before all hooks
|
|
193
|
+
* completed, and `output` contains the early-return value.
|
|
194
|
+
* - When `early` is `false`, all hooks were executed and no
|
|
195
|
+
* early termination occurred. In this case, `output` is not present.
|
|
196
|
+
*
|
|
197
|
+
* The `early` field acts as a discriminant for safe narrowing.
|
|
198
|
+
*
|
|
199
|
+
* @public
|
|
200
|
+
*/
|
|
201
|
+
export declare type RuraResult<Ctx, Out> = {
|
|
202
|
+
early: true;
|
|
203
|
+
ctx: Ctx;
|
|
204
|
+
output: Out;
|
|
205
|
+
} | {
|
|
206
|
+
early: false;
|
|
207
|
+
ctx: Ctx;
|
|
208
|
+
output?: never;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export { }
|
package/dist/index.js
CHANGED
|
@@ -1,85 +1,93 @@
|
|
|
1
|
+
// src/hooks/internal.ts
|
|
2
|
+
var ASYNC_HOOK = /* @__PURE__ */ Symbol("rura.async");
|
|
3
|
+
|
|
1
4
|
// src/hooks/create-hook.ts
|
|
2
5
|
function createHook(name, run2, order) {
|
|
3
|
-
|
|
6
|
+
const hook = {
|
|
7
|
+
name,
|
|
8
|
+
run: run2,
|
|
9
|
+
...order !== void 0 ? { order } : {}
|
|
10
|
+
};
|
|
11
|
+
return hook;
|
|
4
12
|
}
|
|
5
13
|
function createHookAsync(name, run2, order) {
|
|
6
|
-
|
|
14
|
+
const hook = {
|
|
15
|
+
name,
|
|
16
|
+
run: run2,
|
|
17
|
+
...order !== void 0 ? { order } : {},
|
|
18
|
+
[ASYNC_HOOK]: true
|
|
19
|
+
};
|
|
20
|
+
return hook;
|
|
7
21
|
}
|
|
8
22
|
|
|
9
23
|
// src/hooks/utils/is-async-hook.ts
|
|
10
24
|
function isAsyncHook(hook) {
|
|
11
|
-
return hook
|
|
25
|
+
return hook[ASYNC_HOOK] === true;
|
|
12
26
|
}
|
|
13
27
|
|
|
14
|
-
// src/pipeline/create/utils/
|
|
15
|
-
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
${
|
|
19
|
-
|
|
20
|
-
const label = name ? ` <${name}>` : "";
|
|
21
|
-
console.log(`
|
|
22
|
-
\u{1F30A} Rura Pipeline${label} (${hooks.length} hooks)`);
|
|
28
|
+
// src/pipeline/create/utils/assert-sync-hook.ts
|
|
29
|
+
function assertSyncHook(hook) {
|
|
30
|
+
if (isAsyncHook(hook)) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Async hook "${hook.name}" is not allowed in sync pipeline.`
|
|
33
|
+
);
|
|
23
34
|
}
|
|
24
|
-
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/pipeline/create/utils/log-pipeline-hooks.ts
|
|
38
|
+
function logPipelineHooks(hooks, name = "Rura") {
|
|
39
|
+
const count = hooks.length;
|
|
40
|
+
const suffix = count === 1 ? "hook" : "hooks";
|
|
41
|
+
console.log(`${name} (${count} ${suffix})`);
|
|
42
|
+
console.log("\u2500\u2500\u2500");
|
|
25
43
|
hooks.forEach((hook, i) => {
|
|
26
44
|
const kind = isAsyncHook(hook) ? "async" : "sync";
|
|
45
|
+
const order = hook.order ?? "-";
|
|
27
46
|
console.log(
|
|
28
|
-
`${i + 1}. order
|
|
47
|
+
`${i + 1}. order: ${String(order).padEnd(4)} | ${hook.name} (${kind})`
|
|
29
48
|
);
|
|
30
49
|
});
|
|
31
|
-
console.log(
|
|
32
|
-
|
|
33
|
-
};
|
|
50
|
+
console.log("\u2500\u2500\u2500");
|
|
51
|
+
}
|
|
34
52
|
|
|
35
53
|
// src/pipeline/create/create-pipeline-base.ts
|
|
36
54
|
function createPipelineBase(initialHooks, runFn, options) {
|
|
55
|
+
const isSync = options.mode === "sync";
|
|
56
|
+
if (isSync) for (const hook of initialHooks) assertSyncHook(hook);
|
|
37
57
|
const name = options.name;
|
|
38
|
-
const hooks = initialHooks
|
|
39
|
-
function applyDefaultOrder(h) {
|
|
40
|
-
return { ...h, order: h.order ?? 0 };
|
|
41
|
-
}
|
|
58
|
+
const hooks = [...initialHooks];
|
|
42
59
|
function sortHooks() {
|
|
43
|
-
hooks.sort((a, b) =>
|
|
60
|
+
hooks.sort((a, b) => {
|
|
61
|
+
const ao = a.order ?? Number.POSITIVE_INFINITY;
|
|
62
|
+
const bo = b.order ?? Number.POSITIVE_INFINITY;
|
|
63
|
+
return ao - bo;
|
|
64
|
+
});
|
|
44
65
|
}
|
|
45
|
-
function use(
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
hooks.push(applyDefaultOrder(h));
|
|
50
|
-
sortHooks();
|
|
51
|
-
return api;
|
|
52
|
-
}
|
|
53
|
-
function merge(other) {
|
|
54
|
-
other.getHooks().forEach((h) => hooks.push(applyDefaultOrder(h)));
|
|
66
|
+
function use(hook) {
|
|
67
|
+
if (isSync) assertSyncHook(hook);
|
|
68
|
+
hooks.push(hook);
|
|
55
69
|
sortHooks();
|
|
56
|
-
return
|
|
70
|
+
return pipeline;
|
|
57
71
|
}
|
|
58
72
|
function getHooks() {
|
|
59
73
|
return [...hooks];
|
|
60
74
|
}
|
|
61
|
-
function
|
|
62
|
-
|
|
75
|
+
function logHooks() {
|
|
76
|
+
logPipelineHooks(hooks, name);
|
|
63
77
|
}
|
|
64
78
|
function run2(ctx) {
|
|
65
|
-
return runFn(ctx, hooks);
|
|
79
|
+
return runFn(ctx, [...hooks]);
|
|
66
80
|
}
|
|
67
|
-
const
|
|
81
|
+
const pipeline = { use, getHooks, logHooks, run: run2 };
|
|
68
82
|
sortHooks();
|
|
69
|
-
return
|
|
83
|
+
return pipeline;
|
|
70
84
|
}
|
|
71
85
|
|
|
72
86
|
// src/pipeline/run/run.ts
|
|
73
87
|
function run(ctx, hooks) {
|
|
74
88
|
for (const hook of hooks) {
|
|
75
|
-
if (isAsyncHook(hook)) {
|
|
76
|
-
throw new Error(
|
|
77
|
-
`Async hook "${hook.name}" detected in run(). Use runAsync() instead.`
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
89
|
const result = hook.run(ctx);
|
|
81
|
-
|
|
82
|
-
if (isEarlyReturn) {
|
|
90
|
+
if (result?.early === true) {
|
|
83
91
|
return {
|
|
84
92
|
early: true,
|
|
85
93
|
ctx,
|
|
@@ -93,20 +101,11 @@ function run(ctx, hooks) {
|
|
|
93
101
|
};
|
|
94
102
|
}
|
|
95
103
|
|
|
96
|
-
// src/pipeline/create/create-pipeline.ts
|
|
97
|
-
function createPipeline(hooks = []) {
|
|
98
|
-
return createPipelineBase(
|
|
99
|
-
hooks,
|
|
100
|
-
run,
|
|
101
|
-
{ mode: "sync" }
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
104
|
// src/pipeline/run/run-async.ts
|
|
106
105
|
async function runAsync(ctx, hooks) {
|
|
107
106
|
for (const hook of hooks) {
|
|
108
107
|
const result = await hook.run(ctx);
|
|
109
|
-
if (result
|
|
108
|
+
if (result?.early === true) {
|
|
110
109
|
return {
|
|
111
110
|
early: true,
|
|
112
111
|
ctx,
|
|
@@ -120,6 +119,15 @@ async function runAsync(ctx, hooks) {
|
|
|
120
119
|
};
|
|
121
120
|
}
|
|
122
121
|
|
|
122
|
+
// src/pipeline/create/create-pipeline.ts
|
|
123
|
+
function createPipeline(hooks = []) {
|
|
124
|
+
return createPipelineBase(
|
|
125
|
+
hooks,
|
|
126
|
+
run,
|
|
127
|
+
{ mode: "sync" }
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
123
131
|
// src/pipeline/create/create-pipeline-async.ts
|
|
124
132
|
function createPipelineAsync(hooks = []) {
|
|
125
133
|
return createPipelineBase(
|
|
@@ -130,18 +138,16 @@ function createPipelineAsync(hooks = []) {
|
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
// src/rura.ts
|
|
133
|
-
var rura = {
|
|
134
|
-
//
|
|
141
|
+
var rura = Object.freeze({
|
|
142
|
+
// Hook creation
|
|
135
143
|
createHook,
|
|
136
144
|
createHookAsync,
|
|
137
|
-
//
|
|
145
|
+
// Direct execution
|
|
138
146
|
run,
|
|
139
147
|
runAsync,
|
|
140
|
-
//
|
|
148
|
+
// Pipeline construction
|
|
141
149
|
createPipeline,
|
|
142
|
-
createPipelineAsync
|
|
143
|
-
|
|
144
|
-
isAsyncHook
|
|
145
|
-
};
|
|
150
|
+
createPipelineAsync
|
|
151
|
+
});
|
|
146
152
|
|
|
147
|
-
export { rura };
|
|
153
|
+
export { createHook, createHookAsync, createPipeline, createPipelineAsync, run, runAsync, rura };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rura",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "The hook pipeline",
|
|
5
5
|
"author": "Yiming Liao",
|
|
6
6
|
"homepage": "https://github.com/yiming-liao/rura#readme",
|
|
7
7
|
"repository": {
|
|
@@ -17,51 +17,47 @@
|
|
|
17
17
|
},
|
|
18
18
|
"keywords": [
|
|
19
19
|
"pipeline",
|
|
20
|
-
"
|
|
20
|
+
"hook",
|
|
21
21
|
"hook-system",
|
|
22
|
-
"pipeline-engine",
|
|
23
|
-
"flow",
|
|
24
|
-
"workflow",
|
|
25
22
|
"middleware",
|
|
26
|
-
"typescript",
|
|
27
|
-
"ts",
|
|
28
|
-
"utility",
|
|
29
|
-
"functional",
|
|
30
23
|
"composition",
|
|
31
|
-
"
|
|
24
|
+
"functional",
|
|
25
|
+
"typescript"
|
|
32
26
|
],
|
|
33
27
|
"license": "MIT",
|
|
34
28
|
"type": "module",
|
|
35
29
|
"exports": {
|
|
36
30
|
".": {
|
|
37
|
-
"types": "./dist/index.d.ts",
|
|
31
|
+
"types": "./dist/types/export/index.d.ts",
|
|
38
32
|
"import": "./dist/index.js",
|
|
39
33
|
"require": "./dist/index.cjs"
|
|
40
34
|
}
|
|
41
35
|
},
|
|
42
36
|
"main": "./dist/index.cjs",
|
|
43
37
|
"module": "./dist/index.js",
|
|
44
|
-
"types": "./dist/index.d.ts",
|
|
38
|
+
"types": "./dist/types/export/index.d.ts",
|
|
45
39
|
"files": [
|
|
46
|
-
"dist",
|
|
40
|
+
"dist/index.*",
|
|
47
41
|
"README.md",
|
|
48
42
|
"LICENSE"
|
|
49
43
|
],
|
|
50
44
|
"sideEffects": false,
|
|
51
45
|
"engines": {
|
|
52
|
-
"node": ">=
|
|
46
|
+
"node": ">=20"
|
|
53
47
|
},
|
|
54
48
|
"scripts": {
|
|
55
|
-
"
|
|
56
|
-
"prepublishOnly": "yarn build",
|
|
57
|
-
"test": "vitest",
|
|
58
|
-
"type": "tsc --noEmit",
|
|
49
|
+
"type": "tsc -p tsconfig.json --noEmit",
|
|
59
50
|
"lint": "eslint",
|
|
60
|
-
"
|
|
61
|
-
"knip": "knip --config .config/knip.config.ts"
|
|
51
|
+
"test": "vitest",
|
|
52
|
+
"knip": "knip --config .config/knip.config.ts",
|
|
53
|
+
"build": "tsup && tsc -p tsconfig.build.json",
|
|
54
|
+
"prepublishOnly": "yarn build && yarn api:check",
|
|
55
|
+
"api:build": "api-extractor run -c api-extractor/index.json --local",
|
|
56
|
+
"api:check": "api-extractor run -c api-extractor/index.json"
|
|
62
57
|
},
|
|
63
58
|
"dependencies": {},
|
|
64
59
|
"devDependencies": {
|
|
60
|
+
"@microsoft/api-extractor": "^7.57.6",
|
|
65
61
|
"@types/node": "^24.10.1",
|
|
66
62
|
"@vitest/coverage-v8": "4.0.15",
|
|
67
63
|
"eslint": "^9.39.1",
|
package/dist/index.d.cts
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base shape for all Rura hooks.
|
|
3
|
-
* - Every hook must have a unique `name`.
|
|
4
|
-
* - `order` controls execution priority (lower runs earlier).
|
|
5
|
-
*/
|
|
6
|
-
interface RuraHookBase {
|
|
7
|
-
/** Unique identifier for debugging and inspection. */
|
|
8
|
-
name: string;
|
|
9
|
-
/** Execution priority. Hooks with lower values run first. */
|
|
10
|
-
order?: number;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Synchronous Rura hook.
|
|
14
|
-
* - Runs without awaiting.
|
|
15
|
-
* - May optionally signal early termination.
|
|
16
|
-
*/
|
|
17
|
-
interface RuraHookSync<Ctx, Out> extends RuraHookBase {
|
|
18
|
-
/**
|
|
19
|
-
* Executes the hook.
|
|
20
|
-
* @returns Nothing, or an early-return signal with output.
|
|
21
|
-
*/
|
|
22
|
-
run(ctx: Ctx): void | {
|
|
23
|
-
early: true;
|
|
24
|
-
output: Out;
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Asynchronous Rura hook.
|
|
29
|
-
* - Executes via `await`.
|
|
30
|
-
* - May optionally signal early termination.
|
|
31
|
-
*/
|
|
32
|
-
interface RuraHookAsync<Ctx, Out> extends RuraHookBase {
|
|
33
|
-
/**
|
|
34
|
-
* Executes the hook asynchronously.
|
|
35
|
-
* @returns A promise resolving to nothing or an early-return signal.
|
|
36
|
-
*/
|
|
37
|
-
run(ctx: Ctx): Promise<void | {
|
|
38
|
-
early: true;
|
|
39
|
-
output: Out;
|
|
40
|
-
}>;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* A union of all hook types accepted by the Rura pipeline.
|
|
44
|
-
*/
|
|
45
|
-
type RuraHook<Ctx = unknown, Out = unknown> = RuraHookSync<Ctx, Out> | RuraHookAsync<Ctx, Out>;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Creates a synchronous Rura hook.
|
|
49
|
-
* - The hook will be executed without `await`.
|
|
50
|
-
* - Suitable for lightweight, purely synchronous operations.
|
|
51
|
-
*/
|
|
52
|
-
declare function createHook<Ctx = unknown, Out = unknown>(name: string, run: RuraHookSync<Ctx, Out>["run"], order?: number): RuraHookSync<Ctx, Out>;
|
|
53
|
-
/**
|
|
54
|
-
* Creates an asynchronous Rura hook.
|
|
55
|
-
* - The hook will be awaited during pipeline execution.
|
|
56
|
-
* - Suitable for I/O, timers, or any async workflow.
|
|
57
|
-
*/
|
|
58
|
-
declare function createHookAsync<Ctx = unknown, Out = unknown>(name: string, run: RuraHookAsync<Ctx, Out>["run"], order?: number): RuraHookAsync<Ctx, Out>;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Final result of a pipeline execution.
|
|
62
|
-
*
|
|
63
|
-
* - If `early` is true, the pipeline stopped before completing all hooks.
|
|
64
|
-
* - The `ctx` field always contains the context after the last executed hook.
|
|
65
|
-
* - The `output` field is present only when early termination occurs.
|
|
66
|
-
*/
|
|
67
|
-
type RuraResult<Ctx, Out> = {
|
|
68
|
-
early: true;
|
|
69
|
-
ctx: Ctx;
|
|
70
|
-
output: Out;
|
|
71
|
-
} | {
|
|
72
|
-
early: false;
|
|
73
|
-
ctx: Ctx;
|
|
74
|
-
output?: never;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Executes a __synchronous__ Rura pipeline.
|
|
79
|
-
*
|
|
80
|
-
* - Hooks are executed in order.
|
|
81
|
-
* - If any hook returns `{ early: true, output }`, the pipeline
|
|
82
|
-
* stops immediately and returns the early result.
|
|
83
|
-
* - Async hooks are not allowed. Detecting one will throw an error.
|
|
84
|
-
*
|
|
85
|
-
* @param ctx - The initial pipeline context.
|
|
86
|
-
* @param hooks - A list of hooks to execute sequentially.
|
|
87
|
-
* @returns A promise resolving to a `RuraResult`.
|
|
88
|
-
*/
|
|
89
|
-
declare function run<Ctx = unknown, Out = unknown>(ctx: Ctx, hooks: RuraHook<Ctx, Out>[]): RuraResult<Ctx, Out>;
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Executes an __asynchronous__ Rura pipeline.
|
|
93
|
-
*
|
|
94
|
-
* - Hooks are awaited in order.
|
|
95
|
-
* - If any hook returns `{ early: true, output }`, the pipeline
|
|
96
|
-
* stops immediately and returns the early result.
|
|
97
|
-
* - Both sync and async hooks are allowed.
|
|
98
|
-
*
|
|
99
|
-
* @param ctx - The initial pipeline context.
|
|
100
|
-
* @param hooks - A list of hooks to execute sequentially.
|
|
101
|
-
* @returns A promise resolving to a `RuraResult`.
|
|
102
|
-
*/
|
|
103
|
-
declare function runAsync<Ctx = unknown, Out = unknown>(ctx: Ctx, hooks: RuraHook<Ctx, Out>[]): Promise<RuraResult<Ctx, Out>>;
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Creates a __synchronous__ Rura pipeline.
|
|
107
|
-
*
|
|
108
|
-
* - Only synchronous hooks are allowed.
|
|
109
|
-
* - Hooks are executed in order using `run()`.
|
|
110
|
-
* - Returns a chainable pipeline instance with `use`, `merge`, `getHooks`, and `run`.
|
|
111
|
-
*/
|
|
112
|
-
declare function createPipeline<Ctx = unknown, Out = unknown>(hooks?: RuraHook<Ctx, Out>[]): {
|
|
113
|
-
use: (h: RuraHook<Ctx, Out>) => /*elided*/ any;
|
|
114
|
-
merge: (other: {
|
|
115
|
-
getHooks(): RuraHook<Ctx, Out>[];
|
|
116
|
-
}) => /*elided*/ any;
|
|
117
|
-
getHooks: () => RuraHook<Ctx, Out>[];
|
|
118
|
-
debugHooks: (title?: ((hooks: RuraHook<Ctx, Out>[]) => string) | undefined) => void;
|
|
119
|
-
run: (ctx: Ctx) => RuraResult<Ctx, Out>;
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Creates an __asynchronous__ Rura pipeline.
|
|
124
|
-
*
|
|
125
|
-
* - Accepts both synchronous and asynchronous hooks.
|
|
126
|
-
* - Hooks are awaited in order using `runAsync()`.
|
|
127
|
-
* - Returns a chainable pipeline instance with `use`, `merge`, `getHooks`, and `run`.
|
|
128
|
-
*/
|
|
129
|
-
declare function createPipelineAsync<Ctx = unknown, Out = unknown>(hooks?: RuraHook<Ctx, Out>[]): {
|
|
130
|
-
use: (h: RuraHook<Ctx, Out>) => /*elided*/ any;
|
|
131
|
-
merge: (other: {
|
|
132
|
-
getHooks(): RuraHook<Ctx, Out>[];
|
|
133
|
-
}) => /*elided*/ any;
|
|
134
|
-
getHooks: () => RuraHook<Ctx, Out>[];
|
|
135
|
-
debugHooks: (title?: ((hooks: RuraHook<Ctx, Out>[]) => string) | undefined) => void;
|
|
136
|
-
run: (ctx: Ctx) => Promise<RuraResult<Ctx, Out>>;
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Determines whether a given hook is asynchronous.
|
|
141
|
-
*
|
|
142
|
-
* A hook is considered async if its `run` function
|
|
143
|
-
* is an `AsyncFunction` (i.e. declared with `async`).
|
|
144
|
-
*
|
|
145
|
-
* @returns `true` if the hook executes asynchronously, otherwise `false`.
|
|
146
|
-
*/
|
|
147
|
-
declare function isAsyncHook<Ctx, Out>(hook: RuraHook<Ctx, Out>): hook is RuraHookAsync<Ctx, Out>;
|
|
148
|
-
|
|
149
|
-
declare const rura: {
|
|
150
|
-
readonly createHook: typeof createHook;
|
|
151
|
-
readonly createHookAsync: typeof createHookAsync;
|
|
152
|
-
readonly run: typeof run;
|
|
153
|
-
readonly runAsync: typeof runAsync;
|
|
154
|
-
readonly createPipeline: typeof createPipeline;
|
|
155
|
-
readonly createPipelineAsync: typeof createPipelineAsync;
|
|
156
|
-
readonly isAsyncHook: typeof isAsyncHook;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
export { type RuraHook, type RuraHookAsync, type RuraHookSync, type RuraResult, rura };
|