@bagelink/sdk 1.7.104 → 1.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -7,23 +7,109 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
7
7
  const axios__default = /*#__PURE__*/_interopDefaultCompat(axios$1);
8
8
 
9
9
  function isSSEStream(operation) {
10
- const description = operation.description?.toLowerCase() || "";
11
- const summary = operation.summary?.toLowerCase() || "";
12
- const hasSSEKeywords = description.includes("sse") || description.includes("server-sent events") || description.includes("event stream") || summary.includes("stream");
13
10
  const responses = operation.responses || {};
14
- const hasEventStreamContentType = Object.values(responses).some((response) => {
15
- if ("content" in response) {
16
- return "text/event-stream" in (response.content || {});
11
+ for (const [statusCode, response] of Object.entries(responses)) {
12
+ if (!statusCode.startsWith("2")) continue;
13
+ if ("$ref" in response) continue;
14
+ const content = response.content || {};
15
+ const contentTypes = Object.keys(content);
16
+ if (contentTypes.length > 0) {
17
+ if ("text/event-stream" in content) {
18
+ return true;
19
+ }
17
20
  }
18
- return false;
21
+ }
22
+ const hasDefinedContentTypes = Object.entries(responses).some(([statusCode, response]) => {
23
+ if (!statusCode.startsWith("2")) return false;
24
+ if ("$ref" in response) return false;
25
+ const content = response.content || {};
26
+ return Object.keys(content).length > 0;
19
27
  });
20
- return hasSSEKeywords || hasEventStreamContentType;
28
+ if (hasDefinedContentTypes) {
29
+ return false;
30
+ }
31
+ const description = operation.description?.toLowerCase() || "";
32
+ const summary = operation.summary?.toLowerCase() || "";
33
+ const hasExplicitSSEMention = /\bsse\b/.test(description) || /server-sent events/i.test(description) || /text\/event-stream/i.test(description) || /\bsse\b/.test(summary);
34
+ return hasExplicitSSEMention;
21
35
  }
22
36
  function extractSSEEventTypes(operation) {
23
37
  const description = operation.description || "";
38
+ const types = /* @__PURE__ */ new Set();
39
+ const markdownMatches = description.matchAll(/^\d+\.\s+\*\*([a-z_]+)\*\*/gm);
40
+ for (const match of markdownMatches) {
41
+ types.add(match[1]);
42
+ }
24
43
  const typeMatches = description.matchAll(/type:\s*"([^"]+)"/g);
25
- const types = Array.from(typeMatches).map((match) => match[1]);
26
- return types.length > 0 ? types : void 0;
44
+ for (const match of typeMatches) {
45
+ types.add(match[1]);
46
+ }
47
+ const responses = operation.responses || {};
48
+ for (const response of Object.values(responses)) {
49
+ if ("content" in response) {
50
+ const content = response.content || {};
51
+ const eventStream = content["text/event-stream"];
52
+ if (eventStream?.example) {
53
+ const exampleMatches = eventStream.example.matchAll(/^event:\s*([a-z_]+)/gm);
54
+ for (const match of exampleMatches) {
55
+ types.add(match[1]);
56
+ }
57
+ }
58
+ }
59
+ }
60
+ return types.size > 0 ? Array.from(types).sort() : void 0;
61
+ }
62
+ function extractSSEEventInfo(operation) {
63
+ const description = operation.description || "";
64
+ const eventInfos = [];
65
+ const eventSections = description.split(/(?=^\d+\.\s+\*\*)/m);
66
+ for (const section of eventSections) {
67
+ const eventMatch = section.match(/^\d+\.\s+\*\*([a-z_]+)\*\*:\s*([^\n]+)/m);
68
+ if (!eventMatch) continue;
69
+ const [, eventName, eventDescription] = eventMatch;
70
+ const fields = [];
71
+ const fieldMatches = section.matchAll(/^\s+[-*]\s+`([^`]+)`:\s*([^\n]+)/gm);
72
+ for (const fieldMatch of fieldMatches) {
73
+ const [, fieldName, fieldDescription] = fieldMatch;
74
+ fields.push({
75
+ name: fieldName,
76
+ description: fieldDescription.trim()
77
+ });
78
+ }
79
+ eventInfos.push({
80
+ name: eventName,
81
+ description: eventDescription,
82
+ fields: fields.length > 0 ? fields : void 0
83
+ });
84
+ }
85
+ if (eventInfos.length === 0) {
86
+ const responses = operation.responses || {};
87
+ for (const response of Object.values(responses)) {
88
+ if ("content" in response) {
89
+ const content = response.content || {};
90
+ const eventStream = content["text/event-stream"];
91
+ if (eventStream?.example) {
92
+ const exampleMatches = eventStream.example.matchAll(/data:\s*(\{[^}]+\})/g);
93
+ const seenEvents = /* @__PURE__ */ new Set();
94
+ for (const match of exampleMatches) {
95
+ try {
96
+ const data = JSON.parse(match[1]);
97
+ if (data.type && !seenEvents.has(data.type)) {
98
+ seenEvents.add(data.type);
99
+ const fields = Object.keys(data).filter((key) => key !== "type" && key !== "sequence").map((key) => ({ name: key }));
100
+ eventInfos.push({
101
+ name: data.type,
102
+ fields: fields.length > 0 ? fields : void 0
103
+ });
104
+ }
105
+ } catch {
106
+ }
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ return eventInfos.length > 0 ? eventInfos : void 0;
27
113
  }
28
114
 
29
115
  function getPath(pathsObject, path) {
@@ -305,6 +391,7 @@ const primitiveTypes = [
305
391
  const functionsInventory = {};
306
392
  const pathOperations = [];
307
393
  const streamEventTypes = {};
394
+ const streamEventInfo = {};
308
395
  function collectTypeForImportStatement(typeName) {
309
396
  typeName = typeName.trim().replace("[]", "");
310
397
  if (typeName.includes("|")) {
@@ -488,6 +575,7 @@ function generateStreamFunction(method, path, formattedPath, allParams, requestB
488
575
  const eventTypesExample = eventTypes?.length ? eventTypes.map((t) => `
489
576
  * .on('${t}', (data) => console.log(data))`).join("") : "\n * .on('message', (data) => console.log(data))";
490
577
  const typeAnnotation = eventTypes?.length ? `StreamController<${streamTypeName}>` : "StreamController";
578
+ const pathForUrl = formattedPath.startsWith("`") ? formattedPath.slice(1, -1) : formattedPath.slice(1, -1);
491
579
  if (method === "post") {
492
580
  return `{
493
581
  /**
@@ -502,7 +590,7 @@ function generateStreamFunction(method, path, formattedPath, allParams, requestB
502
590
  * stream.close()
503
591
  */
504
592
  stream: (${allParams}, options?: SSEStreamOptions): ${typeAnnotation} => {
505
- const url = \`\${${baseUrlRef}}${formattedPath}\`
593
+ const url = \`\${${baseUrlRef}}${pathForUrl}\`
506
594
  return createSSEStreamPost<${streamTypeName}>(url, ${bodyVar}, options)
507
595
  },
508
596
  /**
@@ -526,7 +614,7 @@ function generateStreamFunction(method, path, formattedPath, allParams, requestB
526
614
  * stream.close()
527
615
  */
528
616
  stream: (${allParams}, options?: SSEStreamOptions): ${typeAnnotation} => {
529
- const url = \`\${${baseUrlRef}}${formattedPath}\`
617
+ const url = \`\${${baseUrlRef}}${pathForUrl}\`
530
618
  return createSSEStream<${streamTypeName}>(url, options)
531
619
  },
532
620
  /**
@@ -626,6 +714,11 @@ function generateFunctionForOperation(method, path, operation) {
626
714
  const functionComment = buildJSDocComment(operation, method, path);
627
715
  if (isStream) {
628
716
  const eventTypes = extractSSEEventTypes(operation);
717
+ const eventInfo = extractSSEEventInfo(operation);
718
+ const streamTypeName = generateStreamTypeName(path);
719
+ if (eventInfo?.length) {
720
+ streamEventInfo[streamTypeName] = eventInfo;
721
+ }
629
722
  return functionComment + generateStreamFunction(
630
723
  method,
631
724
  path,
@@ -738,19 +831,60 @@ function generateStreamEventTypeDefinitions() {
738
831
  typeDefs += "// Stream Event Type Definitions (Fully Typed!)\n";
739
832
  typeDefs += "// ============================================================================\n\n";
740
833
  for (const [typeName, events] of Object.entries(streamEventTypes)) {
834
+ const eventInfo = streamEventInfo[typeName];
741
835
  typeDefs += `/**
742
836
  * Event types for ${typeName.replace("StreamEvents", "")} stream
743
837
  `;
744
838
  typeDefs += ` * Events: ${events.map((e) => `"${e}"`).join(", ")}
745
- */
839
+ `;
840
+ if (eventInfo?.length) {
841
+ typeDefs += ` *
842
+ `;
843
+ for (const info of eventInfo) {
844
+ typeDefs += ` * - **${info.name}**: ${info.description || "Event data"}
845
+ `;
846
+ }
847
+ }
848
+ typeDefs += ` */
746
849
  `;
747
850
  typeDefs += `export interface ${typeName} {
748
851
  `;
749
- for (const event of events) {
750
- typeDefs += ` /** ${event} event data */
852
+ if (eventInfo?.length) {
853
+ for (const info of eventInfo) {
854
+ typeDefs += ` /**
855
+ * ${info.description || info.name}
856
+ `;
857
+ if (info.fields?.length) {
858
+ info.fields.forEach((field) => {
859
+ typeDefs += ` * - \`${field.name}\`: ${field.description || "Field data"}
860
+ `;
861
+ });
862
+ }
863
+ typeDefs += ` */
864
+ `;
865
+ if (info.fields?.length) {
866
+ typeDefs += ` ${info.name}: {
867
+ `;
868
+ for (const field of info.fields) {
869
+ typeDefs += ` /** ${field.description || field.name} */
870
+ `;
871
+ typeDefs += ` ${field.name}: any
872
+ `;
873
+ }
874
+ typeDefs += ` }
751
875
  `;
752
- typeDefs += ` ${event}: any // TODO: Define specific type from OpenAPI schema
876
+ } else {
877
+ typeDefs += ` ${info.name}: any
753
878
  `;
879
+ }
880
+ }
881
+ } else {
882
+ for (const event of events) {
883
+ typeDefs += ` /** ${event} event data */
884
+ `;
885
+ typeDefs += ` ${event}: any
886
+ `;
887
+ }
754
888
  }
755
889
  typeDefs += "}\n\n";
756
890
  }
@@ -1584,6 +1718,7 @@ exports.StreamController = StreamController;
1584
1718
  exports.createSSEStream = createSSEStream;
1585
1719
  exports.createSSEStreamPost = createSSEStreamPost;
1586
1720
  exports.dereference = dereference;
1721
+ exports.extractSSEEventInfo = extractSSEEventInfo;
1587
1722
  exports.extractSSEEventTypes = extractSSEEventTypes;
1588
1723
  exports.formatAPIErrorMessage = formatAPIErrorMessage;
1589
1724
  exports.getPath = getPath;
package/dist/index.d.cts CHANGED
@@ -508,16 +508,55 @@ interface SecurityRequirementObject {
508
508
 
509
509
  /**
510
510
  * Detects if an operation is an SSE (Server-Sent Events) stream endpoint
511
+ *
512
+ * Primary detection: Checks for text/event-stream content type in responses
513
+ * Fallback: Only if no content type is specified, check for explicit SSE keywords
514
+ *
511
515
  * @param operation - The OpenAPI operation object
512
516
  * @returns Whether the operation is an SSE stream
513
517
  */
514
518
  declare function isSSEStream(operation: OperationObject): boolean;
515
519
  /**
516
- * Extracts SSE event types from operation description
520
+ * Information about an SSE event type including its fields
521
+ */
522
+ interface SSEEventTypeInfo {
523
+ /** Event type name */
524
+ name: string;
525
+ /** Event description */
526
+ description?: string;
527
+ /** Fields in the event data */
528
+ fields?: Array<{
529
+ name: string;
530
+ description?: string;
531
+ type?: string;
532
+ }>;
533
+ }
534
+ /**
535
+ * Extracts SSE event types from operation description and examples
536
+ *
537
+ * Supports multiple documentation formats:
538
+ * 1. Markdown format: "1. **event_name**: Description"
539
+ * 2. Legacy format: type: "event_name"
540
+ * 3. SSE examples: event: event_name
541
+ *
517
542
  * @param operation - The OpenAPI operation object
518
543
  * @returns Array of event types or undefined
519
544
  */
520
545
  declare function extractSSEEventTypes(operation: OperationObject): string[] | undefined;
546
+ /**
547
+ * Extracts detailed SSE event information including field definitions
548
+ *
549
+ * Parses markdown documentation format:
550
+ * ```
551
+ * 1. **event_name**: Event description
552
+ * - `field_name`: Field description
553
+ * - `another_field`: Another description
554
+ * ```
555
+ *
556
+ * @param operation - The OpenAPI operation object
557
+ * @returns Array of event type information with fields
558
+ */
559
+ declare function extractSSEEventInfo(operation: OperationObject): SSEEventTypeInfo[] | undefined;
521
560
 
522
561
  interface OpenAPIResponse {
523
562
  types: string;
@@ -629,5 +668,5 @@ declare class Bagel {
629
668
  uploadFile<T>(file: File, options?: UploadOptions): Promise<T>;
630
669
  }
631
670
 
632
- export { Bagel, StreamController, createSSEStream, createSSEStreamPost, dereference, extractSSEEventTypes, formatAPIErrorMessage, getPath, isReferenceObject, isSSEStream, isSchemaObject, _default as openAPI };
633
- export type { BaseParameterObject, CallbackObject, CallbacksObject, ComponentsObject, ContactObject, ContentObject, DiscriminatorObject, EncodingObject, EncodingPropertyObject, ExampleObject, ExamplesObject, ExternalDocumentationObject, HeaderObject, HeadersObject, InfoObject, LicenseObject, LinkObject, LinkParametersObject, LinksObject, MediaTypeObject, OAuthFlowObject, OAuthFlowsObject, OpenAPIObject, OperationObject, ParameterLocation, ParameterObject, ParameterStyle, PathItemObject, PathsObject, ReferenceObject, RequestBodyObject, ResponseObject, ResponsesObject, SSEEvent, SSEStreamOptions, SchemaObject, SchemaObjectType, SchemasObject, ScopesObject, SecurityRequirementObject, SecuritySchemeObject, SecuritySchemeType, ServerObject, ServerVariableObject, StreamEventMap, TableToTypeMapping, Tables, TagObject, TypeFormatT, UploadOptions, User, XmlObject };
671
+ export { Bagel, StreamController, createSSEStream, createSSEStreamPost, dereference, extractSSEEventInfo, extractSSEEventTypes, formatAPIErrorMessage, getPath, isReferenceObject, isSSEStream, isSchemaObject, _default as openAPI };
672
+ export type { BaseParameterObject, CallbackObject, CallbacksObject, ComponentsObject, ContactObject, ContentObject, DiscriminatorObject, EncodingObject, EncodingPropertyObject, ExampleObject, ExamplesObject, ExternalDocumentationObject, HeaderObject, HeadersObject, InfoObject, LicenseObject, LinkObject, LinkParametersObject, LinksObject, MediaTypeObject, OAuthFlowObject, OAuthFlowsObject, OpenAPIObject, OperationObject, ParameterLocation, ParameterObject, ParameterStyle, PathItemObject, PathsObject, ReferenceObject, RequestBodyObject, ResponseObject, ResponsesObject, SSEEvent, SSEEventTypeInfo, SSEStreamOptions, SchemaObject, SchemaObjectType, SchemasObject, ScopesObject, SecurityRequirementObject, SecuritySchemeObject, SecuritySchemeType, ServerObject, ServerVariableObject, StreamEventMap, TableToTypeMapping, Tables, TagObject, TypeFormatT, UploadOptions, User, XmlObject };
package/dist/index.d.mts CHANGED
@@ -508,16 +508,55 @@ interface SecurityRequirementObject {
508
508
 
509
509
  /**
510
510
  * Detects if an operation is an SSE (Server-Sent Events) stream endpoint
511
+ *
512
+ * Primary detection: Checks for text/event-stream content type in responses
513
+ * Fallback: Only if no content type is specified, check for explicit SSE keywords
514
+ *
511
515
  * @param operation - The OpenAPI operation object
512
516
  * @returns Whether the operation is an SSE stream
513
517
  */
514
518
  declare function isSSEStream(operation: OperationObject): boolean;
515
519
  /**
516
- * Extracts SSE event types from operation description
520
+ * Information about an SSE event type including its fields
521
+ */
522
+ interface SSEEventTypeInfo {
523
+ /** Event type name */
524
+ name: string;
525
+ /** Event description */
526
+ description?: string;
527
+ /** Fields in the event data */
528
+ fields?: Array<{
529
+ name: string;
530
+ description?: string;
531
+ type?: string;
532
+ }>;
533
+ }
534
+ /**
535
+ * Extracts SSE event types from operation description and examples
536
+ *
537
+ * Supports multiple documentation formats:
538
+ * 1. Markdown format: "1. **event_name**: Description"
539
+ * 2. Legacy format: type: "event_name"
540
+ * 3. SSE examples: event: event_name
541
+ *
517
542
  * @param operation - The OpenAPI operation object
518
543
  * @returns Array of event types or undefined
519
544
  */
520
545
  declare function extractSSEEventTypes(operation: OperationObject): string[] | undefined;
546
+ /**
547
+ * Extracts detailed SSE event information including field definitions
548
+ *
549
+ * Parses markdown documentation format:
550
+ * ```
551
+ * 1. **event_name**: Event description
552
+ * - `field_name`: Field description
553
+ * - `another_field`: Another description
554
+ * ```
555
+ *
556
+ * @param operation - The OpenAPI operation object
557
+ * @returns Array of event type information with fields
558
+ */
559
+ declare function extractSSEEventInfo(operation: OperationObject): SSEEventTypeInfo[] | undefined;
521
560
 
522
561
  interface OpenAPIResponse {
523
562
  types: string;
@@ -629,5 +668,5 @@ declare class Bagel {
629
668
  uploadFile<T>(file: File, options?: UploadOptions): Promise<T>;
630
669
  }
631
670
 
632
- export { Bagel, StreamController, createSSEStream, createSSEStreamPost, dereference, extractSSEEventTypes, formatAPIErrorMessage, getPath, isReferenceObject, isSSEStream, isSchemaObject, _default as openAPI };
633
- export type { BaseParameterObject, CallbackObject, CallbacksObject, ComponentsObject, ContactObject, ContentObject, DiscriminatorObject, EncodingObject, EncodingPropertyObject, ExampleObject, ExamplesObject, ExternalDocumentationObject, HeaderObject, HeadersObject, InfoObject, LicenseObject, LinkObject, LinkParametersObject, LinksObject, MediaTypeObject, OAuthFlowObject, OAuthFlowsObject, OpenAPIObject, OperationObject, ParameterLocation, ParameterObject, ParameterStyle, PathItemObject, PathsObject, ReferenceObject, RequestBodyObject, ResponseObject, ResponsesObject, SSEEvent, SSEStreamOptions, SchemaObject, SchemaObjectType, SchemasObject, ScopesObject, SecurityRequirementObject, SecuritySchemeObject, SecuritySchemeType, ServerObject, ServerVariableObject, StreamEventMap, TableToTypeMapping, Tables, TagObject, TypeFormatT, UploadOptions, User, XmlObject };
671
+ export { Bagel, StreamController, createSSEStream, createSSEStreamPost, dereference, extractSSEEventInfo, extractSSEEventTypes, formatAPIErrorMessage, getPath, isReferenceObject, isSSEStream, isSchemaObject, _default as openAPI };
672
+ export type { BaseParameterObject, CallbackObject, CallbacksObject, ComponentsObject, ContactObject, ContentObject, DiscriminatorObject, EncodingObject, EncodingPropertyObject, ExampleObject, ExamplesObject, ExternalDocumentationObject, HeaderObject, HeadersObject, InfoObject, LicenseObject, LinkObject, LinkParametersObject, LinksObject, MediaTypeObject, OAuthFlowObject, OAuthFlowsObject, OpenAPIObject, OperationObject, ParameterLocation, ParameterObject, ParameterStyle, PathItemObject, PathsObject, ReferenceObject, RequestBodyObject, ResponseObject, ResponsesObject, SSEEvent, SSEEventTypeInfo, SSEStreamOptions, SchemaObject, SchemaObjectType, SchemasObject, ScopesObject, SecurityRequirementObject, SecuritySchemeObject, SecuritySchemeType, ServerObject, ServerVariableObject, StreamEventMap, TableToTypeMapping, Tables, TagObject, TypeFormatT, UploadOptions, User, XmlObject };
package/dist/index.d.ts CHANGED
@@ -508,16 +508,55 @@ interface SecurityRequirementObject {
508
508
 
509
509
  /**
510
510
  * Detects if an operation is an SSE (Server-Sent Events) stream endpoint
511
+ *
512
+ * Primary detection: Checks for text/event-stream content type in responses
513
+ * Fallback: Only if no content type is specified, check for explicit SSE keywords
514
+ *
511
515
  * @param operation - The OpenAPI operation object
512
516
  * @returns Whether the operation is an SSE stream
513
517
  */
514
518
  declare function isSSEStream(operation: OperationObject): boolean;
515
519
  /**
516
- * Extracts SSE event types from operation description
520
+ * Information about an SSE event type including its fields
521
+ */
522
+ interface SSEEventTypeInfo {
523
+ /** Event type name */
524
+ name: string;
525
+ /** Event description */
526
+ description?: string;
527
+ /** Fields in the event data */
528
+ fields?: Array<{
529
+ name: string;
530
+ description?: string;
531
+ type?: string;
532
+ }>;
533
+ }
534
+ /**
535
+ * Extracts SSE event types from operation description and examples
536
+ *
537
+ * Supports multiple documentation formats:
538
+ * 1. Markdown format: "1. **event_name**: Description"
539
+ * 2. Legacy format: type: "event_name"
540
+ * 3. SSE examples: event: event_name
541
+ *
517
542
  * @param operation - The OpenAPI operation object
518
543
  * @returns Array of event types or undefined
519
544
  */
520
545
  declare function extractSSEEventTypes(operation: OperationObject): string[] | undefined;
546
+ /**
547
+ * Extracts detailed SSE event information including field definitions
548
+ *
549
+ * Parses markdown documentation format:
550
+ * ```
551
+ * 1. **event_name**: Event description
552
+ * - `field_name`: Field description
553
+ * - `another_field`: Another description
554
+ * ```
555
+ *
556
+ * @param operation - The OpenAPI operation object
557
+ * @returns Array of event type information with fields
558
+ */
559
+ declare function extractSSEEventInfo(operation: OperationObject): SSEEventTypeInfo[] | undefined;
521
560
 
522
561
  interface OpenAPIResponse {
523
562
  types: string;
@@ -629,5 +668,5 @@ declare class Bagel {
629
668
  uploadFile<T>(file: File, options?: UploadOptions): Promise<T>;
630
669
  }
631
670
 
632
- export { Bagel, StreamController, createSSEStream, createSSEStreamPost, dereference, extractSSEEventTypes, formatAPIErrorMessage, getPath, isReferenceObject, isSSEStream, isSchemaObject, _default as openAPI };
633
- export type { BaseParameterObject, CallbackObject, CallbacksObject, ComponentsObject, ContactObject, ContentObject, DiscriminatorObject, EncodingObject, EncodingPropertyObject, ExampleObject, ExamplesObject, ExternalDocumentationObject, HeaderObject, HeadersObject, InfoObject, LicenseObject, LinkObject, LinkParametersObject, LinksObject, MediaTypeObject, OAuthFlowObject, OAuthFlowsObject, OpenAPIObject, OperationObject, ParameterLocation, ParameterObject, ParameterStyle, PathItemObject, PathsObject, ReferenceObject, RequestBodyObject, ResponseObject, ResponsesObject, SSEEvent, SSEStreamOptions, SchemaObject, SchemaObjectType, SchemasObject, ScopesObject, SecurityRequirementObject, SecuritySchemeObject, SecuritySchemeType, ServerObject, ServerVariableObject, StreamEventMap, TableToTypeMapping, Tables, TagObject, TypeFormatT, UploadOptions, User, XmlObject };
671
+ export { Bagel, StreamController, createSSEStream, createSSEStreamPost, dereference, extractSSEEventInfo, extractSSEEventTypes, formatAPIErrorMessage, getPath, isReferenceObject, isSSEStream, isSchemaObject, _default as openAPI };
672
+ export type { BaseParameterObject, CallbackObject, CallbacksObject, ComponentsObject, ContactObject, ContentObject, DiscriminatorObject, EncodingObject, EncodingPropertyObject, ExampleObject, ExamplesObject, ExternalDocumentationObject, HeaderObject, HeadersObject, InfoObject, LicenseObject, LinkObject, LinkParametersObject, LinksObject, MediaTypeObject, OAuthFlowObject, OAuthFlowsObject, OpenAPIObject, OperationObject, ParameterLocation, ParameterObject, ParameterStyle, PathItemObject, PathsObject, ReferenceObject, RequestBodyObject, ResponseObject, ResponsesObject, SSEEvent, SSEEventTypeInfo, SSEStreamOptions, SchemaObject, SchemaObjectType, SchemasObject, ScopesObject, SecurityRequirementObject, SecuritySchemeObject, SecuritySchemeType, ServerObject, ServerVariableObject, StreamEventMap, TableToTypeMapping, Tables, TagObject, TypeFormatT, UploadOptions, User, XmlObject };
package/dist/index.mjs CHANGED
@@ -1,23 +1,109 @@
1
1
  import axios$1 from 'axios';
2
2
 
3
3
  function isSSEStream(operation) {
4
- const description = operation.description?.toLowerCase() || "";
5
- const summary = operation.summary?.toLowerCase() || "";
6
- const hasSSEKeywords = description.includes("sse") || description.includes("server-sent events") || description.includes("event stream") || summary.includes("stream");
7
4
  const responses = operation.responses || {};
8
- const hasEventStreamContentType = Object.values(responses).some((response) => {
9
- if ("content" in response) {
10
- return "text/event-stream" in (response.content || {});
5
+ for (const [statusCode, response] of Object.entries(responses)) {
6
+ if (!statusCode.startsWith("2")) continue;
7
+ if ("$ref" in response) continue;
8
+ const content = response.content || {};
9
+ const contentTypes = Object.keys(content);
10
+ if (contentTypes.length > 0) {
11
+ if ("text/event-stream" in content) {
12
+ return true;
13
+ }
11
14
  }
12
- return false;
15
+ }
16
+ const hasDefinedContentTypes = Object.entries(responses).some(([statusCode, response]) => {
17
+ if (!statusCode.startsWith("2")) return false;
18
+ if ("$ref" in response) return false;
19
+ const content = response.content || {};
20
+ return Object.keys(content).length > 0;
13
21
  });
14
- return hasSSEKeywords || hasEventStreamContentType;
22
+ if (hasDefinedContentTypes) {
23
+ return false;
24
+ }
25
+ const description = operation.description?.toLowerCase() || "";
26
+ const summary = operation.summary?.toLowerCase() || "";
27
+ const hasExplicitSSEMention = /\bsse\b/.test(description) || /server-sent events/i.test(description) || /text\/event-stream/i.test(description) || /\bsse\b/.test(summary);
28
+ return hasExplicitSSEMention;
15
29
  }
16
30
  function extractSSEEventTypes(operation) {
17
31
  const description = operation.description || "";
32
+ const types = /* @__PURE__ */ new Set();
33
+ const markdownMatches = description.matchAll(/^\d+\.\s+\*\*([a-z_]+)\*\*/gm);
34
+ for (const match of markdownMatches) {
35
+ types.add(match[1]);
36
+ }
18
37
  const typeMatches = description.matchAll(/type:\s*"([^"]+)"/g);
19
- const types = Array.from(typeMatches).map((match) => match[1]);
20
- return types.length > 0 ? types : void 0;
38
+ for (const match of typeMatches) {
39
+ types.add(match[1]);
40
+ }
41
+ const responses = operation.responses || {};
42
+ for (const response of Object.values(responses)) {
43
+ if ("content" in response) {
44
+ const content = response.content || {};
45
+ const eventStream = content["text/event-stream"];
46
+ if (eventStream?.example) {
47
+ const exampleMatches = eventStream.example.matchAll(/^event:\s*([a-z_]+)/gm);
48
+ for (const match of exampleMatches) {
49
+ types.add(match[1]);
50
+ }
51
+ }
52
+ }
53
+ }
54
+ return types.size > 0 ? Array.from(types).sort() : void 0;
55
+ }
56
+ function extractSSEEventInfo(operation) {
57
+ const description = operation.description || "";
58
+ const eventInfos = [];
59
+ const eventSections = description.split(/(?=^\d+\.\s+\*\*)/m);
60
+ for (const section of eventSections) {
61
+ const eventMatch = section.match(/^\d+\.\s+\*\*([a-z_]+)\*\*:\s*([^\n]+)/m);
62
+ if (!eventMatch) continue;
63
+ const [, eventName, eventDescription] = eventMatch;
64
+ const fields = [];
65
+ const fieldMatches = section.matchAll(/^\s+[-*]\s+`([^`]+)`:\s*([^\n]+)/gm);
66
+ for (const fieldMatch of fieldMatches) {
67
+ const [, fieldName, fieldDescription] = fieldMatch;
68
+ fields.push({
69
+ name: fieldName,
70
+ description: fieldDescription.trim()
71
+ });
72
+ }
73
+ eventInfos.push({
74
+ name: eventName,
75
+ description: eventDescription,
76
+ fields: fields.length > 0 ? fields : void 0
77
+ });
78
+ }
79
+ if (eventInfos.length === 0) {
80
+ const responses = operation.responses || {};
81
+ for (const response of Object.values(responses)) {
82
+ if ("content" in response) {
83
+ const content = response.content || {};
84
+ const eventStream = content["text/event-stream"];
85
+ if (eventStream?.example) {
86
+ const exampleMatches = eventStream.example.matchAll(/data:\s*(\{[^}]+\})/g);
87
+ const seenEvents = /* @__PURE__ */ new Set();
88
+ for (const match of exampleMatches) {
89
+ try {
90
+ const data = JSON.parse(match[1]);
91
+ if (data.type && !seenEvents.has(data.type)) {
92
+ seenEvents.add(data.type);
93
+ const fields = Object.keys(data).filter((key) => key !== "type" && key !== "sequence").map((key) => ({ name: key }));
94
+ eventInfos.push({
95
+ name: data.type,
96
+ fields: fields.length > 0 ? fields : void 0
97
+ });
98
+ }
99
+ } catch {
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ return eventInfos.length > 0 ? eventInfos : void 0;
21
107
  }
22
108
 
23
109
  function getPath(pathsObject, path) {
@@ -299,6 +385,7 @@ const primitiveTypes = [
299
385
  const functionsInventory = {};
300
386
  const pathOperations = [];
301
387
  const streamEventTypes = {};
388
+ const streamEventInfo = {};
302
389
  function collectTypeForImportStatement(typeName) {
303
390
  typeName = typeName.trim().replace("[]", "");
304
391
  if (typeName.includes("|")) {
@@ -482,6 +569,7 @@ function generateStreamFunction(method, path, formattedPath, allParams, requestB
482
569
  const eventTypesExample = eventTypes?.length ? eventTypes.map((t) => `
483
570
  * .on('${t}', (data) => console.log(data))`).join("") : "\n * .on('message', (data) => console.log(data))";
484
571
  const typeAnnotation = eventTypes?.length ? `StreamController<${streamTypeName}>` : "StreamController";
572
+ const pathForUrl = formattedPath.startsWith("`") ? formattedPath.slice(1, -1) : formattedPath.slice(1, -1);
485
573
  if (method === "post") {
486
574
  return `{
487
575
  /**
@@ -496,7 +584,7 @@ function generateStreamFunction(method, path, formattedPath, allParams, requestB
496
584
  * stream.close()
497
585
  */
498
586
  stream: (${allParams}, options?: SSEStreamOptions): ${typeAnnotation} => {
499
- const url = \`\${${baseUrlRef}}${formattedPath}\`
587
+ const url = \`\${${baseUrlRef}}${pathForUrl}\`
500
588
  return createSSEStreamPost<${streamTypeName}>(url, ${bodyVar}, options)
501
589
  },
502
590
  /**
@@ -520,7 +608,7 @@ function generateStreamFunction(method, path, formattedPath, allParams, requestB
520
608
  * stream.close()
521
609
  */
522
610
  stream: (${allParams}, options?: SSEStreamOptions): ${typeAnnotation} => {
523
- const url = \`\${${baseUrlRef}}${formattedPath}\`
611
+ const url = \`\${${baseUrlRef}}${pathForUrl}\`
524
612
  return createSSEStream<${streamTypeName}>(url, options)
525
613
  },
526
614
  /**
@@ -620,6 +708,11 @@ function generateFunctionForOperation(method, path, operation) {
620
708
  const functionComment = buildJSDocComment(operation, method, path);
621
709
  if (isStream) {
622
710
  const eventTypes = extractSSEEventTypes(operation);
711
+ const eventInfo = extractSSEEventInfo(operation);
712
+ const streamTypeName = generateStreamTypeName(path);
713
+ if (eventInfo?.length) {
714
+ streamEventInfo[streamTypeName] = eventInfo;
715
+ }
623
716
  return functionComment + generateStreamFunction(
624
717
  method,
625
718
  path,
@@ -732,19 +825,60 @@ function generateStreamEventTypeDefinitions() {
732
825
  typeDefs += "// Stream Event Type Definitions (Fully Typed!)\n";
733
826
  typeDefs += "// ============================================================================\n\n";
734
827
  for (const [typeName, events] of Object.entries(streamEventTypes)) {
828
+ const eventInfo = streamEventInfo[typeName];
735
829
  typeDefs += `/**
736
830
  * Event types for ${typeName.replace("StreamEvents", "")} stream
737
831
  `;
738
832
  typeDefs += ` * Events: ${events.map((e) => `"${e}"`).join(", ")}
739
- */
833
+ `;
834
+ if (eventInfo?.length) {
835
+ typeDefs += ` *
836
+ `;
837
+ for (const info of eventInfo) {
838
+ typeDefs += ` * - **${info.name}**: ${info.description || "Event data"}
839
+ `;
840
+ }
841
+ }
842
+ typeDefs += ` */
740
843
  `;
741
844
  typeDefs += `export interface ${typeName} {
742
845
  `;
743
- for (const event of events) {
744
- typeDefs += ` /** ${event} event data */
846
+ if (eventInfo?.length) {
847
+ for (const info of eventInfo) {
848
+ typeDefs += ` /**
849
+ * ${info.description || info.name}
850
+ `;
851
+ if (info.fields?.length) {
852
+ info.fields.forEach((field) => {
853
+ typeDefs += ` * - \`${field.name}\`: ${field.description || "Field data"}
854
+ `;
855
+ });
856
+ }
857
+ typeDefs += ` */
858
+ `;
859
+ if (info.fields?.length) {
860
+ typeDefs += ` ${info.name}: {
861
+ `;
862
+ for (const field of info.fields) {
863
+ typeDefs += ` /** ${field.description || field.name} */
864
+ `;
865
+ typeDefs += ` ${field.name}: any
866
+ `;
867
+ }
868
+ typeDefs += ` }
745
869
  `;
746
- typeDefs += ` ${event}: any // TODO: Define specific type from OpenAPI schema
870
+ } else {
871
+ typeDefs += ` ${info.name}: any
747
872
  `;
873
+ }
874
+ }
875
+ } else {
876
+ for (const event of events) {
877
+ typeDefs += ` /** ${event} event data */
878
+ `;
879
+ typeDefs += ` ${event}: any
880
+ `;
881
+ }
748
882
  }
749
883
  typeDefs += "}\n\n";
750
884
  }
@@ -1573,4 +1707,4 @@ class Bagel {
1573
1707
  }
1574
1708
  }
1575
1709
 
1576
- export { Bagel, StreamController, createSSEStream, createSSEStreamPost, dereference, extractSSEEventTypes, formatAPIErrorMessage, getPath, isReferenceObject, isSSEStream, isSchemaObject, index as openAPI };
1710
+ export { Bagel, StreamController, createSSEStream, createSSEStreamPost, dereference, extractSSEEventInfo, extractSSEEventTypes, formatAPIErrorMessage, getPath, isReferenceObject, isSSEStream, isSchemaObject, index as openAPI };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/sdk",
3
3
  "type": "module",
4
- "version": "1.7.104",
4
+ "version": "1.8.3",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
@@ -3,6 +3,7 @@
3
3
  /* eslint-disable jsdoc/check-param-names */
4
4
  /* eslint-disable ts/no-non-null-assertion */
5
5
  /* eslint-disable prefer-destructuring */
6
+ import type { SSEEventTypeInfo } from './streamDetector'
6
7
  import type {
7
8
  OperationObject as OpenAPIOperation,
8
9
  PathItemObject as OpenAPIPath,
@@ -13,8 +14,7 @@ import type {
13
14
  ReferenceObject,
14
15
  SchemaObject,
15
16
  } from './types'
16
- import { isSSEStream, extractSSEEventTypes } from './streamDetector'
17
-
17
+ import { isSSEStream, extractSSEEventTypes, extractSSEEventInfo } from './streamDetector'
18
18
  import { dereference, isReferenceObject } from './types/utils'
19
19
  import {
20
20
  cleanPath,
@@ -63,6 +63,7 @@ export interface PathOperation {
63
63
  const functionsInventory: Record<string, string> = {}
64
64
  const pathOperations: PathOperation[] = []
65
65
  const streamEventTypes: Record<string, string[]> = {} // Track stream endpoints and their event types
66
+ const streamEventInfo: Record<string, SSEEventTypeInfo[]> = {} // Track detailed event info for type generation
66
67
 
67
68
  /**
68
69
  * Collects non-primitive types for import statements
@@ -404,6 +405,12 @@ function generateStreamFunction(
404
405
  ? `StreamController<${streamTypeName}>`
405
406
  : 'StreamController'
406
407
 
408
+ // Convert formattedPath from string representation to actual path for URL construction
409
+ // formattedPath can be either 'path' or `path/${param}` (as strings)
410
+ const pathForUrl = formattedPath.startsWith('`')
411
+ ? formattedPath.slice(1, -1) // Remove backticks to get the content
412
+ : formattedPath.slice(1, -1) // Remove quotes to get the content
413
+
407
414
  if (method === 'post') {
408
415
  return `{
409
416
  /**
@@ -418,7 +425,7 @@ function generateStreamFunction(
418
425
  * stream.close()
419
426
  */
420
427
  stream: (${allParams}, options?: SSEStreamOptions): ${typeAnnotation} => {
421
- const url = \`\${${baseUrlRef}}${formattedPath}\`
428
+ const url = \`\${${baseUrlRef}}${pathForUrl}\`
422
429
  return createSSEStreamPost<${streamTypeName}>(url, ${bodyVar}, options)
423
430
  },
424
431
  /**
@@ -442,7 +449,7 @@ function generateStreamFunction(
442
449
  * stream.close()
443
450
  */
444
451
  stream: (${allParams}, options?: SSEStreamOptions): ${typeAnnotation} => {
445
- const url = \`\${${baseUrlRef}}${formattedPath}\`
452
+ const url = \`\${${baseUrlRef}}${pathForUrl}\`
446
453
  return createSSEStream<${streamTypeName}>(url, options)
447
454
  },
448
455
  /**
@@ -615,6 +622,14 @@ function generateFunctionForOperation(
615
622
  // Generate stream function for SSE endpoints
616
623
  if (isStream) {
617
624
  const eventTypes = extractSSEEventTypes(operation)
625
+ const eventInfo = extractSSEEventInfo(operation)
626
+
627
+ // Store detailed event info for type generation
628
+ const streamTypeName = generateStreamTypeName(path)
629
+ if (eventInfo?.length) {
630
+ streamEventInfo[streamTypeName] = eventInfo
631
+ }
632
+
618
633
  return functionComment + generateStreamFunction(
619
634
  method,
620
635
  path,
@@ -801,6 +816,7 @@ export const ${parent} = ${JSON.stringify(object, undefined, 2)};\n`
801
816
 
802
817
  /**
803
818
  * Generates TypeScript type definitions for stream events
819
+ * Uses detailed event info if available, falls back to simple event list
804
820
  * @returns TypeScript type definitions string
805
821
  */
806
822
  function generateStreamEventTypeDefinitions(): string {
@@ -813,13 +829,53 @@ function generateStreamEventTypeDefinitions(): string {
813
829
  typeDefs += '// ============================================================================\n\n'
814
830
 
815
831
  for (const [typeName, events] of Object.entries(streamEventTypes)) {
832
+ const eventInfo = streamEventInfo[typeName]
833
+
816
834
  typeDefs += `/**\n * Event types for ${typeName.replace('StreamEvents', '')} stream\n`
817
- typeDefs += ` * Events: ${events.map(e => `"${e}"`).join(', ')}\n */\n`
835
+ typeDefs += ` * Events: ${events.map(e => `"${e}"`).join(', ')}\n`
836
+
837
+ // Add event descriptions if available
838
+ if (eventInfo?.length) {
839
+ typeDefs += ` *\n`
840
+ for (const info of eventInfo) {
841
+ typeDefs += ` * - **${info.name}**: ${info.description || 'Event data'}\n`
842
+ }
843
+ }
844
+
845
+ typeDefs += ` */\n`
818
846
  typeDefs += `export interface ${typeName} {\n`
819
847
 
820
- for (const event of events) {
821
- typeDefs += ` /** ${event} event data */\n`
822
- typeDefs += ` ${event}: any // TODO: Define specific type from OpenAPI schema\n`
848
+ // Generate event type definitions with field information if available
849
+ if (eventInfo?.length) {
850
+ for (const info of eventInfo) {
851
+ typeDefs += ` /**\n * ${info.description || info.name}\n`
852
+
853
+ if (info.fields?.length) {
854
+ info.fields.forEach((field) => {
855
+ typeDefs += ` * - \`${field.name}\`: ${field.description || 'Field data'}\n`
856
+ })
857
+ }
858
+
859
+ typeDefs += ` */\n`
860
+
861
+ // Generate interface for this event
862
+ if (info.fields?.length) {
863
+ typeDefs += ` ${info.name}: {\n`
864
+ for (const field of info.fields) {
865
+ typeDefs += ` /** ${field.description || field.name} */\n`
866
+ typeDefs += ` ${field.name}: any\n`
867
+ }
868
+ typeDefs += ` }\n`
869
+ } else {
870
+ typeDefs += ` ${info.name}: any\n`
871
+ }
872
+ }
873
+ } else {
874
+ // Fallback: simple event list without field info
875
+ for (const event of events) {
876
+ typeDefs += ` /** ${event} event data */\n`
877
+ typeDefs += ` ${event}: any\n`
878
+ }
823
879
  }
824
880
 
825
881
  typeDefs += '}\n\n'
@@ -2,43 +2,206 @@ import type { OperationObject as OpenAPIOperation } from './types'
2
2
 
3
3
  /**
4
4
  * Detects if an operation is an SSE (Server-Sent Events) stream endpoint
5
+ *
6
+ * Primary detection: Checks for text/event-stream content type in responses
7
+ * Fallback: Only if no content type is specified, check for explicit SSE keywords
8
+ *
5
9
  * @param operation - The OpenAPI operation object
6
10
  * @returns Whether the operation is an SSE stream
7
11
  */
8
12
  export function isSSEStream(operation: OpenAPIOperation): boolean {
9
- const description = operation.description?.toLowerCase() || ''
10
- const summary = operation.summary?.toLowerCase() || ''
13
+ const responses = operation.responses || {}
11
14
 
12
- // Check for SSE indicators in description or summary
13
- const hasSSEKeywords
14
- = description.includes('sse')
15
- || description.includes('server-sent events')
16
- || description.includes('event stream')
17
- || summary.includes('stream')
15
+ // Check all response codes (200, 201, etc.)
16
+ for (const [statusCode, response] of Object.entries(responses)) {
17
+ // Only check successful responses (2xx)
18
+ if (!statusCode.startsWith('2')) continue
18
19
 
19
- // Check for text/event-stream content type in responses
20
- const responses = operation.responses || {}
21
- const hasEventStreamContentType = Object.values(responses).some((response: any) => {
22
- if ('content' in response) {
23
- return 'text/event-stream' in (response.content || {})
20
+ // Skip reference objects for now
21
+ if ('$ref' in response) continue
22
+
23
+ const content = (response as any).content || {}
24
+ const contentTypes = Object.keys(content)
25
+
26
+ // If response has content types defined
27
+ if (contentTypes.length > 0) {
28
+ // It's a stream if text/event-stream is present
29
+ // (even if application/json is also present as a fallback)
30
+ if ('text/event-stream' in content) {
31
+ return true
32
+ }
24
33
  }
25
- return false
34
+ }
35
+
36
+ // If we checked responses and found only application/json (no text/event-stream), it's not a stream
37
+ const hasDefinedContentTypes = Object.entries(responses).some(([statusCode, response]) => {
38
+ if (!statusCode.startsWith('2')) return false
39
+ if ('$ref' in response) return false
40
+ const content = (response as any).content || {}
41
+ return Object.keys(content).length > 0
26
42
  })
27
43
 
28
- return hasSSEKeywords || hasEventStreamContentType
44
+ if (hasDefinedContentTypes) {
45
+ return false
46
+ }
47
+
48
+ // Fallback: If no content types are defined at all, check description
49
+ // (Some OpenAPI specs might not properly define response content types)
50
+ const description = operation.description?.toLowerCase() || ''
51
+ const summary = operation.summary?.toLowerCase() || ''
52
+
53
+ const hasExplicitSSEMention
54
+ = /\bsse\b/.test(description)
55
+ || /server-sent events/i.test(description)
56
+ || /text\/event-stream/i.test(description)
57
+ || /\bsse\b/.test(summary)
58
+
59
+ return hasExplicitSSEMention
60
+ }
61
+
62
+ /**
63
+ * Information about an SSE event type including its fields
64
+ */
65
+ export interface SSEEventTypeInfo {
66
+ /** Event type name */
67
+ name: string
68
+ /** Event description */
69
+ description?: string
70
+ /** Fields in the event data */
71
+ fields?: Array<{
72
+ name: string
73
+ description?: string
74
+ type?: string
75
+ }>
29
76
  }
30
77
 
31
78
  /**
32
- * Extracts SSE event types from operation description
79
+ * Extracts SSE event types from operation description and examples
80
+ *
81
+ * Supports multiple documentation formats:
82
+ * 1. Markdown format: "1. **event_name**: Description"
83
+ * 2. Legacy format: type: "event_name"
84
+ * 3. SSE examples: event: event_name
85
+ *
33
86
  * @param operation - The OpenAPI operation object
34
87
  * @returns Array of event types or undefined
35
88
  */
36
89
  export function extractSSEEventTypes(operation: OpenAPIOperation): string[] | undefined {
37
90
  const description = operation.description || ''
91
+ const types = new Set<string>()
38
92
 
39
- // Look for patterns like: type: "token", type: "done", etc.
93
+ // Method 1: Extract from markdown numbered lists with bold event names
94
+ // Pattern: "1. **event_name**: Description"
95
+ const markdownMatches = description.matchAll(/^\d+\.\s+\*\*([a-z_]+)\*\*/gm)
96
+ for (const match of markdownMatches) {
97
+ types.add(match[1])
98
+ }
99
+
100
+ // Method 2: Legacy format - type: "event_name"
40
101
  const typeMatches = description.matchAll(/type:\s*"([^"]+)"/g)
41
- const types = Array.from(typeMatches).map(match => match[1])
102
+ for (const match of typeMatches) {
103
+ types.add(match[1])
104
+ }
105
+
106
+ // Method 3: Extract from SSE examples in response content
107
+ const responses = operation.responses || {}
108
+ for (const response of Object.values(responses)) {
109
+ if ('content' in response) {
110
+ const content = (response as any).content || {}
111
+ const eventStream = content['text/event-stream']
112
+ if (eventStream?.example) {
113
+ // Parse SSE format: event: event_name
114
+ const exampleMatches = eventStream.example.matchAll(/^event:\s*([a-z_]+)/gm)
115
+ for (const match of exampleMatches) {
116
+ types.add(match[1])
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ return types.size > 0 ? Array.from(types).sort() : undefined
123
+ }
124
+
125
+ /**
126
+ * Extracts detailed SSE event information including field definitions
127
+ *
128
+ * Parses markdown documentation format:
129
+ * ```
130
+ * 1. **event_name**: Event description
131
+ * - `field_name`: Field description
132
+ * - `another_field`: Another description
133
+ * ```
134
+ *
135
+ * @param operation - The OpenAPI operation object
136
+ * @returns Array of event type information with fields
137
+ */
138
+ export function extractSSEEventInfo(operation: OpenAPIOperation): SSEEventTypeInfo[] | undefined {
139
+ const description = operation.description || ''
140
+ const eventInfos: SSEEventTypeInfo[] = []
141
+
142
+ // Split description into sections by numbered events
143
+ const eventSections = description.split(/(?=^\d+\.\s+\*\*)/m)
144
+
145
+ for (const section of eventSections) {
146
+ // Extract event name and description
147
+ const eventMatch = section.match(/^\d+\.\s+\*\*([a-z_]+)\*\*:\s*([^\n]+)/m)
148
+ if (!eventMatch) continue
149
+
150
+ const [, eventName, eventDescription] = eventMatch
151
+
152
+ // Extract fields from the section
153
+ const fields: Array<{ name: string, description?: string, type?: string }> = []
154
+ const fieldMatches = section.matchAll(/^\s+[-*]\s+`([^`]+)`:\s*([^\n]+)/gm)
155
+
156
+ for (const fieldMatch of fieldMatches) {
157
+ const [, fieldName, fieldDescription] = fieldMatch
158
+ fields.push({
159
+ name: fieldName,
160
+ description: fieldDescription.trim(),
161
+ })
162
+ }
163
+
164
+ eventInfos.push({
165
+ name: eventName,
166
+ description: eventDescription,
167
+ fields: fields.length > 0 ? fields : undefined,
168
+ })
169
+ }
170
+
171
+ // If no markdown events found, try to extract from examples
172
+ if (eventInfos.length === 0) {
173
+ const responses = operation.responses || {}
174
+ for (const response of Object.values(responses)) {
175
+ if ('content' in response) {
176
+ const content = (response as any).content || {}
177
+ const eventStream = content['text/event-stream']
178
+ if (eventStream?.example) {
179
+ // Parse example JSON to extract field names
180
+ const exampleMatches = eventStream.example.matchAll(/data:\s*(\{[^}]+\})/g)
181
+ const seenEvents = new Set<string>()
182
+
183
+ for (const match of exampleMatches) {
184
+ try {
185
+ const data = JSON.parse(match[1])
186
+ if (data.type && !seenEvents.has(data.type)) {
187
+ seenEvents.add(data.type)
188
+ const fields = Object.keys(data)
189
+ .filter(key => key !== 'type' && key !== 'sequence')
190
+ .map(key => ({ name: key }))
191
+
192
+ eventInfos.push({
193
+ name: data.type,
194
+ fields: fields.length > 0 ? fields : undefined,
195
+ })
196
+ }
197
+ } catch {
198
+ // Skip invalid JSON
199
+ }
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }
42
205
 
43
- return types.length > 0 ? types : undefined
206
+ return eventInfos.length > 0 ? eventInfos : undefined
44
207
  }