social components inlay-proto.up.railway.app/
atproto components sdui

always validate

+9 -23
+2 -3
packages/@inlay/render/README.md
··· 88 89 if (isValidElement(node)) { 90 const { resolved, node: out, context: outCtx } = await render( 91 - node, ctx, { resolver, validate: false } 92 ); 93 if (resolved && isValidElement(out)) { 94 const Primitive = primitives[out.type]; ··· 168 **Render options:** 169 - `resolver: Resolver` — I/O callbacks (required) 170 - `maxDepth?: number` — nesting limit (default 30) 171 - - `validate?: boolean` — prop validation (default true) 172 173 ### Types 174 175 - **`Resolver`** — `{ fetchRecord, xrpc, resolveLexicon }` 176 - **`RenderContext`** — `{ imports, component?, componentUri?, depth?, scope?, stack? }` 177 - **`RenderResult`** — `{ resolved, node, context, cache? }` 178 - - **`RenderOptions`** — `{ resolver, maxDepth?, validate? }` 179 - **`ComponentRecord`** — re-exported from generated lexicon defs 180 - **`CachePolicy`** — re-exported from generated lexicon defs
··· 88 89 if (isValidElement(node)) { 90 const { resolved, node: out, context: outCtx } = await render( 91 + node, ctx, { resolver } 92 ); 93 if (resolved && isValidElement(out)) { 94 const Primitive = primitives[out.type]; ··· 168 **Render options:** 169 - `resolver: Resolver` — I/O callbacks (required) 170 - `maxDepth?: number` — nesting limit (default 30) 171 172 ### Types 173 174 - **`Resolver`** — `{ fetchRecord, xrpc, resolveLexicon }` 175 - **`RenderContext`** — `{ imports, component?, componentUri?, depth?, scope?, stack? }` 176 - **`RenderResult`** — `{ resolved, node, context, cache? }` 177 + - **`RenderOptions`** — `{ resolver, maxDepth? }` 178 - **`ComponentRecord`** — re-exported from generated lexicon defs 179 - **`CachePolicy`** — re-exported from generated lexicon defs
+4 -16
packages/@inlay/render/src/index.ts
··· 48 export type RenderOptions = { 49 resolver: Resolver; 50 maxDepth?: number; 51 - validate?: boolean; 52 }; 53 54 /** ··· 121 const ctx = slotContexts.get(element) ?? context; 122 const depth = ctx.depth ?? 0; 123 const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH; 124 - const validate = options.validate ?? true; 125 126 let errorStack = ctx.stack; 127 ··· 148 ctx.componentUri, 149 element, 150 props, 151 - ctx, 152 - validate 153 ); 154 } 155 ··· 192 componentUri, 193 element, 194 props, 195 - ctx, 196 - validate 197 ); 198 } catch (e) { 199 if (e instanceof MissingError) throw e; ··· 270 componentUri: string | undefined, 271 element: Element, 272 props: Record<string, unknown>, 273 - ctx: RenderContext, 274 - validate: boolean 275 ): Promise<RenderResult> { 276 const depth = ctx.depth ?? 0; 277 const type = element.type; ··· 281 resolvedProps = expandBareDid(resolvedProps, component.view); 282 } 283 284 - if (validate) { 285 - resolvedProps = await validateProps( 286 - type, 287 - resolvedProps, 288 - component, 289 - resolver 290 - ); 291 - } 292 293 // Primitive: no body, return element with resolved props 294 if (!component.body) {
··· 48 export type RenderOptions = { 49 resolver: Resolver; 50 maxDepth?: number; 51 }; 52 53 /** ··· 120 const ctx = slotContexts.get(element) ?? context; 121 const depth = ctx.depth ?? 0; 122 const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH; 123 124 let errorStack = ctx.stack; 125 ··· 146 ctx.componentUri, 147 element, 148 props, 149 + ctx 150 ); 151 } 152 ··· 189 componentUri, 190 element, 191 props, 192 + ctx 193 ); 194 } catch (e) { 195 if (e instanceof MissingError) throw e; ··· 266 componentUri: string | undefined, 267 element: Element, 268 props: Record<string, unknown>, 269 + ctx: RenderContext 270 ): Promise<RenderResult> { 271 const depth = ctx.depth ?? 0; 272 const type = element.type; ··· 276 resolvedProps = expandBareDid(resolvedProps, component.view); 277 } 278 279 + resolvedProps = await validateProps(type, resolvedProps, component, resolver); 280 281 // Primitive: no body, return element with resolved props 282 if (!component.body) {
+3 -4
packages/@inlay/render/test/render.test.ts
··· 2919 assert.deepEqual(output, h("span", { value: "Alice" })); 2920 }); 2921 2922 - it("does not expand DID when viewRecord has no rkey", async () => { 2923 const post = "test.app.Post" as const; 2924 const postComponent: ComponentRecord = { 2925 $type: "at.inlay.component", ··· 2927 body: { 2928 $type: "at.inlay.component#bodyTemplate", 2929 node: serializeTree( 2930 - // Bind to uri — shows exactly what the component received 2931 $(Text, { value: $(Binding, { path: ["props", "uri"] }) }) 2932 ), 2933 }, ··· 2948 2949 const output = await renderToCompletion( 2950 $(post, { uri: "did:plc:alice" }), 2951 - { resolver, validate: false }, 2952 createContext(postComponent) 2953 ); 2954 2955 - assert.deepEqual(output, h("span", { value: "did:plc:alice" })); 2956 }); 2957 }); 2958
··· 2919 assert.deepEqual(output, h("span", { value: "Alice" })); 2920 }); 2921 2922 + it("rejects bare DID when viewRecord has no rkey", async () => { 2923 const post = "test.app.Post" as const; 2924 const postComponent: ComponentRecord = { 2925 $type: "at.inlay.component", ··· 2927 body: { 2928 $type: "at.inlay.component#bodyTemplate", 2929 node: serializeTree( 2930 $(Text, { value: $(Binding, { path: ["props", "uri"] }) }) 2931 ), 2932 }, ··· 2947 2948 const output = await renderToCompletion( 2949 $(post, { uri: "did:plc:alice" }), 2950 + { resolver }, 2951 createContext(postComponent) 2952 ); 2953 2954 + assert.equal((output as any).tag, "error"); 2955 }); 2956 }); 2957