feat(blocks): add typed personalization resolution
Quality / quality (ubuntu-latest) (push) Failing after 9m58s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
Clintchiz committed 2026-09-05 11:23:02 +05:30
1 parent 2428df6e78
commit 157eec7a78
9 files changed
+128 -12

No files matched your search

+22 -1
View File
@@ -10,6 +10,8 @@ import {
json,
object,
record,
resolveDocumentVariables,
safeUrl,
string,
validateDocument,
type BlockDocument,
@@ -69,7 +71,7 @@ const kind = defineDocumentKind({
metadata: object({ title: string() }) as never,
rootTypes: ["layout.root"],
blocks: [root, text],
variables: {},
variables: { recipientName: "string" },
breakpoints: {},
capabilities: [],
targets: ["html"],
@@ -169,4 +171,23 @@ describe("dynamic blocks engine", () => {
"a", "a-copy",
]);
});
test("validates and resolves typed personalization tokens", () => {
const document = doc();
document.root.slots.content = [
{ ...text.create({ id: () => "personalized" }), props: { text: "Hi {{recipientName}}" } },
];
expect(validateDocument(document, kind, registry).ok).toBe(true);
const resolved = resolveDocumentVariables(document, kind.variables, { recipientName: "Ada" });
expect(resolved.ok && resolved.value.root.slots.content[0]?.props.text).toBe("Hi Ada");
document.root.slots.content[0]!.props.text = "{{notAllowed}}";
expect(validateDocument(document, kind, registry).ok).toBe(false);
});
test("safe URLs allow bounded variable templates without allowing unsafe schemes", () => {
const schema = safeUrl({ schemes: ["https"] });
expect(schema.parse("https://example.com/u/{{recipientName}}").ok).toBe(true);
expect(schema.parse("{{unsubscribeUrl}}").ok).toBe(true);
expect(schema.parse("javascript:alert(1)").ok).toBe(false);
});
});