@shoalrun/cli 0.1.0-alpha.19 → 1.0.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 (219) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/CODE-REFERENCES.md +137 -0
  3. package/README.md +19 -6
  4. package/adapters/python/requirements.txt +1 -0
  5. package/adapters/python/shoalrun_reference.py +234 -0
  6. package/agent-docs/.well-known/shoalrun/navigation/project-updates-50f26fe22670.txt +7 -3
  7. package/agent-docs/SNAPSHOT.json +5 -5
  8. package/agent-docs/about.md +3 -3
  9. package/agent-docs/api-docs/code-reference-settings.md +160 -0
  10. package/agent-docs/api-docs/code-reference-settings.sections/catalog-settings.md +50 -0
  11. package/agent-docs/api-docs/code-reference-settings.sections/complete-reference-page.md +46 -0
  12. package/agent-docs/api-docs/code-reference-settings.sections/converter-snapshot.md +58 -0
  13. package/agent-docs/api-docs/code-reference-settings.sections/descriptions-type-links-and-source-links.md +40 -0
  14. package/agent-docs/api-docs/code-reference-settings.sections/html-markdown-and-agents.md +38 -0
  15. package/agent-docs/api-docs/code-reference-settings.sections/metadata-and-selection.md +38 -0
  16. package/agent-docs/api-docs/code-reference-settings.sections/publication-sources.md +54 -0
  17. package/agent-docs/api-docs/code-reference-settings.sections/python-adapter-limits.md +38 -0
  18. package/agent-docs/api-docs/code-reference-settings.sections/related-reading.md +32 -0
  19. package/agent-docs/api-docs/code-reference-settings.sections/symbol-fields.md +56 -0
  20. package/agent-docs/api-docs/code-references.md +143 -0
  21. package/agent-docs/author/agent-overview.md +2 -2
  22. package/agent-docs/author/components/code-blocks.md +46 -1
  23. package/agent-docs/author/frontmatter-reference.md +1 -0
  24. package/agent-docs/author/frontmatter-reference.sections/categorize-connect-and-present-the-page.md +1 -0
  25. package/agent-docs/author/workspace.md +2 -2
  26. package/agent-docs/author/writing-style.md +9 -3
  27. package/agent-docs/author/writing-style.sections/adapt-this-example.md +1 -1
  28. package/agent-docs/author/writing-style.sections/choose-components-by-reader-need.md +2 -2
  29. package/agent-docs/author/writing-style.sections/evidence-and-uncertainty.md +1 -1
  30. package/agent-docs/author/writing-style.sections/example-audience-and-scope.md +1 -1
  31. package/agent-docs/author/writing-style.sections/feature-availability.md +1 -1
  32. package/agent-docs/author/writing-style.sections/ingredients-recipes-and-cookbooks.md +1 -1
  33. package/agent-docs/author/writing-style.sections/page-purpose-and-structure.md +2 -2
  34. package/agent-docs/author/writing-style.sections/procedures-and-examples.md +1 -1
  35. package/agent-docs/author/writing-style.sections/related-reading.md +1 -1
  36. package/agent-docs/author/writing-style.sections/release-notes.md +30 -0
  37. package/agent-docs/author/writing-style.sections/review-a-draft.md +1 -1
  38. package/agent-docs/author/writing-style.sections/reviewed-writing-examples.md +1 -1
  39. package/agent-docs/author/writing-style.sections/voice-and-explanations.md +1 -1
  40. package/agent-docs/changelog/2026-10-16-navigation-layouts.md +1 -1
  41. package/agent-docs/changelog/code-api-references.md +44 -0
  42. package/agent-docs/changelog/pdf-custom-languages.md +48 -0
  43. package/agent-docs/changelog.md +17 -1
  44. package/agent-docs/cli.md +11 -1
  45. package/agent-docs/cli.sections/audit-a-project-and-api-sources.md +1 -1
  46. package/agent-docs/cli.sections/author-pages.md +1 -1
  47. package/agent-docs/cli.sections/build-and-serve.md +1 -1
  48. package/agent-docs/cli.sections/inspect-config-and-operations.md +1 -1
  49. package/agent-docs/cli.sections/npm-scripts-in-this-repository.md +1 -1
  50. package/agent-docs/cli.sections/related-reading.md +1 -1
  51. package/agent-docs/cli.sections/search-and-chat.md +1 -1
  52. package/agent-docs/cli.sections/start-a-project.md +1 -1
  53. package/agent-docs/cli.sections/validate-code-reference-snapshots.md +41 -0
  54. package/agent-docs/cli.sections/working-tree-authoring-tools.md +1 -1
  55. package/agent-docs/config/apis.md +5 -1
  56. package/agent-docs/config/examples.md +3 -3
  57. package/agent-docs/config/examples.sections/feature-showcase.md +3 -3
  58. package/agent-docs/config/examples.sections/minimal-site.md +2 -2
  59. package/agent-docs/config/examples.sections/multilingual-help-center.md +2 -2
  60. package/agent-docs/config/examples.sections/parcel-api-documentation.md +2 -2
  61. package/agent-docs/config/examples.sections/related-reading.md +2 -2
  62. package/agent-docs/config/examples.sections/shared-company-preset.md +2 -2
  63. package/agent-docs/config/outputs.md +33 -12
  64. package/agent-docs/config/outputs.sections/exports.md +3 -3
  65. package/agent-docs/config/outputs.sections/metadata.md +3 -3
  66. package/agent-docs/config/outputs.sections/pdf-output.md +51 -0
  67. package/agent-docs/config/outputs.sections/related-reading.md +3 -3
  68. package/agent-docs/config.md +2 -2
  69. package/agent-docs/configuration.md +2 -2
  70. package/agent-docs/configuration.sections/build-several-sites-from-shared-content.md +1 -1
  71. package/agent-docs/configuration.sections/change-inherited-lists-deliberately.md +1 -1
  72. package/agent-docs/configuration.sections/extract-a-section-with-include.md +1 -1
  73. package/agent-docs/configuration.sections/inspect-and-validate-changes.md +1 -1
  74. package/agent-docs/configuration.sections/related-reading.md +1 -1
  75. package/agent-docs/configuration.sections/separate-config-inheritance-from-page-metadata.md +1 -1
  76. package/agent-docs/configuration.sections/share-defaults-through-extends.md +1 -1
  77. package/agent-docs/configuration.sections/start-with-one-working-file.md +2 -2
  78. package/agent-docs/configuration.sections/understand-the-three-stages.md +1 -1
  79. package/agent-docs/configuration.sections/understand-where-paths-point.md +1 -1
  80. package/agent-docs/contribute/agent-skills.md +3 -1
  81. package/agent-docs/documents.json +1009 -356
  82. package/agent-docs/features.md +6 -2
  83. package/agent-docs/index.md +2 -2
  84. package/agent-docs/provenance.json +6 -6
  85. package/agent-docs/publish/api-references.md +3 -1
  86. package/agent-docs/publish/api-references.sections/author-the-reference-page.md +3 -1
  87. package/agent-docs/publish/api-references.sections/build-and-verify-the-output.md +3 -1
  88. package/agent-docs/publish/api-references.sections/inspect-the-machine-readable-contract.md +3 -1
  89. package/agent-docs/publish/api-references.sections/next-steps.md +3 -1
  90. package/agent-docs/publish/api-references.sections/preview-a-small-api-reference.md +3 -1
  91. package/agent-docs/publish/api-references.sections/put-the-spec-in-your-project.md +3 -1
  92. package/agent-docs/publish/api-references.sections/register-the-operation.md +3 -1
  93. package/agent-docs/publish/api-references.sections/related-reading.md +3 -1
  94. package/agent-docs/publish/api-references.sections/review-imported-content-before-publishing.md +3 -1
  95. package/agent-docs/publish/api-references.sections/what-readers-get.md +3 -1
  96. package/agent-docs/publish/content-export.md +2 -0
  97. package/agent-docs/publish/content-export.sections/assets-downloads-and-notices.md +2 -0
  98. package/agent-docs/publish/content-export.sections/check-the-result.md +2 -0
  99. package/agent-docs/publish/content-export.sections/inspect-the-manifest.md +2 -0
  100. package/agent-docs/publish/content-export.sections/prerequisites.md +2 -0
  101. package/agent-docs/publish/content-export.sections/related-reading.md +2 -0
  102. package/agent-docs/publish/content-export.sections/review-exported-api-material.md +2 -0
  103. package/agent-docs/publish/content-export.sections/try-the-help-drawer-example.md +2 -0
  104. package/agent-docs/publish/content-export.sections/what-the-representations-retain.md +2 -0
  105. package/agent-docs/publish/pdf.md +184 -0
  106. package/agent-docs/publish/pdf.sections/apply-print-styling.md +65 -0
  107. package/agent-docs/publish/pdf.sections/check-the-result.md +33 -0
  108. package/agent-docs/publish/pdf.sections/export-a-page-or-publication.md +61 -0
  109. package/agent-docs/publish/pdf.sections/html-markdown-and-agents.md +31 -0
  110. package/agent-docs/publish/pdf.sections/offer-browser-page-printing.md +49 -0
  111. package/agent-docs/publish/pdf.sections/offer-direct-downloads-through-a-node-server.md +46 -0
  112. package/agent-docs/publish/pdf.sections/prerequisites.md +37 -0
  113. package/agent-docs/publish/pdf.sections/publish-an-optional-manual-download.md +39 -0
  114. package/agent-docs/publish.md +2 -0
  115. package/agent-docs/reference/agent-skills.md +7 -3
  116. package/agent-docs/search-index.json +1 -1
  117. package/agent-docs/start.md +4 -4
  118. package/agent-docs/start.sections/add-the-page-to-navigation.md +2 -2
  119. package/agent-docs/start.sections/build-and-check-both-outputs.md +2 -2
  120. package/agent-docs/start.sections/build-this-site.md +2 -2
  121. package/agent-docs/start.sections/check-writing-style.md +2 -2
  122. package/agent-docs/start.sections/create-a-page.md +2 -2
  123. package/agent-docs/start.sections/make-it-yours.md +2 -2
  124. package/agent-docs/start.sections/migrating-from-github-packages.md +2 -2
  125. package/agent-docs/start.sections/prerequisites.md +2 -2
  126. package/agent-docs/start.sections/related-reading.md +2 -2
  127. package/agent-docs/start.sections/start-your-own-project.md +4 -4
  128. package/agent-docs/status.md +9 -5
  129. package/agent-docs/styling/themes.md +7 -3
  130. package/agent-docs/what-is-shoalrun.md +2 -2
  131. package/assets/copy-markdown.js +1 -1
  132. package/assets/print-view.js +165 -0
  133. package/assets/print.js +63 -0
  134. package/dist/artifact-bundle.d.ts +2 -0
  135. package/dist/artifact-bundle.js +13 -3
  136. package/dist/authoring-snapshot.js +1 -1
  137. package/dist/browser-print-assets.d.ts +9 -0
  138. package/dist/browser-print-assets.js +45 -0
  139. package/dist/cli-discovery.d.ts +1 -1
  140. package/dist/cli-discovery.js +1 -0
  141. package/dist/cli.js +7 -0
  142. package/dist/code-reference-model.d.ts +85 -0
  143. package/dist/code-reference-model.js +73 -0
  144. package/dist/code-references.d.ts +23 -0
  145. package/dist/code-references.js +371 -0
  146. package/dist/compiler-config.d.ts +1 -0
  147. package/dist/compiler.d.ts +4 -2
  148. package/dist/compiler.js +33 -7
  149. package/dist/config.js +1 -1
  150. package/dist/content-export-command.d.ts +77 -1
  151. package/dist/content-export-command.js +5 -2
  152. package/dist/content-export-config.d.ts +4 -1
  153. package/dist/content-export-config.js +10 -5
  154. package/dist/content-export-render.d.ts +9 -3
  155. package/dist/content-export-render.js +11 -7
  156. package/dist/content-export.d.ts +126 -0
  157. package/dist/content-export.js +267 -124
  158. package/dist/content-source.d.ts +2 -0
  159. package/dist/content-workspace.d.ts +1 -0
  160. package/dist/content-workspace.js +2 -0
  161. package/dist/fonts.d.ts +9 -0
  162. package/dist/fonts.js +58 -3
  163. package/dist/frontmatter.js +5 -2
  164. package/dist/highlight-languages.d.ts +9 -0
  165. package/dist/highlight-languages.js +52 -0
  166. package/dist/highlight.d.ts +4 -1
  167. package/dist/highlight.js +20 -5
  168. package/dist/output-commit.d.ts +1 -1
  169. package/dist/output-commit.js +4 -2
  170. package/dist/page-materialization.js +3 -2
  171. package/dist/page-renderer.d.ts +1 -0
  172. package/dist/page-renderer.js +8 -1
  173. package/dist/pdf-button.d.ts +22 -0
  174. package/dist/pdf-button.js +55 -0
  175. package/dist/pdf-config.d.ts +28 -0
  176. package/dist/pdf-config.js +43 -0
  177. package/dist/pdf-http.d.ts +36 -0
  178. package/dist/pdf-http.js +132 -0
  179. package/dist/pdf-project.d.ts +6 -0
  180. package/dist/pdf-project.js +31 -0
  181. package/dist/pdf-renderer.d.ts +45 -0
  182. package/dist/pdf-renderer.js +208 -0
  183. package/dist/pdf-site.d.ts +37 -0
  184. package/dist/pdf-site.js +65 -0
  185. package/dist/pdf-stylesheet.d.ts +1 -0
  186. package/dist/pdf-stylesheet.js +57 -0
  187. package/dist/publication-render.d.ts +2 -1
  188. package/dist/publication-render.js +2 -2
  189. package/dist/representation-tree.js +5 -1
  190. package/dist/server.d.ts +2 -0
  191. package/dist/server.js +35 -8
  192. package/dist/vercel.js +6 -0
  193. package/examples/code-references/content/start.md +53 -0
  194. package/examples/code-references/docs.config.yaml +262 -0
  195. package/examples/code-references/python/orchard/__init__.py +11 -0
  196. package/examples/code-references/python/orchard/client.py +212 -0
  197. package/examples/code-references/python/orchard/errors.py +31 -0
  198. package/examples/code-references/python/orchard/models.py +69 -0
  199. package/examples/code-references/python/orchard/pagination.py +39 -0
  200. package/examples/code-references/reference.json +1244 -0
  201. package/examples/custom-language/README.md +32 -0
  202. package/examples/custom-language/content/start.mdx +28 -0
  203. package/examples/custom-language/docs.config.yaml +18 -0
  204. package/examples/custom-language/grammars/custompy.tmLanguage.json +7 -0
  205. package/examples/pdf/README.md +27 -0
  206. package/examples/pdf/content/operations.mdx +26 -0
  207. package/examples/pdf/content/start.mdx +28 -0
  208. package/examples/pdf/docs.config.yaml +29 -0
  209. package/examples/pdf/print.css +9 -0
  210. package/package.json +11 -2
  211. package/schemas/code-reference.schema.json +306 -0
  212. package/skills/shoalrun-author/SKILL.md +6 -0
  213. package/skills/shoalrun-author/references/code-api-references.md +15 -0
  214. package/skills/shoalrun-author/references/pdf-export.md +15 -0
  215. package/skills/shoalrun-brand/SKILL.md +2 -0
  216. package/skills/shoalrun-pdf/SKILL.md +49 -0
  217. package/skills/shoalrun-pdf/assets/print.css +49 -0
  218. package/skills/shoalrun-pdf/references/print-design.md +75 -0
  219. package/styles/site.css +64 -3
@@ -53,7 +53,7 @@ export function publicTree(tree, representation, documentRoutes, mode = 'light',
53
53
  if (node.type === 'docReaderSection') {
54
54
  const children = node.children.flatMap(walk);
55
55
  return representation === 'markdown' ? [paragraph({ type: 'strong', children: [text(`For ${node.description}.`)] }), ...children]
56
- : [element('section', { className: ['reader-section'], 'data-reader-filters': JSON.stringify(node.selection) }, children)];
56
+ : [element('section', { className: ['reader-section'], 'data-reader-filters': JSON.stringify(node.selection), 'data-reader-print-label': `For ${node.description}.` }, children)];
57
57
  }
58
58
  if (node.type === 'docReaderChoice') {
59
59
  if (representation === 'markdown') {
@@ -66,6 +66,10 @@ export function publicTree(tree, representation, documentRoutes, mode = 'light',
66
66
  if (node.type === 'docReaderFilter') {
67
67
  return representation === 'markdown' ? [] : [element('div', { className: ['reader-filter-slot'], 'data-reader-filter': JSON.stringify(node.control) }, [])];
68
68
  }
69
+ if (node.type === 'table' && node.data?.referenceTableLabel && representation === 'html') {
70
+ const table = { ...node, children: node.children.flatMap(walk) };
71
+ return [element('div', { className: ['code-reference-table-scroll'], role: 'region', 'aria-label': node.data.referenceTableLabel, tabIndex: 0 }, [table])];
72
+ }
69
73
  if (node.type === 'docTable') {
70
74
  const table = node.children.flatMap(walk);
71
75
  if (representation === 'markdown')
package/dist/server.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { type Server, type ServerResponse } from 'node:http';
2
+ export declare function canonicalSitePath(url: string): string;
3
+ export declare function privateSitePath(pathname: string): boolean;
2
4
  export type PreviewStatus = {
3
5
  profile?: string;
4
6
  profileDigest?: string;
package/dist/server.js CHANGED
@@ -1,11 +1,22 @@
1
1
  import { loadMcpRuntime, createMcpHttpHandler } from './mcp-http.js';
2
2
  import { bridgeMcp } from './mcp-node.js';
3
+ import { loadPdfRuntime, createPdfHttpHandler } from './pdf-http.js';
3
4
  import { notFoundPage, addNotFoundTwin } from './not-found.js';
4
5
  import { createServer } from 'node:http';
5
6
  import { readFile, realpath, stat } from 'node:fs/promises';
6
7
  import path from 'node:path';
7
8
  import { homeUrl, negotiatedDocument } from './negotiation.js';
8
- const types = { '.html': 'text/html; charset=utf-8', '.md': 'text/markdown; charset=utf-8', '.xml': 'application/xml; charset=utf-8', '.json': 'application/json', '.yaml': 'application/yaml', '.yml': 'application/yaml', '.css': 'text/css; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.txt': 'text/plain; charset=utf-8', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.svg': 'image/svg+xml', '.woff2': 'font/woff2', '.woff': 'font/woff', '.ttf': 'font/ttf', '.otf': 'font/otf' };
9
+ export function canonicalSitePath(url) {
10
+ const decoded = decodeURIComponent(new URL(url, 'http://localhost').pathname).replace(/\\/g, '/');
11
+ if (decoded.includes('\0') || decoded.split('/').some(segment => segment === '..' || segment === '.'))
12
+ throw new Error('Invalid path');
13
+ return decoded.replace(/\/{2,}/g, '/');
14
+ }
15
+ export function privateSitePath(pathname) {
16
+ const internal = pathname.toLowerCase().replace(/\/+$/, '');
17
+ return ['/mcp-runtime.json', '/pdf-runtime.json', '/__shoalrun/mcp-data', '/__shoalrun/pdf-data'].some(privatePath => internal === privatePath || internal.startsWith(privatePath + '/'));
18
+ }
19
+ const types = { '.pdf': 'application/pdf', '.html': 'text/html; charset=utf-8', '.md': 'text/markdown; charset=utf-8', '.xml': 'application/xml; charset=utf-8', '.json': 'application/json', '.yaml': 'application/yaml', '.yml': 'application/yaml', '.css': 'text/css; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.txt': 'text/plain; charset=utf-8', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.svg': 'image/svg+xml', '.woff2': 'font/woff2', '.woff': 'font/woff', '.ttf': 'font/ttf', '.otf': 'font/otf' };
9
20
  const escapeHtml = (value) => value.replace(/[&<>"']/g, character => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[character]));
10
21
  const scriptJson = (value) => JSON.stringify(value).replace(/</g, '\\u003c');
11
22
  export function previewMarkup(basePath, preview) {
@@ -58,6 +69,9 @@ export async function createSiteServer(directory, liveClients, extras = {}) {
58
69
  let mcp = await loadMcpRuntime(root);
59
70
  let mcpHandler = mcp ? createMcpHttpHandler(mcp) : undefined;
60
71
  let mcpBuild = mcp?.runtime.buildId;
72
+ let pdf = await loadPdfRuntime(root);
73
+ let pdfHandler = pdf ? createPdfHttpHandler(pdf) : undefined;
74
+ let pdfBuild = initialState.manifest.buildId;
61
75
  async function siteFile(route) {
62
76
  const file = path.resolve(root, `.${route}`);
63
77
  if (file !== root && !file.startsWith(`${root}${path.sep}`))
@@ -73,9 +87,7 @@ export async function createSiteServer(directory, liveClients, extras = {}) {
73
87
  let pathname = '';
74
88
  let recoveryState = initialState;
75
89
  try {
76
- pathname = decodeURIComponent(new URL(request.url ?? '/', 'http://localhost').pathname);
77
- if (pathname.includes('\0') || pathname.split('/').includes('..'))
78
- throw new Error('Invalid path');
90
+ pathname = canonicalSitePath(request.url ?? '/');
79
91
  const { headers, manifest, redirects, search, errorHtml, home, routes } = liveClients ? await loadState() : initialState;
80
92
  recoveryState = { headers, manifest, redirects, search, errorHtml, home, routes };
81
93
  const basePath = manifest.basePath ?? '';
@@ -85,12 +97,28 @@ export async function createSiteServer(directory, liveClients, extras = {}) {
85
97
  mcpHandler = next ? createMcpHttpHandler(next) : undefined;
86
98
  mcpBuild = manifest.buildId;
87
99
  }
88
- const internalPath = pathname.toLowerCase().replace(/\/+$/, '');
89
- if (internalPath === '/mcp-runtime.json' || internalPath === '/__shoalrun/mcp-data' || internalPath.startsWith('/__shoalrun/mcp-data/')) {
100
+ if (privateSitePath(pathname)) {
90
101
  response.writeHead(404);
91
102
  response.end();
92
103
  return;
93
104
  }
105
+ if (liveClients && manifest.buildId !== pdfBuild) {
106
+ pdf = await loadPdfRuntime(root);
107
+ pdfHandler = pdf ? createPdfHttpHandler(pdf) : undefined;
108
+ pdfBuild = manifest.buildId;
109
+ }
110
+ if (pdf && pdfHandler && pathname === pdf.runtime.endpoint) {
111
+ try {
112
+ await bridge(request, response, pdfHandler);
113
+ }
114
+ catch {
115
+ if (!response.headersSent) {
116
+ response.writeHead(503, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' });
117
+ response.end('{"error":"PDF generation is unavailable."}');
118
+ }
119
+ }
120
+ return;
121
+ }
94
122
  if (mcp && mcpHandler && pathname === mcp.runtime.path) {
95
123
  await bridgeMcp(request, response, mcpHandler);
96
124
  return;
@@ -201,8 +229,7 @@ export async function createSiteServer(directory, liveClients, extras = {}) {
201
229
  mcpHandler = next ? createMcpHttpHandler(next) : undefined;
202
230
  mcpBuild = manifest.buildId;
203
231
  }
204
- const internalPath = pathname.toLowerCase().replace(/\/+$/, '');
205
- if (internalPath === '/mcp-runtime.json' || internalPath === '/__shoalrun/mcp-data' || internalPath.startsWith('/__shoalrun/mcp-data/')) {
232
+ if (privateSitePath(pathname)) {
206
233
  response.writeHead(404);
207
234
  response.end();
208
235
  return;
package/dist/vercel.js CHANGED
@@ -5,6 +5,9 @@ import path from 'node:path';
5
5
  import { compile } from './compiler.js';
6
6
  import { homeUrl } from './negotiation.js';
7
7
  import { addAskFunction } from './vercel-function.js';
8
+ import { loadConfig } from './config.js';
9
+ import { resolvedCompilerConfig } from './compiler-config.js';
10
+ import { resolvePdfButton } from './pdf-button.js';
8
11
  function exact(route) {
9
12
  return route.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
10
13
  }
@@ -28,6 +31,9 @@ async function removeStaleFunctions(directory, keep = new Set()) {
28
31
  await visit(directory);
29
32
  }
30
33
  export async function buildVercel(configFile, outputDirectory, provenance = {}, options = {}) {
34
+ const settings = resolvedCompilerConfig((await loadConfig(configFile, { profile: options.profile })).value);
35
+ if (resolvePdfButton(settings.outputs)?.mode === 'endpoint')
36
+ throw new Error('On-demand PDF generation requires a Node server with an installed Chromium browser. Use outputs.html.pdf.mode: print for Vercel builds.');
31
37
  const output = path.resolve(outputDirectory);
32
38
  const staticDirectory = path.join(output, 'static');
33
39
  if (options.profile && options.chat)
@@ -0,0 +1,53 @@
1
+ ---
2
+ id: guide.start
3
+ title: Python SDK
4
+ summary: Explore a typed SDK with synchronous and async clients, models, pagination, and exceptions.
5
+ type: guide
6
+ slug: /
7
+ channel: production
8
+ version: current
9
+ language: en
10
+ access: { policy: public }
11
+ ---
12
+ This example SDK manages records. Its reference pages are generated from Python source and docstrings, then rendered with Shoalrun's normal theme, navigation, search, and Markdown output.
13
+
14
+ The sample client stores records in memory. It doesn't connect to a live service.
15
+
16
+ ## Fetch a typed record
17
+
18
+ ```python
19
+ from orchard import Client
20
+
21
+ client = Client("demo-key")
22
+ record = client.fetch("rec_42")
23
+ print(record.title)
24
+ ```
25
+
26
+ Read [Client](doc:reference.client) for configuration and [fetch](doc:reference.fetch) for its typed and raw-response overloads.
27
+
28
+ ## Work with more than one page
29
+
30
+ ```python
31
+ from orchard import RecordStatus
32
+
33
+ for record in client.iter_records(page_size=50, status=RecordStatus.READY):
34
+ print(record.id, record.title)
35
+ ```
36
+
37
+ Choose [list_records](doc:reference.Client.list_records) when your application needs explicit cursor control, or [Page.map](doc:reference.pagination.Page.map) to transform a response without losing its pagination metadata.
38
+
39
+ ## Use an async client
40
+
41
+ ```python
42
+ from orchard import AsyncClient
43
+
44
+ client = AsyncClient("demo-key")
45
+ async for record in client.iter_records(page_size=50):
46
+ print(record.title)
47
+ ```
48
+
49
+ Browse [AsyncClient](doc:reference.AsyncClient), [Record](doc:reference.models.Record), and the [typed exceptions](doc:reference.errors) to see how the generated reference fits together.
50
+
51
+ ## Choose how to browse
52
+
53
+ Read the [Client class with member details inline](doc:reference.client), or open the [complete Python SDK reference](doc:reference.complete) to browse everything on one page. The example uses `layout: classes` and enables `singlePage` in its catalog configuration. Individual symbol links remain available.
@@ -0,0 +1,262 @@
1
+ schemaVersion: 1
2
+ site:
3
+ title: Python SDK documentation
4
+ publications:
5
+ channels:
6
+ production: {}
7
+ versions:
8
+ current: {}
9
+ defaults:
10
+ channel: production
11
+ version: current
12
+ language: en
13
+ enabled:
14
+ - channel: production
15
+ version: current
16
+ language: en
17
+ codeReferences:
18
+ sdk:
19
+ source:
20
+ kind: file
21
+ path: ./reference.json
22
+ symbols:
23
+ orchard:
24
+ documentId: reference.orchard
25
+ slug: /reference/orchard
26
+ orchard.client:
27
+ documentId: reference.clients
28
+ slug: /reference/clients
29
+ orchard.client.AsyncClient:
30
+ documentId: reference.AsyncClient
31
+ slug: /reference/async-client
32
+ orchard.client.AsyncClient.__init__:
33
+ documentId: reference.AsyncClient.__init__
34
+ slug: /reference/async-client/constructor
35
+ orchard.client.AsyncClient.fetch:
36
+ documentId: reference.AsyncClient.fetch
37
+ slug: /reference/async-client/fetch
38
+ orchard.client.AsyncClient.iter_records:
39
+ documentId: reference.AsyncClient.iter_records
40
+ slug: /reference/async-client/iter-records
41
+ orchard.client.Client:
42
+ documentId: reference.client
43
+ slug: /reference/client
44
+ orchard.client.Client.__init__:
45
+ documentId: reference.Client.__init__
46
+ slug: /reference/client/constructor
47
+ orchard.client.Client.base_url:
48
+ documentId: reference.Client.base_url
49
+ slug: /reference/client/base-url
50
+ orchard.client.Client.bulk_create:
51
+ documentId: reference.Client.bulk_create
52
+ slug: /reference/client/bulk-create
53
+ orchard.client.Client.create_record:
54
+ documentId: reference.Client.create_record
55
+ slug: /reference/client/create-record
56
+ orchard.client.Client.fetch:
57
+ documentId: reference.fetch
58
+ slug: /reference/client/fetch
59
+ orchard.client.Client.iter_records:
60
+ documentId: reference.Client.iter_records
61
+ slug: /reference/client/iter-records
62
+ orchard.client.Client.list_records:
63
+ documentId: reference.Client.list_records
64
+ slug: /reference/client/list-records
65
+ orchard.client.Client.max_retries:
66
+ documentId: reference.Client.max_retries
67
+ slug: /reference/client/max-retries
68
+ orchard.client.Client.timeout:
69
+ documentId: reference.Client.timeout
70
+ slug: /reference/client/timeout
71
+ orchard.errors:
72
+ documentId: reference.errors
73
+ slug: /reference/errors
74
+ orchard.errors.APIError:
75
+ documentId: reference.errors.APIError
76
+ slug: /reference/errors/apierror
77
+ orchard.errors.APIError.__init__:
78
+ documentId: reference.errors.APIError.__init__
79
+ slug: /reference/errors/apierror/constructor
80
+ orchard.errors.APIError.request_id:
81
+ documentId: reference.errors.APIError.request_id
82
+ slug: /reference/errors/apierror/request-id
83
+ orchard.errors.AuthenticationError:
84
+ documentId: reference.errors.AuthenticationError
85
+ slug: /reference/errors/authenticationerror
86
+ orchard.errors.NotFoundError:
87
+ documentId: reference.errors.NotFoundError
88
+ slug: /reference/errors/notfounderror
89
+ orchard.errors.RateLimitError:
90
+ documentId: reference.errors.RateLimitError
91
+ slug: /reference/errors/ratelimiterror
92
+ orchard.errors.RateLimitError.__init__:
93
+ documentId: reference.errors.RateLimitError.__init__
94
+ slug: /reference/errors/ratelimiterror/constructor
95
+ orchard.errors.RateLimitError.retry_after:
96
+ documentId: reference.errors.RateLimitError.retry_after
97
+ slug: /reference/errors/ratelimiterror/retry-after
98
+ orchard.models:
99
+ documentId: reference.models
100
+ slug: /reference/models
101
+ orchard.models.Record:
102
+ documentId: reference.models.Record
103
+ slug: /reference/models/record
104
+ orchard.models.Record.created_at:
105
+ documentId: reference.models.Record.created_at
106
+ slug: /reference/models/record/created-at
107
+ orchard.models.Record.id:
108
+ documentId: reference.models.Record.id
109
+ slug: /reference/models/record/id
110
+ orchard.models.Record.labels:
111
+ documentId: reference.models.Record.labels
112
+ slug: /reference/models/record/labels
113
+ orchard.models.Record.status:
114
+ documentId: reference.models.Record.status
115
+ slug: /reference/models/record/status
116
+ orchard.models.Record.title:
117
+ documentId: reference.models.Record.title
118
+ slug: /reference/models/record/title
119
+ orchard.models.Record.to_dict:
120
+ documentId: reference.models.Record.to_dict
121
+ slug: /reference/models/record/to-dict
122
+ orchard.models.RecordDict:
123
+ documentId: reference.models.RecordDict
124
+ slug: /reference/models/recorddict
125
+ orchard.models.RecordDict.id:
126
+ documentId: reference.models.RecordDict.id
127
+ slug: /reference/models/recorddict/id
128
+ orchard.models.RecordDict.status:
129
+ documentId: reference.models.RecordDict.status
130
+ slug: /reference/models/recorddict/status
131
+ orchard.models.RecordDict.title:
132
+ documentId: reference.models.RecordDict.title
133
+ slug: /reference/models/recorddict/title
134
+ orchard.models.RecordInput:
135
+ documentId: reference.models.RecordInput
136
+ slug: /reference/models/recordinput
137
+ orchard.models.RecordInput.labels:
138
+ documentId: reference.models.RecordInput.labels
139
+ slug: /reference/models/recordinput/labels
140
+ orchard.models.RecordInput.title:
141
+ documentId: reference.models.RecordInput.title
142
+ slug: /reference/models/recordinput/title
143
+ orchard.models.RecordStatus:
144
+ documentId: reference.models.RecordStatus
145
+ slug: /reference/models/recordstatus
146
+ orchard.models.RecordStatus.FAILED:
147
+ documentId: reference.models.RecordStatus.FAILED
148
+ slug: /reference/models/recordstatus/failed
149
+ orchard.models.RecordStatus.PENDING:
150
+ documentId: reference.models.RecordStatus.PENDING
151
+ slug: /reference/models/recordstatus/pending
152
+ orchard.models.RecordStatus.READY:
153
+ documentId: reference.models.RecordStatus.READY
154
+ slug: /reference/models/recordstatus/ready
155
+ orchard.pagination:
156
+ documentId: reference.pagination
157
+ slug: /reference/pagination
158
+ orchard.pagination.Page:
159
+ documentId: reference.pagination.Page
160
+ slug: /reference/pagination/page
161
+ orchard.pagination.Page.has_more:
162
+ documentId: reference.pagination.Page.has_more
163
+ slug: /reference/pagination/page/has-more
164
+ orchard.pagination.Page.items:
165
+ documentId: reference.pagination.Page.items
166
+ slug: /reference/pagination/page/items
167
+ orchard.pagination.Page.map:
168
+ documentId: reference.pagination.Page.map
169
+ slug: /reference/pagination/page/map
170
+ orchard.pagination.Page.next_cursor:
171
+ documentId: reference.pagination.Page.next_cursor
172
+ slug: /reference/pagination/page/next-cursor
173
+ orchard.pagination.Page.total:
174
+ documentId: reference.pagination.Page.total
175
+ slug: /reference/pagination/page/total
176
+ orchard.pagination.T:
177
+ documentId: reference.pagination.T
178
+ slug: /reference/pagination/t
179
+ orchard.pagination.U:
180
+ documentId: reference.pagination.U
181
+ slug: /reference/pagination/u
182
+ layout: classes
183
+ singlePage:
184
+ documentId: reference.complete
185
+ slug: /reference/all
186
+ title: Complete Python SDK reference
187
+ summary: Browse every public module, class, method, and field on one page.
188
+ navigation:
189
+ - id: start
190
+ doc: guide.start
191
+ - id: complete
192
+ doc: reference.complete
193
+ - id: overview
194
+ label: Package
195
+ items:
196
+ - id: orchard
197
+ doc: reference.orchard
198
+ - id: orchard.client
199
+ doc: reference.clients
200
+ - id: client
201
+ label: Synchronous client
202
+ items:
203
+ - id: orchard.client.Client
204
+ doc: reference.client
205
+ - id: orchard.client.Client.__init__
206
+ doc: reference.Client.__init__
207
+ - id: orchard.client.Client.fetch
208
+ doc: reference.fetch
209
+ - id: orchard.client.Client.list_records
210
+ doc: reference.Client.list_records
211
+ - id: orchard.client.Client.iter_records
212
+ doc: reference.Client.iter_records
213
+ - id: orchard.client.Client.create_record
214
+ doc: reference.Client.create_record
215
+ - id: orchard.client.Client.bulk_create
216
+ doc: reference.Client.bulk_create
217
+ - id: async
218
+ label: Asynchronous client
219
+ items:
220
+ - id: orchard.client.AsyncClient
221
+ doc: reference.AsyncClient
222
+ - id: orchard.client.AsyncClient.fetch
223
+ doc: reference.AsyncClient.fetch
224
+ - id: orchard.client.AsyncClient.iter_records
225
+ doc: reference.AsyncClient.iter_records
226
+ - id: models
227
+ label: Models
228
+ items:
229
+ - id: orchard.models
230
+ doc: reference.models
231
+ - id: orchard.models.Record
232
+ doc: reference.models.Record
233
+ - id: orchard.models.RecordInput
234
+ doc: reference.models.RecordInput
235
+ - id: orchard.models.RecordDict
236
+ doc: reference.models.RecordDict
237
+ - id: orchard.models.RecordStatus
238
+ doc: reference.models.RecordStatus
239
+ - id: orchard.models.Record.to_dict
240
+ doc: reference.models.Record.to_dict
241
+ - id: pagination
242
+ label: Pagination
243
+ items:
244
+ - id: orchard.pagination.Page
245
+ doc: reference.pagination.Page
246
+ - id: orchard.pagination.Page.map
247
+ doc: reference.pagination.Page.map
248
+ - id: orchard.pagination.Page.has_more
249
+ doc: reference.pagination.Page.has_more
250
+ - id: errors
251
+ label: Exceptions
252
+ items:
253
+ - id: orchard.errors
254
+ doc: reference.errors
255
+ - id: orchard.errors.APIError
256
+ doc: reference.errors.APIError
257
+ - id: orchard.errors.AuthenticationError
258
+ doc: reference.errors.AuthenticationError
259
+ - id: orchard.errors.NotFoundError
260
+ doc: reference.errors.NotFoundError
261
+ - id: orchard.errors.RateLimitError
262
+ doc: reference.errors.RateLimitError
@@ -0,0 +1,11 @@
1
+ """A sample SDK for working with records, collections, and paginated results.
2
+
3
+ Use [Client](symbol:orchard.client.Client) in synchronous applications, or
4
+ [AsyncClient](symbol:orchard.client.AsyncClient) in an async service. Models,
5
+ pagination helpers, and typed exceptions have their own reference pages.
6
+
7
+ This package is documentation example code. It doesn't contact a live service.
8
+ """
9
+ from .client import AsyncClient, Client
10
+ from .models import Record, RecordInput, RecordStatus
11
+ from .pagination import Page
@@ -0,0 +1,212 @@
1
+ """Synchronous and asynchronous clients for the sample records SDK."""
2
+ from collections.abc import AsyncIterator, Iterator, Sequence
3
+ from typing import Literal, overload
4
+ from .errors import AuthenticationError, NotFoundError
5
+ from .models import Record, RecordDict, RecordInput, RecordStatus
6
+ from .pagination import Page
7
+
8
+ class Client:
9
+ """Manage records from a synchronous Python application.
10
+
11
+ Create one client per API credential and reuse it across requests. Use
12
+ [AsyncClient](symbol:orchard.client.AsyncClient) in an async application.
13
+
14
+ The sample client keeps records in memory; it doesn't make network calls.
15
+ """
16
+ def __init__(self, api_key: str, *, base_url: str = "https://api.example.com/v1", timeout: float = 10.0, max_retries: int = 2):
17
+ """Configure a reusable client.
18
+
19
+ Args:
20
+ api_key: Credential used to identify the application.
21
+ base_url: Root URL of the API deployment.
22
+ timeout: Default request timeout, in seconds.
23
+ max_retries: Number of retries for transient failures.
24
+
25
+ Raises:
26
+ AuthenticationError: If the API key is empty.
27
+ """
28
+ if not api_key:
29
+ raise AuthenticationError("API key must not be empty")
30
+ self.base_url = base_url
31
+ self.timeout = timeout
32
+ self.max_retries = max_retries
33
+ self._records: dict[str, Record] = {"rec_42": Record("rec_42", "Getting started")}
34
+
35
+ @overload
36
+ def fetch(self, record_id: str, *, raw: Literal[False] = False, timeout: float | None = None) -> Record: ...
37
+ @overload
38
+ def fetch(self, record_id: str, *, raw: Literal[True], timeout: float | None = None) -> RecordDict: ...
39
+ def fetch(self, record_id: str, *, raw: bool = False, timeout: float | None = None) -> Record | RecordDict:
40
+ """Retrieve a record by its stable identifier.
41
+
42
+ The default response is a typed [Record](symbol:orchard.models.Record).
43
+ Set `raw=True` to receive a dictionary for serialization or a legacy
44
+ integration. An explicit timeout overrides the client's default.
45
+
46
+ Args:
47
+ record_id: Stable identifier of the record to retrieve.
48
+ raw: Return a wire-format dictionary instead of a model.
49
+ timeout: Request timeout override, in seconds.
50
+
51
+ Returns:
52
+ The record model, or a dictionary when `raw=True`.
53
+
54
+ Raises:
55
+ ValueError: If the record identifier is empty.
56
+ NotFoundError: If no record matches the identifier.
57
+
58
+ Examples:
59
+ >>> client = Client("demo-key")
60
+ >>> record = client.fetch("rec_42")
61
+ >>> record.title
62
+ 'Getting started'
63
+ >>> payload = client.fetch("rec_42", raw=True)
64
+ >>> payload["status"]
65
+ 'ready'
66
+ """
67
+ if not record_id:
68
+ raise ValueError("record_id must not be empty")
69
+ if record_id not in self._records:
70
+ raise NotFoundError(f"Record {record_id} was not found")
71
+ record = self._records[record_id]
72
+ return record.to_dict() if raw else record
73
+
74
+ def list_records(self, *, limit: int = 20, cursor: str | None = None, status: RecordStatus | None = None) -> Page[Record]:
75
+ """List a single page of records with optional status filtering.
76
+
77
+ Use [iter_records](symbol:orchard.client.Client.iter_records) when you
78
+ want to read every page without managing continuation cursors.
79
+
80
+ Args:
81
+ limit: Maximum number of records in a page, from 1 to 100.
82
+ cursor: Opaque continuation token from an earlier response.
83
+ status: Include only records with this processing status.
84
+
85
+ Returns:
86
+ A page of matching records with an optional continuation cursor.
87
+
88
+ Raises:
89
+ ValueError: If the page size is outside the supported range.
90
+
91
+ Examples:
92
+ >>> page = client.list_records(limit=10, status=RecordStatus.READY)
93
+ >>> titles = page.map(lambda record: record.title)
94
+ """
95
+ if not 1 <= limit <= 100:
96
+ raise ValueError("limit must be between 1 and 100")
97
+ records = [r for r in self._records.values() if status is None or r.status == status]
98
+ start = int(cursor or "0")
99
+ end = start + limit
100
+ return Page(records[start:end], str(end) if end < len(records) else None, len(records))
101
+
102
+ def iter_records(self, *, page_size: int = 20, status: RecordStatus | None = None) -> Iterator[Record]:
103
+ """Iterate through records across every page.
104
+
105
+ Pagination is lazy: another page is read only when the iterator needs it.
106
+ Stop iteration early to avoid fetching pages your application won't use.
107
+
108
+ Args:
109
+ page_size: Number of records requested per page, from 1 to 100.
110
+ status: Optional processing status filter.
111
+
112
+ Yields:
113
+ Records in the service's response order.
114
+
115
+ Examples:
116
+ >>> for record in client.iter_records(page_size=50):
117
+ ... print(record.id, record.title)
118
+ """
119
+ cursor = None
120
+ while True:
121
+ page = self.list_records(limit=page_size, cursor=cursor, status=status)
122
+ yield from page.items
123
+ if not page.has_more:
124
+ return
125
+ cursor = page.next_cursor
126
+
127
+ def create_record(self, data: RecordInput, *, idempotency_key: str | None = None) -> Record:
128
+ """Create a record from a typed input model.
129
+
130
+ Use an idempotency key when retrying a request after a timeout. Reuse the
131
+ same key only for retries of the same logical operation.
132
+
133
+ Args:
134
+ data: Title and labels for the new record.
135
+ idempotency_key: Application-generated retry token.
136
+
137
+ Returns:
138
+ An immutable snapshot of the created record.
139
+
140
+ Examples:
141
+ >>> data = RecordInput(title="Deployment notes", labels={"team": "platform"})
142
+ >>> record = client.create_record(data, idempotency_key="deploy-42")
143
+ """
144
+ record = Record(f"rec_{len(self._records) + 1}", data.title, labels=data.labels)
145
+ self._records[record.id] = record
146
+ return record
147
+
148
+ def bulk_create(self, records: Sequence[RecordInput], *, batch_size: int = 25) -> list[Record]:
149
+ """Create multiple records in bounded batches.
150
+
151
+ Args:
152
+ records: Input models to create, in submission order.
153
+ batch_size: Maximum number of records per batch, from 1 to 100.
154
+
155
+ Returns:
156
+ Created records in the same order as the submitted inputs.
157
+
158
+ Raises:
159
+ ValueError: If the batch size is outside the supported range.
160
+ """
161
+ if not 1 <= batch_size <= 100:
162
+ raise ValueError("batch_size must be between 1 and 100")
163
+ return [self.create_record(item) for item in records]
164
+
165
+ class AsyncClient:
166
+ """Use the records SDK from an async Python application.
167
+
168
+ Await individual operations, or use `async for` to consume a record iterator.
169
+ This example delegates to an in-memory client; no network calls are made.
170
+ """
171
+ def __init__(self, api_key: str, *, timeout: float = 10.0):
172
+ """Configure an async client.
173
+
174
+ Args:
175
+ api_key: Application credential.
176
+ timeout: Default request timeout, in seconds.
177
+ """
178
+ self._client = Client(api_key, timeout=timeout)
179
+
180
+ async def fetch(self, record_id: str, *, timeout: float | None = None) -> Record:
181
+ """Await a typed record response.
182
+
183
+ Args:
184
+ record_id: Stable record identifier.
185
+ timeout: Optional request timeout override, in seconds.
186
+
187
+ Returns:
188
+ The matching record model.
189
+
190
+ Raises:
191
+ NotFoundError: If the record doesn't exist.
192
+
193
+ Examples:
194
+ >>> record = await client.fetch("rec_42")
195
+ """
196
+ return self._client.fetch(record_id, timeout=timeout)
197
+
198
+ async def iter_records(self, *, page_size: int = 20) -> AsyncIterator[Record]:
199
+ """Consume paginated records using asynchronous iteration.
200
+
201
+ Args:
202
+ page_size: Maximum number of records read in one page.
203
+
204
+ Yields:
205
+ Typed records from successive pages.
206
+
207
+ Examples:
208
+ >>> async for record in client.iter_records(page_size=50):
209
+ ... print(record.title)
210
+ """
211
+ for record in self._client.iter_records(page_size=page_size):
212
+ yield record