neuron-inspector 1.2.1 → 1.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/dist/collections/assertions.js +11 -0
- package/dist/collections/assertions.js.map +1 -1
- package/dist/collections/dynamic-vars-core.d.ts +79 -0
- package/dist/collections/dynamic-vars-core.js +214 -0
- package/dist/collections/dynamic-vars-core.js.map +1 -0
- package/dist/collections/dynamic-vars.d.ts +2 -61
- package/dist/collections/dynamic-vars.js +6 -146
- package/dist/collections/dynamic-vars.js.map +1 -1
- package/dist/collections/encoding.d.ts +3 -0
- package/dist/collections/encoding.js +20 -0
- package/dist/collections/encoding.js.map +1 -0
- package/dist/collections/executor.d.ts +114 -5
- package/dist/collections/executor.js +315 -91
- package/dist/collections/executor.js.map +1 -1
- package/dist/collections/index.d.ts +11 -3
- package/dist/collections/index.js +7 -1
- package/dist/collections/index.js.map +1 -1
- package/dist/collections/masking.d.ts +42 -0
- package/dist/collections/masking.js +188 -0
- package/dist/collections/masking.js.map +1 -0
- package/dist/collections/resolver-core.d.ts +74 -0
- package/dist/collections/resolver-core.js +309 -0
- package/dist/collections/resolver-core.js.map +1 -0
- package/dist/collections/resolver.d.ts +9 -62
- package/dist/collections/resolver.js +21 -281
- package/dist/collections/resolver.js.map +1 -1
- package/dist/collections/scenario-run.d.ts +33 -0
- package/dist/collections/scenario-run.js +68 -0
- package/dist/collections/scenario-run.js.map +1 -0
- package/dist/collections/script-api.d.ts +18 -0
- package/dist/collections/script-api.js +208 -0
- package/dist/collections/script-api.js.map +1 -0
- package/dist/collections/scripts.d.ts +1 -8
- package/dist/collections/scripts.js +6 -196
- package/dist/collections/scripts.js.map +1 -1
- package/dist/collections/slug.d.ts +2 -0
- package/dist/collections/slug.js +8 -0
- package/dist/collections/slug.js.map +1 -0
- package/dist/collections/store.d.ts +5 -2
- package/dist/collections/store.js +6 -6
- package/dist/collections/store.js.map +1 -1
- package/dist/collections/tools.d.ts +8 -1
- package/dist/collections/tools.js +253 -90
- package/dist/collections/tools.js.map +1 -1
- package/dist/collections/types.d.ts +42 -3
- package/dist/correlation.js +10 -0
- package/dist/correlation.js.map +1 -1
- package/dist/extension-slot.d.ts +45 -0
- package/dist/extension-slot.js +81 -0
- package/dist/extension-slot.js.map +1 -0
- package/dist/monitor.d.ts +5 -0
- package/dist/monitor.js +7 -2
- package/dist/monitor.js.map +1 -1
- package/dist/server.js +113 -25
- package/dist/server.js.map +1 -1
- package/dist/tools.js +1 -1
- package/dist/tools.js.map +1 -1
- package/package.json +3 -2
- package/dist/collections/__tests__/assertions.test.d.ts +0 -1
- package/dist/collections/__tests__/assertions.test.js +0 -203
- package/dist/collections/__tests__/assertions.test.js.map +0 -1
- package/dist/collections/__tests__/dynamic-vars.test.d.ts +0 -1
- package/dist/collections/__tests__/dynamic-vars.test.js +0 -169
- package/dist/collections/__tests__/dynamic-vars.test.js.map +0 -1
- package/dist/collections/__tests__/execution.test.d.ts +0 -1
- package/dist/collections/__tests__/execution.test.js +0 -271
- package/dist/collections/__tests__/execution.test.js.map +0 -1
- package/dist/collections/__tests__/integration.test.d.ts +0 -1
- package/dist/collections/__tests__/integration.test.js +0 -478
- package/dist/collections/__tests__/integration.test.js.map +0 -1
- package/dist/collections/__tests__/merge.test.d.ts +0 -1
- package/dist/collections/__tests__/merge.test.js +0 -411
- package/dist/collections/__tests__/merge.test.js.map +0 -1
- package/dist/collections/__tests__/resolver.test.d.ts +0 -1
- package/dist/collections/__tests__/resolver.test.js +0 -282
- package/dist/collections/__tests__/resolver.test.js.map +0 -1
- package/dist/collections/__tests__/scenario.test.d.ts +0 -1
- package/dist/collections/__tests__/scenario.test.js +0 -367
- package/dist/collections/__tests__/scenario.test.js.map +0 -1
- package/dist/collections/__tests__/scripts.test.d.ts +0 -1
- package/dist/collections/__tests__/scripts.test.js +0 -245
- package/dist/collections/__tests__/scripts.test.js.map +0 -1
- package/dist/collections/__tests__/store.test.d.ts +0 -1
- package/dist/collections/__tests__/store.test.js +0 -313
- package/dist/collections/__tests__/store.test.js.map +0 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// The `nr` script API, independent of how the code is executed. The bridge
|
|
2
|
+
// runs scripts in node:vm; the extension's browser engine runs them in a
|
|
3
|
+
// sandboxed extension page. Both build their scope here so scripts behave
|
|
4
|
+
// identically.
|
|
5
|
+
import { toBase64, fromBase64 } from "./encoding.js";
|
|
6
|
+
import { dotGet } from "./assertions.js";
|
|
7
|
+
export function createScriptScope(context, phase, response) {
|
|
8
|
+
const variables = { ...context.variables };
|
|
9
|
+
const mutations = {};
|
|
10
|
+
const tests = [];
|
|
11
|
+
const logs = [];
|
|
12
|
+
// Build mutable request state from context
|
|
13
|
+
const reqState = {
|
|
14
|
+
method: context.request?.method ?? "GET",
|
|
15
|
+
url: context.request?.path ?? "/",
|
|
16
|
+
headers: { ...(context.request?.headers ?? {}) },
|
|
17
|
+
body: context.request?.body,
|
|
18
|
+
cookies: { ...(context.request?.cookies ?? {}) },
|
|
19
|
+
};
|
|
20
|
+
// ── nr.variables ──
|
|
21
|
+
const nrVariables = {
|
|
22
|
+
get(key) { return variables[key]; },
|
|
23
|
+
set(key, value) { variables[key] = String(value); },
|
|
24
|
+
};
|
|
25
|
+
// ── nr.request ──
|
|
26
|
+
const nrRequestHeaders = {
|
|
27
|
+
get(key) { return reqState.headers[key] ?? reqState.headers[key.toLowerCase()]; },
|
|
28
|
+
set(key, value) {
|
|
29
|
+
reqState.headers[key] = value;
|
|
30
|
+
if (!mutations.headers)
|
|
31
|
+
mutations.headers = {};
|
|
32
|
+
mutations.headers[key] = value;
|
|
33
|
+
},
|
|
34
|
+
remove(key) {
|
|
35
|
+
delete reqState.headers[key];
|
|
36
|
+
if (!mutations.headers)
|
|
37
|
+
mutations.headers = {};
|
|
38
|
+
mutations.headers[key] = "";
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
const nrRequest = {
|
|
42
|
+
get method() { return reqState.method; },
|
|
43
|
+
set method(v) { reqState.method = v; mutations.method = v; },
|
|
44
|
+
get url() { return reqState.url; },
|
|
45
|
+
set url(v) { reqState.url = v; mutations.path = v; },
|
|
46
|
+
headers: nrRequestHeaders,
|
|
47
|
+
get body() { return reqState.body; },
|
|
48
|
+
set body(v) { reqState.body = v; mutations.body = v; },
|
|
49
|
+
};
|
|
50
|
+
// ── nr.cookies ──
|
|
51
|
+
const nrCookies = {
|
|
52
|
+
set(name, value) {
|
|
53
|
+
reqState.cookies[name] = value;
|
|
54
|
+
if (!mutations.cookies)
|
|
55
|
+
mutations.cookies = {};
|
|
56
|
+
mutations.cookies[name] = value;
|
|
57
|
+
},
|
|
58
|
+
get(name) { return reqState.cookies[name]; },
|
|
59
|
+
};
|
|
60
|
+
// ── nr.environment (read-only) ──
|
|
61
|
+
const nrEnvironment = {
|
|
62
|
+
get(key) { return context.environment?.variables?.[key]; },
|
|
63
|
+
get name() { return context.environment?.name ?? ""; },
|
|
64
|
+
get base_url() { return context.environment?.base_url ?? ""; },
|
|
65
|
+
};
|
|
66
|
+
// ── nr.response (post-response only) ──
|
|
67
|
+
let nrResponse;
|
|
68
|
+
if (phase === "post" && response) {
|
|
69
|
+
const rawBody = response.body;
|
|
70
|
+
const textBody = typeof rawBody === "string" ? rawBody : JSON.stringify(rawBody);
|
|
71
|
+
nrResponse = {
|
|
72
|
+
status: response.status,
|
|
73
|
+
headers: {
|
|
74
|
+
get(key) {
|
|
75
|
+
return response.headers[key] ?? response.headers[key.toLowerCase()];
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
body: rawBody,
|
|
79
|
+
json() { return rawBody; },
|
|
80
|
+
text() { return textBody; },
|
|
81
|
+
time: response.timing.total_ms,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// ── nr.test ──
|
|
85
|
+
function nrTest(name, fn) {
|
|
86
|
+
try {
|
|
87
|
+
fn();
|
|
88
|
+
tests.push({ name, passed: true });
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
tests.push({
|
|
92
|
+
name,
|
|
93
|
+
passed: false,
|
|
94
|
+
error: err instanceof Error ? err.message : String(err),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// ── nr.log ──
|
|
99
|
+
function nrLog(...args) {
|
|
100
|
+
logs.push(args.map(String).join(" "));
|
|
101
|
+
}
|
|
102
|
+
// Assemble the nr object
|
|
103
|
+
const nr = {
|
|
104
|
+
variables: nrVariables,
|
|
105
|
+
request: nrRequest,
|
|
106
|
+
cookies: nrCookies,
|
|
107
|
+
environment: nrEnvironment,
|
|
108
|
+
test: nrTest,
|
|
109
|
+
log: nrLog,
|
|
110
|
+
};
|
|
111
|
+
if (nrResponse)
|
|
112
|
+
nr.response = nrResponse;
|
|
113
|
+
const globals = {
|
|
114
|
+
nr,
|
|
115
|
+
console: { log: nrLog, warn: nrLog, error: nrLog },
|
|
116
|
+
JSON,
|
|
117
|
+
parseInt,
|
|
118
|
+
parseFloat,
|
|
119
|
+
Math,
|
|
120
|
+
Date,
|
|
121
|
+
Array,
|
|
122
|
+
Object,
|
|
123
|
+
String,
|
|
124
|
+
Number,
|
|
125
|
+
Boolean,
|
|
126
|
+
RegExp,
|
|
127
|
+
Map,
|
|
128
|
+
Set,
|
|
129
|
+
encodeURIComponent,
|
|
130
|
+
decodeURIComponent,
|
|
131
|
+
encodeURI,
|
|
132
|
+
decodeURI,
|
|
133
|
+
btoa: (s) => toBase64(String(s)),
|
|
134
|
+
atob: (s) => fromBase64(String(s)),
|
|
135
|
+
isNaN,
|
|
136
|
+
isFinite,
|
|
137
|
+
undefined,
|
|
138
|
+
NaN,
|
|
139
|
+
Infinity,
|
|
140
|
+
};
|
|
141
|
+
return {
|
|
142
|
+
globals,
|
|
143
|
+
fail(message) {
|
|
144
|
+
logs.push(`[script error] ${message}`);
|
|
145
|
+
},
|
|
146
|
+
finish() {
|
|
147
|
+
const result = { variables };
|
|
148
|
+
const hasMutations = mutations.headers || mutations.body !== undefined ||
|
|
149
|
+
mutations.cookies || mutations.path || mutations.method;
|
|
150
|
+
if (hasMutations)
|
|
151
|
+
result.mutations = mutations;
|
|
152
|
+
if (tests.length)
|
|
153
|
+
result.tests = tests;
|
|
154
|
+
if (logs.length)
|
|
155
|
+
result.logs = logs;
|
|
156
|
+
return result;
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Declarative script runner.
|
|
162
|
+
* Handles pre_request.set and post_response.extract/set without executing arbitrary code.
|
|
163
|
+
*/
|
|
164
|
+
export class DeclarativeScriptRunner {
|
|
165
|
+
runPreRequest(script, context) {
|
|
166
|
+
const variables = { ...context.variables };
|
|
167
|
+
if (script.set) {
|
|
168
|
+
for (const [key, value] of Object.entries(script.set)) {
|
|
169
|
+
variables[key] = value;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return { variables };
|
|
173
|
+
}
|
|
174
|
+
runPostResponse(script, response, context) {
|
|
175
|
+
const variables = { ...context.variables };
|
|
176
|
+
if (script.extract) {
|
|
177
|
+
for (const [varName, path] of Object.entries(script.extract)) {
|
|
178
|
+
const value = extractFromResponse(response, path);
|
|
179
|
+
if (value !== undefined && value !== null) {
|
|
180
|
+
variables[varName] = String(value);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (script.set) {
|
|
185
|
+
for (const [key, value] of Object.entries(script.set)) {
|
|
186
|
+
variables[key] = value;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return { variables };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/** Extract a value from a response using dot notation with body.* prefix handling. */
|
|
193
|
+
function extractFromResponse(response, path) {
|
|
194
|
+
if (path === "status")
|
|
195
|
+
return response.status;
|
|
196
|
+
if (path === "body")
|
|
197
|
+
return response.body;
|
|
198
|
+
if (path.startsWith("body."))
|
|
199
|
+
return dotGet(response.body, path.slice(5));
|
|
200
|
+
if (path.startsWith("header."))
|
|
201
|
+
return response.headers[path.slice(7).toLowerCase()];
|
|
202
|
+
if (path === "timing.total")
|
|
203
|
+
return response.timing.total_ms;
|
|
204
|
+
if (path === "timing.ttfb")
|
|
205
|
+
return response.timing.ttfb_ms;
|
|
206
|
+
return dotGet(response.body, path);
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=script-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script-api.js","sourceRoot":"","sources":["../../src/collections/script-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import type { ScriptRunner, Script, ScriptContext, ScriptResult, AssertionTarget } from "./types.js";
|
|
2
|
-
|
|
3
|
-
* Declarative script runner.
|
|
4
|
-
* Handles pre_request.set and post_response.extract/set without executing arbitrary code.
|
|
5
|
-
*/
|
|
6
|
-
export declare class DeclarativeScriptRunner implements ScriptRunner {
|
|
7
|
-
runPreRequest(script: Script, context: ScriptContext): ScriptResult;
|
|
8
|
-
runPostResponse(script: Script, response: AssertionTarget, context: ScriptContext): ScriptResult;
|
|
9
|
-
}
|
|
2
|
+
export { DeclarativeScriptRunner } from "./script-api.js";
|
|
10
3
|
/**
|
|
11
4
|
* JavaScript script runner.
|
|
12
5
|
* If a script has `code`, runs it in a sandboxed node:vm context
|
|
@@ -1,53 +1,6 @@
|
|
|
1
1
|
import { createContext, runInContext } from "node:vm";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
* Declarative script runner.
|
|
5
|
-
* Handles pre_request.set and post_response.extract/set without executing arbitrary code.
|
|
6
|
-
*/
|
|
7
|
-
export class DeclarativeScriptRunner {
|
|
8
|
-
runPreRequest(script, context) {
|
|
9
|
-
const variables = { ...context.variables };
|
|
10
|
-
if (script.set) {
|
|
11
|
-
for (const [key, value] of Object.entries(script.set)) {
|
|
12
|
-
variables[key] = value;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
return { variables };
|
|
16
|
-
}
|
|
17
|
-
runPostResponse(script, response, context) {
|
|
18
|
-
const variables = { ...context.variables };
|
|
19
|
-
if (script.extract) {
|
|
20
|
-
for (const [varName, path] of Object.entries(script.extract)) {
|
|
21
|
-
const value = extractFromResponse(response, path);
|
|
22
|
-
if (value !== undefined && value !== null) {
|
|
23
|
-
variables[varName] = String(value);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
if (script.set) {
|
|
28
|
-
for (const [key, value] of Object.entries(script.set)) {
|
|
29
|
-
variables[key] = value;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return { variables };
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
/** Extract a value from a response using dot notation with body.* prefix handling. */
|
|
36
|
-
function extractFromResponse(response, path) {
|
|
37
|
-
if (path === "status")
|
|
38
|
-
return response.status;
|
|
39
|
-
if (path === "body")
|
|
40
|
-
return response.body;
|
|
41
|
-
if (path.startsWith("body."))
|
|
42
|
-
return dotGet(response.body, path.slice(5));
|
|
43
|
-
if (path.startsWith("header."))
|
|
44
|
-
return response.headers[path.slice(7).toLowerCase()];
|
|
45
|
-
if (path === "timing.total")
|
|
46
|
-
return response.timing.total_ms;
|
|
47
|
-
if (path === "timing.ttfb")
|
|
48
|
-
return response.timing.ttfb_ms;
|
|
49
|
-
return dotGet(response.body, path);
|
|
50
|
-
}
|
|
2
|
+
import { createScriptScope, DeclarativeScriptRunner } from "./script-api.js";
|
|
3
|
+
export { DeclarativeScriptRunner } from "./script-api.js";
|
|
51
4
|
// ── JS script runner ────────────────────────────────────────────
|
|
52
5
|
const declarativeFallback = new DeclarativeScriptRunner();
|
|
53
6
|
/**
|
|
@@ -68,158 +21,15 @@ export class JsScriptRunner {
|
|
|
68
21
|
return declarativeFallback.runPostResponse(script, response, context);
|
|
69
22
|
}
|
|
70
23
|
executeCode(code, context, phase, response) {
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const tests = [];
|
|
74
|
-
const logs = [];
|
|
75
|
-
// Build mutable request state from context
|
|
76
|
-
const reqState = {
|
|
77
|
-
method: context.request?.method ?? "GET",
|
|
78
|
-
url: context.request?.path ?? "/",
|
|
79
|
-
headers: { ...(context.request?.headers ?? {}) },
|
|
80
|
-
body: context.request?.body,
|
|
81
|
-
cookies: { ...(context.request?.cookies ?? {}) },
|
|
82
|
-
};
|
|
83
|
-
// ── nr.variables ──
|
|
84
|
-
const nrVariables = {
|
|
85
|
-
get(key) { return variables[key]; },
|
|
86
|
-
set(key, value) { variables[key] = String(value); },
|
|
87
|
-
};
|
|
88
|
-
// ── nr.request ──
|
|
89
|
-
const nrRequestHeaders = {
|
|
90
|
-
get(key) { return reqState.headers[key] ?? reqState.headers[key.toLowerCase()]; },
|
|
91
|
-
set(key, value) {
|
|
92
|
-
reqState.headers[key] = value;
|
|
93
|
-
if (!mutations.headers)
|
|
94
|
-
mutations.headers = {};
|
|
95
|
-
mutations.headers[key] = value;
|
|
96
|
-
},
|
|
97
|
-
remove(key) {
|
|
98
|
-
delete reqState.headers[key];
|
|
99
|
-
if (!mutations.headers)
|
|
100
|
-
mutations.headers = {};
|
|
101
|
-
mutations.headers[key] = "";
|
|
102
|
-
},
|
|
103
|
-
};
|
|
104
|
-
const nrRequest = {
|
|
105
|
-
get method() { return reqState.method; },
|
|
106
|
-
set method(v) { reqState.method = v; mutations.method = v; },
|
|
107
|
-
get url() { return reqState.url; },
|
|
108
|
-
set url(v) { reqState.url = v; mutations.path = v; },
|
|
109
|
-
headers: nrRequestHeaders,
|
|
110
|
-
get body() { return reqState.body; },
|
|
111
|
-
set body(v) { reqState.body = v; mutations.body = v; },
|
|
112
|
-
};
|
|
113
|
-
// ── nr.cookies ──
|
|
114
|
-
const nrCookies = {
|
|
115
|
-
set(name, value) {
|
|
116
|
-
reqState.cookies[name] = value;
|
|
117
|
-
if (!mutations.cookies)
|
|
118
|
-
mutations.cookies = {};
|
|
119
|
-
mutations.cookies[name] = value;
|
|
120
|
-
},
|
|
121
|
-
get(name) { return reqState.cookies[name]; },
|
|
122
|
-
};
|
|
123
|
-
// ── nr.environment (read-only) ──
|
|
124
|
-
const nrEnvironment = {
|
|
125
|
-
get(key) { return context.environment?.variables?.[key]; },
|
|
126
|
-
get name() { return context.environment?.name ?? ""; },
|
|
127
|
-
get base_url() { return context.environment?.base_url ?? ""; },
|
|
128
|
-
};
|
|
129
|
-
// ── nr.response (post-response only) ──
|
|
130
|
-
let nrResponse;
|
|
131
|
-
if (phase === "post" && response) {
|
|
132
|
-
const rawBody = response.body;
|
|
133
|
-
const textBody = typeof rawBody === "string" ? rawBody : JSON.stringify(rawBody);
|
|
134
|
-
nrResponse = {
|
|
135
|
-
status: response.status,
|
|
136
|
-
headers: {
|
|
137
|
-
get(key) {
|
|
138
|
-
return response.headers[key] ?? response.headers[key.toLowerCase()];
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
body: rawBody,
|
|
142
|
-
json() { return rawBody; },
|
|
143
|
-
text() { return textBody; },
|
|
144
|
-
time: response.timing.total_ms,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
// ── nr.test ──
|
|
148
|
-
function nrTest(name, fn) {
|
|
149
|
-
try {
|
|
150
|
-
fn();
|
|
151
|
-
tests.push({ name, passed: true });
|
|
152
|
-
}
|
|
153
|
-
catch (err) {
|
|
154
|
-
tests.push({
|
|
155
|
-
name,
|
|
156
|
-
passed: false,
|
|
157
|
-
error: err instanceof Error ? err.message : String(err),
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
// ── nr.log ──
|
|
162
|
-
function nrLog(...args) {
|
|
163
|
-
logs.push(args.map(String).join(" "));
|
|
164
|
-
}
|
|
165
|
-
// Assemble the nr object
|
|
166
|
-
const nr = {
|
|
167
|
-
variables: nrVariables,
|
|
168
|
-
request: nrRequest,
|
|
169
|
-
cookies: nrCookies,
|
|
170
|
-
environment: nrEnvironment,
|
|
171
|
-
test: nrTest,
|
|
172
|
-
log: nrLog,
|
|
173
|
-
};
|
|
174
|
-
if (nrResponse)
|
|
175
|
-
nr.response = nrResponse;
|
|
176
|
-
// ── Sandbox ──
|
|
177
|
-
const sandbox = createContext({
|
|
178
|
-
nr,
|
|
179
|
-
console: { log: nrLog, warn: nrLog, error: nrLog },
|
|
180
|
-
JSON,
|
|
181
|
-
parseInt,
|
|
182
|
-
parseFloat,
|
|
183
|
-
Math,
|
|
184
|
-
Date,
|
|
185
|
-
Array,
|
|
186
|
-
Object,
|
|
187
|
-
String,
|
|
188
|
-
Number,
|
|
189
|
-
Boolean,
|
|
190
|
-
RegExp,
|
|
191
|
-
Map,
|
|
192
|
-
Set,
|
|
193
|
-
encodeURIComponent,
|
|
194
|
-
decodeURIComponent,
|
|
195
|
-
encodeURI,
|
|
196
|
-
decodeURI,
|
|
197
|
-
btoa: (s) => Buffer.from(s).toString("base64"),
|
|
198
|
-
atob: (s) => Buffer.from(s, "base64").toString("utf-8"),
|
|
199
|
-
isNaN,
|
|
200
|
-
isFinite,
|
|
201
|
-
undefined,
|
|
202
|
-
NaN,
|
|
203
|
-
Infinity,
|
|
204
|
-
});
|
|
24
|
+
const scope = createScriptScope(context, phase, response);
|
|
25
|
+
const sandbox = createContext(scope.globals);
|
|
205
26
|
try {
|
|
206
27
|
runInContext(code, sandbox, { timeout: 5000, filename: `script-${phase}.js` });
|
|
207
28
|
}
|
|
208
29
|
catch (err) {
|
|
209
|
-
|
|
210
|
-
logs.push(`[script error] ${msg}`);
|
|
30
|
+
scope.fail(err instanceof Error ? err.message : String(err));
|
|
211
31
|
}
|
|
212
|
-
|
|
213
|
-
const result = { variables };
|
|
214
|
-
const hasMutations = mutations.headers || mutations.body !== undefined ||
|
|
215
|
-
mutations.cookies || mutations.path || mutations.method;
|
|
216
|
-
if (hasMutations)
|
|
217
|
-
result.mutations = mutations;
|
|
218
|
-
if (tests.length)
|
|
219
|
-
result.tests = tests;
|
|
220
|
-
if (logs.length)
|
|
221
|
-
result.logs = logs;
|
|
222
|
-
return result;
|
|
32
|
+
return scope.finish();
|
|
223
33
|
}
|
|
224
34
|
}
|
|
225
35
|
//# sourceMappingURL=scripts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../src/collections/scripts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQtD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../src/collections/scripts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQtD,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,mEAAmE;AAEnE,MAAM,mBAAmB,GAAG,IAAI,uBAAuB,EAAE,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,OAAO,cAAc;IACzB,aAAa,CAAC,MAAc,EAAE,OAAsB;QAClD,IAAI,MAAM,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QACtE,OAAO,mBAAmB,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5D,CAAC;IAED,eAAe,CAAC,MAAc,EAAE,QAAyB,EAAE,OAAsB;QAC/E,IAAI,MAAM,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QACjF,OAAO,mBAAmB,CAAC,eAAe,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACxE,CAAC;IAEO,WAAW,CACjB,IAAY,EACZ,OAAsB,EACtB,KAAqB,EACrB,QAA0B;QAE1B,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC1D,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,CAAC;YACH,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,KAAK,KAAK,EAAE,CAAC,CAAC;QACjF,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,KAAK,CAAC,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC;IACxB,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slug.js","sourceRoot":"","sources":["../../src/collections/slug.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAO,KAAK;SACT,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Collection, CollectionStore, CollectionSummary, Defaults, Endpoint, Folder, ScenarioStep } from "./types.js";
|
|
1
|
+
import type { Collection, CollectionStore, CollectionSummary, Defaults, Endpoint, Folder, RunInput, ScenarioStep } from "./types.js";
|
|
2
2
|
/** Slugify a string: lowercase, replace non-alphanumeric with hyphens, collapse runs, trim. */
|
|
3
|
-
export
|
|
3
|
+
export { slugify } from "./slug.js";
|
|
4
4
|
/** Generate a human-readable name from method + path. */
|
|
5
5
|
export declare function generateEndpointName(method: string, path: string): string;
|
|
6
6
|
export declare class YamlCollectionStore implements CollectionStore {
|
|
@@ -34,6 +34,7 @@ export declare class YamlCollectionStore implements CollectionStore {
|
|
|
34
34
|
variables?: Record<string, string>;
|
|
35
35
|
headers?: Record<string, string>;
|
|
36
36
|
timeout_ms?: number;
|
|
37
|
+
inputs?: RunInput[];
|
|
37
38
|
}): Promise<Collection>;
|
|
38
39
|
removeEnvironment(id: string, envName: string): Promise<Collection>;
|
|
39
40
|
switchEnvironment(id: string, envName: string): Promise<Collection>;
|
|
@@ -58,10 +59,12 @@ export declare class YamlCollectionStore implements CollectionStore {
|
|
|
58
59
|
deleteFolder(id: string, folderName: string): Promise<Collection>;
|
|
59
60
|
createScenario(id: string, scenarioName: string, scenario: {
|
|
60
61
|
description?: string;
|
|
62
|
+
inputs?: RunInput[];
|
|
61
63
|
steps: ScenarioStep[];
|
|
62
64
|
}): Promise<Collection>;
|
|
63
65
|
updateScenario(id: string, scenarioName: string, updates: {
|
|
64
66
|
description?: string;
|
|
67
|
+
inputs?: RunInput[];
|
|
65
68
|
steps?: ScenarioStep[];
|
|
66
69
|
}): Promise<Collection>;
|
|
67
70
|
deleteScenario(id: string, scenarioName: string): Promise<Collection>;
|
|
@@ -3,14 +3,10 @@ import { readFile, writeFile, mkdir, readdir, unlink } from "node:fs/promises";
|
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import * as YAML from "yaml";
|
|
5
5
|
import { NEURON_DIR } from "../paths.js";
|
|
6
|
+
import { slugify } from "./slug.js";
|
|
6
7
|
// ── Helpers ─────────────────────────────────────────────────────
|
|
7
8
|
/** Slugify a string: lowercase, replace non-alphanumeric with hyphens, collapse runs, trim. */
|
|
8
|
-
export
|
|
9
|
-
return input
|
|
10
|
-
.toLowerCase()
|
|
11
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
12
|
-
.replace(/^-+|-+$/g, "");
|
|
13
|
-
}
|
|
9
|
+
export { slugify } from "./slug.js";
|
|
14
10
|
/** Count all endpoints across all folders in a collection. */
|
|
15
11
|
function countEndpoints(c) {
|
|
16
12
|
if (!c.folders)
|
|
@@ -469,6 +465,8 @@ export class YamlCollectionStore {
|
|
|
469
465
|
const s = { steps: scenario.steps };
|
|
470
466
|
if (scenario.description)
|
|
471
467
|
s.description = scenario.description;
|
|
468
|
+
if (scenario.inputs?.length)
|
|
469
|
+
s.inputs = scenario.inputs;
|
|
472
470
|
collection.scenarios[scenarioName] = s;
|
|
473
471
|
const resolved = await this.resolve(id);
|
|
474
472
|
await this.save(id, collection, resolved?.location ?? "global");
|
|
@@ -486,6 +484,8 @@ export class YamlCollectionStore {
|
|
|
486
484
|
scenario.description = updates.description || undefined;
|
|
487
485
|
if (updates.steps !== undefined)
|
|
488
486
|
scenario.steps = updates.steps;
|
|
487
|
+
if (updates.inputs !== undefined)
|
|
488
|
+
scenario.inputs = updates.inputs.length ? updates.inputs : undefined;
|
|
489
489
|
const resolved = await this.resolve(id);
|
|
490
490
|
await this.save(id, collection, resolved?.location ?? "global");
|
|
491
491
|
return collection;
|