api-invoke 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,2430 @@
1
+ 'use strict';
2
+
3
+ var SwaggerParser = require('@apidevtools/swagger-parser');
4
+
5
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
+
7
+ var SwaggerParser__default = /*#__PURE__*/_interopDefault(SwaggerParser);
8
+
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __esm = (fn, res) => function __init() {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+
19
+ // src/core/types.ts
20
+ exports.HttpMethod = void 0; exports.ParamLocation = void 0; exports.AuthType = void 0; exports.SpecFormat = void 0; exports.HeaderName = void 0; exports.ContentType = void 0;
21
+ var init_types = __esm({
22
+ "src/core/types.ts"() {
23
+ exports.HttpMethod = {
24
+ GET: "GET",
25
+ POST: "POST",
26
+ PUT: "PUT",
27
+ PATCH: "PATCH",
28
+ DELETE: "DELETE",
29
+ HEAD: "HEAD",
30
+ OPTIONS: "OPTIONS"
31
+ };
32
+ exports.ParamLocation = {
33
+ PATH: "path",
34
+ QUERY: "query",
35
+ HEADER: "header",
36
+ COOKIE: "cookie"
37
+ };
38
+ exports.AuthType = {
39
+ BEARER: "bearer",
40
+ BASIC: "basic",
41
+ API_KEY: "apiKey",
42
+ QUERY_PARAM: "queryParam",
43
+ OAUTH2: "oauth2",
44
+ COOKIE: "cookie"
45
+ };
46
+ exports.SpecFormat = {
47
+ OPENAPI_3: "openapi-3",
48
+ OPENAPI_2: "openapi-2",
49
+ RAW_URL: "raw-url",
50
+ MANUAL: "manual",
51
+ GRAPHQL: "graphql"
52
+ };
53
+ exports.HeaderName = {
54
+ ACCEPT: "Accept",
55
+ AUTHORIZATION: "Authorization",
56
+ CONTENT_TYPE: "Content-Type",
57
+ COOKIE: "Cookie"
58
+ };
59
+ exports.ContentType = {
60
+ JSON: "application/json",
61
+ FORM_URLENCODED: "application/x-www-form-urlencoded",
62
+ MULTIPART: "multipart/form-data",
63
+ XML: "application/xml",
64
+ OCTET_STREAM: "application/octet-stream",
65
+ TEXT: "text/plain",
66
+ SSE: "text/event-stream"
67
+ };
68
+ }
69
+ });
70
+
71
+ // src/core/errors.ts
72
+ function corsError(url) {
73
+ return new exports.ApiInvokeError({
74
+ kind: exports.ErrorKind.CORS,
75
+ message: `Cannot access ${url} \u2014 blocked by CORS policy.`,
76
+ suggestion: "This API does not allow browser requests. Use a CORS proxy or server-side execution.",
77
+ retryable: false
78
+ });
79
+ }
80
+ function networkError(url) {
81
+ return new exports.ApiInvokeError({
82
+ kind: exports.ErrorKind.NETWORK,
83
+ message: `Network error while fetching ${url}.`,
84
+ suggestion: "Check your internet connection and verify the URL is correct.",
85
+ retryable: true
86
+ });
87
+ }
88
+ function authError(url, status, responseBody) {
89
+ return new exports.ApiInvokeError({
90
+ kind: exports.ErrorKind.AUTH,
91
+ message: status === 401 ? `Authentication failed for ${url} (401)` : `Authorization denied for ${url} (403)`,
92
+ suggestion: status === 401 ? "Check your credentials. The server rejected your authentication." : "Your credentials are valid but you lack permission for this resource.",
93
+ retryable: false,
94
+ status,
95
+ responseBody
96
+ });
97
+ }
98
+ function httpError(url, status, statusText, responseBody) {
99
+ const retryable = status === 429 || status >= 500;
100
+ const kind = status === 429 ? exports.ErrorKind.RATE_LIMIT : exports.ErrorKind.HTTP;
101
+ let suggestion;
102
+ if (status === 404) {
103
+ suggestion = "The endpoint was not found. Check the URL path.";
104
+ } else if (status === 429) {
105
+ suggestion = "Rate limited. Wait and retry.";
106
+ } else if (status >= 500) {
107
+ suggestion = "The API server is having issues. Try again later.";
108
+ } else {
109
+ suggestion = `The API returned an error (${status}). Verify the request.`;
110
+ }
111
+ return new exports.ApiInvokeError({
112
+ kind,
113
+ message: `API returned ${status} ${statusText} for ${url}.`,
114
+ suggestion,
115
+ retryable,
116
+ status,
117
+ responseBody
118
+ });
119
+ }
120
+ function parseError(url, expectedType = "JSON") {
121
+ return new exports.ApiInvokeError({
122
+ kind: exports.ErrorKind.PARSE,
123
+ message: `Failed to parse response from ${url} as ${expectedType}.`,
124
+ suggestion: `The API response body could not be read as ${expectedType}. Verify the endpoint returns the expected content type.`,
125
+ retryable: false
126
+ });
127
+ }
128
+ function graphqlError(messages, status, responseBody) {
129
+ return new exports.ApiInvokeError({
130
+ kind: exports.ErrorKind.GRAPHQL,
131
+ message: `GraphQL errors: ${messages}`,
132
+ suggestion: "Check the query and variables for correctness.",
133
+ retryable: false,
134
+ status,
135
+ responseBody
136
+ });
137
+ }
138
+ function timeoutError(url) {
139
+ return new exports.ApiInvokeError({
140
+ kind: exports.ErrorKind.TIMEOUT,
141
+ message: `Request to ${url} timed out.`,
142
+ suggestion: "The server took too long to respond. Try again or increase the timeout.",
143
+ retryable: true
144
+ });
145
+ }
146
+ exports.ErrorKind = void 0; exports.API_INVOKE_ERROR_NAME = void 0; exports.ApiInvokeError = void 0;
147
+ var init_errors = __esm({
148
+ "src/core/errors.ts"() {
149
+ exports.ErrorKind = {
150
+ CORS: "cors",
151
+ NETWORK: "network",
152
+ AUTH: "auth",
153
+ HTTP: "http",
154
+ PARSE: "parse",
155
+ RATE_LIMIT: "rate-limit",
156
+ TIMEOUT: "timeout",
157
+ GRAPHQL: "graphql"
158
+ };
159
+ exports.API_INVOKE_ERROR_NAME = "ApiInvokeError";
160
+ exports.ApiInvokeError = class extends Error {
161
+ /** Error classification for programmatic handling. */
162
+ kind;
163
+ /** HTTP status code, if the error originated from an HTTP response. */
164
+ status;
165
+ /** Human-readable suggestion for how to resolve this error. */
166
+ suggestion;
167
+ /** Whether retrying the request might succeed (e.g. true for rate limits, network errors). */
168
+ retryable;
169
+ /** Response body from the API (when available). May be parsed JSON, a string, or binary data depending on the response content type. */
170
+ responseBody;
171
+ constructor(opts) {
172
+ super(opts.message);
173
+ this.name = exports.API_INVOKE_ERROR_NAME;
174
+ this.kind = opts.kind;
175
+ this.suggestion = opts.suggestion;
176
+ this.retryable = opts.retryable ?? false;
177
+ this.status = opts.status;
178
+ this.responseBody = opts.responseBody;
179
+ }
180
+ };
181
+ }
182
+ });
183
+
184
+ // src/adapters/graphql/introspection.ts
185
+ var TypeKind, INTROSPECTION_QUERY;
186
+ var init_introspection = __esm({
187
+ "src/adapters/graphql/introspection.ts"() {
188
+ TypeKind = {
189
+ SCALAR: "SCALAR",
190
+ OBJECT: "OBJECT",
191
+ ENUM: "ENUM",
192
+ INPUT_OBJECT: "INPUT_OBJECT",
193
+ NON_NULL: "NON_NULL",
194
+ LIST: "LIST",
195
+ UNION: "UNION",
196
+ INTERFACE: "INTERFACE"
197
+ };
198
+ INTROSPECTION_QUERY = `query IntrospectionQuery {
199
+ __schema {
200
+ queryType { name }
201
+ mutationType { name }
202
+ subscriptionType { name }
203
+ types {
204
+ kind name description
205
+ fields(includeDeprecated: false) {
206
+ name description
207
+ args { name description type { ...TypeRef } defaultValue }
208
+ type { ...TypeRef }
209
+ }
210
+ inputFields { name description type { ...TypeRef } defaultValue }
211
+ enumValues(includeDeprecated: false) { name description }
212
+ }
213
+ }
214
+ }
215
+ fragment TypeRef on __Type {
216
+ kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } } } }
217
+ }`;
218
+ }
219
+ });
220
+
221
+ // src/adapters/graphql/query-builder.ts
222
+ function formatTypeRef(ref) {
223
+ if (ref.kind === TypeKind.NON_NULL) {
224
+ return ref.ofType ? `${formatTypeRef(ref.ofType)}!` : "unknown!";
225
+ }
226
+ if (ref.kind === TypeKind.LIST) {
227
+ return ref.ofType ? `[${formatTypeRef(ref.ofType)}]` : "[unknown]";
228
+ }
229
+ return ref.name ?? "unknown";
230
+ }
231
+ function unwrapType(ref) {
232
+ let current = ref;
233
+ while ((current.kind === TypeKind.NON_NULL || current.kind === TypeKind.LIST) && current.ofType) {
234
+ current = current.ofType;
235
+ }
236
+ return current;
237
+ }
238
+ function isNonNull(ref) {
239
+ return ref.kind === TypeKind.NON_NULL;
240
+ }
241
+ function buildQueryString(operationType, field, typeMap, maxDepth = DEFAULT_MAX_DEPTH) {
242
+ const varDefs = buildVariableDefinitions(field.args);
243
+ const fieldArgs = buildFieldArguments(field.args);
244
+ const selection = buildSelectionSet(field.type, typeMap, maxDepth, /* @__PURE__ */ new Set());
245
+ const varPart = varDefs ? `(${varDefs})` : "";
246
+ const argPart = fieldArgs ? `(${fieldArgs})` : "";
247
+ const selPart = selection ? ` ${selection}` : "";
248
+ return `${operationType} ${field.name}${varPart} { ${field.name}${argPart}${selPart} }`;
249
+ }
250
+ function buildVariableDefinitions(args) {
251
+ if (args.length === 0) return "";
252
+ return args.map((a) => `$${a.name}: ${formatTypeRef(a.type)}`).join(", ");
253
+ }
254
+ function buildFieldArguments(args) {
255
+ if (args.length === 0) return "";
256
+ return args.map((a) => `${a.name}: $${a.name}`).join(", ");
257
+ }
258
+ function buildSelectionSet(typeRef, typeMap, depth, visited) {
259
+ const base = unwrapType(typeRef);
260
+ if (!base.name) return "";
261
+ const type = typeMap.get(base.name);
262
+ if (!type || !type.fields) return "";
263
+ if (base.kind === TypeKind.UNION || base.kind === TypeKind.INTERFACE) {
264
+ return buildUnionSelection();
265
+ }
266
+ if (base.kind !== TypeKind.OBJECT) return "";
267
+ if (visited.has(base.name)) return "";
268
+ visited.add(base.name);
269
+ const fields = [];
270
+ for (const f of type.fields) {
271
+ const fieldBase = unwrapType(f.type);
272
+ if (fieldBase.kind === TypeKind.SCALAR || fieldBase.kind === TypeKind.ENUM) {
273
+ fields.push(f.name);
274
+ } else if (depth > 0 && (fieldBase.kind === TypeKind.OBJECT || fieldBase.kind === TypeKind.UNION || fieldBase.kind === TypeKind.INTERFACE)) {
275
+ const nested = buildSelectionSet(f.type, typeMap, depth - 1, new Set(visited));
276
+ if (nested) {
277
+ fields.push(`${f.name} ${nested}`);
278
+ }
279
+ }
280
+ }
281
+ visited.delete(base.name);
282
+ if (fields.length === 0) return "";
283
+ return `{ ${fields.join(" ")} }`;
284
+ }
285
+ function buildUnionSelection(type, typeMap, depth, visited) {
286
+ return "{ __typename }";
287
+ }
288
+ var DEFAULT_MAX_DEPTH;
289
+ var init_query_builder = __esm({
290
+ "src/adapters/graphql/query-builder.ts"() {
291
+ init_introspection();
292
+ DEFAULT_MAX_DEPTH = 2;
293
+ }
294
+ });
295
+
296
+ // src/adapters/graphql/parser.ts
297
+ var parser_exports = {};
298
+ __export(parser_exports, {
299
+ parseGraphQLSchema: () => parseGraphQLSchema
300
+ });
301
+ async function parseGraphQLSchema(input, options) {
302
+ const maxDepth = options?.maxDepth ?? 2;
303
+ let schema;
304
+ let endpoint;
305
+ if (typeof input === "string") {
306
+ if (!input.startsWith("http")) {
307
+ throw new exports.ApiInvokeError({
308
+ kind: exports.ErrorKind.PARSE,
309
+ message: "GraphQL input must be an endpoint URL (starting with http) or an introspection JSON object. SDL parsing is not yet supported.",
310
+ suggestion: 'Pass a URL like "https://api.example.com/graphql" or an introspection result object.',
311
+ retryable: false
312
+ });
313
+ }
314
+ endpoint = options?.endpoint ?? input;
315
+ schema = await fetchIntrospection(input, options?.fetch);
316
+ } else {
317
+ schema = extractSchema(input);
318
+ endpoint = options?.endpoint ?? "/graphql";
319
+ }
320
+ const path = extractPath(endpoint);
321
+ const typeMap = buildTypeMap(schema.types);
322
+ const operations = [];
323
+ if (schema.queryType?.name) {
324
+ const queryType = typeMap.get(schema.queryType.name);
325
+ if (queryType?.fields) {
326
+ for (const field of queryType.fields) {
327
+ operations.push(buildOperation(field, "query", path, typeMap, maxDepth));
328
+ }
329
+ }
330
+ }
331
+ if (schema.mutationType?.name) {
332
+ const mutationType = typeMap.get(schema.mutationType.name);
333
+ if (mutationType?.fields) {
334
+ for (const field of mutationType.fields) {
335
+ operations.push(buildOperation(field, "mutation", path, typeMap, maxDepth));
336
+ }
337
+ }
338
+ }
339
+ if (schema.subscriptionType?.name) {
340
+ const subType = typeMap.get(schema.subscriptionType.name);
341
+ if (subType?.fields) {
342
+ for (const field of subType.fields) {
343
+ operations.push(buildOperation(field, "subscription", path, typeMap, maxDepth));
344
+ }
345
+ }
346
+ }
347
+ const title = extractTitle(endpoint);
348
+ return {
349
+ title,
350
+ version: "1.0.0",
351
+ baseUrl: extractBaseUrl(endpoint),
352
+ operations,
353
+ authSchemes: [],
354
+ specFormat: exports.SpecFormat.GRAPHQL
355
+ };
356
+ }
357
+ async function fetchIntrospection(url, fetchFn) {
358
+ const doFetch = fetchFn ?? globalThis.fetch;
359
+ let response;
360
+ try {
361
+ response = await doFetch(url, {
362
+ method: "POST",
363
+ headers: { "Content-Type": "application/json" },
364
+ body: JSON.stringify({ query: INTROSPECTION_QUERY })
365
+ });
366
+ } catch (err) {
367
+ throw new exports.ApiInvokeError({
368
+ kind: exports.ErrorKind.NETWORK,
369
+ message: `Failed to fetch GraphQL introspection from ${url}: ${err instanceof Error ? err.message : String(err)}`,
370
+ suggestion: "Check the endpoint URL and your network connection.",
371
+ retryable: true
372
+ });
373
+ }
374
+ if (!response.ok) {
375
+ throw new exports.ApiInvokeError({
376
+ kind: exports.ErrorKind.HTTP,
377
+ message: `GraphQL introspection failed with HTTP ${response.status} from ${url}.`,
378
+ suggestion: "Verify the endpoint supports introspection and is accessible.",
379
+ retryable: response.status >= 500,
380
+ status: response.status
381
+ });
382
+ }
383
+ let json;
384
+ const cloned = response.clone();
385
+ try {
386
+ json = await response.json();
387
+ } catch (err) {
388
+ let body;
389
+ try {
390
+ body = (await cloned.text()).slice(0, 200);
391
+ } catch {
392
+ }
393
+ throw new exports.ApiInvokeError({
394
+ kind: exports.ErrorKind.PARSE,
395
+ message: `GraphQL introspection response from ${url} is not valid JSON${err instanceof Error ? `: ${err.message}` : ""}.`,
396
+ suggestion: `The endpoint returned a non-JSON response${body ? ` (starts with: "${body}")` : ""}. Verify it is a GraphQL endpoint.`,
397
+ retryable: false
398
+ });
399
+ }
400
+ return extractSchema(json);
401
+ }
402
+ function extractSchema(obj) {
403
+ const record = obj;
404
+ if (record.__schema && typeof record.__schema === "object") {
405
+ return record.__schema;
406
+ }
407
+ if (record.data && typeof record.data === "object") {
408
+ const data = record.data;
409
+ if (data.__schema && typeof data.__schema === "object") {
410
+ return data.__schema;
411
+ }
412
+ }
413
+ throw new exports.ApiInvokeError({
414
+ kind: exports.ErrorKind.PARSE,
415
+ message: "Invalid GraphQL introspection result: expected { __schema: ... } or { data: { __schema: ... } }.",
416
+ suggestion: "Pass a valid introspection result object or a URL to a GraphQL endpoint.",
417
+ retryable: false
418
+ });
419
+ }
420
+ function buildTypeMap(types) {
421
+ const map = /* @__PURE__ */ new Map();
422
+ for (const type of types) {
423
+ map.set(type.name, type);
424
+ }
425
+ return map;
426
+ }
427
+ function extractPath(endpoint) {
428
+ try {
429
+ return new URL(endpoint).pathname;
430
+ } catch {
431
+ return "/graphql";
432
+ }
433
+ }
434
+ function extractBaseUrl(endpoint) {
435
+ try {
436
+ const url = new URL(endpoint);
437
+ return url.origin;
438
+ } catch {
439
+ return "";
440
+ }
441
+ }
442
+ function extractTitle(endpoint, _schema) {
443
+ try {
444
+ const url = new URL(endpoint);
445
+ return `GraphQL API (${url.hostname})`;
446
+ } catch {
447
+ return "GraphQL API";
448
+ }
449
+ }
450
+ function buildOperation(field, operationType, path, typeMap, maxDepth) {
451
+ const id = operationType === "mutation" ? `mutation_${field.name}` : operationType === "subscription" ? `subscription_${field.name}` : field.name;
452
+ const tags = [operationType];
453
+ const queryString = operationType !== "subscription" ? buildQueryString(operationType, field, typeMap, maxDepth) : void 0;
454
+ const requestBody = buildRequestBody(field.args, typeMap);
455
+ const responseSchema = buildResponseSchema(field.type, typeMap);
456
+ const operation = {
457
+ id,
458
+ path,
459
+ method: exports.HttpMethod.POST,
460
+ summary: field.description ?? `GraphQL ${operationType}: ${field.name}`,
461
+ parameters: [],
462
+ requestBody,
463
+ responseSchema,
464
+ tags
465
+ };
466
+ if (queryString) {
467
+ const argNames = new Set(field.args.map((a) => a.name));
468
+ operation.buildBody = (args) => {
469
+ const variables = {};
470
+ for (const [k, v] of Object.entries(args)) {
471
+ if (argNames.has(k)) variables[k] = v;
472
+ }
473
+ return { query: queryString, variables };
474
+ };
475
+ }
476
+ return operation;
477
+ }
478
+ function buildRequestBody(args, typeMap) {
479
+ if (args.length === 0) return void 0;
480
+ const properties = {};
481
+ const required = [];
482
+ for (const arg of args) {
483
+ properties[arg.name] = mapInputValueToProperty(arg, typeMap);
484
+ if (isNonNull(arg.type)) {
485
+ required.push(arg.name);
486
+ }
487
+ }
488
+ return {
489
+ required: required.length > 0,
490
+ contentType: exports.ContentType.JSON,
491
+ schema: {
492
+ type: "object",
493
+ raw: { type: "object", properties },
494
+ properties,
495
+ required: required.length > 0 ? required : void 0
496
+ }
497
+ };
498
+ }
499
+ function mapInputValueToProperty(input, typeMap) {
500
+ const base = unwrapType(input.type);
501
+ const description = input.description ? `${input.description} (${formatTypeRef(input.type)})` : formatTypeRef(input.type);
502
+ const isList = isListType(input.type);
503
+ if (base.kind === TypeKind.SCALAR) {
504
+ const mapped = mapScalarType(base.name ?? "String");
505
+ const prop = {
506
+ type: isList ? "array" : mapped.type,
507
+ description
508
+ };
509
+ if (mapped.format) prop.format = mapped.format;
510
+ if (input.defaultValue != null) prop.default = input.defaultValue;
511
+ return prop;
512
+ }
513
+ if (base.kind === TypeKind.ENUM) {
514
+ const enumType = typeMap.get(base.name ?? "");
515
+ const enumValues = enumType?.enumValues?.map((e) => e.name);
516
+ return {
517
+ type: isList ? "array" : "string",
518
+ description,
519
+ enum: enumValues
520
+ };
521
+ }
522
+ if (base.kind === TypeKind.INPUT_OBJECT) {
523
+ return {
524
+ type: isList ? "array" : "object",
525
+ description,
526
+ nested: true
527
+ };
528
+ }
529
+ return { type: "string", description };
530
+ }
531
+ function isListType(ref) {
532
+ let current = ref;
533
+ while (current) {
534
+ if (current.kind === TypeKind.LIST) return true;
535
+ current = current.ofType;
536
+ }
537
+ return false;
538
+ }
539
+ function mapScalarType(name) {
540
+ switch (name) {
541
+ case "String":
542
+ case "ID":
543
+ return { type: "string" };
544
+ case "Int":
545
+ return { type: "integer" };
546
+ case "Float":
547
+ return { type: "number" };
548
+ case "Boolean":
549
+ return { type: "boolean" };
550
+ default:
551
+ return { type: "string", format: name.toLowerCase() };
552
+ }
553
+ }
554
+ function buildResponseSchema(typeRef, typeMap) {
555
+ const base = unwrapType(typeRef);
556
+ if (!base.name) return void 0;
557
+ if (base.kind === TypeKind.SCALAR) {
558
+ const mapped = mapScalarType(base.name);
559
+ return { type: mapped.type, format: mapped.format };
560
+ }
561
+ if (base.kind === TypeKind.ENUM) {
562
+ const enumType = typeMap.get(base.name);
563
+ return { type: "string", enum: enumType?.enumValues?.map((e) => e.name) };
564
+ }
565
+ if (base.kind === TypeKind.OBJECT) {
566
+ const objType = typeMap.get(base.name);
567
+ if (!objType?.fields) return { type: "object" };
568
+ const properties = {};
569
+ for (const f of objType.fields) {
570
+ const fieldBase = unwrapType(f.type);
571
+ if (fieldBase.kind === TypeKind.SCALAR) {
572
+ properties[f.name] = mapScalarType(fieldBase.name ?? "String");
573
+ } else if (fieldBase.kind === TypeKind.ENUM) {
574
+ properties[f.name] = { type: "string" };
575
+ } else {
576
+ properties[f.name] = { type: "object" };
577
+ }
578
+ }
579
+ return { type: "object", properties };
580
+ }
581
+ return { type: "object" };
582
+ }
583
+ var init_parser = __esm({
584
+ "src/adapters/graphql/parser.ts"() {
585
+ init_types();
586
+ init_errors();
587
+ init_introspection();
588
+ init_query_builder();
589
+ }
590
+ });
591
+
592
+ // src/core/executor.ts
593
+ init_types();
594
+
595
+ // src/core/sse.ts
596
+ async function* parseSSE(body) {
597
+ const reader = body.getReader();
598
+ const decoder = new TextDecoder("utf-8", { fatal: true });
599
+ let buffer = "";
600
+ let eventType;
601
+ let dataLines = [];
602
+ let id;
603
+ let retry;
604
+ let hasData = false;
605
+ function buildEvent() {
606
+ const event = { data: dataLines.join("\n") };
607
+ if (eventType !== void 0) event.event = eventType;
608
+ if (id !== void 0) event.id = id;
609
+ if (retry !== void 0) event.retry = retry;
610
+ return event;
611
+ }
612
+ function resetAccumulator() {
613
+ eventType = void 0;
614
+ dataLines = [];
615
+ id = void 0;
616
+ retry = void 0;
617
+ hasData = false;
618
+ }
619
+ function processLine(line) {
620
+ if (line === "") {
621
+ if (hasData) {
622
+ const event = buildEvent();
623
+ resetAccumulator();
624
+ return event;
625
+ }
626
+ resetAccumulator();
627
+ return void 0;
628
+ }
629
+ if (line[0] === ":") return void 0;
630
+ const colonIdx = line.indexOf(":");
631
+ let field;
632
+ let value;
633
+ if (colonIdx === -1) {
634
+ field = line;
635
+ value = "";
636
+ } else {
637
+ field = line.slice(0, colonIdx);
638
+ value = line[colonIdx + 1] === " " ? line.slice(colonIdx + 2) : line.slice(colonIdx + 1);
639
+ }
640
+ switch (field) {
641
+ case "data":
642
+ dataLines.push(value);
643
+ hasData = true;
644
+ break;
645
+ case "event":
646
+ eventType = value;
647
+ break;
648
+ case "id":
649
+ if (!value.includes("\0")) id = value;
650
+ break;
651
+ case "retry": {
652
+ const n = parseInt(value, 10);
653
+ if (!isNaN(n) && n >= 0 && String(n) === value) retry = n;
654
+ break;
655
+ }
656
+ }
657
+ return void 0;
658
+ }
659
+ try {
660
+ while (true) {
661
+ const { done, value } = await reader.read();
662
+ if (done) break;
663
+ try {
664
+ buffer += decoder.decode(value, { stream: true });
665
+ } catch (decodeError) {
666
+ throw new Error("SSE stream contains invalid UTF-8 data", { cause: decodeError });
667
+ }
668
+ let startIdx = 0;
669
+ const limit = buffer.length - (buffer[buffer.length - 1] === "\r" ? 1 : 0);
670
+ for (let i = 0; i < limit; i++) {
671
+ if (buffer[i] === "\r" || buffer[i] === "\n") {
672
+ const line = buffer.slice(startIdx, i);
673
+ if (buffer[i] === "\r" && buffer[i + 1] === "\n") i++;
674
+ startIdx = i + 1;
675
+ const event = processLine(line);
676
+ if (event) yield event;
677
+ }
678
+ }
679
+ buffer = buffer.slice(startIdx);
680
+ }
681
+ try {
682
+ buffer += decoder.decode();
683
+ } catch (decodeError) {
684
+ throw new Error("SSE stream contains invalid UTF-8 data", { cause: decodeError });
685
+ }
686
+ if (buffer.length > 0) {
687
+ const event = processLine(buffer);
688
+ if (event) yield event;
689
+ }
690
+ if (hasData) yield buildEvent();
691
+ } finally {
692
+ reader.releaseLock();
693
+ }
694
+ }
695
+
696
+ // src/core/url-builder.ts
697
+ init_types();
698
+ function buildUrl(baseUrl, operation, args) {
699
+ let path = operation.path;
700
+ for (const param of operation.parameters) {
701
+ if (param.in === exports.ParamLocation.PATH && args[param.name] !== void 0) {
702
+ path = path.replace(
703
+ `{${param.name}}`,
704
+ encodeURIComponent(String(args[param.name]))
705
+ );
706
+ }
707
+ }
708
+ const fullBase = baseUrl.replace(/\/$/, "");
709
+ const fullPath = path.startsWith("/") ? path : `/${path}`;
710
+ const url = new URL(`${fullBase}${fullPath}`);
711
+ for (const param of operation.parameters) {
712
+ if (param.in === exports.ParamLocation.QUERY) {
713
+ const value = args[param.name] ?? param.schema.default;
714
+ if (value !== void 0 && value !== null) {
715
+ serializeQueryParam(url, param.name, value);
716
+ }
717
+ }
718
+ }
719
+ return url.toString();
720
+ }
721
+ function deriveBaseUrl(specUrl) {
722
+ try {
723
+ const u = new URL(specUrl);
724
+ u.pathname = u.pathname.replace(/\/[^/]*$/, "");
725
+ const base = u.origin + u.pathname;
726
+ return base.replace(/\/$/, "");
727
+ } catch {
728
+ return "";
729
+ }
730
+ }
731
+ function extractHeaderParams(parameters, args) {
732
+ const headers = {};
733
+ for (const param of parameters) {
734
+ if (param.in === exports.ParamLocation.HEADER && args[param.name] !== void 0) {
735
+ headers[param.name] = String(args[param.name]);
736
+ }
737
+ }
738
+ return headers;
739
+ }
740
+ function extractCookieParams(parameters, args) {
741
+ const cookies = [];
742
+ for (const param of parameters) {
743
+ if (param.in === exports.ParamLocation.COOKIE) {
744
+ const value = args[param.name] ?? param.schema.default;
745
+ if (value !== void 0 && value !== null) {
746
+ cookies.push(`${encodeURIComponent(param.name)}=${encodeURIComponent(String(value))}`);
747
+ }
748
+ }
749
+ }
750
+ return cookies.length > 0 ? cookies.join("; ") : void 0;
751
+ }
752
+ function serializeQueryParam(url, name, value) {
753
+ if (Array.isArray(value)) {
754
+ url.searchParams.set(name, value.map(String).join(","));
755
+ } else if (typeof value === "object" && value !== null) {
756
+ const pairs = Object.entries(value).filter(([, v]) => v !== void 0 && v !== null).map(([k, v]) => {
757
+ if (typeof v === "object" && v !== null) {
758
+ throw new Error(
759
+ `Cannot serialize nested object for query parameter "${name}.${k}". Only flat key-value objects are supported.`
760
+ );
761
+ }
762
+ if (typeof v === "symbol" || typeof v === "function") {
763
+ throw new Error(
764
+ `Cannot serialize ${typeof v} value for query parameter "${name}.${k}". Use a string or number.`
765
+ );
766
+ }
767
+ return `${k},${String(v)}`;
768
+ });
769
+ url.searchParams.set(name, pairs.join(","));
770
+ } else {
771
+ url.searchParams.set(name, String(value));
772
+ }
773
+ }
774
+
775
+ // src/core/auth.ts
776
+ init_types();
777
+ init_errors();
778
+ function injectAuth(url, headers, auth) {
779
+ if (Array.isArray(auth)) {
780
+ let result2 = { url, headers: { ...headers } };
781
+ for (const a of auth) {
782
+ result2 = injectAuth(result2.url, result2.headers, a);
783
+ }
784
+ return result2;
785
+ }
786
+ const result = { url, headers: { ...headers } };
787
+ switch (auth.type) {
788
+ case exports.AuthType.BEARER:
789
+ result.headers[exports.HeaderName.AUTHORIZATION] = `Bearer ${auth.token}`;
790
+ break;
791
+ case exports.AuthType.BASIC: {
792
+ const encoded = btoa(`${auth.username}:${auth.password}`);
793
+ result.headers[exports.HeaderName.AUTHORIZATION] = `Basic ${encoded}`;
794
+ break;
795
+ }
796
+ case exports.AuthType.API_KEY:
797
+ if (auth.location === exports.ParamLocation.HEADER) {
798
+ result.headers[auth.name] = auth.value;
799
+ } else if (auth.location === exports.ParamLocation.QUERY) {
800
+ const u = new URL(url);
801
+ u.searchParams.set(auth.name, auth.value);
802
+ result.url = u.toString();
803
+ }
804
+ break;
805
+ case exports.AuthType.OAUTH2:
806
+ result.headers[exports.HeaderName.AUTHORIZATION] = `Bearer ${auth.accessToken}`;
807
+ break;
808
+ case exports.AuthType.COOKIE: {
809
+ const existing = result.headers[exports.HeaderName.COOKIE];
810
+ const cookie = `${encodeURIComponent(auth.name)}=${encodeURIComponent(auth.value)}`;
811
+ result.headers[exports.HeaderName.COOKIE] = existing ? `${existing}; ${cookie}` : cookie;
812
+ break;
813
+ }
814
+ }
815
+ return result;
816
+ }
817
+ async function refreshOAuth2Token(tokenUrl, refreshToken, options) {
818
+ const fetchFn = options?.fetch ?? globalThis.fetch;
819
+ const body = new URLSearchParams({
820
+ grant_type: "refresh_token",
821
+ refresh_token: refreshToken
822
+ });
823
+ if (options?.clientId) body.set("client_id", options.clientId);
824
+ if (options?.clientSecret) body.set("client_secret", options.clientSecret);
825
+ if (options?.scopes?.length) body.set("scope", options.scopes.join(" "));
826
+ const response = await fetchFn(tokenUrl, {
827
+ method: "POST",
828
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
829
+ body: body.toString()
830
+ });
831
+ if (!response.ok) {
832
+ let errorDetail = "";
833
+ try {
834
+ const body2 = await response.text();
835
+ errorDetail = body2 ? `: ${body2.slice(0, 500)}` : "";
836
+ } catch (bodyReadError) {
837
+ errorDetail = ` (error body unreadable: ${bodyReadError instanceof Error ? bodyReadError.message : String(bodyReadError)})`;
838
+ }
839
+ throw new exports.ApiInvokeError({
840
+ kind: exports.ErrorKind.AUTH,
841
+ message: `OAuth2 token refresh failed: ${response.status} ${response.statusText}${errorDetail}`,
842
+ suggestion: "Check the refresh token, client credentials, and token endpoint URL.",
843
+ retryable: response.status >= 500,
844
+ status: response.status
845
+ });
846
+ }
847
+ let data;
848
+ try {
849
+ data = await response.json();
850
+ } catch (parseError2) {
851
+ throw new exports.ApiInvokeError({
852
+ kind: exports.ErrorKind.PARSE,
853
+ message: `OAuth2 token refresh succeeded (${response.status}) but response body is not valid JSON`,
854
+ suggestion: "The token endpoint returned a non-JSON response. Verify the endpoint URL.",
855
+ retryable: false
856
+ });
857
+ }
858
+ const accessToken = data.access_token;
859
+ if (typeof accessToken !== "string" || !accessToken) {
860
+ throw new exports.ApiInvokeError({
861
+ kind: exports.ErrorKind.AUTH,
862
+ message: `OAuth2 token refresh response missing required "access_token" field. Got keys: [${Object.keys(data).join(", ")}]`,
863
+ suggestion: "The token endpoint response did not include a valid access_token. Verify the endpoint and grant type.",
864
+ retryable: false
865
+ });
866
+ }
867
+ return {
868
+ accessToken,
869
+ refreshToken: typeof data.refresh_token === "string" && data.refresh_token ? data.refresh_token : void 0,
870
+ expiresIn: typeof data.expires_in === "number" ? data.expires_in : void 0
871
+ };
872
+ }
873
+ function maskAuth(auth) {
874
+ switch (auth.type) {
875
+ case exports.AuthType.BEARER: {
876
+ if (auth.token.length <= 4) return "Bearer ***";
877
+ return `Bearer ${auth.token.substring(0, 4)}***`;
878
+ }
879
+ case exports.AuthType.BASIC:
880
+ return `Basic ${auth.username}:***`;
881
+ case exports.AuthType.API_KEY:
882
+ return `${auth.name}: ***`;
883
+ case exports.AuthType.OAUTH2:
884
+ return `OAuth2 ***`;
885
+ case exports.AuthType.COOKIE:
886
+ return `Cookie ${auth.name}=***`;
887
+ }
888
+ }
889
+
890
+ // src/core/executor.ts
891
+ init_errors();
892
+ var ABORT_ERROR_NAME = "AbortError";
893
+ var OPAQUE_RESPONSE_TYPE = "opaque";
894
+ var NO_CORS_MODE = "no-cors";
895
+ var JSON_SUFFIX = "+json";
896
+ var XML_SUBTYPE = "/xml";
897
+ var XML_SUFFIX = "+xml";
898
+ function buildRequest(baseUrl, operation, args, options = {}) {
899
+ const missing = operation.parameters.filter((p) => p.required && args[p.name] === void 0).map((p) => p.name);
900
+ if (missing.length > 0) {
901
+ throw new Error(
902
+ `Missing required parameter${missing.length > 1 ? "s" : ""}: ${missing.join(", ")} for operation "${operation.id}"`
903
+ );
904
+ }
905
+ let url = buildUrl(baseUrl, operation, args);
906
+ const method = operation.method.toUpperCase();
907
+ const accept = options.accept || operation.responseContentType || exports.ContentType.JSON;
908
+ const headers = {
909
+ [exports.HeaderName.ACCEPT]: accept,
910
+ ...extractHeaderParams(operation.parameters, args)
911
+ };
912
+ const cookieHeader = extractCookieParams(operation.parameters, args);
913
+ if (cookieHeader) {
914
+ headers[exports.HeaderName.COOKIE] = cookieHeader;
915
+ }
916
+ let bodyData = args["body"];
917
+ const allowsBody = method !== exports.HttpMethod.GET && method !== exports.HttpMethod.HEAD && method !== exports.HttpMethod.OPTIONS;
918
+ if (!bodyData && operation.buildBody && allowsBody) {
919
+ bodyData = operation.buildBody(args);
920
+ } else if (!bodyData && operation.requestBody && allowsBody) {
921
+ const bodyProps = operation.requestBody.schema.properties;
922
+ if (bodyProps) {
923
+ const assembled = {};
924
+ for (const propName of Object.keys(bodyProps)) {
925
+ if (args[propName] !== void 0) {
926
+ assembled[propName] = args[propName];
927
+ }
928
+ }
929
+ if (Object.keys(assembled).length > 0) {
930
+ bodyData = assembled;
931
+ }
932
+ }
933
+ }
934
+ let body;
935
+ if (bodyData && allowsBody) {
936
+ const contentType = operation.requestBody?.contentType ?? exports.ContentType.JSON;
937
+ if (contentType === exports.ContentType.FORM_URLENCODED) {
938
+ const params = new URLSearchParams();
939
+ const obj = typeof bodyData === "object" && bodyData !== null ? bodyData : {};
940
+ for (const [key, value] of Object.entries(obj)) {
941
+ if (value !== void 0 && value !== null) {
942
+ params.set(key, String(value));
943
+ }
944
+ }
945
+ body = params.toString();
946
+ headers[exports.HeaderName.CONTENT_TYPE] = exports.ContentType.FORM_URLENCODED;
947
+ } else if (contentType === exports.ContentType.MULTIPART) {
948
+ if (typeof bodyData !== "object" || bodyData === null) {
949
+ throw new Error(
950
+ `Multipart/form-data body for operation "${operation.id}" must be an object, got ${typeof bodyData}`
951
+ );
952
+ }
953
+ const formData = new FormData();
954
+ const obj = bodyData;
955
+ for (const [key, value] of Object.entries(obj)) {
956
+ if (value === void 0 || value === null) continue;
957
+ if (value instanceof Blob) {
958
+ const filename = value instanceof File ? value.name : key;
959
+ formData.append(key, value, filename);
960
+ } else if (value instanceof ArrayBuffer) {
961
+ formData.append(key, new Blob([value]), key);
962
+ } else if (ArrayBuffer.isView(value)) {
963
+ formData.append(key, new Blob([new Uint8Array(value.buffer, value.byteOffset, value.byteLength)]), key);
964
+ } else {
965
+ formData.append(key, String(value));
966
+ }
967
+ }
968
+ body = formData;
969
+ } else {
970
+ body = typeof bodyData === "string" ? bodyData : JSON.stringify(bodyData);
971
+ headers[exports.HeaderName.CONTENT_TYPE] = exports.ContentType.JSON;
972
+ }
973
+ }
974
+ if (options.auth) {
975
+ const authed = injectAuth(url, headers, options.auth);
976
+ url = authed.url;
977
+ Object.assign(headers, authed.headers);
978
+ }
979
+ return { method, url, headers, body };
980
+ }
981
+ async function executeFetch(baseUrl, operation, args, options) {
982
+ const fetchFn = options.fetch ?? globalThis.fetch;
983
+ let { method, url, headers, body } = buildRequest(baseUrl, operation, args, {
984
+ auth: options.auth,
985
+ accept: options.accept
986
+ });
987
+ if (options.headers) {
988
+ Object.assign(headers, options.headers);
989
+ }
990
+ let signal = options.signal;
991
+ let timeoutId;
992
+ let abortHandler;
993
+ if (options.timeoutMs && options.timeoutMs > 0) {
994
+ const controller = new AbortController();
995
+ timeoutId = setTimeout(() => controller.abort(), options.timeoutMs);
996
+ if (options.signal) {
997
+ abortHandler = () => controller.abort();
998
+ options.signal.addEventListener("abort", abortHandler, { once: true });
999
+ }
1000
+ signal = controller.signal;
1001
+ }
1002
+ let init = { method, headers, body, signal, redirect: options.redirect };
1003
+ if (options.middleware) {
1004
+ for (const mw of options.middleware) {
1005
+ if (mw.onRequest) {
1006
+ const result = await mw.onRequest(url, init);
1007
+ url = result.url;
1008
+ init = result.init;
1009
+ }
1010
+ }
1011
+ }
1012
+ const start = performance.now();
1013
+ let response;
1014
+ try {
1015
+ response = await fetchFn(url, init);
1016
+ } catch (error) {
1017
+ if (timeoutId) clearTimeout(timeoutId);
1018
+ if (abortHandler && options.signal) options.signal.removeEventListener("abort", abortHandler);
1019
+ if (options.middleware) {
1020
+ for (const mw of options.middleware) {
1021
+ if (mw.onError) {
1022
+ const normalized = error instanceof Error ? error : new Error(String(error));
1023
+ try {
1024
+ mw.onError(normalized);
1025
+ } catch (mwError) {
1026
+ console.warn(`[api-invoke] middleware "${mw.name ?? "unnamed"}" onError handler threw (suppressed):`, mwError);
1027
+ }
1028
+ }
1029
+ }
1030
+ }
1031
+ if (error instanceof DOMException && error.name === ABORT_ERROR_NAME) {
1032
+ if (options.timeoutMs && options.timeoutMs > 0) {
1033
+ throw timeoutError(url);
1034
+ }
1035
+ throw error;
1036
+ }
1037
+ if (error instanceof TypeError) {
1038
+ if (typeof window !== "undefined") {
1039
+ try {
1040
+ const probe = await fetchFn(url, { mode: NO_CORS_MODE });
1041
+ if (probe.type === OPAQUE_RESPONSE_TYPE) throw corsError(url);
1042
+ } catch (probeError) {
1043
+ if (probeError instanceof Error && probeError.name === exports.API_INVOKE_ERROR_NAME) throw probeError;
1044
+ }
1045
+ }
1046
+ throw networkError(url);
1047
+ }
1048
+ throw networkError(url);
1049
+ }
1050
+ if (timeoutId) clearTimeout(timeoutId);
1051
+ if (abortHandler && options.signal) options.signal.removeEventListener("abort", abortHandler);
1052
+ const elapsedMs = Math.round(performance.now() - start);
1053
+ if (options.middleware) {
1054
+ for (const mw of options.middleware) {
1055
+ if (mw.onResponse) {
1056
+ response = await mw.onResponse(response);
1057
+ }
1058
+ }
1059
+ }
1060
+ const responseHeaders = {};
1061
+ response.headers.forEach((value, key) => {
1062
+ responseHeaders[key] = value;
1063
+ });
1064
+ return { response, request: { method, url, headers, body }, headers: responseHeaders, elapsedMs };
1065
+ }
1066
+ async function executeOperation(baseUrl, operation, args, options = {}) {
1067
+ const { response, request, headers: responseHeaders, elapsedMs } = await executeFetch(baseUrl, operation, args, options);
1068
+ const { method, url, headers, body } = request;
1069
+ let data;
1070
+ const contentType = response.headers.get(exports.HeaderName.CONTENT_TYPE) || "";
1071
+ if (contentType.includes(exports.ContentType.JSON) || contentType.includes(JSON_SUFFIX)) {
1072
+ const cloned = response.clone();
1073
+ try {
1074
+ data = await response.json();
1075
+ } catch (jsonError) {
1076
+ if (options.throwOnHttpError !== false) throw parseError(url);
1077
+ console.warn("[api-invoke] JSON parse failed, falling back to text:", jsonError);
1078
+ try {
1079
+ data = await cloned.text();
1080
+ } catch {
1081
+ throw parseError(url);
1082
+ }
1083
+ }
1084
+ } else if (isBinaryContentType(contentType)) {
1085
+ try {
1086
+ data = await response.arrayBuffer();
1087
+ } catch {
1088
+ throw parseError(url, "binary");
1089
+ }
1090
+ } else if (contentType.includes(XML_SUBTYPE) || contentType.includes(XML_SUFFIX)) {
1091
+ try {
1092
+ data = await response.text();
1093
+ } catch {
1094
+ throw parseError(url, "XML");
1095
+ }
1096
+ } else {
1097
+ let text;
1098
+ try {
1099
+ text = await response.text();
1100
+ } catch {
1101
+ throw parseError(url, "text");
1102
+ }
1103
+ try {
1104
+ data = JSON.parse(text);
1105
+ } catch {
1106
+ data = text;
1107
+ }
1108
+ }
1109
+ const result = {
1110
+ status: response.status,
1111
+ data,
1112
+ contentType,
1113
+ headers: responseHeaders,
1114
+ request: { method, url, headers, body },
1115
+ elapsedMs
1116
+ };
1117
+ if (options.throwOnHttpError !== false) {
1118
+ if (response.status === 401 || response.status === 403) {
1119
+ throw authError(url, response.status, data);
1120
+ }
1121
+ if (!response.ok) {
1122
+ throw httpError(url, response.status, response.statusText, data);
1123
+ }
1124
+ } else if (!response.ok) {
1125
+ if (response.status === 401 || response.status === 403) {
1126
+ result.errorKind = exports.ErrorKind.AUTH;
1127
+ } else if (response.status === 429) {
1128
+ result.errorKind = exports.ErrorKind.RATE_LIMIT;
1129
+ } else {
1130
+ result.errorKind = exports.ErrorKind.HTTP;
1131
+ }
1132
+ }
1133
+ return result;
1134
+ }
1135
+ async function executeRaw(url, options = {}) {
1136
+ const operation = {
1137
+ id: "raw",
1138
+ path: "",
1139
+ method: options.method ?? exports.HttpMethod.GET,
1140
+ parameters: [],
1141
+ tags: []
1142
+ };
1143
+ return executeOperation(url, operation, { body: options.body }, {
1144
+ auth: options.auth,
1145
+ middleware: options.middleware,
1146
+ fetch: options.fetch,
1147
+ timeoutMs: options.timeoutMs,
1148
+ signal: options.signal,
1149
+ accept: options.accept,
1150
+ redirect: options.redirect,
1151
+ headers: options.headers
1152
+ });
1153
+ }
1154
+ async function executeOperationStream(baseUrl, operation, args, options = {}) {
1155
+ const streamOptions = {
1156
+ ...options,
1157
+ accept: options.accept ?? operation.responseContentType ?? exports.ContentType.SSE
1158
+ };
1159
+ const { response, request, headers: responseHeaders, elapsedMs } = await executeFetch(baseUrl, operation, args, streamOptions);
1160
+ if (!response.ok) {
1161
+ let body;
1162
+ try {
1163
+ const text = await response.text();
1164
+ try {
1165
+ body = JSON.parse(text);
1166
+ } catch {
1167
+ body = text;
1168
+ }
1169
+ } catch (readError) {
1170
+ body = `[api-invoke: failed to read error response body: ${readError instanceof Error ? readError.message : String(readError)}]`;
1171
+ }
1172
+ if (response.status === 401 || response.status === 403) {
1173
+ throw authError(request.url, response.status, body);
1174
+ }
1175
+ throw httpError(request.url, response.status, response.statusText, body);
1176
+ }
1177
+ if (!response.body) {
1178
+ throw parseError(request.url, "SSE (response body is null)");
1179
+ }
1180
+ const contentType = response.headers.get(exports.HeaderName.CONTENT_TYPE) || "";
1181
+ if (contentType && !contentType.includes("text/event-stream")) {
1182
+ console.warn(`[api-invoke] Expected content-type text/event-stream but got "${contentType}" \u2014 SSE parsing may produce unexpected results`);
1183
+ }
1184
+ let stream = parseSSE(response.body);
1185
+ if (options.onEvent) {
1186
+ const inner = stream;
1187
+ const onEvent = options.onEvent;
1188
+ stream = (async function* () {
1189
+ for await (const event of inner) {
1190
+ try {
1191
+ onEvent(event);
1192
+ } catch (callbackError) {
1193
+ throw new Error(
1194
+ `onEvent callback threw for event "${event.event ?? "message"}": ${callbackError instanceof Error ? callbackError.message : String(callbackError)}`,
1195
+ { cause: callbackError }
1196
+ );
1197
+ }
1198
+ yield event;
1199
+ }
1200
+ })();
1201
+ }
1202
+ return {
1203
+ status: response.status,
1204
+ stream,
1205
+ contentType,
1206
+ headers: responseHeaders,
1207
+ request,
1208
+ elapsedMs
1209
+ };
1210
+ }
1211
+ async function executeRawStream(url, options = {}) {
1212
+ const operation = {
1213
+ id: "raw-stream",
1214
+ path: "",
1215
+ method: options.method ?? exports.HttpMethod.POST,
1216
+ parameters: [],
1217
+ tags: []
1218
+ };
1219
+ return executeOperationStream(url, operation, { body: options.body }, {
1220
+ auth: options.auth,
1221
+ middleware: options.middleware,
1222
+ fetch: options.fetch,
1223
+ timeoutMs: options.timeoutMs,
1224
+ signal: options.signal,
1225
+ accept: options.accept,
1226
+ redirect: options.redirect,
1227
+ headers: options.headers,
1228
+ onEvent: options.onEvent
1229
+ });
1230
+ }
1231
+ var BINARY_CONTENT_PATTERNS = [
1232
+ "application/octet-stream",
1233
+ "application/pdf",
1234
+ "application/zip",
1235
+ "audio/",
1236
+ "image/",
1237
+ "video/"
1238
+ ];
1239
+ function isBinaryContentType(contentType) {
1240
+ return BINARY_CONTENT_PATTERNS.some((p) => contentType.includes(p));
1241
+ }
1242
+
1243
+ // src/adapters/openapi/parser.ts
1244
+ init_types();
1245
+
1246
+ // src/adapters/openapi/base-url.ts
1247
+ function extractOpenAPI3BaseUrl(api, specUrl) {
1248
+ const server = api.servers?.[0];
1249
+ if (!server) return "";
1250
+ let url = server.url;
1251
+ if (server.variables) {
1252
+ for (const [name, variable] of Object.entries(server.variables)) {
1253
+ const value = variable.default ?? variable.enum?.[0];
1254
+ if (value === void 0) return "";
1255
+ url = url.replaceAll(`{${name}}`, String(value));
1256
+ }
1257
+ }
1258
+ if (url && !url.startsWith("http")) {
1259
+ if (!specUrl) return "";
1260
+ try {
1261
+ const resolved = new URL(url, specUrl);
1262
+ return resolved.origin + resolved.pathname.replace(/\/$/, "");
1263
+ } catch {
1264
+ return "";
1265
+ }
1266
+ }
1267
+ return url;
1268
+ }
1269
+ function extractSwagger2BaseUrl(api) {
1270
+ const host = api.host;
1271
+ if (!host) return "";
1272
+ const scheme = api.schemes?.[0] ?? "https";
1273
+ const basePath = api.basePath ?? "";
1274
+ return `${scheme}://${host}${basePath}`;
1275
+ }
1276
+
1277
+ // src/adapters/openapi/security.ts
1278
+ init_types();
1279
+ function mapSecuritySchemes(schemes) {
1280
+ const results = [];
1281
+ for (const [name, scheme] of Object.entries(schemes)) {
1282
+ results.push(mapSingleScheme(name, scheme));
1283
+ }
1284
+ return results;
1285
+ }
1286
+ function mapSingleScheme(name, scheme) {
1287
+ const baseDescription = scheme.description || name;
1288
+ if (scheme.type === "apiKey") {
1289
+ const apiKeyScheme = scheme;
1290
+ if (apiKeyScheme.in === exports.ParamLocation.HEADER) {
1291
+ return { name, authType: exports.AuthType.API_KEY, metadata: { headerName: apiKeyScheme.name }, description: baseDescription };
1292
+ }
1293
+ if (apiKeyScheme.in === exports.ParamLocation.QUERY) {
1294
+ return { name, authType: exports.AuthType.QUERY_PARAM, metadata: { paramName: apiKeyScheme.name }, description: baseDescription };
1295
+ }
1296
+ if (apiKeyScheme.in === exports.ParamLocation.COOKIE) {
1297
+ return { name, authType: exports.AuthType.COOKIE, metadata: { cookieName: apiKeyScheme.name }, description: baseDescription };
1298
+ }
1299
+ return { name, authType: null, metadata: {}, description: `${baseDescription} (unsupported: apiKey in "${apiKeyScheme.in}")` };
1300
+ }
1301
+ if (scheme.type === "http") {
1302
+ const httpScheme = scheme;
1303
+ if (httpScheme.scheme === exports.AuthType.BEARER) {
1304
+ return { name, authType: exports.AuthType.BEARER, metadata: {}, description: baseDescription };
1305
+ }
1306
+ if (httpScheme.scheme === exports.AuthType.BASIC) {
1307
+ return { name, authType: exports.AuthType.BASIC, metadata: {}, description: baseDescription };
1308
+ }
1309
+ return { name, authType: null, metadata: {}, description: `${baseDescription} (unsupported: HTTP scheme "${httpScheme.scheme}")` };
1310
+ }
1311
+ if (scheme.type === "basic") {
1312
+ return { name, authType: exports.AuthType.BASIC, metadata: {}, description: baseDescription };
1313
+ }
1314
+ if (scheme.type === "oauth2") {
1315
+ const metadata = {};
1316
+ let flowResolved = false;
1317
+ if ("flows" in scheme) {
1318
+ const oauth3 = scheme;
1319
+ const flow = oauth3.flows.authorizationCode ?? oauth3.flows.clientCredentials ?? oauth3.flows.implicit ?? oauth3.flows.password;
1320
+ if (flow) {
1321
+ flowResolved = true;
1322
+ if ("authorizationUrl" in flow && flow.authorizationUrl) metadata.authorizationUrl = flow.authorizationUrl;
1323
+ if ("tokenUrl" in flow && flow.tokenUrl) metadata.tokenUrl = flow.tokenUrl;
1324
+ if (flow.refreshUrl) metadata.refreshUrl = flow.refreshUrl;
1325
+ if (flow.scopes && Object.keys(flow.scopes).length > 0) {
1326
+ metadata.scopes = Object.keys(flow.scopes).join(",");
1327
+ }
1328
+ }
1329
+ }
1330
+ if ("flow" in scheme) {
1331
+ flowResolved = true;
1332
+ const oauth2 = scheme;
1333
+ if (typeof oauth2.authorizationUrl === "string") metadata.authorizationUrl = oauth2.authorizationUrl;
1334
+ if (typeof oauth2.tokenUrl === "string") metadata.tokenUrl = oauth2.tokenUrl;
1335
+ const scopes = oauth2.scopes;
1336
+ if (scopes && Object.keys(scopes).length > 0) {
1337
+ metadata.scopes = Object.keys(scopes).join(",");
1338
+ }
1339
+ }
1340
+ const description = flowResolved ? baseDescription : `${baseDescription} (OAuth2 detected but no supported flow found)`;
1341
+ return { name, authType: exports.AuthType.OAUTH2, metadata, description };
1342
+ }
1343
+ if (scheme.type === "openIdConnect") {
1344
+ return { name, authType: null, metadata: {}, description: `${baseDescription} (unsupported: OpenID Connect)` };
1345
+ }
1346
+ return { name, authType: null, metadata: {}, description: `${baseDescription} (unsupported: unknown scheme type)` };
1347
+ }
1348
+
1349
+ // src/adapters/openapi/parser.ts
1350
+ var SUPPORTED_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
1351
+ function normalizeType(type, fallback = "string") {
1352
+ if (Array.isArray(type)) {
1353
+ const nonNull = type.filter((t) => t !== "null");
1354
+ return nonNull[0] ?? fallback;
1355
+ }
1356
+ if (typeof type === "string") return type;
1357
+ return fallback;
1358
+ }
1359
+ async function parseOpenAPISpec(specUrlOrObject, options) {
1360
+ try {
1361
+ let apiRaw;
1362
+ try {
1363
+ apiRaw = await SwaggerParser__default.default.dereference(specUrlOrObject);
1364
+ } catch {
1365
+ apiRaw = await SwaggerParser__default.default.parse(specUrlOrObject);
1366
+ }
1367
+ const api = apiRaw;
1368
+ const isOpenAPI3 = "openapi" in api;
1369
+ const specVersion = isOpenAPI3 ? api.openapi : api.swagger;
1370
+ const title = api.info.title;
1371
+ const version = api.info.version;
1372
+ const sourceUrl = options?.specUrl ?? (typeof specUrlOrObject === "string" ? specUrlOrObject : void 0);
1373
+ let baseUrl = isOpenAPI3 ? extractOpenAPI3BaseUrl(api, sourceUrl) : extractSwagger2BaseUrl(api);
1374
+ if (!baseUrl && sourceUrl) {
1375
+ baseUrl = deriveBaseUrl(sourceUrl);
1376
+ }
1377
+ const operations = extractOperations(api, isOpenAPI3);
1378
+ if (baseUrl) {
1379
+ try {
1380
+ const basePath = new URL(baseUrl).pathname.replace(/\/$/, "");
1381
+ if (basePath && basePath !== "/") {
1382
+ const allOverlap = operations.length > 0 && operations.every(
1383
+ (op) => op.path === basePath || op.path.startsWith(basePath + "/")
1384
+ );
1385
+ if (allOverlap) {
1386
+ for (const op of operations) {
1387
+ op.path = op.path.slice(basePath.length) || "/";
1388
+ }
1389
+ }
1390
+ }
1391
+ } catch {
1392
+ }
1393
+ }
1394
+ const authSchemes = extractSecuritySchemes(api, isOpenAPI3);
1395
+ return {
1396
+ title,
1397
+ version,
1398
+ baseUrl,
1399
+ operations,
1400
+ authSchemes,
1401
+ specFormat: isOpenAPI3 ? exports.SpecFormat.OPENAPI_3 : exports.SpecFormat.OPENAPI_2,
1402
+ rawSpecVersion: specVersion
1403
+ };
1404
+ } catch (error) {
1405
+ const message = error instanceof Error ? error.message : "Unknown error";
1406
+ const urlInfo = typeof specUrlOrObject === "string" ? ` from ${specUrlOrObject}` : "";
1407
+ throw new Error(`Failed to parse OpenAPI spec${urlInfo}: ${message}`, { cause: error });
1408
+ }
1409
+ }
1410
+ function extractOperations(api, isOpenAPI3) {
1411
+ const operations = [];
1412
+ if (!api.paths) return operations;
1413
+ for (const [path, pathItem] of Object.entries(api.paths)) {
1414
+ if (!pathItem) continue;
1415
+ const pathLevelParams = "parameters" in pathItem ? pathItem.parameters ?? [] : [];
1416
+ for (const method of SUPPORTED_METHODS) {
1417
+ if (!(method in pathItem) || !pathItem[method]) continue;
1418
+ const op = pathItem[method];
1419
+ const operationParams = op.parameters ?? [];
1420
+ const allParams = [...pathLevelParams, ...operationParams].filter(
1421
+ (p) => p.in !== "body"
1422
+ // 'body' is a Swagger 2.0-specific location, not in ParamLocation
1423
+ );
1424
+ const parameters = allParams.map(
1425
+ (param) => parseParameter(param, isOpenAPI3)
1426
+ );
1427
+ const requestBody = extractRequestBody(op, isOpenAPI3);
1428
+ const { primary: responseSchema, all: responseSchemas } = extractResponseSchemas(op, isOpenAPI3);
1429
+ const responseContentType = extractResponseContentType(op, isOpenAPI3);
1430
+ const errorHints = extractErrorHints(op);
1431
+ const id = op.operationId ?? `${method}_${path.replace(/[{}\/]/g, "_").replace(/^_|_$/g, "").replace(/_+/g, "_")}`;
1432
+ operations.push({
1433
+ id,
1434
+ path,
1435
+ method: method.toUpperCase(),
1436
+ summary: op.summary,
1437
+ description: op.description,
1438
+ parameters,
1439
+ requestBody,
1440
+ responseSchema,
1441
+ responseSchemas: Object.keys(responseSchemas).length > 0 ? responseSchemas : void 0,
1442
+ responseContentType,
1443
+ errorHints,
1444
+ tags: op.tags ?? []
1445
+ });
1446
+ }
1447
+ }
1448
+ return operations;
1449
+ }
1450
+ function parseParameter(param, isOpenAPI3) {
1451
+ const name = param.name;
1452
+ const location = param.in;
1453
+ const required = param.required ?? location === exports.ParamLocation.PATH;
1454
+ const description = param.description ?? "";
1455
+ let schema;
1456
+ if (isOpenAPI3) {
1457
+ const p = param;
1458
+ const s = p.schema;
1459
+ schema = {
1460
+ type: normalizeType(s?.type),
1461
+ format: s?.format,
1462
+ enum: s?.enum,
1463
+ default: s?.default,
1464
+ example: p.example ?? s?.example,
1465
+ minimum: s?.minimum,
1466
+ maximum: s?.maximum,
1467
+ maxLength: s?.maxLength
1468
+ // TODO: extract s?.items for array parameters (ParameterSchema.items)
1469
+ };
1470
+ } else {
1471
+ const p = param;
1472
+ schema = {
1473
+ type: ("type" in p ? p.type : void 0) ?? "string",
1474
+ format: "format" in p ? p.format : void 0,
1475
+ enum: "enum" in p ? p.enum : void 0,
1476
+ default: "default" in p ? p.default : void 0,
1477
+ example: "x-example" in p ? p["x-example"] : void 0,
1478
+ minimum: "minimum" in p ? p.minimum : void 0,
1479
+ maximum: "maximum" in p ? p.maximum : void 0,
1480
+ maxLength: "maxLength" in p ? p.maxLength : void 0
1481
+ // TODO: extract p.items for array parameters (ParameterSchema.items)
1482
+ };
1483
+ }
1484
+ return { name, in: location, required, description, schema };
1485
+ }
1486
+ var CONTENT_TYPE_PRIORITY = [
1487
+ exports.ContentType.JSON,
1488
+ exports.ContentType.FORM_URLENCODED,
1489
+ exports.ContentType.MULTIPART,
1490
+ exports.ContentType.XML,
1491
+ exports.ContentType.TEXT,
1492
+ exports.ContentType.OCTET_STREAM
1493
+ ];
1494
+ function extractRequestBody(operation, isOpenAPI3) {
1495
+ if (isOpenAPI3) {
1496
+ const op = operation;
1497
+ if (!op.requestBody) return void 0;
1498
+ const body = op.requestBody;
1499
+ if (!body.content) return void 0;
1500
+ for (const ct of CONTENT_TYPE_PRIORITY) {
1501
+ const mediaType = body.content[ct];
1502
+ if (mediaType?.schema) {
1503
+ return {
1504
+ required: body.required ?? false,
1505
+ description: body.description,
1506
+ contentType: ct,
1507
+ schema: flattenSchema(mediaType.schema)
1508
+ };
1509
+ }
1510
+ }
1511
+ const firstKey = Object.keys(body.content)[0];
1512
+ if (firstKey) {
1513
+ const mediaType = body.content[firstKey];
1514
+ if (mediaType?.schema) {
1515
+ return {
1516
+ required: body.required ?? false,
1517
+ description: body.description,
1518
+ contentType: firstKey,
1519
+ schema: flattenSchema(mediaType.schema)
1520
+ };
1521
+ }
1522
+ }
1523
+ return void 0;
1524
+ } else {
1525
+ const op = operation;
1526
+ const bodyParam = op.parameters?.find(
1527
+ (p) => p.in === "body"
1528
+ );
1529
+ if (!bodyParam?.schema) return void 0;
1530
+ const consumes = op.consumes ?? [];
1531
+ let contentType = exports.ContentType.JSON;
1532
+ if (consumes.includes(exports.ContentType.FORM_URLENCODED)) {
1533
+ contentType = exports.ContentType.FORM_URLENCODED;
1534
+ } else if (consumes.includes(exports.ContentType.MULTIPART)) {
1535
+ contentType = exports.ContentType.MULTIPART;
1536
+ } else if (consumes.length > 0) {
1537
+ contentType = consumes[0];
1538
+ }
1539
+ return {
1540
+ required: bodyParam.required ?? false,
1541
+ description: bodyParam.description,
1542
+ contentType,
1543
+ schema: flattenSchema(bodyParam.schema)
1544
+ };
1545
+ }
1546
+ }
1547
+ function flattenSchema(schema) {
1548
+ const normalizedType = normalizeType(schema.type, "object");
1549
+ const result = {
1550
+ type: normalizedType,
1551
+ raw: schema
1552
+ };
1553
+ if (normalizedType === "object" && schema.properties) {
1554
+ result.properties = {};
1555
+ result.required = schema.required;
1556
+ for (const [name, propSchema] of Object.entries(schema.properties)) {
1557
+ const prop = propSchema;
1558
+ const propType = normalizeType(prop.type);
1559
+ const isNested = propType === "object" || propType === "array";
1560
+ result.properties[name] = {
1561
+ type: propType,
1562
+ format: prop.format,
1563
+ description: prop.description,
1564
+ enum: prop.enum,
1565
+ default: prop.default,
1566
+ example: prop.example,
1567
+ nested: isNested || void 0
1568
+ };
1569
+ }
1570
+ }
1571
+ return result;
1572
+ }
1573
+ var RESPONSE_STATUS_CODES = ["200", "201", "202", "204", "2XX", "default"];
1574
+ function extractResponseSchemas(operation, isOpenAPI3) {
1575
+ const responses = operation.responses;
1576
+ if (!responses) return { primary: void 0, all: {} };
1577
+ const all = {};
1578
+ for (const code of RESPONSE_STATUS_CODES) {
1579
+ const resp = responses[code];
1580
+ if (!resp) continue;
1581
+ let schema;
1582
+ if (isOpenAPI3) {
1583
+ schema = resp.content?.[exports.ContentType.JSON]?.schema;
1584
+ } else {
1585
+ schema = resp.schema;
1586
+ }
1587
+ if (schema) {
1588
+ all[code] = schema;
1589
+ }
1590
+ }
1591
+ const primary = all["200"] ?? all["201"] ?? all["202"] ?? all["2XX"];
1592
+ return { primary, all };
1593
+ }
1594
+ var ERROR_STATUS_CODES = ["400", "401", "403", "404", "409", "422", "429", "500"];
1595
+ function extractErrorHints(operation) {
1596
+ const responses = operation.responses;
1597
+ if (!responses) return void 0;
1598
+ const hints = {};
1599
+ for (const code of ERROR_STATUS_CODES) {
1600
+ const resp = responses[code];
1601
+ if (!resp?.description) continue;
1602
+ hints[code] = resp.description;
1603
+ }
1604
+ return Object.keys(hints).length > 0 ? hints : void 0;
1605
+ }
1606
+ function extractResponseContentType(operation, isOpenAPI3) {
1607
+ const responses = operation.responses;
1608
+ if (!responses) return void 0;
1609
+ const successResponse = responses["200"] ?? responses["201"] ?? responses["202"] ?? responses["2XX"];
1610
+ if (!successResponse) return void 0;
1611
+ if (isOpenAPI3) {
1612
+ const resp = successResponse;
1613
+ if (!resp.content) return void 0;
1614
+ const types = Object.keys(resp.content);
1615
+ if (types.includes(exports.ContentType.JSON)) return exports.ContentType.JSON;
1616
+ return types[0];
1617
+ } else {
1618
+ const op = operation;
1619
+ const produces = op.produces;
1620
+ if (produces && produces.length > 0) {
1621
+ if (produces.includes(exports.ContentType.JSON)) return exports.ContentType.JSON;
1622
+ return produces[0];
1623
+ }
1624
+ return void 0;
1625
+ }
1626
+ }
1627
+ function extractSecuritySchemes(api, isOpenAPI3) {
1628
+ if (isOpenAPI3) {
1629
+ const openapi3 = api;
1630
+ const rawSchemes = openapi3.components?.securitySchemes ?? {};
1631
+ const schemes = {};
1632
+ for (const [name, scheme] of Object.entries(rawSchemes)) {
1633
+ if (scheme && !("$ref" in scheme)) {
1634
+ schemes[name] = scheme;
1635
+ }
1636
+ }
1637
+ return mapSecuritySchemes(schemes);
1638
+ } else {
1639
+ const swagger2 = api;
1640
+ return mapSecuritySchemes(swagger2.securityDefinitions ?? {});
1641
+ }
1642
+ }
1643
+
1644
+ // src/adapters/raw/parser.ts
1645
+ init_types();
1646
+ function parseRawUrl(url) {
1647
+ return parseRawUrls([{ url }]);
1648
+ }
1649
+ function parseRawUrls(endpoints) {
1650
+ if (endpoints.length === 0) {
1651
+ throw new Error("At least one endpoint is required");
1652
+ }
1653
+ let firstParsed;
1654
+ try {
1655
+ firstParsed = new URL(endpoints[0].url);
1656
+ } catch {
1657
+ throw new Error(`Invalid URL "${endpoints[0].url}". Expected an absolute URL like "https://api.example.com/path".`);
1658
+ }
1659
+ const baseUrl = firstParsed.origin;
1660
+ const operations = endpoints.map((ep) => {
1661
+ let parsed;
1662
+ try {
1663
+ parsed = new URL(ep.url);
1664
+ } catch {
1665
+ throw new Error(`Invalid URL "${ep.url}". Expected an absolute URL like "https://api.example.com/path".`);
1666
+ }
1667
+ if (parsed.origin !== firstParsed.origin) {
1668
+ throw new Error(
1669
+ `All endpoints must share the same origin. Got "${parsed.origin}" but expected "${firstParsed.origin}"`
1670
+ );
1671
+ }
1672
+ const method = (ep.method ?? exports.HttpMethod.GET).toUpperCase();
1673
+ const pathname = parsed.pathname;
1674
+ const parameters = [];
1675
+ const entries = /* @__PURE__ */ new Map();
1676
+ for (const [rawKey, value] of parsed.searchParams.entries()) {
1677
+ const name = rawKey.replace(/(\[\])+$/, "");
1678
+ const isBracket = name !== rawKey;
1679
+ if (!name) continue;
1680
+ const existing = entries.get(name);
1681
+ if (existing) {
1682
+ existing.values.push(value);
1683
+ existing.isBracket = existing.isBracket || isBracket;
1684
+ } else {
1685
+ entries.set(name, { values: [value], isBracket });
1686
+ }
1687
+ }
1688
+ for (const [name, { values, isBracket }] of entries) {
1689
+ const isArray = values.length > 1 || isBracket;
1690
+ parameters.push({
1691
+ name,
1692
+ in: exports.ParamLocation.QUERY,
1693
+ required: false,
1694
+ description: isArray ? `Default: ${JSON.stringify(values)}` : `Default: ${values[0]}`,
1695
+ schema: isArray ? { type: "array", default: values, items: { type: "string" } } : { type: "string", default: values[0] }
1696
+ });
1697
+ }
1698
+ const id = ep.id ?? `${method.toLowerCase()}_${pathname.replace(/^\//, "").replace(/[\/]/g, "_") || "root"}`;
1699
+ return {
1700
+ id,
1701
+ path: pathname,
1702
+ method,
1703
+ summary: ep.summary ?? `${method} ${parsed.hostname}${pathname}`,
1704
+ parameters,
1705
+ tags: []
1706
+ };
1707
+ });
1708
+ return {
1709
+ title: firstParsed.hostname,
1710
+ version: "1.0.0",
1711
+ baseUrl,
1712
+ operations,
1713
+ authSchemes: [],
1714
+ specFormat: exports.SpecFormat.RAW_URL
1715
+ };
1716
+ }
1717
+
1718
+ // src/core/detection.ts
1719
+ function isSpecUrl(url) {
1720
+ const lower = url.toLowerCase();
1721
+ return lower.endsWith("/openapi.json") || lower.endsWith("/openapi.yaml") || lower.endsWith("/openapi.yml") || lower.endsWith("/swagger.json") || lower.endsWith("/swagger.yaml") || lower.endsWith("/swagger.yml") || lower.endsWith("/spec.json") || lower.endsWith("/spec.yaml") || lower.endsWith("/spec.yml") || lower.endsWith("/api-docs") || lower.endsWith("/api-docs.json") || lower.endsWith("/api-docs.yaml") || lower.endsWith("/v2/api-docs") || lower.endsWith("/v3/api-docs") || lower.includes("swagger") || lower.includes("openapi");
1722
+ }
1723
+ function isSpecContent(data) {
1724
+ if (!data || typeof data !== "object" || Array.isArray(data)) return false;
1725
+ const obj = data;
1726
+ return typeof obj.openapi === "string" || typeof obj.swagger === "string";
1727
+ }
1728
+ function isGraphQLUrl(url) {
1729
+ try {
1730
+ return /\/graphql(?:$|[\/\?#])/i.test(new URL(url).pathname);
1731
+ } catch {
1732
+ return false;
1733
+ }
1734
+ }
1735
+
1736
+ // src/client.ts
1737
+ function isGraphQLIntrospection(obj) {
1738
+ if (obj.__schema !== void 0) return true;
1739
+ if (typeof obj.data === "object" && obj.data !== null && "__schema" in obj.data) return true;
1740
+ return false;
1741
+ }
1742
+ var ApiInvokeClient = class {
1743
+ /** The parsed API specification backing this client. */
1744
+ api;
1745
+ auth;
1746
+ middleware;
1747
+ fetchFn;
1748
+ timeoutMs;
1749
+ /**
1750
+ * @param api - Parsed API specification (from any adapter)
1751
+ * @param options - Client configuration (auth, middleware, fetch, timeout)
1752
+ */
1753
+ constructor(api, options = {}) {
1754
+ this.api = api;
1755
+ this.auth = options.auth;
1756
+ this.middleware = options.middleware ?? [];
1757
+ this.fetchFn = options.fetch ?? globalThis.fetch;
1758
+ this.timeoutMs = options.timeoutMs ?? 0;
1759
+ }
1760
+ /** The resolved base URL for this API. */
1761
+ get baseUrl() {
1762
+ return this.api.baseUrl;
1763
+ }
1764
+ /** All available operations from the parsed spec. */
1765
+ get operations() {
1766
+ return this.api.operations;
1767
+ }
1768
+ /** Authentication schemes declared in the spec. Useful for building auth UIs. */
1769
+ get authSchemes() {
1770
+ return this.api.authSchemes;
1771
+ }
1772
+ /**
1773
+ * Set authentication credentials for all subsequent requests.
1774
+ * @param auth - Single credential or array for composing multiple schemes
1775
+ */
1776
+ setAuth(auth) {
1777
+ this.auth = auth;
1778
+ }
1779
+ /**
1780
+ * Clear authentication credentials.
1781
+ */
1782
+ clearAuth() {
1783
+ this.auth = void 0;
1784
+ }
1785
+ /**
1786
+ * Find an operation by its ID.
1787
+ * @param operationId - The operation ID to search for (e.g. 'listUsers', 'get_users_userId')
1788
+ * @returns The operation, or undefined if not found
1789
+ */
1790
+ findOperation(operationId) {
1791
+ return this.api.operations.find((op) => op.id === operationId);
1792
+ }
1793
+ /**
1794
+ * Execute an operation by ID with arguments.
1795
+ *
1796
+ * @param operationId - The operation ID from the parsed spec
1797
+ * @param args - Key-value pairs for path, query, header, and body parameters
1798
+ * @param options - Per-call overrides for auth, accept header, error behavior, and redirect mode
1799
+ * @returns The execution result with status, parsed data, and response metadata
1800
+ * @throws {ApiInvokeError} For network, CORS, timeout, and (by default) HTTP errors
1801
+ * @throws {Error} If the operation ID is not found
1802
+ *
1803
+ * @example
1804
+ * ```ts
1805
+ * const result = await client.execute('getUser', { userId: 123 })
1806
+ * console.log(result.status, result.data)
1807
+ * ```
1808
+ */
1809
+ async execute(operationId, args = {}, options) {
1810
+ const operation = this.findOperation(operationId);
1811
+ if (!operation) {
1812
+ throw new Error(`Operation "${operationId}" not found. Available: ${this.api.operations.map((o) => o.id).join(", ")}`);
1813
+ }
1814
+ return executeOperation(this.api.baseUrl, operation, args, {
1815
+ auth: options?.auth ?? this.auth,
1816
+ middleware: this.middleware,
1817
+ fetch: this.fetchFn,
1818
+ timeoutMs: this.timeoutMs,
1819
+ accept: options?.accept,
1820
+ throwOnHttpError: options?.throwOnHttpError,
1821
+ redirect: options?.redirect
1822
+ });
1823
+ }
1824
+ /**
1825
+ * Execute an operation as a stream, returning an async iterable of SSE events.
1826
+ * Errors always throw (no non-throwing mode for streams).
1827
+ *
1828
+ * @param operationId - The operation ID from the parsed spec
1829
+ * @param args - Key-value pairs for path, query, header, and body parameters
1830
+ * @param options - Per-call overrides for auth, accept header, abort signal, and event callback. The client-level `timeoutMs` applies to the initial connection.
1831
+ * @returns Streaming result with an async iterable `stream` property
1832
+ * @throws {ApiInvokeError} For network, CORS, timeout, and HTTP errors
1833
+ * @throws {Error} If the operation ID is not found
1834
+ *
1835
+ * @example
1836
+ * ```ts
1837
+ * const result = await client.executeStream('chatCompletion', {
1838
+ * model: 'gpt-4', messages: [{ role: 'user', content: 'Hi' }], stream: true,
1839
+ * })
1840
+ * for await (const event of result.stream) {
1841
+ * console.log(event.data)
1842
+ * }
1843
+ * ```
1844
+ */
1845
+ async executeStream(operationId, args = {}, options) {
1846
+ const operation = this.findOperation(operationId);
1847
+ if (!operation) {
1848
+ throw new Error(`Operation "${operationId}" not found. Available: ${this.api.operations.map((o) => o.id).join(", ")}`);
1849
+ }
1850
+ return executeOperationStream(this.api.baseUrl, operation, args, {
1851
+ auth: options?.auth ?? this.auth,
1852
+ middleware: this.middleware,
1853
+ fetch: this.fetchFn,
1854
+ timeoutMs: this.timeoutMs,
1855
+ accept: options?.accept,
1856
+ signal: options?.signal,
1857
+ onEvent: options?.onEvent
1858
+ });
1859
+ }
1860
+ };
1861
+ async function createClient(input, options = {}) {
1862
+ let api;
1863
+ if (typeof input === "string") {
1864
+ if (isSpecUrl(input)) {
1865
+ api = await fetchAndParseSpec(input, options);
1866
+ } else if (isGraphQLUrl(input)) {
1867
+ const { parseGraphQLSchema: parseGraphQLSchema2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
1868
+ api = await parseGraphQLSchema2(input, { endpoint: input, fetch: options.fetch });
1869
+ } else {
1870
+ api = await tryContentDetection(input, options);
1871
+ }
1872
+ } else {
1873
+ const obj = input;
1874
+ if (isGraphQLIntrospection(obj)) {
1875
+ const { parseGraphQLSchema: parseGraphQLSchema2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
1876
+ api = await parseGraphQLSchema2(input, { endpoint: options.specUrl, fetch: options.fetch });
1877
+ } else {
1878
+ api = await parseOpenAPISpec(input, { specUrl: options.specUrl });
1879
+ }
1880
+ }
1881
+ return finalize(api, options);
1882
+ }
1883
+ async function fetchAndParseSpec(url, options) {
1884
+ const fetchFn = options.fetch ?? globalThis.fetch;
1885
+ const response = await fetchFn(url);
1886
+ if (!response.ok) {
1887
+ throw new Error(`Failed to fetch spec: ${response.status} ${response.statusText}`);
1888
+ }
1889
+ const text = await response.text();
1890
+ let specObject;
1891
+ try {
1892
+ specObject = JSON.parse(text);
1893
+ } catch {
1894
+ return parseOpenAPISpec(url, { specUrl: url });
1895
+ }
1896
+ return parseOpenAPISpec(specObject, { specUrl: url });
1897
+ }
1898
+ async function tryContentDetection(url, options) {
1899
+ const fetchFn = options.fetch ?? globalThis.fetch;
1900
+ let response;
1901
+ try {
1902
+ response = await fetchFn(url);
1903
+ } catch (error) {
1904
+ if (error instanceof TypeError || error instanceof DOMException && error.name === "AbortError") {
1905
+ return parseRawUrl(url);
1906
+ }
1907
+ throw error;
1908
+ }
1909
+ if (!response.ok) {
1910
+ return parseRawUrl(url);
1911
+ }
1912
+ let text;
1913
+ try {
1914
+ text = await response.text();
1915
+ } catch {
1916
+ return parseRawUrl(url);
1917
+ }
1918
+ let parsed;
1919
+ try {
1920
+ parsed = JSON.parse(text);
1921
+ } catch {
1922
+ return parseRawUrl(url);
1923
+ }
1924
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1925
+ return parseRawUrl(url);
1926
+ }
1927
+ const obj = parsed;
1928
+ if (isSpecContent(obj)) {
1929
+ return parseOpenAPISpec(obj, { specUrl: url });
1930
+ }
1931
+ if (isGraphQLIntrospection(obj)) {
1932
+ const { parseGraphQLSchema: parseGraphQLSchema2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
1933
+ return parseGraphQLSchema2(obj, { endpoint: url, fetch: options.fetch });
1934
+ }
1935
+ return parseRawUrl(url);
1936
+ }
1937
+ async function finalize(api, options) {
1938
+ if (options.enricher) {
1939
+ api = await Promise.resolve(options.enricher.enrichAPI(api));
1940
+ }
1941
+ return new ApiInvokeClient(api, options);
1942
+ }
1943
+
1944
+ // src/index.ts
1945
+ init_types();
1946
+ init_errors();
1947
+
1948
+ // src/core/auth-config.ts
1949
+ init_types();
1950
+ var AuthConfigType = {
1951
+ BEARER: "bearer",
1952
+ HEADER: "header",
1953
+ API_KEY: "apikey",
1954
+ NONE: "none"
1955
+ };
1956
+ function toAuth(config) {
1957
+ switch (config.type) {
1958
+ case AuthConfigType.BEARER:
1959
+ return config.token ? { type: exports.AuthType.BEARER, token: config.token } : void 0;
1960
+ case AuthConfigType.HEADER:
1961
+ return config.headerName && config.headerValue ? { type: exports.AuthType.API_KEY, location: exports.ParamLocation.HEADER, name: config.headerName, value: config.headerValue } : void 0;
1962
+ case AuthConfigType.API_KEY:
1963
+ return config.paramName && config.paramValue ? { type: exports.AuthType.API_KEY, location: exports.ParamLocation.QUERY, name: config.paramName, value: config.paramValue } : void 0;
1964
+ case AuthConfigType.NONE:
1965
+ return void 0;
1966
+ }
1967
+ }
1968
+
1969
+ // src/middleware/retry.ts
1970
+ var DEFAULT_RETRYABLE_STATUSES = [429, 500, 502, 503, 504];
1971
+ function parseRetryAfter(value) {
1972
+ const seconds = Number(value);
1973
+ if (!Number.isNaN(seconds) && seconds >= 0) {
1974
+ return seconds * 1e3;
1975
+ }
1976
+ const date = Date.parse(value);
1977
+ if (!Number.isNaN(date)) {
1978
+ const delayMs = date - Date.now();
1979
+ return delayMs > 0 ? delayMs : 0;
1980
+ }
1981
+ return void 0;
1982
+ }
1983
+ function calculateDelay(attempt, initialDelayMs, multiplier, maxDelayMs, jitter) {
1984
+ const base = initialDelayMs * Math.pow(multiplier, attempt);
1985
+ const capped = Math.min(base, maxDelayMs);
1986
+ const jitterAmount = capped * jitter * (Math.random() * 2 - 1);
1987
+ return Math.max(0, Math.round(capped + jitterAmount));
1988
+ }
1989
+ function withRetry(options = {}, baseFetch) {
1990
+ const {
1991
+ maxRetries = 3,
1992
+ initialDelayMs = 1e3,
1993
+ maxDelayMs = 3e4,
1994
+ multiplier = 2,
1995
+ jitter = 0.1,
1996
+ retryableStatuses = DEFAULT_RETRYABLE_STATUSES,
1997
+ onRetry
1998
+ } = options;
1999
+ const fetchFn = baseFetch ?? globalThis.fetch;
2000
+ return async function retryFetch(input, init) {
2001
+ let lastError;
2002
+ let lastResponse;
2003
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
2004
+ try {
2005
+ const response = await fetchFn(input, init);
2006
+ if (retryableStatuses.includes(response.status) && attempt < maxRetries) {
2007
+ lastResponse = response;
2008
+ const retryAfter = response.headers.get("retry-after");
2009
+ let delayMs;
2010
+ if (retryAfter) {
2011
+ const parsed = parseRetryAfter(retryAfter);
2012
+ delayMs = parsed !== void 0 ? Math.min(parsed, maxDelayMs) : calculateDelay(attempt, initialDelayMs, multiplier, maxDelayMs, jitter);
2013
+ } else {
2014
+ delayMs = calculateDelay(attempt, initialDelayMs, multiplier, maxDelayMs, jitter);
2015
+ }
2016
+ onRetry?.(attempt + 1, delayMs, response.status);
2017
+ await sleep(delayMs);
2018
+ continue;
2019
+ }
2020
+ return response;
2021
+ } catch (error) {
2022
+ lastError = error;
2023
+ if (attempt < maxRetries) {
2024
+ const delayMs = calculateDelay(attempt, initialDelayMs, multiplier, maxDelayMs, jitter);
2025
+ onRetry?.(attempt + 1, delayMs);
2026
+ await sleep(delayMs);
2027
+ continue;
2028
+ }
2029
+ }
2030
+ }
2031
+ if (lastResponse) return lastResponse;
2032
+ throw lastError;
2033
+ };
2034
+ }
2035
+ function sleep(ms) {
2036
+ return new Promise((resolve) => setTimeout(resolve, ms));
2037
+ }
2038
+
2039
+ // src/middleware/cors-proxy.ts
2040
+ function defaultRewrite(url) {
2041
+ return `/api-proxy/${encodeURIComponent(url)}`;
2042
+ }
2043
+ function defaultShouldProxy(url) {
2044
+ return url.startsWith("http://") || url.startsWith("https://");
2045
+ }
2046
+ function corsProxy(options = {}) {
2047
+ const rewrite = options.rewrite ?? defaultRewrite;
2048
+ const shouldProxy = options.shouldProxy ?? defaultShouldProxy;
2049
+ return {
2050
+ name: "cors-proxy",
2051
+ onRequest(url, init) {
2052
+ if (shouldProxy(url)) {
2053
+ return { url: rewrite(url), init };
2054
+ }
2055
+ return { url, init };
2056
+ }
2057
+ };
2058
+ }
2059
+
2060
+ // src/middleware/logging.ts
2061
+ init_types();
2062
+ var DEFAULT_SENSITIVE_HEADERS = ["authorization", "x-api-key", "cookie"];
2063
+ var DEFAULT_SENSITIVE_PARAMS = ["api_key", "apikey", "key", "token", "access_token"];
2064
+ function maskHeaderValue(name, value) {
2065
+ const lower = name.toLowerCase();
2066
+ if (lower === "authorization") {
2067
+ const space = value.indexOf(" ");
2068
+ if (space > 0) {
2069
+ return `${value.substring(0, space)} ***`;
2070
+ }
2071
+ return "***";
2072
+ }
2073
+ return "***";
2074
+ }
2075
+ function maskUrl(url, sensitiveParams) {
2076
+ try {
2077
+ const parsed = new URL(url);
2078
+ let masked = false;
2079
+ for (const param of sensitiveParams) {
2080
+ if (parsed.searchParams.has(param)) {
2081
+ parsed.searchParams.set(param, "***");
2082
+ masked = true;
2083
+ }
2084
+ }
2085
+ return masked ? parsed.toString() : url;
2086
+ } catch {
2087
+ return url;
2088
+ }
2089
+ }
2090
+ function formatHeaders(init, sensitiveHeaders) {
2091
+ const headers = init.headers;
2092
+ if (!headers) return void 0;
2093
+ const result = {};
2094
+ const sensitiveSet = new Set(sensitiveHeaders.map((h) => h.toLowerCase()));
2095
+ if (headers instanceof Headers) {
2096
+ headers.forEach((value, key) => {
2097
+ result[key] = sensitiveSet.has(key.toLowerCase()) ? maskHeaderValue(key, value) : value;
2098
+ });
2099
+ } else if (Array.isArray(headers)) {
2100
+ for (const [key, value] of headers) {
2101
+ result[key] = sensitiveSet.has(key.toLowerCase()) ? maskHeaderValue(key, value) : value;
2102
+ }
2103
+ } else {
2104
+ for (const [key, value] of Object.entries(headers)) {
2105
+ result[key] = sensitiveSet.has(key.toLowerCase()) ? maskHeaderValue(key, value) : value;
2106
+ }
2107
+ }
2108
+ return Object.keys(result).length > 0 ? result : void 0;
2109
+ }
2110
+ function logging(options = {}) {
2111
+ const {
2112
+ log = console.log,
2113
+ logBody = false,
2114
+ prefix = "api-invoke"
2115
+ } = options;
2116
+ const sensitiveHeaders = [
2117
+ ...DEFAULT_SENSITIVE_HEADERS,
2118
+ ...(options.sensitiveHeaders ?? []).map((h) => h.toLowerCase())
2119
+ ];
2120
+ const sensitiveParams = [
2121
+ ...DEFAULT_SENSITIVE_PARAMS,
2122
+ ...options.sensitiveParams ?? []
2123
+ ];
2124
+ let requestStart = 0;
2125
+ return {
2126
+ name: "logging",
2127
+ onRequest(url, init) {
2128
+ requestStart = performance.now();
2129
+ const maskedUrl = maskUrl(url, sensitiveParams);
2130
+ const method = (init.method ?? exports.HttpMethod.GET).toUpperCase();
2131
+ const headers = formatHeaders(init, sensitiveHeaders);
2132
+ const parts = [`[${prefix}] \u2192 ${method} ${maskedUrl}`];
2133
+ if (headers) {
2134
+ parts.push(` headers: ${JSON.stringify(headers)}`);
2135
+ }
2136
+ if (logBody && init.body) {
2137
+ const bodyStr = typeof init.body === "string" ? init.body : "<binary>";
2138
+ parts.push(` body: ${bodyStr}`);
2139
+ }
2140
+ log(parts.join("\n"));
2141
+ return { url, init };
2142
+ },
2143
+ onResponse(response) {
2144
+ const elapsed = requestStart ? `${Math.round(performance.now() - requestStart)}ms` : "?ms";
2145
+ const status = response.status;
2146
+ const statusText = response.statusText;
2147
+ log(`[${prefix}] \u2190 ${status} ${statusText} (${elapsed})`);
2148
+ return response;
2149
+ },
2150
+ onError(error) {
2151
+ log(`[${prefix}] \u2715 ${error.message}`);
2152
+ }
2153
+ };
2154
+ }
2155
+
2156
+ // src/middleware/oauth-refresh.ts
2157
+ function withOAuthRefresh(options, baseFetch) {
2158
+ const fetchFn = baseFetch ?? globalThis.fetch;
2159
+ let currentRefreshToken = options.refreshToken;
2160
+ let refreshPromise = null;
2161
+ return async function oauthRefreshFetch(input, init) {
2162
+ const response = await fetchFn(input, init);
2163
+ if (response.status !== 401) return response;
2164
+ if (init?.body instanceof ReadableStream || typeof init?.body === "object" && init.body !== null && Symbol.asyncIterator in init.body || typeof init?.body === "object" && init.body !== null && typeof init.body.pipe === "function") {
2165
+ console.warn(
2166
+ "[api-invoke] Cannot retry request with stream body after 401 \u2014 the stream was consumed. Use a string or Blob body for OAuth2-protected requests."
2167
+ );
2168
+ return response;
2169
+ }
2170
+ let tokens;
2171
+ try {
2172
+ if (!refreshPromise) {
2173
+ refreshPromise = refreshOAuth2Token(options.tokenUrl, currentRefreshToken, {
2174
+ clientId: options.clientId,
2175
+ clientSecret: options.clientSecret,
2176
+ scopes: options.scopes,
2177
+ fetch: fetchFn
2178
+ }).then(async (result) => {
2179
+ if (result.refreshToken) currentRefreshToken = result.refreshToken;
2180
+ if (options.onTokenRefresh) {
2181
+ try {
2182
+ await options.onTokenRefresh(result);
2183
+ } catch (callbackError) {
2184
+ console.warn(
2185
+ "[api-invoke] onTokenRefresh callback threw \u2014 new tokens were NOT persisted. The refreshed token is used for this request, but may be lost on restart.",
2186
+ callbackError
2187
+ );
2188
+ }
2189
+ }
2190
+ return result;
2191
+ }).finally(() => {
2192
+ refreshPromise = null;
2193
+ });
2194
+ }
2195
+ tokens = await refreshPromise;
2196
+ } catch (error) {
2197
+ console.warn(
2198
+ "[api-invoke] OAuth2 token refresh failed, returning original 401.",
2199
+ error
2200
+ );
2201
+ return response;
2202
+ }
2203
+ let baseHeaders = {};
2204
+ if (input instanceof Request) {
2205
+ input.headers.forEach((value, key) => {
2206
+ baseHeaders[key] = value;
2207
+ });
2208
+ }
2209
+ const initHeaders = typeof init?.headers === "object" && !Array.isArray(init.headers) && !(init.headers instanceof Headers) ? { ...init.headers } : Object.fromEntries(new Headers(init?.headers).entries());
2210
+ const existingHeaders = { ...baseHeaders, ...initHeaders };
2211
+ for (const key of Object.keys(existingHeaders)) {
2212
+ if (key.toLowerCase() === "authorization") delete existingHeaders[key];
2213
+ }
2214
+ existingHeaders["Authorization"] = `Bearer ${tokens.accessToken}`;
2215
+ return fetchFn(input, { ...init, headers: existingHeaders });
2216
+ };
2217
+ }
2218
+
2219
+ // src/adapters/manual/builder.ts
2220
+ init_types();
2221
+ var APIBuilder = class {
2222
+ _title;
2223
+ _version = "1.0.0";
2224
+ _baseUrl = "";
2225
+ _operations = [];
2226
+ constructor(title) {
2227
+ this._title = title;
2228
+ }
2229
+ /**
2230
+ * Set the API version string.
2231
+ * @param v - Version string (e.g. '2.0.0'). Default: '1.0.0'.
2232
+ */
2233
+ version(v) {
2234
+ this._version = v;
2235
+ return this;
2236
+ }
2237
+ /**
2238
+ * Set the base URL for all endpoints.
2239
+ * @param url - Base URL (e.g. 'https://api.example.com/v1'). Required before calling {@link build}.
2240
+ */
2241
+ baseUrl(url) {
2242
+ this._baseUrl = url;
2243
+ return this;
2244
+ }
2245
+ /** Add a GET endpoint. */
2246
+ get(path, options = {}) {
2247
+ return this.endpoint(exports.HttpMethod.GET, path, options);
2248
+ }
2249
+ /** Add a POST endpoint. */
2250
+ post(path, options = {}) {
2251
+ return this.endpoint(exports.HttpMethod.POST, path, options);
2252
+ }
2253
+ /** Add a PUT endpoint. */
2254
+ put(path, options = {}) {
2255
+ return this.endpoint(exports.HttpMethod.PUT, path, options);
2256
+ }
2257
+ /** Add a PATCH endpoint. */
2258
+ patch(path, options = {}) {
2259
+ return this.endpoint(exports.HttpMethod.PATCH, path, options);
2260
+ }
2261
+ /** Add a DELETE endpoint. */
2262
+ delete(path, options = {}) {
2263
+ return this.endpoint(exports.HttpMethod.DELETE, path, options);
2264
+ }
2265
+ /**
2266
+ * Add an endpoint with any HTTP method.
2267
+ * Path parameters are auto-detected from `{placeholder}` segments.
2268
+ *
2269
+ * @param method - HTTP method (e.g. 'GET', 'POST')
2270
+ * @param path - URL path template (e.g. '/users/{userId}')
2271
+ * @param options - Endpoint configuration
2272
+ */
2273
+ endpoint(method, path, options = {}) {
2274
+ const id = options.id ?? `${method.toLowerCase()}_${path.replace(/[{}\/]/g, "_").replace(/^_|_$/g, "").replace(/_+/g, "_")}`;
2275
+ const pathParamNames = [...path.matchAll(/\{(\w+)\}/g)].map((m) => m[1]);
2276
+ const parameters = [];
2277
+ for (const name of pathParamNames) {
2278
+ const explicit = options.params?.[name];
2279
+ const def = typeof explicit === "string" ? { type: explicit } : explicit ?? {};
2280
+ parameters.push({
2281
+ name,
2282
+ in: exports.ParamLocation.PATH,
2283
+ required: true,
2284
+ description: def.description ?? "",
2285
+ schema: { type: def.type ?? "string", default: def.default }
2286
+ });
2287
+ }
2288
+ if (options.params) {
2289
+ for (const [name, raw] of Object.entries(options.params)) {
2290
+ if (pathParamNames.includes(name)) continue;
2291
+ const def = typeof raw === "string" ? { type: raw } : raw;
2292
+ parameters.push({
2293
+ name,
2294
+ in: def.in ?? exports.ParamLocation.QUERY,
2295
+ required: def.required ?? false,
2296
+ description: def.description ?? "",
2297
+ schema: { type: def.type ?? "string", default: def.default }
2298
+ });
2299
+ }
2300
+ }
2301
+ let requestBody;
2302
+ if (options.body) {
2303
+ const properties = {};
2304
+ if (options.body.properties) {
2305
+ for (const [name, raw] of Object.entries(options.body.properties)) {
2306
+ if (typeof raw === "string") {
2307
+ properties[name] = { type: raw };
2308
+ } else {
2309
+ properties[name] = {
2310
+ type: raw.type,
2311
+ description: raw.description,
2312
+ format: raw.format,
2313
+ enum: raw.enum
2314
+ };
2315
+ }
2316
+ }
2317
+ }
2318
+ requestBody = {
2319
+ required: options.body.required ?? true,
2320
+ contentType: options.body.contentType ?? exports.ContentType.JSON,
2321
+ schema: {
2322
+ type: "object",
2323
+ raw: {},
2324
+ properties,
2325
+ required: options.body.requiredFields
2326
+ }
2327
+ };
2328
+ }
2329
+ this._operations.push({
2330
+ id,
2331
+ path,
2332
+ method: method.toUpperCase(),
2333
+ summary: options.summary,
2334
+ description: options.description,
2335
+ parameters,
2336
+ requestBody,
2337
+ responseContentType: options.responseContentType,
2338
+ tags: options.tags ?? []
2339
+ });
2340
+ return this;
2341
+ }
2342
+ /**
2343
+ * Build the {@link ParsedAPI} from the configured endpoints.
2344
+ * @returns A ParsedAPI ready to use with {@link ApiInvokeClient}
2345
+ * @throws {Error} If `baseUrl` is not set or no endpoints are defined
2346
+ */
2347
+ build() {
2348
+ if (!this._baseUrl) {
2349
+ throw new Error('baseUrl is required. Call .baseUrl("https://...") before .build().');
2350
+ }
2351
+ if (this._operations.length === 0) {
2352
+ throw new Error("At least one endpoint is required. Call .get(), .post(), etc. before .build().");
2353
+ }
2354
+ return {
2355
+ title: this._title,
2356
+ version: this._version,
2357
+ baseUrl: this._baseUrl,
2358
+ operations: [...this._operations],
2359
+ authSchemes: [],
2360
+ specFormat: exports.SpecFormat.MANUAL
2361
+ };
2362
+ }
2363
+ };
2364
+ function defineAPI(title) {
2365
+ return new APIBuilder(title);
2366
+ }
2367
+
2368
+ // src/index.ts
2369
+ init_parser();
2370
+
2371
+ // src/adapters/graphql/errors.ts
2372
+ init_errors();
2373
+ function hasGraphQLErrors(result) {
2374
+ const body = result.data;
2375
+ return body != null && Array.isArray(body.errors) && body.errors.length > 0;
2376
+ }
2377
+ function getGraphQLErrors(result) {
2378
+ const body = result.data;
2379
+ if (body != null && Array.isArray(body.errors)) return body.errors;
2380
+ return [];
2381
+ }
2382
+ function throwOnGraphQLErrors(result) {
2383
+ const body = result.data;
2384
+ if (body == null || !Array.isArray(body.errors) || body.errors.length === 0) return;
2385
+ if (body.data != null) return;
2386
+ const errors = body.errors;
2387
+ const messages = errors.map((e) => e.message).join("; ");
2388
+ throw graphqlError(messages, result.status, body);
2389
+ }
2390
+
2391
+ exports.APIBuilder = APIBuilder;
2392
+ exports.ApiInvokeClient = ApiInvokeClient;
2393
+ exports.AuthConfigType = AuthConfigType;
2394
+ exports.authError = authError;
2395
+ exports.buildRequest = buildRequest;
2396
+ exports.buildUrl = buildUrl;
2397
+ exports.corsError = corsError;
2398
+ exports.corsProxy = corsProxy;
2399
+ exports.createClient = createClient;
2400
+ exports.defineAPI = defineAPI;
2401
+ exports.deriveBaseUrl = deriveBaseUrl;
2402
+ exports.executeOperation = executeOperation;
2403
+ exports.executeOperationStream = executeOperationStream;
2404
+ exports.executeRaw = executeRaw;
2405
+ exports.executeRawStream = executeRawStream;
2406
+ exports.getGraphQLErrors = getGraphQLErrors;
2407
+ exports.graphqlError = graphqlError;
2408
+ exports.hasGraphQLErrors = hasGraphQLErrors;
2409
+ exports.httpError = httpError;
2410
+ exports.injectAuth = injectAuth;
2411
+ exports.isGraphQLUrl = isGraphQLUrl;
2412
+ exports.isSpecContent = isSpecContent;
2413
+ exports.isSpecUrl = isSpecUrl;
2414
+ exports.logging = logging;
2415
+ exports.maskAuth = maskAuth;
2416
+ exports.networkError = networkError;
2417
+ exports.parseError = parseError;
2418
+ exports.parseGraphQLSchema = parseGraphQLSchema;
2419
+ exports.parseOpenAPISpec = parseOpenAPISpec;
2420
+ exports.parseRawUrl = parseRawUrl;
2421
+ exports.parseRawUrls = parseRawUrls;
2422
+ exports.parseSSE = parseSSE;
2423
+ exports.refreshOAuth2Token = refreshOAuth2Token;
2424
+ exports.throwOnGraphQLErrors = throwOnGraphQLErrors;
2425
+ exports.timeoutError = timeoutError;
2426
+ exports.toAuth = toAuth;
2427
+ exports.withOAuthRefresh = withOAuthRefresh;
2428
+ exports.withRetry = withRetry;
2429
+ //# sourceMappingURL=index.cjs.map
2430
+ //# sourceMappingURL=index.cjs.map