tangled
alpha
login
or
join now
danabra.mov
/
inlay
42
fork
atom
social components
inlay-proto.up.railway.app/
atproto
components
sdui
42
fork
atom
overview
issues
pulls
pipelines
always validate
danabra.mov
6 days ago
d3ae060e
96349168
+9
-23
3 changed files
expand all
collapse all
unified
split
packages
@inlay
render
README.md
src
index.ts
test
render.test.ts
+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)
0
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;
0
51
};
52
53
/**
···
120
const ctx = slotContexts.get(element) ?? context;
121
const depth = ctx.depth ?? 0;
122
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
0
123
124
let errorStack = ctx.stack;
125
···
146
ctx.componentUri,
147
element,
148
props,
149
+
ctx
0
150
);
151
}
152
···
189
componentUri,
190
element,
191
props,
192
+
ctx
0
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
0
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);
0
0
0
0
0
0
0
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(
0
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