Skip to content

Commit 7020e4e

Browse files
authored
Merge branch 'main' into docs/functions-fetch
2 parents b5fe7ea + 0af6790 commit 7020e4e

4 files changed

Lines changed: 247 additions & 36 deletions

File tree

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

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const APPENDED_ARTICLES_PATH = path.join(
3030
__dirname,
3131
"../appended-articles.json"
3232
);
33+
const METHOD_ORDER_PATH = path.join(__dirname, "..", "method-order.json");
3334

3435
// Controlled via env var so we can re-enable Panel injection when needed.
3536
const PANELS_ENABLED = process.env.MINTLIFY_INCLUDE_PANELS === "true";
@@ -1964,6 +1965,40 @@ function cleanupSortFieldSignature(dir) {
19641965
}
19651966
}
19661967

1968+
/**
1969+
* Fix truncated intersection types in ParamField type attributes.
1970+
* TypeDoc renders (Partial<T> & { id: string })[] as "Partial<...> & object[]".
1971+
*/
1972+
function cleanupTruncatedParamTypes(dir) {
1973+
if (!fs.existsSync(dir)) return;
1974+
const entries = fs.readdirSync(dir, { withFileTypes: true });
1975+
for (const entry of entries) {
1976+
const entryPath = path.join(dir, entry.name);
1977+
if (entry.isDirectory()) {
1978+
cleanupTruncatedParamTypes(entryPath);
1979+
} else if (
1980+
entry.isFile() &&
1981+
(entry.name.endsWith(".mdx") || entry.name.endsWith(".md"))
1982+
) {
1983+
let content = fs.readFileSync(entryPath, "utf-8");
1984+
let modified = false;
1985+
1986+
if (content.includes('type="Partial<...> & object[]"')) {
1987+
content = content.replace(
1988+
/type="Partial<\.\.\.> & object\[\]"/g,
1989+
'type="(Partial<T> & { id: string })[]"'
1990+
);
1991+
modified = true;
1992+
}
1993+
1994+
if (modified) {
1995+
fs.writeFileSync(entryPath, content, "utf-8");
1996+
console.log(`Cleaned truncated param type: ${path.relative(DOCS_DIR, entryPath)}`);
1997+
}
1998+
}
1999+
}
2000+
}
2001+
19672002
/**
19682003
* Main function
19692004
*/
@@ -2009,6 +2044,127 @@ function deleteTypesAfterProcessing(docsDir) {
20092044
}
20102045
}
20112046

2047+
/**
2048+
* Load method-order.json config.
2049+
* Keys are MDX base filenames (e.g. "entities"), values are ordered method name arrays.
2050+
*/
2051+
function loadMethodOrderConfig() {
2052+
if (!fs.existsSync(METHOD_ORDER_PATH)) return {};
2053+
return JSON.parse(fs.readFileSync(METHOD_ORDER_PATH, "utf-8"));
2054+
}
2055+
2056+
/**
2057+
* Reorder method blocks inside an MDX file according to a given ordered list.
2058+
*
2059+
* The file is split on `***` horizontal-rule separators into sections. Sections
2060+
* whose first `### name()` heading matches an entry in `orderedNames` are
2061+
* reordered accordingly; unlisted method sections keep their relative order
2062+
* and appear after explicitly ordered ones.
2063+
*
2064+
* Type-definition sections that are displaced between the `## ... Methods`
2065+
* heading and the first method are relocated to after the footer (where
2066+
* `## Type Definitions` lives), so they appear under the correct TOC group.
2067+
*/
2068+
function reorderMethods(content, orderedNames) {
2069+
const sections = content.split(/\n*\*{3}\n*/);
2070+
const HR = "\n\n***\n\n";
2071+
2072+
const methodRe = /^### (\w+)\(\)/m;
2073+
const methodsHeadingRe = /^## .+ Methods$/m;
2074+
2075+
const tagged = sections.map((text) => ({
2076+
text,
2077+
methodName: (text.match(methodRe) || [])[1] || null,
2078+
hasMethodsHeading: methodsHeadingRe.test(text),
2079+
}));
2080+
2081+
const firstMethodIdx = tagged.findIndex((t) => t.methodName);
2082+
if (firstMethodIdx === -1) return { content, modified: false };
2083+
2084+
let lastMethodIdx = -1;
2085+
for (let i = tagged.length - 1; i >= 0; i--) {
2086+
if (tagged[i].methodName) {
2087+
lastMethodIdx = i;
2088+
break;
2089+
}
2090+
}
2091+
2092+
const headingIdx = tagged.findIndex((t) => t.hasMethodsHeading);
2093+
const displacedStart = headingIdx !== -1 ? headingIdx + 1 : firstMethodIdx;
2094+
2095+
const header = tagged.slice(0, displacedStart);
2096+
const displaced = tagged.slice(displacedStart, firstMethodIdx);
2097+
const middle = tagged.slice(firstMethodIdx, lastMethodIdx + 1);
2098+
const footer = tagged.slice(lastMethodIdx + 1);
2099+
2100+
const methods = middle.filter((t) => t.methodName);
2101+
const stray = middle.filter((t) => !t.methodName);
2102+
2103+
const orderMap = new Map(orderedNames.map((n, i) => [n, i]));
2104+
const origOrder = methods.map((m) => m.methodName).join(",");
2105+
2106+
methods.sort((a, b) => {
2107+
const ai = orderMap.has(a.methodName)
2108+
? orderMap.get(a.methodName)
2109+
: orderedNames.length + methods.indexOf(a);
2110+
const bi = orderMap.has(b.methodName)
2111+
? orderMap.get(b.methodName)
2112+
: orderedNames.length + methods.indexOf(b);
2113+
return ai - bi;
2114+
});
2115+
2116+
const newOrder = methods.map((m) => m.methodName).join(",");
2117+
if (origOrder === newOrder && displaced.length === 0 && stray.length === 0) {
2118+
return { content, modified: false };
2119+
}
2120+
2121+
const ordered = [
2122+
...header,
2123+
...methods,
2124+
...footer,
2125+
...displaced,
2126+
...stray,
2127+
];
2128+
2129+
return { content: ordered.map((t) => t.text).join(HR), modified: true };
2130+
}
2131+
2132+
/**
2133+
* Apply method ordering to MDX files whose base name matches a key in method-order.json.
2134+
*/
2135+
function applyMethodOrdering(dir) {
2136+
const config = loadMethodOrderConfig();
2137+
if (Object.keys(config).length === 0) return;
2138+
2139+
if (!fs.existsSync(dir)) return;
2140+
const entries = fs.readdirSync(dir, { withFileTypes: true });
2141+
for (const entry of entries) {
2142+
const entryPath = path.join(dir, entry.name);
2143+
if (entry.isDirectory()) {
2144+
applyMethodOrdering(entryPath);
2145+
} else if (
2146+
entry.isFile() &&
2147+
(entry.name.endsWith(".mdx") || entry.name.endsWith(".md"))
2148+
) {
2149+
const baseName = path.basename(entry.name, path.extname(entry.name));
2150+
const orderedNames = config[baseName];
2151+
if (!orderedNames) continue;
2152+
2153+
const content = fs.readFileSync(entryPath, "utf-8");
2154+
const { content: updated, modified } = reorderMethods(
2155+
content,
2156+
orderedNames
2157+
);
2158+
if (modified) {
2159+
fs.writeFileSync(entryPath, updated, "utf-8");
2160+
console.log(
2161+
`Reordered methods: ${path.relative(DOCS_DIR, entryPath)}`
2162+
);
2163+
}
2164+
}
2165+
}
2166+
}
2167+
20122168
function main() {
20132169
console.log("Processing TypeDoc MDX files for Mintlify...\n");
20142170

@@ -2038,6 +2194,9 @@ function main() {
20382194
// Clean up SortField signature specifically (before general signature cleanup)
20392195
cleanupSortFieldSignature(DOCS_DIR);
20402196

2197+
// Fix truncated intersection types in ParamField type attributes
2198+
cleanupTruncatedParamTypes(DOCS_DIR);
2199+
20412200
// Clean up signatures: fix truncated generics, simplify keyof constraints, break long lines
20422201
applySignatureCleanup(DOCS_DIR);
20432202

@@ -2055,6 +2214,9 @@ function main() {
20552214
// Group type definitions under a parent heading
20562215
applyTypeDefinitionGrouping(DOCS_DIR);
20572216

2217+
// Reorder methods according to method-order.json
2218+
applyMethodOrdering(DOCS_DIR);
2219+
20582220
// Link type names in Type Declarations sections to their corresponding headings
20592221
applyTypeDeclarationLinking(DOCS_DIR);
20602222

scripts/mintlify-post-processing/types-to-delete-after-processing.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
"DeleteManyResult",
33
"DeleteResult",
44
"ImportResult",
5-
"SortField"
5+
"SortField",
6+
"UpdateManyResult"
67
]

scripts/mintlify-post-processing/types-to-expose.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@
2222
"IntegrationsModule",
2323
"CoreIntegrations",
2424
"SortField",
25-
"SsoModule"
25+
"SsoModule",
26+
"UpdateManyResult"
2627
]

src/modules/entities.types.ts

Lines changed: 81 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export interface DeleteManyResult {
4545
}
4646

4747
/**
48-
* Result returned when updating multiple entities via a query.
48+
* Result returned when updating multiple entities using a query.
4949
*/
5050
export interface UpdateManyResult {
5151
/** Whether the operation was successful. */
@@ -307,6 +307,11 @@ export interface EntityHandler<T = any> {
307307
* Updates a record by ID with the provided data. Only the fields
308308
* included in the data object will be updated.
309309
*
310+
* To update a single record by ID, use this method. To apply the same
311+
* update to many records matching a query, use {@linkcode updateMany | updateMany()}.
312+
* To update multiple specific records with different data each, use
313+
* {@linkcode bulkUpdate | bulkUpdate()}.
314+
*
310315
* @param id - The unique identifier of the record to update.
311316
* @param data - Object containing the fields to update.
312317
* @returns Promise resolving to the updated record.
@@ -392,29 +397,39 @@ export interface EntityHandler<T = any> {
392397
bulkCreate(data: Partial<T>[]): Promise<T[]>;
393398

394399
/**
395-
* Updates multiple records matching a query using a MongoDB update operator.
400+
* Applies the same update to all records that match a query.
396401
*
397-
* Applies the same update operation to all records matching the query.
398-
* The `data` parameter must contain one or more MongoDB update operators
399-
* (e.g., `$set`, `$inc`, `$push`). Multiple operators can be combined in a
400-
* single call, but each field may only appear in one operator.
402+
* Use this when you need to make the same change across all records that
403+
* match specific criteria. For example, you could set every completed order
404+
* to "archived", or increment a counter on all active users.
401405
*
402-
* Results are batched in groups of up to 500 — when `has_more` is `true`
406+
* Results are batched in groups of up to 500. When `has_more` is `true`
403407
* in the response, call `updateMany` again with the same query to update
404-
* the next batch.
405-
*
406-
* @param query - Query object to filter which records to update. Records matching all
407-
* specified criteria will be updated.
408-
* @param data - Update operation object containing one or more MongoDB update operators.
408+
* the next batch. Make sure the query excludes already-updated records
409+
* so you don't re-process the same entities on each iteration. For
410+
* example, filter by `status: 'pending'` when setting status to `'processed'`.
411+
*
412+
* To update a single record by ID, use {@linkcode update | update()} instead. To update
413+
* multiple specific records with different data each, use {@linkcode bulkUpdate | bulkUpdate()}.
414+
*
415+
* @param query - Query object to filter which records to update. Use field-value
416+
* pairs for exact matches, or
417+
* [MongoDB query operators](https://www.mongodb.com/docs/manual/reference/operator/query/)
418+
* for advanced filtering. Supported query operators include `$eq`, `$ne`, `$gt`,
419+
* `$gte`, `$lt`, `$lte`, `$in`, `$nin`, `$and`, `$or`, `$not`, `$nor`,
420+
* `$exists`, `$regex`, `$all`, `$elemMatch`, and `$size`.
421+
* @param data - Update operation object containing one or more
422+
* [MongoDB update operators](https://www.mongodb.com/docs/manual/reference/operator/update/).
409423
* Each field may only appear in one operator per call.
410-
* Supported operators: `$set`, `$rename`, `$unset`, `$inc`, `$mul`, `$min`, `$max`,
411-
* `$currentDate`, `$addToSet`, `$push`, `$pull`.
424+
* Supported update operators include `$set`, `$rename`, `$unset`, `$inc`, `$mul`, `$min`, `$max`,
425+
* `$currentDate`, `$addToSet`, `$push`, and `$pull`.
412426
* @returns Promise resolving to the update result.
413427
*
414428
* @example
415429
* ```typescript
416-
* // Set status to 'archived' for all completed records
417-
* const result = await base44.entities.MyEntity.updateMany(
430+
* // Basic usage
431+
* // Archive all completed orders
432+
* const result = await base44.entities.Order.updateMany(
418433
* { status: 'completed' },
419434
* { $set: { status: 'archived' } }
420435
* );
@@ -423,20 +438,34 @@ export interface EntityHandler<T = any> {
423438
*
424439
* @example
425440
* ```typescript
426-
* // Combine multiple operators in a single call
427-
* const result = await base44.entities.MyEntity.updateMany(
441+
* // Multiple query operators
442+
* // Flag urgent items that haven't been handled yet
443+
* const result = await base44.entities.Task.updateMany(
444+
* { priority: { $in: ['high', 'critical'] }, status: { $ne: 'done' } },
445+
* { $set: { flagged: true } }
446+
* );
447+
* ```
448+
*
449+
* @example
450+
* ```typescript
451+
* // Multiple update operators
452+
* // Close out sales records and bump the view count
453+
* const result = await base44.entities.Deal.updateMany(
428454
* { category: 'sales' },
429455
* { $set: { status: 'done' }, $inc: { view_count: 1 } }
430456
* );
431457
* ```
432458
*
433459
* @example
434460
* ```typescript
435-
* // Handle batched updates for large datasets
461+
* // Batched updates
462+
* // Process all pending items in batches of 500.
463+
* // The query filters by 'pending', so updated records (now 'processed')
464+
* // are automatically excluded from the next batch.
436465
* let hasMore = true;
437466
* let totalUpdated = 0;
438467
* while (hasMore) {
439-
* const result = await base44.entities.MyEntity.updateMany(
468+
* const result = await base44.entities.Job.updateMany(
440469
* { status: 'pending' },
441470
* { $set: { status: 'processed' } }
442471
* );
@@ -445,30 +474,48 @@ export interface EntityHandler<T = any> {
445474
* }
446475
* ```
447476
*/
448-
updateMany(query: Partial<T>, data: Record<string, Record<string, any>>): Promise<UpdateManyResult>;
477+
updateMany(
478+
query: Partial<T>,
479+
data: Record<string, Record<string, any>>,
480+
): Promise<UpdateManyResult>;
449481

450482
/**
451-
* Updates multiple records in a single request, each with its own update data.
483+
* Updates the specified records in a single request, each with its own data.
484+
*
485+
* Use this when you already know which records to update and each one needs
486+
* different field values. For example, you could update the status and amount
487+
* on three separate invoices in one call.
452488
*
453-
* Unlike `updateMany` which applies the same update to all matching records,
454-
* `bulkUpdate` allows different updates for each record. Each item in the
455-
* array must include an `id` field identifying which record to update.
489+
* You can update up to 500 records per request.
456490
*
457-
* **Note:** Maximum 500 items per request.
491+
* To apply the same update to all records matching a query, use
492+
* {@linkcode updateMany | updateMany()}. To update a single record by ID, use
493+
* {@linkcode update | update()}.
458494
*
459-
* @param data - Array of update objects (max 500). Each object must have an `id` field
460-
* and any number of fields to update.
461-
* @returns Promise resolving to an array of updated records.
495+
* @param data - Array of objects to update. Each object must contain an `id` field identifying which record to update and any fields to change.
496+
* @returns Promise resolving to an array of the updated records.
462497
*
463498
* @example
464499
* ```typescript
465-
* // Update multiple records with different data
466-
* const updated = await base44.entities.MyEntity.bulkUpdate([
467-
* { id: 'entity-1', status: 'paid', amount: 999 },
468-
* { id: 'entity-2', status: 'cancelled' },
469-
* { id: 'entity-3', name: 'Renamed Item' }
500+
* // Basic usage
501+
* // Update three invoices with different statuses and amounts
502+
* const updated = await base44.entities.Invoice.bulkUpdate([
503+
* { id: 'inv-1', status: 'paid', amount: 999 },
504+
* { id: 'inv-2', status: 'cancelled' },
505+
* { id: 'inv-3', amount: 450 }
470506
* ]);
471507
* ```
508+
*
509+
* @example
510+
* ```typescript
511+
* // More than 500 items
512+
* // Reassign each task to a different owner in batches
513+
* const allUpdates = reassignments.map(r => ({ id: r.taskId, owner: r.newOwner }));
514+
* for (let i = 0; i < allUpdates.length; i += 500) {
515+
* const batch = allUpdates.slice(i, i + 500);
516+
* await base44.entities.Task.bulkUpdate(batch);
517+
* }
518+
* ```
472519
*/
473520
bulkUpdate(data: (Partial<T> & { id: string })[]): Promise<T[]>;
474521

0 commit comments

Comments
 (0)