@nshipster/sosumi 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -1
- package/package.json +4 -2
- package/public/index.html +3 -1
- package/public/llms.txt +1 -1
- package/public/robots.txt +1 -0
- package/public/webmcp.js +71 -0
- package/src/index.ts +112 -1
- package/src/lib/a2a.ts +90 -0
- package/src/lib/external/fetch.ts +3 -0
- package/src/lib/hig/render.ts +94 -73
- package/src/lib/markdown.ts +115 -0
- package/src/lib/mcp.ts +150 -115
- package/src/lib/reference/render.ts +208 -147
- package/src/lib/search.ts +115 -55
- package/src/lib/types.ts +21 -0
- package/src/lib/url.ts +35 -0
- package/src/lib/webbotauth.ts +261 -0
- package/src/lib/webmcp.ts +38 -0
- package/wrangler.jsonc +1 -1
|
@@ -2,11 +2,24 @@
|
|
|
2
2
|
* Apple Developer Reference documentation rendering functionality
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import {
|
|
6
|
+
CONTENT_TOO_DEEP,
|
|
7
|
+
formatCallout,
|
|
8
|
+
formatCodeBlock,
|
|
9
|
+
formatList,
|
|
10
|
+
formatTable,
|
|
11
|
+
INLINE_CONTENT_TOO_DEEP,
|
|
12
|
+
MAX_CONTENT_DEPTH,
|
|
13
|
+
MAX_INLINE_DEPTH,
|
|
14
|
+
mapAsideStyleToCallout,
|
|
15
|
+
} from "../markdown"
|
|
16
|
+
import { extractTitleFromIdentifier } from "../url"
|
|
5
17
|
import type {
|
|
6
18
|
AppleDocJSON,
|
|
7
19
|
ContentItem,
|
|
8
20
|
Declaration,
|
|
9
21
|
IndexContentItem,
|
|
22
|
+
Platform,
|
|
10
23
|
PossibleValueItem,
|
|
11
24
|
PropertyItem,
|
|
12
25
|
TopicSection,
|
|
@@ -67,6 +80,8 @@ export async function renderFromJSON(
|
|
|
67
80
|
}
|
|
68
81
|
}
|
|
69
82
|
|
|
83
|
+
markdown += renderDeprecationNotice(jsonData, jsonData.references, options.externalOrigin)
|
|
84
|
+
|
|
70
85
|
// Add declaration
|
|
71
86
|
if (jsonData.primaryContentSections) {
|
|
72
87
|
const declarationSection = jsonData.primaryContentSections.find(
|
|
@@ -193,6 +208,10 @@ function generateFrontMatterFromJSON(jsonData: AppleDocJSON, sourceUrl: string):
|
|
|
193
208
|
frontMatter.source = sourceUrl
|
|
194
209
|
frontMatter.timestamp = new Date().toISOString()
|
|
195
210
|
|
|
211
|
+
if (isSymbolDeprecated(jsonData)) {
|
|
212
|
+
frontMatter.deprecated = "true"
|
|
213
|
+
}
|
|
214
|
+
|
|
196
215
|
// Convert to YAML format
|
|
197
216
|
const yamlLines = Object.entries(frontMatter).map(([key, value]) => `${key}: ${value}`)
|
|
198
217
|
return `---\n${yamlLines.join("\n")}\n---\n\n`
|
|
@@ -228,14 +247,6 @@ function generateBreadcrumbs(sourceUrl: string, externalOrigin?: string): string
|
|
|
228
247
|
return `${breadcrumbs}\n\n`
|
|
229
248
|
}
|
|
230
249
|
|
|
231
|
-
/**
|
|
232
|
-
* Map a DocC language code to a Markdown code-fence identifier. DocC uses
|
|
233
|
-
* `occ` for Objective-C internally, but Markdown highlighters expect `objc`.
|
|
234
|
-
*/
|
|
235
|
-
function normalizeFenceLanguage(syntax: string): string {
|
|
236
|
-
return syntax === "occ" ? "objc" : syntax
|
|
237
|
-
}
|
|
238
|
-
|
|
239
250
|
/**
|
|
240
251
|
* Tab titles DocC uses for language-variant code examples. Used to detect
|
|
241
252
|
* whether a `tabNavigator` is a language switch (so we can default to Swift)
|
|
@@ -392,9 +403,9 @@ function renderContentArray(
|
|
|
392
403
|
externalOrigin?: string,
|
|
393
404
|
): string {
|
|
394
405
|
// Prevent infinite recursion by limiting depth
|
|
395
|
-
if (depth >
|
|
406
|
+
if (depth > MAX_CONTENT_DEPTH) {
|
|
396
407
|
console.warn("Maximum recursion depth reached in renderContentArray")
|
|
397
|
-
return
|
|
408
|
+
return CONTENT_TOO_DEEP
|
|
398
409
|
}
|
|
399
410
|
|
|
400
411
|
let markdown = ""
|
|
@@ -406,53 +417,29 @@ function renderContentArray(
|
|
|
406
417
|
markdown += `${hashes} ${item.text}\n\n`
|
|
407
418
|
} else if (item.type === "paragraph") {
|
|
408
419
|
if (item.inlineContent) {
|
|
409
|
-
|
|
420
|
+
// Inline nesting is counted separately from block nesting, so a
|
|
421
|
+
// paragraph's inline content starts a fresh depth budget.
|
|
422
|
+
const text = renderInlineContent(item.inlineContent, references, 0, externalOrigin)
|
|
410
423
|
markdown += `${text}\n\n`
|
|
411
424
|
}
|
|
412
425
|
} else if (item.type === "codeListing") {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
code = item.code.join("\n")
|
|
416
|
-
} else {
|
|
417
|
-
code = String(item.code || "")
|
|
418
|
-
}
|
|
419
|
-
const syntax = normalizeFenceLanguage(item.syntax || "swift")
|
|
420
|
-
|
|
421
|
-
markdown += `\`\`\`${syntax}\n${code}\n\`\`\`\n\n`
|
|
422
|
-
} else if (item.type === "unorderedList") {
|
|
426
|
+
markdown += formatCodeBlock(item.code, item.syntax)
|
|
427
|
+
} else if (item.type === "unorderedList" || item.type === "orderedList") {
|
|
423
428
|
if (item.items) {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
listItem.content || [],
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
)
|
|
431
|
-
markdown += `- ${itemText.replace(/\n\n$/, "")}\n`
|
|
432
|
-
}
|
|
433
|
-
markdown += "\n"
|
|
434
|
-
}
|
|
435
|
-
} else if (item.type === "orderedList") {
|
|
436
|
-
if (item.items) {
|
|
437
|
-
item.items.forEach((listItem: ContentItem, index: number) => {
|
|
438
|
-
const itemText = renderContentArray(
|
|
439
|
-
listItem.content || [],
|
|
440
|
-
references,
|
|
441
|
-
depth + 1,
|
|
442
|
-
externalOrigin,
|
|
443
|
-
)
|
|
444
|
-
markdown += `${index + 1}. ${itemText.replace(/\n\n$/, "")}\n`
|
|
445
|
-
})
|
|
446
|
-
markdown += "\n"
|
|
429
|
+
markdown += formatList(
|
|
430
|
+
item.items.map((listItem) =>
|
|
431
|
+
renderContentArray(listItem.content || [], references, depth + 1, externalOrigin),
|
|
432
|
+
),
|
|
433
|
+
item.type === "orderedList",
|
|
434
|
+
)
|
|
447
435
|
}
|
|
448
436
|
} else if (item.type === "aside") {
|
|
449
437
|
const style = item.style || "note"
|
|
450
|
-
const calloutType = mapAsideStyleToCallout(style)
|
|
451
438
|
const asideContent = item.content
|
|
452
439
|
? renderContentArray(item.content, references, depth + 1, externalOrigin)
|
|
453
440
|
: ""
|
|
454
|
-
const
|
|
455
|
-
markdown +=
|
|
441
|
+
const lead = style.toLowerCase() === "deprecated" ? DEPRECATED_LEAD : undefined
|
|
442
|
+
markdown += formatCallout(mapAsideStyleToCallout(style), asideContent, lead)
|
|
456
443
|
} else if (item.type === "table") {
|
|
457
444
|
markdown += renderTable(item, references, depth, externalOrigin)
|
|
458
445
|
} else if (item.type === "tabNavigator" && item.tabs?.length) {
|
|
@@ -499,27 +486,17 @@ function renderTable(
|
|
|
499
486
|
header?: string
|
|
500
487
|
rows?: ContentItem[][][] // rows[rowIndex][cellIndex] = ContentItem[]
|
|
501
488
|
}
|
|
502
|
-
const rows = table.rows ?? []
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
let markdown = ""
|
|
514
|
-
rows.forEach((row, rowIndex) => {
|
|
515
|
-
const cells = row.map((c) => renderCell(c))
|
|
516
|
-
if (cells.length === 0) return
|
|
517
|
-
markdown += `| ${cells.join(" | ")} |\n`
|
|
518
|
-
if (firstRowIsHeader && rowIndex === 0) {
|
|
519
|
-
markdown += `| ${cells.map(() => "---").join(" | ")} |\n`
|
|
520
|
-
}
|
|
521
|
-
})
|
|
522
|
-
return markdown ? `${markdown}\n` : ""
|
|
489
|
+
const rows = (table.rows ?? []).map((row) =>
|
|
490
|
+
row.map((cell) =>
|
|
491
|
+
renderContentArray(
|
|
492
|
+
Array.isArray(cell) ? cell : [cell],
|
|
493
|
+
references,
|
|
494
|
+
depth + 1,
|
|
495
|
+
externalOrigin,
|
|
496
|
+
),
|
|
497
|
+
),
|
|
498
|
+
)
|
|
499
|
+
return formatTable(rows, table.header === "row")
|
|
523
500
|
}
|
|
524
501
|
|
|
525
502
|
/**
|
|
@@ -532,9 +509,9 @@ function renderInlineContent(
|
|
|
532
509
|
externalOrigin?: string,
|
|
533
510
|
): string {
|
|
534
511
|
// Prevent infinite recursion by limiting depth
|
|
535
|
-
if (depth >
|
|
512
|
+
if (depth > MAX_INLINE_DEPTH) {
|
|
536
513
|
console.warn("Maximum recursion depth reached in renderInlineContent")
|
|
537
|
-
return
|
|
514
|
+
return INLINE_CONTENT_TOO_DEEP
|
|
538
515
|
}
|
|
539
516
|
|
|
540
517
|
return inlineContent
|
|
@@ -544,9 +521,11 @@ function renderInlineContent(
|
|
|
544
521
|
} else if (item.type === "codeVoice") {
|
|
545
522
|
return `\`${item.code}\``
|
|
546
523
|
} else if (item.type === "reference") {
|
|
524
|
+
const reference = item.identifier ? references?.[item.identifier] : undefined
|
|
547
525
|
const title =
|
|
548
526
|
item.title ||
|
|
549
527
|
item.text ||
|
|
528
|
+
resolveReferenceTitle(reference, references, depth, externalOrigin) ||
|
|
550
529
|
(item.identifier ? extractTitleFromIdentifier(item.identifier) : "")
|
|
551
530
|
const url = item.identifier
|
|
552
531
|
? convertIdentifierToURL(item.identifier, references, externalOrigin)
|
|
@@ -569,6 +548,56 @@ function renderInlineContent(
|
|
|
569
548
|
.join("")
|
|
570
549
|
}
|
|
571
550
|
|
|
551
|
+
/**
|
|
552
|
+
* Resolve the display title for a reference, preferring its rich
|
|
553
|
+
* `titleInlineContent` (which preserves code spans) over the flat `title`.
|
|
554
|
+
* When there is no rich title, wrap symbol titles in backticks so older DocC
|
|
555
|
+
* pages that omit `titleInlineContent` still render code spans in link text.
|
|
556
|
+
*/
|
|
557
|
+
function resolveReferenceTitle(
|
|
558
|
+
reference: ContentItem | undefined,
|
|
559
|
+
references?: Record<string, ContentItem>,
|
|
560
|
+
depth: number = 0,
|
|
561
|
+
externalOrigin?: string,
|
|
562
|
+
): string | undefined {
|
|
563
|
+
if (reference?.titleInlineContent) {
|
|
564
|
+
return renderInlineContent(reference.titleInlineContent, references, depth + 1, externalOrigin)
|
|
565
|
+
}
|
|
566
|
+
if (reference?.title && reference.role === "symbol") {
|
|
567
|
+
return `\`${reference.title}\``
|
|
568
|
+
}
|
|
569
|
+
return reference?.title
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
interface ResolvedLink {
|
|
573
|
+
title: string
|
|
574
|
+
url: string
|
|
575
|
+
abstract: string
|
|
576
|
+
deprecated: boolean
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Resolve an identifier to a Markdown link, drawing the title and abstract from
|
|
581
|
+
* the page's variants first, then the shared references map, and finally the
|
|
582
|
+
* identifier itself.
|
|
583
|
+
*/
|
|
584
|
+
function resolveLink(
|
|
585
|
+
id: string,
|
|
586
|
+
variants?: Variant[],
|
|
587
|
+
references?: Record<string, ContentItem>,
|
|
588
|
+
externalOrigin?: string,
|
|
589
|
+
): ResolvedLink {
|
|
590
|
+
const info = variants?.find((v) => v.identifier === id)
|
|
591
|
+
const reference = references?.[id]
|
|
592
|
+
const title =
|
|
593
|
+
info?.title ||
|
|
594
|
+
resolveReferenceTitle(reference, references, 0, externalOrigin) ||
|
|
595
|
+
extractTitleFromIdentifier(id)
|
|
596
|
+
const url = convertIdentifierToURL(id, references, externalOrigin)
|
|
597
|
+
const abstract = (info?.abstract ?? reference?.abstract)?.map((a) => a.text).join("") ?? ""
|
|
598
|
+
return { title, url, abstract, deprecated: reference?.deprecated === true }
|
|
599
|
+
}
|
|
600
|
+
|
|
572
601
|
/**
|
|
573
602
|
* Render relationship sections
|
|
574
603
|
*/
|
|
@@ -584,10 +613,7 @@ function renderRelationships(
|
|
|
584
613
|
if (rel.title && rel.identifiers) {
|
|
585
614
|
markdown += `## ${rel.title}\n\n`
|
|
586
615
|
for (const id of rel.identifiers) {
|
|
587
|
-
const
|
|
588
|
-
const reference = references?.[id]
|
|
589
|
-
const title = info?.title || reference?.title || extractTitleFromIdentifier(id)
|
|
590
|
-
const url = convertIdentifierToURL(id, references, externalOrigin)
|
|
616
|
+
const { title, url } = resolveLink(id, variants, references, externalOrigin)
|
|
591
617
|
markdown += `- [${title}](${url})\n`
|
|
592
618
|
}
|
|
593
619
|
markdown += "\n"
|
|
@@ -611,33 +637,24 @@ function renderTopicSections(
|
|
|
611
637
|
for (const topic of topics) {
|
|
612
638
|
if (topic.title) {
|
|
613
639
|
markdown += `## ${topic.title}\n\n`
|
|
640
|
+
}
|
|
614
641
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
markdown += `- [${title}](${url})`
|
|
629
|
-
if (abstract) {
|
|
630
|
-
markdown += ` ${abstract}`
|
|
631
|
-
}
|
|
632
|
-
markdown += "\n"
|
|
633
|
-
} else {
|
|
634
|
-
const title = extractTitleFromIdentifier(id)
|
|
635
|
-
const url = convertIdentifierToURL(id, references, externalOrigin)
|
|
636
|
-
markdown += `- [${title}](${url})\n`
|
|
637
|
-
}
|
|
642
|
+
if (topic.identifiers) {
|
|
643
|
+
for (const id of topic.identifiers) {
|
|
644
|
+
const { title, url, abstract, deprecated } = resolveLink(
|
|
645
|
+
id,
|
|
646
|
+
variants,
|
|
647
|
+
references,
|
|
648
|
+
externalOrigin,
|
|
649
|
+
)
|
|
650
|
+
const deprecatedLabel = deprecated ? " *(Deprecated)*" : ""
|
|
651
|
+
markdown += `- [${title}](${url})${deprecatedLabel}`
|
|
652
|
+
if (abstract) {
|
|
653
|
+
markdown += ` ${abstract}`
|
|
638
654
|
}
|
|
639
655
|
markdown += "\n"
|
|
640
656
|
}
|
|
657
|
+
markdown += "\n"
|
|
641
658
|
}
|
|
642
659
|
}
|
|
643
660
|
|
|
@@ -712,10 +729,7 @@ function renderSeeAlso(
|
|
|
712
729
|
if (section.title && section.identifiers) {
|
|
713
730
|
markdown += `## ${section.title}\n\n`
|
|
714
731
|
for (const id of section.identifiers) {
|
|
715
|
-
const
|
|
716
|
-
const reference = references?.[id]
|
|
717
|
-
const title = info?.title || reference?.title || extractTitleFromIdentifier(id)
|
|
718
|
-
const url = convertIdentifierToURL(id, references, externalOrigin)
|
|
732
|
+
const { title, url } = resolveLink(id, variants, references, externalOrigin)
|
|
719
733
|
markdown += `- [${title}](${url})\n`
|
|
720
734
|
}
|
|
721
735
|
markdown += "\n"
|
|
@@ -726,23 +740,101 @@ function renderSeeAlso(
|
|
|
726
740
|
}
|
|
727
741
|
|
|
728
742
|
/**
|
|
729
|
-
*
|
|
743
|
+
* Whether the symbol page represents a deprecated API.
|
|
744
|
+
*/
|
|
745
|
+
function isSymbolDeprecated(jsonData: AppleDocJSON): boolean {
|
|
746
|
+
return getDeprecationNoticeBody(jsonData, jsonData.references) !== null
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Collect a deprecation message from platform availability metadata.
|
|
751
|
+
*/
|
|
752
|
+
function getDeprecationMessageFromPlatforms(platforms?: Platform[]): string | null {
|
|
753
|
+
if (!platforms?.length) {
|
|
754
|
+
return null
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
const deprecatedPlatforms = platforms.filter(
|
|
758
|
+
(platform) => platform.deprecated === true || platform.deprecatedAt,
|
|
759
|
+
)
|
|
760
|
+
if (deprecatedPlatforms.length === 0) {
|
|
761
|
+
return null
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
const uniqueMessages = [
|
|
765
|
+
...new Set(
|
|
766
|
+
deprecatedPlatforms
|
|
767
|
+
.map((platform) => platform.message?.trim())
|
|
768
|
+
.filter((message): message is string => Boolean(message)),
|
|
769
|
+
),
|
|
770
|
+
]
|
|
771
|
+
|
|
772
|
+
if (uniqueMessages.length === 1) {
|
|
773
|
+
return uniqueMessages[0]
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (uniqueMessages.length > 1) {
|
|
777
|
+
return deprecatedPlatforms
|
|
778
|
+
.filter((platform) => platform.message)
|
|
779
|
+
.map((platform) => `**${platform.name}:** ${platform.message}`)
|
|
780
|
+
.join("\n")
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
return DEFAULT_DEPRECATION_MESSAGE
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
const DEFAULT_DEPRECATION_MESSAGE = "This symbol is deprecated."
|
|
787
|
+
|
|
788
|
+
/** Leading paragraph for callouts that flag deprecated APIs. */
|
|
789
|
+
const DEPRECATED_LEAD = "**Deprecated**"
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Resolve deprecation notice text from DocC JSON (without rendering the callout).
|
|
793
|
+
*/
|
|
794
|
+
function getDeprecationNoticeBody(
|
|
795
|
+
jsonData: AppleDocJSON,
|
|
796
|
+
references?: Record<string, ContentItem>,
|
|
797
|
+
externalOrigin?: string,
|
|
798
|
+
): string | null {
|
|
799
|
+
if (jsonData.deprecationSummary?.length) {
|
|
800
|
+
const summary = renderContentArray(
|
|
801
|
+
jsonData.deprecationSummary,
|
|
802
|
+
references,
|
|
803
|
+
0,
|
|
804
|
+
externalOrigin,
|
|
805
|
+
).trim()
|
|
806
|
+
if (summary) {
|
|
807
|
+
return summary
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
const platformMessage = getDeprecationMessageFromPlatforms(jsonData.metadata?.platforms)
|
|
812
|
+
if (platformMessage) {
|
|
813
|
+
return platformMessage
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
const identifier = jsonData.identifier?.url
|
|
817
|
+
if (identifier && references?.[identifier]?.deprecated === true) {
|
|
818
|
+
return DEFAULT_DEPRECATION_MESSAGE
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
return null
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Render a deprecation callout for deprecated symbol pages.
|
|
730
826
|
*/
|
|
731
|
-
function
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
case "tip":
|
|
740
|
-
return "TIP"
|
|
741
|
-
case "deprecated":
|
|
742
|
-
return "WARNING"
|
|
743
|
-
default:
|
|
744
|
-
return "NOTE"
|
|
827
|
+
function renderDeprecationNotice(
|
|
828
|
+
jsonData: AppleDocJSON,
|
|
829
|
+
references?: Record<string, ContentItem>,
|
|
830
|
+
externalOrigin?: string,
|
|
831
|
+
): string {
|
|
832
|
+
const body = getDeprecationNoticeBody(jsonData, references, externalOrigin)
|
|
833
|
+
if (!body) {
|
|
834
|
+
return ""
|
|
745
835
|
}
|
|
836
|
+
|
|
837
|
+
return formatCallout("WARNING", body, DEPRECATED_LEAD)
|
|
746
838
|
}
|
|
747
839
|
|
|
748
840
|
/**
|
|
@@ -788,34 +880,3 @@ function rewriteDocumentationPath(path: string | undefined, externalOrigin?: str
|
|
|
788
880
|
|
|
789
881
|
return `/external/${externalOrigin}${path}`
|
|
790
882
|
}
|
|
791
|
-
|
|
792
|
-
/**
|
|
793
|
-
* Extract title from identifier
|
|
794
|
-
*/
|
|
795
|
-
export function extractTitleFromIdentifier(identifier: string): string {
|
|
796
|
-
const parts = identifier.split("/")
|
|
797
|
-
const lastPart = parts[parts.length - 1]
|
|
798
|
-
|
|
799
|
-
// Handle disambiguation suffixes (e.g., "body-8kl5o" -> "body", "init(exactly:)-63925" -> "init(exactly:)")
|
|
800
|
-
const disambiguationMatch = lastPart.match(/^(.+?)(?:-\w+)?$/)
|
|
801
|
-
if (disambiguationMatch) {
|
|
802
|
-
const baseName = disambiguationMatch[1]
|
|
803
|
-
|
|
804
|
-
// If it looks like a method signature (contains parentheses), preserve it
|
|
805
|
-
if (baseName.includes("(") && baseName.includes(")")) {
|
|
806
|
-
return baseName
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
// For simple identifiers, convert camelCase to readable format
|
|
810
|
-
return baseName
|
|
811
|
-
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
812
|
-
.replace(/\s+/g, " ")
|
|
813
|
-
.trim()
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
// Fallback: convert camelCase to readable format
|
|
817
|
-
return lastPart
|
|
818
|
-
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
819
|
-
.replace(/\s+/g, " ")
|
|
820
|
-
.trim()
|
|
821
|
-
}
|