@nifrajs/runner 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +7 -7
- package/package.json +2 -2
- package/src/index.ts +9 -9
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# @nifrajs/runner
|
|
2
2
|
|
|
3
|
-
Run requests through a nifra app and capture **structured results**
|
|
3
|
+
Run requests through a nifra app and capture **structured results** - the shared engine behind the
|
|
4
4
|
website playground (humans) and the agent run/verify tool (an AI writes code → runs it → sees the
|
|
5
5
|
failure → fixes it). It only touches `app.fetch(Request) → Response`, which is Web-standard, so it runs
|
|
6
|
-
unchanged in the browser, Bun, Node, Deno, and on the edge
|
|
6
|
+
unchanged in the browser, Bun, Node, Deno, and on the edge - and depends on nothing.
|
|
7
7
|
|
|
8
8
|
```ts
|
|
9
9
|
import { runApp } from "@nifrajs/runner"
|
|
@@ -16,21 +16,21 @@ const results = await runApp(app, [
|
|
|
16
16
|
// → [{ method, path, ok, status, headers, body, durationMs }, …]
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
- **`runApp(app, requests, options?)`**
|
|
19
|
+
- **`runApp(app, requests, options?)`** - run a batch in order; one `RunResult` per request. Never
|
|
20
20
|
throws: a crash on any request becomes that result's `error` and the batch continues.
|
|
21
|
-
- **`runRequest(app, spec, options?)`**
|
|
21
|
+
- **`runRequest(app, spec, options?)`** - a single request.
|
|
22
22
|
|
|
23
23
|
A plain object/array `body` is JSON-encoded (with a JSON content-type unless you set one); a
|
|
24
24
|
string/`Uint8Array` is sent as-is. Relative paths resolve against `options.origin`
|
|
25
25
|
(default `http://nifra.local`). Body text is captured up to `options.maxBodyChars` (default 64 KB).
|
|
26
26
|
|
|
27
27
|
It's a **runner, not a security sandbox**: it calls the app you hand it in-process, with no isolation.
|
|
28
|
-
Isolation, when you need it, comes from the host
|
|
28
|
+
Isolation, when you need it, comes from the host - the browser tab for the playground, your own
|
|
29
29
|
process/CI for the agent runner. Don't feed it code you wouldn't already run.
|
|
30
30
|
|
|
31
31
|
## For AI agents
|
|
32
32
|
|
|
33
|
-
Start with [`LLM.md`](./LLM.md)
|
|
33
|
+
Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
|
|
34
34
|
one cheap read instead of the whole corpus. For the wider framework: the repo's
|
|
35
35
|
[`AGENTS.md`](../../AGENTS.md) is the copy-paste quick reference, and
|
|
36
36
|
[`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Run requests through a nifra app and capture **structured results**
|
|
2
|
+
* Run requests through a nifra app and capture **structured results** - the shared engine behind the
|
|
3
3
|
* website playground (humans) and the agent run/verify tool (AI writes code → runs it → sees the
|
|
4
4
|
* failure → fixes). It only touches `app.fetch(Request) → Response`, which is Web-standard, so it runs
|
|
5
|
-
* unchanged in the browser, Bun, Node, Deno, and on the edge
|
|
5
|
+
* unchanged in the browser, Bun, Node, Deno, and on the edge - and depends on nothing.
|
|
6
6
|
*
|
|
7
7
|
* import { runApp } from "@nifrajs/runner"
|
|
8
8
|
* const results = await runApp(app, [
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
* // → [{ status: 200, ok: true, body: {...}, durationMs }, …]
|
|
13
13
|
*
|
|
14
14
|
* It is a *runner*, not a security sandbox: it calls the app you hand it in-process, with no isolation.
|
|
15
|
-
* Isolation, when you need it, comes from the host
|
|
15
|
+
* Isolation, when you need it, comes from the host - the browser tab for the playground, your own
|
|
16
16
|
* process/CI for the agent runner. Don't feed it code you wouldn't already run.
|
|
17
17
|
*/
|
|
18
|
-
/** Anything with a Web-standard fetch handler
|
|
18
|
+
/** Anything with a Web-standard fetch handler - a nifra app, or any `(Request) => Response`. Declared
|
|
19
19
|
* structurally so this package has zero dependency on `@nifrajs/core`. */
|
|
20
20
|
export interface AppLike {
|
|
21
21
|
fetch(request: Request): Response | Promise<Response>;
|
|
@@ -41,7 +41,7 @@ export interface RunResult {
|
|
|
41
41
|
readonly path: string;
|
|
42
42
|
/** `true` when the app returned a 2xx response. `false` for non-2xx, or when `app.fetch` threw. */
|
|
43
43
|
readonly ok: boolean;
|
|
44
|
-
/** HTTP status
|
|
44
|
+
/** HTTP status - present when the app returned a response (absent when it threw). */
|
|
45
45
|
readonly status?: number;
|
|
46
46
|
readonly statusText?: string;
|
|
47
47
|
readonly headers?: Readonly<Record<string, string>>;
|
|
@@ -66,7 +66,7 @@ export interface RunOptions {
|
|
|
66
66
|
/** Cap on captured body text, in characters. Default 65536. */
|
|
67
67
|
readonly maxBodyChars?: number;
|
|
68
68
|
}
|
|
69
|
-
/** Drive a single request through the app, capturing the outcome (never throws
|
|
69
|
+
/** Drive a single request through the app, capturing the outcome (never throws - a thrown app error
|
|
70
70
|
* becomes `result.error`). */
|
|
71
71
|
export declare function runRequest(app: AppLike, spec: RequestSpec, options?: RunOptions): Promise<RunResult>;
|
|
72
72
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Run requests through a nifra app and capture **structured results**
|
|
2
|
+
* Run requests through a nifra app and capture **structured results** - the shared engine behind the
|
|
3
3
|
* website playground (humans) and the agent run/verify tool (AI writes code → runs it → sees the
|
|
4
4
|
* failure → fixes). It only touches `app.fetch(Request) → Response`, which is Web-standard, so it runs
|
|
5
|
-
* unchanged in the browser, Bun, Node, Deno, and on the edge
|
|
5
|
+
* unchanged in the browser, Bun, Node, Deno, and on the edge - and depends on nothing.
|
|
6
6
|
*
|
|
7
7
|
* import { runApp } from "@nifrajs/runner"
|
|
8
8
|
* const results = await runApp(app, [
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* // → [{ status: 200, ok: true, body: {...}, durationMs }, …]
|
|
13
13
|
*
|
|
14
14
|
* It is a *runner*, not a security sandbox: it calls the app you hand it in-process, with no isolation.
|
|
15
|
-
* Isolation, when you need it, comes from the host
|
|
15
|
+
* Isolation, when you need it, comes from the host - the browser tab for the playground, your own
|
|
16
16
|
* process/CI for the agent runner. Don't feed it code you wouldn't already run.
|
|
17
17
|
*/
|
|
18
18
|
const DEFAULT_ORIGIN = "http://nifra.local";
|
|
@@ -21,7 +21,7 @@ const now = () => typeof performance !== "undefined" && typeof performance.now =
|
|
|
21
21
|
? performance.now()
|
|
22
22
|
: Date.now();
|
|
23
23
|
const METHODS_WITHOUT_BODY = new Set(["GET", "HEAD"]);
|
|
24
|
-
/** A plain JSON-ish object/array (encode it as JSON)
|
|
24
|
+
/** A plain JSON-ish object/array (encode it as JSON) - vs a string/binary body we pass through. */
|
|
25
25
|
function isJsonBody(body) {
|
|
26
26
|
if (body === null || typeof body !== "object")
|
|
27
27
|
return false;
|
|
@@ -57,7 +57,7 @@ function headersToObject(headers) {
|
|
|
57
57
|
});
|
|
58
58
|
return out;
|
|
59
59
|
}
|
|
60
|
-
/** Drive a single request through the app, capturing the outcome (never throws
|
|
60
|
+
/** Drive a single request through the app, capturing the outcome (never throws - a thrown app error
|
|
61
61
|
* becomes `result.error`). */
|
|
62
62
|
export async function runRequest(app, spec, options = {}) {
|
|
63
63
|
const origin = options.origin ?? DEFAULT_ORIGIN;
|
|
@@ -74,7 +74,7 @@ export async function runRequest(app, spec, options = {}) {
|
|
|
74
74
|
request = buildRequest(spec, origin);
|
|
75
75
|
}
|
|
76
76
|
catch (err) {
|
|
77
|
-
// A malformed spec (bad URL, GET-with-body via a custom method, …)
|
|
77
|
+
// A malformed spec (bad URL, GET-with-body via a custom method, …) - report it, don't throw.
|
|
78
78
|
return { ...base, ok: false, durationMs: now() - start, error: toError(err) };
|
|
79
79
|
}
|
|
80
80
|
try {
|
|
@@ -89,7 +89,7 @@ export async function runRequest(app, spec, options = {}) {
|
|
|
89
89
|
body = JSON.parse(text);
|
|
90
90
|
}
|
|
91
91
|
catch {
|
|
92
|
-
// Content-Type lied, or the body is partial
|
|
92
|
+
// Content-Type lied, or the body is partial - keep the text form.
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
return {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nifrajs/runner",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Run requests through a nifra app's fetch and capture structured results
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"description": "Run requests through a nifra app's fetch and capture structured results - the engine behind the playground and the agent run/verify tool. Dependency-free; runs in the browser, Bun, Node, Deno, and on the edge.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
7
7
|
"typescript",
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Run requests through a nifra app and capture **structured results**
|
|
2
|
+
* Run requests through a nifra app and capture **structured results** - the shared engine behind the
|
|
3
3
|
* website playground (humans) and the agent run/verify tool (AI writes code → runs it → sees the
|
|
4
4
|
* failure → fixes). It only touches `app.fetch(Request) → Response`, which is Web-standard, so it runs
|
|
5
|
-
* unchanged in the browser, Bun, Node, Deno, and on the edge
|
|
5
|
+
* unchanged in the browser, Bun, Node, Deno, and on the edge - and depends on nothing.
|
|
6
6
|
*
|
|
7
7
|
* import { runApp } from "@nifrajs/runner"
|
|
8
8
|
* const results = await runApp(app, [
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
* // → [{ status: 200, ok: true, body: {...}, durationMs }, …]
|
|
13
13
|
*
|
|
14
14
|
* It is a *runner*, not a security sandbox: it calls the app you hand it in-process, with no isolation.
|
|
15
|
-
* Isolation, when you need it, comes from the host
|
|
15
|
+
* Isolation, when you need it, comes from the host - the browser tab for the playground, your own
|
|
16
16
|
* process/CI for the agent runner. Don't feed it code you wouldn't already run.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
/** Anything with a Web-standard fetch handler
|
|
19
|
+
/** Anything with a Web-standard fetch handler - a nifra app, or any `(Request) => Response`. Declared
|
|
20
20
|
* structurally so this package has zero dependency on `@nifrajs/core`. */
|
|
21
21
|
export interface AppLike {
|
|
22
22
|
fetch(request: Request): Response | Promise<Response>
|
|
@@ -44,7 +44,7 @@ export interface RunResult {
|
|
|
44
44
|
readonly path: string
|
|
45
45
|
/** `true` when the app returned a 2xx response. `false` for non-2xx, or when `app.fetch` threw. */
|
|
46
46
|
readonly ok: boolean
|
|
47
|
-
/** HTTP status
|
|
47
|
+
/** HTTP status - present when the app returned a response (absent when it threw). */
|
|
48
48
|
readonly status?: number
|
|
49
49
|
readonly statusText?: string
|
|
50
50
|
readonly headers?: Readonly<Record<string, string>>
|
|
@@ -77,7 +77,7 @@ const now = (): number =>
|
|
|
77
77
|
|
|
78
78
|
const METHODS_WITHOUT_BODY: ReadonlySet<string> = new Set(["GET", "HEAD"])
|
|
79
79
|
|
|
80
|
-
/** A plain JSON-ish object/array (encode it as JSON)
|
|
80
|
+
/** A plain JSON-ish object/array (encode it as JSON) - vs a string/binary body we pass through. */
|
|
81
81
|
function isJsonBody(body: unknown): body is Record<string, unknown> | unknown[] {
|
|
82
82
|
if (body === null || typeof body !== "object") return false
|
|
83
83
|
if (body instanceof Uint8Array || body instanceof ArrayBuffer || body instanceof Blob)
|
|
@@ -113,7 +113,7 @@ function headersToObject(headers: Headers): Record<string, string> {
|
|
|
113
113
|
return out
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
/** Drive a single request through the app, capturing the outcome (never throws
|
|
116
|
+
/** Drive a single request through the app, capturing the outcome (never throws - a thrown app error
|
|
117
117
|
* becomes `result.error`). */
|
|
118
118
|
export async function runRequest(
|
|
119
119
|
app: AppLike,
|
|
@@ -134,7 +134,7 @@ export async function runRequest(
|
|
|
134
134
|
try {
|
|
135
135
|
request = buildRequest(spec, origin)
|
|
136
136
|
} catch (err) {
|
|
137
|
-
// A malformed spec (bad URL, GET-with-body via a custom method, …)
|
|
137
|
+
// A malformed spec (bad URL, GET-with-body via a custom method, …) - report it, don't throw.
|
|
138
138
|
return { ...base, ok: false, durationMs: now() - start, error: toError(err) }
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -149,7 +149,7 @@ export async function runRequest(
|
|
|
149
149
|
try {
|
|
150
150
|
body = JSON.parse(text)
|
|
151
151
|
} catch {
|
|
152
|
-
// Content-Type lied, or the body is partial
|
|
152
|
+
// Content-Type lied, or the body is partial - keep the text form.
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
return {
|