@http-forge/core 0.1.0 → 0.2.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 +194 -41
- 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 +256 -0
- package/dist/collection/folder-collection-store.d.ts +168 -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 +22 -14
- 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/environment/environment-config-service.d.ts +95 -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 +88 -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 +149 -0
- package/dist/script/module-loader.d.ts +115 -0
- package/dist/script/request-script-session.d.ts +70 -0
- package/dist/script/script-executor.d.ts +60 -0
- package/dist/script/script-factories.d.ts +83 -0
- package/dist/script/script-utils.d.ts +41 -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 +282 -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 +11 -3
- 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,29 @@
|
|
|
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
|
|
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
11
|
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
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 `pm.*` API (events, variables, assertions)
|
|
15
|
+
- 🔄 **Dynamic Variables** - Built-in generators: `{{$randomInt}}`, `{{$timestamp}}`, `{{$uuid}}`, `{{$guid}}`, etc.
|
|
16
|
+
- 🌍 **Environments** - Full variable scoping (globals, collection, environment, session, flow-level)
|
|
17
|
+
- 🍪 **Cookie Persistence** - Automatic cookie storage and reuse across request sequences
|
|
18
|
+
- 📊 **Test Assertions** - BDD-style testing with `pm.test()` and `expect()` chains
|
|
19
|
+
- 🔌 **Extensible** - Custom interceptors, HTTP clients, and module loaders
|
|
18
20
|
|
|
19
|
-
**
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
21
|
+
**Ideal for:**
|
|
22
|
+
- CI/CD pipeline integration (GitHub Actions, GitLab CI, Jenkins)
|
|
23
|
+
- Headless API testing and contract validation
|
|
24
|
+
- Building custom API testing CLIs
|
|
25
|
+
- Load testing and performance monitoring
|
|
26
|
+
- Automated integration test suites
|
|
24
27
|
|
|
25
28
|
## 🎯 Installation
|
|
26
29
|
|
|
@@ -141,29 +144,79 @@ console.log(result.preRequestResult); // Pre-request script output
|
|
|
141
144
|
console.log(result.postResponseResult); // Test results
|
|
142
145
|
```
|
|
143
146
|
|
|
147
|
+
### Dynamic Variables
|
|
148
|
+
|
|
149
|
+
Automatic variable generation within request templates:
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
// URL with dynamic timestamp
|
|
153
|
+
https://api.example.com/events?timestamp={{$timestamp}}
|
|
154
|
+
|
|
155
|
+
// Headers with unique ID
|
|
156
|
+
X-Request-ID: {{$uuid}}
|
|
157
|
+
|
|
158
|
+
// Query parameters with random value
|
|
159
|
+
?page=1&seed={{$randomInt:1:100}}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Supported dynamic variables:**
|
|
163
|
+
- `{{$randomInt}}` - Random integer (0-2147483647)
|
|
164
|
+
- `{{$randomInt:min:max}}` - Random integer in range
|
|
165
|
+
- `{{$timestamp}}` - Current Unix timestamp (seconds)
|
|
166
|
+
- `{{$uuid}}` - UUID v4
|
|
167
|
+
- `{{$guid}}` - GUID (alias for uuid)
|
|
168
|
+
- `{{$randomString}}` - 10-char alphanumeric string
|
|
169
|
+
- `{{$randomHexadecimal}}` - Random hex string
|
|
170
|
+
- `{{$isoTimestamp}}` - ISO 8601 timestamp
|
|
171
|
+
|
|
144
172
|
### Script Execution
|
|
145
173
|
|
|
146
|
-
Run pre-request and post-response scripts:
|
|
174
|
+
Run pre-request and post-response scripts with full Postman API compatibility:
|
|
147
175
|
|
|
148
|
-
**Pre-request script:**
|
|
176
|
+
**Pre-request script - Set variables & modify request:**
|
|
149
177
|
```javascript
|
|
150
|
-
// Set variables
|
|
151
|
-
|
|
152
|
-
|
|
178
|
+
// Set variables across scopes
|
|
179
|
+
pm.variables.set('requestId', pm.variables.randomUUID());
|
|
180
|
+
pm.environment.set('token', 'abc-123');
|
|
181
|
+
pm.collectionVariables.set('counter', '1');
|
|
182
|
+
|
|
183
|
+
// Modify request headers
|
|
184
|
+
pm.request.headers.add({
|
|
185
|
+
name: 'X-Request-ID',
|
|
186
|
+
value: pm.variables.get('requestId')
|
|
187
|
+
});
|
|
188
|
+
pm.request.headers.update({
|
|
189
|
+
name: 'Authorization',
|
|
190
|
+
value: 'Bearer ' + pm.environment.get('token')
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Modify URL and body
|
|
194
|
+
pm.request.url = 'https://api.example.com' + pm.request.url;
|
|
195
|
+
pm.request.body.raw = JSON.stringify({ timestamp: Date.now() });
|
|
153
196
|
|
|
154
|
-
//
|
|
155
|
-
|
|
197
|
+
// Set cookies for next request
|
|
198
|
+
pm.cookies.set('sessionId', 'sess_abc123');
|
|
156
199
|
```
|
|
157
200
|
|
|
158
|
-
**Post-response script:**
|
|
201
|
+
**Post-response script - Test & extract data:**
|
|
159
202
|
```javascript
|
|
160
|
-
// Run
|
|
161
|
-
|
|
162
|
-
|
|
203
|
+
// Run assertions
|
|
204
|
+
pm.test('Status is 200', () => {
|
|
205
|
+
pm.expect(pm.response.code).to.equal(200);
|
|
206
|
+
});
|
|
207
|
+
pm.test('Response time under 1s', () => {
|
|
208
|
+
pm.expect(pm.response.responseTime).to.be.below(1000);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Extract data for next request
|
|
212
|
+
const data = pm.response.json();
|
|
213
|
+
pm.environment.set('userId', data.id);
|
|
214
|
+
pm.environment.set('authToken', data.token);
|
|
163
215
|
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
216
|
+
// Store cookies from response
|
|
217
|
+
if (pm.response.headers.has('Set-Cookie')) {
|
|
218
|
+
pm.cookies.set('authCookie', data.authCookie);
|
|
219
|
+
}
|
|
167
220
|
```
|
|
168
221
|
|
|
169
222
|
### Environment Management
|
|
@@ -240,21 +293,49 @@ const forge = new ForgeContainer({
|
|
|
240
293
|
});
|
|
241
294
|
```
|
|
242
295
|
|
|
243
|
-
### Cookie Management
|
|
296
|
+
### Cookie Management & Persistence
|
|
244
297
|
|
|
245
|
-
Automatic cookie
|
|
298
|
+
Automatic cookie storage and reuse across multi-request flows:
|
|
246
299
|
|
|
247
300
|
```typescript
|
|
248
301
|
const forge = new ForgeContainer({
|
|
249
|
-
enableCookies: true
|
|
302
|
+
enableCookies: true // Cookies persist across requests in session
|
|
250
303
|
});
|
|
251
304
|
|
|
252
|
-
//
|
|
305
|
+
// Login - response sets Session-ID cookie
|
|
253
306
|
const loginResult = await forge.execute(loginRequest, collection);
|
|
254
|
-
|
|
307
|
+
|
|
308
|
+
// Subsequent requests automatically include Session-ID
|
|
309
|
+
// No need to manually extract and re-add cookies
|
|
255
310
|
const dataResult = await forge.execute(dataRequest, collection);
|
|
311
|
+
const updateResult = await forge.execute(updateRequest, collection);
|
|
256
312
|
```
|
|
257
313
|
|
|
314
|
+
**Access cookies in scripts:**
|
|
315
|
+
```javascript
|
|
316
|
+
// Pre-request script - read stored cookies
|
|
317
|
+
if (pm.cookies.has('sessionId')) {
|
|
318
|
+
const sid = pm.cookies.get('sessionId');
|
|
319
|
+
pm.request.headers.add({
|
|
320
|
+
name: 'Cookie',
|
|
321
|
+
value: 'sessionId=' + sid
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Post-response script - store new cookies
|
|
326
|
+
pm.response.cookies.forEach(cookie => {
|
|
327
|
+
pm.cookies.set(cookie.name, cookie.value);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// List all active cookies
|
|
331
|
+
const allCookies = pm.cookies.list(); // [{name, value}, ...]
|
|
332
|
+
|
|
333
|
+
// Clear cookies
|
|
334
|
+
pm.cookies.clear(); // When switching users/sessions
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Cookies are automatically extracted from `Set-Cookie` response headers and reused in subsequent `Cookie` request headers.
|
|
338
|
+
|
|
258
339
|
### Request History
|
|
259
340
|
|
|
260
341
|
Track all executed requests:
|
|
@@ -450,6 +531,72 @@ class ApiTestRunner {
|
|
|
450
531
|
}
|
|
451
532
|
```
|
|
452
533
|
|
|
534
|
+
### Multi-Request Workflows
|
|
535
|
+
|
|
536
|
+
Execute dependent request chains with automatic cookie and variable management:
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
import { ForgeContainer } from '@http-forge/core';
|
|
540
|
+
|
|
541
|
+
async function apiAuthWorkflow() {
|
|
542
|
+
const forge = new ForgeContainer({
|
|
543
|
+
enableCookies: true, // Cookies persist across requests
|
|
544
|
+
enableHistory: true
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
// Request 1: Login (sets session cookie)
|
|
548
|
+
const loginReq = {
|
|
549
|
+
name: 'Login',
|
|
550
|
+
method: 'POST',
|
|
551
|
+
url: 'https://api.example.com/auth/login',
|
|
552
|
+
body: { type: 'raw', content: JSON.stringify({
|
|
553
|
+
username: '{{email}}',
|
|
554
|
+
password: '{{password}}'
|
|
555
|
+
})},
|
|
556
|
+
scripts: {
|
|
557
|
+
postResponse: `
|
|
558
|
+
const { token, userId } = pm.response.json();
|
|
559
|
+
pm.environment.set('authToken', token);
|
|
560
|
+
pm.environment.set('userId', userId);
|
|
561
|
+
`
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const loginResult = await forge.execute(loginReq, collection);
|
|
566
|
+
console.log('✓ Logged in, token:', forge.getResolvedEnvironment()['authToken']);
|
|
567
|
+
|
|
568
|
+
// Request 2: Fetch user profile (uses session cookie automatically)
|
|
569
|
+
const profileReq = {
|
|
570
|
+
name: 'Get Profile',
|
|
571
|
+
method: 'GET',
|
|
572
|
+
url: 'https://api.example.com/users/{{userId}}',
|
|
573
|
+
headers: {
|
|
574
|
+
'Authorization': 'Bearer {{authToken}}' // Uses token from login
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
const profileResult = await forge.execute(profileReq, collection);
|
|
579
|
+
console.log('✓ Profile:', profileResult.response.body);
|
|
580
|
+
|
|
581
|
+
// Request 3: Update profile (session cookie still active)
|
|
582
|
+
const updateReq = {
|
|
583
|
+
name: 'Update Profile',
|
|
584
|
+
method: 'PATCH',
|
|
585
|
+
url: 'https://api.example.com/users/{{userId}}',
|
|
586
|
+
headers: {
|
|
587
|
+
'Authorization': 'Bearer {{authToken}}'
|
|
588
|
+
},
|
|
589
|
+
body: { type: 'raw', content: JSON.stringify({ status: 'active' })}
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
const updateResult = await forge.execute(updateReq, collection);
|
|
593
|
+
console.log('✓ Updated profile');
|
|
594
|
+
|
|
595
|
+
// Session automatically logged out - cookies cleared
|
|
596
|
+
forge.getRequestHistory().clear();
|
|
597
|
+
}
|
|
598
|
+
```
|
|
599
|
+
|
|
453
600
|
## 📦 Storage Formats
|
|
454
601
|
|
|
455
602
|
### File Format (Single JSON)
|
|
@@ -489,12 +636,18 @@ MIT © Henry Huang
|
|
|
489
636
|
|
|
490
637
|
### 0.1.0 (Initial Release)
|
|
491
638
|
|
|
492
|
-
- ✅ Core request execution
|
|
493
|
-
- ✅
|
|
494
|
-
- ✅
|
|
495
|
-
- ✅
|
|
496
|
-
- ✅
|
|
497
|
-
- ✅
|
|
498
|
-
- ✅
|
|
499
|
-
- ✅
|
|
500
|
-
- ✅
|
|
639
|
+
- ✅ **Core request execution** with Postman collection support
|
|
640
|
+
- ✅ **Dynamic variables** - 7 generators for on-the-fly value generation
|
|
641
|
+
- ✅ **Postman-compatible scripting** - `pm.*` API with full feature parity
|
|
642
|
+
- ✅ **Variable scoping** - globals, collection, environment, session, flow-level
|
|
643
|
+
- ✅ **Cookie persistence** - automatic storage and reuse across request chains
|
|
644
|
+
- ✅ **Pre-request & post-response scripts** with shared VM context
|
|
645
|
+
- ✅ **Test assertions** with BDD-style `pm.test()` and expect chains
|
|
646
|
+
- ✅ **Environment management** with variable substitution
|
|
647
|
+
- ✅ **Cookie jar** with domain-aware matching
|
|
648
|
+
- ✅ **Request/response interceptors** for customization
|
|
649
|
+
- ✅ **Request history** tracking
|
|
650
|
+
- ✅ **File and folder collection formats** (Postman-compatible)
|
|
651
|
+
- ✅ **Full TypeScript support** with comprehensive types
|
|
652
|
+
- ✅ **Dependency injection** for extensibility
|
|
653
|
+
- ✅ **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 { IOAuth2TokenManager, TokenCacheKey, TokenInfo } from './interfaces';
|
|
19
|
+
import { IExternalBrowserService, ISecretStore } from '../types/platform';
|
|
20
|
+
import { OAuth2Config } from '../types/types';
|
|
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 { ICollectionStore } from './collection-store';
|
|
7
|
+
import { IConfigService } from '../config';
|
|
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
|
+
}
|