@apicircle/core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +110 -0
- package/README.md +35 -0
- package/dist/index.cjs +6815 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1801 -0
- package/dist/index.d.ts +1801 -0
- package/dist/index.js +6678 -0
- package/dist/index.js.map +1 -0
- package/dist/patches-N7mvDpXn.d.cts +85 -0
- package/dist/patches-N7mvDpXn.d.ts +85 -0
- package/dist/test/mock-idp.cjs +232 -0
- package/dist/test/mock-idp.cjs.map +1 -0
- package/dist/test/mock-idp.d.cts +32 -0
- package/dist/test/mock-idp.d.ts +32 -0
- package/dist/test/mock-idp.js +207 -0
- package/dist/test/mock-idp.js.map +1 -0
- package/dist/workspace/file-backed.cjs +165 -0
- package/dist/workspace/file-backed.cjs.map +1 -0
- package/dist/workspace/file-backed.d.cts +37 -0
- package/dist/workspace/file-backed.d.ts +37 -0
- package/dist/workspace/file-backed.js +128 -0
- package/dist/workspace/file-backed.js.map +1 -0
- package/package.json +72 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,1801 @@
|
|
|
1
|
+
import { BodyType, RequestAuth, Request, RequestRun, Folder, RequestBody, HttpMethod, EnvironmentVariable, Assertion, ContextExtraction, WorkspaceSynced, LinkedSnapshot, RequestOverride, EnvironmentVariableOverride, ExecutionPlan, PlanRun } from '@apicircle/shared';
|
|
2
|
+
import { W as WorkspaceState, a as WorkspacePatch } from './patches-N7mvDpXn.cjs';
|
|
3
|
+
export { b as WorkspacePatchKind } from './patches-N7mvDpXn.cjs';
|
|
4
|
+
|
|
5
|
+
type HeaderEntry$1 = {
|
|
6
|
+
key: string;
|
|
7
|
+
value: string;
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
};
|
|
10
|
+
declare function getContentTypeForBodyType(bodyType: BodyType): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Reverse-map a Content-Type header value to a BodyType, or null if it
|
|
13
|
+
* doesn't match any known type. Strips parameters (e.g. `;charset=utf-8`)
|
|
14
|
+
* and is case-insensitive.
|
|
15
|
+
*/
|
|
16
|
+
declare function getBodyTypeForContentType(contentType: string): BodyType | null;
|
|
17
|
+
/**
|
|
18
|
+
* Apply (or remove) the Content-Type header on a header list to match the
|
|
19
|
+
* given body type. Pure — returns a new array.
|
|
20
|
+
*
|
|
21
|
+
* - bodyType=none → strips any existing Content-Type entry.
|
|
22
|
+
* - existing Content-Type entry → updated value, preserving order.
|
|
23
|
+
* - no Content-Type entry → appended.
|
|
24
|
+
*/
|
|
25
|
+
declare function applyContentTypeForBodyType(headers: HeaderEntry$1[], bodyType: BodyType): HeaderEntry$1[];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* HTTP Headers dictionary — used for autocomplete in the Headers editor.
|
|
29
|
+
*
|
|
30
|
+
* Each entry maps a header name to its known values (empty array = free text).
|
|
31
|
+
* Values that depend on context (e.g. specific MIME types) provide a curated
|
|
32
|
+
* set of the most common options.
|
|
33
|
+
*/
|
|
34
|
+
interface HeaderEntry {
|
|
35
|
+
name: string;
|
|
36
|
+
description: string;
|
|
37
|
+
values: string[];
|
|
38
|
+
/**
|
|
39
|
+
* `browser` — Forbidden by the Fetch spec; browsers silently ignore any
|
|
40
|
+
* attempt to set this header from JavaScript. On Desktop (native
|
|
41
|
+
* HTTP layer) the restriction does NOT apply — the header can
|
|
42
|
+
* still be set manually.
|
|
43
|
+
*
|
|
44
|
+
* `app` — Automatically injected by APICircle Studio at send-time (see
|
|
45
|
+
* autoHeaders.ts). Users can override these in the Headers tab
|
|
46
|
+
* and their value will take precedence.
|
|
47
|
+
*
|
|
48
|
+
* Omitted — Fully user-controlled on both Web and Desktop.
|
|
49
|
+
*/
|
|
50
|
+
reserved?: 'browser' | 'app';
|
|
51
|
+
/** Short note shown in the autocomplete to explain the reservation. */
|
|
52
|
+
reservedNote?: string;
|
|
53
|
+
}
|
|
54
|
+
declare const HTTP_HEADERS_MAP: HeaderEntry[];
|
|
55
|
+
type HeaderSuggestionMode = 'request' | 'response';
|
|
56
|
+
/**
|
|
57
|
+
* Suggest header names by case-insensitive prefix. Empty prefix returns the
|
|
58
|
+
* full suggestable list; auto-fed (`reserved: 'app'`) headers are excluded.
|
|
59
|
+
* An optional `limit` caps the number of filtered (non-empty prefix) results.
|
|
60
|
+
*
|
|
61
|
+
* `mode` filters by whether the header is request- or response-side
|
|
62
|
+
* relevant. Defaults to `'request'` for back-compat with the request
|
|
63
|
+
* editor's existing call sites.
|
|
64
|
+
*/
|
|
65
|
+
declare function suggestHeaders(prefix: string, limit?: number, mode?: HeaderSuggestionMode): HeaderEntry[];
|
|
66
|
+
/**
|
|
67
|
+
* Get the known values for a specific header name (case-insensitive).
|
|
68
|
+
* Returns an empty array when the header has no predefined values (free text).
|
|
69
|
+
*/
|
|
70
|
+
declare function getHeaderValues(headerName: string): string[];
|
|
71
|
+
/**
|
|
72
|
+
* Returns the HeaderEntry for an exact name match, or undefined.
|
|
73
|
+
*/
|
|
74
|
+
declare function getHeaderEntry(headerName: string): HeaderEntry | undefined;
|
|
75
|
+
|
|
76
|
+
interface AuthApplyTarget {
|
|
77
|
+
url: string;
|
|
78
|
+
method: string;
|
|
79
|
+
headers: Record<string, string>;
|
|
80
|
+
body: BodyInit | null;
|
|
81
|
+
}
|
|
82
|
+
interface AuthApplyOptions {
|
|
83
|
+
/**
|
|
84
|
+
* Called when applyAuth refreshes an expired OAuth2 access token. The
|
|
85
|
+
* store wires this to persist the new accessToken / refreshToken /
|
|
86
|
+
* expiresAt onto the request's auth payload — without it the refresh
|
|
87
|
+
* works for THIS request but the next request would re-refresh because
|
|
88
|
+
* the in-memory state didn't catch up.
|
|
89
|
+
*/
|
|
90
|
+
onTokenRefreshed?: (auth: Extract<RequestAuth, {
|
|
91
|
+
type: 'oauth2-client-credentials';
|
|
92
|
+
} | {
|
|
93
|
+
type: 'oauth2-auth-code';
|
|
94
|
+
} | {
|
|
95
|
+
type: 'oauth2-pkce';
|
|
96
|
+
} | {
|
|
97
|
+
type: 'oauth2-password';
|
|
98
|
+
} | {
|
|
99
|
+
type: 'oauth2-implicit';
|
|
100
|
+
} | {
|
|
101
|
+
type: 'oauth2-device';
|
|
102
|
+
}>, next: {
|
|
103
|
+
accessToken: string;
|
|
104
|
+
tokenType: string;
|
|
105
|
+
refreshToken?: string;
|
|
106
|
+
expiresAt: number;
|
|
107
|
+
obtainedScope?: string;
|
|
108
|
+
}) => void | Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Test seam for the refresh fetch. When omitted, the global fetch is
|
|
111
|
+
* used (matching production behavior).
|
|
112
|
+
*/
|
|
113
|
+
fetchImpl?: typeof fetch;
|
|
114
|
+
/**
|
|
115
|
+
* Refresh tokens when they have less than this many milliseconds left.
|
|
116
|
+
* Default: 60_000 (1 min). Set to 0 to disable proactive refresh.
|
|
117
|
+
*/
|
|
118
|
+
refreshLeewayMs?: number;
|
|
119
|
+
}
|
|
120
|
+
interface AuthApplyResult {
|
|
121
|
+
url: string;
|
|
122
|
+
headers: Record<string, string>;
|
|
123
|
+
/**
|
|
124
|
+
* Non-fatal warnings raised while applying auth (bad JWT key, malformed
|
|
125
|
+
* payload JSON, etc). The request still goes out — usually unauthenticated
|
|
126
|
+
* — but executeRequest surfaces these to the user so they don't see a
|
|
127
|
+
* mysterious 401 with no clue why their auth config didn't apply.
|
|
128
|
+
*/
|
|
129
|
+
warnings?: AuthApplyWarning[];
|
|
130
|
+
}
|
|
131
|
+
interface AuthApplyWarning {
|
|
132
|
+
/** Stable code so callers can match without parsing message strings. */
|
|
133
|
+
code: 'jwt-payload-json-invalid' | 'jwt-headers-json-invalid' | 'jwt-sign-failed' | 'hawk-url-invalid' | 'oauth2-refresh-failed';
|
|
134
|
+
/** Human-readable message — safe to surface in the UI. */
|
|
135
|
+
message: string;
|
|
136
|
+
}
|
|
137
|
+
declare function applyAuth(target: AuthApplyTarget, auth: RequestAuth, opts?: AuthApplyOptions): Promise<AuthApplyResult>;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Auto-fed request headers — platform-specific, non-editable, not stored.
|
|
141
|
+
*
|
|
142
|
+
* These headers are injected at send-time by the execution engine:
|
|
143
|
+
* • They are NEVER written to IndexedDB or Git remote.
|
|
144
|
+
* • A new X-Trace-Span-Id and traceparent are generated for every send.
|
|
145
|
+
* • They do NOT appear in the user-visible "Headers" editor tab — the
|
|
146
|
+
* HeadersTab "Auto-fed at send" aside lists them for reference only.
|
|
147
|
+
*
|
|
148
|
+
* User-set headers always win: if the user typed `X-Client-Version` into the
|
|
149
|
+
* Headers tab, the auto value is suppressed.
|
|
150
|
+
*/
|
|
151
|
+
/** Canonical desktop origin used in requests (also used by the native HTTP layer). */
|
|
152
|
+
declare const DESKTOP_APP_ORIGIN = "http://app.studio.apicircle.dev";
|
|
153
|
+
/**
|
|
154
|
+
* Generate a random hex span-id (16 hex chars = 64 bits, compatible with
|
|
155
|
+
* W3C Trace Context `traceparent` and OpenTelemetry span-id format).
|
|
156
|
+
*/
|
|
157
|
+
declare function generateSpanId(): string;
|
|
158
|
+
/**
|
|
159
|
+
* Generate a W3C traceparent header value.
|
|
160
|
+
* Format: 00-<trace-id (32 hex)>-<span-id (16 hex)>-01
|
|
161
|
+
*/
|
|
162
|
+
declare function generateTraceParent(): string;
|
|
163
|
+
/**
|
|
164
|
+
* Override hooks for tests — let test code feed deterministic values
|
|
165
|
+
* for span-id / traceparent / platform without monkey-patching `crypto`.
|
|
166
|
+
* Production code never passes these.
|
|
167
|
+
*/
|
|
168
|
+
interface AutoHeaderOverrides {
|
|
169
|
+
spanId?: string;
|
|
170
|
+
traceparent?: string;
|
|
171
|
+
platform?: 'desktop' | 'web';
|
|
172
|
+
version?: string;
|
|
173
|
+
name?: string;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Returns the set of auto-fed headers for a single request execution.
|
|
177
|
+
* Call this once per send — `X-Trace-Span-Id` and `traceparent` are
|
|
178
|
+
* re-generated on every invocation.
|
|
179
|
+
*
|
|
180
|
+
* @param userHeaders The headers the user has configured so auto-headers
|
|
181
|
+
* do NOT override anything the user has explicitly set.
|
|
182
|
+
* @param overrides Test-only override hooks; never set in production.
|
|
183
|
+
*/
|
|
184
|
+
declare function buildAutoHeaders(userHeaders: Record<string, string>, overrides?: AutoHeaderOverrides): Record<string, string>;
|
|
185
|
+
/**
|
|
186
|
+
* Merge auto-headers into user headers. User-defined values always win —
|
|
187
|
+
* auto-headers only fill gaps.
|
|
188
|
+
*/
|
|
189
|
+
declare function mergeWithAutoHeaders(userHeaders: Record<string, string>, overrides?: AutoHeaderOverrides): Record<string, string>;
|
|
190
|
+
|
|
191
|
+
interface BuiltRequest {
|
|
192
|
+
url: string;
|
|
193
|
+
method: string;
|
|
194
|
+
headers: Record<string, string>;
|
|
195
|
+
body: BodyInit | null;
|
|
196
|
+
/**
|
|
197
|
+
* Non-fatal warnings raised by applyAuth (bad JWT key, malformed
|
|
198
|
+
* payload JSON, etc). executeRequest forwards these into
|
|
199
|
+
* `ExecutionResult.authWarnings` so the UI can surface them alongside
|
|
200
|
+
* the response — without them, a misconfigured JWT silently produces
|
|
201
|
+
* a 401 with no clue why.
|
|
202
|
+
*/
|
|
203
|
+
authWarnings: AuthApplyWarning[];
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Resolves an attachment slotId to a Blob (with filename for form-data).
|
|
207
|
+
* The host (UI layer) reads this from its IndexedDB attachments store.
|
|
208
|
+
* Returns null when the attachment is missing — composeBody treats missing
|
|
209
|
+
* attachments as empty fields rather than throwing.
|
|
210
|
+
*/
|
|
211
|
+
type AttachmentResolver = (slotId: string) => Promise<{
|
|
212
|
+
blob: Blob;
|
|
213
|
+
filename: string;
|
|
214
|
+
} | null>;
|
|
215
|
+
/**
|
|
216
|
+
* Split a typed URL into the base part (everything before `?`) and a
|
|
217
|
+
* structured query-row list. Used by the editor's URL input to keep the
|
|
218
|
+
* URL field and the Query Params sub-tab in sync — when the user types or
|
|
219
|
+
* pastes `?key=val`, the rows surface in the Params list automatically.
|
|
220
|
+
*
|
|
221
|
+
* Variable references like `{{NAME}}` in keys or values are preserved
|
|
222
|
+
* verbatim — they stay templated and resolve at send time. We use
|
|
223
|
+
* permissive splitting (split on `&` then on the first `=`) rather than
|
|
224
|
+
* `URLSearchParams` because URLSearchParams percent-decodes `{{` into `{{`
|
|
225
|
+
* fine but also collapses whitespace and strips empty keys, which fights
|
|
226
|
+
* the user's intent during in-progress typing.
|
|
227
|
+
*/
|
|
228
|
+
declare function parseUrlQuery(rawUrl: string): {
|
|
229
|
+
base: string;
|
|
230
|
+
query: Array<{
|
|
231
|
+
key: string;
|
|
232
|
+
value: string;
|
|
233
|
+
enabled: boolean;
|
|
234
|
+
}>;
|
|
235
|
+
};
|
|
236
|
+
/**
|
|
237
|
+
* Inverse of `parseUrlQuery`. Renders the structured query rows back into a
|
|
238
|
+
* URL-bar-friendly string, skipping disabled or empty-key rows. Values are
|
|
239
|
+
* left as-is (no double-encoding) — `composeUrl` runs at send time and
|
|
240
|
+
* handles wire encoding properly.
|
|
241
|
+
*/
|
|
242
|
+
declare function composeUrlWithQuery(base: string, query: ReadonlyArray<{
|
|
243
|
+
key: string;
|
|
244
|
+
value: string;
|
|
245
|
+
enabled: boolean;
|
|
246
|
+
}>): string;
|
|
247
|
+
/** Extract the placeholder names appearing in a URL, in document order, deduped. */
|
|
248
|
+
declare function findPathPlaceholders(rawUrl: string): string[];
|
|
249
|
+
/**
|
|
250
|
+
* Substitute `:name` and `{name}` placeholders in a URL's path with the
|
|
251
|
+
* matching values from `pathParams`. The query string passes through
|
|
252
|
+
* untouched — a `{{var}}` in there is a variable reference, not a path
|
|
253
|
+
* placeholder. Missing keys substitute to empty string; the Editor surfaces
|
|
254
|
+
* unbound placeholders as a UI warning, not a runtime error.
|
|
255
|
+
*/
|
|
256
|
+
declare function applyPathParams(rawUrl: string, pathParams: Record<string, string>): string;
|
|
257
|
+
/**
|
|
258
|
+
* Build a `Cookie` header value from a list of name/value pairs. Skips
|
|
259
|
+
* disabled or empty-key rows. Returns the empty string when nothing applies.
|
|
260
|
+
* Per RFC 6265, cookie values may not contain CTL chars or `;` (which is the
|
|
261
|
+
* row separator) — we strip CTLs and also strip `;` from values to keep the
|
|
262
|
+
* row framing intact when a {{var}} resolves to something containing one.
|
|
263
|
+
*/
|
|
264
|
+
declare function composeCookieHeader(rows: ReadonlyArray<{
|
|
265
|
+
key: string;
|
|
266
|
+
value: string;
|
|
267
|
+
enabled: boolean;
|
|
268
|
+
}>): string;
|
|
269
|
+
declare function composeUrl(rawUrl: string, params: ReadonlyArray<{
|
|
270
|
+
key: string;
|
|
271
|
+
value: string;
|
|
272
|
+
enabled: boolean;
|
|
273
|
+
}>): string;
|
|
274
|
+
declare function composeHeaders(rows: ReadonlyArray<{
|
|
275
|
+
key: string;
|
|
276
|
+
value: string;
|
|
277
|
+
enabled: boolean;
|
|
278
|
+
}>): Record<string, string>;
|
|
279
|
+
/**
|
|
280
|
+
* Serialize a request body for fetch(). Async because form-data and binary
|
|
281
|
+
* may need to read attachment blobs from the host's storage layer.
|
|
282
|
+
*/
|
|
283
|
+
declare function composeBody(body: Request['body'], resolveAttachment?: AttachmentResolver): Promise<BodyInit | null>;
|
|
284
|
+
interface BuildRequestOptions {
|
|
285
|
+
resolveAttachment?: AttachmentResolver;
|
|
286
|
+
/**
|
|
287
|
+
* applyAuth options — most importantly the `onTokenRefreshed`
|
|
288
|
+
* callback that lets the store persist refreshed OAuth2 tokens.
|
|
289
|
+
* Forwarded as-is.
|
|
290
|
+
*/
|
|
291
|
+
authOptions?: AuthApplyOptions;
|
|
292
|
+
/**
|
|
293
|
+
* Test-only override hooks for the auto-fed headers. Lets specs feed
|
|
294
|
+
* deterministic values for `X-Trace-Span-Id`, `traceparent`, and
|
|
295
|
+
* `X-Client-Platform`. Production callers omit this.
|
|
296
|
+
*/
|
|
297
|
+
autoHeaderOverrides?: AutoHeaderOverrides;
|
|
298
|
+
}
|
|
299
|
+
declare function buildRequest(req: Request, opts?: BuildRequestOptions): Promise<BuiltRequest>;
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Detect whether the host is the APICircle Studio Desktop shell.
|
|
303
|
+
*
|
|
304
|
+
* The Electron preload script attaches a bridge object on `globalThis.apicircleDesktop`
|
|
305
|
+
* (see `apps/desktop/src/main/preload.ts`). The web app exposes nothing, so a
|
|
306
|
+
* presence check is sufficient — we don't need to inspect the bridge's shape.
|
|
307
|
+
*/
|
|
308
|
+
declare function isDesktop(): boolean;
|
|
309
|
+
|
|
310
|
+
interface ResolutionScope {
|
|
311
|
+
contextVars: Record<string, string>;
|
|
312
|
+
activeEnv: Record<string, string>;
|
|
313
|
+
priorityEnvs: Array<Record<string, string>>;
|
|
314
|
+
secrets: Record<string, string>;
|
|
315
|
+
}
|
|
316
|
+
interface ResolveResult {
|
|
317
|
+
value: string;
|
|
318
|
+
/** Names that were referenced but not found in any scope. */
|
|
319
|
+
missing: string[];
|
|
320
|
+
}
|
|
321
|
+
declare function lookup(scope: ResolutionScope, name: string): string | undefined;
|
|
322
|
+
/**
|
|
323
|
+
* Replace every `{{NAME}}` in `input` with its resolved value. Unknown names
|
|
324
|
+
* are left as-is in the output (so the user can see which placeholder didn't
|
|
325
|
+
* resolve) and reported via `missing`.
|
|
326
|
+
*/
|
|
327
|
+
declare function resolveString(input: string, scope: ResolutionScope): ResolveResult;
|
|
328
|
+
/**
|
|
329
|
+
* Resolve placeholders in every string value of an object (one level deep).
|
|
330
|
+
* Used for header / param row arrays — both keys and values are resolved.
|
|
331
|
+
* Returns a flat list of all missing names across the inputs.
|
|
332
|
+
*/
|
|
333
|
+
declare function resolveStringMap(obj: Record<string, string>, scope: ResolutionScope): {
|
|
334
|
+
result: Record<string, string>;
|
|
335
|
+
missing: string[];
|
|
336
|
+
};
|
|
337
|
+
/**
|
|
338
|
+
* Build a ResolutionScope from a Workspace + a per-request context-vars
|
|
339
|
+
* list. Optional plan-level priority overrides take precedence over the
|
|
340
|
+
* workspace's global priority order.
|
|
341
|
+
*
|
|
342
|
+
* `secrets` is passed in already-decrypted because resolveString runs
|
|
343
|
+
* synchronously — decryption happens once before send.
|
|
344
|
+
*/
|
|
345
|
+
declare function buildScope(args: {
|
|
346
|
+
contextVars: ReadonlyArray<{
|
|
347
|
+
key: string;
|
|
348
|
+
value: string;
|
|
349
|
+
}>;
|
|
350
|
+
environments: Record<string, Record<string, string>>;
|
|
351
|
+
activeEnvName: string | null;
|
|
352
|
+
priorityOrder: string[];
|
|
353
|
+
secrets?: Record<string, string>;
|
|
354
|
+
}): ResolutionScope;
|
|
355
|
+
type VariableSource = 'context' | 'active-env' | 'priority-env' | 'secret';
|
|
356
|
+
interface VariableSuggestion {
|
|
357
|
+
key: string;
|
|
358
|
+
source: VariableSource;
|
|
359
|
+
/** Display-only — secrets always show as the placeholder string. */
|
|
360
|
+
preview: string;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Walk a ResolutionScope in precedence order and produce one suggestion
|
|
364
|
+
* per unique key. Used by the editor's `{{` autocomplete and Monaco's
|
|
365
|
+
* completion provider.
|
|
366
|
+
*/
|
|
367
|
+
declare function collectVariableSuggestions(scope: ResolutionScope): VariableSuggestion[];
|
|
368
|
+
/**
|
|
369
|
+
* Suggestions to show given a cursor position inside a text field. Returns
|
|
370
|
+
* `null` when the cursor isn't inside an open `{{ … }}` token, so callers
|
|
371
|
+
* can hide the popup. When inside a token, returns matches filtered by the
|
|
372
|
+
* partial token text.
|
|
373
|
+
*/
|
|
374
|
+
declare function getVariableAutocomplete(text: string, cursorPosition: number, scope: ResolutionScope): VariableSuggestion[] | null;
|
|
375
|
+
|
|
376
|
+
interface PreSendWarning {
|
|
377
|
+
kind: 'unresolved-variable' | 'unbound-path-param' | 'content-type-mismatch' | 'url-embedded-credentials';
|
|
378
|
+
message: string;
|
|
379
|
+
}
|
|
380
|
+
interface PreSendBlocker {
|
|
381
|
+
kind: 'auth-fields-missing' | 'unparseable-url' | 'empty-url';
|
|
382
|
+
message: string;
|
|
383
|
+
}
|
|
384
|
+
interface PreSendValidationResult {
|
|
385
|
+
warnings: PreSendWarning[];
|
|
386
|
+
blockers: PreSendBlocker[];
|
|
387
|
+
}
|
|
388
|
+
/** Inputs to the validator — the request + resolution scope. */
|
|
389
|
+
interface PreSendValidationInput {
|
|
390
|
+
request: Request;
|
|
391
|
+
scope: ResolutionScope;
|
|
392
|
+
}
|
|
393
|
+
declare function preSendValidation({ request, scope, }: PreSendValidationInput): PreSendValidationResult;
|
|
394
|
+
|
|
395
|
+
interface ExecutionResult {
|
|
396
|
+
startedAt: string;
|
|
397
|
+
durationMs: number;
|
|
398
|
+
status: number | null;
|
|
399
|
+
ok: boolean;
|
|
400
|
+
statusText: string;
|
|
401
|
+
headers: Record<string, string>;
|
|
402
|
+
body: string;
|
|
403
|
+
bodyKind: 'json' | 'text' | 'binary' | 'empty';
|
|
404
|
+
error?: string;
|
|
405
|
+
url: string;
|
|
406
|
+
method: string;
|
|
407
|
+
/**
|
|
408
|
+
* Non-fatal warnings from applyAuth (e.g. malformed JWT key, bad
|
|
409
|
+
* payload JSON). Empty array when auth applied cleanly. UI surfaces
|
|
410
|
+
* these alongside the response so users can see WHY their request
|
|
411
|
+
* went out unauthenticated.
|
|
412
|
+
*/
|
|
413
|
+
authWarnings: AuthApplyWarning[];
|
|
414
|
+
/**
|
|
415
|
+
* True when the response body exceeded `MAX_RESPONSE_BODY_BYTES` and we
|
|
416
|
+
* stopped reading. The `body` field still contains the prefix we did
|
|
417
|
+
* read — callers should render a "Response truncated at X MB" badge.
|
|
418
|
+
* Omitted when the body was read in full.
|
|
419
|
+
*/
|
|
420
|
+
responseTruncated?: boolean;
|
|
421
|
+
}
|
|
422
|
+
interface ExecuteOptions {
|
|
423
|
+
fetchImpl?: typeof fetch;
|
|
424
|
+
timeoutMs?: number | null;
|
|
425
|
+
signal?: AbortSignal;
|
|
426
|
+
resolveAttachment?: AttachmentResolver;
|
|
427
|
+
/**
|
|
428
|
+
* applyAuth options — `onTokenRefreshed` is the important one for
|
|
429
|
+
* production: the store wires it to persist refreshed OAuth2 tokens
|
|
430
|
+
* back into `RequestAuth` so subsequent sends see the new state.
|
|
431
|
+
*/
|
|
432
|
+
authOptions?: AuthApplyOptions;
|
|
433
|
+
/**
|
|
434
|
+
* Test-only override hooks for the auto-fed headers. Lets specs feed
|
|
435
|
+
* deterministic values for `X-Trace-Span-Id`, `traceparent`, etc.
|
|
436
|
+
* Production callers omit this.
|
|
437
|
+
*/
|
|
438
|
+
autoHeaderOverrides?: AutoHeaderOverrides;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Execute a request through the browser's fetch (or an injected impl for
|
|
442
|
+
* tests). Returns a flat ExecutionResult — never throws for HTTP errors.
|
|
443
|
+
* Network failures and timeouts are captured into result.error with status=null.
|
|
444
|
+
*
|
|
445
|
+
* Challenge-driven auth (Digest, NTLM) is handled here: when the first
|
|
446
|
+
* fetch returns 401 with a recognized `WWW-Authenticate` scheme, we
|
|
447
|
+
* compute the response header and re-fetch once. NTLM further requires a
|
|
448
|
+
* second retry to send the Type-3 Authenticate after the Type-2
|
|
449
|
+
* Challenge — that's transparent to the caller, the returned result
|
|
450
|
+
* reflects the FINAL response.
|
|
451
|
+
*/
|
|
452
|
+
declare function executeRequest(req: Request, opts?: ExecuteOptions): Promise<ExecutionResult>;
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Adapt a stored `RequestRun` into the `ExecutionResult` shape that
|
|
456
|
+
* `ResponseViewer` consumes. `RequestRun` is the persisted, post-mortem
|
|
457
|
+
* record (kept on `WorkspaceLocal.history` and capped); `ExecutionResult`
|
|
458
|
+
* is the live in-flight value the editor receives. Both expose the same
|
|
459
|
+
* conceptual fields, so the History and Execution detail views can lean
|
|
460
|
+
* on the same renderer instead of forking the layout.
|
|
461
|
+
*
|
|
462
|
+
* The body is `responseBodyPreview` (already capped by
|
|
463
|
+
* `RUN_BODY_PREVIEW_LIMIT`); the editor will show a truncation hint when
|
|
464
|
+
* `RequestRun.responseTruncated` is true.
|
|
465
|
+
*/
|
|
466
|
+
declare function requestRunToExecutionResult(run: RequestRun): ExecutionResult;
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* HTTP Digest Access Authentication (RFC 7616, supersedes RFC 2617).
|
|
470
|
+
*
|
|
471
|
+
* Digest is a challenge-response scheme: the client makes a request, the
|
|
472
|
+
* server responds 401 with `WWW-Authenticate: Digest ...`, and the client
|
|
473
|
+
* retries with `Authorization: Digest ...` carrying a hash of the
|
|
474
|
+
* credentials + the server-supplied nonce. This module owns the parsing
|
|
475
|
+
* of the challenge directives and the construction of the response
|
|
476
|
+
* header. The challenge round-trip itself lives in `executeRequest` —
|
|
477
|
+
* it's the engine's job to fire the first request, see the 401, call
|
|
478
|
+
* back here, and re-send.
|
|
479
|
+
*
|
|
480
|
+
* Algorithm support: MD5 (default), MD5-sess, SHA-256, SHA-256-sess,
|
|
481
|
+
* SHA-512-256, and the `-sess` variants. MD5 is required for interop
|
|
482
|
+
* with the long tail of legacy servers; the implementation is in the
|
|
483
|
+
* private `_legacyHashes` module so the rest of the codebase doesn't
|
|
484
|
+
* grow new MD5 callsites.
|
|
485
|
+
*/
|
|
486
|
+
interface DigestChallenge {
|
|
487
|
+
realm: string;
|
|
488
|
+
nonce: string;
|
|
489
|
+
/** Comma-separated list per RFC 7616; the first one we recognize wins. */
|
|
490
|
+
qop?: string;
|
|
491
|
+
opaque?: string;
|
|
492
|
+
algorithm?: string;
|
|
493
|
+
/** Server-supplied opaque token returned verbatim in the response. */
|
|
494
|
+
domain?: string;
|
|
495
|
+
/** Stale=true means the previous nonce expired but credentials are still valid. */
|
|
496
|
+
stale?: string;
|
|
497
|
+
/** Any directives we don't model end up here for diagnostics. */
|
|
498
|
+
[extra: string]: string | undefined;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Parse the value of a `WWW-Authenticate: Digest ...` header into its
|
|
502
|
+
* directives. The header may appear with or without the `Digest ` prefix
|
|
503
|
+
* (callers sometimes strip it); both forms work.
|
|
504
|
+
*
|
|
505
|
+
* Quoted values have the surrounding quotes removed; unquoted values are
|
|
506
|
+
* taken as-is up to the next comma or whitespace. Unknown directives are
|
|
507
|
+
* preserved on the returned object so diagnostics can surface them.
|
|
508
|
+
*/
|
|
509
|
+
declare function parseDigestChallenge(header: string): DigestChallenge;
|
|
510
|
+
interface BuildDigestArgs {
|
|
511
|
+
method: string;
|
|
512
|
+
/** Request URI as it appears on the wire (path + query, not the full URL). */
|
|
513
|
+
uri: string;
|
|
514
|
+
username: string;
|
|
515
|
+
password: string;
|
|
516
|
+
challenge: DigestChallenge;
|
|
517
|
+
/**
|
|
518
|
+
* Override for the client nonce. If omitted, 16 random hex chars are
|
|
519
|
+
* generated via `crypto.getRandomValues`. Tests pass a fixed value to
|
|
520
|
+
* make the resulting header deterministic.
|
|
521
|
+
*/
|
|
522
|
+
cnonce?: string;
|
|
523
|
+
/**
|
|
524
|
+
* Nonce-count, per RFC 7616 §3.4. Each request reusing the same server
|
|
525
|
+
* nonce should bump nc; we default to 1 because the engine clears the
|
|
526
|
+
* stored challenge when the server rotates the nonce. 8 hex chars,
|
|
527
|
+
* lowercase.
|
|
528
|
+
*/
|
|
529
|
+
nc?: string;
|
|
530
|
+
/**
|
|
531
|
+
* Body for `qop=auth-int` (entity-body hash). Ignored for `qop=auth`.
|
|
532
|
+
* Strings, `Uint8Array`, `ArrayBuffer`, and `Blob` are all hashable;
|
|
533
|
+
* if `null`/`undefined` we still emit auth-int but with the empty-body
|
|
534
|
+
* hash. FormData / ReadableStream callers should serialize first
|
|
535
|
+
* (auth-int requires the EXACT bytes the server will see).
|
|
536
|
+
*/
|
|
537
|
+
entityBody?: string | Uint8Array | ArrayBuffer | Blob | null;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Build the `Authorization: Digest ...` header value (without the
|
|
541
|
+
* `Authorization:` prefix — caller prepends it).
|
|
542
|
+
*
|
|
543
|
+
* Returns a string ready to put on the wire. Throws when the challenge
|
|
544
|
+
* specifies an algorithm we don't support; the engine should surface
|
|
545
|
+
* that as a "this server's auth scheme isn't supported yet" error
|
|
546
|
+
* rather than retrying forever.
|
|
547
|
+
*/
|
|
548
|
+
declare function buildDigestAuthHeader(args: BuildDigestArgs): Promise<string>;
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Build the Type-1 Negotiate message, base64-encoded for use as
|
|
552
|
+
* `Authorization: NTLM <value>`. Sent by the client to advertise its
|
|
553
|
+
* capabilities and trigger the server's challenge response.
|
|
554
|
+
*/
|
|
555
|
+
declare function buildNtlmType1Negotiate(domain: string, workstation: string): string;
|
|
556
|
+
interface NtlmType2Challenge {
|
|
557
|
+
/** 8-byte server challenge nonce. */
|
|
558
|
+
challenge: Uint8Array;
|
|
559
|
+
/**
|
|
560
|
+
* Variable-length AV_PAIR target-info block. Echoed verbatim in the
|
|
561
|
+
* Type-3 NTLMv2 blob so the server can verify the response.
|
|
562
|
+
*/
|
|
563
|
+
targetInfo: Uint8Array;
|
|
564
|
+
/**
|
|
565
|
+
* Original raw bytes of the entire Type-2 message — required when the
|
|
566
|
+
* caller wants to compute a MIC over `Type1 || Type2 || Type3` per
|
|
567
|
+
* [MS-NLMP] §3.1.5.1.2. Set automatically by `parseNtlmType2Challenge`.
|
|
568
|
+
* Optional so callers constructing the challenge literal in tests can
|
|
569
|
+
* skip it; MIC computation needs the parser-produced form.
|
|
570
|
+
*/
|
|
571
|
+
rawBytes?: Uint8Array;
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Parse the Type-2 Challenge message extracted from the server's
|
|
575
|
+
* `WWW-Authenticate: NTLM <base64>` reply. Returns the server challenge
|
|
576
|
+
* + target info bytes that feed into Type-3 construction.
|
|
577
|
+
*/
|
|
578
|
+
declare function parseNtlmType2Challenge(base64: string): NtlmType2Challenge;
|
|
579
|
+
interface BuildNtlmType3Args {
|
|
580
|
+
username: string;
|
|
581
|
+
password: string;
|
|
582
|
+
domain: string;
|
|
583
|
+
workstation: string;
|
|
584
|
+
challenge: NtlmType2Challenge;
|
|
585
|
+
/** Override for the 8-byte client challenge — tests pass a fixed value. */
|
|
586
|
+
clientChallenge?: Uint8Array;
|
|
587
|
+
/** Override for the timestamp (ms since epoch) — tests pass a fixed value. */
|
|
588
|
+
timestampMs?: number;
|
|
589
|
+
/**
|
|
590
|
+
* Raw bytes of the Type-1 Negotiate message we sent in the previous
|
|
591
|
+
* round. When BOTH this and `type2Message` are provided, we emit a
|
|
592
|
+
* 16-byte MIC at offset 72 of the Type-3 message and set MsvAvFlags
|
|
593
|
+
* bit 0x2 in the AV_PAIR target info — required by hardened Win Server
|
|
594
|
+
* 2019+ AD configurations per [MS-NLMP] §3.1.5.1.2. When either is
|
|
595
|
+
* absent we preserve the legacy "no MIC" layout for back-compat with
|
|
596
|
+
* older servers that reject the longer header.
|
|
597
|
+
*/
|
|
598
|
+
type1Message?: Uint8Array;
|
|
599
|
+
/** Raw bytes of the Type-2 Challenge message — paired with `type1Message`. */
|
|
600
|
+
type2Message?: Uint8Array;
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Build the Type-3 Authenticate message (NTLMv2). Computes the NTLMv2
|
|
604
|
+
* hash, the NTProofStr (HMAC-MD5 over server-challenge + blob), and
|
|
605
|
+
* packs everything into the binary message format. Returns base64 for
|
|
606
|
+
* use as `Authorization: NTLM <value>`.
|
|
607
|
+
*/
|
|
608
|
+
declare function buildNtlmType3Authenticate(args: BuildNtlmType3Args): string;
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Hawk authentication scheme (https://github.com/mozilla/hawk).
|
|
612
|
+
*
|
|
613
|
+
* Hawk uses an HMAC of a normalized request string keyed by a shared
|
|
614
|
+
* secret. Unlike Digest / NTLM there's no challenge round-trip — every
|
|
615
|
+
* request is signed independently using:
|
|
616
|
+
*
|
|
617
|
+
* normalized = "hawk.1.header\n{ts}\n{nonce}\n{METHOD}\n{path?query}\n
|
|
618
|
+
* {host-lc}\n{port}\n{payload-hash}\n{ext}\n"
|
|
619
|
+
* mac = base64(HMAC-{algorithm}(secret, normalized))
|
|
620
|
+
*
|
|
621
|
+
* We default to SHA-256 (the modern recommendation); SHA-1 is supported
|
|
622
|
+
* for interop with legacy Hawk servers. Body-payload hashing is a future
|
|
623
|
+
* extension — for now we always pass an empty payload-hash, which the
|
|
624
|
+
* server must verify by setting `Hash: ""` or skipping payload validation.
|
|
625
|
+
*/
|
|
626
|
+
interface HawkSignArgs {
|
|
627
|
+
method: string;
|
|
628
|
+
/**
|
|
629
|
+
* Full request URL — we extract scheme/host/port/path/query from this
|
|
630
|
+
* since the normalized string needs lowercase host + numeric port.
|
|
631
|
+
*/
|
|
632
|
+
url: string;
|
|
633
|
+
hawkId: string;
|
|
634
|
+
hawkKey: string;
|
|
635
|
+
algorithm?: 'sha256' | 'sha1';
|
|
636
|
+
/**
|
|
637
|
+
* Override for the timestamp (Unix seconds). Tests pass a fixed value;
|
|
638
|
+
* production callers either omit (use `Date.now()`) or apply
|
|
639
|
+
* `timestampOffset` to compensate for client-server clock skew.
|
|
640
|
+
*/
|
|
641
|
+
timestamp?: number;
|
|
642
|
+
/**
|
|
643
|
+
* 8 hex chars by default; tests pass a fixed value. The server tracks
|
|
644
|
+
* (id, ts, nonce) tuples to detect replay, so even fixed tests are
|
|
645
|
+
* one-shot.
|
|
646
|
+
*/
|
|
647
|
+
nonce?: string;
|
|
648
|
+
/** Optional `app=` and `dlg=` directives for delegated requests. */
|
|
649
|
+
app?: string;
|
|
650
|
+
delegation?: string;
|
|
651
|
+
/** Optional `ext=` directive for application-specific data. */
|
|
652
|
+
ext?: string;
|
|
653
|
+
/**
|
|
654
|
+
* Optional payload + content-type for body-binding. When present, the
|
|
655
|
+
* normalized request string includes a `hash=BASE64(H(payload))` line
|
|
656
|
+
* AND we emit a `hash="…"` directive in the Authorization header.
|
|
657
|
+
* Servers configured with body-binding reject requests that lack this;
|
|
658
|
+
* leaving the field undefined preserves the prior behavior (server
|
|
659
|
+
* must accept body-less signing).
|
|
660
|
+
*/
|
|
661
|
+
payload?: {
|
|
662
|
+
body: string | ArrayBuffer | Uint8Array;
|
|
663
|
+
contentType: string;
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
declare function buildHawkAuthHeader(args: HawkSignArgs): Promise<string>;
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* AWS Signature Version 4 — request signing for AWS APIs.
|
|
670
|
+
*
|
|
671
|
+
* SigV4 is per-request HMAC chain over a canonical-request string. The
|
|
672
|
+
* server (or a STS-issued temporary credentials provider) verifies the
|
|
673
|
+
* signature using the same algorithm, so request integrity is end-to-end
|
|
674
|
+
* without any callback / handshake.
|
|
675
|
+
*
|
|
676
|
+
* canonical = METHOD\nPATH\nQUERY\nHEADERS\nSIGNED_HEADERS\nPAYLOAD_HASH
|
|
677
|
+
* stringToSign = "AWS4-HMAC-SHA256\n{amzDate}\n{credScope}\n{sha256(canonical)}"
|
|
678
|
+
* kSigning = HMAC(HMAC(HMAC(HMAC("AWS4{secret}", date), region), service), "aws4_request")
|
|
679
|
+
* signature = hex(HMAC(kSigning, stringToSign))
|
|
680
|
+
*
|
|
681
|
+
* Two delivery modes:
|
|
682
|
+
* - `header`: sets `Authorization: AWS4-HMAC-SHA256 Credential=..., SignedHeaders=..., Signature=...`
|
|
683
|
+
* - `query`: rewrites the URL with `X-Amz-*` query params (presigned URL).
|
|
684
|
+
*
|
|
685
|
+
* Body hashing only runs in `header` mode (presigned URLs always
|
|
686
|
+
* advertise `UNSIGNED-PAYLOAD`). Streaming / chunked bodies fall through
|
|
687
|
+
* to `UNSIGNED-PAYLOAD` because we'd otherwise have to buffer the entire
|
|
688
|
+
* stream before signing.
|
|
689
|
+
*/
|
|
690
|
+
/**
|
|
691
|
+
* Body shapes we can hash for SigV4 payload signing. Anything we can't
|
|
692
|
+
* read as bytes synchronously goes to `UNSIGNED-PAYLOAD` — AWS accepts
|
|
693
|
+
* that as long as `x-amz-content-sha256: UNSIGNED-PAYLOAD` is in the
|
|
694
|
+
* signed headers. ReadableStream falls into this bucket since draining
|
|
695
|
+
* it before signing would consume the body before fetch can send it.
|
|
696
|
+
*/
|
|
697
|
+
type SigV4Body = string | ArrayBuffer | Uint8Array | Blob | URLSearchParams | FormData | ReadableStream<Uint8Array> | null | undefined;
|
|
698
|
+
interface SigV4SignArgs {
|
|
699
|
+
method: string;
|
|
700
|
+
url: string;
|
|
701
|
+
/** Existing request headers — passed through and merged with SigV4 additions. */
|
|
702
|
+
headers: Record<string, string>;
|
|
703
|
+
/**
|
|
704
|
+
* Body for payload hashing. `string`, `ArrayBuffer`, `Uint8Array`,
|
|
705
|
+
* `Blob`, and `URLSearchParams` are hashed verbatim. `FormData` and
|
|
706
|
+
* `ReadableStream` fall through to `UNSIGNED-PAYLOAD` — AWS accepts
|
|
707
|
+
* that signing mode and many AWS SDKs default to it for streaming.
|
|
708
|
+
*/
|
|
709
|
+
body?: SigV4Body;
|
|
710
|
+
accessKeyId: string;
|
|
711
|
+
secretAccessKey: string;
|
|
712
|
+
region: string;
|
|
713
|
+
service: string;
|
|
714
|
+
/** STS-issued session token; copied to `X-Amz-Security-Token`. */
|
|
715
|
+
sessionToken?: string;
|
|
716
|
+
/** Where to put the signature — header (default) or query (presigned URL). */
|
|
717
|
+
addTo?: 'header' | 'query';
|
|
718
|
+
/**
|
|
719
|
+
* S3 specifically does NOT collapse double slashes or normalize dot
|
|
720
|
+
* segments — it treats `bucket/foo//bar` as a different object from
|
|
721
|
+
* `bucket/foo/bar`. Set true when `service: 's3'` to preserve the
|
|
722
|
+
* raw path. Default false (matches non-S3 services like execute-api).
|
|
723
|
+
*
|
|
724
|
+
* If you don't pass this and `service === 's3'`, we auto-enable it —
|
|
725
|
+
* the common case for S3 callers is "preserve my path exactly".
|
|
726
|
+
*/
|
|
727
|
+
preservePathSlashes?: boolean;
|
|
728
|
+
/**
|
|
729
|
+
* Override `now` for tests. The amz-date stamp must be the SAME instant
|
|
730
|
+
* used to build the credential scope, so we capture once.
|
|
731
|
+
*/
|
|
732
|
+
now?: Date;
|
|
733
|
+
}
|
|
734
|
+
interface SigV4SignResult {
|
|
735
|
+
url: string;
|
|
736
|
+
headers: Record<string, string>;
|
|
737
|
+
}
|
|
738
|
+
declare function applyAwsSigV4(args: SigV4SignArgs): Promise<SigV4SignResult>;
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* JWT (RFC 7519) signing — Bearer token generation.
|
|
742
|
+
*
|
|
743
|
+
* This is the "self-signed assertion" variant of `jwt-bearer` auth: the
|
|
744
|
+
* client mints a JWT signed with its own key, then sends it as
|
|
745
|
+
* `Authorization: Bearer <jwt>`. Different from OAuth2 JWT-bearer flow
|
|
746
|
+
* (RFC 7523) where the JWT is exchanged at a token endpoint for an
|
|
747
|
+
* access token — that flow goes through `auth/oauth2/grants.ts` and
|
|
748
|
+
* uses this signer to mint the assertion.
|
|
749
|
+
*
|
|
750
|
+
* Algorithms:
|
|
751
|
+
* - HS256 / HS384 / HS512: HMAC with shared secret (most common).
|
|
752
|
+
* - RS256 / RS384 / RS512: RSA-SHA — caller supplies a private key
|
|
753
|
+
* in PKCS#8 PEM. Imported via `crypto.subtle.importKey`.
|
|
754
|
+
* - ES256 / ES384 / ES512: ECDSA-P256/384/521 — same PKCS#8 path.
|
|
755
|
+
* - **none**: refused. Always reject `alg: "none"` regardless of caller
|
|
756
|
+
* intent — RFC 8725 §3.1 calls this out as the canonical JWT mistake.
|
|
757
|
+
*
|
|
758
|
+
* The header `typ` defaults to `"JWT"`; callers can override via the
|
|
759
|
+
* `additionalHeaders` arg to set e.g. `kid` for key discovery.
|
|
760
|
+
*/
|
|
761
|
+
type JwtAlgorithm = 'HS256' | 'HS384' | 'HS512' | 'RS256' | 'RS384' | 'RS512' | 'PS256' | 'PS384' | 'PS512' | 'ES256' | 'ES384' | 'ES512' | 'EdDSA';
|
|
762
|
+
interface JwtSignArgs {
|
|
763
|
+
/** Signing algorithm — must match the key material in `secretOrKey`. */
|
|
764
|
+
algorithm: JwtAlgorithm;
|
|
765
|
+
/**
|
|
766
|
+
* Shared secret for HMAC (HS256/384/512) or PEM-encoded PKCS#8
|
|
767
|
+
* private key for RSA / ECDSA. For HMAC, plain UTF-8 strings work;
|
|
768
|
+
* for asymmetric, the PEM must include the BEGIN/END markers.
|
|
769
|
+
*/
|
|
770
|
+
secretOrKey: string;
|
|
771
|
+
/** Token claims. `iat` is auto-added when missing; `exp` is left to the caller. */
|
|
772
|
+
payload: Record<string, unknown>;
|
|
773
|
+
/** Extra header fields beyond `alg` and `typ` (e.g. `kid`, `cty`). */
|
|
774
|
+
additionalHeaders?: Record<string, unknown>;
|
|
775
|
+
}
|
|
776
|
+
declare function signJwt(args: JwtSignArgs): Promise<string>;
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* PKCE (Proof Key for Code Exchange — RFC 7636) primitives.
|
|
780
|
+
*
|
|
781
|
+
* Authorization-code grant adds a `code_verifier` (43–128 random URL-safe
|
|
782
|
+
* chars) generated on the client. The auth request carries a derived
|
|
783
|
+
* `code_challenge` (either the verifier itself for `plain`, or
|
|
784
|
+
* `BASE64URL(SHA-256(verifier))` for `S256`); the token-exchange request
|
|
785
|
+
* carries the verifier itself. The server confirms `H(verifier) === challenge`
|
|
786
|
+
* before issuing tokens, neutralizing intercepted authorization codes.
|
|
787
|
+
*
|
|
788
|
+
* S256 is the only method we recommend; `plain` exists in the spec for
|
|
789
|
+
* environments without SHA-256 (basically none today). We support both
|
|
790
|
+
* because some legacy IdPs still negotiate `plain` even when S256 is
|
|
791
|
+
* available.
|
|
792
|
+
*/
|
|
793
|
+
/**
|
|
794
|
+
* Generate a fresh PKCE code verifier — 43..128 random characters from
|
|
795
|
+
* the unreserved URL set (RFC 3986 §2.3). Default length is 64, well
|
|
796
|
+
* inside the spec's allowed range.
|
|
797
|
+
*
|
|
798
|
+
* Sampling is uniform: we mask each random byte with `& 0x3f` to project
|
|
799
|
+
* onto exactly 64 alphabet positions, so every alphabet character is
|
|
800
|
+
* equally likely. The earlier `% 66` implementation introduced a small
|
|
801
|
+
* but measurable modulo bias.
|
|
802
|
+
*/
|
|
803
|
+
declare function generateCodeVerifier(length?: number): string;
|
|
804
|
+
type PkceMethod = 'S256' | 'plain';
|
|
805
|
+
/**
|
|
806
|
+
* Compute the `code_challenge` that pairs with a verifier. The browser's
|
|
807
|
+
* SubtleCrypto handles SHA-256; for `plain` we just echo the verifier.
|
|
808
|
+
*/
|
|
809
|
+
declare function computeCodeChallenge(verifier: string, method?: PkceMethod): Promise<string>;
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* The single token-endpoint client used by every OAuth2 grant.
|
|
813
|
+
*
|
|
814
|
+
* RFC 6749 §4 endpoints accept `application/x-www-form-urlencoded` with
|
|
815
|
+
* `grant_type` plus grant-specific fields. Client credentials may go in
|
|
816
|
+
* the Authorization header (`client_secret_basic`) or the body
|
|
817
|
+
* (`client_secret_post`); some IdPs only accept one or the other, so the
|
|
818
|
+
* caller picks via `clientAuthMethod`.
|
|
819
|
+
*
|
|
820
|
+
* On success the IdP returns:
|
|
821
|
+
*
|
|
822
|
+
* { access_token, token_type, expires_in?, refresh_token?, scope? }
|
|
823
|
+
*
|
|
824
|
+
* On failure (RFC 6749 §5.2):
|
|
825
|
+
*
|
|
826
|
+
* { error, error_description?, error_uri? } with HTTP 400/401
|
|
827
|
+
*
|
|
828
|
+
* We surface the failure as an Error whose `.message` includes both
|
|
829
|
+
* `error` and `error_description` so the UI can show "invalid_grant: bad
|
|
830
|
+
* code" without parsing JSON itself. The structured response is also
|
|
831
|
+
* attached to `.cause` for callers who need to dispatch on `error_code`
|
|
832
|
+
* (e.g. device flow's `authorization_pending` / `slow_down` cases).
|
|
833
|
+
*/
|
|
834
|
+
interface OAuth2TokenResponse {
|
|
835
|
+
accessToken: string;
|
|
836
|
+
tokenType: string;
|
|
837
|
+
expiresIn?: number;
|
|
838
|
+
refreshToken?: string;
|
|
839
|
+
scope?: string;
|
|
840
|
+
/** Captured raw response — useful for OpenID Connect `id_token` etc. */
|
|
841
|
+
raw: Record<string, unknown>;
|
|
842
|
+
}
|
|
843
|
+
interface OAuth2ErrorResponse {
|
|
844
|
+
error: string;
|
|
845
|
+
errorDescription?: string;
|
|
846
|
+
errorUri?: string;
|
|
847
|
+
/** HTTP status the IdP returned (400, 401, 403, …). */
|
|
848
|
+
status: number;
|
|
849
|
+
/** Captured raw body — useful for diagnostics and grant-specific dispatch. */
|
|
850
|
+
raw: Record<string, unknown>;
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Pre-signed client-assertion JWT for `private_key_jwt` client
|
|
854
|
+
* authentication (RFC 7521 §4.2 / RFC 7523 §2.2). The auth tab signs
|
|
855
|
+
* the assertion locally via `signJwt` and passes it here; we add the
|
|
856
|
+
* required `client_assertion` + `client_assertion_type` body fields.
|
|
857
|
+
* Used in place of `clientSecret` — the IdP verifies the assertion
|
|
858
|
+
* against the registered public key.
|
|
859
|
+
*/
|
|
860
|
+
interface ClientAssertion {
|
|
861
|
+
/** The signed JWT (header.payload.signature) — output of `signJwt`. */
|
|
862
|
+
jwt: string;
|
|
863
|
+
/** RFC 7523 mandates this exact value. Other types exist (e.g. SAML2) but aren't used here. */
|
|
864
|
+
type?: string;
|
|
865
|
+
}
|
|
866
|
+
interface FetchOAuth2TokenArgs {
|
|
867
|
+
tokenUrl: string;
|
|
868
|
+
/**
|
|
869
|
+
* Grant-specific body fields. `grant_type` MUST be set by the caller —
|
|
870
|
+
* this helper doesn't infer it. Value type is `URLSearchParams` so the
|
|
871
|
+
* caller controls the exact wire format and can reuse the same body
|
|
872
|
+
* for retry / refresh paths.
|
|
873
|
+
*/
|
|
874
|
+
body: URLSearchParams;
|
|
875
|
+
clientId: string;
|
|
876
|
+
/**
|
|
877
|
+
* Confidential clients only. Public clients (PKCE in a SPA) leave this
|
|
878
|
+
* undefined — the IdP recognizes the client by id alone.
|
|
879
|
+
*/
|
|
880
|
+
clientSecret?: string;
|
|
881
|
+
/**
|
|
882
|
+
* Where to put the client credentials. `header` = HTTP Basic with
|
|
883
|
+
* `id:secret`. `body` = `client_id` + `client_secret` URL-form fields.
|
|
884
|
+
* Defaults to `header` (the RFC's preferred method).
|
|
885
|
+
*
|
|
886
|
+
* Ignored when `clientAssertion` is set — assertion takes precedence
|
|
887
|
+
* because mixing both is a misconfiguration the server will reject.
|
|
888
|
+
*/
|
|
889
|
+
clientAuthMethod?: 'header' | 'body';
|
|
890
|
+
/**
|
|
891
|
+
* Optional `private_key_jwt` style client authentication. When set,
|
|
892
|
+
* the body carries `client_assertion` + `client_assertion_type` and
|
|
893
|
+
* `clientSecret` is NOT sent. Used by Azure AD, GCP, and any IdP
|
|
894
|
+
* that prefers signed assertions over shared secrets.
|
|
895
|
+
*/
|
|
896
|
+
clientAssertion?: ClientAssertion;
|
|
897
|
+
/** Optional extra parameters appended to the body — IdP-specific knobs. */
|
|
898
|
+
extraParams?: Record<string, string>;
|
|
899
|
+
/** Override fetch for tests / desktop bridge. */
|
|
900
|
+
fetchImpl?: typeof fetch;
|
|
901
|
+
/** Per-call abort. Composed with the global request signal upstream. */
|
|
902
|
+
signal?: AbortSignal;
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* OAuth2 token endpoint failure. The structured `errorBody` carries the
|
|
906
|
+
* error code (`invalid_grant`, `authorization_pending`, etc) plus
|
|
907
|
+
* `error_description` / `error_uri` and the HTTP status — callers can
|
|
908
|
+
* dispatch on `errorBody.error` for grant-specific behavior (device
|
|
909
|
+
* flow's `authorization_pending` / `slow_down`, refresh re-auth, etc).
|
|
910
|
+
*
|
|
911
|
+
* Naming note: we deliberately don't shadow the standard `Error.cause`
|
|
912
|
+
* field. Callers using TypeScript can still use `instanceof` + the
|
|
913
|
+
* `errorBody` accessor; callers reading the `cause` property get the
|
|
914
|
+
* standard "what was the original exception" semantics.
|
|
915
|
+
*/
|
|
916
|
+
declare class OAuth2TokenError extends Error {
|
|
917
|
+
readonly errorBody: OAuth2ErrorResponse;
|
|
918
|
+
constructor(errorBody: OAuth2ErrorResponse);
|
|
919
|
+
}
|
|
920
|
+
declare function fetchOAuth2Token(args: FetchOAuth2TokenArgs): Promise<OAuth2TokenResponse>;
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Per-grant OAuth2 runners. Each function POSTs to the token endpoint
|
|
924
|
+
* with the body shape RFC 6749 specifies for that grant, and returns a
|
|
925
|
+
* normalized `OAuth2TokenResponse`. Callbacks (browser redirects to
|
|
926
|
+
* `redirect_uri?code=...`) and device-code polling intervals are the
|
|
927
|
+
* caller's job — these runners ONLY exchange.
|
|
928
|
+
*
|
|
929
|
+
* Refresh handling is in `refreshToken()` rather than per-grant: the
|
|
930
|
+
* refresh-token grant is identical regardless of which grant minted the
|
|
931
|
+
* original token.
|
|
932
|
+
*/
|
|
933
|
+
|
|
934
|
+
type ClientAuthMethod = 'header' | 'body';
|
|
935
|
+
type FetchImpl = typeof fetch;
|
|
936
|
+
interface ClientCredentialsArgs {
|
|
937
|
+
tokenUrl: string;
|
|
938
|
+
clientId: string;
|
|
939
|
+
clientSecret: string;
|
|
940
|
+
scope?: string;
|
|
941
|
+
clientAuthMethod?: ClientAuthMethod;
|
|
942
|
+
extraParams?: Record<string, string>;
|
|
943
|
+
fetchImpl?: FetchImpl;
|
|
944
|
+
signal?: AbortSignal;
|
|
945
|
+
}
|
|
946
|
+
/** RFC 6749 §4.4 — machine-to-machine, no user. */
|
|
947
|
+
declare function runClientCredentials(args: ClientCredentialsArgs): Promise<OAuth2TokenResponse>;
|
|
948
|
+
interface RopcArgs {
|
|
949
|
+
tokenUrl: string;
|
|
950
|
+
clientId: string;
|
|
951
|
+
clientSecret?: string;
|
|
952
|
+
username: string;
|
|
953
|
+
password: string;
|
|
954
|
+
scope?: string;
|
|
955
|
+
clientAuthMethod?: ClientAuthMethod;
|
|
956
|
+
extraParams?: Record<string, string>;
|
|
957
|
+
fetchImpl?: FetchImpl;
|
|
958
|
+
signal?: AbortSignal;
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* RFC 6749 §4.3 — Resource Owner Password Credentials. Marked DEPRECATED
|
|
962
|
+
* in OAuth 2.1; we support it because legacy IdPs still require it for
|
|
963
|
+
* specific testing / migration scenarios. The auth panel surfaces a
|
|
964
|
+
* warning banner about the deprecation.
|
|
965
|
+
*/
|
|
966
|
+
declare function runRopc(args: RopcArgs): Promise<OAuth2TokenResponse>;
|
|
967
|
+
interface AuthCodeExchangeArgs {
|
|
968
|
+
tokenUrl: string;
|
|
969
|
+
clientId: string;
|
|
970
|
+
clientSecret?: string;
|
|
971
|
+
/** The `code` value the IdP redirected back with. */
|
|
972
|
+
code: string;
|
|
973
|
+
/** Must match the redirect_uri sent in the initial /authorize request. */
|
|
974
|
+
redirectUri: string;
|
|
975
|
+
clientAuthMethod?: ClientAuthMethod;
|
|
976
|
+
extraParams?: Record<string, string>;
|
|
977
|
+
fetchImpl?: FetchImpl;
|
|
978
|
+
signal?: AbortSignal;
|
|
979
|
+
}
|
|
980
|
+
/** RFC 6749 §4.1 — Authorization Code grant. */
|
|
981
|
+
declare function exchangeAuthCode(args: AuthCodeExchangeArgs): Promise<OAuth2TokenResponse>;
|
|
982
|
+
interface PkceExchangeArgs extends AuthCodeExchangeArgs {
|
|
983
|
+
/** The verifier we generated when constructing the auth URL. */
|
|
984
|
+
codeVerifier: string;
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* RFC 7636 — Authorization Code with PKCE. Same body as plain auth-code
|
|
988
|
+
* plus `code_verifier`. clientSecret is optional (public clients omit it).
|
|
989
|
+
*/
|
|
990
|
+
declare function exchangePkce(args: PkceExchangeArgs): Promise<OAuth2TokenResponse>;
|
|
991
|
+
interface DeviceAuthorizationResponse {
|
|
992
|
+
deviceCode: string;
|
|
993
|
+
userCode: string;
|
|
994
|
+
verificationUri: string;
|
|
995
|
+
/** Optional convenience URI with the user_code already embedded. */
|
|
996
|
+
verificationUriComplete?: string;
|
|
997
|
+
/** Seconds the device should poll the token endpoint. */
|
|
998
|
+
interval: number;
|
|
999
|
+
/** Seconds before deviceCode expires. */
|
|
1000
|
+
expiresIn: number;
|
|
1001
|
+
raw: Record<string, unknown>;
|
|
1002
|
+
}
|
|
1003
|
+
interface DeviceAuthorizationArgs {
|
|
1004
|
+
deviceAuthorizationUrl: string;
|
|
1005
|
+
clientId: string;
|
|
1006
|
+
clientSecret?: string;
|
|
1007
|
+
scope?: string;
|
|
1008
|
+
fetchImpl?: FetchImpl;
|
|
1009
|
+
signal?: AbortSignal;
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* RFC 8628 §3.1 — Device Authorization Request. Returns the device_code
|
|
1013
|
+
* + user_code so the caller can show the user_code + verification URI to
|
|
1014
|
+
* a human, then poll the token endpoint with `pollDeviceFlow`.
|
|
1015
|
+
*/
|
|
1016
|
+
declare function requestDeviceAuthorization(args: DeviceAuthorizationArgs): Promise<DeviceAuthorizationResponse>;
|
|
1017
|
+
interface PollDeviceFlowArgs {
|
|
1018
|
+
tokenUrl: string;
|
|
1019
|
+
clientId: string;
|
|
1020
|
+
clientSecret?: string;
|
|
1021
|
+
deviceCode: string;
|
|
1022
|
+
/** Initial poll interval in seconds. Bumped to `interval + 5` on `slow_down`. */
|
|
1023
|
+
intervalSeconds: number;
|
|
1024
|
+
/** Max overall wait. When elapsed, throws an `OAuth2TokenError` of error="expired_token". */
|
|
1025
|
+
maxWaitMs?: number;
|
|
1026
|
+
fetchImpl?: FetchImpl;
|
|
1027
|
+
signal?: AbortSignal;
|
|
1028
|
+
/**
|
|
1029
|
+
* Optional progress callback fired on every poll cycle. UI uses this
|
|
1030
|
+
* to update the visible "still waiting…" indicator and tick down the
|
|
1031
|
+
* remaining time. Receives `{ pollCount, elapsedMs, lastError }` —
|
|
1032
|
+
* `lastError` is set when the IdP responded `slow_down` so the UI
|
|
1033
|
+
* can hint the user to wait.
|
|
1034
|
+
*/
|
|
1035
|
+
onPoll?: (info: {
|
|
1036
|
+
pollCount: number;
|
|
1037
|
+
elapsedMs: number;
|
|
1038
|
+
lastError?: string;
|
|
1039
|
+
}) => void;
|
|
1040
|
+
/** Test seam — overrides `setTimeout` / `Date.now` for fake clocks. */
|
|
1041
|
+
scheduler?: {
|
|
1042
|
+
sleep: (ms: number) => Promise<void>;
|
|
1043
|
+
now: () => number;
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* RFC 8628 §3.4 — poll the token endpoint until the user authorizes the
|
|
1048
|
+
* device, then return the token. Honors `authorization_pending` /
|
|
1049
|
+
* `slow_down` / `access_denied` / `expired_token`.
|
|
1050
|
+
*/
|
|
1051
|
+
declare function pollDeviceFlow(args: PollDeviceFlowArgs): Promise<OAuth2TokenResponse>;
|
|
1052
|
+
interface RefreshTokenArgs {
|
|
1053
|
+
tokenUrl: string;
|
|
1054
|
+
clientId: string;
|
|
1055
|
+
clientSecret?: string;
|
|
1056
|
+
refreshToken: string;
|
|
1057
|
+
/** Some IdPs require the original scope on refresh. */
|
|
1058
|
+
scope?: string;
|
|
1059
|
+
clientAuthMethod?: ClientAuthMethod;
|
|
1060
|
+
extraParams?: Record<string, string>;
|
|
1061
|
+
fetchImpl?: FetchImpl;
|
|
1062
|
+
signal?: AbortSignal;
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* RFC 6749 §6 — refresh a previously obtained token. Identical wire
|
|
1066
|
+
* shape regardless of the grant that minted the original token; the
|
|
1067
|
+
* caller decides when to call it (typical heuristic: when
|
|
1068
|
+
* `expiresAt < now + 60s`).
|
|
1069
|
+
*/
|
|
1070
|
+
declare function refreshToken(args: RefreshTokenArgs): Promise<OAuth2TokenResponse>;
|
|
1071
|
+
/**
|
|
1072
|
+
* Build the `/authorize` URL the user is redirected to for auth-code or
|
|
1073
|
+
* implicit flows. PKCE callers append `code_challenge` + `code_challenge_method`
|
|
1074
|
+
* via `extraParams`. Doesn't open a browser — that's the host bridge's job.
|
|
1075
|
+
*/
|
|
1076
|
+
declare function buildAuthorizeUrl(args: {
|
|
1077
|
+
authorizeUrl: string;
|
|
1078
|
+
clientId: string;
|
|
1079
|
+
redirectUri: string;
|
|
1080
|
+
responseType: 'code' | 'token';
|
|
1081
|
+
scope?: string;
|
|
1082
|
+
state?: string;
|
|
1083
|
+
extraParams?: Record<string, string>;
|
|
1084
|
+
}): string;
|
|
1085
|
+
|
|
1086
|
+
interface ResolveInheritedAuthArgs {
|
|
1087
|
+
/** The request's stated auth (may be `{ type: 'inherit' }`). */
|
|
1088
|
+
requestAuth: RequestAuth;
|
|
1089
|
+
/** The folderId the request lives in (null if at the root). */
|
|
1090
|
+
folderId: string | null;
|
|
1091
|
+
/** All known folders, keyed by id. */
|
|
1092
|
+
folders: Record<string, Folder>;
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* If `requestAuth` is anything other than `inherit`, returns it unchanged.
|
|
1096
|
+
* Otherwise walks up the folder chain looking for the first folder whose
|
|
1097
|
+
* own `auth` is set and is not itself `inherit` or `none`. Folders with
|
|
1098
|
+
* `inherit` or `none` auth are transparent (skipped, walk continues).
|
|
1099
|
+
*/
|
|
1100
|
+
declare function resolveInheritedAuth({ requestAuth, folderId, folders, }: ResolveInheritedAuthArgs): RequestAuth;
|
|
1101
|
+
|
|
1102
|
+
interface ParsedCurl {
|
|
1103
|
+
method: Request['method'];
|
|
1104
|
+
url: string;
|
|
1105
|
+
headers: Request['headers'];
|
|
1106
|
+
query: Request['query'];
|
|
1107
|
+
body: RequestBody;
|
|
1108
|
+
auth: RequestAuth;
|
|
1109
|
+
/** Unrecognised flags / fragments — the UI can show these as a warning. */
|
|
1110
|
+
warnings: string[];
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* Tokenise a shell-style argv from a string. Handles single/double quotes,
|
|
1114
|
+
* backslash escapes, and whitespace-splitting. Doesn't try to be a full
|
|
1115
|
+
* POSIX shell — `$VAR` expansion, command substitution, and globs all
|
|
1116
|
+
* pass through verbatim.
|
|
1117
|
+
*/
|
|
1118
|
+
declare function tokenizeCurl(input: string): string[];
|
|
1119
|
+
declare function parseCurl(input: string): ParsedCurl;
|
|
1120
|
+
|
|
1121
|
+
interface ImportedRequest {
|
|
1122
|
+
name: string;
|
|
1123
|
+
method: HttpMethod;
|
|
1124
|
+
url: string;
|
|
1125
|
+
headers: Array<{
|
|
1126
|
+
key: string;
|
|
1127
|
+
value: string;
|
|
1128
|
+
enabled: boolean;
|
|
1129
|
+
}>;
|
|
1130
|
+
query: Array<{
|
|
1131
|
+
key: string;
|
|
1132
|
+
value: string;
|
|
1133
|
+
enabled: boolean;
|
|
1134
|
+
}>;
|
|
1135
|
+
body: RequestBody;
|
|
1136
|
+
auth: RequestAuth;
|
|
1137
|
+
}
|
|
1138
|
+
interface ImportedFolder {
|
|
1139
|
+
name: string;
|
|
1140
|
+
/** Index path from root (deterministic id assignment is the caller's job). */
|
|
1141
|
+
pathIds: number[];
|
|
1142
|
+
parentPathIds: number[] | null;
|
|
1143
|
+
}
|
|
1144
|
+
interface ParsedPostmanCollection {
|
|
1145
|
+
collectionName: string;
|
|
1146
|
+
folders: ImportedFolder[];
|
|
1147
|
+
requests: Array<ImportedRequest & {
|
|
1148
|
+
folderPathIds: number[] | null;
|
|
1149
|
+
}>;
|
|
1150
|
+
warnings: string[];
|
|
1151
|
+
}
|
|
1152
|
+
declare function isPostmanV2Collection(doc: unknown): boolean;
|
|
1153
|
+
declare function parsePostmanCollection(input: string): ParsedPostmanCollection;
|
|
1154
|
+
|
|
1155
|
+
interface ParsedPostmanEnvironment {
|
|
1156
|
+
/** Suggested env name; the user can change at import time. */
|
|
1157
|
+
name: string;
|
|
1158
|
+
variables: EnvironmentVariable[];
|
|
1159
|
+
warnings: string[];
|
|
1160
|
+
}
|
|
1161
|
+
declare function isPostmanEnvironment(doc: unknown): boolean;
|
|
1162
|
+
declare function parsePostmanEnvironment(input: string): ParsedPostmanEnvironment;
|
|
1163
|
+
|
|
1164
|
+
declare function isInsomniaExport(doc: unknown): boolean;
|
|
1165
|
+
declare function parseInsomniaCollection(input: string): ParsedPostmanCollection;
|
|
1166
|
+
|
|
1167
|
+
/**
|
|
1168
|
+
* Result of evaluating one assertion against a response. Carries a snapshot
|
|
1169
|
+
* of the assertion definition so downstream UI (History detail view, run
|
|
1170
|
+
* exports, plan reports) can render the verdict without joining back to the
|
|
1171
|
+
* source request — which may have been renamed, edited, or deleted by the
|
|
1172
|
+
* time the user looks at history.
|
|
1173
|
+
*/
|
|
1174
|
+
interface AssertionResult {
|
|
1175
|
+
assertionId: string;
|
|
1176
|
+
kind: Assertion['kind'];
|
|
1177
|
+
op: Assertion['op'];
|
|
1178
|
+
target?: string;
|
|
1179
|
+
expected: string | number;
|
|
1180
|
+
passed: boolean;
|
|
1181
|
+
/**
|
|
1182
|
+
* Human-readable explanation. Always populated by `runAssertions` — pass
|
|
1183
|
+
* cases get positive descriptions ("status: 200 equals 200"), fail cases
|
|
1184
|
+
* get the diff. Optional in the type because the persisted shape in
|
|
1185
|
+
* `RequestRun.assertions` predates this and may carry undefined for older
|
|
1186
|
+
* history entries.
|
|
1187
|
+
*/
|
|
1188
|
+
detail?: string;
|
|
1189
|
+
}
|
|
1190
|
+
declare function runAssertions(assertions: ReadonlyArray<Assertion>, exec: ExecutionResult): AssertionResult[];
|
|
1191
|
+
/**
|
|
1192
|
+
* Read a dotted-path value from a JSON tree. Supports `a.b.c` and bracket
|
|
1193
|
+
* indexing `arr[0]`. Returns `undefined` for missing segments.
|
|
1194
|
+
*/
|
|
1195
|
+
declare function readJsonPath(root: unknown, path: string): unknown;
|
|
1196
|
+
|
|
1197
|
+
interface ContextExtractionResult {
|
|
1198
|
+
extracted: Record<string, string>;
|
|
1199
|
+
warnings: string[];
|
|
1200
|
+
}
|
|
1201
|
+
declare function extractContext(result: ExecutionResult, extractions: ReadonlyArray<ContextExtraction>): ContextExtractionResult;
|
|
1202
|
+
|
|
1203
|
+
interface EncryptedPayload {
|
|
1204
|
+
iv: string;
|
|
1205
|
+
ciphertext: string;
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Encrypt a UTF-8 string with the given AES-GCM key. Returns base64-encoded
|
|
1209
|
+
* iv + ciphertext, safe to embed in JSON / push to Git.
|
|
1210
|
+
*/
|
|
1211
|
+
declare function encryptString(plaintext: string, key: CryptoKey): Promise<EncryptedPayload>;
|
|
1212
|
+
/**
|
|
1213
|
+
* Decrypt a payload produced by `encryptString`. Throws on bad key, tampered
|
|
1214
|
+
* ciphertext, or malformed input.
|
|
1215
|
+
*/
|
|
1216
|
+
declare function decryptString(payload: EncryptedPayload, key: CryptoKey): Promise<string>;
|
|
1217
|
+
/**
|
|
1218
|
+
* Generate a fresh AES-GCM 256-bit key. The host persists it (typically as
|
|
1219
|
+
* a JWK in IndexedDB) so subsequent sessions can decrypt prior values.
|
|
1220
|
+
*/
|
|
1221
|
+
declare function generateAesKey(): Promise<CryptoKey>;
|
|
1222
|
+
/**
|
|
1223
|
+
* Generate a fresh per-slot salt (16 random bytes, base64-encoded). Salts are
|
|
1224
|
+
* stored in `synced.secretKeys[id].salt` — they're not secret, but they do
|
|
1225
|
+
* need to be stable for the slot's lifetime so ciphertext encrypted on one
|
|
1226
|
+
* device is decryptable on another.
|
|
1227
|
+
*/
|
|
1228
|
+
declare function generateSlotSalt(): string;
|
|
1229
|
+
/**
|
|
1230
|
+
* Derive an AES-GCM key from a slot's plaintext value via PBKDF2-SHA-256.
|
|
1231
|
+
* The salt is base64 and travels through Git in `synced.secretKeys[id].salt`;
|
|
1232
|
+
* the value is user-supplied and never leaves the device. Same `(value,
|
|
1233
|
+
* salt)` pair always derives the same key, so a teammate cloning the repo
|
|
1234
|
+
* gets matching keys once they enter the slot value on their machine.
|
|
1235
|
+
*/
|
|
1236
|
+
declare function deriveKeyFromSlotValue(value: string, saltBase64: string): Promise<CryptoKey>;
|
|
1237
|
+
/** Export an AES-GCM key as a JSON Web Key (for IDB storage). */
|
|
1238
|
+
declare function exportKey(key: CryptoKey): Promise<JsonWebKey>;
|
|
1239
|
+
/** Import an AES-GCM key previously exported via `exportKey`. */
|
|
1240
|
+
declare function importKey(jwk: JsonWebKey): Promise<CryptoKey>;
|
|
1241
|
+
/**
|
|
1242
|
+
* Serialize an EncryptedPayload to a single string we can store in
|
|
1243
|
+
* `Environment.variables[i].value`. The schema is `enc:v1:<iv>:<ciphertext>`
|
|
1244
|
+
* — versioned so we can rotate algorithms later without ambiguity.
|
|
1245
|
+
*/
|
|
1246
|
+
declare function serializePayload(payload: EncryptedPayload): string;
|
|
1247
|
+
declare function tryParsePayload(value: string): EncryptedPayload | null;
|
|
1248
|
+
|
|
1249
|
+
/**
|
|
1250
|
+
* Validate a branch name against GitHub's ref rules. Returns null when the
|
|
1251
|
+
* name is acceptable, otherwise a short reason. We enforce a stricter
|
|
1252
|
+
* subset (no spaces, ASCII only, length ≤ 100) so the auto-generated names
|
|
1253
|
+
* always pass.
|
|
1254
|
+
*/
|
|
1255
|
+
declare function validateBranchName(name: string): string | null;
|
|
1256
|
+
/** Lowercase ASCII slug, hyphenated, no leading/trailing/double hyphens. */
|
|
1257
|
+
declare function slugify(input: string): string;
|
|
1258
|
+
interface BranchNameOptions {
|
|
1259
|
+
/** The workspace's local display name (from the registry entry). */
|
|
1260
|
+
displayName: string;
|
|
1261
|
+
/** Inject a fixed id in tests; defaults to 6 random hex chars. */
|
|
1262
|
+
idGen?: () => string;
|
|
1263
|
+
}
|
|
1264
|
+
declare function generateWorkingBranchName(opts: BranchNameOptions): string;
|
|
1265
|
+
|
|
1266
|
+
/**
|
|
1267
|
+
* Stringify a WorkspaceSynced doc with deeply-sorted object keys + 2-space
|
|
1268
|
+
* indent + trailing newline (so editors don't re-stamp the file when the
|
|
1269
|
+
* user opens it). Arrays preserve their existing order — that's part of
|
|
1270
|
+
* the workspace's user-visible shape (priority list, tree children, etc).
|
|
1271
|
+
*/
|
|
1272
|
+
declare function serializeWorkspaceForGit(synced: WorkspaceSynced): string;
|
|
1273
|
+
|
|
1274
|
+
/** Error thrown when the input fails any of our checks. `code` lets the UI
|
|
1275
|
+
* branch on the specific failure (oversized, bad JSON, wrong shape, etc.)
|
|
1276
|
+
* without parsing the message string. */
|
|
1277
|
+
declare class RemoteWorkspaceParseError extends Error {
|
|
1278
|
+
readonly code: 'oversized' | 'invalid-json' | 'not-object' | 'missing-workspace-id' | 'missing-collections' | 'missing-environments';
|
|
1279
|
+
constructor(message: string, code: 'oversized' | 'invalid-json' | 'not-object' | 'missing-workspace-id' | 'missing-collections' | 'missing-environments');
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Parse a remote `workspace.json` and return a `WorkspaceSynced` we can
|
|
1283
|
+
* safely merge into store state. Throws `RemoteWorkspaceParseError` on
|
|
1284
|
+
* any failure — callers should catch and surface to the user as a
|
|
1285
|
+
* "this workspace was modified by an incompatible version" message.
|
|
1286
|
+
*
|
|
1287
|
+
* The returned object is NOT a deep clone of the input; if any nested
|
|
1288
|
+
* object had a `__proto__` etc. key, that key was dropped at the reviver
|
|
1289
|
+
* level. Strings, numbers, and arrays pass through unchanged.
|
|
1290
|
+
*
|
|
1291
|
+
* The function is intentionally PERMISSIVE about unknown fields — newer
|
|
1292
|
+
* versions of Studio may add fields we don't know about, and we want
|
|
1293
|
+
* those workspaces to remain readable. We only enforce the fields the
|
|
1294
|
+
* existing codebase positively requires.
|
|
1295
|
+
*/
|
|
1296
|
+
declare function parseWorkspaceJson(content: string): WorkspaceSynced;
|
|
1297
|
+
|
|
1298
|
+
/**
|
|
1299
|
+
* Return a copy of `synced` with every credential-bearing field in every
|
|
1300
|
+
* Request.auth blanked to ''. Identity fields are preserved. Pure — does
|
|
1301
|
+
* not mutate the input. Safe to call on partially-shaped workspaces.
|
|
1302
|
+
*/
|
|
1303
|
+
declare function redactForGit(synced: WorkspaceSynced): WorkspaceSynced;
|
|
1304
|
+
/**
|
|
1305
|
+
* Scan the already-serialised workspace JSON for any credential-only
|
|
1306
|
+
* field name with a non-empty value. Throws if found — the push path
|
|
1307
|
+
* should treat the throw as fatal (refuse to upload).
|
|
1308
|
+
*
|
|
1309
|
+
* The match is intentionally narrow: only the names in
|
|
1310
|
+
* `PLAINTEXT_CREDENTIAL_FIELD_NAMES`, only with a NON-EMPTY string value.
|
|
1311
|
+
* An empty-string credential (`"password":""`) is acceptable — that's
|
|
1312
|
+
* what `redactForGit` produces.
|
|
1313
|
+
*
|
|
1314
|
+
* Implementation note: we use a regex rather than walking the parsed
|
|
1315
|
+
* tree because (a) the input has already been serialised, and (b) the
|
|
1316
|
+
* regex catches every nesting level without us having to know the shape.
|
|
1317
|
+
* The risk of false positives is bounded because the field names are
|
|
1318
|
+
* specific (no `value` / `token` / `key` in the list).
|
|
1319
|
+
*/
|
|
1320
|
+
declare function assertNoPlaintextCredentials(serialized: string): void;
|
|
1321
|
+
|
|
1322
|
+
interface AttachmentSlotRef {
|
|
1323
|
+
slotId: string;
|
|
1324
|
+
sha256?: string;
|
|
1325
|
+
filename?: string;
|
|
1326
|
+
mimeType?: string;
|
|
1327
|
+
size?: number;
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Walk every request in the synced doc and return one entry per unique
|
|
1331
|
+
* attachment slotId it references. Form-data file rows and the binary
|
|
1332
|
+
* body's attachment ref both contribute. Duplicates (same slotId
|
|
1333
|
+
* referenced twice — defensive only; slot ids are normally unique) are
|
|
1334
|
+
* collapsed; the first occurrence wins.
|
|
1335
|
+
*/
|
|
1336
|
+
declare function collectAttachmentSlots(synced: WorkspaceSynced): AttachmentSlotRef[];
|
|
1337
|
+
|
|
1338
|
+
interface ParsedVersion {
|
|
1339
|
+
major: number;
|
|
1340
|
+
minor: number;
|
|
1341
|
+
patch: number;
|
|
1342
|
+
prerelease: string | null;
|
|
1343
|
+
build: string | null;
|
|
1344
|
+
}
|
|
1345
|
+
declare function parseSemver(version: string): ParsedVersion | null;
|
|
1346
|
+
declare function isValidSemver(version: string): boolean;
|
|
1347
|
+
/**
|
|
1348
|
+
* Compare two semver strings. Returns negative if `a < b`, positive if
|
|
1349
|
+
* `a > b`, 0 if equal. Build metadata is ignored (per semver spec). A
|
|
1350
|
+
* prerelease label sorts BEFORE its corresponding release (1.0.0-rc.1 <
|
|
1351
|
+
* 1.0.0). Within prereleases, dot-separated identifiers compare numeric
|
|
1352
|
+
* vs string per the spec.
|
|
1353
|
+
*/
|
|
1354
|
+
declare function compareSemver(a: string, b: string): number;
|
|
1355
|
+
/** Sort an array of semver strings — newest first (descending). */
|
|
1356
|
+
declare function sortVersionsDesc(versions: readonly string[]): string[];
|
|
1357
|
+
|
|
1358
|
+
interface PublishReleaseArgs {
|
|
1359
|
+
version: string;
|
|
1360
|
+
notes: string;
|
|
1361
|
+
/** Optional bookkeeping — the git commit SHA the release points at. */
|
|
1362
|
+
sha?: string;
|
|
1363
|
+
/** Optional bookkeeping — git tag name (the source of truth is the ledger). */
|
|
1364
|
+
tagName?: string;
|
|
1365
|
+
publishedAt?: string;
|
|
1366
|
+
}
|
|
1367
|
+
/**
|
|
1368
|
+
* Append a new release to `synced.releases.self.versions` and bump
|
|
1369
|
+
* `currentVersion`. Pure — does not touch IDB or Git.
|
|
1370
|
+
*
|
|
1371
|
+
* Throws on invalid semver, duplicate version, or invalid notes shape.
|
|
1372
|
+
*/
|
|
1373
|
+
declare function publishRelease(synced: WorkspaceSynced, args: PublishReleaseArgs): Promise<WorkspaceSynced>;
|
|
1374
|
+
/** Flip the `deprecated` flag on a version. Soft signal — version is still installable. */
|
|
1375
|
+
declare function deprecateRelease(synced: WorkspaceSynced, version: string): WorkspaceSynced;
|
|
1376
|
+
/**
|
|
1377
|
+
* Flip the `yanked` flag on a version. Hard signal — consumers should
|
|
1378
|
+
* be told this version is broken / unsafe and offered a different one.
|
|
1379
|
+
*/
|
|
1380
|
+
declare function yankRelease(synced: WorkspaceSynced, version: string): WorkspaceSynced;
|
|
1381
|
+
|
|
1382
|
+
type MonacoLanguage = 'json' | 'xml' | 'html' | 'graphql' | 'javascript' | 'yaml' | 'plaintext';
|
|
1383
|
+
declare function normalizeContentType(contentType?: string): string;
|
|
1384
|
+
declare function getLanguageFromContentType(contentType?: string): MonacoLanguage;
|
|
1385
|
+
/**
|
|
1386
|
+
* Map a workspace BodyType to its Monaco language. Used by the editor
|
|
1387
|
+
* to set the right syntax highlighter even before Content-Type lands.
|
|
1388
|
+
*/
|
|
1389
|
+
declare function getLanguageFromBodyType(bodyType: 'none' | 'json' | 'text' | 'urlencoded' | 'form-data' | 'binary' | 'xml' | 'graphql'): MonacoLanguage;
|
|
1390
|
+
declare const supportedContentTypeLanguageMap: Readonly<Record<string, MonacoLanguage>>;
|
|
1391
|
+
|
|
1392
|
+
interface GraphQLSchemaInfo {
|
|
1393
|
+
/** Object/Interface types and their fields. */
|
|
1394
|
+
types: Map<string, {
|
|
1395
|
+
fields: GraphQLField[];
|
|
1396
|
+
}>;
|
|
1397
|
+
/** Top-level operations (Query, Mutation, Subscription). */
|
|
1398
|
+
rootTypes: {
|
|
1399
|
+
query?: string;
|
|
1400
|
+
mutation?: string;
|
|
1401
|
+
subscription?: string;
|
|
1402
|
+
};
|
|
1403
|
+
/** Scalar + enum names. */
|
|
1404
|
+
scalars: string[];
|
|
1405
|
+
enums: string[];
|
|
1406
|
+
}
|
|
1407
|
+
interface GraphQLField {
|
|
1408
|
+
name: string;
|
|
1409
|
+
type: string;
|
|
1410
|
+
description?: string;
|
|
1411
|
+
}
|
|
1412
|
+
declare function parseGraphqlSchema(source: string, kind: 'sdl' | 'introspection'): GraphQLSchemaInfo;
|
|
1413
|
+
|
|
1414
|
+
type DiffStatus = 'unchanged' | 'local-only' | 'remote-only' | 'both-equal' | 'conflict';
|
|
1415
|
+
type EntityBucket = 'request' | 'folder' | 'environment' | 'linkedWorkspace' | 'mockServer' | 'executionPlan' | 'secretKey' | 'globalSchema' | 'globalGraphql' | 'linkedRequestOverride' | 'linkedEnvOverride' | 'releasePerLink' | 'tree' | 'environmentsActive' | 'environmentsPriority' | 'releaseSelf' | 'secretCrypto';
|
|
1416
|
+
interface DiffEntry {
|
|
1417
|
+
bucket: EntityBucket;
|
|
1418
|
+
/** Entity id within the bucket. Empty string for singleton buckets. */
|
|
1419
|
+
key: string;
|
|
1420
|
+
status: DiffStatus;
|
|
1421
|
+
/** Human-readable label for the resolver UI. */
|
|
1422
|
+
label: string;
|
|
1423
|
+
base: unknown;
|
|
1424
|
+
local: unknown;
|
|
1425
|
+
remote: unknown;
|
|
1426
|
+
}
|
|
1427
|
+
interface ThreeWayDiff {
|
|
1428
|
+
entries: DiffEntry[];
|
|
1429
|
+
conflicts: DiffEntry[];
|
|
1430
|
+
}
|
|
1431
|
+
type ConflictResolution = 'mine' | 'theirs';
|
|
1432
|
+
/** Map keyed by `bucket:key` (e.g. `request:r-1`, `releaseSelf:`). */
|
|
1433
|
+
type ResolutionMap = Record<string, ConflictResolution>;
|
|
1434
|
+
/**
|
|
1435
|
+
* Compute the per-entity diff. Returns every entity touched on at least
|
|
1436
|
+
* one side, plus a flat list of conflicts (subset of entries with status
|
|
1437
|
+
* 'conflict') for the resolver.
|
|
1438
|
+
*
|
|
1439
|
+
* `base` is the lastPulledSnapshot. When null (first refresh ever), every
|
|
1440
|
+
* remote entity that doesn't match local becomes a conflict — there's no
|
|
1441
|
+
* shared ancestor to pick a side automatically.
|
|
1442
|
+
*/
|
|
1443
|
+
declare function computeThreeWayDiff(base: WorkspaceSynced | null, local: WorkspaceSynced, remote: WorkspaceSynced): ThreeWayDiff;
|
|
1444
|
+
/**
|
|
1445
|
+
* Apply a fully-resolved diff: take fast-forwards (remote-only) into
|
|
1446
|
+
* local, keep local-only changes verbatim, and resolve every conflict
|
|
1447
|
+
* via the supplied `resolutions` map (`bucket:key` → 'mine' | 'theirs').
|
|
1448
|
+
*
|
|
1449
|
+
* Throws when any conflict is missing a resolution — the caller is
|
|
1450
|
+
* expected to populate the modal first.
|
|
1451
|
+
*/
|
|
1452
|
+
declare function applyMerge(local: WorkspaceSynced, remote: WorkspaceSynced, diff: ThreeWayDiff, resolutions: ResolutionMap): WorkspaceSynced;
|
|
1453
|
+
|
|
1454
|
+
interface UnpushedChange {
|
|
1455
|
+
bucket: EntityBucket;
|
|
1456
|
+
/** Entity id within the bucket; empty string for singletons (tree, etc.). */
|
|
1457
|
+
key: string;
|
|
1458
|
+
label: string;
|
|
1459
|
+
kind: 'added' | 'modified' | 'removed';
|
|
1460
|
+
base: unknown;
|
|
1461
|
+
local: unknown;
|
|
1462
|
+
}
|
|
1463
|
+
interface UnpushedSummary {
|
|
1464
|
+
added: number;
|
|
1465
|
+
modified: number;
|
|
1466
|
+
removed: number;
|
|
1467
|
+
total: number;
|
|
1468
|
+
/** Per-entry list, sorted by bucket then label so the preview list renders predictably. */
|
|
1469
|
+
changes: UnpushedChange[];
|
|
1470
|
+
computedAt: string;
|
|
1471
|
+
}
|
|
1472
|
+
declare function summarizeUnpushedChanges(base: WorkspaceSynced | null, current: WorkspaceSynced, options?: {
|
|
1473
|
+
now?: () => Date;
|
|
1474
|
+
}): UnpushedSummary;
|
|
1475
|
+
/**
|
|
1476
|
+
* Cheap "anything to push?" check for the BranchCard badge. Avoids
|
|
1477
|
+
* recomputing the full preview list when the caller only needs a
|
|
1478
|
+
* boolean. Identity short-circuits on referential equality of `current`
|
|
1479
|
+
* and `base` — common when the store hasn't mutated since pull.
|
|
1480
|
+
*/
|
|
1481
|
+
declare function hasUnpushedChanges(base: WorkspaceSynced | null, current: WorkspaceSynced): boolean;
|
|
1482
|
+
/** Stable empty value for callers that want to default-render an empty summary. */
|
|
1483
|
+
declare const EMPTY_UNPUSHED_SUMMARY: UnpushedSummary;
|
|
1484
|
+
|
|
1485
|
+
type LinkedUpdateStatus = 'unchanged' | 'source-only' | 'local-only' | 'both-changed' | 'new-in-source' | 'removed-in-source';
|
|
1486
|
+
type LinkedUpdateBucket = 'request' | 'folder' | 'environment-var';
|
|
1487
|
+
interface LinkedUpdateEntry<TBase = unknown, TTarget = unknown, TOverride = unknown> {
|
|
1488
|
+
bucket: LinkedUpdateBucket;
|
|
1489
|
+
/** Identifier scoped to the bucket. For env-var, format `<envName>:<varKey>`. */
|
|
1490
|
+
key: string;
|
|
1491
|
+
label: string;
|
|
1492
|
+
status: LinkedUpdateStatus;
|
|
1493
|
+
base: TBase | null;
|
|
1494
|
+
target: TTarget | null;
|
|
1495
|
+
override: TOverride | null;
|
|
1496
|
+
}
|
|
1497
|
+
interface LinkedUpdatePreview {
|
|
1498
|
+
fromVersion: string | null;
|
|
1499
|
+
toVersion: string;
|
|
1500
|
+
entries: LinkedUpdateEntry[];
|
|
1501
|
+
/** Quick counts for the modal summary line. */
|
|
1502
|
+
summary: Record<LinkedUpdateStatus, number>;
|
|
1503
|
+
}
|
|
1504
|
+
interface PreviewArgs {
|
|
1505
|
+
fromVersion: string | null;
|
|
1506
|
+
toVersion: string;
|
|
1507
|
+
base: LinkedSnapshot | null;
|
|
1508
|
+
target: LinkedSnapshot;
|
|
1509
|
+
/** All request overrides keyed by `${linkedWorkspaceId}:${itemId}` — caller pre-filters to one link. */
|
|
1510
|
+
requestOverrides: RequestOverride[];
|
|
1511
|
+
/** All env-var overrides for this link. */
|
|
1512
|
+
envVarOverrides: EnvironmentVariableOverride[];
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Pure function — returns a structured preview of every change between
|
|
1516
|
+
* `base` and `target`, classified by status and annotated with the
|
|
1517
|
+
* consumer's overrides where applicable. Caller renders the modal and
|
|
1518
|
+
* collects resolutions for `both-changed` entries.
|
|
1519
|
+
*/
|
|
1520
|
+
declare function previewLinkedUpdate(args: PreviewArgs): LinkedUpdatePreview;
|
|
1521
|
+
/**
|
|
1522
|
+
* Map status → 'mine' | 'theirs' for entries the user has resolved.
|
|
1523
|
+
* 'mine' = keep the override / orphan. 'theirs' = adopt source.
|
|
1524
|
+
*
|
|
1525
|
+
* `source-only`, `new-in-source`, and `local-only` don't need a
|
|
1526
|
+
* resolution (auto-applied), so this map is keyed only by entries that
|
|
1527
|
+
* are `both-changed` or `removed-in-source` (the latter optionally lets
|
|
1528
|
+
* the user keep their override as a consumer-only request — a rarer
|
|
1529
|
+
* choice).
|
|
1530
|
+
*/
|
|
1531
|
+
type LinkedUpdateResolutionMap = Record<string, 'mine' | 'theirs'>;
|
|
1532
|
+
interface ApplyArgs {
|
|
1533
|
+
base: LinkedSnapshot | null;
|
|
1534
|
+
target: LinkedSnapshot;
|
|
1535
|
+
preview: LinkedUpdatePreview;
|
|
1536
|
+
resolutions: LinkedUpdateResolutionMap;
|
|
1537
|
+
/** All overrides for this link, BEFORE the apply. */
|
|
1538
|
+
requestOverrides: RequestOverride[];
|
|
1539
|
+
envVarOverrides: EnvironmentVariableOverride[];
|
|
1540
|
+
}
|
|
1541
|
+
interface ApplyResult {
|
|
1542
|
+
/** New canonical snapshot to cache (replaces base). */
|
|
1543
|
+
nextSnapshot: LinkedSnapshot;
|
|
1544
|
+
/** Override entries the consumer keeps after applying. */
|
|
1545
|
+
nextRequestOverrides: RequestOverride[];
|
|
1546
|
+
nextEnvVarOverrides: EnvironmentVariableOverride[];
|
|
1547
|
+
/** Per-entry record of what we did, surfaced to the toast / activity log. */
|
|
1548
|
+
log: Array<{
|
|
1549
|
+
entryKey: string;
|
|
1550
|
+
bucket: LinkedUpdateBucket;
|
|
1551
|
+
action: string;
|
|
1552
|
+
}>;
|
|
1553
|
+
}
|
|
1554
|
+
/**
|
|
1555
|
+
* Apply a fully-resolved preview. Pure — does not touch IDB or the store.
|
|
1556
|
+
*
|
|
1557
|
+
* Throws when any `both-changed` entry is missing a resolution.
|
|
1558
|
+
*/
|
|
1559
|
+
declare function applyLinkedUpdate(args: ApplyArgs): ApplyResult;
|
|
1560
|
+
|
|
1561
|
+
interface ApplyMutationOptions {
|
|
1562
|
+
/** ISO timestamp to stamp into `updatedAt`. Defaults to the current time. */
|
|
1563
|
+
now?: string;
|
|
1564
|
+
}
|
|
1565
|
+
interface ApplyMutationResult {
|
|
1566
|
+
next: WorkspaceState;
|
|
1567
|
+
changedIds: string[];
|
|
1568
|
+
}
|
|
1569
|
+
declare function applyMutation(state: WorkspaceState, patch: WorkspacePatch, options?: ApplyMutationOptions): ApplyMutationResult;
|
|
1570
|
+
|
|
1571
|
+
/**
|
|
1572
|
+
* Best-known identity of whoever launched a plan run. Recorded for display
|
|
1573
|
+
* and handed to the `authorize` hook. `unknown` is the headless default when
|
|
1574
|
+
* no GitHub session or OS user can be determined.
|
|
1575
|
+
*/
|
|
1576
|
+
interface RunActor {
|
|
1577
|
+
kind: 'github' | 'os' | 'unknown';
|
|
1578
|
+
/** GitHub login, OS username, or 'unknown'. */
|
|
1579
|
+
name: string;
|
|
1580
|
+
}
|
|
1581
|
+
declare const ANONYMOUS_ACTOR: RunActor;
|
|
1582
|
+
/**
|
|
1583
|
+
* Thrown by an `authorize` hook to deny a run. `runPlan` lets it propagate
|
|
1584
|
+
* untouched so callers (the CLI) can map it to a distinct exit code. Today no
|
|
1585
|
+
* built-in hook throws it — it exists for the per-user run restrictions that
|
|
1586
|
+
* are planned but not yet designed.
|
|
1587
|
+
*/
|
|
1588
|
+
declare class PlanRunDeniedError extends Error {
|
|
1589
|
+
constructor(message: string);
|
|
1590
|
+
}
|
|
1591
|
+
/** Context handed to the `authorize` hook before any HTTP request fires. */
|
|
1592
|
+
interface PlanRunAuthorizationContext {
|
|
1593
|
+
planId: string;
|
|
1594
|
+
plan: ExecutionPlan;
|
|
1595
|
+
actor: RunActor;
|
|
1596
|
+
state: WorkspaceState;
|
|
1597
|
+
}
|
|
1598
|
+
interface RunPlanOptions {
|
|
1599
|
+
/** Evaluate the per-request assertions. Defaults to `true`. */
|
|
1600
|
+
withAssertions?: boolean;
|
|
1601
|
+
/**
|
|
1602
|
+
* Halt the run after the first failed step — including missing / linked
|
|
1603
|
+
* steps — regardless of the plan's own `stopOnAssertionFailure`. This is
|
|
1604
|
+
* the `apicircle run --bail` behaviour. Defaults to `false`.
|
|
1605
|
+
*/
|
|
1606
|
+
bail?: boolean;
|
|
1607
|
+
/**
|
|
1608
|
+
* Name of a local environment to layer on top of the run's env priority
|
|
1609
|
+
* order (highest precedence). Used by `apicircle run --env <name>`. A name
|
|
1610
|
+
* with no matching environment simply contributes nothing.
|
|
1611
|
+
*/
|
|
1612
|
+
env?: string;
|
|
1613
|
+
/** Injected fetch — defaults to `globalThis.fetch`. Tests pass a stub. */
|
|
1614
|
+
fetchImpl?: typeof fetch;
|
|
1615
|
+
/** Aborts the run between steps and the in-flight request. */
|
|
1616
|
+
signal?: AbortSignal;
|
|
1617
|
+
/** Per-request hard timeout in ms. `null` disables. Defaults to executeRequest's 30s. */
|
|
1618
|
+
timeoutMs?: number | null;
|
|
1619
|
+
/** Plaintext secret values keyed by `secretKeyId`, for encrypted env vars. */
|
|
1620
|
+
secretsById?: Record<string, string>;
|
|
1621
|
+
/** Identity of whoever launched the run. Defaults to {@link ANONYMOUS_ACTOR}. */
|
|
1622
|
+
actor?: RunActor;
|
|
1623
|
+
/**
|
|
1624
|
+
* Authorization seam. Called once, before the first request, with the
|
|
1625
|
+
* resolved plan + actor. Throw (ideally {@link PlanRunDeniedError}) to deny
|
|
1626
|
+
* the run. Omit for an unrestricted run — the current default everywhere.
|
|
1627
|
+
*/
|
|
1628
|
+
authorize?: (ctx: PlanRunAuthorizationContext) => void | Promise<void>;
|
|
1629
|
+
/** Invoked after each step settles — lets a CLI stream progress live. */
|
|
1630
|
+
onStep?: (step: PlanStepResult) => void;
|
|
1631
|
+
}
|
|
1632
|
+
interface PlanStepResult {
|
|
1633
|
+
/** Index into `plan.steps` — stable even when steps are skipped. */
|
|
1634
|
+
stepIndex: number;
|
|
1635
|
+
requestId: string;
|
|
1636
|
+
requestName: string;
|
|
1637
|
+
requestMethod: string;
|
|
1638
|
+
/** True when the step was skipped via `enabled: false`. */
|
|
1639
|
+
skipped: boolean;
|
|
1640
|
+
/** Execution result, or `null` for a skipped / unresolvable step. */
|
|
1641
|
+
result: ExecutionResult | null;
|
|
1642
|
+
assertionResults: AssertionResult[];
|
|
1643
|
+
/** `{{VAR}}` placeholders that didn't resolve in url / headers / query / body / auth. */
|
|
1644
|
+
missingVariables: string[];
|
|
1645
|
+
/** True when the request succeeded and (if enabled) every assertion passed. */
|
|
1646
|
+
passed: boolean;
|
|
1647
|
+
/** Set when the step couldn't run at all (missing / linked / unsupported). */
|
|
1648
|
+
error?: string;
|
|
1649
|
+
}
|
|
1650
|
+
interface RunPlanResult {
|
|
1651
|
+
planRun: PlanRun;
|
|
1652
|
+
/** One entry per step, including skipped ones (in `plan.steps` order). */
|
|
1653
|
+
steps: PlanStepResult[];
|
|
1654
|
+
/**
|
|
1655
|
+
* Workspace with the plan-run + request-runs appended to history and any
|
|
1656
|
+
* refreshed OAuth2 tokens persisted onto `synced`. Save this back to disk.
|
|
1657
|
+
*/
|
|
1658
|
+
nextState: WorkspaceState;
|
|
1659
|
+
/** True when every executed (non-skipped) step passed. Vacuously true when none ran. */
|
|
1660
|
+
passed: boolean;
|
|
1661
|
+
}
|
|
1662
|
+
type ResolvePlanRefResult = {
|
|
1663
|
+
ok: true;
|
|
1664
|
+
id: string;
|
|
1665
|
+
plan: ExecutionPlan;
|
|
1666
|
+
} | {
|
|
1667
|
+
ok: false;
|
|
1668
|
+
error: string;
|
|
1669
|
+
available: string[];
|
|
1670
|
+
};
|
|
1671
|
+
/**
|
|
1672
|
+
* Resolve a user-supplied plan reference (a plan id, or a plan name) against a
|
|
1673
|
+
* workspace. Name matching is case-insensitive and trimmed; an ambiguous name
|
|
1674
|
+
* (two plans share it) is rejected so the caller can ask for an id instead.
|
|
1675
|
+
*/
|
|
1676
|
+
declare function resolvePlanRef(synced: WorkspaceSynced, ref: string): ResolvePlanRefResult;
|
|
1677
|
+
/**
|
|
1678
|
+
* Execute every enabled step of `planId` against the workspace. Never throws
|
|
1679
|
+
* for HTTP / assertion failures — those land in the returned step results.
|
|
1680
|
+
* Throws only for a missing plan or a denial from the `authorize` hook.
|
|
1681
|
+
*/
|
|
1682
|
+
declare function runPlan(state: WorkspaceState, planId: string, opts?: RunPlanOptions): Promise<RunPlanResult>;
|
|
1683
|
+
|
|
1684
|
+
/**
|
|
1685
|
+
* Token Oriented Object Notation (TOON) encoder.
|
|
1686
|
+
*
|
|
1687
|
+
* Compact, indentation-based serialization that drops most of JSON's
|
|
1688
|
+
* structural noise (quotes around keys/values where unambiguous, braces,
|
|
1689
|
+
* commas, colons-with-spaces). Optimized for LLM token budgets and
|
|
1690
|
+
* eyeballing — typical JSON shrinks 25–50% when re-encoded.
|
|
1691
|
+
*
|
|
1692
|
+
* Two encoding shapes are produced:
|
|
1693
|
+
*
|
|
1694
|
+
* - **Tabular** for arrays of homogeneous flat objects (common API list
|
|
1695
|
+
* payloads). The header lists keys once, the rows list values once:
|
|
1696
|
+
*
|
|
1697
|
+
* users[2]{id,name,active}:
|
|
1698
|
+
* 1,Alice,true
|
|
1699
|
+
* 2,Bob,false
|
|
1700
|
+
*
|
|
1701
|
+
* - **Indented** for everything else:
|
|
1702
|
+
*
|
|
1703
|
+
* meta:
|
|
1704
|
+
* page: 1
|
|
1705
|
+
* items:
|
|
1706
|
+
* - id: 1
|
|
1707
|
+
* name: Alice
|
|
1708
|
+
*
|
|
1709
|
+
* Strings are quoted only when they contain characters that would otherwise
|
|
1710
|
+
* break the line shape (commas, colons, leading/trailing whitespace, etc.).
|
|
1711
|
+
* Output is intentionally lossless for round-tripping primitive types — but
|
|
1712
|
+
* a TOON decoder is out of scope for this module: the encoder exists so the
|
|
1713
|
+
* UI can show an "X% smaller" hint and an optional preview.
|
|
1714
|
+
*/
|
|
1715
|
+
type Json$2 = string | number | boolean | null | Json$2[] | {
|
|
1716
|
+
[key: string]: Json$2;
|
|
1717
|
+
};
|
|
1718
|
+
declare function toToon(value: Json$2): string;
|
|
1719
|
+
|
|
1720
|
+
/**
|
|
1721
|
+
* Minimal block-style YAML encoder. Sibling of `toon.ts` — same job
|
|
1722
|
+
* (compact, indentation-based representation of JSON-shaped data) but
|
|
1723
|
+
* sticks to standard YAML syntax instead of TOON's tabular shorthand,
|
|
1724
|
+
* so the user can compare the two.
|
|
1725
|
+
*
|
|
1726
|
+
* For arrays of homogeneous flat objects YAML still emits one list item
|
|
1727
|
+
* per row (unlike TOON's `name[count]{cols}: rows` table), so YAML is
|
|
1728
|
+
* usually slightly larger than TOON on tabular payloads but identical or
|
|
1729
|
+
* very close on nested ones. Keeping both gives the user the full picture
|
|
1730
|
+
* when deciding which format to feed downstream.
|
|
1731
|
+
*/
|
|
1732
|
+
type Json$1 = string | number | boolean | null | Json$1[] | {
|
|
1733
|
+
[key: string]: Json$1;
|
|
1734
|
+
};
|
|
1735
|
+
declare function toYaml(value: Json$1): string;
|
|
1736
|
+
|
|
1737
|
+
/**
|
|
1738
|
+
* Minimal RFC 4180-ish CSV encoder, used purely as a "what if you sent
|
|
1739
|
+
* this as CSV" savings preview. Returns null when the input isn't an
|
|
1740
|
+
* array of homogeneous flat objects — CSV only makes sense for tabular
|
|
1741
|
+
* data, and forcing it on nested JSON would either lose information or
|
|
1742
|
+
* inflate the payload, defeating the point of the savings hint.
|
|
1743
|
+
*/
|
|
1744
|
+
type Json = string | number | boolean | null | Json[] | {
|
|
1745
|
+
[key: string]: Json;
|
|
1746
|
+
};
|
|
1747
|
+
declare function toCsv(value: Json): string | null;
|
|
1748
|
+
|
|
1749
|
+
/**
|
|
1750
|
+
* Output formats we measure savings for. Minification is NOT in this
|
|
1751
|
+
* list on purpose — stripping whitespace from pretty-printed JSON isn't
|
|
1752
|
+
* a "transformation", it's the wire-effective baseline most APIs already
|
|
1753
|
+
* send. Comparing TOON/YAML/CSV against pretty JSON would inflate the
|
|
1754
|
+
* apparent savings; we always normalize to minified JSON first.
|
|
1755
|
+
*/
|
|
1756
|
+
type TransformFormat = 'toon' | 'yaml' | 'csv';
|
|
1757
|
+
interface TransformCandidate {
|
|
1758
|
+
format: TransformFormat;
|
|
1759
|
+
/** Encoded payload. Available so the UI can offer "view" / "copy". */
|
|
1760
|
+
preview: string;
|
|
1761
|
+
/** UTF-8 bytes of `preview`. */
|
|
1762
|
+
bytes: number;
|
|
1763
|
+
/**
|
|
1764
|
+
* Bytes saved vs `minifiedBytes` (the wire baseline), expressed as a
|
|
1765
|
+
* percentage with one decimal. Clamped at 0 — candidates that don't
|
|
1766
|
+
* beat the baseline are dropped from `candidates` entirely.
|
|
1767
|
+
*/
|
|
1768
|
+
percentSaved: number;
|
|
1769
|
+
}
|
|
1770
|
+
interface TransformSavings {
|
|
1771
|
+
/** UTF-8 bytes of the body as received (may be pretty-printed). */
|
|
1772
|
+
originalBytes: number;
|
|
1773
|
+
/**
|
|
1774
|
+
* UTF-8 bytes of the same body re-emitted as compact JSON. This is
|
|
1775
|
+
* the honest wire-baseline — most APIs already send minified JSON,
|
|
1776
|
+
* and any "transformation savings" should be measured against that,
|
|
1777
|
+
* not against a verbose pretty-printed version. When `originalBytes ===
|
|
1778
|
+
* minifiedBytes`, the wire body was already compact; when they differ,
|
|
1779
|
+
* the UI can surface that delta separately as a "minify only" tip
|
|
1780
|
+
* without mixing it into transformation savings.
|
|
1781
|
+
*/
|
|
1782
|
+
minifiedBytes: number;
|
|
1783
|
+
/** Sorted by percentSaved descending. Empty when nothing beats minified. */
|
|
1784
|
+
candidates: TransformCandidate[];
|
|
1785
|
+
}
|
|
1786
|
+
/**
|
|
1787
|
+
* Compute savings candidates for a response body. Only JSON-shaped
|
|
1788
|
+
* content is inspected — binary, plain text, and HTML return an empty
|
|
1789
|
+
* candidate list. Pure, no side effects.
|
|
1790
|
+
*
|
|
1791
|
+
* Baselines:
|
|
1792
|
+
* - `originalBytes` : received-as-is. What the editor is currently rendering.
|
|
1793
|
+
* - `minifiedBytes` : what the wire would have carried with whitespace stripped.
|
|
1794
|
+
* - `candidates[].percentSaved` : measured against `minifiedBytes`. So a
|
|
1795
|
+
* "20% smaller as TOON" claim means TOON beats compact JSON by 20%,
|
|
1796
|
+
* not that it beats pretty JSON by 20%.
|
|
1797
|
+
*/
|
|
1798
|
+
declare function computeTransformSavings(body: string, contentType?: string): TransformSavings;
|
|
1799
|
+
declare const TRANSFORM_FORMAT_LABELS: Record<TransformFormat, string>;
|
|
1800
|
+
|
|
1801
|
+
export { ANONYMOUS_ACTOR, type ApplyMutationOptions, type ApplyMutationResult, type AssertionResult, type AttachmentResolver, type AttachmentSlotRef, type AuthApplyOptions, type AuthApplyResult, type AuthApplyTarget, type AuthApplyWarning, type AuthCodeExchangeArgs, type AutoHeaderOverrides, type BranchNameOptions, type BuildDigestArgs, type BuildNtlmType3Args, type BuildRequestOptions, type BuiltRequest, type ClientCredentialsArgs, type ConflictResolution, type HeaderEntry$1 as ContentTypeHeaderEntry, type ContextExtractionResult, DESKTOP_APP_ORIGIN, type DeviceAuthorizationArgs, type DeviceAuthorizationResponse, type DiffEntry, type DiffStatus, type DigestChallenge, EMPTY_UNPUSHED_SUMMARY, type EncryptedPayload, type EntityBucket, type ExecuteOptions, type ExecutionResult, type FetchOAuth2TokenArgs, type GraphQLField, type GraphQLSchemaInfo, HTTP_HEADERS_MAP, type HawkSignArgs, type HeaderEntry, type HeaderSuggestionMode, type ImportedFolder, type ImportedRequest, type JwtAlgorithm, type JwtSignArgs, type ApplyArgs as LinkedApplyArgs, type ApplyResult as LinkedApplyResult, type PreviewArgs as LinkedPreviewArgs, type LinkedUpdateBucket, type LinkedUpdateEntry, type LinkedUpdatePreview, type LinkedUpdateResolutionMap, type LinkedUpdateStatus, type MonacoLanguage, type NtlmType2Challenge, type OAuth2ErrorResponse, OAuth2TokenError, type OAuth2TokenResponse, type ParsedCurl, type ParsedPostmanCollection, type ParsedPostmanEnvironment, type ParsedVersion, type PkceExchangeArgs, type PkceMethod, type PlanRunAuthorizationContext, PlanRunDeniedError, type PlanStepResult, type PollDeviceFlowArgs, type PreSendBlocker, type PreSendValidationInput, type PreSendValidationResult, type PreSendWarning, type PublishReleaseArgs, type RefreshTokenArgs, RemoteWorkspaceParseError, type ResolutionMap, type ResolutionScope, type ResolveInheritedAuthArgs, type ResolvePlanRefResult, type ResolveResult, type RopcArgs, type RunActor, type RunPlanOptions, type RunPlanResult, type SigV4SignArgs, type SigV4SignResult, TRANSFORM_FORMAT_LABELS, type ThreeWayDiff, type TransformCandidate, type TransformFormat, type TransformSavings, type UnpushedChange, type UnpushedSummary, type VariableSource, type VariableSuggestion, WorkspacePatch, WorkspaceState, applyAuth, applyAwsSigV4, applyContentTypeForBodyType, applyLinkedUpdate, applyMerge, applyMutation, applyPathParams, assertNoPlaintextCredentials, buildAuthorizeUrl, buildAutoHeaders, buildDigestAuthHeader, buildHawkAuthHeader, buildNtlmType1Negotiate, buildNtlmType3Authenticate, buildRequest, buildScope, collectAttachmentSlots, collectVariableSuggestions, compareSemver, composeBody, composeCookieHeader, composeHeaders, composeUrl, composeUrlWithQuery, computeCodeChallenge, computeThreeWayDiff, computeTransformSavings, decryptString, deprecateRelease, deriveKeyFromSlotValue, encryptString, exchangeAuthCode, exchangePkce, executeRequest, exportKey, extractContext, fetchOAuth2Token, findPathPlaceholders, generateAesKey, generateCodeVerifier, generateSlotSalt, generateSpanId, generateTraceParent, generateWorkingBranchName, getBodyTypeForContentType, getContentTypeForBodyType, getHeaderEntry, getHeaderValues, getLanguageFromBodyType, getLanguageFromContentType, getVariableAutocomplete, hasUnpushedChanges, importKey, isDesktop, isInsomniaExport, isPostmanEnvironment, isPostmanV2Collection, isValidSemver, lookup, mergeWithAutoHeaders, normalizeContentType, parseCurl, parseDigestChallenge, parseGraphqlSchema, parseInsomniaCollection, parseNtlmType2Challenge, parsePostmanCollection, parsePostmanEnvironment, parseSemver, parseUrlQuery, parseWorkspaceJson, pollDeviceFlow, preSendValidation, previewLinkedUpdate, publishRelease, readJsonPath, redactForGit, refreshToken, requestDeviceAuthorization, requestRunToExecutionResult, resolveInheritedAuth, resolvePlanRef, resolveString, resolveStringMap, runAssertions, runClientCredentials, runPlan, runRopc, serializePayload, serializeWorkspaceForGit, signJwt, slugify, sortVersionsDesc, suggestHeaders, summarizeUnpushedChanges, supportedContentTypeLanguageMap, toCsv, toToon, toYaml, tokenizeCurl, tryParsePayload, validateBranchName, yankRelease };
|