@nifrajs/runner 4.0.0 → 5.0.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/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +22 -16
package/dist/index.d.ts
CHANGED
|
@@ -44,7 +44,11 @@ export interface RunResult {
|
|
|
44
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
|
+
/** Response headers. Repeated `set-cookie` values are joined with `, `, as `Headers.get` does;
|
|
48
|
+
* `setCookie` keeps them apart. */
|
|
47
49
|
readonly headers?: Readonly<Record<string, string>>;
|
|
50
|
+
/** Each `set-cookie` value, in order - present when the response set at least one cookie. */
|
|
51
|
+
readonly setCookie?: readonly string[];
|
|
48
52
|
/** Parsed JSON when the response is JSON; otherwise the (possibly truncated) text. */
|
|
49
53
|
readonly body?: unknown;
|
|
50
54
|
/** The response body as text, truncated to `maxBodyChars`. */
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;0EAC0E;AAC1E,MAAM,WAAW,OAAO;IACtB,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CACtD;AAED,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,6FAA6F;IAC7F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,uBAAuB;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACnD;6FACyF;IACzF,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,2CAA2C;AAC3C,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,mGAAmG;IACnG,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,qFAAqF;IACrF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACnD,sFAAsF;IACtF,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,iDAAiD;IACjD,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,qFAAqF;IACrF,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAC9F;AAED,MAAM,WAAW,UAAU;IACzB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAC/B;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;0EAC0E;AAC1E,MAAM,WAAW,OAAO;IACtB,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CACtD;AAED,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,6FAA6F;IAC7F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,uBAAuB;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACnD;6FACyF;IACzF,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,2CAA2C;AAC3C,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,mGAAmG;IACnG,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,qFAAqF;IACrF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B;uCACmC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACnD,6FAA6F;IAC7F,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,sFAAsF;IACtF,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,iDAAiD;IACjD,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,qFAAqF;IACrF,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAC9F;AAED,MAAM,WAAW,UAAU;IACzB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAC/B;AAqHD;8BAC8B;AAC9B,wBAAsB,UAAU,CAC9B,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,WAAW,EACjB,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,SAAS,CAAC,CA+CpB;AASD;;;GAGG;AACH,wBAAgB,MAAM,CACpB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,SAAS,WAAW,EAAE,EAChC,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,SAAS,EAAE,CAAC,CAMtB"}
|
package/dist/index.js
CHANGED
|
@@ -21,17 +21,9 @@ 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
|
-
/**
|
|
24
|
+
/** Any object that is not a native Fetch body; callers check `isRequestBody` first. */
|
|
25
25
|
function isJsonBody(body) {
|
|
26
|
-
|
|
27
|
-
return false;
|
|
28
|
-
if (body instanceof Uint8Array || body instanceof ArrayBuffer || body instanceof Blob)
|
|
29
|
-
return false;
|
|
30
|
-
if (typeof FormData !== "undefined" && body instanceof FormData)
|
|
31
|
-
return false;
|
|
32
|
-
if (typeof ReadableStream !== "undefined" && body instanceof ReadableStream)
|
|
33
|
-
return false;
|
|
34
|
-
return true;
|
|
26
|
+
return body !== null && typeof body === "object";
|
|
35
27
|
}
|
|
36
28
|
/** Runtime-check the non-JSON body shapes advertised by RequestSpec before crossing Fetch's API. */
|
|
37
29
|
function isRequestBody(body) {
|
|
@@ -55,16 +47,18 @@ function buildRequest(spec, origin) {
|
|
|
55
47
|
const headers = new Headers(spec.headers);
|
|
56
48
|
const init = { method, headers };
|
|
57
49
|
if (spec.body !== undefined && !METHODS_WITHOUT_BODY.has(method)) {
|
|
58
|
-
|
|
50
|
+
// A native Fetch body (URLSearchParams, FormData, a typed array) is an object too, so it is
|
|
51
|
+
// matched first and sent with the content type Fetch gives it.
|
|
52
|
+
if (isRequestBody(spec.body)) {
|
|
53
|
+
init.body = spec.body;
|
|
54
|
+
}
|
|
55
|
+
else if (isJsonBody(spec.body)) {
|
|
59
56
|
if (!headers.has("content-type"))
|
|
60
57
|
headers.set("content-type", "application/json");
|
|
61
58
|
init.body = JSON.stringify(spec.body);
|
|
62
59
|
}
|
|
63
60
|
else {
|
|
64
|
-
|
|
65
|
-
throw new TypeError("@nifrajs/runner: body must be a Fetch Request body");
|
|
66
|
-
}
|
|
67
|
-
init.body = spec.body;
|
|
61
|
+
throw new TypeError("@nifrajs/runner: body must be a Fetch Request body");
|
|
68
62
|
}
|
|
69
63
|
}
|
|
70
64
|
return new Request(url, init);
|
|
@@ -72,10 +66,15 @@ function buildRequest(spec, origin) {
|
|
|
72
66
|
function headersToObject(headers) {
|
|
73
67
|
const out = {};
|
|
74
68
|
headers.forEach((value, key) => {
|
|
75
|
-
|
|
69
|
+
// Iteration yields each set-cookie on its own, so plain assignment would keep only the last.
|
|
70
|
+
out[key] = key === "set-cookie" && key in out ? `${out[key]}, ${value}` : value;
|
|
76
71
|
});
|
|
77
72
|
return out;
|
|
78
73
|
}
|
|
74
|
+
function setCookieOf(headers) {
|
|
75
|
+
const cookies = headers.getSetCookie();
|
|
76
|
+
return cookies.length === 0 ? {} : { setCookie: cookies };
|
|
77
|
+
}
|
|
79
78
|
/** Read at most the caller's character budget while enforcing a byte budget before decoding more data. */
|
|
80
79
|
async function readBoundedText(response, maxChars) {
|
|
81
80
|
if (response.body === null || maxChars === 0) {
|
|
@@ -107,9 +106,8 @@ async function readBoundedText(response, maxChars) {
|
|
|
107
106
|
// while the final string slice preserves the public character-count contract.
|
|
108
107
|
const byteBudget = Math.max(1, Math.min(Number.MAX_SAFE_INTEGER, remaining * 4));
|
|
109
108
|
const allowed = Math.min(value.byteLength, byteBudget);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
});
|
|
109
|
+
// Always streamed: a character split across two chunks decodes once the second arrives.
|
|
110
|
+
const chunk = decoder.decode(value.subarray(0, allowed), { stream: true });
|
|
113
111
|
chunks.push(chunk);
|
|
114
112
|
totalChars += chunk.length;
|
|
115
113
|
if (allowed < value.byteLength || chunk.length > remaining) {
|
|
@@ -165,6 +163,7 @@ export async function runRequest(app, spec, options = {}) {
|
|
|
165
163
|
status: res.status,
|
|
166
164
|
statusText: res.statusText,
|
|
167
165
|
headers: headersToObject(res.headers),
|
|
166
|
+
...setCookieOf(res.headers),
|
|
168
167
|
body,
|
|
169
168
|
bodyText,
|
|
170
169
|
...(captured.truncated ? { truncated: true } : {}),
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nifrajs/runner",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
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",
|
package/src/index.ts
CHANGED
|
@@ -47,7 +47,11 @@ export interface RunResult {
|
|
|
47
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
|
+
/** Response headers. Repeated `set-cookie` values are joined with `, `, as `Headers.get` does;
|
|
51
|
+
* `setCookie` keeps them apart. */
|
|
50
52
|
readonly headers?: Readonly<Record<string, string>>
|
|
53
|
+
/** Each `set-cookie` value, in order - present when the response set at least one cookie. */
|
|
54
|
+
readonly setCookie?: readonly string[]
|
|
51
55
|
/** Parsed JSON when the response is JSON; otherwise the (possibly truncated) text. */
|
|
52
56
|
readonly body?: unknown
|
|
53
57
|
/** The response body as text, truncated to `maxBodyChars`. */
|
|
@@ -77,14 +81,9 @@ const now = (): number =>
|
|
|
77
81
|
|
|
78
82
|
const METHODS_WITHOUT_BODY: ReadonlySet<string> = new Set(["GET", "HEAD"])
|
|
79
83
|
|
|
80
|
-
/**
|
|
84
|
+
/** Any object that is not a native Fetch body; callers check `isRequestBody` first. */
|
|
81
85
|
function isJsonBody(body: unknown): body is Record<string, unknown> | unknown[] {
|
|
82
|
-
|
|
83
|
-
if (body instanceof Uint8Array || body instanceof ArrayBuffer || body instanceof Blob)
|
|
84
|
-
return false
|
|
85
|
-
if (typeof FormData !== "undefined" && body instanceof FormData) return false
|
|
86
|
-
if (typeof ReadableStream !== "undefined" && body instanceof ReadableStream) return false
|
|
87
|
-
return true
|
|
86
|
+
return body !== null && typeof body === "object"
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
type RequestBody = Exclude<RequestInit["body"], undefined>
|
|
@@ -108,14 +107,15 @@ function buildRequest(spec: RequestSpec, origin: string): Request {
|
|
|
108
107
|
const init: RequestInit = { method, headers }
|
|
109
108
|
|
|
110
109
|
if (spec.body !== undefined && !METHODS_WITHOUT_BODY.has(method)) {
|
|
111
|
-
|
|
110
|
+
// A native Fetch body (URLSearchParams, FormData, a typed array) is an object too, so it is
|
|
111
|
+
// matched first and sent with the content type Fetch gives it.
|
|
112
|
+
if (isRequestBody(spec.body)) {
|
|
113
|
+
init.body = spec.body
|
|
114
|
+
} else if (isJsonBody(spec.body)) {
|
|
112
115
|
if (!headers.has("content-type")) headers.set("content-type", "application/json")
|
|
113
116
|
init.body = JSON.stringify(spec.body)
|
|
114
117
|
} else {
|
|
115
|
-
|
|
116
|
-
throw new TypeError("@nifrajs/runner: body must be a Fetch Request body")
|
|
117
|
-
}
|
|
118
|
-
init.body = spec.body
|
|
118
|
+
throw new TypeError("@nifrajs/runner: body must be a Fetch Request body")
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
return new Request(url, init)
|
|
@@ -124,11 +124,17 @@ function buildRequest(spec: RequestSpec, origin: string): Request {
|
|
|
124
124
|
function headersToObject(headers: Headers): Record<string, string> {
|
|
125
125
|
const out: Record<string, string> = {}
|
|
126
126
|
headers.forEach((value, key) => {
|
|
127
|
-
|
|
127
|
+
// Iteration yields each set-cookie on its own, so plain assignment would keep only the last.
|
|
128
|
+
out[key] = key === "set-cookie" && key in out ? `${out[key]}, ${value}` : value
|
|
128
129
|
})
|
|
129
130
|
return out
|
|
130
131
|
}
|
|
131
132
|
|
|
133
|
+
function setCookieOf(headers: Headers): { readonly setCookie?: readonly string[] } {
|
|
134
|
+
const cookies = headers.getSetCookie()
|
|
135
|
+
return cookies.length === 0 ? {} : { setCookie: cookies }
|
|
136
|
+
}
|
|
137
|
+
|
|
132
138
|
interface BoundedText {
|
|
133
139
|
readonly text: string
|
|
134
140
|
readonly truncated: boolean
|
|
@@ -163,9 +169,8 @@ async function readBoundedText(response: Response, maxChars: number): Promise<Bo
|
|
|
163
169
|
// while the final string slice preserves the public character-count contract.
|
|
164
170
|
const byteBudget = Math.max(1, Math.min(Number.MAX_SAFE_INTEGER, remaining * 4))
|
|
165
171
|
const allowed = Math.min(value.byteLength, byteBudget)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
})
|
|
172
|
+
// Always streamed: a character split across two chunks decodes once the second arrives.
|
|
173
|
+
const chunk = decoder.decode(value.subarray(0, allowed), { stream: true })
|
|
169
174
|
chunks.push(chunk)
|
|
170
175
|
totalChars += chunk.length
|
|
171
176
|
if (allowed < value.byteLength || chunk.length > remaining) {
|
|
@@ -225,6 +230,7 @@ export async function runRequest(
|
|
|
225
230
|
status: res.status,
|
|
226
231
|
statusText: res.statusText,
|
|
227
232
|
headers: headersToObject(res.headers),
|
|
233
|
+
...setCookieOf(res.headers),
|
|
228
234
|
body,
|
|
229
235
|
bodyText,
|
|
230
236
|
...(captured.truncated ? { truncated: true } : {}),
|