Skip to content

Commit

Permalink
feat: Support nested unions in typedUnion (#2693)
Browse files Browse the repository at this point in the history
Add support for nested unions in `typedUnion`. The structs will be
flattened before validation occurs.

Progresses #2692

---------

Co-authored-by: MetaMask Bot <[email protected]>
  • Loading branch information
FrederikBolding and metamaskbot authored Sep 3, 2024
1 parent bd23ab7 commit 5f82540
Show file tree
Hide file tree
Showing 32 changed files with 59 additions and 39 deletions.
2 changes: 1 addition & 1 deletion packages/examples/packages/bip32/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "YL3tBwZZwvriXQEJ81yqfWgY9sBtZcmhLxrFsufIZEA=",
"shasum": "4BL6Jd1ugM5bMcgSAHY2pLElwTfDUFjq6w0n2O/Im4U=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/bip44/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "68Bn2bAFeEaOGFZrBquwB8Xd2D0ByTroomTP4XrwUbI=",
"shasum": "abe6r4FVjGVzWmhzIIx0fYZNADgr4VdtltsDeb2BE28=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "5RPHrRu37v31Y+MBPQnvNWxbJr1qiBJ6qc7sFqBR76M=",
"shasum": "fxRFuVGOLsP/ALHeZXwlq8PIarpFGCyqkNviBX0hB7A=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/browserify/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "jHJ8pWAqkkbT2tUNeszJmlGWIrPGQzVv0tG7EqjZjVo=",
"shasum": "jXLaAeTYhjHiQca4Y1UQOmySIuJ3RGQ9mJNc4er/4ZA=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "0FhVIsJy7+ReFyDOG57j2hesl4qm0j4uyv+S0D+A+F4=",
"shasum": "mQIYo1bLokB7M9qQJxG35svVVPksCoOOeeRDDtiu7HU=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/cronjobs/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "jAHcphna3zBODV4jICIXFJgaeU5ixid/yY9qSzdhA00=",
"shasum": "NcfRhmrZNMUJOc9h+n6KiCo/Ot0aeoJ9WWJSy2HL1Gg=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/dialogs/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "I9cJhiNXqy5K8X7hlt2xjhsYnfQlExf6RrAdG3r6pJk=",
"shasum": "uWEAITpuEjxaw2+B0pq0gmg+FTOqzM9jv2O9KyiivdQ=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "q1UkLNVWoeCw2yIhmR1NGZS84dyWGYHcpYaN/PhkQSU=",
"shasum": "tBYEmIv6cCEk42IQ5CSOIIkh9QmB945KyoW8cSl1P/o=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/ethers-js/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "8pCXeweoc4+vXywsbAA+nGeARrjr4x5UsscK+r18PlU=",
"shasum": "eEqE9FxXKHDEw6FFYb0bCijPQk18wNs6m+SaBHbZzxA=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/file-upload/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "Y7a5cHmd4QyPJHRefvpIQg6EpSqpBi0Q9XgFCHmPbdA=",
"shasum": "jRN7tvhMpGou1Nkz4K8DiNTMkVydzknsJ+tP2K5aW9E=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/get-entropy/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "oyXbM/tktEz0+fiVnBtfrjYKmw93o6D/WcZVPBIHpog=",
"shasum": "47IeGhwtRAwOhUeI/oC2AE6kinf0nakTZqgRl/lgDIo=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/get-file/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "b13cFbJMZrzvNHq6/0agErncOEY8/5JgQ4l4t5K3fOw=",
"shasum": "A7lRE1zaLuwC5B4cqFuwT1os0b9OhgzCfrGsAqG0uZM=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/home-page/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "25JkQfhnvgUgOxpEw89f542IjY3hIMPtf9CrYhosEn4=",
"shasum": "/2H3Z/T9NtPu9bi4oTQl8STDvd2hS6Y38WD42eZFu2Q=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/images/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "wpiBios6W96jCD5Juqn+LPj8JfKaQmRuptSuUP4XDw0=",
"shasum": "VzvCIKqxh7xkhpY3JgZrZbsgB4rtMV4ux6d5aYxLK+0=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "hn4aNEH2sIhFs8fgEAgsmHeo0Sz1Q+1AMlxJkX5ucK0=",
"shasum": "jpBXpGnue+kKtRDoadC8MBWahxpHxvH6CYrPnPQzcrE=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "eIerqKNbNZq8pToV/+x7jZue/db+ZYBO6JaimNvOQ9E=",
"shasum": "GzEMmSxdzB7yXYXbiWDYj+hYcDsjbiVOYy3tsfGpUFE=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "OLsKBfLSijAgw3h6BRI2STgGb72arqgsmB/8iJ+GjBM=",
"shasum": "Dq88h1Jg8IugLbV6bu7YlWycxbzjFitBnvx0vSK3E7Q=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/json-rpc/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "XHz2QihCWyO3AkogObjFYP1hEYIw2EYoI3n4V0lF8is=",
"shasum": "eAXZmOyliFI5XNCUU1/1yUKF/ifrgpD320x9wOpCVeE=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/jsx/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "SCKlJ20hnBxI20+5ofiX8CN82x52Tga5K0SzEOr3I9I=",
"shasum": "kb0XwfkEk3arGA858y9W6TiMBTJqZnfatcQvu331A/0=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "1CfKV/FrMYNEmW7p0ALCwxGPYNu+1acohAHxvTwc50I=",
"shasum": "XjQQe1QEuXCd9cuKib4y1SWp5nLMCxEMGPB6D96pQbw=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/localization/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "nrRcIDz46Yv59prxSp8PgodvnJzQzbODSdyf1FOP0p4=",
"shasum": "jrbRHzmOPuyHJocVX8Sq+qoV8KAcJHAaPxMCZemU+E0=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/manage-state/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "/EBquYb00j9+pA1om64GJTvwrzTnarBW1yOQWXgqw0A=",
"shasum": "8kvitczxyMNE1BV9aqDIlMcmY5uaU3mI1IqKigbrW00=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "DMlhexJQ5cGP4ITfYz8B3alGvGKGDvxObhWKPoCpo1s=",
"shasum": "PtScya2yLdGfSz4hZFdXDM3YWw4vw5QXctoZZ78VTWo=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "it5ZninU66hvjgdYpE0mxQO++uDM+Hkl3fGkBn1F6Oc=",
"shasum": "Dv5+/l1uok0muzLpmvq5yyITG1Td6kZOjLuDKBhNjJc=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "w3ABxR7hs6ncp7rXGzo6HWDx54GkotIhAHHq2mswMdY=",
"shasum": "/MVd7yap6qpqkzwLVAX88fbyDyXAH4NtremDShWw1FY=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "/w9bdq7DBuDOjbPnouCgduRrpt1sNqGwVXxqw2iK03M=",
"shasum": "xRnWfG3zdelOzrmPEo2HFYX2Qmj7e7OExF23FO4+tXg=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "BDJ5kvWUqXV8HryKVd6TIe735KhMLbFERx0jU3url+k=",
"shasum": "wjv+gvLqJbyxldS1LZmJaEUs/1aAkO1whl0LCMdddCk=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/packages/wasm/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "Mi/+oiYGqXLlUmf/MXJNuqNiWUJ392+ot2+v5eD8GnY=",
"shasum": "/lWk6yg2McC875r/z6DnNqjcmVjDBDu/QENuDJEqiBo=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snaps.git"
},
"source": {
"shasum": "RuaNyr2DVh0BcfUTZuP/y5kYi2MSuC8qelLoc8IyjN8=",
"shasum": "m5aK+J1xwQhJT4lu/z15NYSFKmlxsYFu9RhhwlfrknY=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
14 changes: 14 additions & 0 deletions packages/snaps-sdk/src/internals/structs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ describe('literal', () => {

describe('typedUnion', () => {
const unionStruct = typedUnion([BoxStruct, TextStruct, FieldStruct]);
const nestedUnionStruct = typedUnion([
BoxStruct,
typedUnion([TextStruct, FieldStruct]),
]);

it('validates strictly the part of the union that matches the type', () => {
// @ts-expect-error Invalid props.
const result = validate(Text({}), unionStruct);
Expand All @@ -52,6 +57,15 @@ describe('typedUnion', () => {
);
});

it('validates nested unions', () => {
// @ts-expect-error Invalid props.
const result = validate(Text({}), nestedUnionStruct);

expect(result[0]?.message).toBe(
'At path: props.children -- Expected the value to satisfy a union of `union | array`, but received: undefined',
);
});

it('returns an error if the value has no type', () => {
const result = validate({}, unionStruct);

Expand Down
15 changes: 11 additions & 4 deletions packages/snaps-sdk/src/internals/structs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,23 @@ export function enumValue<Type extends string>(
export function typedUnion<Head extends AnyStruct, Tail extends AnyStruct[]>(
structs: [head: Head, ...tail: Tail],
): Struct<Infer<Head> | InferStructTuple<Tail>[number], null> {
const flatStructs = structs
.map((struct) =>
struct.type === 'union' && Array.isArray(struct.schema)
? struct.schema
: struct,
)
.flat(Infinity);
return new Struct({
type: 'union',
schema: structs,
schema: flatStructs,
*entries(value, context) {
if (!isPlainObject(value) || !hasProperty(value, 'type')) {
return;
}

const { type } = value;
const struct = structs.find(({ schema }) => is(type, schema.type));
const struct = flatStructs.find(({ schema }) => is(type, schema.type));

if (!struct) {
return;
Expand All @@ -110,7 +117,7 @@ export function typedUnion<Head extends AnyStruct, Tail extends AnyStruct[]>(
}
},
validator(value, context) {
const types = structs.map(({ schema }) => schema.type.type);
const types = flatStructs.map(({ schema }) => schema.type.type);

if (
!isPlainObject(value) ||
Expand All @@ -124,7 +131,7 @@ export function typedUnion<Head extends AnyStruct, Tail extends AnyStruct[]>(

const { type } = value;

const struct = structs.find(({ schema }) => is(type, schema.type));
const struct = flatStructs.find(({ schema }) => is(type, schema.type));

if (struct) {
// This only validates the root of the struct, entries does the rest of the work.
Expand Down
11 changes: 5 additions & 6 deletions packages/snaps-sdk/src/jsx/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ const FIELD_CHILDREN_ARRAY = [
* A union of the allowed children of the Field component.
* This is mainly used in the simulator for validation purposes.
*/
export const FieldChildUnionStruct = nullUnion([
export const FieldChildUnionStruct = typedUnion([
...FIELD_CHILDREN_ARRAY,
...BUTTON_INPUT,
]);
Expand Down Expand Up @@ -380,10 +380,9 @@ export const ItalicStruct: Describe<ItalicElement> = element('Italic', {
]),
});

export const FormattingStruct: Describe<StandardFormattingElement> = nullUnion([
BoldStruct,
ItalicStruct,
]);
export const FormattingStruct: Describe<StandardFormattingElement> = typedUnion(
[BoldStruct, ItalicStruct],
);

/**
* A struct for the {@link AddressElement} type.
Expand Down Expand Up @@ -641,7 +640,7 @@ export const BoxChildStruct = typedUnion([
* For now, the allowed JSX elements at the root are the same as the allowed
* children of the Box component.
*/
export const RootJSXElementStruct = nullUnion([
export const RootJSXElementStruct = typedUnion([
BoxChildStruct,
ContainerStruct,
]);
Expand Down

0 comments on commit 5f82540

Please sign in to comment.