Rewrite store JSDoc, split quote/order into modules, and generate typed API docs

- Rewrite JSDoc across all store methods to document the actual async
  methods (real params, returns, throws) instead of the internal wrappers
- Split quote/order into individual module files backed by shared
  manifestDoc helpers; remove the createStore factory and thin wrappers
- Deduplicate manifest line helpers into src/store/manifestLines.js
- Extend generate-api-doc.js to render parameter/return/throws types and
  emit per-namespace API.<module>.md files with an API.md index
- Regenerate all API docs
This commit is contained in:
seb
2026-08-07 06:23:17 +02:00
parent 396389457f
commit aefd97a171
127 changed files with 3956 additions and 1684 deletions

View File

@@ -58,13 +58,27 @@ const INTERNAL_FILES = new Set([
'peekNumber.js',
'shape.js',
'createStore.js',
'manifestLines.js',
'index.js',
]);
/**
* Strip the outer braces from a JSDoc type (e.g. `{number}` -> `number`),
* preserving nested braces inside generics.
* @param {string} type
* @returns {string}
*/
function stripBraces(type) {
if (type.startsWith('{') && type.endsWith('}')) {
return type.slice(1, -1);
}
return type;
}
/**
* Extract the leading JSDoc block from a source string.
* @param {string} source
* @returns {{ description: string, example: string } | null}
* @returns {{ description: string, example: string, params: Array<object>, returns: string|null, throws: string|null } | null}
*/
function extractJsDoc(source) {
const match = source.match(/\/\*\*([\s\S]*?)\*\//);
@@ -77,27 +91,53 @@ function extractJsDoc(source) {
const description = [];
const example = [];
const params = [];
let returns = null;
let throws = null;
let inExample = false;
for (const line of lines) {
if (line.trim() === '@example') {
const trimmed = line.trim();
if (trimmed === '@example') {
inExample = true;
continue;
}
if (line.trim().startsWith('@')) {
if (trimmed.startsWith('@')) {
inExample = false;
const paramMatch = trimmed.match(/^@param\s+(\{.*\})\s+(\S+)\s*[-–]?\s*(.*)$/);
if (paramMatch) {
params.push({
type: stripBraces(paramMatch[1]),
name: paramMatch[2],
description: paramMatch[3].trim(),
});
continue;
}
const returnsMatch = trimmed.match(/^@returns\s+(\{.*\})\s*[-–]?\s*(.*)$/);
if (returnsMatch) {
returns = { type: stripBraces(returnsMatch[1]), description: returnsMatch[2].trim() };
continue;
}
const throwsMatch = trimmed.match(/^@throws\s+(\{.*\})\s*[-–]?\s*(.*)$/);
if (throwsMatch) {
throws = { type: stripBraces(throwsMatch[1]), description: throwsMatch[2].trim() };
continue;
}
continue;
}
if (inExample) {
example.push(line);
} else if (line.trim()) {
description.push(line.trim());
} else if (trimmed) {
description.push(trimmed);
}
}
return {
description: description.join(' ').trim(),
example: example.join('\n').trim(),
params,
returns,
throws,
};
}
@@ -146,6 +186,9 @@ function buildModules() {
file: rel.join('/'),
description: doc.description,
example: doc.example,
params: doc.params,
returns: doc.returns,
throws: doc.throws,
});
}
@@ -170,7 +213,31 @@ function renderSection(moduleName, entries) {
lines.push(entry.description);
lines.push('');
}
if (entry.params && entry.params.length) {
lines.push('**Parameters**');
lines.push('');
lines.push('| Name | Type | Description |');
lines.push('| --- | --- | --- |');
for (const p of entry.params) {
lines.push(`| \`${p.name}\` | \`${p.type}\` | ${p.description} |`);
}
lines.push('');
}
if (entry.returns) {
lines.push('**Returns**');
lines.push('');
lines.push(`\`${entry.returns.type}\`${entry.returns.description ? ` — ${entry.returns.description}` : ''}`);
lines.push('');
}
if (entry.throws) {
lines.push('**Throws**');
lines.push('');
lines.push(`\`${entry.throws.type}\`${entry.throws.description ? ` — ${entry.throws.description}` : ''}`);
lines.push('');
}
if (entry.example) {
lines.push('**Example**');
lines.push('');
lines.push('```js');
lines.push(entry.example);
lines.push('```');