@softprobe/softprobe-js 2.0.0 → 2.0.3
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 +55 -4
- package/dist/cli/capture.d.ts +13 -0
- package/dist/cli/capture.d.ts.map +1 -0
- package/dist/cli/capture.js +34 -0
- package/dist/cli/capture.js.map +1 -0
- package/dist/cli.js +55 -12
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,9 +46,7 @@ node -r ./instrumentation.ts ./server.ts
|
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
TRACE_ID=11111111111111111111111111111111
|
|
49
|
-
|
|
50
|
-
-H "x-softprobe-trace-id: ${TRACE_ID}" \
|
|
51
|
-
http://localhost:3000/your-route
|
|
49
|
+
softprobe capture "http://localhost:3000/your-route" --trace-id "${TRACE_ID}"
|
|
52
50
|
```
|
|
53
51
|
|
|
54
52
|
4. Replay in tests with `run({ mode, storage, traceId }, fn)`:
|
|
@@ -85,7 +83,34 @@ softprobe diff ./cassettes/11111111111111111111111111111111.ndjson http://localh
|
|
|
85
83
|
|
|
86
84
|
For a full walkthrough, see [examples/basic-app/README.md](examples/basic-app/README.md) and [examples/pricing-regression-demo/README.md](examples/pricing-regression-demo/README.md).
|
|
87
85
|
|
|
88
|
-
## CLI — `softprobe diff`
|
|
86
|
+
## CLI — `softprobe capture` and `softprobe diff`
|
|
87
|
+
|
|
88
|
+
Use `softprobe capture` to record via HTTP headers, and `softprobe diff` to replay + compare.
|
|
89
|
+
|
|
90
|
+
### Capture usage
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
softprobe capture <url> --trace-id <traceId> [--method <METHOD>] [--data <body>] [--header <k:v> ...] [--output <file>]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`softprobe capture` invokes `curl` and always sends:
|
|
97
|
+
|
|
98
|
+
- `x-softprobe-mode: CAPTURE`
|
|
99
|
+
- `x-softprobe-trace-id: <traceId>`
|
|
100
|
+
|
|
101
|
+
Example:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
softprobe capture "http://localhost:3000/orders/42" \
|
|
105
|
+
--trace-id 11111111111111111111111111111111 \
|
|
106
|
+
--method POST \
|
|
107
|
+
--header "content-type: application/json" \
|
|
108
|
+
--data '{"quantity":2}'
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
This confirms: yes, capture mode can be turned on by HTTP headers, and this command standardizes it.
|
|
112
|
+
|
|
113
|
+
### Diff usage
|
|
89
114
|
|
|
90
115
|
Replay the recorded inbound request against a running service and compare. The CLI sends the request with **coordination headers** so the service can run in replay mode for that request.
|
|
91
116
|
|
|
@@ -186,6 +211,32 @@ Manual validation flow:
|
|
|
186
211
|
|
|
187
212
|
No `NPM_TOKEN` repository secret is required for this workflow.
|
|
188
213
|
|
|
214
|
+
## Cursor Skills
|
|
215
|
+
|
|
216
|
+
This repo ships a ready-to-use Cursor Skill at:
|
|
217
|
+
|
|
218
|
+
- `cursor-skills/softprobe/SKILL.md`
|
|
219
|
+
- `cursor-skills/softprobe/templates/capture.sh`
|
|
220
|
+
- `cursor-skills/softprobe/templates/diff.sh`
|
|
221
|
+
- `cursor-skills/softprobe/templates/demo-pricing.sh`
|
|
222
|
+
|
|
223
|
+
### Install into Cursor
|
|
224
|
+
|
|
225
|
+
1. Open Cursor Settings.
|
|
226
|
+
2. Open Skills management.
|
|
227
|
+
3. Add/import the skill from this repo path: `cursor-skills/softprobe`.
|
|
228
|
+
4. Ensure global CLI is installed: `npm install -g @softprobe/softprobe-js`.
|
|
229
|
+
5. Reload Cursor window.
|
|
230
|
+
|
|
231
|
+
### Use in Cursor
|
|
232
|
+
|
|
233
|
+
1. Ask Cursor to run the Softprobe skill workflow.
|
|
234
|
+
2. Provide `TARGET_URL`, `ROUTE`, `TRACE_ID`, and cassette directory.
|
|
235
|
+
3. Use the templates directly or let Cursor fill them in:
|
|
236
|
+
- capture: `templates/capture.sh`
|
|
237
|
+
- replay compare: `templates/diff.sh`
|
|
238
|
+
- demo flow: `templates/demo-pricing.sh`
|
|
239
|
+
|
|
189
240
|
## Package Layout
|
|
190
241
|
|
|
191
242
|
- `src/core` contains shared framework-agnostic contracts and runtime helpers.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type CaptureOptions = {
|
|
2
|
+
url: string;
|
|
3
|
+
traceId: string;
|
|
4
|
+
method?: string;
|
|
5
|
+
data?: string;
|
|
6
|
+
headers?: string[];
|
|
7
|
+
output?: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Executes curl with Softprobe capture headers for one request.
|
|
11
|
+
*/
|
|
12
|
+
export declare function runCapture(opts: CaptureOptions): number;
|
|
13
|
+
//# sourceMappingURL=capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../src/cli/capture.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CA+BvD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runCapture = runCapture;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
/**
|
|
6
|
+
* Executes curl with Softprobe capture headers for one request.
|
|
7
|
+
*/
|
|
8
|
+
function runCapture(opts) {
|
|
9
|
+
const args = ['-sS'];
|
|
10
|
+
if (opts.method) {
|
|
11
|
+
args.push('-X', opts.method);
|
|
12
|
+
}
|
|
13
|
+
args.push('-H', 'x-softprobe-mode: CAPTURE');
|
|
14
|
+
args.push('-H', `x-softprobe-trace-id: ${opts.traceId}`);
|
|
15
|
+
for (const header of opts.headers ?? []) {
|
|
16
|
+
args.push('-H', header);
|
|
17
|
+
}
|
|
18
|
+
if (opts.data !== undefined) {
|
|
19
|
+
args.push('--data', opts.data);
|
|
20
|
+
}
|
|
21
|
+
if (opts.output) {
|
|
22
|
+
args.push('-o', opts.output);
|
|
23
|
+
}
|
|
24
|
+
args.push(opts.url);
|
|
25
|
+
const result = (0, child_process_1.spawnSync)('curl', args, {
|
|
26
|
+
stdio: 'inherit',
|
|
27
|
+
env: process.env,
|
|
28
|
+
});
|
|
29
|
+
if (result.error) {
|
|
30
|
+
throw result.error;
|
|
31
|
+
}
|
|
32
|
+
return result.status ?? 1;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/cli/capture.ts"],"names":[],"mappings":";;AAcA,gCA+BC;AA7CD,iDAA0C;AAW1C;;GAEG;AACH,SAAgB,UAAU,CAAC,IAAoB;IAC7C,MAAM,IAAI,GAAa,CAAC,KAAK,CAAC,CAAC;IAE/B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,2BAA2B,CAAC,CAAC;IAC7C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,yBAAyB,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAEzD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEpB,MAAM,MAAM,GAAG,IAAA,yBAAS,EAAC,MAAM,EAAE,IAAI,EAAE;QACrC,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,OAAO,CAAC,GAAG;KACjB,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,MAAM,CAAC,KAAK,CAAC;IACrB,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;AAC5B,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -7,27 +7,18 @@
|
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
const diff_1 = require("./cli/diff");
|
|
9
9
|
const diff_reporter_1 = require("./cli/diff-reporter");
|
|
10
|
+
const capture_1 = require("./cli/capture");
|
|
10
11
|
const args = process.argv.slice(2);
|
|
11
12
|
const command = args[0];
|
|
12
13
|
const HELP_COMMANDS = new Set(['help', '--help', '-h']);
|
|
13
14
|
const VERSION_COMMANDS = new Set(['version', '--version', '-v']);
|
|
14
|
-
const ignoreBodyPaths = [];
|
|
15
|
-
const positional = [];
|
|
16
|
-
for (let i = 1; i < args.length; i++) {
|
|
17
|
-
if (args[i] === '--ignore-paths' && args[i + 1]) {
|
|
18
|
-
ignoreBodyPaths.push(args[++i]);
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
positional.push(args[i]);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
const file = positional[0];
|
|
25
|
-
const target = positional[1];
|
|
26
15
|
function usage() {
|
|
27
16
|
console.error('Usage: softprobe diff [--ignore-paths <path> ...] <cassette.ndjson> <targetUrl>');
|
|
17
|
+
console.error(' softprobe capture <url> --trace-id <traceId> [--method <METHOD>] [--data <body>] [--header <k:v> ...] [--output <file>]');
|
|
28
18
|
console.error(' softprobe --help');
|
|
29
19
|
console.error(' softprobe --version');
|
|
30
20
|
console.error(' Replays the recorded inbound request to the target with coordination headers.');
|
|
21
|
+
console.error(' capture: invokes curl with Softprobe capture headers for one request.');
|
|
31
22
|
console.error(' --ignore-paths JSON path to omit from body comparison (e.g. http.headers for upstream variance).');
|
|
32
23
|
}
|
|
33
24
|
function printVersion() {
|
|
@@ -54,6 +45,58 @@ async function main() {
|
|
|
54
45
|
printVersion();
|
|
55
46
|
return 0;
|
|
56
47
|
}
|
|
48
|
+
if (command === 'capture') {
|
|
49
|
+
const captureArgs = args.slice(1);
|
|
50
|
+
const captureUrl = captureArgs[0];
|
|
51
|
+
if (!captureUrl) {
|
|
52
|
+
usage();
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
let traceId = '';
|
|
56
|
+
let method = 'GET';
|
|
57
|
+
let data;
|
|
58
|
+
const headers = [];
|
|
59
|
+
let output;
|
|
60
|
+
for (let i = 1; i < captureArgs.length; i++) {
|
|
61
|
+
const token = captureArgs[i];
|
|
62
|
+
if (token === '--trace-id' && captureArgs[i + 1]) {
|
|
63
|
+
traceId = captureArgs[++i];
|
|
64
|
+
}
|
|
65
|
+
else if (token === '--method' && captureArgs[i + 1]) {
|
|
66
|
+
method = captureArgs[++i];
|
|
67
|
+
}
|
|
68
|
+
else if (token === '--data' && captureArgs[i + 1]) {
|
|
69
|
+
data = captureArgs[++i];
|
|
70
|
+
}
|
|
71
|
+
else if (token === '--header' && captureArgs[i + 1]) {
|
|
72
|
+
headers.push(captureArgs[++i]);
|
|
73
|
+
}
|
|
74
|
+
else if (token === '--output' && captureArgs[i + 1]) {
|
|
75
|
+
output = captureArgs[++i];
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
console.error(`Unknown or incomplete capture option: ${token}`);
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!traceId) {
|
|
83
|
+
console.error('capture requires --trace-id <traceId>');
|
|
84
|
+
return 1;
|
|
85
|
+
}
|
|
86
|
+
return (0, capture_1.runCapture)({ url: captureUrl, traceId, method, data, headers, output });
|
|
87
|
+
}
|
|
88
|
+
const ignoreBodyPaths = [];
|
|
89
|
+
const positional = [];
|
|
90
|
+
for (let i = 1; i < args.length; i++) {
|
|
91
|
+
if (args[i] === '--ignore-paths' && args[i + 1]) {
|
|
92
|
+
ignoreBodyPaths.push(args[++i]);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
positional.push(args[i]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const file = positional[0];
|
|
99
|
+
const target = positional[1];
|
|
57
100
|
if (command !== 'diff' || !file || !target) {
|
|
58
101
|
usage();
|
|
59
102
|
return 1;
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;AACA;;;GAGG;;AAEH,qCAAqC;AACrC,uDAAiD;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|