@stainless-api/docs-ui 0.1.0-beta.13 → 0.1.0-beta.15

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 (39) hide show
  1. package/package.json +15 -7
  2. package/src/components/breadcrumbs.tsx +1 -1
  3. package/src/components/chat.tsx +18 -15
  4. package/src/components/method.tsx +8 -9
  5. package/src/components/overview.tsx +26 -12
  6. package/src/components/primitives.tsx +36 -19
  7. package/src/components/properties.tsx +4 -2
  8. package/src/components/scripts/dropdown.ts +1 -1
  9. package/src/components/sdk.tsx +25 -20
  10. package/src/components/sidebar.tsx +3 -3
  11. package/src/components/snippets.tsx +26 -9
  12. package/src/contexts/component-generics.tsx +10 -15
  13. package/src/contexts/docs.tsx +15 -4
  14. package/src/contexts/index.tsx +8 -5
  15. package/src/contexts/markdown.tsx +7 -6
  16. package/src/contexts/search.tsx +4 -5
  17. package/src/hooks/use-strict-context.tsx +16 -0
  18. package/src/languages/go.tsx +3 -3
  19. package/src/languages/http.tsx +31 -23
  20. package/src/languages/index.ts +7 -7
  21. package/src/languages/java.tsx +4 -4
  22. package/src/languages/python.tsx +12 -9
  23. package/src/languages/ruby.tsx +20 -13
  24. package/src/languages/typescript.tsx +18 -12
  25. package/src/markdown/index.ts +17 -12
  26. package/src/markdown/utils.ts +6 -3
  27. package/src/routing.ts +9 -9
  28. package/src/search/form.tsx +26 -24
  29. package/src/search/indexer.ts +17 -15
  30. package/src/search/mcp.ts +108 -16
  31. package/src/search/printer.tsx +1 -1
  32. package/src/search/providers/algolia.ts +5 -5
  33. package/src/search/providers/fuse.ts +4 -4
  34. package/src/search/providers/pagefind.ts +1 -1
  35. package/src/search/providers/walker.ts +5 -3
  36. package/src/search/results.tsx +7 -6
  37. package/src/search/types.ts +2 -2
  38. package/src/styles/main.css +2 -1
  39. package/src/utils.ts +9 -8
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stainless-api/docs-ui",
3
3
  "private": false,
4
- "version": "0.1.0-beta.13",
4
+ "version": "0.1.0-beta.15",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -10,17 +10,21 @@
10
10
  "dist",
11
11
  "src"
12
12
  ],
13
+ "peerDependencies": {
14
+ "react": ">=19.0.0",
15
+ "react-dom": ">=19.0.0"
16
+ },
13
17
  "dependencies": {
14
18
  "@algolia/client-search": "^5.25.0",
19
+ "@langchain/community": "^0.3.16",
15
20
  "@markdoc/markdoc": "^0.5.2",
16
21
  "ai": "4.3.19",
17
22
  "clsx": "^2.1.1",
18
23
  "fuse.js": "^7.1.0",
19
24
  "htmlparser2": "^10.0.0",
20
25
  "lucide-react": "^0.544.0",
21
- "react": "^19.2.0",
22
- "react-dom": "^19.2.0",
23
- "@stainless-api/ui-primitives": "0.1.0-beta.14"
26
+ "natural": "^8.0.1",
27
+ "@stainless-api/ui-primitives": "0.1.0-beta.15"
24
28
  },
25
29
  "devDependencies": {
26
30
  "@types/node": "^24.4.0",
@@ -28,12 +32,16 @@
28
32
  "@types/react-dom": "^19.2.2",
29
33
  "dotenv": "17.2.3",
30
34
  "esbuild": "^0.25.4",
35
+ "react": "^19.2.0",
36
+ "react-dom": "^19.2.0",
31
37
  "tsx": "^4.20.3",
32
- "@stainless/eslint-config": "0.0.0"
38
+ "typescript": "5.9.3",
39
+ "@stainless/eslint-config": "0.0.0",
40
+ "@stainless/sdk-json": "^0.0.0"
33
41
  },
34
42
  "scripts": {
35
- "symlink": "node make-symlink.js",
36
43
  "build": "node build.js",
37
- "lint": "eslint ."
44
+ "lint": "eslint .",
45
+ "check:types": "tsc --noEmit"
38
46
  }
39
47
  }
@@ -1,4 +1,4 @@
1
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
1
+ import type * as SDKJSON from '@stainless/sdk-json';
2
2
  import { Languages } from '../routing';
3
3
  import { ChevronRight } from 'lucide-react';
4
4
  import style from '../style';
@@ -4,29 +4,32 @@ import { ArrowUpIcon, BotMessageSquareIcon, LoaderCircleIcon, SquareIcon, TrashI
4
4
  import { useLanguage, useSearchContext } from '../contexts';
5
5
  import { Markdown } from './primitives';
6
6
  import type { SearchSettings } from '../search/types';
7
- import type { UIMessage } from 'ai';
7
+ import type { Message, UIMessage } from 'ai';
8
8
  import style from '../style';
9
9
 
10
10
  const BASEURL = 'https://askai.algolia.com';
11
11
 
12
12
  function buildFetch({ appId, searchKey, indexName, assistant }: SearchSettings) {
13
- const headers = {
13
+ const headers: Record<string, string> = {
14
14
  'X-Algolia-Application-Id': appId,
15
15
  'X-Algolia-API-Key': searchKey,
16
16
  'X-Algolia-Index-Name': `${indexName}-chat`,
17
- 'X-Algolia-Assistant-Id': assistant,
18
17
  };
18
+ if (assistant) {
19
+ headers['X-Algolia-Assistant-Id'] = assistant;
20
+ }
19
21
 
20
- return async (input, init) => {
22
+ return async (input: RequestInfo | URL, init: RequestInit | undefined) => {
21
23
  const response = await fetch(`${BASEURL}/chat/token`, { method: 'POST', headers });
22
- const data = await response.json();
24
+ const data = (await response.json()) as Record<string, unknown>;
25
+ const reqHeaders = new Headers(init?.headers);
26
+ reqHeaders.set('Authorization', `TOKEN ${data['token']}`);
27
+ for (const name in headers) {
28
+ reqHeaders.set(name, headers[name]!);
29
+ }
23
30
  return fetch(input, {
24
31
  ...init,
25
- headers: {
26
- ...init.headers,
27
- ...headers,
28
- Authorization: `TOKEN ${data['token']}`,
29
- },
32
+ headers: reqHeaders,
30
33
  });
31
34
  };
32
35
  }
@@ -53,7 +56,7 @@ export function Chat() {
53
56
  const content = sessionStorage.getItem('stldocs-chat-messages');
54
57
  if (!content) return;
55
58
 
56
- setMessages(JSON.parse(content));
59
+ setMessages(JSON.parse(content) as Message[]);
57
60
  setTimeout(() => {
58
61
  if (!messageListRef.current) return;
59
62
  messageListRef.current.scrollTop = messageListRef.current.scrollHeight;
@@ -61,8 +64,8 @@ export function Chat() {
61
64
  }, [setMessages]);
62
65
 
63
66
  React.useEffect(() => {
64
- if (status !== 'submitted') return;
65
- const messageElements = messageListRef?.current.querySelectorAll('[data-stldocs-chat-message]');
67
+ if (status !== 'submitted' || !messageListRef.current) return;
68
+ const messageElements = messageListRef.current.querySelectorAll('[data-stldocs-chat-message]');
66
69
  [...messageElements].at(-1)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
67
70
  }, [status]);
68
71
 
@@ -75,7 +78,7 @@ export function Chat() {
75
78
  setInput('');
76
79
  }
77
80
 
78
- function handleKeyUp(ev) {
81
+ function handleKeyUp(ev: React.KeyboardEvent) {
79
82
  if (ev.key === 'Enter' && !ev.shiftKey) {
80
83
  ev.preventDefault();
81
84
  handleSend();
@@ -134,7 +137,7 @@ export type ChatModalProps = {
134
137
  };
135
138
 
136
139
  export function ChatModal({ id, open: isOpen }: ChatModalProps) {
137
- const [open, setOpen] = React.useState<boolean>(isOpen);
140
+ const [open, setOpen] = React.useState<boolean>(isOpen ?? false);
138
141
 
139
142
  return (
140
143
  <div
@@ -18,23 +18,24 @@ export const HttpMethodIcons: Record<HTTPMethod, LucideIcon> = {
18
18
  };
19
19
 
20
20
  export type MethodIconProps = {
21
- httpMethod: string;
21
+ httpMethod?: string;
22
22
  showName?: boolean;
23
23
  };
24
24
 
25
25
  export function MethodIconBadge({ httpMethod, showName }: MethodIconProps) {
26
- if (!HttpMethods.includes(httpMethod)) return null;
26
+ if (!httpMethod || !HttpMethods.includes(httpMethod)) return null;
27
27
 
28
28
  return (
29
29
  <span
30
30
  className={clsx(style.MethodRouteHttpMethod, style.MethodRouteHttpMethodIconOnly)}
31
31
  data-method={httpMethod}
32
32
  >
33
- {React.createElement(HttpMethodIcons[httpMethod], {
34
- size: 14,
35
- strokeWidth: 3,
36
- className: style.Icon,
37
- })}
33
+ {HttpMethodIcons[httpMethod] &&
34
+ React.createElement(HttpMethodIcons[httpMethod], {
35
+ size: 14,
36
+ strokeWidth: 3,
37
+ className: style.Icon,
38
+ })}
38
39
  {showName && httpMethod}
39
40
  </span>
40
41
  );
@@ -135,8 +136,6 @@ export type MethodProps = {
135
136
  } & React.HTMLProps<HTMLDivElement>;
136
137
 
137
138
  export function Method({ id, header, children, className, ...props }: MethodProps) {
138
- const Docs = useComponents();
139
-
140
139
  return (
141
140
  <div id={id} className={clsx(style.Method, className)} tabIndex={0} {...props}>
142
141
  {header}
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { ChevronRight } from 'lucide-react';
3
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
3
+ import type * as SDKJSON from '@stainless/sdk-json';
4
4
 
5
5
  import { flatResources, getResourceFromSpec } from '../utils';
6
6
  import { useDeclaration, useLanguage, useLanguageComponents, useSpec } from '../contexts';
@@ -54,7 +54,7 @@ export type SDKMethodSummaryProps = {
54
54
  export function SDKMethodSummary({ method }: SDKMethodSummaryProps) {
55
55
  const Docs = useComponents();
56
56
  const Lang = useLanguageComponents();
57
- const decl = useDeclaration(method.stainlessPath);
57
+ const decl = useDeclaration(method.stainlessPath, true);
58
58
 
59
59
  return (
60
60
  <Docs.MethodHeader
@@ -76,11 +76,11 @@ export function SDKResource({ resource, parents, showModels }: SDKResourceProps
76
76
  const spec = useSpec();
77
77
 
78
78
  const methods = Object.values(resource.methods).filter(
79
- (method) => spec.decls?.[language]?.[method.stainlessPath],
79
+ (method) => spec?.decls?.[language]?.[method.stainlessPath],
80
80
  );
81
81
 
82
82
  const models = Object.values(resource.models).filter(
83
- (model) => spec.decls?.[language]?.[`${model.stainlessPath} > (schema)`],
83
+ (model) => spec?.decls?.[language]?.[`${model.stainlessPath} > (schema)`],
84
84
  );
85
85
 
86
86
  return (
@@ -144,15 +144,29 @@ export function SDKRoot({ stainlessPath }: SDKRootProps) {
144
144
  const Docs = useComponents();
145
145
 
146
146
  const parsed = parseStainlessPath(stainlessPath);
147
- const resource = getResourceFromSpec(stainlessPath, spec);
147
+ const resource = spec && getResourceFromSpec(stainlessPath, spec);
148
148
 
149
- if (!resource || !parsed) return null;
149
+ if (!resource || !parsed) {
150
+ console.warn(`Could not find resource or parsed path for '${stainlessPath}'`);
151
+ return null;
152
+ }
150
153
 
151
- const content = parsed.method ? (
152
- <Docs.SDKMethod method={resource.methods[parsed.method]} />
153
- ) : (
154
- <Docs.SDKOverview resource={resource} />
155
- );
154
+ if (parsed.method) {
155
+ const method = resource.methods[parsed.method];
156
+ if (!method) {
157
+ console.warn(`Method '${parsed.method}' not found in resource '${resource.stainlessPath}'`);
158
+ return null;
159
+ }
160
+ return (
161
+ <div className={style.Root}>
162
+ <Docs.SDKMethod method={method} />
163
+ </div>
164
+ );
165
+ }
156
166
 
157
- return <div className={style.Root}>{content}</div>;
167
+ return (
168
+ <div className={style.Root}>
169
+ <Docs.SDKOverview resource={resource} />
170
+ </div>
171
+ );
158
172
  }
@@ -10,7 +10,12 @@ type JoinProps = { items: ReactNode[]; limit?: number; children: ReactNode };
10
10
  export function Join({ items, limit, children }: JoinProps) {
11
11
  const arr =
12
12
  limit && items.length > limit + 1
13
- ? [...items.slice(0, limit), <span className={style.Truncation}>{items.length - limit} more</span>]
13
+ ? [
14
+ ...items.slice(0, limit),
15
+ <span className={style.Truncation} key="truncation">
16
+ {items.length - limit} more
17
+ </span>,
18
+ ]
14
19
  : items;
15
20
 
16
21
  return arr.map((item, index) => (
@@ -31,7 +36,8 @@ type ExpanderProps = {
31
36
  };
32
37
 
33
38
  export function Expander({ id, open, summary, virtual, muted, children }: ExpanderProps) {
34
- const { virtualExpanders } = useSettings();
39
+ const settings = useSettings();
40
+ const virtualExpanders = settings?.virtualExpanders;
35
41
 
36
42
  if (virtual || virtualExpanders)
37
43
  return (
@@ -154,8 +160,8 @@ export function Link({ stainlessPath, scroll = true, children, ...props }: LinkP
154
160
 
155
161
  const href = React.useMemo(() => {
156
162
  if (props.href) return props.href;
157
- if (stainlessPath) return generateRoute(basePath, language, stainlessPath);
158
- }, [basePath, language, stainlessPath]);
163
+ if (stainlessPath && basePath) return generateRoute(basePath, language, stainlessPath);
164
+ }, [basePath, language, stainlessPath, props.href]);
159
165
 
160
166
  const handleClick = React.useCallback(
161
167
  (e: React.MouseEvent<HTMLAnchorElement>) => {
@@ -163,7 +169,7 @@ export function Link({ stainlessPath, scroll = true, children, ...props }: LinkP
163
169
  if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
164
170
  if (href) onNavigate?.(e, { href, language, stainlessPath, scroll });
165
171
  },
166
- [href, scroll, onNavigate],
172
+ [href, scroll, onNavigate, language, props, stainlessPath],
167
173
  );
168
174
 
169
175
  if (!href) return children;
@@ -180,7 +186,10 @@ export type InputProps = {
180
186
  right?: ReactNode;
181
187
  } & React.InputHTMLAttributes<HTMLInputElement>;
182
188
 
183
- export const Input = React.forwardRef<HTMLInputElement, InputProps>(({ left, right, ...props }, ref) => {
189
+ export const Input = React.forwardRef<HTMLInputElement, InputProps>(function Input(
190
+ { left, right, ...props },
191
+ ref,
192
+ ) {
184
193
  return (
185
194
  <div className={style.Input}>
186
195
  {left}
@@ -195,15 +204,16 @@ export type ToggleButtonProps = {
195
204
  selected?: boolean;
196
205
  } & React.ButtonHTMLAttributes<HTMLButtonElement>;
197
206
 
198
- export const ToggleButton = React.forwardRef<HTMLButtonElement, ToggleButtonProps>(
199
- ({ children, selected, ...props }, ref) => {
200
- return (
201
- <button {...props} ref={ref} className={style.ToggleButton} data-stldocs-toggle-selected={selected}>
202
- {children}
203
- </button>
204
- );
205
- },
206
- );
207
+ export const ToggleButton = React.forwardRef<HTMLButtonElement, ToggleButtonProps>(function ToggleButton(
208
+ { children, selected, ...props },
209
+ ref,
210
+ ) {
211
+ return (
212
+ <button {...props} ref={ref} className={style.ToggleButton} data-stldocs-toggle-selected={selected}>
213
+ {children}
214
+ </button>
215
+ );
216
+ });
207
217
 
208
218
  export type ListViewProps<TItem> = {
209
219
  items: Array<TItem>;
@@ -222,6 +232,13 @@ export function ListView<TItem>({ items, itemDelegate, onSelectListItem, ...rest
222
232
  setKeyboardIndex(0);
223
233
  }, [items]);
224
234
 
235
+ const handleSelect = React.useCallback(() => {
236
+ const item = items[selectedIndex];
237
+ if (item) {
238
+ onSelectListItem?.(item);
239
+ }
240
+ }, [items, selectedIndex, onSelectListItem]);
241
+
225
242
  React.useEffect(() => {
226
243
  function handleKeyPress(ev: KeyboardEvent) {
227
244
  switch (ev.key) {
@@ -243,17 +260,17 @@ export function ListView<TItem>({ items, itemDelegate, onSelectListItem, ...rest
243
260
 
244
261
  case 'Enter':
245
262
  ev.preventDefault();
246
- onSelectListItem?.(items[selectedIndex]);
263
+ handleSelect();
247
264
  break;
248
265
  }
249
266
  }
250
267
 
251
268
  addEventListener('keydown', handleKeyPress);
252
269
  return () => removeEventListener('keydown', handleKeyPress);
253
- }, [items, selectedIndex]);
270
+ }, [items, selectedIndex, handleSelect]);
254
271
 
255
272
  React.useEffect(() => {
256
- if (!keyboardIndex) {
273
+ if (!keyboardIndex || !itemRef.current || !listRef.current) {
257
274
  listRef?.current?.scroll(0, 0);
258
275
  return;
259
276
  }
@@ -273,7 +290,7 @@ export function ListView<TItem>({ items, itemDelegate, onSelectListItem, ...rest
273
290
  ref={index === selectedIndex ? itemRef : null}
274
291
  className={style.ListViewItem}
275
292
  data-stldocs-listview-selected={index === selectedIndex}
276
- onClick={() => onSelectListItem?.(items[selectedIndex])}
293
+ onClick={handleSelect}
277
294
  onMouseMove={() => setSelectedIndex(index)}
278
295
  >
279
296
  {itemDelegate(item, index === selectedIndex)}
@@ -1,4 +1,4 @@
1
- import React, { type ReactNode } from 'react';
1
+ import { type ReactNode } from 'react';
2
2
  import { useLanguage, useSettings } from '../contexts';
3
3
  import { useComponents } from '../contexts/use-components';
4
4
  import style from '../style';
@@ -72,7 +72,9 @@ export function Property({
72
72
  const Docs = useComponents();
73
73
  const language = useLanguage();
74
74
 
75
- const { collapseDescription, types } = useSettings().properties ?? {};
75
+ const properties = useSettings()?.properties;
76
+ const collapseDescription = properties?.collapseDescription;
77
+ const types = properties?.types;
76
78
 
77
79
  const textContent = (
78
80
  <>
@@ -46,7 +46,7 @@ export function initDropdown({
46
46
  }
47
47
 
48
48
  // Toggle dropdown on button click
49
- button.addEventListener('click', (e) => {
49
+ button.addEventListener('click', () => {
50
50
  toggleDropdown();
51
51
  });
52
52
 
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
2
+ import type * as SDKJSON from '@stainless/sdk-json';
3
3
  import { useComponents } from '../contexts/use-components';
4
4
  import {
5
5
  useLanguageComponents,
@@ -32,7 +32,7 @@ export type PropertyModelContextType = {
32
32
  propertyPath?: string;
33
33
  };
34
34
 
35
- export const PropertyModelContext = React.createContext<PropertyModelContextType>(null);
35
+ export const PropertyModelContext = React.createContext<PropertyModelContextType>({});
36
36
  export function usePropertyModel() {
37
37
  return React.useContext(PropertyModelContext);
38
38
  }
@@ -93,16 +93,15 @@ type SDKDeclarationProps = {
93
93
  export function SDKDeclaration({ path, expand, depth = 0 }: SDKDeclarationProps) {
94
94
  const Lang = useLanguageComponents();
95
95
  const Docs = useComponents();
96
- const decl = useDeclaration(path);
96
+ const decl = useDeclaration(path, true);
97
97
  const settings = useSettings();
98
98
  const model = usePropertyModel();
99
99
  const nesting = useReferenceNesting();
100
100
  const { selectedPath } = useNavigation();
101
101
 
102
- if (!decl) return null;
103
-
104
102
  if (decl.kind.endsWith('Reference')) {
105
- const refId = decl['type']['$ref'];
103
+ const refId =
104
+ 'type' in decl && typeof decl.type !== 'string' && '$ref' in decl.type ? decl.type['$ref'] : undefined;
106
105
  if (refId && refId !== path && !nesting.includes(refId)) {
107
106
  return (
108
107
  <ReferenceNestingContext.Provider value={[...nesting, refId]}>
@@ -112,11 +111,14 @@ export function SDKDeclaration({ path, expand, depth = 0 }: SDKDeclarationProps)
112
111
  }
113
112
  }
114
113
 
115
- const isUnion = 'childrenParentSchema' in decl && ['enum', 'union'].includes(decl.childrenParentSchema);
114
+ const isUnion =
115
+ 'childrenParentSchema' in decl &&
116
+ decl.childrenParentSchema &&
117
+ ['enum', 'union'].includes(decl.childrenParentSchema);
116
118
  const id = model?.propertyPath ? `${model.propertyPath} + ${path}` : path;
117
119
  const shouldExpand =
118
- (selectedPath.startsWith(path) && nesting.length < 1) ||
119
- (settings.properties?.expandDepth && depth <= settings.properties?.expandDepth && !isUnion) ||
120
+ (selectedPath?.startsWith(path) && nesting.length < 1) ||
121
+ (settings?.properties?.expandDepth && depth <= settings?.properties?.expandDepth && !isUnion) ||
120
122
  expand;
121
123
 
122
124
  const content = (
@@ -125,18 +127,18 @@ export function SDKDeclaration({ path, expand, depth = 0 }: SDKDeclarationProps)
125
127
  <Docs.Property
126
128
  id={id}
127
129
  expand={shouldExpand}
128
- constraints={decl['constraints'] && <Docs.SDKConstraints constraints={decl['constraints']} />}
130
+ constraints={'constraints' in decl && <Docs.SDKConstraints constraints={decl['constraints']} />}
129
131
  declaration={<Lang.Declaration decl={decl} />}
130
- description={decl['docstring']}
132
+ description={'docstring' in decl ? decl['docstring'] : undefined}
131
133
  deprecated={decl.deprecated}
132
134
  {...props}
133
135
  >
134
136
  {'children' in decl &&
135
- decl.children.length > 0 &&
136
- (settings.properties?.includeModelProperties !== false || !('modelPath' in decl)) && (
137
+ (decl.children?.length ?? 0) > 0 &&
138
+ (settings?.properties?.includeModelProperties !== false || !('modelPath' in decl)) && (
137
139
  <>
138
140
  {isUnion && <div className={style.PropertyAnnotation}>Accepts one of the following:</div>}
139
- <Docs.SDKChildren paths={decl.children} depth={depth + 1} />
141
+ <Docs.SDKChildren paths={decl.children ?? []} depth={depth + 1} />
140
142
  </>
141
143
  )}
142
144
  </Docs.Property>
@@ -239,7 +241,7 @@ export type SDKMethodProps = {
239
241
  export function SDKMethodHeader({ method }: SDKMethodProps) {
240
242
  const Docs = useComponents();
241
243
  const Lang = useLanguageComponents();
242
- const decl = useDeclaration(method.stainlessPath);
244
+ const decl = useDeclaration(method.stainlessPath, true);
243
245
 
244
246
  return (
245
247
  <Docs.MethodHeader
@@ -256,7 +258,7 @@ export function SDKMethodHeader({ method }: SDKMethodProps) {
256
258
  export function SDKMethodInfo({ method }: SDKMethodProps) {
257
259
  const Docs = useComponents();
258
260
  const Lang = useLanguageComponents();
259
- const decl = useDeclaration(method.stainlessPath);
261
+ const decl = useDeclaration(method.stainlessPath, true);
260
262
  const spec = useSpec();
261
263
  const language = useLanguage();
262
264
 
@@ -264,8 +266,10 @@ export function SDKMethodInfo({ method }: SDKMethodProps) {
264
266
 
265
267
  function shouldExpand(items: SDKJSON.ID[]) {
266
268
  if (items.length > 1) return false;
267
- const decl = spec?.decls?.[language]?.[items[0]];
268
- return decl && 'children' in decl && decl.children.length > 0;
269
+ const item = items[0];
270
+ if (!item) return false;
271
+ const decl = spec?.decls?.[language]?.[item];
272
+ return decl && 'children' in decl && decl.children && decl.children.length > 0;
269
273
  }
270
274
 
271
275
  return (
@@ -279,6 +283,7 @@ export function SDKMethodInfo({ method }: SDKMethodProps) {
279
283
  }
280
284
  returns={
281
285
  'responseChildren' in decl &&
286
+ decl.responseChildren &&
282
287
  decl.responseChildren.length > 0 && (
283
288
  <Docs.SDKChildren expand={shouldExpand(decl.responseChildren)} paths={decl.responseChildren} />
284
289
  )
@@ -289,7 +294,7 @@ export function SDKMethodInfo({ method }: SDKMethodProps) {
289
294
 
290
295
  export function SDKMethod({ method, transformRequestSnippet }: SDKMethodProps) {
291
296
  const Docs = useComponents();
292
- const decl = useDeclaration(method?.stainlessPath);
297
+ const decl = useDeclaration(method?.stainlessPath, true);
293
298
  const layout = useContentPanelLayout();
294
299
 
295
300
  if (!decl) return;
@@ -324,7 +329,7 @@ export type SDKModelProps = {
324
329
 
325
330
  export function SDKModel({ model }: SDKModelProps) {
326
331
  const Docs = useComponents();
327
- const decl = useDeclaration(`${model.stainlessPath} > (schema)`);
332
+ const decl = useDeclaration(`${model.stainlessPath} > (schema)`, true);
328
333
 
329
334
  if (!decl) return null;
330
335
 
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
2
+ import type * as SDKJSON from '@stainless/sdk-json';
3
3
  import { useNavigation } from '../contexts';
4
4
  import { useComponents } from '../contexts/use-components';
5
5
  import { ChevronDown, ChevronRight } from 'lucide-react';
@@ -50,8 +50,8 @@ export type SidebarResourceProps = {
50
50
 
51
51
  export function SidebarResource({ resource }: SidebarResourceProps) {
52
52
  const Docs = useComponents();
53
- const { selectedPath, navigationPath } = useNavigation();
54
- const subresources = Object.values(resource.subresources).map((sub) => (
53
+ const { selectedPath } = useNavigation();
54
+ const subresources = Object.values(resource.subresources ?? {}).map((sub) => (
55
55
  <SidebarResource resource={sub} key={sub.stainlessPath} />
56
56
  ));
57
57
 
@@ -4,7 +4,7 @@ import { useDeclaration, useHighlight, useLanguage, useSnippet } from '../contex
4
4
  import { useComponents } from '../contexts/use-components';
5
5
  import style from '../style';
6
6
  import clsx from 'clsx';
7
- import type * as SDKJSON from '~/lib/json-spec-v2/types';
7
+ import type * as SDKJSON from '@stainless/sdk-json';
8
8
  import type { TransformRequestSnippetFn } from './sdk';
9
9
  import { Button } from '@stainless-api/ui-primitives';
10
10
 
@@ -59,15 +59,24 @@ export function Snippet({ requestTitle, method, transformRequestSnippet }: Snipp
59
59
  const language = useLanguage();
60
60
  const [CopyButtonIcon, setCopyIcon] = React.useState<LucideIcon>(CopyIcon);
61
61
 
62
- let snippet = useSnippet(method.stainlessPath, null, language === 'http' ? 'curl' : 'default');
63
- const decl = useDeclaration(method.stainlessPath);
62
+ const originalSnippet = useSnippet(
63
+ method.stainlessPath,
64
+ undefined,
65
+ language === 'http' ? 'curl' : 'default',
66
+ );
67
+ const decl = useDeclaration(method.stainlessPath, false);
68
+
69
+ if (!originalSnippet) {
70
+ console.warn(`Snippet not found for method '${method.stainlessPath}'`);
71
+ return null;
72
+ }
64
73
 
65
- const signature = 'qualified' in decl ? decl.qualified : undefined;
74
+ const signature = decl && 'qualified' in decl ? decl.qualified : undefined;
66
75
  const responses = method.exampleResponses;
67
76
 
68
- if (transformRequestSnippet) {
69
- snippet = transformRequestSnippet({ snippet, language });
70
- }
77
+ const snippet = transformRequestSnippet
78
+ ? transformRequestSnippet({ snippet: originalSnippet, language })
79
+ : originalSnippet;
71
80
 
72
81
  async function handleCopy() {
73
82
  try {
@@ -113,9 +122,15 @@ export function SnippetResponse({ responses }: { responses: SDKJSON.Method['exam
113
122
 
114
123
  const mappedResponses = Object.keys(responses)
115
124
  .map((key) => {
125
+ const responseContent = responses[key];
126
+ if (!responseContent) return null;
127
+
116
128
  // Get the first response type ie application/json or text/plain
117
- const responseType = Object.keys(responses[key])[0];
118
- const response = responses[key][responseType];
129
+ const responseType = Object.keys(responseContent)[0];
130
+ if (!responseType) return null;
131
+
132
+ const response = responseContent[responseType];
133
+ if (!response) return null;
119
134
 
120
135
  const examples = response?.examples;
121
136
 
@@ -123,6 +138,8 @@ export function SnippetResponse({ responses }: { responses: SDKJSON.Method['exam
123
138
 
124
139
  // Get the first example type, ie Example or html
125
140
  const exampleType = Object.keys(examples)[0];
141
+ if (!exampleType) return null;
142
+
126
143
  let value = examples[exampleType]?.value;
127
144
 
128
145
  if (!value) return null;