* Emit ServerRpc as an instance class (like SessionRpc but without sessionId).
(node: Record<string, unknown>, classes: string[])
| 1710 | * Emit ServerRpc as an instance class (like SessionRpc but without sessionId). |
| 1711 | */ |
| 1712 | function emitServerRpcClasses(node: Record<string, unknown>, classes: string[]): string[] { |
| 1713 | const result: string[] = []; |
| 1714 | |
| 1715 | // Find top-level groups (e.g. "models", "tools", "account") |
| 1716 | const groups = Object.entries(node).filter(([, v]) => typeof v === "object" && v !== null && !isRpcMethod(v)); |
| 1717 | // Find top-level methods (e.g. "ping") |
| 1718 | const topLevelMethods = Object.entries(node).filter(([, v]) => isRpcMethod(v)); |
| 1719 | |
| 1720 | // ServerRpc class |
| 1721 | const srLines: string[] = []; |
| 1722 | srLines.push(`/// <summary>Provides server-scoped RPC methods (no session required).</summary>`); |
| 1723 | srLines.push(`public sealed class ServerRpc`); |
| 1724 | srLines.push(`{`); |
| 1725 | srLines.push(` private readonly JsonRpc _rpc;`); |
| 1726 | srLines.push(""); |
| 1727 | srLines.push(` internal ServerRpc(JsonRpc rpc)`); |
| 1728 | srLines.push(` {`); |
| 1729 | srLines.push(` _rpc = rpc;`); |
| 1730 | srLines.push(` }`); |
| 1731 | |
| 1732 | // Top-level methods (like ping) |
| 1733 | for (const [key, value] of topLevelMethods) { |
| 1734 | if (!isRpcMethod(value)) continue; |
| 1735 | emitServerInstanceMethod(key, value, srLines, classes, " ", false, false); |
| 1736 | } |
| 1737 | |
| 1738 | // Group properties |
| 1739 | for (const [groupName] of groups) { |
| 1740 | const propertyName = toPascalCase(groupName); |
| 1741 | srLines.push(""); |
| 1742 | srLines.push(` /// <summary>${propertyName} APIs.</summary>`); |
| 1743 | srLines.push( |
| 1744 | ` public Server${propertyName}Api ${propertyName} =>`, |
| 1745 | ` field ??`, |
| 1746 | ` Interlocked.CompareExchange(ref field, new(_rpc), null) ??`, |
| 1747 | ` field;` |
| 1748 | ); |
| 1749 | } |
| 1750 | |
| 1751 | srLines.push(`}`); |
| 1752 | result.push(srLines.join("\n")); |
| 1753 | |
| 1754 | // Per-group API classes |
| 1755 | for (const [groupName, groupNode] of groups) { |
| 1756 | result.push(...emitServerApiClass(`Server${toPascalCase(groupName)}Api`, groupNode as Record<string, unknown>, classes)); |
| 1757 | } |
| 1758 | |
| 1759 | return result; |
| 1760 | } |
| 1761 | |
| 1762 | function emitServerApiClass(className: string, node: Record<string, unknown>, classes: string[]): string[] { |
| 1763 | const parts: string[] = []; |
no test coverage detected
searching dependent graphs…