@http-forge/core 0.1.0 → 0.2.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 +217 -43
- package/dist/auth/interfaces.d.ts +63 -0
- package/dist/auth/oauth2-token-manager.d.ts +103 -0
- package/dist/collection/collection-loader-factory.d.ts +20 -0
- package/dist/{services → collection}/collection-loader.d.ts +3 -3
- package/dist/collection/collection-service-interfaces.d.ts +119 -0
- package/dist/collection/collection-service.d.ts +75 -0
- package/dist/collection/collection-store.d.ts +109 -0
- package/dist/collection/folder-collection-loader.d.ts +45 -0
- package/dist/collection/folder-collection-store.d.ts +175 -0
- package/dist/collection/folder-io.d.ts +113 -0
- package/dist/collection/interfaces.d.ts +32 -0
- package/dist/collection/json-collection-loader.d.ts +95 -0
- package/dist/{services → collection}/parser-registry.d.ts +1 -2
- package/dist/config/config-service.d.ts +79 -0
- package/dist/config/config.interface.d.ts +140 -0
- package/dist/config/default-config.d.ts +29 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/container.d.ts +26 -19
- package/dist/{implementations → cookie}/cookie-jar.d.ts +2 -3
- package/dist/cookie/cookie-service.d.ts +98 -0
- package/dist/{implementations → cookie}/cookie-utils.d.ts +1 -2
- package/dist/cookie/in-memory-cookie-jar.d.ts +44 -0
- package/dist/{interfaces/cookie.d.ts → cookie/interfaces.d.ts} +22 -3
- package/dist/cookie/persistent-cookie-jar.d.ts +35 -0
- package/dist/di/core-bootstrap.d.ts +25 -0
- package/dist/di/index.d.ts +11 -0
- package/dist/di/platform-adapters.d.ts +53 -0
- package/dist/di/service-container.d.ts +97 -0
- package/dist/di/service-identifiers.d.ts +34 -0
- package/dist/environment/environment-config-service.d.ts +98 -0
- package/dist/environment/environment-file-loader.d.ts +42 -0
- package/dist/{services → environment}/environment-resolver.d.ts +6 -5
- package/dist/{services → environment}/forge-env.d.ts +1 -2
- package/dist/environment/interfaces.d.ts +139 -0
- package/dist/environment/variable-interpolator.d.ts +100 -0
- package/dist/execution/collection-request-executor-interfaces.d.ts +36 -0
- package/dist/execution/collection-request-executor.d.ts +78 -0
- package/dist/{services → execution}/request-executor.d.ts +23 -11
- package/dist/execution/request-preparer-interfaces.d.ts +36 -0
- package/dist/execution/request-preparer.d.ts +35 -0
- package/dist/graphql/graphql-completion-provider.d.ts +39 -0
- package/dist/graphql/graphql-schema-service.d.ts +89 -0
- package/dist/{interfaces/history.d.ts → history/history-interfaces.d.ts} +29 -6
- package/dist/history/request-history-service-interfaces.d.ts +43 -0
- package/dist/history/request-history-service.d.ts +133 -0
- package/dist/{implementations → history}/request-history.d.ts +2 -3
- package/dist/{implementations → http}/fetch-http-client.d.ts +4 -5
- package/dist/http/http-request-service.d.ts +36 -0
- package/dist/{implementations → http}/interceptor-chain.d.ts +1 -2
- package/dist/http/interfaces.d.ts +25 -0
- package/dist/http/merge-request-settings.d.ts +12 -0
- package/dist/{implementations → http}/native-http-client.d.ts +6 -15
- package/dist/{implementations → http}/request-preprocessor.d.ts +1 -2
- package/dist/{services → http}/url-builder.d.ts +7 -10
- package/dist/import-export/import-postman-environment.d.ts +21 -0
- package/dist/import-export/rest-client-export.d.ts +35 -0
- package/dist/index.d.ts +94 -6
- package/dist/index.js +262 -35
- package/dist/index.mjs +262 -35
- package/dist/openapi/example-generator.d.ts +26 -0
- package/dist/openapi/history-analyzer.d.ts +29 -0
- package/dist/openapi/index.d.ts +16 -0
- package/dist/openapi/interfaces.d.ts +42 -0
- package/dist/openapi/openapi-exporter.d.ts +73 -0
- package/dist/openapi/openapi-importer.d.ts +72 -0
- package/dist/openapi/ref-resolver.d.ts +28 -0
- package/dist/openapi/schema-inference-service.d.ts +40 -0
- package/dist/openapi/schema-inferrer.d.ts +26 -0
- package/dist/openapi/script-analyzer.d.ts +41 -0
- package/dist/parsers/http-forge-parser.d.ts +2 -3
- package/dist/parsers/index.d.ts +0 -1
- package/dist/{implementations → platform}/data-file-parser.d.ts +0 -1
- package/dist/{implementations → platform}/node-file-system.d.ts +1 -2
- package/dist/script/interfaces.d.ts +161 -0
- package/dist/script/module-loader.d.ts +115 -0
- package/dist/script/request-script-session.d.ts +73 -0
- package/dist/script/script-executor.d.ts +60 -0
- package/dist/script/script-factories.d.ts +94 -0
- package/dist/script/script-utils.d.ts +42 -0
- package/dist/test-suite/index.d.ts +10 -0
- package/dist/test-suite/interfaces.d.ts +164 -0
- package/dist/test-suite/result-storage-service.d.ts +70 -0
- package/dist/test-suite/result-storage.d.ts +296 -0
- package/dist/test-suite/statistics-service.d.ts +51 -0
- package/dist/test-suite/test-suite-service.d.ts +97 -0
- package/dist/test-suite/test-suite-store.d.ts +155 -0
- package/dist/types/console-service.d.ts +40 -0
- package/dist/types/platform.d.ts +206 -0
- package/dist/{interfaces → types}/types.d.ts +289 -12
- package/dist/utils/dynamic-variables.d.ts +38 -0
- package/dist/utils/expression-evaluator.d.ts +34 -0
- package/dist/utils/filter-engine.d.ts +47 -0
- package/dist/utils/helpers.d.ts +47 -0
- package/package.json +12 -4
- package/dist/container.d.ts.map +0 -1
- package/dist/implementations/cookie-jar.d.ts.map +0 -1
- package/dist/implementations/cookie-utils.d.ts.map +0 -1
- package/dist/implementations/data-file-parser.d.ts.map +0 -1
- package/dist/implementations/fetch-http-client.d.ts.map +0 -1
- package/dist/implementations/index.d.ts +0 -22
- package/dist/implementations/index.d.ts.map +0 -1
- package/dist/implementations/interceptor-chain.d.ts.map +0 -1
- package/dist/implementations/module-loader.d.ts +0 -74
- package/dist/implementations/module-loader.d.ts.map +0 -1
- package/dist/implementations/native-http-client.d.ts.map +0 -1
- package/dist/implementations/node-file-system.d.ts.map +0 -1
- package/dist/implementations/request-history.d.ts.map +0 -1
- package/dist/implementations/request-preprocessor.d.ts.map +0 -1
- package/dist/implementations/variable-interpolator.d.ts +0 -55
- package/dist/implementations/variable-interpolator.d.ts.map +0 -1
- package/dist/implementations/vm2-script-runner.d.ts +0 -76
- package/dist/implementations/vm2-script-runner.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/interfaces/cookie.d.ts.map +0 -1
- package/dist/interfaces/history.d.ts.map +0 -1
- package/dist/interfaces/index.d.ts +0 -170
- package/dist/interfaces/index.d.ts.map +0 -1
- package/dist/interfaces/types.d.ts.map +0 -1
- package/dist/parsers/http-forge-parser.d.ts.map +0 -1
- package/dist/parsers/index.d.ts.map +0 -1
- package/dist/services/collection-loader.d.ts.map +0 -1
- package/dist/services/environment-resolver.d.ts.map +0 -1
- package/dist/services/folder-collection-loader.d.ts +0 -91
- package/dist/services/folder-collection-loader.d.ts.map +0 -1
- package/dist/services/forge-env.d.ts.map +0 -1
- package/dist/services/index.d.ts +0 -20
- package/dist/services/index.d.ts.map +0 -1
- package/dist/services/parser-registry.d.ts.map +0 -1
- package/dist/services/request-executor.d.ts.map +0 -1
- package/dist/services/script-pipeline.d.ts +0 -43
- package/dist/services/script-pipeline.d.ts.map +0 -1
- package/dist/services/script-session.d.ts +0 -66
- package/dist/services/script-session.d.ts.map +0 -1
- package/dist/services/url-builder.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
# @http-forge/core
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Standalone HTTP testing engine with Postman collection support and JavaScript-based automation.**
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@http-forge/core)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
8
|
## 📦 What is @http-forge/core?
|
|
9
9
|
|
|
10
|
-
`@http-forge/core` is a
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
**
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
|
|
10
|
+
`@http-forge/core` is a headless, framework-agnostic HTTP execution engine with **full Postman collection compatibility**. Execute complex API workflows, test suites, and automated flows without the overhead of a UI.
|
|
11
|
+
|
|
12
|
+
**Core Features:**
|
|
13
|
+
- 🚀 **Postman Collections** - Load and execute `.postman_collection.json` and `.forge.json` files
|
|
14
|
+
- 📝 **JavaScript Scripting** - Pre-request and post-response scripts with full `pm.*` API (variables, assertions, execution flow, visualizer)
|
|
15
|
+
- 🔄 **Dynamic Variables** - Built-in generators: `{{$randomInt}}`, `{{$timestamp}}`, `{{$uuid}}`, `{{$guid}}`, etc.
|
|
16
|
+
- 🌍 **Environments** - Full variable scoping (globals, collection, environment, session, iterationData)
|
|
17
|
+
- 🍪 **Cookie Persistence** - Automatic cookie storage and reuse, `pm.cookies.jar()` and `.toObject()`
|
|
18
|
+
- 📊 **Test Assertions** - BDD-style testing with `pm.test()` (sync/async) and full Chai `expect()` chains
|
|
19
|
+
- 🔐 **CryptoJS** - Full crypto library: hash, HMAC, AES/DES/TripleDES, PBKDF2, encoding helpers
|
|
20
|
+
- 🎯 **Execution Flow** - `pm.setNextRequest()`, `pm.execution.skipRequest()` for suite runner flow control
|
|
21
|
+
- 📈 **Visualizer** - `pm.visualizer.set(template, data)` for custom Handlebars-based HTML output
|
|
22
|
+
- 🔌 **Extensible** - Custom interceptors, HTTP clients, and module loaders
|
|
23
|
+
|
|
24
|
+
**Ideal for:**
|
|
25
|
+
- CI/CD pipeline integration (GitHub Actions, GitLab CI, Jenkins)
|
|
26
|
+
- Headless API testing and contract validation
|
|
27
|
+
- Building custom API testing CLIs
|
|
28
|
+
- Load testing and performance monitoring
|
|
29
|
+
- Automated integration test suites
|
|
24
30
|
|
|
25
31
|
## 🎯 Installation
|
|
26
32
|
|
|
@@ -141,29 +147,79 @@ console.log(result.preRequestResult); // Pre-request script output
|
|
|
141
147
|
console.log(result.postResponseResult); // Test results
|
|
142
148
|
```
|
|
143
149
|
|
|
150
|
+
### Dynamic Variables
|
|
151
|
+
|
|
152
|
+
Automatic variable generation within request templates:
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
// URL with dynamic timestamp
|
|
156
|
+
https://api.example.com/events?timestamp={{$timestamp}}
|
|
157
|
+
|
|
158
|
+
// Headers with unique ID
|
|
159
|
+
X-Request-ID: {{$uuid}}
|
|
160
|
+
|
|
161
|
+
// Query parameters with random value
|
|
162
|
+
?page=1&seed={{$randomInt:1:100}}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Supported dynamic variables:**
|
|
166
|
+
- `{{$randomInt}}` - Random integer (0-2147483647)
|
|
167
|
+
- `{{$randomInt:min:max}}` - Random integer in range
|
|
168
|
+
- `{{$timestamp}}` - Current Unix timestamp (seconds)
|
|
169
|
+
- `{{$uuid}}` - UUID v4
|
|
170
|
+
- `{{$guid}}` - GUID (alias for uuid)
|
|
171
|
+
- `{{$randomString}}` - 10-char alphanumeric string
|
|
172
|
+
- `{{$randomHexadecimal}}` - Random hex string
|
|
173
|
+
- `{{$isoTimestamp}}` - ISO 8601 timestamp
|
|
174
|
+
|
|
144
175
|
### Script Execution
|
|
145
176
|
|
|
146
|
-
Run pre-request and post-response scripts:
|
|
177
|
+
Run pre-request and post-response scripts with full Postman API compatibility:
|
|
147
178
|
|
|
148
|
-
**Pre-request script:**
|
|
179
|
+
**Pre-request script - Set variables & modify request:**
|
|
149
180
|
```javascript
|
|
150
|
-
// Set variables
|
|
151
|
-
|
|
152
|
-
|
|
181
|
+
// Set variables across scopes
|
|
182
|
+
pm.variables.set('requestId', pm.variables.randomUUID());
|
|
183
|
+
pm.environment.set('token', 'abc-123');
|
|
184
|
+
pm.collectionVariables.set('counter', '1');
|
|
185
|
+
|
|
186
|
+
// Modify request headers
|
|
187
|
+
pm.request.headers.add({
|
|
188
|
+
name: 'X-Request-ID',
|
|
189
|
+
value: pm.variables.get('requestId')
|
|
190
|
+
});
|
|
191
|
+
pm.request.headers.update({
|
|
192
|
+
name: 'Authorization',
|
|
193
|
+
value: 'Bearer ' + pm.environment.get('token')
|
|
194
|
+
});
|
|
153
195
|
|
|
154
|
-
// Modify
|
|
155
|
-
|
|
196
|
+
// Modify URL and body
|
|
197
|
+
pm.request.url = 'https://api.example.com' + pm.request.url;
|
|
198
|
+
pm.request.body.raw = JSON.stringify({ timestamp: Date.now() });
|
|
199
|
+
|
|
200
|
+
// Set cookies for next request
|
|
201
|
+
pm.cookies.set('sessionId', 'sess_abc123');
|
|
156
202
|
```
|
|
157
203
|
|
|
158
|
-
**Post-response script:**
|
|
204
|
+
**Post-response script - Test & extract data:**
|
|
159
205
|
```javascript
|
|
160
|
-
// Run
|
|
161
|
-
|
|
162
|
-
|
|
206
|
+
// Run assertions
|
|
207
|
+
pm.test('Status is 200', () => {
|
|
208
|
+
pm.expect(pm.response.code).to.equal(200);
|
|
209
|
+
});
|
|
210
|
+
pm.test('Response time under 1s', () => {
|
|
211
|
+
pm.expect(pm.response.responseTime).to.be.below(1000);
|
|
212
|
+
});
|
|
163
213
|
|
|
164
|
-
// Extract data
|
|
165
|
-
const
|
|
166
|
-
|
|
214
|
+
// Extract data for next request
|
|
215
|
+
const data = pm.response.json();
|
|
216
|
+
pm.environment.set('userId', data.id);
|
|
217
|
+
pm.environment.set('authToken', data.token);
|
|
218
|
+
|
|
219
|
+
// Store cookies from response
|
|
220
|
+
if (pm.response.headers.has('Set-Cookie')) {
|
|
221
|
+
pm.cookies.set('authCookie', data.authCookie);
|
|
222
|
+
}
|
|
167
223
|
```
|
|
168
224
|
|
|
169
225
|
### Environment Management
|
|
@@ -240,21 +296,49 @@ const forge = new ForgeContainer({
|
|
|
240
296
|
});
|
|
241
297
|
```
|
|
242
298
|
|
|
243
|
-
### Cookie Management
|
|
299
|
+
### Cookie Management & Persistence
|
|
244
300
|
|
|
245
|
-
Automatic cookie
|
|
301
|
+
Automatic cookie storage and reuse across multi-request flows:
|
|
246
302
|
|
|
247
303
|
```typescript
|
|
248
304
|
const forge = new ForgeContainer({
|
|
249
|
-
enableCookies: true
|
|
305
|
+
enableCookies: true // Cookies persist across requests in session
|
|
250
306
|
});
|
|
251
307
|
|
|
252
|
-
//
|
|
308
|
+
// Login - response sets Session-ID cookie
|
|
253
309
|
const loginResult = await forge.execute(loginRequest, collection);
|
|
254
|
-
|
|
310
|
+
|
|
311
|
+
// Subsequent requests automatically include Session-ID
|
|
312
|
+
// No need to manually extract and re-add cookies
|
|
255
313
|
const dataResult = await forge.execute(dataRequest, collection);
|
|
314
|
+
const updateResult = await forge.execute(updateRequest, collection);
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**Access cookies in scripts:**
|
|
318
|
+
```javascript
|
|
319
|
+
// Pre-request script - read stored cookies
|
|
320
|
+
if (pm.cookies.has('sessionId')) {
|
|
321
|
+
const sid = pm.cookies.get('sessionId');
|
|
322
|
+
pm.request.headers.add({
|
|
323
|
+
name: 'Cookie',
|
|
324
|
+
value: 'sessionId=' + sid
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Post-response script - store new cookies
|
|
329
|
+
pm.response.cookies.forEach(cookie => {
|
|
330
|
+
pm.cookies.set(cookie.name, cookie.value);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// List all active cookies
|
|
334
|
+
const allCookies = pm.cookies.list(); // [{name, value}, ...]
|
|
335
|
+
|
|
336
|
+
// Clear cookies
|
|
337
|
+
pm.cookies.clear(); // When switching users/sessions
|
|
256
338
|
```
|
|
257
339
|
|
|
340
|
+
Cookies are automatically extracted from `Set-Cookie` response headers and reused in subsequent `Cookie` request headers.
|
|
341
|
+
|
|
258
342
|
### Request History
|
|
259
343
|
|
|
260
344
|
Track all executed requests:
|
|
@@ -450,6 +534,72 @@ class ApiTestRunner {
|
|
|
450
534
|
}
|
|
451
535
|
```
|
|
452
536
|
|
|
537
|
+
### Multi-Request Workflows
|
|
538
|
+
|
|
539
|
+
Execute dependent request chains with automatic cookie and variable management:
|
|
540
|
+
|
|
541
|
+
```typescript
|
|
542
|
+
import { ForgeContainer } from '@http-forge/core';
|
|
543
|
+
|
|
544
|
+
async function apiAuthWorkflow() {
|
|
545
|
+
const forge = new ForgeContainer({
|
|
546
|
+
enableCookies: true, // Cookies persist across requests
|
|
547
|
+
enableHistory: true
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// Request 1: Login (sets session cookie)
|
|
551
|
+
const loginReq = {
|
|
552
|
+
name: 'Login',
|
|
553
|
+
method: 'POST',
|
|
554
|
+
url: 'https://api.example.com/auth/login',
|
|
555
|
+
body: { type: 'raw', content: JSON.stringify({
|
|
556
|
+
username: '{{email}}',
|
|
557
|
+
password: '{{password}}'
|
|
558
|
+
})},
|
|
559
|
+
scripts: {
|
|
560
|
+
postResponse: `
|
|
561
|
+
const { token, userId } = pm.response.json();
|
|
562
|
+
pm.environment.set('authToken', token);
|
|
563
|
+
pm.environment.set('userId', userId);
|
|
564
|
+
`
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
const loginResult = await forge.execute(loginReq, collection);
|
|
569
|
+
console.log('✓ Logged in, token:', forge.getResolvedEnvironment()['authToken']);
|
|
570
|
+
|
|
571
|
+
// Request 2: Fetch user profile (uses session cookie automatically)
|
|
572
|
+
const profileReq = {
|
|
573
|
+
name: 'Get Profile',
|
|
574
|
+
method: 'GET',
|
|
575
|
+
url: 'https://api.example.com/users/{{userId}}',
|
|
576
|
+
headers: {
|
|
577
|
+
'Authorization': 'Bearer {{authToken}}' // Uses token from login
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
const profileResult = await forge.execute(profileReq, collection);
|
|
582
|
+
console.log('✓ Profile:', profileResult.response.body);
|
|
583
|
+
|
|
584
|
+
// Request 3: Update profile (session cookie still active)
|
|
585
|
+
const updateReq = {
|
|
586
|
+
name: 'Update Profile',
|
|
587
|
+
method: 'PATCH',
|
|
588
|
+
url: 'https://api.example.com/users/{{userId}}',
|
|
589
|
+
headers: {
|
|
590
|
+
'Authorization': 'Bearer {{authToken}}'
|
|
591
|
+
},
|
|
592
|
+
body: { type: 'raw', content: JSON.stringify({ status: 'active' })}
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
const updateResult = await forge.execute(updateReq, collection);
|
|
596
|
+
console.log('✓ Updated profile');
|
|
597
|
+
|
|
598
|
+
// Session automatically logged out - cookies cleared
|
|
599
|
+
forge.getRequestHistory().clear();
|
|
600
|
+
}
|
|
601
|
+
```
|
|
602
|
+
|
|
453
603
|
## 📦 Storage Formats
|
|
454
604
|
|
|
455
605
|
### File Format (Single JSON)
|
|
@@ -487,14 +637,38 @@ MIT © Henry Huang
|
|
|
487
637
|
|
|
488
638
|
## 📝 Changelog
|
|
489
639
|
|
|
640
|
+
### 0.2.0 (Postman API Parity)
|
|
641
|
+
|
|
642
|
+
- ✅ **Async `pm.test()` support** - Tests with async callbacks are properly awaited
|
|
643
|
+
- ✅ **Expect chain assertions** - `.a(type)`, `.an(type)`, `.deep.equal()`, `.lengthOf()`, `.exist`, `.members()`, `.keys()`, `.string()`
|
|
644
|
+
- ✅ **`pm.iterationData`** scope - Data-driven testing with iteration variables
|
|
645
|
+
- ✅ **Response status getters** - `response.to.be.ok`, `.error`, `.clientError`, `.serverError` work as getters and functions
|
|
646
|
+
- ✅ **Response headers HeaderList** - `.get()`, `.has()`, `.toObject()`, `.each()` on `pm.response.headers`
|
|
647
|
+
- ✅ **`pm.cookies.toObject()`** - Flat `{name: value}` cookie map
|
|
648
|
+
- ✅ **`replaceIn()` on all scopes** - Available on `pm.variables`, `pm.environment`, `pm.collectionVariables`, `pm.globals`
|
|
649
|
+
- ✅ **Sandbox globals** - `xml2Json()`, `jsonStringify()`, `jsonParse()` available in scripts
|
|
650
|
+
- ✅ **Request headers API** - `.toObject()`, `.each()` on `pm.request.headers`
|
|
651
|
+
- ✅ **`pm.execution.setNextRequest()`** - Runner flow control (jump to named request or stop with `null`)
|
|
652
|
+
- ✅ **`pm.execution.skipRequest()`** - Skip HTTP call from pre-request scripts
|
|
653
|
+
- ✅ **`pm.setNextRequest()`** - Top-level alias for flow control
|
|
654
|
+
- ✅ **`pm.visualizer.set(template, data)`** - Custom HTML visualization with Handlebars templates
|
|
655
|
+
- ✅ **`pm.request.url` as Url object** - Postman SDK-compatible Url with `getHost()`, `getPath()`, `getQueryString()`, `protocol`, `host`, `port`, `path`, `query`, `hash`
|
|
656
|
+
- ✅ **Full CryptoJS** - AES/DES/TripleDES encrypt/decrypt, PBKDF2, all hash algorithms (SHA1/256/384/512/SHA3/MD5/RIPEMD160), HMAC, encoding helpers (Hex/Base64/Utf8/Latin1/Utf16)
|
|
657
|
+
|
|
490
658
|
### 0.1.0 (Initial Release)
|
|
491
659
|
|
|
492
|
-
- ✅ Core request execution
|
|
493
|
-
- ✅
|
|
494
|
-
- ✅
|
|
495
|
-
- ✅
|
|
496
|
-
- ✅
|
|
497
|
-
- ✅
|
|
498
|
-
- ✅
|
|
499
|
-
- ✅
|
|
500
|
-
- ✅
|
|
660
|
+
- ✅ **Core request execution** with Postman collection support
|
|
661
|
+
- ✅ **Dynamic variables** - 7 generators for on-the-fly value generation
|
|
662
|
+
- ✅ **Postman-compatible scripting** - `pm.*` API with full feature parity
|
|
663
|
+
- ✅ **Variable scoping** - globals, collection, environment, session, flow-level
|
|
664
|
+
- ✅ **Cookie persistence** - automatic storage and reuse across request chains
|
|
665
|
+
- ✅ **Pre-request & post-response scripts** with shared VM context
|
|
666
|
+
- ✅ **Test assertions** with BDD-style `pm.test()` and expect chains
|
|
667
|
+
- ✅ **Environment management** with variable substitution
|
|
668
|
+
- ✅ **Cookie jar** with domain-aware matching
|
|
669
|
+
- ✅ **Request/response interceptors** for customization
|
|
670
|
+
- ✅ **Request history** tracking
|
|
671
|
+
- ✅ **File and folder collection formats** (Postman-compatible)
|
|
672
|
+
- ✅ **Full TypeScript support** with comprehensive types
|
|
673
|
+
- ✅ **Dependency injection** for extensibility
|
|
674
|
+
- ✅ **Comprehensive test coverage**
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth2 Token Manager Interface
|
|
3
|
+
*
|
|
4
|
+
* Platform-independent interface for OAuth2 token management.
|
|
5
|
+
* Handles all grant types: client_credentials, password, authorization_code, implicit.
|
|
6
|
+
*
|
|
7
|
+
* VS Code impl: uses vscode.env.openExternal, vscode.SecretStorage
|
|
8
|
+
* CLI/Standalone impl: uses system browser + local HTTP callback server
|
|
9
|
+
* Browser impl: uses window.open() + postMessage
|
|
10
|
+
*/
|
|
11
|
+
import { OAuth2Config } from '../types/types';
|
|
12
|
+
export interface TokenInfo {
|
|
13
|
+
accessToken: string;
|
|
14
|
+
/** e.g. "Bearer" */
|
|
15
|
+
tokenType: string;
|
|
16
|
+
/** Unix timestamp (ms) */
|
|
17
|
+
expiresAt?: number;
|
|
18
|
+
refreshToken?: string;
|
|
19
|
+
scope?: string;
|
|
20
|
+
/** Original token response */
|
|
21
|
+
raw: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
export interface TokenCacheKey {
|
|
24
|
+
tokenUrl: string;
|
|
25
|
+
clientId: string;
|
|
26
|
+
scope?: string;
|
|
27
|
+
grantType: string;
|
|
28
|
+
}
|
|
29
|
+
export interface IOAuth2TokenManager {
|
|
30
|
+
/**
|
|
31
|
+
* Get a valid access token for the given OAuth2 configuration.
|
|
32
|
+
* May return a cached token, refresh an expired token, or initiate a new flow.
|
|
33
|
+
*/
|
|
34
|
+
getToken(config: OAuth2Config, environment: string): Promise<TokenInfo>;
|
|
35
|
+
/**
|
|
36
|
+
* Refresh an expired token using the refresh token.
|
|
37
|
+
*/
|
|
38
|
+
refreshToken(config: OAuth2Config, currentRefreshToken: string, environment: string): Promise<TokenInfo>;
|
|
39
|
+
/**
|
|
40
|
+
* Initiate the authorization code flow (opens browser, waits for callback).
|
|
41
|
+
*/
|
|
42
|
+
authorizationCodeFlow(config: OAuth2Config, environment: string): Promise<TokenInfo>;
|
|
43
|
+
/**
|
|
44
|
+
* Initiate the implicit flow (opens browser, waits for callback).
|
|
45
|
+
*/
|
|
46
|
+
implicitFlow(config: OAuth2Config, environment: string): Promise<TokenInfo>;
|
|
47
|
+
/**
|
|
48
|
+
* Clear a cached token.
|
|
49
|
+
*/
|
|
50
|
+
clearToken(cacheKey: TokenCacheKey): void;
|
|
51
|
+
/**
|
|
52
|
+
* Clear all cached tokens.
|
|
53
|
+
*/
|
|
54
|
+
clearAllTokens(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Handle authorization code callback (from browser redirect).
|
|
57
|
+
*/
|
|
58
|
+
handleAuthorizationCallback(code: string | undefined, state: string | undefined, error?: string): void;
|
|
59
|
+
/**
|
|
60
|
+
* Handle implicit flow callback (from browser redirect with token in fragment).
|
|
61
|
+
*/
|
|
62
|
+
handleImplicitCallback(accessToken: string, tokenType: string | undefined, expiresIn: number | undefined, state: string | undefined): void;
|
|
63
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth2 Token Manager
|
|
3
|
+
*
|
|
4
|
+
* Platform-independent implementation of OAuth2 token lifecycle management:
|
|
5
|
+
* - Token caching with expiration
|
|
6
|
+
* - PKCE (S256 / plain) challenge generation
|
|
7
|
+
* - Authorization Code flow via system browser + URI handler callback
|
|
8
|
+
* - Implicit flow via system browser
|
|
9
|
+
* - Client Credentials and Password grant token fetching
|
|
10
|
+
* - Token refresh with automatic retry
|
|
11
|
+
* - Secure storage for refresh tokens via ISecretStore
|
|
12
|
+
*
|
|
13
|
+
* Platform-specific behaviour (browser opening, secret storage, callback URIs)
|
|
14
|
+
* is injected via IExternalBrowserService and ISecretStore interfaces.
|
|
15
|
+
*/
|
|
16
|
+
import { IEnvironmentConfigService } from '../environment/interfaces';
|
|
17
|
+
import { IHttpRequestService } from '../http/interfaces';
|
|
18
|
+
import { IExternalBrowserService, ISecretStore } from '../types/platform';
|
|
19
|
+
import { OAuth2Config } from '../types/types';
|
|
20
|
+
import { IOAuth2TokenManager, TokenCacheKey, TokenInfo } from './interfaces';
|
|
21
|
+
export declare class OAuth2TokenManager implements IOAuth2TokenManager {
|
|
22
|
+
private readonly secretStore;
|
|
23
|
+
private readonly browserService;
|
|
24
|
+
private readonly envConfigService;
|
|
25
|
+
private readonly httpService;
|
|
26
|
+
private readonly callbackPath;
|
|
27
|
+
private tokenCache;
|
|
28
|
+
/**
|
|
29
|
+
* Pending authorization-code resolve/reject.
|
|
30
|
+
* Set when we launch the browser; resolved when the URI handler fires.
|
|
31
|
+
*/
|
|
32
|
+
private pendingAuthCallback;
|
|
33
|
+
/**
|
|
34
|
+
* Pending implicit-flow resolve/reject.
|
|
35
|
+
*/
|
|
36
|
+
private pendingImplicitCallback;
|
|
37
|
+
/**
|
|
38
|
+
* @param secretStore Secure storage for refresh tokens (wraps vscode.SecretStorage, keytar, etc.)
|
|
39
|
+
* @param browserService External browser interaction (wraps vscode.env.openExternal, etc.)
|
|
40
|
+
* @param envConfigService Environment variable resolution
|
|
41
|
+
* @param httpService HTTP client for token endpoint calls
|
|
42
|
+
* @param callbackPath Platform-specific callback path (e.g. "henry-huang.http-forge/oauth2/callback")
|
|
43
|
+
*/
|
|
44
|
+
constructor(secretStore: ISecretStore, browserService: IExternalBrowserService, envConfigService: IEnvironmentConfigService, httpService: IHttpRequestService, callbackPath?: string);
|
|
45
|
+
/**
|
|
46
|
+
* Get a valid token — uses cache, refresh, or full re-auth as needed.
|
|
47
|
+
*/
|
|
48
|
+
getToken(config: OAuth2Config, environment: string): Promise<TokenInfo>;
|
|
49
|
+
/**
|
|
50
|
+
* Refresh an existing token.
|
|
51
|
+
*/
|
|
52
|
+
refreshToken(config: OAuth2Config, currentRefreshToken: string, environment: string): Promise<TokenInfo>;
|
|
53
|
+
/**
|
|
54
|
+
* Authorization Code + PKCE flow.
|
|
55
|
+
*
|
|
56
|
+
* 1. Generate code_verifier & code_challenge (S256)
|
|
57
|
+
* 2. Open system browser with authorize URL
|
|
58
|
+
* 3. Wait for URI handler callback with ?code=xxx&state=yyy
|
|
59
|
+
* 4. Exchange code + code_verifier for token
|
|
60
|
+
*/
|
|
61
|
+
authorizationCodeFlow(config: OAuth2Config, environment: string): Promise<TokenInfo>;
|
|
62
|
+
/**
|
|
63
|
+
* Implicit flow — opens browser, waits for hash fragment with access_token.
|
|
64
|
+
* Since the URI handler receives query params (not fragments), a small
|
|
65
|
+
* redirect page is needed to convert fragment → query. We handle both cases.
|
|
66
|
+
*/
|
|
67
|
+
implicitFlow(config: OAuth2Config, environment: string): Promise<TokenInfo>;
|
|
68
|
+
/**
|
|
69
|
+
* Called by the URI handler when browser redirects back.
|
|
70
|
+
* Handles both authorization_code (?code=) and implicit (?access_token=) callbacks.
|
|
71
|
+
*/
|
|
72
|
+
handleAuthorizationCallback(code: string | undefined, state: string | undefined, error?: string): void;
|
|
73
|
+
/**
|
|
74
|
+
* Handle implicit callback with access_token directly.
|
|
75
|
+
*/
|
|
76
|
+
handleImplicitCallback(accessToken: string, tokenType: string | undefined, expiresIn: number | undefined, state: string | undefined): void;
|
|
77
|
+
clearToken(cacheKey: TokenCacheKey): void;
|
|
78
|
+
clearAllTokens(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Fetch token using client_credentials or password grant.
|
|
81
|
+
*/
|
|
82
|
+
private fetchToken;
|
|
83
|
+
/**
|
|
84
|
+
* Apply client authentication to the request (body or Basic Auth header).
|
|
85
|
+
*/
|
|
86
|
+
private applyClientAuth;
|
|
87
|
+
/**
|
|
88
|
+
* Parse token endpoint response body into TokenInfo.
|
|
89
|
+
*/
|
|
90
|
+
private parseTokenResponse;
|
|
91
|
+
private isExpired;
|
|
92
|
+
private buildCacheKeyString;
|
|
93
|
+
/**
|
|
94
|
+
* Get the callback URI for OAuth2 redirects.
|
|
95
|
+
* Uses the browser service to create a valid, externally accessible callback URI.
|
|
96
|
+
*/
|
|
97
|
+
private getCallbackUri;
|
|
98
|
+
/** Resolve environment variables in a string */
|
|
99
|
+
private resolve;
|
|
100
|
+
private generateCodeVerifier;
|
|
101
|
+
private generateCodeChallengeS256;
|
|
102
|
+
private storeRefreshToken;
|
|
103
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection Loader Factory
|
|
3
|
+
*
|
|
4
|
+
* Factory Pattern: Creates appropriate loader based on configuration
|
|
5
|
+
*/
|
|
6
|
+
import { IConfigService } from '../config';
|
|
7
|
+
import { ICollectionStore } from './collection-store';
|
|
8
|
+
/**
|
|
9
|
+
* Factory for creating collection loaders
|
|
10
|
+
*/
|
|
11
|
+
export declare class CollectionLoaderFactory {
|
|
12
|
+
/**
|
|
13
|
+
* Create a collection loader based on configuration
|
|
14
|
+
*/
|
|
15
|
+
static create(configService: IConfigService): ICollectionStore;
|
|
16
|
+
/**
|
|
17
|
+
* Create a loader for a specific format
|
|
18
|
+
*/
|
|
19
|
+
static createForFormat(format: 'folder' | 'json', collectionsPath: string): ICollectionStore;
|
|
20
|
+
}
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Single Responsibility: Load collections from file system
|
|
5
5
|
* Dependency Inversion: Uses IFileSystem and ParserRegistry abstractions
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import { UnifiedCollection } from '../
|
|
7
|
+
import { IFileSystem } from '../types/platform';
|
|
8
|
+
import { UnifiedCollection } from '../types/types';
|
|
9
|
+
import { ICollectionLoader } from './interfaces';
|
|
9
10
|
import { ParserRegistry } from './parser-registry';
|
|
10
11
|
/**
|
|
11
12
|
* Options for loading a collection
|
|
@@ -49,4 +50,3 @@ export declare class CollectionLoader implements ICollectionLoader {
|
|
|
49
50
|
*/
|
|
50
51
|
getSupportedFormats(): string[];
|
|
51
52
|
}
|
|
52
|
-
//# sourceMappingURL=collection-loader.d.ts.map
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection Service Interface
|
|
3
|
+
*
|
|
4
|
+
* Interface Segregation: Separated into focused sub-interfaces
|
|
5
|
+
* Dependency Inversion: Consumers depend on this abstraction
|
|
6
|
+
*/
|
|
7
|
+
import { CollectionRequest, KeyValueEntry, RequestAuth, RequestScripts, RequestSettings } from '../types/types';
|
|
8
|
+
export type { CollectionRequest, KeyValueEntry, RequestAuth, RequestScripts, RequestSettings };
|
|
9
|
+
/**
|
|
10
|
+
* Request item in a collection - CollectionRequest with type discriminator
|
|
11
|
+
*/
|
|
12
|
+
export type CollectionRequestItem = CollectionRequest & {
|
|
13
|
+
type: 'request';
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Folder item in a collection
|
|
17
|
+
*/
|
|
18
|
+
export interface CollectionFolderItem {
|
|
19
|
+
type: 'folder';
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
auth?: RequestAuth;
|
|
24
|
+
scripts?: RequestScripts;
|
|
25
|
+
items?: CollectionItem[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Collection item - discriminated union of request or folder
|
|
29
|
+
*/
|
|
30
|
+
export type CollectionItem = CollectionRequestItem | CollectionFolderItem;
|
|
31
|
+
/**
|
|
32
|
+
* Collection definition
|
|
33
|
+
*/
|
|
34
|
+
export interface Collection {
|
|
35
|
+
id: string;
|
|
36
|
+
name: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
version?: string;
|
|
39
|
+
variables?: Record<string, string>;
|
|
40
|
+
auth?: RequestAuth;
|
|
41
|
+
scripts?: RequestScripts;
|
|
42
|
+
items: CollectionItem[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Options for creating a request in a collection
|
|
46
|
+
*/
|
|
47
|
+
export type CreateRequestOptions = Omit<CollectionRequest, 'id'> & {
|
|
48
|
+
collectionId: string;
|
|
49
|
+
parentId?: string;
|
|
50
|
+
id?: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Options for creating a folder
|
|
54
|
+
*/
|
|
55
|
+
export interface CreateFolderOptions {
|
|
56
|
+
name: string;
|
|
57
|
+
collectionId: string;
|
|
58
|
+
parentId?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Read-only collection operations
|
|
62
|
+
*/
|
|
63
|
+
export interface ICollectionReader {
|
|
64
|
+
getAllCollections(): Collection[];
|
|
65
|
+
getCollection(id: string): Collection | undefined;
|
|
66
|
+
getCollectionById(id: string): Collection | undefined;
|
|
67
|
+
getCollectionByName(name: string): Collection | undefined;
|
|
68
|
+
findRequest(collectionId: string, requestId: string): CollectionRequestItem | undefined;
|
|
69
|
+
getCollectionVariables(collectionId: string): Record<string, string>;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Collection modification operations
|
|
73
|
+
*/
|
|
74
|
+
export interface ICollectionWriter {
|
|
75
|
+
createCollection(name: string): Promise<Collection>;
|
|
76
|
+
saveCollection(collection: Collection): Promise<void>;
|
|
77
|
+
deleteCollection(id: string): Promise<boolean>;
|
|
78
|
+
renameCollection(id: string, newName: string): Promise<boolean>;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Request management operations
|
|
82
|
+
*/
|
|
83
|
+
export interface IRequestManager {
|
|
84
|
+
createRequest(options: CreateRequestOptions): Promise<CollectionItem>;
|
|
85
|
+
updateRequest(collectionId: string, requestId: string, updates: Partial<CollectionItem>): Promise<boolean>;
|
|
86
|
+
deleteRequest(collectionId: string, requestId: string): Promise<boolean>;
|
|
87
|
+
renameRequest(collectionId: string, requestId: string, newName: string): Promise<boolean>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Folder management operations
|
|
91
|
+
*/
|
|
92
|
+
export interface IFolderManager {
|
|
93
|
+
createFolder(options: CreateFolderOptions): Promise<CollectionItem>;
|
|
94
|
+
deleteFolder(collectionId: string, folderId: string): Promise<boolean>;
|
|
95
|
+
renameFolder(collectionId: string, folderId: string, newName: string): Promise<boolean>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Collection variable management
|
|
99
|
+
*/
|
|
100
|
+
export interface ICollectionVariableManager {
|
|
101
|
+
setCollectionVariable(collectionId: string, key: string, value: unknown): void;
|
|
102
|
+
deleteCollectionVariable(collectionId: string, key: string): void;
|
|
103
|
+
clearCollectionVariables(collectionId: string): void;
|
|
104
|
+
getCollectionVariableLocals(collectionId: string): Record<string, string>;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Collection import/export operations
|
|
108
|
+
*/
|
|
109
|
+
export interface ICollectionImportExport {
|
|
110
|
+
importCollection(filePath: string): Promise<Collection>;
|
|
111
|
+
exportCollection(id: string, filePath: string): Promise<void>;
|
|
112
|
+
exportCollectionAsRestClientFolder(id: string, outDir: string): Promise<void>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Full collection service interface
|
|
116
|
+
*/
|
|
117
|
+
export interface ICollectionService extends ICollectionReader, ICollectionWriter, IRequestManager, IFolderManager, ICollectionVariableManager, ICollectionImportExport {
|
|
118
|
+
dispose(): void;
|
|
119
|
+
}
|