rspress-plugin-api-extractor 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.md +2 -2
  2. package/api-extracted-package.js +426 -0
  3. package/build-program.js +120 -0
  4. package/build-stages.js +664 -0
  5. package/category-resolver.js +50 -0
  6. package/code-post-processor.js +38 -0
  7. package/config-helpers.js +106 -0
  8. package/config-utils.js +258 -0
  9. package/content-hash.js +79 -0
  10. package/errors.js +29 -0
  11. package/formatter.js +69 -0
  12. package/hide-cut-transformer.js +96 -0
  13. package/index.d.ts +901 -831
  14. package/index.js +4 -6128
  15. package/layers/ConfigServiceLive.js +377 -0
  16. package/layers/ObservabilityLive.js +136 -0
  17. package/layers/PathDerivationServiceLive.js +16 -0
  18. package/layers/SnapshotServiceLive.js +94 -0
  19. package/layers/TypeRegistryServiceLive.js +46 -0
  20. package/llms-processing.js +270 -0
  21. package/llms-program.js +262 -0
  22. package/loader.js +186 -0
  23. package/markdown/cross-linker.js +156 -0
  24. package/markdown/helpers.js +364 -0
  25. package/markdown/index.js +11 -0
  26. package/markdown/page-generators/class-page.js +357 -0
  27. package/markdown/page-generators/enum-page.js +152 -0
  28. package/markdown/page-generators/function-page.js +127 -0
  29. package/markdown/page-generators/index-pages.js +27 -0
  30. package/markdown/page-generators/interface-page.js +307 -0
  31. package/markdown/page-generators/namespace-page.js +280 -0
  32. package/markdown/page-generators/type-alias-page.js +110 -0
  33. package/markdown/page-generators/variable-page.js +110 -0
  34. package/markdown/shiki-utils.js +48 -0
  35. package/migrations/001_create_snapshots.js +25 -0
  36. package/model-loader.js +95 -0
  37. package/multi-entry-resolver.js +70 -0
  38. package/og-resolver.js +271 -0
  39. package/package.json +63 -73
  40. package/path-derivation.js +48 -0
  41. package/plugin.js +218 -0
  42. package/prettier-formatter.js +73 -0
  43. package/public/tsconfig/rspress.json +44 -0
  44. package/remark-api-codeblocks.js +130 -0
  45. package/remark-with-api.js +172 -0
  46. package/route-collisions.js +52 -0
  47. package/runtime/components/ApiExample/index.js +29 -25
  48. package/runtime/components/ApiLlmsPackageActions/index.js +252 -323
  49. package/runtime/components/ApiLlmsViewOptions/index.js +269 -340
  50. package/runtime/components/ApiMember/index.js +49 -47
  51. package/runtime/components/ApiSignature/index.js +32 -28
  52. package/runtime/components/EnumMembersTable/{index_module.css → index.css} +18 -19
  53. package/runtime/components/EnumMembersTable/index.js +36 -67
  54. package/runtime/components/EnumMembersTable/index.module.js +9 -6
  55. package/runtime/components/ExampleBlock/{index_module.css → index.css} +2 -3
  56. package/runtime/components/ExampleBlock/index.js +23 -28
  57. package/runtime/components/ExampleBlock/index.module.js +8 -5
  58. package/runtime/components/MarkdownContent/index.js +26 -18
  59. package/runtime/components/MarkdownText/index.js +28 -22
  60. package/runtime/components/MemberSignature/{index_module.css → index.css} +5 -6
  61. package/runtime/components/MemberSignature/index.js +46 -46
  62. package/runtime/components/MemberSignature/index.module.js +9 -6
  63. package/runtime/components/ParametersTable/{index_module.css → index.css} +19 -20
  64. package/runtime/components/ParametersTable/index.js +36 -67
  65. package/runtime/components/ParametersTable/index.module.js +9 -6
  66. package/runtime/components/SignatureBlock/{index_module.css → index.css} +5 -6
  67. package/runtime/components/SignatureBlock/index.js +30 -29
  68. package/runtime/components/SignatureBlock/index.module.js +9 -6
  69. package/runtime/components/SignatureCode/{index_module.css → index.css} +9 -10
  70. package/runtime/components/SignatureCode/index.js +40 -32
  71. package/runtime/components/SignatureCode/index.module.js +9 -6
  72. package/runtime/components/SignatureToolbar/{index_module.css → index.css} +18 -20
  73. package/runtime/components/SignatureToolbar/index.js +48 -51
  74. package/runtime/components/SignatureToolbar/index.module.js +13 -10
  75. package/runtime/components/buttons/ButtonGroup.js +13 -6
  76. package/runtime/components/buttons/CopyCodeButton.js +40 -38
  77. package/runtime/components/buttons/WrapSignatureButton.js +20 -16
  78. package/runtime/components/buttons/{index_module.css → index.css} +5 -6
  79. package/runtime/components/buttons/index.module.js +8 -5
  80. package/runtime/components/icons/CheckIcon/index.js +20 -17
  81. package/runtime/components/icons/CopyIcon/index.js +20 -17
  82. package/runtime/components/icons/UnwrapIcon/index.js +22 -18
  83. package/runtime/components/icons/WrapIcon/index.js +20 -17
  84. package/runtime/components/shared/_twoslash.css +3 -10
  85. package/runtime/components/shared/variables.css +0 -3
  86. package/runtime/hooks/useWrapToggle.js +32 -9
  87. package/runtime/index.d.ts +513 -173
  88. package/runtime/index.js +11 -9
  89. package/runtime/utils/decode-hast.js +31 -16
  90. package/runtime/utils/hast-renderer.js +21 -7
  91. package/schemas/config.js +199 -0
  92. package/schemas/index.js +5 -0
  93. package/schemas/opengraph.js +26 -0
  94. package/schemas/performance.js +19 -0
  95. package/serve.js +133 -0
  96. package/services/ConfigService.js +7 -0
  97. package/services/PathDerivationService.js +7 -0
  98. package/services/SnapshotService.js +7 -0
  99. package/services/TypeRegistryService.js +7 -0
  100. package/shiki-transformer.js +758 -0
  101. package/tsconfig-parser.js +127 -0
  102. package/tsdoc-metadata.json +11 -11
  103. package/twoslash-patterns.js +87 -0
  104. package/twoslash-transformer.js +316 -0
  105. package/type-reference-extractor.js +201 -0
  106. package/typescript-config.js +168 -0
  107. package/vfs-registry.js +121 -0
  108. package/0~llms-program.js +0 -344
  109. package/runtime/components/ApiLlmsPackageActions/index.module.js +0 -2
@@ -0,0 +1,110 @@
1
+ import { ApiParser } from "../../loader.js";
2
+ import { TypeReferenceExtractor } from "../../type-reference-extractor.js";
3
+ import { escapeMdxGenerics, formatExampleCode, generateAvailableFrom, generateFrontmatter, prepareExampleCode, prependHiddenImports, stripTwoslashDirectives } from "../helpers.js";
4
+ import { markdownCrossLinker } from "../cross-linker.js";
5
+ import { TypeSignatureFormatter } from "../../formatter.js";
6
+
7
+ //#region src/markdown/page-generators/type-alias-page.ts
8
+ /**
9
+ * Generates MDX documentation pages for TypeScript type aliases.
10
+ *
11
+ * This class transforms API Extractor type alias models into rich MDX documentation
12
+ * pages with syntax-highlighted signatures and cross-linked type references.
13
+ *
14
+ * **Page Structure:**
15
+ * 1. Frontmatter with title, description, and Open Graph metadata
16
+ * 2. Component imports
17
+ * 3. Page title (H1) and summary
18
+ * 4. Optional deprecation warning and release tag badge
19
+ * 5. Source code link toolbar
20
+ * 6. Type alias signature block (full type definition)
21
+ * 7. Examples section with Twoslash-enabled code blocks
22
+ * 8. See Also references
23
+ *
24
+ * **Relationships:**
25
+ * - Created and invoked by {@link ApiExtractorPlugin} during page generation
26
+ * - Uses {@link TypeSignatureFormatter} for formatting type signatures
27
+ * - Uses {@link ApiParser} for extracting documentation from API models
28
+ * - Uses {@link MarkdownCrossLinker} for adding type reference links
29
+ *
30
+ * @see {@link InterfacePageGenerator} for interface documentation
31
+ * @see {@link EnumPageGenerator} for enum documentation
32
+ */
33
+ var TypeAliasPageGenerator = class {
34
+ typeFormatter = new TypeSignatureFormatter();
35
+ /**
36
+ * Generate a markdown page for a type alias
37
+ *
38
+ * @param apiScope - API scope identifier for VFS lookup
39
+ */
40
+ async generate(apiTypeAlias, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom) {
41
+ const shouldSuppressErrors = suppressExampleErrors ?? true;
42
+ const name = apiTypeAlias.displayName;
43
+ const summary = ApiParser.getSummary(apiTypeAlias) || "No description available.";
44
+ const releaseTag = ApiParser.getReleaseTag(apiTypeAlias);
45
+ let content = generateFrontmatter(name, summary, singularName, apiName);
46
+ content += `import { SourceCode } from "@rspress/core/theme";\n`;
47
+ content += `import { ParametersTable } from "rspress-plugin-api-extractor/runtime";\n`;
48
+ content += `import { ApiSignature, ApiExample } from "rspress-plugin-api-extractor/runtime";\n\n`;
49
+ content += `# ${name}\n\n`;
50
+ const deprecation = ApiParser.getDeprecation(apiTypeAlias);
51
+ if (deprecation) {
52
+ const message = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(deprecation.message));
53
+ content += `> ⚠️ **Deprecated:** ${message}\n\n`;
54
+ }
55
+ if (releaseTag !== "Public") content += `\`${releaseTag}\`\n\n`;
56
+ content += `${summary}\n\n`;
57
+ content += generateAvailableFrom(packageName, availableFrom);
58
+ const sourceLink = ApiParser.getSourceLink(apiTypeAlias, sourceConfig);
59
+ if (sourceLink) {
60
+ content += `<div className="api-docs-toolbar">\n`;
61
+ content += ` <div className="api-docs-toolbar-left">\n`;
62
+ content += ` <SourceCode href="${sourceLink}" />\n`;
63
+ content += ` </div>\n`;
64
+ if (llmsPlugin?.enabled) {
65
+ content += ` <div className="api-docs-toolbar-right">\n`;
66
+ content += ` </div>\n`;
67
+ }
68
+ content += `</div>\n\n`;
69
+ }
70
+ if (apiTypeAlias.excerpt.text) {
71
+ const signature = this.typeFormatter.format(apiTypeAlias.excerpt).trim();
72
+ let signatureWithImports = signature;
73
+ const apiPackage = apiTypeAlias.getAssociatedPackage?.();
74
+ if (apiPackage) signatureWithImports = prependHiddenImports(signature, new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(apiTypeAlias));
75
+ const displayCode = stripTwoslashDirectives(signatureWithImports);
76
+ content += `<ApiSignature code={${JSON.stringify(displayCode)}} source={${JSON.stringify(signatureWithImports)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
77
+ }
78
+ const examples = ApiParser.getExamples(apiTypeAlias);
79
+ if (examples.length > 0) {
80
+ content += `## Examples\n\n`;
81
+ for (const example of examples) {
82
+ const prepared = prepareExampleCode(example, name, packageName, shouldSuppressErrors);
83
+ const formattedCode = await formatExampleCode(prepared.code, prepared.language, {
84
+ api: packageName,
85
+ blockType: "example"
86
+ });
87
+ if (prepared.isTypeScript) {
88
+ const displayCode = stripTwoslashDirectives(formattedCode);
89
+ content += `<ApiExample code={${JSON.stringify(displayCode)}} source={${JSON.stringify(formattedCode)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
90
+ } else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
91
+ }
92
+ }
93
+ const seeReferences = ApiParser.getSeeReferences(apiTypeAlias);
94
+ if (seeReferences.length > 0) {
95
+ content += `## See Also\n\n`;
96
+ for (const reference of seeReferences) {
97
+ const refText = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(reference.text));
98
+ content += `- ${refText}\n`;
99
+ }
100
+ content += `\n`;
101
+ }
102
+ return {
103
+ routePath: `${baseRoute}/type/${name.toLowerCase()}`,
104
+ content
105
+ };
106
+ }
107
+ };
108
+
109
+ //#endregion
110
+ export { TypeAliasPageGenerator };
@@ -0,0 +1,110 @@
1
+ import { ApiParser } from "../../loader.js";
2
+ import { TypeReferenceExtractor } from "../../type-reference-extractor.js";
3
+ import { escapeMdxGenerics, formatExampleCode, generateAvailableFrom, generateFrontmatter, prepareExampleCode, prependHiddenImports, stripTwoslashDirectives } from "../helpers.js";
4
+ import { markdownCrossLinker } from "../cross-linker.js";
5
+ import { TypeSignatureFormatter } from "../../formatter.js";
6
+
7
+ //#region src/markdown/page-generators/variable-page.ts
8
+ /**
9
+ * Generates MDX documentation pages for exported variables and constants.
10
+ *
11
+ * This class transforms API Extractor variable models into rich MDX documentation
12
+ * pages with syntax-highlighted signatures and cross-linked type references.
13
+ *
14
+ * **Page Structure:**
15
+ * 1. Frontmatter with title, description, and Open Graph metadata
16
+ * 2. Component imports
17
+ * 3. Page title (H1) and summary
18
+ * 4. Optional deprecation warning and release tag badge
19
+ * 5. Source code link toolbar
20
+ * 6. Variable signature block (const/let declaration with type)
21
+ * 7. Examples section with Twoslash-enabled code blocks
22
+ * 8. See Also references
23
+ *
24
+ * **Relationships:**
25
+ * - Created and invoked by {@link ApiExtractorPlugin} during page generation
26
+ * - Uses {@link TypeSignatureFormatter} for formatting type signatures
27
+ * - Uses {@link ApiParser} for extracting documentation from API models
28
+ * - Uses {@link MarkdownCrossLinker} for adding type reference links
29
+ *
30
+ * @see {@link FunctionPageGenerator} for function documentation
31
+ * @see {@link EnumPageGenerator} for enum documentation
32
+ */
33
+ var VariablePageGenerator = class {
34
+ typeFormatter = new TypeSignatureFormatter();
35
+ /**
36
+ * Generate a markdown page for a variable
37
+ *
38
+ * @param apiScope - API scope identifier for VFS lookup
39
+ */
40
+ async generate(apiVariable, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom) {
41
+ const shouldSuppressErrors = suppressExampleErrors ?? true;
42
+ const name = apiVariable.displayName;
43
+ const summary = ApiParser.getSummary(apiVariable) || "No description available.";
44
+ const releaseTag = ApiParser.getReleaseTag(apiVariable);
45
+ let content = generateFrontmatter(name, summary, singularName, apiName);
46
+ content += `import { SourceCode } from "@rspress/core/theme";\n`;
47
+ content += `import { ParametersTable } from "rspress-plugin-api-extractor/runtime";\n`;
48
+ content += `import { ApiSignature, ApiExample } from "rspress-plugin-api-extractor/runtime";\n\n`;
49
+ content += `# ${name}\n\n`;
50
+ const deprecation = ApiParser.getDeprecation(apiVariable);
51
+ if (deprecation) {
52
+ const message = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(deprecation.message));
53
+ content += `> ⚠️ **Deprecated:** ${message}\n\n`;
54
+ }
55
+ if (releaseTag !== "Public") content += `\`${releaseTag}\`\n\n`;
56
+ content += `${summary}\n\n`;
57
+ content += generateAvailableFrom(packageName, availableFrom);
58
+ const sourceLink = ApiParser.getSourceLink(apiVariable, sourceConfig);
59
+ if (sourceLink) {
60
+ content += `<div className="api-docs-toolbar">\n`;
61
+ content += ` <div className="api-docs-toolbar-left">\n`;
62
+ content += ` <SourceCode href="${sourceLink}" />\n`;
63
+ content += ` </div>\n`;
64
+ if (llmsPlugin?.enabled) {
65
+ content += ` <div className="api-docs-toolbar-right">\n`;
66
+ content += ` </div>\n`;
67
+ }
68
+ content += `</div>\n\n`;
69
+ }
70
+ if (apiVariable.excerpt.text) {
71
+ const signature = this.typeFormatter.format(apiVariable.excerpt).trim();
72
+ let signatureWithImports = signature;
73
+ const apiPackage = apiVariable.getAssociatedPackage?.();
74
+ if (apiPackage) signatureWithImports = prependHiddenImports(signature, new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(apiVariable));
75
+ const displayCode = stripTwoslashDirectives(signatureWithImports);
76
+ content += `<ApiSignature code={${JSON.stringify(displayCode)}} source={${JSON.stringify(signatureWithImports)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
77
+ }
78
+ const examples = ApiParser.getExamples(apiVariable);
79
+ if (examples.length > 0) {
80
+ content += `## Examples\n\n`;
81
+ for (const example of examples) {
82
+ const prepared = prepareExampleCode(example, name, packageName, shouldSuppressErrors);
83
+ const formattedCode = await formatExampleCode(prepared.code, prepared.language, {
84
+ api: packageName,
85
+ blockType: "example"
86
+ });
87
+ if (prepared.isTypeScript) {
88
+ const displayCode = stripTwoslashDirectives(formattedCode);
89
+ content += `<ApiExample code={${JSON.stringify(displayCode)}} source={${JSON.stringify(formattedCode)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
90
+ } else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
91
+ }
92
+ }
93
+ const seeReferences = ApiParser.getSeeReferences(apiVariable);
94
+ if (seeReferences.length > 0) {
95
+ content += `## See Also\n\n`;
96
+ for (const reference of seeReferences) {
97
+ const refText = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(reference.text));
98
+ content += `- ${refText}\n`;
99
+ }
100
+ content += `\n`;
101
+ }
102
+ return {
103
+ routePath: `${baseRoute}/variable/${name.toLowerCase()}`,
104
+ content
105
+ };
106
+ }
107
+ };
108
+
109
+ //#endregion
110
+ export { VariablePageGenerator };
@@ -0,0 +1,48 @@
1
+ //#region src/markdown/shiki-utils.ts
2
+ /**
3
+ * Default Shiki theme configuration
4
+ */
5
+ const DEFAULT_SHIKI_THEMES = {
6
+ light: "github-light-default",
7
+ dark: "github-dark-default"
8
+ };
9
+ /**
10
+ * Generate a HAST (Hypertext Abstract Syntax Tree) from TypeScript code using Shiki.
11
+ *
12
+ * This function replaces the duplicated `generateShikiHtml` methods across page generators.
13
+ * It produces a JSON-serializable HAST tree instead of HTML strings, which:
14
+ * - Eliminates MDX parsing issues caused by long HTML strings with special characters
15
+ * - Allows clean JSON serialization in generated MDX files
16
+ * - Enables runtime rendering via `hast-util-to-jsx-runtime` without `dangerouslySetInnerHTML`
17
+ *
18
+ * @param code - The TypeScript code to highlight
19
+ * @param highlighter - Shiki highlighter instance (optional, returns null if not provided)
20
+ * @param transformers - Optional array of Shiki transformers (e.g., Twoslash, cross-linker)
21
+ * @param enableTwoslash - If true, adds meta to trigger Twoslash directive processing
22
+ * @param theme - Optional theme configuration (defaults to github-light/github-dark)
23
+ * @returns A HAST root node, or null if no highlighter is provided or an error occurs
24
+ */
25
+ async function generateShikiHast(code, highlighter, transformers, enableTwoslash, theme) {
26
+ if (!highlighter) return null;
27
+ const resolvedTheme = theme ?? DEFAULT_SHIKI_THEMES;
28
+ try {
29
+ const options = {
30
+ lang: "typescript",
31
+ themes: {
32
+ light: resolvedTheme.light,
33
+ dark: resolvedTheme.dark
34
+ },
35
+ defaultColor: false,
36
+ cssVariablePrefix: "--api-shiki-",
37
+ transformers: transformers || []
38
+ };
39
+ if (enableTwoslash) options.meta = { __raw: "twoslash" };
40
+ return await highlighter.codeToHast(code, options);
41
+ } catch (error) {
42
+ console.error("Failed to generate Shiki HAST:", error);
43
+ return null;
44
+ }
45
+ }
46
+
47
+ //#endregion
48
+ export { DEFAULT_SHIKI_THEMES, generateShikiHast };
@@ -0,0 +1,25 @@
1
+ import { Effect } from "effect";
2
+ import * as SqlClient from "@effect/sql/SqlClient";
3
+
4
+ //#region src/migrations/001_create_snapshots.ts
5
+ const migration = Effect.gen(function* () {
6
+ const sql = yield* SqlClient.SqlClient;
7
+ yield* sql`
8
+ CREATE TABLE IF NOT EXISTS file_snapshots (
9
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
10
+ output_dir TEXT NOT NULL,
11
+ file_path TEXT NOT NULL,
12
+ published_time TEXT NOT NULL,
13
+ modified_time TEXT NOT NULL,
14
+ content_hash TEXT NOT NULL,
15
+ frontmatter_hash TEXT NOT NULL,
16
+ build_time TEXT NOT NULL,
17
+ UNIQUE(output_dir, file_path)
18
+ )
19
+ `;
20
+ yield* sql`CREATE INDEX IF NOT EXISTS idx_output_dir ON file_snapshots(output_dir)`;
21
+ yield* sql`CREATE INDEX IF NOT EXISTS idx_file_path ON file_snapshots(file_path)`;
22
+ });
23
+
24
+ //#endregion
25
+ export { migration as default };
@@ -0,0 +1,95 @@
1
+ import { isLoadedModel, isVersionConfig } from "./config-utils.js";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { loadApiModel } from "api-extractor-llms";
5
+
6
+ //#region src/model-loader.ts
7
+ /**
8
+ * Utility class for loading API models from various sources
9
+ */
10
+ var ApiModelLoader = class ApiModelLoader {
11
+ /**
12
+ * Private constructor to prevent instantiation
13
+ */
14
+ constructor() {}
15
+ /**
16
+ * Load an API model from a path (string, URL, or Buffer)
17
+ */
18
+ static async loadFromPath(modelPath) {
19
+ const resolvedPath = path.resolve(modelPath.toString());
20
+ if (!fs.existsSync(resolvedPath)) throw new Error(`API model file not found: ${resolvedPath}`);
21
+ return loadApiModel(resolvedPath);
22
+ }
23
+ /**
24
+ * Load package.json from a path (string, URL, or Buffer)
25
+ */
26
+ static async loadPackageJsonFromPath(pkgPath) {
27
+ const resolvedPath = path.resolve(pkgPath.toString());
28
+ if (!fs.existsSync(resolvedPath)) throw new Error(`Package.json file not found: ${resolvedPath}`);
29
+ const content = fs.readFileSync(resolvedPath, "utf-8");
30
+ try {
31
+ return JSON.parse(content);
32
+ } catch (error) {
33
+ throw new Error(`Failed to parse package.json at ${resolvedPath}: ${error.message}`);
34
+ }
35
+ }
36
+ /**
37
+ * Load package.json from PathLike or async function
38
+ */
39
+ static async loadPackageJson(loader) {
40
+ if (typeof loader === "function") return await loader();
41
+ return await ApiModelLoader.loadPackageJsonFromPath(loader);
42
+ }
43
+ /**
44
+ * Load an API model from PathLike or async function
45
+ */
46
+ static async loadApiModel(loader) {
47
+ if (typeof loader === "function") {
48
+ const result = await loader();
49
+ if (isLoadedModel(result)) {
50
+ const model = result.model;
51
+ if (model && typeof model === "object" && "packages" in model) {
52
+ const packages = model.packages;
53
+ if (packages.length === 0) throw new Error("API model returned by function contains no packages");
54
+ const loadedResult = { apiPackage: packages[0] };
55
+ if (result.source != null) loadedResult.source = result.source;
56
+ return loadedResult;
57
+ }
58
+ throw new Error("API model loader function must return an ApiModel");
59
+ }
60
+ if (result && typeof result === "object" && "packages" in result) {
61
+ const packages = result.packages;
62
+ if (packages.length === 0) throw new Error("API model returned by function contains no packages");
63
+ return { apiPackage: packages[0] };
64
+ }
65
+ throw new Error("API model loader function must return an ApiModel or LoadedModel");
66
+ }
67
+ return { apiPackage: await ApiModelLoader.loadFromPath(loader) };
68
+ }
69
+ /**
70
+ * Resolve and load a version config
71
+ */
72
+ static async loadVersionModel(versionValue) {
73
+ if (isVersionConfig(versionValue)) {
74
+ const { apiPackage, source: loaderSource } = await ApiModelLoader.loadApiModel(versionValue.model);
75
+ const packageJson = versionValue.packageJson ? await ApiModelLoader.loadPackageJson(versionValue.packageJson) : void 0;
76
+ const versionResult = { apiPackage };
77
+ if (packageJson != null) versionResult.packageJson = packageJson;
78
+ if (versionValue.categories != null) versionResult.categories = versionValue.categories;
79
+ const resolvedSource = loaderSource || versionValue.source;
80
+ if (resolvedSource != null) versionResult.source = resolvedSource;
81
+ if (versionValue.externalPackages != null) versionResult.externalPackages = versionValue.externalPackages;
82
+ if (versionValue.autoDetectDependencies != null) versionResult.autoDetectDependencies = versionValue.autoDetectDependencies;
83
+ if (versionValue.ogImage != null) versionResult.ogImage = versionValue.ogImage;
84
+ if (versionValue.llmsPlugin != null) versionResult.llmsPlugin = versionValue.llmsPlugin;
85
+ return versionResult;
86
+ }
87
+ const { apiPackage, source } = await ApiModelLoader.loadApiModel(versionValue);
88
+ const simpleResult = { apiPackage };
89
+ if (source != null) simpleResult.source = source;
90
+ return simpleResult;
91
+ }
92
+ };
93
+
94
+ //#endregion
95
+ export { ApiModelLoader };
@@ -0,0 +1,70 @@
1
+ //#region src/multi-entry-resolver.ts
2
+ /**
3
+ * Derive an entry point name from its display name in the API model.
4
+ *
5
+ * - Empty string (main entry "." in package.json) maps to "default"
6
+ * - Named entries (e.g., "testing") keep their name
7
+ */
8
+ function getEntryPointName(displayName) {
9
+ return displayName === "" ? "default" : displayName;
10
+ }
11
+ /**
12
+ * Create a stable identity key for an API item based on its display name and kind.
13
+ * Used to detect re-exports across entry points.
14
+ */
15
+ function itemKey(item) {
16
+ return `${item.displayName}::${item.kind}`;
17
+ }
18
+ /**
19
+ * Resolve all entry points from an API package into a flat list of
20
+ * deduplicated items.
21
+ *
22
+ * - Re-exported items (same displayName + kind across entries) are
23
+ * deduplicated to a single entry with availableFrom listing all
24
+ * entry points. The defining entry point prefers "default".
25
+ * - Items with different kinds but the same displayName (e.g. the
26
+ * Effect const + type companion pattern) remain as separate entries.
27
+ *
28
+ * @param apiPackage - The merged API package with 1+ entry points
29
+ * @returns Flat array of resolved items
30
+ */
31
+ function resolveEntryPoints(apiPackage) {
32
+ const itemsByKey = /* @__PURE__ */ new Map();
33
+ for (const entryPoint of apiPackage.entryPoints) {
34
+ const epName = getEntryPointName(entryPoint.displayName);
35
+ for (const member of entryPoint.members) {
36
+ const key = itemKey(member);
37
+ const existing = itemsByKey.get(key) || [];
38
+ existing.push({
39
+ item: member,
40
+ entryPointName: epName
41
+ });
42
+ itemsByKey.set(key, existing);
43
+ }
44
+ }
45
+ const intermediate = [];
46
+ for (const [, entries] of itemsByKey) if (entries.length === 1) {
47
+ const { item, entryPointName } = entries[0];
48
+ intermediate.push({
49
+ item,
50
+ definingEntryPoint: entryPointName,
51
+ availableFrom: [entryPointName]
52
+ });
53
+ } else {
54
+ const definingEntry = entries.find((e) => e.entryPointName === "default") || entries[0];
55
+ const allEntryPoints = [...new Set(entries.map((e) => e.entryPointName))];
56
+ intermediate.push({
57
+ item: definingEntry.item,
58
+ definingEntryPoint: definingEntry.entryPointName,
59
+ availableFrom: allEntryPoints
60
+ });
61
+ }
62
+ return intermediate.map((r) => ({
63
+ item: r.item,
64
+ definingEntryPoint: r.definingEntryPoint,
65
+ availableFrom: r.availableFrom
66
+ }));
67
+ }
68
+
69
+ //#endregion
70
+ export { resolveEntryPoints };