Skip to content

Commit bf24f62

Browse files
wixysamcursoragent
andauthored
Update docs and type generation for better clarity (#127)
* Update docs and type generation for better clarity - Add Generated Types sections to SDK interfaces - Improve JSDoc comments for type registries - Clean up signatures and remove redundant Type Parameters sections - Simplify field-selection generics in entity methods Co-authored-by: Cursor <cursoragent@cursor.com> * Update FunctionName example to use calculateTotal Changed from sendEmail to calculateTotal to match the invoke() function examples for consistency. Co-authored-by: Cursor <cursoragent@cursor.com> * Regenerate docs with updated FunctionName example Co-authored-by: Cursor <cursoragent@cursor.com> * Fix JSDoc links and add connectors to type generation docs - Fix all module JSDoc links to reference /dynamic-types instead of /generated-types - Remove bad import example from EntityRecord JSDoc (don't import from @base44/.types/types) - Add connectors to type generation documentation following the pattern for entities/functions/agents - Update ConnectorIntegrationTypeRegistry and ConnectorIntegrationType JSDoc to match other registries - Add Dynamic Types section to ConnectorsModule JSDoc Addresses PR feedback from kfirstri on PR #127. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent e3ed400 commit bf24f62

10 files changed

Lines changed: 447 additions & 56 deletions

File tree

scripts/mintlify-post-processing/appended-articles.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
{
2-
"interfaces/EntitiesModule": "interfaces/EntityHandler",
2+
"type-aliases/EntitiesModule": [
3+
"interfaces/EntityHandler",
4+
"type-aliases/EntityRecord",
5+
"interfaces/EntityTypeRegistry"
6+
],
7+
"interfaces/FunctionsModule": [
8+
"type-aliases/FunctionName",
9+
"interfaces/FunctionNameRegistry"
10+
],
11+
"interfaces/AgentsModule": [
12+
"type-aliases/AgentName",
13+
"interfaces/AgentNameRegistry"
14+
],
315
"type-aliases/integrations": [
416
"interfaces/CoreIntegrations",
517
"interfaces/CustomIntegrationsModule"

scripts/mintlify-post-processing/file-processing/file-processing.js

Lines changed: 303 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const TEMPLATE_PATH = path.join(__dirname, "docs-json-template.json");
2525
const STYLING_CSS_PATH = path.join(__dirname, "styling.css");
2626
const CATEGORY_MAP_PATH = path.join(__dirname, "../category-map.json");
2727
const TYPES_TO_EXPOSE_PATH = path.join(__dirname, "..", "types-to-expose.json");
28+
const TYPES_TO_DELETE_PATH = path.join(__dirname, "..", "types-to-delete-after-processing.json");
2829
const APPENDED_ARTICLES_PATH = path.join(
2930
__dirname,
3031
"../appended-articles.json"
@@ -135,9 +136,11 @@ function processLinksInFile(filePath) {
135136
modified = true;
136137
}
137138

138-
// Remove undesirable lines like "> **IntegrationsModule** = `object` & `object`"
139-
// This typically appears in type alias files using intersection types
140-
const typeDefinitionRegex = /^> \*\*\w+\*\* = `object` & `object`\s*$/m;
139+
// Remove undesirable type-alias definition lines like:
140+
// > **IntegrationsModule** = `object` & `object`
141+
// > **EntitiesModule** = `TypedEntitiesModule` & `DynamicEntitiesModule`
142+
// These appear in type alias files using intersection types and are not useful in docs.
143+
const typeDefinitionRegex = /^> \*\*\w+\*\* = `\w+` & `\w+`\s*$/m;
141144
if (typeDefinitionRegex.test(content)) {
142145
content = content.replace(typeDefinitionRegex, "");
143146
modified = true;
@@ -761,6 +764,255 @@ function applyAppendedArticles(appendedArticles) {
761764
}
762765
}
763766

767+
/**
768+
* Clean up method signatures and type parameter sections:
769+
* 1. Replace truncated generics (e.g., Pick<..., ...> → Pick<T, K>)
770+
* 2. Simplify resolved keyof constraints (string | number | symbol → keyof T)
771+
* 3. Break long signature lines into multi-line blockquote format
772+
* 4. Remove method-level Type Parameters sections (redundant with signature + param docs)
773+
* 4b. Remove page-level ## Type Parameters sections (not useful in docs)
774+
* 5. Clean up broken function-return-type sections (e.g., () => void returns)
775+
* 7. Simplify field-selection generics: remove \<K\> from signatures, Pick<T,K> → T, K[] → (keyof T)[]
776+
*/
777+
function cleanupSignatures(content) {
778+
let modified = false;
779+
780+
// Fix 7: Simplify field-selection generic K out of signatures.
781+
// K is a TypeScript implementation detail for field selection (Pick<T, K>).
782+
// In docs it's confusing — replace with clearer types.
783+
784+
// 7a: Annotate \<`K`\> with its constraint → \<`K extends keyof T`\>
785+
if (content.includes("\\<`K`\\>")) {
786+
content = content.replace(/\\<`K`\\>/g, "\\<`K extends keyof T`\\>");
787+
modified = true;
788+
}
789+
790+
// 7b: Expand truncated `Pick`\<..., ...\> to `Pick`\<`T`, `K`\>
791+
if (content.includes("`Pick`\\<..., ...\\>")) {
792+
content = content.replace(/`Pick`\\<\.\.\., \.\.\.\\>/g, "`Pick`\\<`T`, `K`\\>");
793+
modified = true;
794+
}
795+
796+
// 7c: Replace type="K[]" with type="(keyof T)[]" in ParamField elements
797+
if (content.includes('type="K[]"')) {
798+
content = content.replace(/type="K\[\]"/g, 'type="(keyof T)[]"');
799+
modified = true;
800+
}
801+
802+
// Fix 5: Clean up broken function-return-type patterns.
803+
// When a method returns a function (e.g., () => void), TypeDoc generates a stray
804+
// function signature and an empty Accordion in the Returns section. Remove them.
805+
// Pattern: "> (): `void`" followed by empty Accordion with "Returns" ResponseField.
806+
content = content.replace(
807+
/\n> \(\): `void`\n\n<Accordion title="Properties">\n\n<ResponseField name="Returns" type="void" required>\n\n<\/ResponseField>\n<\/Accordion>\n/g,
808+
() => {
809+
modified = true;
810+
return "\n";
811+
}
812+
);
813+
814+
// Fix 6: Clean up truncated EntityRecord mapped type signature.
815+
// TypeDoc renders `EntityTypeRegistry[K]` as `(...)[(...)]`.
816+
content = content.replace(
817+
/\(\.\.\.\)\[\(\.\.\.\)\]\s*&\s*ServerEntityFields/g,
818+
() => {
819+
modified = true;
820+
return "EntityTypeRegistry[K] & ServerEntityFields";
821+
}
822+
);
823+
824+
const lines = content.split("\n");
825+
826+
// Collect page-level type parameter names from ## Type Parameters section.
827+
// Before heading demotion, these are ### headings (e.g., ### T).
828+
const pageTypeParams = [];
829+
for (let i = 0; i < lines.length; i++) {
830+
if (lines[i].trim() === "## Type Parameters") {
831+
for (let j = i + 1; j < lines.length; j++) {
832+
if (lines[j].startsWith("## ") && lines[j].trim() !== "## Type Parameters")
833+
break;
834+
const paramMatch = lines[j].match(/^#{3,5}\s+(\w+)\s*$/);
835+
if (paramMatch) {
836+
pageTypeParams.push(paramMatch[1]);
837+
}
838+
}
839+
break;
840+
}
841+
}
842+
843+
const result = [];
844+
for (let i = 0; i < lines.length; i++) {
845+
let line = lines[i];
846+
847+
// Fix 1: Replace `string` | `number` | `symbol` with keyof `T`
848+
// TypeDoc resolves `keyof T` to `string | number | symbol` when T is unconstrained.
849+
if (line.includes("`string` | `number` | `symbol`")) {
850+
const defaultMatch = line.match(/= keyof `(\w+)`/);
851+
const typeName =
852+
defaultMatch ? defaultMatch[1] : pageTypeParams[0] || "T";
853+
line = line.replace(
854+
/`string` \| `number` \| `symbol`( = keyof `\w+`)?/,
855+
"keyof `" + typeName + "`"
856+
);
857+
modified = true;
858+
}
859+
860+
// Fix 4b: Remove page-level ## Type Parameters sections.
861+
// These are not useful in docs — generic type params are an implementation detail.
862+
// Skip from "## Type Parameters" until the next "## " heading.
863+
if (line.trim() === "## Type Parameters") {
864+
let j = i + 1;
865+
while (j < lines.length) {
866+
const upcoming = lines[j].trim();
867+
if (upcoming.startsWith("## ") && upcoming !== "## Type Parameters") break;
868+
j++;
869+
}
870+
// Skip trailing blank lines
871+
while (j > i + 1 && lines[j - 1].trim() === "") {
872+
j--;
873+
}
874+
i = j - 1;
875+
modified = true;
876+
continue;
877+
}
878+
879+
// Fix 4: Remove method-level #### Type Parameters sections.
880+
// These are redundant — the info is already in the signature and parameter docs.
881+
// Skip from "#### Type Parameters" until the next "#### " heading.
882+
if (line.trim() === "#### Type Parameters") {
883+
// Skip ahead past this section until the next #### heading or ### heading
884+
let j = i + 1;
885+
while (j < lines.length) {
886+
const upcoming = lines[j].trim();
887+
if (upcoming.startsWith("#### ") && upcoming !== "#### Type Parameters") break;
888+
if (upcoming.startsWith("### ")) break;
889+
if (upcoming.startsWith("## ")) break;
890+
j++;
891+
}
892+
// Also skip any trailing blank lines
893+
while (j > i + 1 && lines[j - 1].trim() === "") {
894+
j--;
895+
}
896+
i = j - 1; // -1 because the loop will increment
897+
modified = true;
898+
continue;
899+
}
900+
901+
// Fix 2 & 3: Signatures starting with > **methodName**
902+
if (line.startsWith("> **") && line.includes("(")) {
903+
// Extract method-level type params from signature (e.g., \<`K`\>)
904+
const methodTypeParams = [];
905+
const typeParamMatch = line.match(/\\<`(\w+)`\\>/);
906+
if (typeParamMatch) {
907+
methodTypeParams.push(typeParamMatch[1]);
908+
}
909+
910+
// Replace truncated generics: \<..., ...\> → \<`T`, `K`\>
911+
if (line.includes("\\<..., ...\\>")) {
912+
const allTypeParams = [...pageTypeParams, ...methodTypeParams];
913+
if (allTypeParams.length >= 2) {
914+
line = line.replace(
915+
/\\<\.\.\., \.\.\.\\>/g,
916+
"\\<`" + allTypeParams[0] + "`, `" + allTypeParams[1] + "`\\>"
917+
);
918+
modified = true;
919+
}
920+
}
921+
922+
// Break long signatures into multi-line blockquote format.
923+
// Each line ends with two trailing spaces to force a hard line break
924+
// in Mintlify's Markdown renderer (otherwise blockquote lines get joined).
925+
if (line.length > 85) {
926+
const openParen = line.indexOf("(");
927+
const returnMarker = line.lastIndexOf("): ");
928+
929+
if (openParen > -1 && returnMarker > openParen) {
930+
const prefix = line.slice(0, openParen);
931+
const params = line.slice(openParen + 1, returnMarker);
932+
const returnType = line.slice(returnMarker + 1);
933+
934+
const paramList = params.split(", ");
935+
if (paramList.length >= 3) {
936+
result.push(prefix + "( ");
937+
for (let j = 0; j < paramList.length; j++) {
938+
const comma = j < paramList.length - 1 ? "," : "";
939+
result.push("> " + paramList[j] + comma + " ");
940+
}
941+
result.push("> )" + returnType);
942+
modified = true;
943+
continue;
944+
}
945+
}
946+
}
947+
}
948+
949+
result.push(line);
950+
}
951+
952+
// Fix 6: Enrich bare type names in Returns sections with generics from signatures.
953+
// E.g., `ImportResult` → `ImportResult<T>` when the signature shows ImportResult\<T\>.
954+
for (let i = 0; i < result.length; i++) {
955+
const line = result[i];
956+
// Match a standalone backtick-wrapped type name (only content on the line)
957+
const bareTypeMatch = line.match(/^`([A-Z]\w+)`$/);
958+
if (!bareTypeMatch) continue;
959+
const typeName = bareTypeMatch[1];
960+
961+
// Verify this follows a Returns heading (scan back past blank lines)
962+
let isInReturns = false;
963+
for (let j = i - 1; j >= Math.max(0, i - 3); j--) {
964+
if (result[j].trim() === "") continue;
965+
if (/^#{2,5} Returns/.test(result[j])) {
966+
isInReturns = true;
967+
}
968+
break;
969+
}
970+
if (!isInReturns) continue;
971+
972+
// Scan backwards for the nearest signature line
973+
for (let j = i - 1; j >= Math.max(0, i - 30); j--) {
974+
const sigLine = result[j];
975+
if (!sigLine.startsWith("> **") && !sigLine.startsWith("> )")) continue;
976+
// Look for TypeName\<`GenericParam`\> in the signature
977+
const genericPattern = new RegExp(
978+
"`" + typeName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") +
979+
"`\\\\<`(\\w+)`\\\\>"
980+
);
981+
const genMatch = sigLine.match(genericPattern);
982+
if (genMatch) {
983+
result[i] = "`" + typeName + "<" + genMatch[1] + ">`";
984+
modified = true;
985+
}
986+
break;
987+
}
988+
}
989+
990+
return { content: result.join("\n"), modified };
991+
}
992+
993+
function applySignatureCleanup(dir) {
994+
if (!fs.existsSync(dir)) return;
995+
const entries = fs.readdirSync(dir, { withFileTypes: true });
996+
for (const entry of entries) {
997+
const entryPath = path.join(dir, entry.name);
998+
if (entry.isDirectory()) {
999+
applySignatureCleanup(entryPath);
1000+
} else if (
1001+
entry.isFile() &&
1002+
(entry.name.endsWith(".mdx") || entry.name.endsWith(".md"))
1003+
) {
1004+
const content = fs.readFileSync(entryPath, "utf-8");
1005+
const { content: updated, modified } = cleanupSignatures(content);
1006+
if (modified) {
1007+
fs.writeFileSync(entryPath, updated, "utf-8");
1008+
console.log(
1009+
`Cleaned up signatures: ${path.relative(DOCS_DIR, entryPath)}`
1010+
);
1011+
}
1012+
}
1013+
}
1014+
}
1015+
7641016
function demoteNonCallableHeadings(content) {
7651017
const lines = content.split("\n");
7661018
let inFence = false;
@@ -967,6 +1219,48 @@ function applyNonExposedTypeLinkRemoval(dir, exposedTypeNames) {
9671219
/**
9681220
* Main function
9691221
*/
1222+
/**
1223+
* Delete types that should not appear in navigation but were needed for inline rendering.
1224+
* These types are listed in types-to-delete-after-processing.json
1225+
*/
1226+
function deleteTypesAfterProcessing(docsDir) {
1227+
let typesToDelete = new Set();
1228+
try {
1229+
const content = fs.readFileSync(TYPES_TO_DELETE_PATH, "utf-8");
1230+
const parsed = JSON.parse(content);
1231+
if (Array.isArray(parsed)) {
1232+
typesToDelete = new Set(parsed);
1233+
}
1234+
} catch (e) {
1235+
// No types to delete, that's fine
1236+
return;
1237+
}
1238+
1239+
if (typesToDelete.size === 0) {
1240+
return;
1241+
}
1242+
1243+
const contentDir = path.join(docsDir, "content");
1244+
const sections = ["functions", "interfaces", "classes", "type-aliases"];
1245+
1246+
for (const section of sections) {
1247+
const sectionDir = path.join(contentDir, section);
1248+
if (!fs.existsSync(sectionDir)) continue;
1249+
1250+
const files = fs.readdirSync(sectionDir);
1251+
for (const file of files) {
1252+
if (!file.endsWith(".mdx") && !file.endsWith(".md")) continue;
1253+
1254+
const fileName = path.basename(file, path.extname(file));
1255+
if (typesToDelete.has(fileName)) {
1256+
const filePath = path.join(sectionDir, file);
1257+
fs.unlinkSync(filePath);
1258+
console.log(`Removed (after processing): content/${section}/${file}`);
1259+
}
1260+
}
1261+
}
1262+
}
1263+
9701264
function main() {
9711265
console.log("Processing TypeDoc MDX files for Mintlify...\n");
9721266

@@ -990,6 +1284,9 @@ function main() {
9901284
const appendedArticles = loadAppendedArticlesConfig();
9911285
applyAppendedArticles(appendedArticles);
9921286

1287+
// Clean up signatures: fix truncated generics, simplify keyof constraints, break long lines
1288+
applySignatureCleanup(DOCS_DIR);
1289+
9931290
applyHeadingDemotion(DOCS_DIR);
9941291

9951292
// Link type names in Type Declarations sections to their corresponding headings
@@ -998,6 +1295,9 @@ function main() {
9981295
// Remove links to types that aren't exposed (would 404)
9991296
applyNonExposedTypeLinkRemoval(DOCS_DIR, exposedTypeNames);
10001297

1298+
// Delete types that should not appear in navigation but were needed for inline rendering
1299+
deleteTypesAfterProcessing(DOCS_DIR);
1300+
10011301
// Clean up the linked types file
10021302
try {
10031303
if (fs.existsSync(LINKED_TYPES_FILE)) {

0 commit comments

Comments
 (0)