getsyntux 0.8.0 → 0.9.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.
package/README.md CHANGED
@@ -10,11 +10,9 @@ You give it a <code>value</code> and it designs the UI to display it.
10
10
 
11
11
  ---
12
12
 
13
- https://github.com/user-attachments/assets/c85de55d-21f3-43ff-8bd3-d6ede2171447
14
-
15
- *syntux* is designed to **display data**. Do not let this fact intimidate you - that is simply a testament to how token-efficient it is.
13
+ ![](https://raw.githubusercontent.com/puffinsoft/syntux/HEAD/docs/images/features.png)
16
14
 
17
- For instance, if you provide an array `value` with 10,000 items, it will cost you the same as one with 10 items. **That is how efficient syntux is**.
15
+ https://github.com/user-attachments/assets/c85de55d-21f3-43ff-8bd3-d6ede2171447
18
16
 
19
17
  ### Features
20
18
 
@@ -31,7 +29,7 @@ For instance, if you provide an array `value` with 10,000 items, it will cost yo
31
29
 
32
30
  ### API
33
31
 
34
- <i>syntux</i> is built for React and Next.js.
32
+ <i>syntux</i> is built for React, supporting Next.js, React Router / Remix and Astro.
35
33
 
36
34
  One component is all you need:
37
35
 
@@ -43,52 +41,42 @@ const valueToDisplay = {
43
41
  }
44
42
 
45
43
  <GeneratedUI
46
- model={anthropic('claude-sonnet-4-5')}
44
+ endpoint="/api/syntux"
47
45
  value={valueToDisplay}
48
46
  hint="UI should look like..."
49
47
  />
50
48
  ```
51
49
 
52
- *syntux* takes the `value` into consideration and designs a UI to best display it. `value` can be anything; an object, array or primitive.
50
+ *syntux* reads the `value` and designs the UI to best display it, taking the `hint` into consideration.
53
51
 
54
52
  > [!TIP]
55
- > If you are passing in a **large array** as a value, or an object with untrusted input, use the `skeletonize` property. See [the explanation](https://docs.getsyntux.com/advanced#skeletonize-property).
56
-
57
- ### Installation
58
-
59
- In the root of your project:
60
-
61
- ```
62
- $ npx getsyntux@latest
63
- ```
64
-
65
- This will automatically install the required components in the `lib/getsyntux` folder.
66
-
67
- We use the [Vercel AI SDK](https://github.com/vercel/ai) to provide support for all LLM providers. To install the model providers:
68
-
69
- ```
70
- $ npm i ai
71
- $ npm i @ai-sdk/anthropic (if you're using Claude)
72
- ```
53
+ > If you are passing in a **large array** as a value, use the `skeletonize` property. See [the explanation](https://docs.getsyntux.com/usage/advanced#skeletonize-property).
73
54
 
74
55
  ---
75
56
 
76
57
  ### Examples
77
58
 
59
+ The following examples are meant to give you an idea of how *syntux* works.
60
+
61
+ See [wiki: Installation](https://docs.getsyntux.com/installation) once you're ready to begin.
62
+
78
63
  #### Basic Example
79
64
 
80
65
  Generate a simple UI with a hint:
81
66
 
82
67
  ```jsx
83
- import { GeneratedUI } from "@/lib/getsyntux/GeneratedUI";
84
- import { createAnthropic } from "@ai-sdk/anthropic";
68
+ import { GeneratedUI } from 'getsyntux/client';
85
69
 
86
- /* this example uses Claude, but all models are supported! */
87
- const anthropic = createAnthropic({ apiKey: ... })
70
+ export default function Page() {
71
+ const value = { username: 'John', email: 'john@gmail.com', age: 22 };
88
72
 
89
- export default function Home(){
90
- const valueToDisplay = { ... };
91
- return <GeneratedUI model={anthropic("claude-sonnet-4-5")} value={valueToDisplay} hint="UI should look like..." />
73
+ return (
74
+ <GeneratedUI
75
+ endpoint="/api/syntux"
76
+ value={value}
77
+ hint="display as a profile card"
78
+ />
79
+ );
92
80
  }
93
81
  ```
94
82
 
@@ -97,21 +85,19 @@ export default function Home(){
97
85
  Cache generated UI based on a user ID:
98
86
 
99
87
  ```jsx
100
- const cache: Map<number, string> = new Map(); // user id → UI schema
88
+ import { cache } from './api/syntux/route';
101
89
 
102
- export default function Home() {
103
- const userID = 10;
104
- const valueToDisplay = { ... };
90
+ export default function Page() {
91
+ const userId = 10;
92
+ const value = { username: 'John', email: 'john@gmail.com', age: 22 };
105
93
 
106
94
  return (
107
95
  <GeneratedUI
108
- cached={cache.get(userID)}
109
- onGenerate={(result) => {
110
- cache.set(userID, result);
111
- }}
96
+ endpoint="/api/syntux"
97
+ value={value}
98
+ hint="UI should look like..."
112
99
 
113
- model={anthropic("claude-sonnet-4-5")}
114
- value={valueToDisplay}
100
+ cached={cache.get(userId)}
115
101
  />
116
102
  );
117
103
  }
@@ -122,26 +108,31 @@ export default function Home() {
122
108
  Use your own components, or someone else's (a library):
123
109
 
124
110
  ```jsx
125
- import { CustomOne, CustomTwo } from "@/my_components";
111
+ import { GeneratedUI } from 'getsyntux/client';
112
+ import { Card, Avatar } from '@/components/ui';
126
113
 
127
- export default function Home() {
128
- const valueToDisplay = { ... };
114
+ export default function Page() {
115
+ const value = { username: 'John', email: 'john@gmail.com', avatar: '/john.png' };
129
116
 
130
117
  return (
131
118
  <GeneratedUI
132
- components={[{
133
- name: "Button",
134
- props: "{ color: string, text: string }",
135
- component: CustomOne,
136
- }, {
137
- name: "Input",
138
- props: "{ initial: string, disabled: boolean }",
139
- component: CustomTwo,
140
- context: "Creates an input field with an (initial) value. Can be disabled.",
141
- }]}
142
-
143
- model={anthropic("claude-sonnet-4-5")}
144
- value={valueToDisplay}
119
+ endpoint="/api/syntux"
120
+ value={value}
121
+ hint="use custom components when possible"
122
+
123
+ components={[
124
+ {
125
+ name: 'Card',
126
+ props: '{ title: string, body: string }',
127
+ component: Card,
128
+ },
129
+ {
130
+ name: 'Avatar',
131
+ props: '{ src: string, alt: string }',
132
+ component: Avatar,
133
+ context: 'Displays a circular profile image.', /* optional */
134
+ },
135
+ ]}
145
136
  />
146
137
  );
147
138
  }
@@ -155,47 +146,21 @@ export default function Home() {
155
146
 
156
147
  Make sure components are marked with `"use client"`.
157
148
 
158
- #### Update value (static)
159
-
160
- Use the `useSyntux` hook to retrieve and update the `value` inside a custom component:
161
-
162
- ```jsx
163
- "use client";
164
-
165
- export default function CustomComponent() {
166
- const { value, setValue } = useSyntux();
167
-
168
- return (
169
- <button
170
- onClick={() => {
171
- const newArr = [...value];
172
- newArr.push({ ... });
173
- setValue(newArr);
174
- }}
175
- >
176
- Add value
177
- </button>
178
- );
179
- }
180
- ```
181
-
182
- #### Regenerate UI (dynamic)
149
+ #### Reactivity
183
150
 
184
- Provide the `rerender` prop a server action.
185
-
186
- **The server action is already provided.** However, you will need to configure it (i.e., add an API key etc,.)
151
+ To regenerate the UI dynamically, in response to user action, create a separate endpoint:
187
152
 
188
153
  ```jsx
189
- import { rerenderAction } from "@/lib/getsyntux/RerenderHandler"; // preinstalled
154
+ <GeneratedUI
155
+ endpoint="/api/syntux"
156
+ rerenderEndpoint="/api/syntux/rerender" /* <-- over here */
190
157
 
191
- <GeneratedUI
192
- model={anthropic("claude-sonnet-4-5")}
193
- value={valueToDisplay}
194
- rerender={rerenderAction}
158
+ value={value}
159
+ hint="display as a profile card"
195
160
  />
196
161
  ```
197
162
 
198
- Inside a custom component, use the `useSyntux` hook and provide a hint to regenerate the UI:
163
+ Then use the `useSyntux` hook:
199
164
 
200
165
  ```jsx
201
166
  "use client";
@@ -217,60 +182,39 @@ export default function CustomComponent() {
217
182
  );
218
183
  }
219
184
  ```
185
+ <h3 align="center" margin="0"><a href="https://docs.getsyntux.com/">➡️ view documentation</a></h3>
220
186
 
221
- The new user interface will be streamed.
222
-
223
- #### Customize animation
224
-
225
- By default, new elements fade in from below when mounted.
226
-
227
- This motion cannot yet be customized. However, the duration and offset can, using the `animate` property:
228
-
229
- ```jsx
230
- <GeneratedUI
231
- model={anthropic("claude-sonnet-4-5")}
232
- value={valueToDisplay}
233
- animate={{
234
- offset: 10, // pixels
235
- duration: 100 // ms
236
- }}
237
- />
238
- ```
239
-
240
- In order to disable the animation, set `offset` to 0 or `duration` to 0.
241
187
 
242
188
  ---
243
189
 
244
190
  ### FAQ
245
191
 
246
- <details>
247
- <summary>How expensive is generation?</summary>
248
-
249
- *syntux* is highly optimized to save tokens. See [here](https://docs.getsyntux.com/advanced#cost-estimation) for a cost estimation table and an explanation.
250
- </details>
251
-
252
192
  <details>
253
193
  <summary>How does generation work? (Does it generate source code?)</summary>
254
194
 
255
- Generated UIs must be *secure*, *reusable* and *cacheable*.
195
+ Generated interfaces must be *secure*, *reusable* and *cacheable*.
256
196
 
257
- As such, *syntux* does not generate source code. It generates a schema for the UI, known as a "React Interface Schema" (RIS). See the question below to get a better understanding.
197
+ As such, syntux does not:
198
+ - generate code (HTML/JSX), or...
199
+ - hardcode the `value`
258
200
 
259
- This schema is tailored to the `value` that you provide. It is then hydrated by *syntux* and rendered.
201
+ Instead, syntux generates a JSON-DSL representation of the UI, known as the React Interface Schema (RIS).
260
202
 
261
- ![](https://raw.githubusercontent.com/puffinsoft/syntux/HEAD/docs/images/workflow.png)
262
- </details>
203
+ The RIS **does not hardcode values**. It **binds** to properties of the `value` and has **built-in iterators**, making it reusable and token-efficient for arrays.
263
204
 
264
- <details>
265
- <summary>How does caching work?</summary>
205
+ An example of the RIS:
266
206
 
267
- The generated UI is determined by the React Interface Schema (see the above question).
207
+ ```json
208
+ {"id":"loop_1", "parentId":"root", "type":"__ForEach__", "props":{"source":"authors"}}
209
+ {"id":"card_1", "parentId":"loop_1", "type":"div", "props":{"className":"card"}, "content": {"$bind": "$item.name"}}
210
+ ```
268
211
 
269
- Thus, if the same schema is provided, the same UI will be generated.
212
+ </details>
270
213
 
271
- For simplicity, the schema is simply a string. It is up to you how you wish to store it; in memory, in a file, in a database etc,.
214
+ <details>
215
+ <summary>How expensive is generation?</summary>
272
216
 
273
- Use the `onGenerate` and `cached` properties to retrieve/provide a cached schema respectively.
217
+ *syntux* is highly optimized to save tokens. See [here](https://docs.getsyntux.com/usage/advanced#cost-estimation) for a cost estimation table and an explanation.
274
218
  </details>
275
219
 
276
220
  <details>
@@ -0,0 +1,87 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { JSX } from 'react';
3
+
4
+ type SchemaNode = {
5
+ id: string;
6
+ parentId: string | null;
7
+ type: string;
8
+ props?: Record<string, any>;
9
+ content?: any | {
10
+ "$bind": string;
11
+ };
12
+ };
13
+ type ComponentMap = Record<string, SchemaNode>;
14
+ type ChildrenMap = Record<string, string[]>;
15
+ type UISchema = {
16
+ componentMap: ComponentMap;
17
+ childrenMap: ChildrenMap;
18
+ root: SchemaNode | null;
19
+ };
20
+ /**
21
+ * for providing context on custom components
22
+ * used for AllowedComponents and ComponentContext
23
+ */
24
+ type ComponentMetadata = {
25
+ name: string;
26
+ props: string;
27
+ context?: string;
28
+ };
29
+ type SyntuxComponent = ComponentMetadata & {
30
+ component: React.ComponentType<any>;
31
+ };
32
+ type AnimateOptions = {
33
+ offset: number;
34
+ duration: number;
35
+ };
36
+ /**
37
+ * setValue will not send a request if regenerate is false.
38
+ * however, the value will still be updated (statically).
39
+ * as opposed to a falsy options existence check, this is more robust for DX.
40
+ */
41
+ type RerenderOptions = {
42
+ regenerate: boolean;
43
+ hint: string;
44
+ };
45
+ type RerenderContext = {
46
+ context: string;
47
+ endpoint?: string;
48
+ };
49
+
50
+ interface GeneratedUIProps {
51
+ value: any;
52
+ endpoint: string;
53
+ hint?: string;
54
+ components?: (SyntuxComponent | string)[];
55
+ placeholder?: JSX.Element;
56
+ cached?: string;
57
+ onGenerate?: (schema: string) => void;
58
+ skeletonize?: boolean;
59
+ errorFallback?: JSX.Element;
60
+ animate?: AnimateOptions;
61
+ rerenderEndpoint?: string;
62
+ }
63
+ /**
64
+ * Section of user interface for LLM to generate.
65
+ *
66
+ * Required:
67
+ * @param value The value (object, primitive, or array) to be displayed.
68
+ * @param endpoint The relative URL endpoint created with createSyntuxHandler.
69
+ *
70
+ * Optional:
71
+ * @param hint Custom instructions for the LLM.
72
+ * @param components List of allowed components that the LLM can use.
73
+ * @param placeholder Element to be displayed whilst awaiting streaming to begin.
74
+ * @param errorFallback Element to be displayed if an error occurs.
75
+ * @param animate configuration for on-mount animation
76
+ * @param rerenderEndpoint The relative URL endpoint for regeneration.
77
+ *
78
+ * Caching:
79
+ * @param cached Pre-generated schema string (from onGenerate), skips API call.
80
+ * @param onGenerate Callback which accepts the generated schema, for reuse.
81
+ *
82
+ * Advanced:
83
+ * @param skeletonize compresses the value for large inputs (arrays) or untrusted input
84
+ */
85
+ declare function GeneratedUI(props: GeneratedUIProps): react_jsx_runtime.JSX.Element;
86
+
87
+ export { type AnimateOptions as A, type ComponentMetadata as C, type GeneratedUIProps as G, type RerenderContext as R, type SyntuxComponent as S, type UISchema as U, type ChildrenMap as a, type ComponentMap as b, type RerenderOptions as c, type SchemaNode as d, GeneratedUI as e };
package/dist/client.d.mts CHANGED
@@ -1,19 +1,22 @@
1
+ import { A as AnimateOptions, R as RerenderContext, b as ComponentMap, a as ChildrenMap, c as RerenderOptions } from './GeneratedUI-DFOjlyg6.mjs';
2
+ export { e as GeneratedUI, G as GeneratedUIProps } from './GeneratedUI-DFOjlyg6.mjs';
1
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { StreamableValue } from '@ai-sdk/rsc';
3
4
  import * as react from 'react';
4
5
  import react__default, { JSX, ComponentType } from 'react';
5
- import { A as AnimateOptions, R as RerenderContext, a as ComponentMap, C as ChildrenMap, b as RerenderOptions } from './types-0XgxLwqz.mjs';
6
6
 
7
7
  /**
8
- * Client wrapper for Renderer that handles streaming and parsing with server.
8
+ * Internal client component that handles streaming, parsing, and rendering.
9
+ * For most use cases, use GeneratedUI instead.
9
10
  */
10
- declare function GeneratedClient({ value, allowedComponents, inputStream, placeholder, errorFallback, animate, rerender }: {
11
+ declare function GeneratedClient({ value, allowedComponents, endpoint, fetchBody, placeholder, errorFallback, animate, onGenerate, rerender, }: {
11
12
  value: any;
12
13
  allowedComponents: Record<string, react__default.ComponentType<any> | string>;
13
- inputStream: StreamableValue<string>;
14
+ endpoint: string;
15
+ fetchBody: object;
14
16
  placeholder?: JSX.Element;
15
17
  errorFallback?: JSX.Element;
16
18
  animate?: AnimateOptions;
19
+ onGenerate?: (schema: string) => void;
17
20
  rerender: RerenderContext;
18
21
  }): react_jsx_runtime.JSX.Element;
19
22
 
@@ -33,7 +36,7 @@ declare function Renderer(props: RendererProps): react_jsx_runtime.JSX.Element;
33
36
 
34
37
  type SyntuxContextType = {
35
38
  value: any;
36
- setValue: (value: any, options?: RerenderOptions) => Promise<null> | null;
39
+ setValue: (value: any, options?: RerenderOptions) => void;
37
40
  };
38
41
  declare const SyntuxContext: react.Context<SyntuxContextType>;
39
42
  declare function useSyntux(): SyntuxContextType;
package/dist/client.mjs CHANGED
@@ -1,3 +1,11 @@
1
- "use client";import{readStreamableValue as W}from"@ai-sdk/rsc";import{useEffect as J,useMemo as z,useReducer as B,useRef as K,useState as I}from"react";var R=class{buffer="";total="";schema={childrenMap:{},componentMap:{},root:null};addDelta(t){this.total+=t,this.buffer+=t;let e=this.buffer.split(`
2
- `);return e.length>1?(e.slice(0,e.length-1).forEach(n=>this.handleLine(n)),this.buffer=e[e.length-1],!0):!1}handleLine(t){try{let e=JSON.parse(t),{childrenMap:n,componentMap:a}=this.schema;a[e.id]=e,e.parentId===null?this.schema.root=e:(n[e.parentId]||(n[e.parentId]=[]),n[e.parentId].push(e.id))}catch{}}finish(){this.handleLine(this.buffer),this.buffer=""}};import{Fragment as D,useEffect as G,useState as Y}from"react";import{Fragment as F,jsx as h}from"react/jsx-runtime";import{createElement as j}from"react";var T=(r,t)=>t==="$"?r:t.split(".").reduce((e,n)=>e==null?void 0:e[n],r),E=(r,t,e)=>e.startsWith("$item.")?(e=e.slice(6),T(t,e)):e==="$item"?t:T(r,e),$=new Set(["dangerouslySetInnerHTML"]),U=(r,t,e)=>{if(!e)return e;if("$bind"in e){let n=E(r,t,e.$bind);return Object.keys(n).forEach(a=>{$.has(a)&&delete n[a]}),n}return Object.keys(e).forEach(n=>{if($.has(n)){delete e[n];return}let a=e[n];typeof a=="object"&&(e[n]=U(r,t,a))}),e},N=(r,t,e)=>typeof e=="object"?E(r,t,e.$bind):e;function v(r){var S,u,y;let[t,e]=Y(!1);G(()=>{let o=requestAnimationFrame(()=>e(!0));return()=>cancelAnimationFrame(o)},[]);let{id:n,componentMap:a,childrenMap:b,global:p,local:m,allowedComponents:g,animate:i}=r,s=a[n];if(s.type==="TEXT")return h(F,{children:N(p,m,s.content)});let x=(S=s.props)==null?void 0:S.source;if(s.type==="__ForEach__"&&x){let o=E(p,m,x);if(!Array.isArray(o))return null;let f=b[s.id];return h(F,{children:f==null?void 0:f.map((L,X)=>h(D,{children:o.map((_,k)=>j(v,{...r,id:L,local:_,key:k}))},X))})}let l=g[s.type]||s.type,c={...U(p,m,s.props)};c.style={...c.style||{}};let d=((u=c.style)==null?void 0:u.opacity)??1;c.style.opacity=t?d:0,c.style.transform=t?"translateY(0)":`translateY(${(i==null?void 0:i.offset)??10}px)`,c.style.transition=`opacity ${(i==null?void 0:i.duration)??200}ms ease-out, transform ${(i==null?void 0:i.duration)??200}ms ease-out`,c.style.willChange="opacity, transform";let P=N(p,m,s.content),w=((y=b[s.id])==null?void 0:y.map((o,f)=>h(v,{...r,id:o},f)))||[],M=[P,...w].filter(o=>o!=null);return M.length>0?h(l,{...c,children:M}):h(l,{...c})}import{createContext as q,useContext as H}from"react";var O=q(null);function se(){let r=H(O);if(!r)throw new Error("useSyntux must be used inside a GeneratedUI.");return r}import{Fragment as V,jsx as C}from"react/jsx-runtime";function ye({value:r,allowedComponents:t,inputStream:e,placeholder:n,errorFallback:a,animate:b,rerender:p}){var S;let[m,g]=I(r),[i,s]=I(e),[,x]=B(u=>u+1,0),l=K(null),[A,c]=I(!1);J(()=>{s(e)},[e]),J(()=>{let u=!0;return l.current=new R,(async()=>{var o;try{for await(let f of W(i)){if(!u)break;l.current&&f!==void 0&&l.current.addDelta(f)&&x()}u&&((o=l.current)==null||o.finish(),x())}catch{u&&c(!0)}})(),()=>{u=!1}},[i]);let d=(S=l==null?void 0:l.current)==null?void 0:S.schema,P=()=>A&&a?C(V,{children:a}):d!=null&&d.root?C(v,{id:d.root.id,componentMap:d.componentMap,childrenMap:d.childrenMap,allowedComponents:t,global:m,local:m,animate:b}):C(V,{children:n}),w=async(u,y)=>{if(!y||!y.regenerate)g(u);else if(p.action){if(l.current)return g(u),new Promise(async o=>{let{value:f}=await p.action(p.context,l.current.total,y.hint);s(f),o(null)})}else throw new Error("No rerender server action provided. Use the 'rerender' prop.")},M=z(()=>({value:m,setValue:w}),[m]);return C(V,{children:C(O.Provider,{value:M,children:P()})})}export{ye as GeneratedClient,v as Renderer,O as SyntuxContext,se as useSyntux};
1
+ "use client";var S=class{buffer="";total="";schema={childrenMap:{},componentMap:{},root:null};addDelta(n){this.total+=n,this.buffer+=n;let e=this.buffer.split(`
2
+ `);return e.length>1?(e.slice(0,e.length-1).forEach(r=>this.handleLine(r)),this.buffer=e[e.length-1],!0):!1}handleLine(n){try{let e=JSON.parse(n),{childrenMap:r,componentMap:a}=this.schema;a[e.id]=e,e.parentId===null?this.schema.root=e:(r[e.parentId]||(r[e.parentId]=[]),r[e.parentId].push(e.id))}catch{}}finish(){this.handleLine(this.buffer),this.buffer=""}};function j(t){return t.reduce((n,e)=>typeof e=="string"?(n[e]=e,n):(n[e.name]=e.component,n),{})}function K({value:t,skeletonize:n=!1,components:e,hint:r}){let a=(e==null?void 0:e.map(o=>typeof o=="string"?o:o.name).join(","))||"",m=e==null?void 0:e.filter(o=>typeof o!="string"),y=(m==null?void 0:m.map(o=>o.context?`${o.name} [props: ${o.props}, details: ${o.context}]`:`${o.name} [props: ${o.props}]`).join(","))||"",p=JSON.stringify(n?T(t):t);return`<AllowedComponents>${a}</AllowedComponents>
3
+ <ComponentContext>${y}</ComponentContext>
4
+ <UserContext>${r||""}</UserContext>
5
+ <IsSkeleton>${n.toString()}</IsSkeleton>
6
+ <Value>
7
+ ${p}
8
+ </Value>`}function N(t){return K(t).split(`
9
+ `).slice(0,2).join(`
10
+ `)}function T(t){return t===null?"null":typeof t!="object"?typeof t:Array.isArray(t)?t.length==0?"null":[T(t[0])]:Object.entries(t).reduce((n,[e,r])=>(n[e]=T(r),n),{})}import{useCallback as oe,useEffect as ie,useMemo as ae,useReducer as se,useRef as le,useState as V}from"react";import{Fragment as Q,useEffect as Z,useState as ee}from"react";import{Fragment as _,jsx as M}from"react/jsx-runtime";import{createElement as te}from"react";var X=(t,n)=>n==="$"?t:n.split(".").reduce((e,r)=>e==null?void 0:e[r],t),U=(t,n,e)=>e.startsWith("$item.")?(e=e.slice(6),X(n,e)):e==="$item"?n:X(t,e),k=new Set(["dangerouslySetInnerHTML"]),D=t=>t.length>2&&t.startsWith("on")&&t[2]===t[2].toUpperCase(),B=(t,n,e)=>{if(!e)return e;if("$bind"in e){let r=U(t,n,e.$bind);return Object.keys(r).forEach(a=>{(k.has(a)||D(a)&&typeof r[a]!="function")&&delete r[a]}),r}return Object.keys(e).forEach(r=>{if(k.has(r)){delete e[r];return}let a=e[r];typeof a=="object"&&(e[r]=B(t,n,a),D(r)&&typeof e[r]!="function"&&delete e[r])}),e},L=(t,n,e)=>typeof e=="object"?U(t,n,e.$bind):e;function R(t){var I,P,v;let[n,e]=ee(!1);Z(()=>{let i=requestAnimationFrame(()=>e(!0));return()=>cancelAnimationFrame(i)},[]);let{id:r,componentMap:a,childrenMap:m,global:y,local:p,allowedComponents:o,animate:s}=t,l=a[r];if(l.type==="TEXT")return M(_,{children:L(y,p,l.content)});let C=(I=l.props)==null?void 0:I.source;if(l.type==="__ForEach__"&&C){let i=U(y,p,C);if(!Array.isArray(i))return null;let h=m[l.id];return M(_,{children:h==null?void 0:h.map((x,$)=>M(Q,{children:i.map((b,A)=>te(R,{...t,id:x,local:b,key:A}))},$))})}let d=o[l.type]||l.type,c={...B(y,p,l.props)};c.style={...c.style||{}};let g=((P=c.style)==null?void 0:P.opacity)??1;c.style.opacity=n?g:0,c.style.transform=n?"translateY(0)":`translateY(${(s==null?void 0:s.offset)??10}px)`,c.style.transition=`opacity ${(s==null?void 0:s.duration)??200}ms ease-out, transform ${(s==null?void 0:s.duration)??200}ms ease-out`,c.style.willChange="opacity, transform";let u=L(y,p,l.content),f=((v=m[l.id])==null?void 0:v.map((i,h)=>M(R,{...t,id:i},h)))||[],w=[u,...f].filter(i=>i!=null);return w.length>0?M(d,{...c,children:w}):M(d,{...c})}import{createContext as ne,useContext as re}from"react";var F=ne(null);function ge(){let t=re(F);if(!t)throw new Error("useSyntux must be used inside a GeneratedUI.");return t}import{Fragment as H,jsx as O}from"react/jsx-runtime";function z({value:t,allowedComponents:n,endpoint:e,fetchBody:r,placeholder:a,errorFallback:m,animate:y,onGenerate:p,rerender:o}){var v;let[s,l]=V(t),[,C]=se(i=>i+1,0),d=le(null),[E,c]=V(!1),[g,u]=V(()=>({url:e,body:r}));ie(()=>{let i=!0;return d.current=new S,c(!1),(async()=>{var x,$;try{let b=await fetch(g.url,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(g.body)});if(!b.ok||!b.body)throw new Error(`HTTP ${b.status}`);let A=b.body.getReader(),W=new TextDecoder;for(;;){let{done:Y,value:q}=await A.read();if(!i||Y)break;let J=W.decode(q);d.current&&J!==void 0&&d.current.addDelta(J)&&C()}i&&((x=d.current)==null||x.finish(),C(),p==null||p((($=d.current)==null?void 0:$.total)??""))}catch{i&&c(!0)}})(),()=>{i=!1}},[g]);let f=(v=d.current)==null?void 0:v.schema,w=oe((i,h)=>{var x;if(!h||!h.regenerate)l(i);else{if(!o.endpoint)throw new Error("No rerenderEndpoint provided. Pass rerenderEndpoint to <GeneratedUI>.");l(i),u({url:o.endpoint,body:{context:o.context,existing:((x=d.current)==null?void 0:x.total)??"",hint:h.hint}})}},[o.endpoint,o.context]),I=ae(()=>({value:s,setValue:w}),[s,w]),P=()=>E&&m?O(H,{children:m}):f!=null&&f.root?O(R,{id:f.root.id,componentMap:f.componentMap,childrenMap:f.childrenMap,allowedComponents:n,global:s,local:s,animate:y}):O(H,{children:a});return O(F.Provider,{value:I,children:P()})}import{Fragment as ce,jsx as G}from"react/jsx-runtime";function Te(t){let{endpoint:n,value:e,hint:r,components:a,skeletonize:m,placeholder:y,cached:p,onGenerate:o,errorFallback:s,animate:l,rerenderEndpoint:C}=t,d=j(a||[]);if(p){let u=new S;u.addDelta(p),u.finish();let f=u.schema;return f.root?G(R,{id:f.root.id,componentMap:f.componentMap,childrenMap:f.childrenMap,allowedComponents:d,global:e,local:e,animate:l}):G(ce,{})}let E=(a||[]).map(u=>typeof u=="string"?u:{name:u.name,props:u.props,context:u.context}),c={value:e,hint:r,components:E,skeletonize:m},g=N(t);return G(z,{value:e,allowedComponents:d,endpoint:n,fetchBody:c,placeholder:y,errorFallback:s,animate:l,onGenerate:o,rerender:{context:g,endpoint:C}})}export{z as GeneratedClient,Te as GeneratedUI,R as Renderer,F as SyntuxContext,ge as useSyntux};
3
11
  //# sourceMappingURL=client.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/GeneratedClient.tsx","../src/ResponseParser.ts","../src/client/Renderer.tsx","../src/client/SyntuxContext.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport { StreamableValue, readStreamableValue } from '@ai-sdk/rsc';\r\nimport React, { JSX, useEffect, useMemo, useReducer, useRef, useState } from 'react';\r\nimport { AnimateOptions, RerenderContext, RerenderOptions } from 'src/types';\r\nimport { ResponseParser } from 'src/ResponseParser';\r\nimport { Renderer } from './Renderer';\r\nimport { SyntuxContext } from './SyntuxContext';\r\n\r\n\r\n\r\n/**\r\n * Client wrapper for Renderer that handles streaming and parsing with server.\r\n */\r\nexport function GeneratedClient({\r\n value,\r\n allowedComponents,\r\n inputStream,\r\n placeholder,\r\n errorFallback,\r\n animate,\r\n rerender\r\n}: {\r\n value: any,\r\n allowedComponents: Record<string, React.ComponentType<any> | string>,\r\n inputStream: StreamableValue<string>,\r\n placeholder?: JSX.Element,\r\n errorFallback?: JSX.Element,\r\n animate?: AnimateOptions,\r\n rerender: RerenderContext\r\n}) {\r\n const [statefulValue, setStatefulValue] = useState(value); // stateful because changeable through context\r\n const [statefulInputStream, setStatefulInputStream] = useState(inputStream);\r\n\r\n const [, forceUpdate] = useReducer(x => x + 1, 0);\r\n const parser = useRef<ResponseParser | null>(null);\r\n const [errored, setErrored] = useState(false)\r\n\r\n // HMR support\r\n useEffect(() => {\r\n setStatefulInputStream(inputStream)\r\n }, [inputStream])\r\n\r\n useEffect(() => {\r\n /**\r\n * flag to avoid conflicting streams from mutating UI.\r\n */\r\n let isActive = true;\r\n\r\n // forcibly create a new one for HMR\r\n parser.current = new ResponseParser();\r\n\r\n const parseStream = async () => {\r\n try {\r\n for await (const delta of readStreamableValue(statefulInputStream)) {\r\n if (!isActive) break;\r\n\r\n if (parser.current && delta !== undefined) {\r\n if (parser.current.addDelta(delta)) {\r\n forceUpdate();\r\n }\r\n }\r\n }\r\n\r\n if (isActive) {\r\n parser.current?.finish();\r\n forceUpdate();\r\n }\r\n } catch (err) {\r\n if (isActive) setErrored(true);\r\n }\r\n };\r\n\r\n parseStream();\r\n\r\n return () => {\r\n isActive = false;\r\n }\r\n }, [statefulInputStream]);\r\n\r\n const schema = parser?.current?.schema;\r\n\r\n\r\n const renderContent = () => {\r\n if (errored && errorFallback) return <>{errorFallback}</>\r\n\r\n if (schema?.root) {\r\n return <Renderer id={schema.root.id} componentMap={schema.componentMap} childrenMap={schema.childrenMap} allowedComponents={allowedComponents} global={statefulValue} local={statefulValue} animate={animate} />\r\n } else {\r\n return <>{placeholder}</>\r\n }\r\n }\r\n\r\n const modifyValue = async (value: any, options?: RerenderOptions): Promise<null> | null => {\r\n if (!options || !options.regenerate) {\r\n setStatefulValue(value);\r\n } else {\r\n if (!rerender.action) {\r\n throw new Error(\"No rerender server action provided. Use the 'rerender' prop.\")\r\n } else {\r\n if (parser.current) {\r\n setStatefulValue(value);\r\n return new Promise(async (resolve) => {\r\n const { value } = await rerender.action(rerender.context, parser.current.total, options.hint);\r\n setStatefulInputStream(value);\r\n resolve(null);\r\n })\r\n }\r\n }\r\n }\r\n }\r\n\r\n const providerValue = useMemo(() => ({\r\n value: statefulValue, setValue: modifyValue\r\n }), [statefulValue]);\r\n return (\r\n <>\r\n <SyntuxContext.Provider value={providerValue}>\r\n {renderContent()}\r\n </SyntuxContext.Provider >\r\n </>\r\n )\r\n}\r\n","import { SchemaNode, UISchema } from \"./types\";\r\n\r\n/**\r\n * Utility class for parsing UISchema from stream.\r\n */\r\nexport class ResponseParser {\r\n buffer = \"\"; // unflushed existing deltas w/o newline\r\n total = \"\"; // accumulator\r\n\r\n // schema assembled thus far\r\n schema: UISchema = {\r\n childrenMap: {},\r\n componentMap: {},\r\n root: null\r\n }\r\n\r\n /**\r\n * Update schema with latest data chunk.\r\n * \r\n * Handles multiline input gracefully; can be used to load entire schemas from cache.\r\n * \r\n * @param delta delta from stream.\r\n * @returns true if update is warranted, false otherwise.\r\n */\r\n addDelta(delta: string) {\r\n this.total += delta;\r\n this.buffer += delta;\r\n const split = this.buffer.split(\"\\n\")\r\n if (split.length > 1) {\r\n split.slice(0, split.length - 1).forEach((line) => this.handleLine(line));\r\n this.buffer = split[split.length - 1];\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Parses a single line (full JSON object) and updates schema.\r\n * Generally should not be used when streaming data.\r\n */\r\n handleLine(line: string) {\r\n try {\r\n const node: SchemaNode = JSON.parse(line);\r\n\r\n const { childrenMap, componentMap } = this.schema;\r\n\r\n componentMap[node.id] = node;\r\n if (node.parentId === null) {\r\n this.schema.root = node;\r\n } else {\r\n if (!childrenMap[node.parentId]) childrenMap[node.parentId] = []\r\n childrenMap[node.parentId].push(node.id)\r\n }\r\n } catch (err) { /* probably markdown or generation inconsistency */ }\r\n }\r\n\r\n /**\r\n * Clears the buffer and handles any remaining information within.\r\n */\r\n finish(){\r\n this.handleLine(this.buffer);\r\n this.buffer = \"\";\r\n }\r\n}","\"use client\";\r\n\r\nimport { ComponentType, Fragment, useEffect, useState } from 'react';\r\nimport { AnimateOptions, ChildrenMap, ComponentMap } from '../types';\r\n\r\n/**\r\n * lightweight implementation of lodash.get\r\n */\r\nconst resolvePath = (obj: any, path: string) => {\r\n if (path === '$') return obj;\r\n return path.split('.').reduce((acc, curr) => acc?.[curr], obj)\r\n}\r\n\r\n/**\r\n * parses binding protocol and performs property lookup w/ scope resolution\r\n */\r\nconst get = (global: any, local: any, path: string) => {\r\n if (path.startsWith(\"$item.\")) {\r\n path = path.slice(6)\r\n return resolvePath(local, path);\r\n } else {\r\n if (path === \"$item\") return local;\r\n return resolvePath(global, path);\r\n }\r\n}\r\n\r\n\r\nconst blacklistedProps = new Set([\"dangerouslySetInnerHTML\"])\r\n/**\r\n * recursively parses props for bindings, replacing with true values\r\n */\r\nconst resolveProps = (global: any, local: any, props: any) => {\r\n if (!props) return props;\r\n\r\n if (\"$bind\" in props) { // $bind may be falsy value\r\n const resolved = get(global, local, props.$bind);\r\n Object.keys(resolved).forEach((key) => {\r\n if (blacklistedProps.has(key)) {\r\n delete resolved[key];\r\n }\r\n })\r\n return resolved;\r\n }\r\n\r\n Object.keys(props).forEach((key) => {\r\n if (blacklistedProps.has(key)) {\r\n delete props[key];\r\n return;\r\n }\r\n\r\n const val = props[key];\r\n if (typeof val === \"object\") {\r\n props[key] = resolveProps(global, local, val);\r\n }\r\n })\r\n return props;\r\n}\r\n\r\n/**\r\n * output node.content, with check for $bind\r\n*/\r\nconst renderContent = (global: any, local: any, content: any) => {\r\n if (typeof content === \"object\") {\r\n return get(global, local, content.$bind);\r\n } else {\r\n return content;\r\n }\r\n}\r\n\r\nexport interface RendererProps {\r\n id: string;\r\n componentMap: ComponentMap;\r\n childrenMap: ChildrenMap;\r\n allowedComponents: Record<string, ComponentType<any> | string>;\r\n global: any;\r\n local: any;\r\n animate?: AnimateOptions;\r\n}\r\n\r\n/**\r\n * Renders a UISchema recursively, in accordance to the spec.\r\n */\r\nexport function Renderer(props: RendererProps) {\r\n const [isVisible, setIsVisible] = useState(false);\r\n\r\n useEffect(() => {\r\n const frame = requestAnimationFrame(() => setIsVisible(true));\r\n return () => cancelAnimationFrame(frame)\r\n }, [])\r\n\r\n const {\r\n id, componentMap, childrenMap, global, local, allowedComponents, animate\r\n } = props;\r\n const element = componentMap[id];\r\n\r\n if (element.type === \"TEXT\") return <>{renderContent(global, local, element.content)}</>\r\n\r\n const sourceArrPath = element.props?.source;\r\n if (element.type === '__ForEach__' && sourceArrPath) {\r\n const sourceArr = get(global, local, sourceArrPath)\r\n if (!Array.isArray(sourceArr)) return null;\r\n\r\n const childrenArr = childrenMap[element.id];\r\n return <>{childrenArr?.map((childId: string, index: number) => <Fragment key={index}>\r\n {sourceArr.map((item: any, index1: number) => <Renderer {...props} id={childId} local={item} key={index1} />)}\r\n </Fragment>)}</>\r\n }\r\n\r\n const Component = allowedComponents[element.type] || element.type;\r\n const componentProps = resolveProps(global, local, element.props);\r\n\r\n const animatedProps = {...componentProps}\r\n animatedProps.style = {...(animatedProps.style) || {}}\r\n\r\n const initialOpacity = animatedProps.style?.opacity ?? 1;\r\n animatedProps.style.opacity = isVisible ? initialOpacity : 0;\r\n animatedProps.style.transform = isVisible ? 'translateY(0)' : `translateY(${animate?.offset ?? 10}px)`;\r\n animatedProps.style.transition = `opacity ${animate?.duration ?? 200}ms ease-out, transform ${animate?.duration ?? 200}ms ease-out`;\r\n animatedProps.style.willChange = 'opacity, transform';\r\n\r\n const contentNode = renderContent(global, local, element.content);\r\n const childNodes = childrenMap[element.id]?.map((childId: string, index: number) => {\r\n return <Renderer\r\n key={index}\r\n {...props}\r\n id={childId}\r\n />\r\n }) || []\r\n\r\n const nodesToRender = [contentNode, ...childNodes].filter(node => node !== null && node !== undefined) // 0 is falsy\r\n\r\n if (nodesToRender.length > 0) {\r\n return <Component {...animatedProps}>\r\n {nodesToRender}\r\n </Component>\r\n }\r\n\r\n return <Component {...animatedProps}/>\r\n}\r\n","import { createContext, useContext } from \"react\";\r\nimport { RerenderOptions } from \"src/types\";\r\n\r\nexport type SyntuxContextType = {\r\n value: any,\r\n setValue: (value: any, options?: RerenderOptions) => Promise<null> | null\r\n}\r\n\r\nexport const SyntuxContext = createContext<SyntuxContextType | null>(null)\r\n\r\nexport function useSyntux(){\r\n const context = useContext(SyntuxContext);\r\n if(!context) throw new Error(\"useSyntux must be used inside a GeneratedUI.\");\r\n return context;\r\n}"],"mappings":"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","names":["readStreamableValue","useEffect","useMemo","useReducer","useRef","useState","ResponseParser","delta","split","line","node","childrenMap","componentMap","Fragment","useEffect","useState","jsx","createElement","resolvePath","obj","path","acc","curr","get","global","local","blacklistedProps","resolveProps","props","resolved","key","val","renderContent","content","Renderer","_a","_b","_c","isVisible","setIsVisible","frame","id","componentMap","childrenMap","allowedComponents","animate","element","sourceArrPath","sourceArr","childrenArr","childId","index","item","index1","Component","animatedProps","initialOpacity","contentNode","childNodes","nodesToRender","node","createContext","useContext","SyntuxContext","useSyntux","context","Fragment","jsx","GeneratedClient","value","allowedComponents","inputStream","placeholder","errorFallback","animate","rerender","_a","statefulValue","setStatefulValue","useState","statefulInputStream","setStatefulInputStream","forceUpdate","useReducer","x","parser","useRef","errored","setErrored","useEffect","isActive","ResponseParser","delta","readStreamableValue","schema","renderContent","Renderer","modifyValue","options","resolve","providerValue","useMemo","SyntuxContext"]}
1
+ {"version":3,"sources":["../src/ResponseParser.ts","../src/util.ts","../src/client/GeneratedClient.tsx","../src/client/Renderer.tsx","../src/client/SyntuxContext.tsx","../src/client/GeneratedUI.tsx"],"sourcesContent":["import { SchemaNode, UISchema } from \"./types\";\r\n\r\n/**\r\n * Utility class for parsing UISchema from stream.\r\n */\r\nexport class ResponseParser {\r\n buffer = \"\"; // unflushed existing deltas w/o newline\r\n total = \"\"; // accumulator\r\n\r\n // schema assembled thus far\r\n schema: UISchema = {\r\n childrenMap: {},\r\n componentMap: {},\r\n root: null\r\n }\r\n\r\n /**\r\n * Update schema with latest data chunk.\r\n * \r\n * Handles multiline input gracefully; can be used to load entire schemas from cache.\r\n * \r\n * @param delta delta from stream.\r\n * @returns true if update is warranted, false otherwise.\r\n */\r\n addDelta(delta: string) {\r\n this.total += delta;\r\n this.buffer += delta;\r\n const split = this.buffer.split(\"\\n\")\r\n if (split.length > 1) {\r\n split.slice(0, split.length - 1).forEach((line) => this.handleLine(line));\r\n this.buffer = split[split.length - 1];\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Parses a single line (full JSON object) and updates schema.\r\n * Generally should not be used when streaming data.\r\n */\r\n handleLine(line: string) {\r\n try {\r\n const node: SchemaNode = JSON.parse(line);\r\n\r\n const { childrenMap, componentMap } = this.schema;\r\n\r\n componentMap[node.id] = node;\r\n if (node.parentId === null) {\r\n this.schema.root = node;\r\n } else {\r\n if (!childrenMap[node.parentId]) childrenMap[node.parentId] = []\r\n childrenMap[node.parentId].push(node.id)\r\n }\r\n } catch (err) { /* probably markdown or generation inconsistency */ }\r\n }\r\n\r\n /**\r\n * Clears the buffer and handles any remaining information within.\r\n */\r\n finish(){\r\n this.handleLine(this.buffer);\r\n this.buffer = \"\";\r\n }\r\n}","import { GeneratedUIProps } from \"./client\";\r\nimport { ComponentMetadata, SyntuxComponent } from \"./types\";\r\n\r\n/**\r\n * Converts a list of components into a dictionary for fast-retrieval\r\n * during rendering.\r\n */\r\nexport function generateComponentMap(allowedComponents: (SyntuxComponent | string)[]) {\r\n return allowedComponents.reduce((acc: Record<string, React.ComponentType<any> | string>, curr: SyntuxComponent | string) => {\r\n if (typeof curr === \"string\") {\r\n acc[curr] = curr;\r\n return acc;\r\n }\r\n\r\n acc[curr.name] = curr.component;\r\n return acc;\r\n }, {})\r\n}\r\n\r\n/**\r\n * Creates LLM input in accordance to the spec.\r\n */\r\nexport function constructInput({\r\n value, skeletonize = false, components, hint\r\n}: {\r\n value: any;\r\n components?: (ComponentMetadata | string)[];\r\n hint?: string;\r\n skeletonize?: boolean;\r\n}) {\r\n const allowedComponents = components?.map((item: ComponentMetadata | string) => {\r\n if (typeof item === \"string\") return item;\r\n return item.name;\r\n }).join(',') || \"\"\r\n\r\n const customComponents = components?.filter((item): item is ComponentMetadata => typeof item !== \"string\");\r\n const componentContext = customComponents?.map((item) => {\r\n if (!item.context) {\r\n return `${item.name} [props: ${item.props}]`\r\n } else {\r\n return `${item.name} [props: ${item.props}, details: ${item.context}]`\r\n }\r\n }).join(',') || \"\"\r\n\r\n const inputValue = JSON.stringify(skeletonize ? createSkeleton(value) : value)\r\n\r\n return `<AllowedComponents>${allowedComponents}</AllowedComponents>\\n<ComponentContext>${componentContext}</ComponentContext>\\n<UserContext>${hint || \"\"}</UserContext>\\n<IsSkeleton>${skeletonize.toString()}</IsSkeleton>\\n<Value>\\n${inputValue}\\n</Value>`\r\n}\r\n\r\n/**\r\n * Builds the AllowedComponents + ComponentContext header used as context for rerender requests.\r\n */\r\nexport function constructRerenderContext(props: GeneratedUIProps) {\r\n return constructInput(props).split('\\n').slice(0, 2).join('\\n');\r\n}\r\n\r\n/**\r\n * generates a skeleton of the input value, ideal for large arrays or untrusted input.\r\n * see the FAQ for more information: https://github.com/puffinsoft/syntux/wiki/FAQ#handling-untrusted-input--large-arrays.\r\n *\r\n * *important*: assumes arrays are non-polymorphic\r\n */\r\nexport function createSkeleton(input: any) {\r\n if (input === null) return \"null\";\r\n\r\n if (typeof input !== \"object\") return typeof input;\r\n\r\n if (Array.isArray(input)) {\r\n if (input.length == 0) {\r\n return \"null\"; // ignore this field completely\r\n } else {\r\n return [createSkeleton(input[0])]\r\n }\r\n }\r\n return Object.entries(input).reduce((acc, [key, value]) => {\r\n acc[key] = createSkeleton(value);\r\n return acc;\r\n }, {})\r\n}\r\n","\"use client\";\r\n\r\nimport React, { JSX, useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';\r\nimport { AnimateOptions, RerenderContext, RerenderOptions } from '../types';\r\nimport { ResponseParser } from '../ResponseParser';\r\nimport { Renderer } from './Renderer';\r\nimport { SyntuxContext } from './SyntuxContext';\r\n\r\n// stateful, see below\r\ntype FetchConfig = {\r\n url: string;\r\n body: object;\r\n};\r\n\r\n/**\r\n * Internal client component that handles streaming, parsing, and rendering.\r\n * For most use cases, use GeneratedUI instead.\r\n */\r\nexport function GeneratedClient({\r\n value,\r\n allowedComponents,\r\n endpoint,\r\n fetchBody,\r\n placeholder,\r\n errorFallback,\r\n animate,\r\n onGenerate,\r\n rerender,\r\n}: {\r\n value: any;\r\n allowedComponents: Record<string, React.ComponentType<any> | string>;\r\n endpoint: string;\r\n fetchBody: object;\r\n placeholder?: JSX.Element;\r\n errorFallback?: JSX.Element;\r\n animate?: AnimateOptions;\r\n onGenerate?: (schema: string) => void;\r\n rerender: RerenderContext;\r\n}) {\r\n const [statefulValue, setStatefulValue] = useState(value);\r\n const [, forceUpdate] = useReducer(x => x + 1, 0);\r\n const parser = useRef<ResponseParser | null>(null);\r\n const [errored, setErrored] = useState(false);\r\n\r\n /**\r\n * single source of truth for useEffect rerenders.\r\n * body is intentionally vague, stringified very casually later.\r\n */\r\n const [fetchConfig, setFetchConfig] = useState<FetchConfig>(() => ({ url: endpoint, body: fetchBody }));\r\n\r\n useEffect(() => {\r\n /**\r\n * flag to avoid conflicting streams from mutating UI.\r\n */\r\n let isActive = true;\r\n parser.current = new ResponseParser();\r\n setErrored(false);\r\n\r\n const initiateStream = async () => {\r\n try {\r\n const response = await fetch(fetchConfig.url, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(fetchConfig.body),\r\n });\r\n\r\n if (!response.ok || !response.body) throw new Error(`HTTP ${response.status}`);\r\n\r\n const reader = response.body.getReader();\r\n const decoder = new TextDecoder();\r\n\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (!isActive) break;\r\n if (done) break;\r\n\r\n const delta = decoder.decode(value);\r\n\r\n if(parser.current && delta !== undefined){\r\n if(parser.current.addDelta(delta)){\r\n forceUpdate();\r\n }\r\n }\r\n }\r\n\r\n if (isActive) {\r\n parser.current?.finish();\r\n forceUpdate();\r\n onGenerate?.(parser.current?.total ?? '');\r\n }\r\n } catch (err) {\r\n if (isActive) setErrored(true);\r\n }\r\n };\r\n\r\n initiateStream();\r\n return () => { isActive = false; };\r\n }, [fetchConfig]);\r\n\r\n const schema = parser.current?.schema;\r\n\r\n const modifyValue = useCallback((value: any, options?: RerenderOptions) => {\r\n if (!options || !options.regenerate) {\r\n setStatefulValue(value);\r\n } else {\r\n if (!rerender.endpoint) {\r\n throw new Error(\"No rerenderEndpoint provided. Pass rerenderEndpoint to <GeneratedUI>.\");\r\n }\r\n setStatefulValue(value);\r\n setFetchConfig({\r\n url: rerender.endpoint,\r\n body: {\r\n context: rerender.context,\r\n existing: parser.current?.total ?? '',\r\n hint: options.hint,\r\n },\r\n });\r\n }\r\n }, [rerender.endpoint, rerender.context]);\r\n\r\n const providerValue = useMemo(() => ({\r\n value: statefulValue,\r\n setValue: modifyValue,\r\n }), [statefulValue, modifyValue]);\r\n\r\n const renderContent = () => {\r\n if (errored && errorFallback) return <>{errorFallback}</>;\r\n if (schema?.root) {\r\n return <Renderer\r\n id={schema.root.id}\r\n componentMap={schema.componentMap}\r\n childrenMap={schema.childrenMap}\r\n allowedComponents={allowedComponents}\r\n global={statefulValue}\r\n local={statefulValue}\r\n animate={animate}\r\n />;\r\n }\r\n return <>{placeholder}</>;\r\n };\r\n\r\n return (\r\n <SyntuxContext.Provider value={providerValue}>\r\n {renderContent()}\r\n </SyntuxContext.Provider>\r\n );\r\n}\r\n","\"use client\";\r\n\r\nimport { ComponentType, Fragment, useEffect, useState } from 'react';\r\nimport { AnimateOptions, ChildrenMap, ComponentMap } from '../types';\r\n\r\n/**\r\n * lightweight implementation of lodash.get\r\n */\r\nconst resolvePath = (obj: any, path: string) => {\r\n if (path === '$') return obj;\r\n return path.split('.').reduce((acc, curr) => acc?.[curr], obj)\r\n}\r\n\r\n/**\r\n * parses binding protocol and performs property lookup w/ scope resolution\r\n */\r\nconst get = (global: any, local: any, path: string) => {\r\n if (path.startsWith(\"$item.\")) {\r\n path = path.slice(6)\r\n return resolvePath(local, path);\r\n } else {\r\n if (path === \"$item\") return local;\r\n return resolvePath(global, path);\r\n }\r\n}\r\n\r\n\r\nconst blacklistedProps = new Set([\"dangerouslySetInnerHTML\"])\r\n\r\n/**\r\n * LLM hallucinations sometimes cause erroneous event handler insertion.\r\n * light detection for camelCase and on[...]\r\n */\r\nconst isEventHandlerKey = (key: string) => key.length > 2 && key.startsWith('on') && key[2] === key[2].toUpperCase();\r\n\r\n/**\r\n * recursively parses props for bindings, replacing with true values\r\n */\r\nconst resolveProps = (global: any, local: any, props: any) => {\r\n if (!props) return props;\r\n\r\n if (\"$bind\" in props) { // $bind may be falsy value\r\n const resolved = get(global, local, props.$bind);\r\n Object.keys(resolved).forEach((key) => {\r\n if (blacklistedProps.has(key) || (isEventHandlerKey(key) && typeof resolved[key] !== 'function')) {\r\n delete resolved[key];\r\n }\r\n })\r\n return resolved;\r\n }\r\n\r\n Object.keys(props).forEach((key) => {\r\n if (blacklistedProps.has(key)) {\r\n delete props[key];\r\n return;\r\n }\r\n\r\n const val = props[key];\r\n if (typeof val === \"object\") {\r\n props[key] = resolveProps(global, local, val);\r\n if (isEventHandlerKey(key) && typeof props[key] !== 'function') {\r\n delete props[key];\r\n }\r\n }\r\n })\r\n return props;\r\n}\r\n\r\n/**\r\n * output node.content, with check for $bind\r\n*/\r\nconst renderContent = (global: any, local: any, content: any) => {\r\n if (typeof content === \"object\") {\r\n return get(global, local, content.$bind);\r\n } else {\r\n return content;\r\n }\r\n}\r\n\r\nexport interface RendererProps {\r\n id: string;\r\n componentMap: ComponentMap;\r\n childrenMap: ChildrenMap;\r\n allowedComponents: Record<string, ComponentType<any> | string>;\r\n global: any;\r\n local: any;\r\n animate?: AnimateOptions;\r\n}\r\n\r\n/**\r\n * Renders a UISchema recursively, in accordance to the spec.\r\n */\r\nexport function Renderer(props: RendererProps) {\r\n const [isVisible, setIsVisible] = useState(false);\r\n\r\n useEffect(() => {\r\n const frame = requestAnimationFrame(() => setIsVisible(true));\r\n return () => cancelAnimationFrame(frame)\r\n }, [])\r\n\r\n const {\r\n id, componentMap, childrenMap, global, local, allowedComponents, animate\r\n } = props;\r\n const element = componentMap[id];\r\n\r\n if (element.type === \"TEXT\") return <>{renderContent(global, local, element.content)}</>\r\n\r\n const sourceArrPath = element.props?.source;\r\n if (element.type === '__ForEach__' && sourceArrPath) {\r\n const sourceArr = get(global, local, sourceArrPath)\r\n if (!Array.isArray(sourceArr)) return null;\r\n\r\n const childrenArr = childrenMap[element.id];\r\n return <>{childrenArr?.map((childId: string, index: number) => <Fragment key={index}>\r\n {sourceArr.map((item: any, index1: number) => <Renderer {...props} id={childId} local={item} key={index1} />)}\r\n </Fragment>)}</>\r\n }\r\n\r\n const Component = allowedComponents[element.type] || element.type;\r\n const componentProps = resolveProps(global, local, element.props);\r\n\r\n const animatedProps = {...componentProps}\r\n animatedProps.style = {...(animatedProps.style) || {}}\r\n\r\n const initialOpacity = animatedProps.style?.opacity ?? 1;\r\n animatedProps.style.opacity = isVisible ? initialOpacity : 0;\r\n animatedProps.style.transform = isVisible ? 'translateY(0)' : `translateY(${animate?.offset ?? 10}px)`;\r\n animatedProps.style.transition = `opacity ${animate?.duration ?? 200}ms ease-out, transform ${animate?.duration ?? 200}ms ease-out`;\r\n animatedProps.style.willChange = 'opacity, transform';\r\n\r\n const contentNode = renderContent(global, local, element.content);\r\n const childNodes = childrenMap[element.id]?.map((childId: string, index: number) => {\r\n return <Renderer\r\n key={index}\r\n {...props}\r\n id={childId}\r\n />\r\n }) || []\r\n\r\n const nodesToRender = [contentNode, ...childNodes].filter(node => node !== null && node !== undefined) // 0 is falsy\r\n\r\n if (nodesToRender.length > 0) {\r\n return <Component {...animatedProps}>\r\n {nodesToRender}\r\n </Component>\r\n }\r\n\r\n return <Component {...animatedProps}/>\r\n}\r\n","import { createContext, useContext } from \"react\";\r\nimport { RerenderOptions } from \"src/types\";\r\n\r\nexport type SyntuxContextType = {\r\n value: any,\r\n setValue: (value: any, options?: RerenderOptions) => void\r\n}\r\n\r\nexport const SyntuxContext = createContext<SyntuxContextType | null>(null)\r\n\r\nexport function useSyntux(){\r\n const context = useContext(SyntuxContext);\r\n if(!context) throw new Error(\"useSyntux must be used inside a GeneratedUI.\");\r\n return context;\r\n}","\"use client\";\n\nimport { JSX } from 'react';\nimport { ResponseParser } from '../ResponseParser';\nimport { AnimateOptions, ComponentMetadata, SyntuxComponent, UISchema } from '../types';\nimport { constructRerenderContext, generateComponentMap } from '../util';\nimport { GeneratedClient } from './GeneratedClient';\nimport { Renderer } from './Renderer';\n\nexport interface GeneratedUIProps {\n value: any;\n endpoint: string;\n hint?: string;\n components?: (SyntuxComponent | string)[];\n placeholder?: JSX.Element;\n cached?: string;\n onGenerate?: (schema: string) => void;\n skeletonize?: boolean;\n errorFallback?: JSX.Element;\n animate?: AnimateOptions;\n rerenderEndpoint?: string;\n}\n\n/**\n * Section of user interface for LLM to generate.\n * \n * Required:\n * @param value The value (object, primitive, or array) to be displayed.\n * @param endpoint The relative URL endpoint created with createSyntuxHandler.\n * \n * Optional:\n * @param hint Custom instructions for the LLM.\n * @param components List of allowed components that the LLM can use.\n * @param placeholder Element to be displayed whilst awaiting streaming to begin.\n * @param errorFallback Element to be displayed if an error occurs.\n * @param animate configuration for on-mount animation\n * @param rerenderEndpoint The relative URL endpoint for regeneration.\n * \n * Caching:\n * @param cached Pre-generated schema string (from onGenerate), skips API call.\n * @param onGenerate Callback which accepts the generated schema, for reuse.\n * \n * Advanced:\n * @param skeletonize compresses the value for large inputs (arrays) or untrusted input\n */\nexport function GeneratedUI(props: GeneratedUIProps) {\n const {\n endpoint,\n value,\n hint,\n components,\n skeletonize,\n placeholder,\n cached,\n onGenerate,\n errorFallback,\n animate,\n rerenderEndpoint,\n } = props;\n\n const allowedComponents = generateComponentMap(components || []);\n\n // prerender if cached\n if (cached) {\n const parser = new ResponseParser();\n parser.addDelta(cached);\n parser.finish();\n\n const schema: UISchema = parser.schema;\n\n if (schema.root) {\n return <Renderer\n id={schema.root.id}\n componentMap={schema.componentMap}\n childrenMap={schema.childrenMap}\n allowedComponents={allowedComponents}\n global={value}\n local={value}\n animate={animate}\n />;\n }\n\n return <></>; // probably bad schema\n }\n\n /**\n * serialize generation information.\n */\n const componentsMetadata: (ComponentMetadata | string)[] = (components || []).map((comp: ComponentMetadata | string) => {\n if (typeof comp === 'string') return comp;\n\n return {\n name: comp.name,\n props: comp.props,\n context: comp.context\n }\n })\n\n const fetchBody = { value, hint, components: componentsMetadata, skeletonize };\n const rerenderContext = constructRerenderContext(props);\n\n return (\n <GeneratedClient\n value={value}\n allowedComponents={allowedComponents}\n endpoint={endpoint}\n fetchBody={fetchBody}\n placeholder={placeholder}\n errorFallback={errorFallback}\n animate={animate}\n onGenerate={onGenerate}\n rerender={{ context: rerenderContext, endpoint: rerenderEndpoint }}\n />\n );\n}\n"],"mappings":"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","names":["ResponseParser","delta","split","line","node","childrenMap","componentMap","generateComponentMap","allowedComponents","acc","curr","constructInput","value","skeletonize","components","hint","item","customComponents","componentContext","inputValue","createSkeleton","constructRerenderContext","props","input","key","useCallback","useEffect","useMemo","useReducer","useRef","useState","Fragment","useEffect","useState","jsx","createElement","resolvePath","obj","path","acc","curr","get","global","local","blacklistedProps","isEventHandlerKey","key","resolveProps","props","resolved","val","renderContent","content","Renderer","_a","_b","_c","isVisible","setIsVisible","frame","id","componentMap","childrenMap","allowedComponents","animate","element","sourceArrPath","sourceArr","childrenArr","childId","index","item","index1","Component","animatedProps","initialOpacity","contentNode","childNodes","nodesToRender","node","createContext","useContext","SyntuxContext","useSyntux","context","Fragment","jsx","GeneratedClient","value","allowedComponents","endpoint","fetchBody","placeholder","errorFallback","animate","onGenerate","rerender","_a","statefulValue","setStatefulValue","useState","forceUpdate","useReducer","x","parser","useRef","errored","setErrored","fetchConfig","setFetchConfig","useEffect","isActive","ResponseParser","_b","response","reader","decoder","done","delta","schema","modifyValue","useCallback","options","providerValue","useMemo","renderContent","Renderer","SyntuxContext","Fragment","jsx","GeneratedUI","props","endpoint","value","hint","components","skeletonize","placeholder","cached","onGenerate","errorFallback","animate","rerenderEndpoint","allowedComponents","generateComponentMap","parser","ResponseParser","schema","Renderer","componentsMetadata","comp","fetchBody","rerenderContext","constructRerenderContext","GeneratedClient"]}
package/dist/index.d.mts CHANGED
@@ -1,37 +1,26 @@
1
- import { S as SyntuxComponent$1, U as UISchema } from './types-0XgxLwqz.mjs';
2
- export { A as AnimateOptions, C as ChildrenMap, a as ComponentMap, R as RerenderContext, b as RerenderOptions, c as SchemaNode } from './types-0XgxLwqz.mjs';
1
+ import { C as ComponentMetadata, G as GeneratedUIProps, S as SyntuxComponent, U as UISchema } from './GeneratedUI-DFOjlyg6.mjs';
2
+ export { A as AnimateOptions, a as ChildrenMap, b as ComponentMap, R as RerenderContext, c as RerenderOptions, d as SchemaNode } from './GeneratedUI-DFOjlyg6.mjs';
3
3
  import * as react from 'react';
4
- import { JSX } from 'react';
5
- import { StreamableValue } from '@ai-sdk/rsc';
6
- import { LanguageModel } from 'ai';
7
- import { SyntuxComponent, AnimateOptions } from 'getsyntux';
8
-
9
- interface GeneratedContentProps {
10
- value: any;
11
- model: LanguageModel;
12
- components?: (SyntuxComponent | string)[];
13
- hint?: string;
14
- placeholder?: JSX.Element;
15
- cached?: string;
16
- onGenerate?: (arg0: string) => void;
17
- skeletonize?: boolean;
18
- onError?: (arg0: any) => void;
19
- errorFallback?: JSX.Element;
20
- animate?: AnimateOptions;
21
- rerender?: (arg0: string, arg1: string, arg2: string) => Promise<{
22
- value: StreamableValue;
23
- }>;
24
- }
4
+ import 'react/jsx-runtime';
25
5
 
26
6
  /**
27
7
  * Converts a list of components into a dictionary for fast-retrieval
28
8
  * during rendering.
29
9
  */
30
- declare function generateComponentMap(allowedComponents: (SyntuxComponent$1 | string)[]): Record<string, string | react.ComponentType<any>>;
10
+ declare function generateComponentMap(allowedComponents: (SyntuxComponent | string)[]): Record<string, string | react.ComponentType<any>>;
11
+ /**
12
+ * Creates LLM input in accordance to the spec.
13
+ */
14
+ declare function constructInput({ value, skeletonize, components, hint }: {
15
+ value: any;
16
+ components?: (ComponentMetadata | string)[];
17
+ hint?: string;
18
+ skeletonize?: boolean;
19
+ }): string;
31
20
  /**
32
- * Creates LLM input in accordance to the spec
21
+ * Builds the AllowedComponents + ComponentContext header used as context for rerender requests.
33
22
  */
34
- declare function constructInput({ value, skeletonize, components, hint }: GeneratedContentProps): string;
23
+ declare function constructRerenderContext(props: GeneratedUIProps): string;
35
24
  /**
36
25
  * generates a skeleton of the input value, ideal for large arrays or untrusted input.
37
26
  * see the FAQ for more information: https://github.com/puffinsoft/syntux/wiki/FAQ#handling-untrusted-input--large-arrays.
@@ -67,4 +56,4 @@ declare class ResponseParser {
67
56
  finish(): void;
68
57
  }
69
58
 
70
- export { ResponseParser, SyntuxComponent$1 as SyntuxComponent, UISchema, constructInput, createSkeleton, generateComponentMap };
59
+ export { ComponentMetadata, ResponseParser, SyntuxComponent, UISchema, constructInput, constructRerenderContext, createSkeleton, generateComponentMap };
package/dist/index.mjs CHANGED
@@ -1,9 +1,11 @@
1
- function h(n){return n.reduce((t,e)=>typeof e=="string"?(t[e]=e,t):(t[e.name]=e.component,t),{})}function d({value:n,skeletonize:t=!1,components:e,hint:o}){let s=(e==null?void 0:e.map(r=>typeof r=="string"?r:r.name).join(","))||"",i=e==null?void 0:e.filter(r=>typeof r!="string"),l=(i==null?void 0:i.map(r=>r.context?`${r.name} [props: ${r.props}, details: ${r.context}]`:`${r.name} [props: ${r.props}]`).join(","))||"",p=o,u=JSON.stringify(t?a(n):n);return`<AllowedComponents>${s}</AllowedComponents>
2
- <ComponentContext>${l}</ComponentContext>
3
- <UserContext>${p||""}</UserContext>
4
- <IsSkeleton>${t.toString()}</IsSkeleton>
1
+ function h(t){return t.reduce((n,e)=>typeof e=="string"?(n[e]=e,n):(n[e.name]=e.component,n),{})}function u({value:t,skeletonize:n=!1,components:e,hint:o}){let a=(e==null?void 0:e.map(r=>typeof r=="string"?r:r.name).join(","))||"",i=e==null?void 0:e.filter(r=>typeof r!="string"),p=(i==null?void 0:i.map(r=>r.context?`${r.name} [props: ${r.props}, details: ${r.context}]`:`${r.name} [props: ${r.props}]`).join(","))||"",f=JSON.stringify(n?s(t):t);return`<AllowedComponents>${a}</AllowedComponents>
2
+ <ComponentContext>${p}</ComponentContext>
3
+ <UserContext>${o||""}</UserContext>
4
+ <IsSkeleton>${n.toString()}</IsSkeleton>
5
5
  <Value>
6
- ${u}
7
- </Value>`}function a(n){return n===null?"null":typeof n!="object"?typeof n:Array.isArray(n)?n.length==0?"null":[a(n[0])]:Object.entries(n).reduce((t,[e,o])=>(t[e]=a(o),t),{})}var f=class{buffer="";total="";schema={childrenMap:{},componentMap:{},root:null};addDelta(t){this.total+=t,this.buffer+=t;let e=this.buffer.split(`
8
- `);return e.length>1?(e.slice(0,e.length-1).forEach(o=>this.handleLine(o)),this.buffer=e[e.length-1],!0):!1}handleLine(t){try{let e=JSON.parse(t),{childrenMap:o,componentMap:s}=this.schema;s[e.id]=e,e.parentId===null?this.schema.root=e:(o[e.parentId]||(o[e.parentId]=[]),o[e.parentId].push(e.id))}catch{}}finish(){this.handleLine(this.buffer),this.buffer=""}};export{f as ResponseParser,d as constructInput,a as createSkeleton,h as generateComponentMap};
6
+ ${f}
7
+ </Value>`}function d(t){return u(t).split(`
8
+ `).slice(0,2).join(`
9
+ `)}function s(t){return t===null?"null":typeof t!="object"?typeof t:Array.isArray(t)?t.length==0?"null":[s(t[0])]:Object.entries(t).reduce((n,[e,o])=>(n[e]=s(o),n),{})}var l=class{buffer="";total="";schema={childrenMap:{},componentMap:{},root:null};addDelta(n){this.total+=n,this.buffer+=n;let e=this.buffer.split(`
10
+ `);return e.length>1?(e.slice(0,e.length-1).forEach(o=>this.handleLine(o)),this.buffer=e[e.length-1],!0):!1}handleLine(n){try{let e=JSON.parse(n),{childrenMap:o,componentMap:a}=this.schema;a[e.id]=e,e.parentId===null?this.schema.root=e:(o[e.parentId]||(o[e.parentId]=[]),o[e.parentId].push(e.id))}catch{}}finish(){this.handleLine(this.buffer),this.buffer=""}};export{l as ResponseParser,u as constructInput,d as constructRerenderContext,s as createSkeleton,h as generateComponentMap};
9
11
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/util.ts","../src/ResponseParser.ts"],"sourcesContent":["import { GeneratedContentProps } from \"./templates/GeneratedUI\";\r\nimport { SyntuxComponent } from \"./types\";\r\n\r\n/**\r\n * Converts a list of components into a dictionary for fast-retrieval\r\n * during rendering.\r\n */\r\nexport function generateComponentMap(allowedComponents: (SyntuxComponent | string)[]) {\r\n return allowedComponents.reduce((acc: Record<string, React.ComponentType<any> | string>, curr: SyntuxComponent | string) => {\r\n if (typeof curr === \"string\") {\r\n acc[curr] = curr;\r\n return acc;\r\n }\r\n\r\n acc[curr.name] = curr.component;\r\n return acc;\r\n }, {})\r\n}\r\n\r\n/**\r\n * Creates LLM input in accordance to the spec\r\n */\r\nexport function constructInput({\r\n value, skeletonize = false, components, hint\r\n}: GeneratedContentProps) {\r\n const allowedComponents = components?.map((item: SyntuxComponent | string) => {\r\n if (typeof item === \"string\") return item;\r\n return item.name;\r\n }).join(',') || \"\"\r\n\r\n const customComponents = components?.filter((item): item is SyntuxComponent => typeof item !== \"string\");\r\n const componentContext = customComponents?.map((item) => {\r\n if (!item.context) {\r\n return `${item.name} [props: ${item.props}]`\r\n } else {\r\n return `${item.name} [props: ${item.props}, details: ${item.context}]`\r\n }\r\n }).join(',') || \"\"\r\n\r\n const userContext = hint;\r\n\r\n const inputValue = JSON.stringify(skeletonize ? createSkeleton(value) : value)\r\n\r\n return `<AllowedComponents>${allowedComponents}</AllowedComponents>\\n<ComponentContext>${componentContext}</ComponentContext>\\n<UserContext>${userContext || \"\"}</UserContext>\\n<IsSkeleton>${skeletonize.toString()}</IsSkeleton>\\n<Value>\\n${inputValue}\\n</Value>`\r\n}\r\n\r\n/**\r\n * generates a skeleton of the input value, ideal for large arrays or untrusted input.\r\n * see the FAQ for more information: https://github.com/puffinsoft/syntux/wiki/FAQ#handling-untrusted-input--large-arrays.\r\n * \r\n * *important*: assumes arrays are non-polymorphic\r\n */\r\nexport function createSkeleton(input: any) {\r\n if (input === null) return \"null\";\r\n\r\n if (typeof input !== \"object\") return typeof input;\r\n\r\n if (Array.isArray(input)) {\r\n if (input.length == 0) {\r\n return \"null\"; // ignore this field completely\r\n } else {\r\n return [createSkeleton(input[0])]\r\n }\r\n }\r\n return Object.entries(input).reduce((acc, [key, value]) => {\r\n acc[key] = createSkeleton(value);\r\n return acc;\r\n }, {})\r\n}","import { SchemaNode, UISchema } from \"./types\";\r\n\r\n/**\r\n * Utility class for parsing UISchema from stream.\r\n */\r\nexport class ResponseParser {\r\n buffer = \"\"; // unflushed existing deltas w/o newline\r\n total = \"\"; // accumulator\r\n\r\n // schema assembled thus far\r\n schema: UISchema = {\r\n childrenMap: {},\r\n componentMap: {},\r\n root: null\r\n }\r\n\r\n /**\r\n * Update schema with latest data chunk.\r\n * \r\n * Handles multiline input gracefully; can be used to load entire schemas from cache.\r\n * \r\n * @param delta delta from stream.\r\n * @returns true if update is warranted, false otherwise.\r\n */\r\n addDelta(delta: string) {\r\n this.total += delta;\r\n this.buffer += delta;\r\n const split = this.buffer.split(\"\\n\")\r\n if (split.length > 1) {\r\n split.slice(0, split.length - 1).forEach((line) => this.handleLine(line));\r\n this.buffer = split[split.length - 1];\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Parses a single line (full JSON object) and updates schema.\r\n * Generally should not be used when streaming data.\r\n */\r\n handleLine(line: string) {\r\n try {\r\n const node: SchemaNode = JSON.parse(line);\r\n\r\n const { childrenMap, componentMap } = this.schema;\r\n\r\n componentMap[node.id] = node;\r\n if (node.parentId === null) {\r\n this.schema.root = node;\r\n } else {\r\n if (!childrenMap[node.parentId]) childrenMap[node.parentId] = []\r\n childrenMap[node.parentId].push(node.id)\r\n }\r\n } catch (err) { /* probably markdown or generation inconsistency */ }\r\n }\r\n\r\n /**\r\n * Clears the buffer and handles any remaining information within.\r\n */\r\n finish(){\r\n this.handleLine(this.buffer);\r\n this.buffer = \"\";\r\n }\r\n}"],"mappings":"AAOO,SAASA,EAAqBC,EAAiD,CAClF,OAAOA,EAAkB,OAAO,CAACC,EAAwDC,IACjF,OAAOA,GAAS,UAChBD,EAAIC,CAAI,EAAIA,EACLD,IAGXA,EAAIC,EAAK,IAAI,EAAIA,EAAK,UACfD,GACR,CAAC,CAAC,CACT,CAKO,SAASE,EAAe,CAC3B,MAAAC,EAAO,YAAAC,EAAc,GAAO,WAAAC,EAAY,KAAAC,CAC5C,EAA0B,CACtB,IAAMP,GAAoBM,GAAA,YAAAA,EAAY,IAAKE,GACnC,OAAOA,GAAS,SAAiBA,EAC9BA,EAAK,MACb,KAAK,OAAQ,GAEVC,EAAmBH,GAAA,YAAAA,EAAY,OAAQE,GAAkC,OAAOA,GAAS,UACzFE,GAAmBD,GAAA,YAAAA,EAAkB,IAAKD,GACvCA,EAAK,QAGC,GAAGA,EAAK,IAAI,YAAYA,EAAK,KAAK,cAAcA,EAAK,OAAO,IAF5D,GAAGA,EAAK,IAAI,YAAYA,EAAK,KAAK,KAI9C,KAAK,OAAQ,GAEVG,EAAcJ,EAEdK,EAAa,KAAK,UAAUP,EAAcQ,EAAeT,CAAK,EAAIA,CAAK,EAE7E,MAAO,sBAAsBJ,CAAiB;AAAA,oBAA2CU,CAAgB;AAAA,eAAqCC,GAAe,EAAE;AAAA,cAA+BN,EAAY,SAAS,CAAC;AAAA;AAAA,EAA2BO,CAAU;AAAA,SAC7P,CAQO,SAASC,EAAeC,EAAY,CACvC,OAAIA,IAAU,KAAa,OAEvB,OAAOA,GAAU,SAAiB,OAAOA,EAEzC,MAAM,QAAQA,CAAK,EACfA,EAAM,QAAU,EACT,OAEA,CAACD,EAAeC,EAAM,CAAC,CAAC,CAAC,EAGjC,OAAO,QAAQA,CAAK,EAAE,OAAO,CAACb,EAAK,CAACc,EAAKX,CAAK,KACjDH,EAAIc,CAAG,EAAIF,EAAeT,CAAK,EACxBH,GACR,CAAC,CAAC,CACT,CC/DO,IAAMe,EAAN,KAAqB,CACxB,OAAS,GACT,MAAQ,GAGR,OAAmB,CACf,YAAa,CAAC,EACd,aAAc,CAAC,EACf,KAAM,IACV,EAUA,SAASC,EAAe,CACpB,KAAK,OAASA,EACd,KAAK,QAAUA,EACf,IAAMC,EAAQ,KAAK,OAAO,MAAM;AAAA,CAAI,EACpC,OAAIA,EAAM,OAAS,GACfA,EAAM,MAAM,EAAGA,EAAM,OAAS,CAAC,EAAE,QAASC,GAAS,KAAK,WAAWA,CAAI,CAAC,EACxE,KAAK,OAASD,EAAMA,EAAM,OAAS,CAAC,EAC7B,IAEJ,EACX,CAMA,WAAWC,EAAc,CACrB,GAAI,CACA,IAAMC,EAAmB,KAAK,MAAMD,CAAI,EAElC,CAAE,YAAAE,EAAa,aAAAC,CAAa,EAAI,KAAK,OAE3CA,EAAaF,EAAK,EAAE,EAAIA,EACpBA,EAAK,WAAa,KAClB,KAAK,OAAO,KAAOA,GAEdC,EAAYD,EAAK,QAAQ,IAAGC,EAAYD,EAAK,QAAQ,EAAI,CAAC,GAC/DC,EAAYD,EAAK,QAAQ,EAAE,KAAKA,EAAK,EAAE,EAE/C,MAAc,CAAsD,CACxE,CAKA,QAAQ,CACJ,KAAK,WAAW,KAAK,MAAM,EAC3B,KAAK,OAAS,EAClB,CACJ","names":["generateComponentMap","allowedComponents","acc","curr","constructInput","value","skeletonize","components","hint","item","customComponents","componentContext","userContext","inputValue","createSkeleton","input","key","ResponseParser","delta","split","line","node","childrenMap","componentMap"]}
1
+ {"version":3,"sources":["../src/util.ts","../src/ResponseParser.ts"],"sourcesContent":["import { GeneratedUIProps } from \"./client\";\r\nimport { ComponentMetadata, SyntuxComponent } from \"./types\";\r\n\r\n/**\r\n * Converts a list of components into a dictionary for fast-retrieval\r\n * during rendering.\r\n */\r\nexport function generateComponentMap(allowedComponents: (SyntuxComponent | string)[]) {\r\n return allowedComponents.reduce((acc: Record<string, React.ComponentType<any> | string>, curr: SyntuxComponent | string) => {\r\n if (typeof curr === \"string\") {\r\n acc[curr] = curr;\r\n return acc;\r\n }\r\n\r\n acc[curr.name] = curr.component;\r\n return acc;\r\n }, {})\r\n}\r\n\r\n/**\r\n * Creates LLM input in accordance to the spec.\r\n */\r\nexport function constructInput({\r\n value, skeletonize = false, components, hint\r\n}: {\r\n value: any;\r\n components?: (ComponentMetadata | string)[];\r\n hint?: string;\r\n skeletonize?: boolean;\r\n}) {\r\n const allowedComponents = components?.map((item: ComponentMetadata | string) => {\r\n if (typeof item === \"string\") return item;\r\n return item.name;\r\n }).join(',') || \"\"\r\n\r\n const customComponents = components?.filter((item): item is ComponentMetadata => typeof item !== \"string\");\r\n const componentContext = customComponents?.map((item) => {\r\n if (!item.context) {\r\n return `${item.name} [props: ${item.props}]`\r\n } else {\r\n return `${item.name} [props: ${item.props}, details: ${item.context}]`\r\n }\r\n }).join(',') || \"\"\r\n\r\n const inputValue = JSON.stringify(skeletonize ? createSkeleton(value) : value)\r\n\r\n return `<AllowedComponents>${allowedComponents}</AllowedComponents>\\n<ComponentContext>${componentContext}</ComponentContext>\\n<UserContext>${hint || \"\"}</UserContext>\\n<IsSkeleton>${skeletonize.toString()}</IsSkeleton>\\n<Value>\\n${inputValue}\\n</Value>`\r\n}\r\n\r\n/**\r\n * Builds the AllowedComponents + ComponentContext header used as context for rerender requests.\r\n */\r\nexport function constructRerenderContext(props: GeneratedUIProps) {\r\n return constructInput(props).split('\\n').slice(0, 2).join('\\n');\r\n}\r\n\r\n/**\r\n * generates a skeleton of the input value, ideal for large arrays or untrusted input.\r\n * see the FAQ for more information: https://github.com/puffinsoft/syntux/wiki/FAQ#handling-untrusted-input--large-arrays.\r\n *\r\n * *important*: assumes arrays are non-polymorphic\r\n */\r\nexport function createSkeleton(input: any) {\r\n if (input === null) return \"null\";\r\n\r\n if (typeof input !== \"object\") return typeof input;\r\n\r\n if (Array.isArray(input)) {\r\n if (input.length == 0) {\r\n return \"null\"; // ignore this field completely\r\n } else {\r\n return [createSkeleton(input[0])]\r\n }\r\n }\r\n return Object.entries(input).reduce((acc, [key, value]) => {\r\n acc[key] = createSkeleton(value);\r\n return acc;\r\n }, {})\r\n}\r\n","import { SchemaNode, UISchema } from \"./types\";\r\n\r\n/**\r\n * Utility class for parsing UISchema from stream.\r\n */\r\nexport class ResponseParser {\r\n buffer = \"\"; // unflushed existing deltas w/o newline\r\n total = \"\"; // accumulator\r\n\r\n // schema assembled thus far\r\n schema: UISchema = {\r\n childrenMap: {},\r\n componentMap: {},\r\n root: null\r\n }\r\n\r\n /**\r\n * Update schema with latest data chunk.\r\n * \r\n * Handles multiline input gracefully; can be used to load entire schemas from cache.\r\n * \r\n * @param delta delta from stream.\r\n * @returns true if update is warranted, false otherwise.\r\n */\r\n addDelta(delta: string) {\r\n this.total += delta;\r\n this.buffer += delta;\r\n const split = this.buffer.split(\"\\n\")\r\n if (split.length > 1) {\r\n split.slice(0, split.length - 1).forEach((line) => this.handleLine(line));\r\n this.buffer = split[split.length - 1];\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Parses a single line (full JSON object) and updates schema.\r\n * Generally should not be used when streaming data.\r\n */\r\n handleLine(line: string) {\r\n try {\r\n const node: SchemaNode = JSON.parse(line);\r\n\r\n const { childrenMap, componentMap } = this.schema;\r\n\r\n componentMap[node.id] = node;\r\n if (node.parentId === null) {\r\n this.schema.root = node;\r\n } else {\r\n if (!childrenMap[node.parentId]) childrenMap[node.parentId] = []\r\n childrenMap[node.parentId].push(node.id)\r\n }\r\n } catch (err) { /* probably markdown or generation inconsistency */ }\r\n }\r\n\r\n /**\r\n * Clears the buffer and handles any remaining information within.\r\n */\r\n finish(){\r\n this.handleLine(this.buffer);\r\n this.buffer = \"\";\r\n }\r\n}"],"mappings":"AAOO,SAASA,EAAqBC,EAAiD,CAClF,OAAOA,EAAkB,OAAO,CAACC,EAAwDC,IACjF,OAAOA,GAAS,UAChBD,EAAIC,CAAI,EAAIA,EACLD,IAGXA,EAAIC,EAAK,IAAI,EAAIA,EAAK,UACfD,GACR,CAAC,CAAC,CACT,CAKO,SAASE,EAAe,CAC3B,MAAAC,EAAO,YAAAC,EAAc,GAAO,WAAAC,EAAY,KAAAC,CAC5C,EAKG,CACC,IAAMP,GAAoBM,GAAA,YAAAA,EAAY,IAAKE,GACnC,OAAOA,GAAS,SAAiBA,EAC9BA,EAAK,MACb,KAAK,OAAQ,GAEVC,EAAmBH,GAAA,YAAAA,EAAY,OAAQE,GAAoC,OAAOA,GAAS,UAC3FE,GAAmBD,GAAA,YAAAA,EAAkB,IAAKD,GACvCA,EAAK,QAGC,GAAGA,EAAK,IAAI,YAAYA,EAAK,KAAK,cAAcA,EAAK,OAAO,IAF5D,GAAGA,EAAK,IAAI,YAAYA,EAAK,KAAK,KAI9C,KAAK,OAAQ,GAEVG,EAAa,KAAK,UAAUN,EAAcO,EAAeR,CAAK,EAAIA,CAAK,EAE7E,MAAO,sBAAsBJ,CAAiB;AAAA,oBAA2CU,CAAgB;AAAA,eAAqCH,GAAQ,EAAE;AAAA,cAA+BF,EAAY,SAAS,CAAC;AAAA;AAAA,EAA2BM,CAAU;AAAA,SACtP,CAKO,SAASE,EAAyBC,EAAyB,CAC9D,OAAOX,EAAeW,CAAK,EAAE,MAAM;AAAA,CAAI,EAAE,MAAM,EAAG,CAAC,EAAE,KAAK;AAAA,CAAI,CAClE,CAQO,SAASF,EAAeG,EAAY,CACvC,OAAIA,IAAU,KAAa,OAEvB,OAAOA,GAAU,SAAiB,OAAOA,EAEzC,MAAM,QAAQA,CAAK,EACfA,EAAM,QAAU,EACT,OAEA,CAACH,EAAeG,EAAM,CAAC,CAAC,CAAC,EAGjC,OAAO,QAAQA,CAAK,EAAE,OAAO,CAACd,EAAK,CAACe,EAAKZ,CAAK,KACjDH,EAAIe,CAAG,EAAIJ,EAAeR,CAAK,EACxBH,GACR,CAAC,CAAC,CACT,CCzEO,IAAMgB,EAAN,KAAqB,CACxB,OAAS,GACT,MAAQ,GAGR,OAAmB,CACf,YAAa,CAAC,EACd,aAAc,CAAC,EACf,KAAM,IACV,EAUA,SAASC,EAAe,CACpB,KAAK,OAASA,EACd,KAAK,QAAUA,EACf,IAAMC,EAAQ,KAAK,OAAO,MAAM;AAAA,CAAI,EACpC,OAAIA,EAAM,OAAS,GACfA,EAAM,MAAM,EAAGA,EAAM,OAAS,CAAC,EAAE,QAASC,GAAS,KAAK,WAAWA,CAAI,CAAC,EACxE,KAAK,OAASD,EAAMA,EAAM,OAAS,CAAC,EAC7B,IAEJ,EACX,CAMA,WAAWC,EAAc,CACrB,GAAI,CACA,IAAMC,EAAmB,KAAK,MAAMD,CAAI,EAElC,CAAE,YAAAE,EAAa,aAAAC,CAAa,EAAI,KAAK,OAE3CA,EAAaF,EAAK,EAAE,EAAIA,EACpBA,EAAK,WAAa,KAClB,KAAK,OAAO,KAAOA,GAEdC,EAAYD,EAAK,QAAQ,IAAGC,EAAYD,EAAK,QAAQ,EAAI,CAAC,GAC/DC,EAAYD,EAAK,QAAQ,EAAE,KAAKA,EAAK,EAAE,EAE/C,MAAc,CAAsD,CACxE,CAKA,QAAQ,CACJ,KAAK,WAAW,KAAK,MAAM,EAC3B,KAAK,OAAS,EAClB,CACJ","names":["generateComponentMap","allowedComponents","acc","curr","constructInput","value","skeletonize","components","hint","item","customComponents","componentContext","inputValue","createSkeleton","constructRerenderContext","props","input","key","ResponseParser","delta","split","line","node","childrenMap","componentMap"]}
@@ -1 +1 @@
1
- {"inputs":{"src/types.ts":{"bytes":1256,"imports":[{"path":"@ai-sdk/rsc","kind":"import-statement","external":true}],"format":"esm"},"src/util.ts":{"bytes":2558,"imports":[{"path":"./templates/GeneratedUI","kind":"import-statement","external":true},{"path":"./types","kind":"import-statement","external":true}],"format":"esm"},"src/ResponseParser.ts":{"bytes":1923,"imports":[{"path":"./types","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":84,"imports":[{"path":"src/types.ts","kind":"import-statement","original":"./types"},{"path":"src/util.ts","kind":"import-statement","original":"./util"},{"path":"src/ResponseParser.ts","kind":"import-statement","original":"./ResponseParser"}],"format":"esm"},"src/client/Renderer.tsx":{"bytes":4570,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"../types","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true}],"format":"esm"},"src/client/SyntuxContext.tsx":{"bytes":497,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/types","kind":"import-statement","external":true}],"format":"esm"},"src/client/GeneratedClient.tsx":{"bytes":3579,"imports":[{"path":"@ai-sdk/rsc","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"src/types","kind":"import-statement","external":true},{"path":"src/ResponseParser.ts","kind":"import-statement","original":"src/ResponseParser"},{"path":"src/client/Renderer.tsx","kind":"import-statement","original":"./Renderer"},{"path":"src/client/SyntuxContext.tsx","kind":"import-statement","original":"./SyntuxContext"},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/client.ts":{"bytes":133,"imports":[{"path":"src/client/GeneratedClient.tsx","kind":"import-statement","original":"./client/GeneratedClient"},{"path":"src/client/Renderer.tsx","kind":"import-statement","original":"./client/Renderer"},{"path":"src/client/SyntuxContext.tsx","kind":"import-statement","original":"./client/SyntuxContext"}],"format":"esm"},"src/bin/cli_util.mjs":{"bytes":115,"imports":[{"path":"chalk","kind":"import-statement","external":true}],"format":"esm"},"src/bin/commands/init.mjs":{"bytes":3700,"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"child_process","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"src/bin/cli_util.mjs","kind":"import-statement","original":"../cli_util.mjs"},{"path":"url","kind":"import-statement","external":true}],"format":"esm"},"src/bin/commands/generate.mjs":{"bytes":2933,"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"react-docgen-typescript","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true},{"path":"src/bin/cli_util.mjs","kind":"import-statement","original":"../cli_util.mjs"}],"format":"esm"},"src/bin/cli.mjs":{"bytes":482,"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"src/bin/commands/init.mjs","kind":"import-statement","original":"./commands/init.mjs"},{"path":"src/bin/commands/generate.mjs","kind":"import-statement","original":"./commands/generate.mjs"}],"format":"esm"}},"outputs":{"dist/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":6941},"dist/index.mjs":{"imports":[],"exports":["ResponseParser","constructInput","createSkeleton","generateComponentMap"],"entryPoint":"src/index.ts","inputs":{"src/index.ts":{"bytesInOutput":0},"src/util.ts":{"bytesInOutput":809},"src/ResponseParser.ts":{"bytesInOutput":508}},"bytes":1412},"dist/client.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":17974},"dist/client.mjs":{"imports":[{"path":"@ai-sdk/rsc","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"exports":["GeneratedClient","Renderer","SyntuxContext","useSyntux"],"entryPoint":"src/client.ts","inputs":{"src/client/GeneratedClient.tsx":{"bytesInOutput":1220},"src/ResponseParser.ts":{"bytesInOutput":508},"src/client/Renderer.tsx":{"bytesInOutput":1727},"src/client/SyntuxContext.tsx":{"bytesInOutput":172},"src/client.ts":{"bytesInOutput":0}},"bytes":3720},"dist/bin/cli.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":12417},"dist/bin/cli.mjs":{"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"commander","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"child_process","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"url","kind":"import-statement","external":true},{"path":"commander","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"react-docgen-typescript","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true}],"exports":[],"entryPoint":"src/bin/cli.mjs","inputs":{"src/bin/cli.mjs":{"bytesInOutput":250},"src/bin/commands/init.mjs":{"bytesInOutput":1851},"src/bin/cli_util.mjs":{"bytesInOutput":78},"src/bin/commands/generate.mjs":{"bytesInOutput":1443}},"bytes":3643}}}
1
+ {"inputs":{"src/types.ts":{"bytes":1308,"imports":[],"format":"esm"},"src/util.ts":{"bytes":2873,"imports":[{"path":"./client","kind":"import-statement","external":true},{"path":"./types","kind":"import-statement","external":true}],"format":"esm"},"src/ResponseParser.ts":{"bytes":1923,"imports":[{"path":"./types","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":84,"imports":[{"path":"src/types.ts","kind":"import-statement","original":"./types"},{"path":"src/util.ts","kind":"import-statement","original":"./util"},{"path":"src/ResponseParser.ts","kind":"import-statement","original":"./ResponseParser"}],"format":"esm"},"src/client/Renderer.tsx":{"bytes":5020,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"../types","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true}],"format":"esm"},"src/client/SyntuxContext.tsx":{"bytes":481,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/types","kind":"import-statement","external":true}],"format":"esm"},"src/client/GeneratedClient.tsx":{"bytes":4814,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"../types","kind":"import-statement","external":true},{"path":"src/ResponseParser.ts","kind":"import-statement","original":"../ResponseParser"},{"path":"src/client/Renderer.tsx","kind":"import-statement","original":"./Renderer"},{"path":"src/client/SyntuxContext.tsx","kind":"import-statement","original":"./SyntuxContext"},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/client/GeneratedUI.tsx":{"bytes":3547,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"src/ResponseParser.ts","kind":"import-statement","original":"../ResponseParser"},{"path":"../types","kind":"import-statement","external":true},{"path":"src/util.ts","kind":"import-statement","original":"../util"},{"path":"src/client/GeneratedClient.tsx","kind":"import-statement","original":"./GeneratedClient"},{"path":"src/client/Renderer.tsx","kind":"import-statement","original":"./Renderer"},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"format":"esm"},"src/client.ts":{"bytes":172,"imports":[{"path":"src/client/GeneratedUI.tsx","kind":"import-statement","original":"./client/GeneratedUI"},{"path":"src/client/GeneratedClient.tsx","kind":"import-statement","original":"./client/GeneratedClient"},{"path":"src/client/Renderer.tsx","kind":"import-statement","original":"./client/Renderer"},{"path":"src/client/SyntuxContext.tsx","kind":"import-statement","original":"./client/SyntuxContext"}],"format":"esm"},"src/server/index.ts":{"bytes":2077,"imports":[{"path":"ai","kind":"import-statement","external":true},{"path":"src/util.ts","kind":"import-statement","original":"../util"}],"format":"esm"},"src/bin/cli_util.mjs":{"bytes":115,"imports":[{"path":"chalk","kind":"import-statement","external":true}],"format":"esm"},"src/bin/commands/init.mjs":{"bytes":3700,"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"child_process","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"src/bin/cli_util.mjs","kind":"import-statement","original":"../cli_util.mjs"},{"path":"url","kind":"import-statement","external":true}],"format":"esm"},"src/bin/commands/generate.mjs":{"bytes":2933,"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"react-docgen-typescript","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true},{"path":"src/bin/cli_util.mjs","kind":"import-statement","original":"../cli_util.mjs"}],"format":"esm"},"src/bin/cli.mjs":{"bytes":482,"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"src/bin/commands/init.mjs","kind":"import-statement","original":"./commands/init.mjs"},{"path":"src/bin/commands/generate.mjs","kind":"import-statement","original":"./commands/generate.mjs"}],"format":"esm"}},"outputs":{"dist/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":7402},"dist/index.mjs":{"imports":[],"exports":["ResponseParser","constructInput","constructRerenderContext","createSkeleton","generateComponentMap"],"entryPoint":"src/index.ts","inputs":{"src/index.ts":{"bytesInOutput":0},"src/util.ts":{"bytesInOutput":863},"src/ResponseParser.ts":{"bytesInOutput":508}},"bytes":1496},"dist/client.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":29788},"dist/client.mjs":{"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true},{"path":"react/jsx-runtime","kind":"import-statement","external":true}],"exports":["GeneratedClient","GeneratedUI","Renderer","SyntuxContext","useSyntux"],"entryPoint":"src/client.ts","inputs":{"src/ResponseParser.ts":{"bytesInOutput":508},"src/util.ts":{"bytesInOutput":863},"src/client/GeneratedClient.tsx":{"bytesInOutput":1522},"src/client/Renderer.tsx":{"bytesInOutput":1870},"src/client/SyntuxContext.tsx":{"bytesInOutput":176},"src/client/GeneratedUI.tsx":{"bytesInOutput":716},"src/client.ts":{"bytesInOutput":0}},"bytes":5765},"dist/server.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":7351},"dist/server.mjs":{"imports":[{"path":"ai","kind":"import-statement","external":true}],"exports":["createSyntuxHandler","createSyntuxRerenderHandler"],"entryPoint":"src/server/index.ts","inputs":{"src/server/index.ts":{"bytesInOutput":524},"src/util.ts":{"bytesInOutput":708}},"bytes":1299},"dist/bin/cli.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":12417},"dist/bin/cli.mjs":{"imports":[{"path":"commander","kind":"import-statement","external":true},{"path":"commander","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"child_process","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"url","kind":"import-statement","external":true},{"path":"commander","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"react-docgen-typescript","kind":"import-statement","external":true},{"path":"prompts","kind":"import-statement","external":true},{"path":"chalk","kind":"import-statement","external":true},{"path":"fs-extra","kind":"import-statement","external":true}],"exports":[],"entryPoint":"src/bin/cli.mjs","inputs":{"src/bin/cli.mjs":{"bytesInOutput":250},"src/bin/commands/init.mjs":{"bytesInOutput":1851},"src/bin/cli_util.mjs":{"bytesInOutput":78},"src/bin/commands/generate.mjs":{"bytesInOutput":1443}},"bytes":3643}}}
@@ -0,0 +1,27 @@
1
+ import { LanguageModel } from 'ai';
2
+
3
+ interface SyntuxHandlerOptions {
4
+ model: LanguageModel;
5
+ spec: string;
6
+ onGenerate?: (schema: string) => void;
7
+ }
8
+ /**
9
+ * HTTP handler for UI generation requests. Framework-agnostic.
10
+ *
11
+ * Important: does not support rerendering. Use `createSyntuxRerenderHandler`.
12
+ *
13
+ * @param model The LanguageModel (as provided from AI SDK) to use. Must support streaming.
14
+ * @param spec The model specification to use.
15
+ * @param onGenerate Schema generation callback, used for caching.
16
+ */
17
+ declare function createSyntuxHandler(options: SyntuxHandlerOptions): (request: Request) => Promise<Response>;
18
+ /**
19
+ * HTTP handler for UI rerender requests. Framework-agnostic.
20
+ *
21
+ * @param model The LanguageModel (as provided from AI SDK) to use. Must support streaming.
22
+ * @param spec The model specification to use.
23
+ * @param onGenerate Schema generation callback, used for caching.
24
+ */
25
+ declare function createSyntuxRerenderHandler(options: SyntuxHandlerOptions): (request: Request) => Promise<Response>;
26
+
27
+ export { type SyntuxHandlerOptions, createSyntuxHandler, createSyntuxRerenderHandler };
@@ -0,0 +1,10 @@
1
+ import{streamText as d}from"ai";function u({value:e,skeletonize:r=!1,components:n,hint:o}){let a=(n==null?void 0:n.map(t=>typeof t=="string"?t:t.name).join(","))||"",s=n==null?void 0:n.filter(t=>typeof t!="string"),i=(s==null?void 0:s.map(t=>t.context?`${t.name} [props: ${t.props}, details: ${t.context}]`:`${t.name} [props: ${t.props}]`).join(","))||"",x=JSON.stringify(r?p(e):e);return`<AllowedComponents>${a}</AllowedComponents>
2
+ <ComponentContext>${i}</ComponentContext>
3
+ <UserContext>${o||""}</UserContext>
4
+ <IsSkeleton>${r.toString()}</IsSkeleton>
5
+ <Value>
6
+ ${x}
7
+ </Value>`}function p(e){return e===null?"null":typeof e!="object"?typeof e:Array.isArray(e)?e.length==0?"null":[p(e[0])]:Object.entries(e).reduce((r,[n,o])=>(r[n]=p(o),r),{})}function y(e){return async r=>{let{value:n,hint:o,components:a,skeletonize:s}=await r.json(),i=u({value:n,hint:o,components:a,skeletonize:s});return l(e.model,e.spec,i,e.onGenerate)}}function C(e){return async r=>{let{context:n,existing:o,hint:a}=await r.json(),s=`${n}
8
+ <Existing>${o}</Existing>
9
+ <UserContext>${a}</UserContext>`;return l(e.model,e.spec,s,e.onGenerate)}}function l(e,r,n,o){return d({model:e,system:r,prompt:n,onFinish:({text:s})=>o==null?void 0:o(s)}).toTextStreamResponse()}export{y as createSyntuxHandler,C as createSyntuxRerenderHandler};
10
+ //# sourceMappingURL=server.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/index.ts","../src/util.ts"],"sourcesContent":["import { LanguageModel, streamText } from 'ai';\nimport { constructInput } from '../util';\nexport interface SyntuxHandlerOptions {\n model: LanguageModel;\n spec: string;\n onGenerate?: (schema: string) => void;\n}\n\n/**\n * HTTP handler for UI generation requests. Framework-agnostic.\n * \n * Important: does not support rerendering. Use `createSyntuxRerenderHandler`.\n * \n * @param model The LanguageModel (as provided from AI SDK) to use. Must support streaming.\n * @param spec The model specification to use.\n * @param onGenerate Schema generation callback, used for caching.\n */\nexport function createSyntuxHandler(options: SyntuxHandlerOptions) {\n return async (request: Request): Promise<Response> => {\n const { value, hint, components, skeletonize } = await request.json();\n const prompt = constructInput({ value, hint, components, skeletonize });\n\n return makeStreamResponse(options.model, options.spec, prompt, options.onGenerate);\n };\n}\n\n/**\n * HTTP handler for UI rerender requests. Framework-agnostic.\n *\n * @param model The LanguageModel (as provided from AI SDK) to use. Must support streaming.\n * @param spec The model specification to use.\n * @param onGenerate Schema generation callback, used for caching.\n */\nexport function createSyntuxRerenderHandler(options: SyntuxHandlerOptions) {\n return async (request: Request): Promise<Response> => {\n const { context, existing, hint } = await request.json();\n const prompt = `${context}\\n<Existing>${existing}</Existing>\\n<UserContext>${hint}</UserContext>`;\n return makeStreamResponse(options.model, options.spec, prompt, options.onGenerate);\n };\n}\n\n\n/**\n * utility function for creating responses using the Stream API.\n */\nfunction makeStreamResponse(\n model: LanguageModel,\n spec: string,\n prompt: string,\n onGenerate?: (s: string) => void,\n): Response {\n const result = streamText({\n model,\n system: spec,\n prompt,\n onFinish: ({ text }) => onGenerate?.(text),\n });\n return result.toTextStreamResponse();\n}\n\n","import { GeneratedUIProps } from \"./client\";\r\nimport { ComponentMetadata, SyntuxComponent } from \"./types\";\r\n\r\n/**\r\n * Converts a list of components into a dictionary for fast-retrieval\r\n * during rendering.\r\n */\r\nexport function generateComponentMap(allowedComponents: (SyntuxComponent | string)[]) {\r\n return allowedComponents.reduce((acc: Record<string, React.ComponentType<any> | string>, curr: SyntuxComponent | string) => {\r\n if (typeof curr === \"string\") {\r\n acc[curr] = curr;\r\n return acc;\r\n }\r\n\r\n acc[curr.name] = curr.component;\r\n return acc;\r\n }, {})\r\n}\r\n\r\n/**\r\n * Creates LLM input in accordance to the spec.\r\n */\r\nexport function constructInput({\r\n value, skeletonize = false, components, hint\r\n}: {\r\n value: any;\r\n components?: (ComponentMetadata | string)[];\r\n hint?: string;\r\n skeletonize?: boolean;\r\n}) {\r\n const allowedComponents = components?.map((item: ComponentMetadata | string) => {\r\n if (typeof item === \"string\") return item;\r\n return item.name;\r\n }).join(',') || \"\"\r\n\r\n const customComponents = components?.filter((item): item is ComponentMetadata => typeof item !== \"string\");\r\n const componentContext = customComponents?.map((item) => {\r\n if (!item.context) {\r\n return `${item.name} [props: ${item.props}]`\r\n } else {\r\n return `${item.name} [props: ${item.props}, details: ${item.context}]`\r\n }\r\n }).join(',') || \"\"\r\n\r\n const inputValue = JSON.stringify(skeletonize ? createSkeleton(value) : value)\r\n\r\n return `<AllowedComponents>${allowedComponents}</AllowedComponents>\\n<ComponentContext>${componentContext}</ComponentContext>\\n<UserContext>${hint || \"\"}</UserContext>\\n<IsSkeleton>${skeletonize.toString()}</IsSkeleton>\\n<Value>\\n${inputValue}\\n</Value>`\r\n}\r\n\r\n/**\r\n * Builds the AllowedComponents + ComponentContext header used as context for rerender requests.\r\n */\r\nexport function constructRerenderContext(props: GeneratedUIProps) {\r\n return constructInput(props).split('\\n').slice(0, 2).join('\\n');\r\n}\r\n\r\n/**\r\n * generates a skeleton of the input value, ideal for large arrays or untrusted input.\r\n * see the FAQ for more information: https://github.com/puffinsoft/syntux/wiki/FAQ#handling-untrusted-input--large-arrays.\r\n *\r\n * *important*: assumes arrays are non-polymorphic\r\n */\r\nexport function createSkeleton(input: any) {\r\n if (input === null) return \"null\";\r\n\r\n if (typeof input !== \"object\") return typeof input;\r\n\r\n if (Array.isArray(input)) {\r\n if (input.length == 0) {\r\n return \"null\"; // ignore this field completely\r\n } else {\r\n return [createSkeleton(input[0])]\r\n }\r\n }\r\n return Object.entries(input).reduce((acc, [key, value]) => {\r\n acc[key] = createSkeleton(value);\r\n return acc;\r\n }, {})\r\n}\r\n"],"mappings":"AAAA,OAAwB,cAAAA,MAAkB,KCsBnC,SAASC,EAAe,CAC3B,MAAAC,EAAO,YAAAC,EAAc,GAAO,WAAAC,EAAY,KAAAC,CAC5C,EAKG,CACC,IAAMC,GAAoBF,GAAA,YAAAA,EAAY,IAAKG,GACnC,OAAOA,GAAS,SAAiBA,EAC9BA,EAAK,MACb,KAAK,OAAQ,GAEVC,EAAmBJ,GAAA,YAAAA,EAAY,OAAQG,GAAoC,OAAOA,GAAS,UAC3FE,GAAmBD,GAAA,YAAAA,EAAkB,IAAKD,GACvCA,EAAK,QAGC,GAAGA,EAAK,IAAI,YAAYA,EAAK,KAAK,cAAcA,EAAK,OAAO,IAF5D,GAAGA,EAAK,IAAI,YAAYA,EAAK,KAAK,KAI9C,KAAK,OAAQ,GAEVG,EAAa,KAAK,UAAUP,EAAcQ,EAAeT,CAAK,EAAIA,CAAK,EAE7E,MAAO,sBAAsBI,CAAiB;AAAA,oBAA2CG,CAAgB;AAAA,eAAqCJ,GAAQ,EAAE;AAAA,cAA+BF,EAAY,SAAS,CAAC;AAAA;AAAA,EAA2BO,CAAU;AAAA,SACtP,CAeO,SAASE,EAAeC,EAAY,CACvC,OAAIA,IAAU,KAAa,OAEvB,OAAOA,GAAU,SAAiB,OAAOA,EAEzC,MAAM,QAAQA,CAAK,EACfA,EAAM,QAAU,EACT,OAEA,CAACD,EAAeC,EAAM,CAAC,CAAC,CAAC,EAGjC,OAAO,QAAQA,CAAK,EAAE,OAAO,CAACC,EAAK,CAACC,EAAKC,CAAK,KACjDF,EAAIC,CAAG,EAAIH,EAAeI,CAAK,EACxBF,GACR,CAAC,CAAC,CACT,CD7DO,SAASG,EAAoBC,EAA+B,CAC/D,MAAO,OAAOC,GAAwC,CAClD,GAAM,CAAE,MAAAC,EAAO,KAAAC,EAAM,WAAAC,EAAY,YAAAC,CAAY,EAAI,MAAMJ,EAAQ,KAAK,EAC9DK,EAASC,EAAe,CAAE,MAAAL,EAAO,KAAAC,EAAM,WAAAC,EAAY,YAAAC,CAAY,CAAC,EAEtE,OAAOG,EAAmBR,EAAQ,MAAOA,EAAQ,KAAMM,EAAQN,EAAQ,UAAU,CACrF,CACJ,CASO,SAASS,EAA4BT,EAA+B,CACvE,MAAO,OAAOC,GAAwC,CAClD,GAAM,CAAE,QAAAS,EAAS,SAAAC,EAAU,KAAAR,CAAK,EAAI,MAAMF,EAAQ,KAAK,EACjDK,EAAS,GAAGI,CAAO;AAAA,YAAeC,CAAQ;AAAA,eAA6BR,CAAI,iBACjF,OAAOK,EAAmBR,EAAQ,MAAOA,EAAQ,KAAMM,EAAQN,EAAQ,UAAU,CACrF,CACJ,CAMA,SAASQ,EACLI,EACAC,EACAP,EACAQ,EACQ,CAOR,OANeC,EAAW,CACtB,MAAAH,EACA,OAAQC,EACR,OAAAP,EACA,SAAU,CAAC,CAAE,KAAAU,CAAK,IAAMF,GAAA,YAAAA,EAAaE,EACzC,CAAC,EACa,qBAAqB,CACvC","names":["streamText","constructInput","value","skeletonize","components","hint","allowedComponents","item","customComponents","componentContext","inputValue","createSkeleton","createSkeleton","input","acc","key","value","createSyntuxHandler","options","request","value","hint","components","skeletonize","prompt","constructInput","makeStreamResponse","createSyntuxRerenderHandler","context","existing","model","spec","onGenerate","streamText","text"]}
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "getsyntux",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Build generative UIs for the web.",
5
+ "keywords": ["generative-ui"],
5
6
  "exports": {
6
7
  ".": {
7
8
  "types": "./dist/index.d.ts",
@@ -12,6 +13,11 @@
12
13
  "types": "./dist/client.d.ts",
13
14
  "import": "./dist/client.mjs",
14
15
  "require": "./dist/client.js"
16
+ },
17
+ "./server": {
18
+ "types": "./dist/server.d.ts",
19
+ "import": "./dist/server.mjs",
20
+ "require": "./dist/server.js"
15
21
  }
16
22
  },
17
23
  "bin": {
@@ -28,7 +34,6 @@
28
34
  },
29
35
  "homepage": "https://github.com/puffinsoft/syntux#readme",
30
36
  "dependencies": {
31
- "@ai-sdk/rsc": "^2.0.3",
32
37
  "@types/node": "^25.0.2",
33
38
  "@types/react": "^19.2.7",
34
39
  "commander": "^14.0.2",
@@ -1,92 +0,0 @@
1
- import { JSX } from 'react';
2
-
3
- import { createStreamableValue, StreamableValue } from '@ai-sdk/rsc';
4
- import { LanguageModel, streamText } from 'ai';
5
-
6
- import { AnimateOptions, constructInput, generateComponentMap, ResponseParser, SyntuxComponent, UISchema } from "getsyntux";
7
- import { GeneratedClient, Renderer } from 'getsyntux/client';
8
-
9
- import spec from './spec';
10
-
11
- export interface GeneratedContentProps {
12
- value: any;
13
- model: LanguageModel;
14
- components?: (SyntuxComponent | string)[];
15
- hint?: string;
16
- placeholder?: JSX.Element;
17
- cached?: string;
18
- /*
19
- * ^ this is a string for two reasons:
20
- * - it is easier to store
21
- * - it is parsed then mutated at runtime. This avoids unintended side effects.
22
- */
23
- onGenerate?: (arg0: string) => void;
24
- skeletonize?: boolean;
25
-
26
- onError?: (arg0: any) => void;
27
- errorFallback?: JSX.Element;
28
- animate?: AnimateOptions
29
-
30
- rerender?: (arg0: string, arg1: string, arg2: string) => Promise<{ value: StreamableValue }>
31
- }
32
-
33
- /**
34
- * Section of user interface for LLM to generate.
35
- * @param values The values (object, primitive, or array) to be displayed.
36
- * @param model The LanguageModel (as provided from AI SDK) to use. Must support streaming
37
- * @param components List of allowed components that LLM can use.
38
- * @param hint Additional custom instructions for the LLM.
39
- * @param placeholder A placeholder to show while awaiting streaming (NOT during streaming)
40
- * @param cached Schema returned from onGenerate, used for caching UI
41
- * @param onGenerate Callback which accepts a string, to be passed to `cached` to reuse same UI
42
- */
43
- export async function GeneratedUI(props: GeneratedContentProps) {
44
- const input = constructInput(props);
45
- const rerenderContext = input.split('\n').slice(0, 2);
46
-
47
- const { value, model, components, placeholder, cached, onGenerate, rerender } = props;
48
-
49
- const allowedComponents = generateComponentMap(components || []);
50
-
51
- // prerender if cached
52
- if(cached){
53
- const parser = new ResponseParser();
54
- parser.addDelta(cached);
55
- parser.finish();
56
-
57
- const schema: UISchema = parser.schema;
58
-
59
- if(schema.root){
60
- return <Renderer id={schema.root.id} componentMap={schema.componentMap} childrenMap={schema.childrenMap}
61
- allowedComponents={allowedComponents} global={value} local={value} />
62
- } else {
63
- return <></>;
64
- }
65
- }
66
-
67
- const stream = createStreamableValue('');
68
-
69
- (async () => {
70
- let total = "";
71
-
72
- const { textStream } = await streamText({
73
- model,
74
- system: spec,
75
- prompt: input
76
- })
77
-
78
- for await(const delta of textStream){
79
- stream.update(delta);
80
- total += delta;
81
- }
82
-
83
- stream.done();
84
-
85
- if(onGenerate) onGenerate(total);
86
- })()
87
-
88
- return <GeneratedClient value={value} allowedComponents={allowedComponents} inputStream={stream.value} placeholder={placeholder} rerender={{
89
- context: rerenderContext.join('\n'),
90
- action: rerender
91
- }} />
92
- }
@@ -1,54 +0,0 @@
1
- "use server";
2
-
3
- /**
4
- * this file is optional!
5
- * you can delete it if you do not need rerender functionality.
6
- *
7
- * run "npm i @ai-sdk/anthropic" if you want to use Claude.
8
- */
9
- import { createAnthropic } from "@ai-sdk/anthropic";
10
- import { createStreamableValue } from "@ai-sdk/rsc";
11
- import { streamText } from "ai";
12
-
13
- import spec from "./spec";
14
-
15
- /**
16
- * IMPORTANT: replace the below with your own model.
17
- */
18
- const anthropic = createAnthropic({
19
- apiKey: "..."
20
- })
21
- const model = anthropic("claude-haiku-4-5");
22
-
23
- /**
24
- * The server action for rerendering the UI.
25
- * This is an UNSECURED ENDPOINT. Please modify it to perform your own authentication etc,.
26
- *
27
- * @param context information about allowed components, component context etc,.
28
- * @param existing the current UI schema.
29
- * @param hint the update request.
30
- */
31
- export async function rerenderAction(context: string, existing: string, hint: string) {
32
- const stream = createStreamableValue('');
33
- (async () => {
34
- let total = "";
35
- let errored = false;
36
-
37
- const { textStream } = await streamText({
38
- model,
39
- system: spec,
40
- prompt: `${context}\n<Existing>${existing}</Existing>\n<UserContext>${hint}</UserContext>`,
41
- })
42
-
43
- for await (const delta of textStream) {
44
- stream.update(delta);
45
- total += delta;
46
- }
47
-
48
- if (!errored) {
49
- stream.done();
50
- }
51
- })()
52
-
53
- return { value: stream.value }
54
- }
@@ -1,45 +0,0 @@
1
- import { StreamableValue } from '@ai-sdk/rsc';
2
-
3
- type SchemaNode = {
4
- id: string;
5
- parentId: string | null;
6
- type: string;
7
- props?: Record<string, any>;
8
- content?: any | {
9
- "$bind": string;
10
- };
11
- };
12
- type ComponentMap = Record<string, SchemaNode>;
13
- type ChildrenMap = Record<string, string[]>;
14
- type UISchema = {
15
- componentMap: ComponentMap;
16
- childrenMap: ChildrenMap;
17
- root: SchemaNode | null;
18
- };
19
- type SyntuxComponent = {
20
- name: string;
21
- props: string;
22
- component: React.ComponentType<any>;
23
- context?: string;
24
- };
25
- type AnimateOptions = {
26
- offset: number;
27
- duration: number;
28
- };
29
- /**
30
- * setValue will not send a request if regenerate is false.
31
- * however, the value will still be updated (statically).
32
- * as opposed to a falsy options existence check, this is more robust for DX.
33
- */
34
- type RerenderOptions = {
35
- regenerate: boolean;
36
- hint: string;
37
- };
38
- type RerenderContext = {
39
- context: string;
40
- action?: (arg0: string, arg1: string, arg2: string) => Promise<{
41
- value: StreamableValue;
42
- }>;
43
- };
44
-
45
- export type { AnimateOptions as A, ChildrenMap as C, RerenderContext as R, SyntuxComponent as S, UISchema as U, ComponentMap as a, RerenderOptions as b, SchemaNode as c };