@farcaster/snap 1.5.0 → 1.5.1
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/dist/constants.d.ts +12 -2
- package/dist/constants.js +19 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/schemas.d.ts +131 -10
- package/dist/schemas.js +125 -32
- package/dist/server/parseRequest.d.ts +6 -0
- package/dist/server/parseRequest.js +1 -2
- package/dist/ui/button-group.d.ts +10 -2
- package/dist/ui/button-group.js +12 -4
- package/dist/ui/button.d.ts +6 -4
- package/dist/ui/button.js +2 -1
- package/dist/ui/catalog.d.ts +17 -9
- package/dist/ui/catalog.js +7 -4
- package/dist/ui/group.d.ts +0 -1
- package/dist/ui/group.js +2 -1
- package/dist/ui/schema.js +1 -1
- package/dist/ui/spacer.d.ts +1 -1
- package/dist/ui/spacer.js +2 -2
- package/dist/ui/toggle.d.ts +1 -1
- package/dist/ui/toggle.js +1 -1
- package/package.json +1 -1
- package/src/constants.ts +22 -2
- package/src/index.ts +4 -0
- package/src/schemas.ts +141 -40
- package/src/server/parseRequest.ts +8 -2
- package/src/ui/button-group.ts +22 -9
- package/src/ui/button.ts +2 -1
- package/src/ui/catalog.ts +8 -4
- package/src/ui/group.ts +2 -1
- package/src/ui/schema.ts +1 -1
- package/src/ui/spacer.ts +2 -2
- package/src/ui/toggle.ts +1 -1
package/dist/constants.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ export declare const LIST_STYLE_VALUES: readonly ["ordered", "unordered", "plain
|
|
|
42
42
|
export declare const DEFAULT_LIST_STYLE: "ordered";
|
|
43
43
|
export declare const GRID_CELL_SIZE_VALUES: readonly ["auto", "square"];
|
|
44
44
|
export declare const GRID_GAP_VALUES: readonly ["none", "small", "medium"];
|
|
45
|
+
export declare const DEFAULT_GRID_GAP: "small";
|
|
45
46
|
export declare const BUTTON_GROUP_STYLE: {
|
|
46
47
|
readonly row: "row";
|
|
47
48
|
readonly stack: "stack";
|
|
@@ -52,9 +53,18 @@ export declare const BUTTON_ACTION: {
|
|
|
52
53
|
readonly post: "post";
|
|
53
54
|
readonly link: "link";
|
|
54
55
|
readonly mini_app: "mini_app";
|
|
55
|
-
readonly
|
|
56
|
+
readonly client: "client";
|
|
56
57
|
};
|
|
57
|
-
export declare const BUTTON_ACTION_VALUES: readonly ["post", "link", "mini_app", "
|
|
58
|
+
export declare const BUTTON_ACTION_VALUES: readonly ["post", "link", "mini_app", "client"];
|
|
59
|
+
export declare const CLIENT_ACTION: {
|
|
60
|
+
readonly view_cast: "view_cast";
|
|
61
|
+
readonly view_profile: "view_profile";
|
|
62
|
+
readonly compose_cast: "compose_cast";
|
|
63
|
+
readonly view_token: "view_token";
|
|
64
|
+
readonly send_token: "send_token";
|
|
65
|
+
readonly swap_token: "swap_token";
|
|
66
|
+
};
|
|
67
|
+
export declare const CLIENT_ACTION_VALUES: readonly ["view_cast", "view_profile", "compose_cast", "view_token", "send_token", "swap_token"];
|
|
58
68
|
export declare const BUTTON_STYLE: {
|
|
59
69
|
readonly primary: "primary";
|
|
60
70
|
readonly secondary: "secondary";
|
package/dist/constants.js
CHANGED
|
@@ -57,6 +57,7 @@ export const LIST_STYLE_VALUES = ["ordered", "unordered", "plain"];
|
|
|
57
57
|
export const DEFAULT_LIST_STYLE = "ordered";
|
|
58
58
|
export const GRID_CELL_SIZE_VALUES = ["auto", "square"];
|
|
59
59
|
export const GRID_GAP_VALUES = ["none", "small", "medium"];
|
|
60
|
+
export const DEFAULT_GRID_GAP = "small";
|
|
60
61
|
export const BUTTON_GROUP_STYLE = {
|
|
61
62
|
row: "row",
|
|
62
63
|
stack: "stack",
|
|
@@ -71,13 +72,29 @@ export const BUTTON_ACTION = {
|
|
|
71
72
|
post: "post",
|
|
72
73
|
link: "link",
|
|
73
74
|
mini_app: "mini_app",
|
|
74
|
-
|
|
75
|
+
client: "client",
|
|
75
76
|
};
|
|
76
77
|
export const BUTTON_ACTION_VALUES = [
|
|
77
78
|
BUTTON_ACTION.post,
|
|
78
79
|
BUTTON_ACTION.link,
|
|
79
80
|
BUTTON_ACTION.mini_app,
|
|
80
|
-
BUTTON_ACTION.
|
|
81
|
+
BUTTON_ACTION.client,
|
|
82
|
+
];
|
|
83
|
+
export const CLIENT_ACTION = {
|
|
84
|
+
view_cast: "view_cast",
|
|
85
|
+
view_profile: "view_profile",
|
|
86
|
+
compose_cast: "compose_cast",
|
|
87
|
+
view_token: "view_token",
|
|
88
|
+
send_token: "send_token",
|
|
89
|
+
swap_token: "swap_token",
|
|
90
|
+
};
|
|
91
|
+
export const CLIENT_ACTION_VALUES = [
|
|
92
|
+
CLIENT_ACTION.view_cast,
|
|
93
|
+
CLIENT_ACTION.view_profile,
|
|
94
|
+
CLIENT_ACTION.compose_cast,
|
|
95
|
+
CLIENT_ACTION.view_token,
|
|
96
|
+
CLIENT_ACTION.send_token,
|
|
97
|
+
CLIENT_ACTION.swap_token,
|
|
81
98
|
];
|
|
82
99
|
export const BUTTON_STYLE = {
|
|
83
100
|
primary: "primary",
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { POST_GRID_TAP_KEY, PAGE_ROOT_TYPE, ELEMENT_TYPE, MEDIA_TYPE, DEFAULT_LIST_STYLE, DEFAULT_SLIDER_STEP, } from "./constants.js";
|
|
1
|
+
export { POST_GRID_TAP_KEY, PAGE_ROOT_TYPE, ELEMENT_TYPE, MEDIA_TYPE, DEFAULT_LIST_STYLE, DEFAULT_SLIDER_STEP, CLIENT_ACTION, CLIENT_ACTION_VALUES, } from "./constants.js";
|
|
2
2
|
export { DEFAULT_THEME_ACCENT, PALETTE_COLOR, PALETTE_COLOR_ACCENT, PALETTE_COLOR_VALUES, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, type PaletteColor, } from "./colors.js";
|
|
3
|
-
export { ACTION_TYPE_GET, ACTION_TYPE_POST, snapResponseSchema, firstPageResponseSchema, payloadSchema, createDefaultDataStore, type Button, type Element, type Elements, type GroupChildElement, type SnapAction, type SnapPageElementInput, type SnapContext, type SnapResponse, type SnapHandlerResult, type SnapFunction, type SnapPayload, type DataStoreValue, type SnapDataStore, type SnapDataStoreOperations, } from "./schemas.js";
|
|
3
|
+
export { ACTION_TYPE_GET, ACTION_TYPE_POST, snapResponseSchema, firstPageResponseSchema, payloadSchema, clientActionSchema, createDefaultDataStore, type Button, type Element, type Elements, type GroupChildElement, type ClientAction, type SnapAction, type SnapPageElementInput, type SnapContext, type SnapResponse, type SnapHandlerResult, type SnapFunction, type SnapPayload, type DataStoreValue, type SnapDataStore, type SnapDataStoreOperations, } from "./schemas.js";
|
|
4
4
|
export { validateSnapResponse, validateFirstPageResponse, type ValidationResult, } from "./validator.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { POST_GRID_TAP_KEY, PAGE_ROOT_TYPE, ELEMENT_TYPE, MEDIA_TYPE, DEFAULT_LIST_STYLE, DEFAULT_SLIDER_STEP, } from "./constants.js";
|
|
1
|
+
export { POST_GRID_TAP_KEY, PAGE_ROOT_TYPE, ELEMENT_TYPE, MEDIA_TYPE, DEFAULT_LIST_STYLE, DEFAULT_SLIDER_STEP, CLIENT_ACTION, CLIENT_ACTION_VALUES, } from "./constants.js";
|
|
2
2
|
export { DEFAULT_THEME_ACCENT, PALETTE_COLOR, PALETTE_COLOR_ACCENT, PALETTE_COLOR_VALUES, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, } from "./colors.js";
|
|
3
|
-
export { ACTION_TYPE_GET, ACTION_TYPE_POST, snapResponseSchema, firstPageResponseSchema, payloadSchema, createDefaultDataStore, } from "./schemas.js";
|
|
3
|
+
export { ACTION_TYPE_GET, ACTION_TYPE_POST, snapResponseSchema, firstPageResponseSchema, payloadSchema, clientActionSchema, createDefaultDataStore, } from "./schemas.js";
|
|
4
4
|
export { validateSnapResponse, validateFirstPageResponse, } from "./validator.js";
|
package/dist/schemas.d.ts
CHANGED
|
@@ -1,13 +1,74 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
export declare const clientActionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
3
|
+
type: z.ZodLiteral<"view_cast">;
|
|
4
|
+
hash: z.ZodString;
|
|
5
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
6
|
+
type: z.ZodLiteral<"view_profile">;
|
|
7
|
+
fid: z.ZodNumber;
|
|
8
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
9
|
+
type: z.ZodLiteral<"compose_cast">;
|
|
10
|
+
text: z.ZodOptional<z.ZodString>;
|
|
11
|
+
embeds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
12
|
+
parent: z.ZodOptional<z.ZodObject<{
|
|
13
|
+
type: z.ZodLiteral<"cast">;
|
|
14
|
+
hash: z.ZodString;
|
|
15
|
+
}, z.core.$strict>>;
|
|
16
|
+
channelKey: z.ZodOptional<z.ZodString>;
|
|
17
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
18
|
+
type: z.ZodLiteral<"view_token">;
|
|
19
|
+
token: z.ZodString;
|
|
20
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
21
|
+
type: z.ZodLiteral<"send_token">;
|
|
22
|
+
token: z.ZodOptional<z.ZodString>;
|
|
23
|
+
amount: z.ZodOptional<z.ZodString>;
|
|
24
|
+
recipientFid: z.ZodOptional<z.ZodNumber>;
|
|
25
|
+
recipientAddress: z.ZodOptional<z.ZodString>;
|
|
26
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
27
|
+
type: z.ZodLiteral<"swap_token">;
|
|
28
|
+
sellToken: z.ZodOptional<z.ZodString>;
|
|
29
|
+
buyToken: z.ZodOptional<z.ZodString>;
|
|
30
|
+
sellAmount: z.ZodOptional<z.ZodString>;
|
|
31
|
+
}, z.core.$strict>], "type">;
|
|
32
|
+
export type ClientAction = z.infer<typeof clientActionSchema>;
|
|
2
33
|
declare const buttonSchema: z.ZodObject<{
|
|
3
34
|
label: z.ZodString;
|
|
4
35
|
action: z.ZodEnum<{
|
|
5
36
|
post: "post";
|
|
6
37
|
link: "link";
|
|
7
38
|
mini_app: "mini_app";
|
|
8
|
-
|
|
39
|
+
client: "client";
|
|
9
40
|
}>;
|
|
10
|
-
target: z.ZodString
|
|
41
|
+
target: z.ZodOptional<z.ZodString>;
|
|
42
|
+
client_action: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
43
|
+
type: z.ZodLiteral<"view_cast">;
|
|
44
|
+
hash: z.ZodString;
|
|
45
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
46
|
+
type: z.ZodLiteral<"view_profile">;
|
|
47
|
+
fid: z.ZodNumber;
|
|
48
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
49
|
+
type: z.ZodLiteral<"compose_cast">;
|
|
50
|
+
text: z.ZodOptional<z.ZodString>;
|
|
51
|
+
embeds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
52
|
+
parent: z.ZodOptional<z.ZodObject<{
|
|
53
|
+
type: z.ZodLiteral<"cast">;
|
|
54
|
+
hash: z.ZodString;
|
|
55
|
+
}, z.core.$strict>>;
|
|
56
|
+
channelKey: z.ZodOptional<z.ZodString>;
|
|
57
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
58
|
+
type: z.ZodLiteral<"view_token">;
|
|
59
|
+
token: z.ZodString;
|
|
60
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
61
|
+
type: z.ZodLiteral<"send_token">;
|
|
62
|
+
token: z.ZodOptional<z.ZodString>;
|
|
63
|
+
amount: z.ZodOptional<z.ZodString>;
|
|
64
|
+
recipientFid: z.ZodOptional<z.ZodNumber>;
|
|
65
|
+
recipientAddress: z.ZodOptional<z.ZodString>;
|
|
66
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
67
|
+
type: z.ZodLiteral<"swap_token">;
|
|
68
|
+
sellToken: z.ZodOptional<z.ZodString>;
|
|
69
|
+
buyToken: z.ZodOptional<z.ZodString>;
|
|
70
|
+
sellAmount: z.ZodOptional<z.ZodString>;
|
|
71
|
+
}, z.core.$strict>], "type">>;
|
|
11
72
|
style: z.ZodOptional<z.ZodEnum<{
|
|
12
73
|
primary: "primary";
|
|
13
74
|
secondary: "secondary";
|
|
@@ -230,7 +291,7 @@ declare const elementSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
230
291
|
auto: "auto";
|
|
231
292
|
square: "square";
|
|
232
293
|
}>>;
|
|
233
|
-
gap: z.
|
|
294
|
+
gap: z.ZodDefault<z.ZodEnum<{
|
|
234
295
|
small: "small";
|
|
235
296
|
medium: "medium";
|
|
236
297
|
none: "none";
|
|
@@ -546,7 +607,7 @@ declare const elementsSchema: z.ZodObject<{
|
|
|
546
607
|
auto: "auto";
|
|
547
608
|
square: "square";
|
|
548
609
|
}>>;
|
|
549
|
-
gap: z.
|
|
610
|
+
gap: z.ZodDefault<z.ZodEnum<{
|
|
550
611
|
small: "small";
|
|
551
612
|
medium: "medium";
|
|
552
613
|
none: "none";
|
|
@@ -885,7 +946,7 @@ export declare const snapResponseSchema: z.ZodObject<{
|
|
|
885
946
|
auto: "auto";
|
|
886
947
|
square: "square";
|
|
887
948
|
}>>;
|
|
888
|
-
gap: z.
|
|
949
|
+
gap: z.ZodDefault<z.ZodEnum<{
|
|
889
950
|
small: "small";
|
|
890
951
|
medium: "medium";
|
|
891
952
|
none: "none";
|
|
@@ -1129,9 +1190,39 @@ export declare const snapResponseSchema: z.ZodObject<{
|
|
|
1129
1190
|
post: "post";
|
|
1130
1191
|
link: "link";
|
|
1131
1192
|
mini_app: "mini_app";
|
|
1132
|
-
|
|
1193
|
+
client: "client";
|
|
1133
1194
|
}>;
|
|
1134
|
-
target: z.ZodString
|
|
1195
|
+
target: z.ZodOptional<z.ZodString>;
|
|
1196
|
+
client_action: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1197
|
+
type: z.ZodLiteral<"view_cast">;
|
|
1198
|
+
hash: z.ZodString;
|
|
1199
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1200
|
+
type: z.ZodLiteral<"view_profile">;
|
|
1201
|
+
fid: z.ZodNumber;
|
|
1202
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1203
|
+
type: z.ZodLiteral<"compose_cast">;
|
|
1204
|
+
text: z.ZodOptional<z.ZodString>;
|
|
1205
|
+
embeds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1206
|
+
parent: z.ZodOptional<z.ZodObject<{
|
|
1207
|
+
type: z.ZodLiteral<"cast">;
|
|
1208
|
+
hash: z.ZodString;
|
|
1209
|
+
}, z.core.$strict>>;
|
|
1210
|
+
channelKey: z.ZodOptional<z.ZodString>;
|
|
1211
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1212
|
+
type: z.ZodLiteral<"view_token">;
|
|
1213
|
+
token: z.ZodString;
|
|
1214
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1215
|
+
type: z.ZodLiteral<"send_token">;
|
|
1216
|
+
token: z.ZodOptional<z.ZodString>;
|
|
1217
|
+
amount: z.ZodOptional<z.ZodString>;
|
|
1218
|
+
recipientFid: z.ZodOptional<z.ZodNumber>;
|
|
1219
|
+
recipientAddress: z.ZodOptional<z.ZodString>;
|
|
1220
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1221
|
+
type: z.ZodLiteral<"swap_token">;
|
|
1222
|
+
sellToken: z.ZodOptional<z.ZodString>;
|
|
1223
|
+
buyToken: z.ZodOptional<z.ZodString>;
|
|
1224
|
+
sellAmount: z.ZodOptional<z.ZodString>;
|
|
1225
|
+
}, z.core.$strict>], "type">>;
|
|
1135
1226
|
style: z.ZodOptional<z.ZodEnum<{
|
|
1136
1227
|
primary: "primary";
|
|
1137
1228
|
secondary: "secondary";
|
|
@@ -1241,7 +1332,7 @@ export declare const firstPageResponseSchema: z.ZodObject<{
|
|
|
1241
1332
|
auto: "auto";
|
|
1242
1333
|
square: "square";
|
|
1243
1334
|
}>>;
|
|
1244
|
-
gap: z.
|
|
1335
|
+
gap: z.ZodDefault<z.ZodEnum<{
|
|
1245
1336
|
small: "small";
|
|
1246
1337
|
medium: "medium";
|
|
1247
1338
|
none: "none";
|
|
@@ -1485,9 +1576,39 @@ export declare const firstPageResponseSchema: z.ZodObject<{
|
|
|
1485
1576
|
post: "post";
|
|
1486
1577
|
link: "link";
|
|
1487
1578
|
mini_app: "mini_app";
|
|
1488
|
-
|
|
1579
|
+
client: "client";
|
|
1489
1580
|
}>;
|
|
1490
|
-
target: z.ZodString
|
|
1581
|
+
target: z.ZodOptional<z.ZodString>;
|
|
1582
|
+
client_action: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1583
|
+
type: z.ZodLiteral<"view_cast">;
|
|
1584
|
+
hash: z.ZodString;
|
|
1585
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1586
|
+
type: z.ZodLiteral<"view_profile">;
|
|
1587
|
+
fid: z.ZodNumber;
|
|
1588
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1589
|
+
type: z.ZodLiteral<"compose_cast">;
|
|
1590
|
+
text: z.ZodOptional<z.ZodString>;
|
|
1591
|
+
embeds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1592
|
+
parent: z.ZodOptional<z.ZodObject<{
|
|
1593
|
+
type: z.ZodLiteral<"cast">;
|
|
1594
|
+
hash: z.ZodString;
|
|
1595
|
+
}, z.core.$strict>>;
|
|
1596
|
+
channelKey: z.ZodOptional<z.ZodString>;
|
|
1597
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1598
|
+
type: z.ZodLiteral<"view_token">;
|
|
1599
|
+
token: z.ZodString;
|
|
1600
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1601
|
+
type: z.ZodLiteral<"send_token">;
|
|
1602
|
+
token: z.ZodOptional<z.ZodString>;
|
|
1603
|
+
amount: z.ZodOptional<z.ZodString>;
|
|
1604
|
+
recipientFid: z.ZodOptional<z.ZodNumber>;
|
|
1605
|
+
recipientAddress: z.ZodOptional<z.ZodString>;
|
|
1606
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
1607
|
+
type: z.ZodLiteral<"swap_token">;
|
|
1608
|
+
sellToken: z.ZodOptional<z.ZodString>;
|
|
1609
|
+
buyToken: z.ZodOptional<z.ZodString>;
|
|
1610
|
+
sellAmount: z.ZodOptional<z.ZodString>;
|
|
1611
|
+
}, z.core.$strict>], "type">>;
|
|
1491
1612
|
style: z.ZodOptional<z.ZodEnum<{
|
|
1492
1613
|
primary: "primary";
|
|
1493
1614
|
secondary: "secondary";
|
package/dist/schemas.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { BUTTON_ACTION, BUTTON_ACTION_VALUES, BUTTON_GROUP_STYLE, BUTTON_GROUP_STYLE_VALUES, BUTTON_LAYOUT_VALUES, BUTTON_STYLE_VALUES, DEFAULT_BUTTON_LAYOUT, DEFAULT_LIST_STYLE, DEFAULT_SLIDER_STEP, EFFECT_VALUES, ELEMENT_TYPE, GRID_CELL_SIZE_VALUES, GRID_GAP_VALUES, GROUP_LAYOUT_VALUES, HEX_COLOR_6_RE, HTTP_PREFIX, HTTPS_PREFIX, IMAGE_ASPECT_VALUES, INTERACTIVE_ELEMENT_TYPES, LIMITS, LIST_STYLE_VALUES, MEDIA_ELEMENT_TYPES, PAGE_ROOT_TYPE, SLIDER_STEP_ALIGN_EPS, SPACER_SIZE, SPACER_SIZE_VALUES, SPEC_VERSION, TEXT_ALIGN_VALUES, TEXT_CONTENT_MAX, TEXT_STYLE, TEXT_STYLE_VALUES, } from "./constants.js";
|
|
2
|
+
import { BUTTON_ACTION, BUTTON_ACTION_VALUES, CLIENT_ACTION, BUTTON_GROUP_STYLE, BUTTON_GROUP_STYLE_VALUES, BUTTON_LAYOUT_VALUES, BUTTON_STYLE_VALUES, DEFAULT_BUTTON_LAYOUT, DEFAULT_GRID_GAP, DEFAULT_LIST_STYLE, DEFAULT_SLIDER_STEP, EFFECT_VALUES, ELEMENT_TYPE, GRID_CELL_SIZE_VALUES, GRID_GAP_VALUES, GROUP_LAYOUT_VALUES, HEX_COLOR_6_RE, HTTP_PREFIX, HTTPS_PREFIX, IMAGE_ASPECT_VALUES, INTERACTIVE_ELEMENT_TYPES, LIMITS, LIST_STYLE_VALUES, MEDIA_ELEMENT_TYPES, PAGE_ROOT_TYPE, SLIDER_STEP_ALIGN_EPS, SPACER_SIZE, SPACER_SIZE_VALUES, SPEC_VERSION, TEXT_ALIGN_VALUES, TEXT_CONTENT_MAX, TEXT_STYLE, TEXT_STYLE_VALUES, } from "./constants.js";
|
|
3
3
|
import { BAR_CHART_COLOR_VALUES, DEFAULT_THEME_ACCENT, PALETTE_COLOR_VALUES, PROGRESS_COLOR_VALUES, } from "./colors.js";
|
|
4
4
|
/**
|
|
5
5
|
* post/link/mini_app targets must be HTTPS in production; allow HTTP only for
|
|
@@ -29,25 +29,8 @@ const themeSchema = z
|
|
|
29
29
|
accent: themeAccentSchema.default(DEFAULT_THEME_ACCENT),
|
|
30
30
|
})
|
|
31
31
|
.strict();
|
|
32
|
-
const
|
|
33
|
-
message: "URL must use HTTPS",
|
|
34
|
-
});
|
|
35
|
-
function hasAllowedMediaExtension(urlString, allowedExtensions) {
|
|
36
|
-
try {
|
|
37
|
-
const url = new URL(urlString);
|
|
38
|
-
if (url.protocol !== "https:")
|
|
39
|
-
return false;
|
|
40
|
-
const lowerPathname = url.pathname.toLowerCase();
|
|
41
|
-
return allowedExtensions.some((extension) => lowerPathname.endsWith(`.${extension}`));
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
const imageUrlSchema = z
|
|
48
|
-
.string()
|
|
49
|
-
.refine((s) => hasAllowedMediaExtension(s, ["jpg", "png", "gif", "webp"]), {
|
|
50
|
-
message: "image URL must use HTTPS and end with a supported extension (.jpg, .png, .gif, .webp)",
|
|
32
|
+
const imageUrlSchema = z.string().refine((s) => s.startsWith(HTTPS_PREFIX), {
|
|
33
|
+
message: "image URL must use HTTPS",
|
|
51
34
|
});
|
|
52
35
|
const textAlignSchema = z.enum(TEXT_ALIGN_VALUES);
|
|
53
36
|
const textElementSchema = z
|
|
@@ -153,7 +136,7 @@ const gridElementSchema = z
|
|
|
153
136
|
rows: z.number().int().min(LIMITS.minGridRows).max(LIMITS.maxGridRows),
|
|
154
137
|
cells: z.array(gridCellSchema),
|
|
155
138
|
cellSize: z.enum(GRID_CELL_SIZE_VALUES).optional(),
|
|
156
|
-
gap: z.enum(GRID_GAP_VALUES).
|
|
139
|
+
gap: z.enum(GRID_GAP_VALUES).default(DEFAULT_GRID_GAP),
|
|
157
140
|
interactive: z.boolean().optional(),
|
|
158
141
|
})
|
|
159
142
|
.superRefine((val, ctx) => {
|
|
@@ -301,24 +284,134 @@ const barChartElementSchema = z
|
|
|
301
284
|
});
|
|
302
285
|
const buttonActionSchema = z.enum(BUTTON_ACTION_VALUES);
|
|
303
286
|
const buttonStyleSchema = z.enum(BUTTON_STYLE_VALUES);
|
|
287
|
+
/* ------------------------------------------------------------------ */
|
|
288
|
+
/* Client action schemas */
|
|
289
|
+
/* ------------------------------------------------------------------ */
|
|
290
|
+
const viewCastClientActionSchema = z
|
|
291
|
+
.object({
|
|
292
|
+
type: z.literal(CLIENT_ACTION.view_cast),
|
|
293
|
+
hash: z.string().min(1),
|
|
294
|
+
})
|
|
295
|
+
.strict();
|
|
296
|
+
const viewProfileClientActionSchema = z
|
|
297
|
+
.object({
|
|
298
|
+
type: z.literal(CLIENT_ACTION.view_profile),
|
|
299
|
+
fid: z.number().int().nonnegative(),
|
|
300
|
+
})
|
|
301
|
+
.strict();
|
|
302
|
+
const composeCastClientActionSchema = z
|
|
303
|
+
.object({
|
|
304
|
+
type: z.literal(CLIENT_ACTION.compose_cast),
|
|
305
|
+
text: z.string().optional(),
|
|
306
|
+
embeds: z
|
|
307
|
+
.array(z.string())
|
|
308
|
+
.max(2, { message: "compose_cast embeds: max 2 URLs" })
|
|
309
|
+
.optional(),
|
|
310
|
+
parent: z
|
|
311
|
+
.object({
|
|
312
|
+
type: z.literal("cast"),
|
|
313
|
+
hash: z.string().min(1),
|
|
314
|
+
})
|
|
315
|
+
.strict()
|
|
316
|
+
.optional(),
|
|
317
|
+
channelKey: z.string().optional(),
|
|
318
|
+
})
|
|
319
|
+
.strict();
|
|
320
|
+
const viewTokenClientActionSchema = z
|
|
321
|
+
.object({
|
|
322
|
+
type: z.literal(CLIENT_ACTION.view_token),
|
|
323
|
+
/** CAIP-19 asset ID (e.g. "eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913") */
|
|
324
|
+
token: z.string().min(1),
|
|
325
|
+
})
|
|
326
|
+
.strict();
|
|
327
|
+
const sendTokenClientActionSchema = z
|
|
328
|
+
.object({
|
|
329
|
+
type: z.literal(CLIENT_ACTION.send_token),
|
|
330
|
+
/** CAIP-19 asset ID */
|
|
331
|
+
token: z.string().optional(),
|
|
332
|
+
/** Amount in raw token units (e.g. "1000000" for 1 USDC) */
|
|
333
|
+
amount: z.string().optional(),
|
|
334
|
+
recipientFid: z.number().int().nonnegative().optional(),
|
|
335
|
+
recipientAddress: z.string().optional(),
|
|
336
|
+
})
|
|
337
|
+
.strict();
|
|
338
|
+
const swapTokenClientActionSchema = z
|
|
339
|
+
.object({
|
|
340
|
+
type: z.literal(CLIENT_ACTION.swap_token),
|
|
341
|
+
/** CAIP-19 asset ID to sell */
|
|
342
|
+
sellToken: z.string().optional(),
|
|
343
|
+
/** CAIP-19 asset ID to buy */
|
|
344
|
+
buyToken: z.string().optional(),
|
|
345
|
+
/** Amount in raw token units */
|
|
346
|
+
sellAmount: z.string().optional(),
|
|
347
|
+
})
|
|
348
|
+
.strict();
|
|
349
|
+
export const clientActionSchema = z.discriminatedUnion("type", [
|
|
350
|
+
viewCastClientActionSchema,
|
|
351
|
+
viewProfileClientActionSchema,
|
|
352
|
+
composeCastClientActionSchema,
|
|
353
|
+
viewTokenClientActionSchema,
|
|
354
|
+
sendTokenClientActionSchema,
|
|
355
|
+
swapTokenClientActionSchema,
|
|
356
|
+
]);
|
|
357
|
+
/* ------------------------------------------------------------------ */
|
|
358
|
+
/* Button schema */
|
|
359
|
+
/* ------------------------------------------------------------------ */
|
|
304
360
|
const buttonSchema = z
|
|
305
361
|
.object({
|
|
306
362
|
label: z.string().min(1).max(LIMITS.maxButtonLabelChars),
|
|
307
363
|
action: buttonActionSchema,
|
|
308
|
-
/** URL
|
|
309
|
-
target: z.string().min(1),
|
|
364
|
+
/** URL target for post/link/mini_app buttons */
|
|
365
|
+
target: z.string().min(1).optional(),
|
|
366
|
+
/** Structured client action for client buttons */
|
|
367
|
+
client_action: clientActionSchema.optional(),
|
|
310
368
|
style: buttonStyleSchema.optional(),
|
|
311
369
|
})
|
|
312
370
|
.superRefine((val, ctx) => {
|
|
313
|
-
if (
|
|
314
|
-
|
|
315
|
-
val.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
371
|
+
if (val.action === BUTTON_ACTION.client) {
|
|
372
|
+
// client buttons require client_action, must not have target
|
|
373
|
+
if (val.client_action === undefined) {
|
|
374
|
+
ctx.addIssue({
|
|
375
|
+
code: "custom",
|
|
376
|
+
message: `button with action "client" must include a "client_action" object`,
|
|
377
|
+
path: ["client_action"],
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
if (val.target !== undefined) {
|
|
381
|
+
ctx.addIssue({
|
|
382
|
+
code: "custom",
|
|
383
|
+
message: `button with action "client" must not include "target"`,
|
|
384
|
+
path: ["target"],
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
// post/link/mini_app buttons require target, must not have client_action
|
|
390
|
+
if (val.target === undefined) {
|
|
391
|
+
ctx.addIssue({
|
|
392
|
+
code: "custom",
|
|
393
|
+
message: `button with action "${val.action}" must include a "target" URL`,
|
|
394
|
+
path: ["target"],
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
if (val.client_action !== undefined) {
|
|
398
|
+
ctx.addIssue({
|
|
399
|
+
code: "custom",
|
|
400
|
+
message: `button with action "${val.action}" must not include "client_action"`,
|
|
401
|
+
path: ["client_action"],
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
if (val.target &&
|
|
405
|
+
(val.action === BUTTON_ACTION.post ||
|
|
406
|
+
val.action === BUTTON_ACTION.link ||
|
|
407
|
+
val.action === BUTTON_ACTION.mini_app) &&
|
|
408
|
+
!isSecureOrLoopbackHttpButtonTarget(val.target)) {
|
|
409
|
+
ctx.addIssue({
|
|
410
|
+
code: "custom",
|
|
411
|
+
message: `button target must use HTTPS (or http:// on localhost / 127.0.0.1 for development) for action "${val.action}" (received: ${val.target})`,
|
|
412
|
+
path: ["target"],
|
|
413
|
+
});
|
|
414
|
+
}
|
|
322
415
|
}
|
|
323
416
|
});
|
|
324
417
|
/** Child elements allowed inside `group` (no media, no nested group) */
|
|
@@ -21,6 +21,12 @@ export type ParseRequestOptions = {
|
|
|
21
21
|
* When true, skip {@link verifyJFSRequestBody} (signature checks).
|
|
22
22
|
*/
|
|
23
23
|
skipJFSVerification?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Maximum allowed absolute difference between the request timestamp and the
|
|
26
|
+
* server clock, in seconds. Requests outside this window are rejected as
|
|
27
|
+
* potential replays. Defaults to 300 (5 minutes) when not provided.
|
|
28
|
+
*/
|
|
29
|
+
maxSkewSeconds?: number;
|
|
24
30
|
};
|
|
25
31
|
export type ParseRequestResult = {
|
|
26
32
|
success: true;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ACTION_TYPE_GET, ACTION_TYPE_POST, payloadSchema, } from "../schemas.js";
|
|
2
2
|
import { decodePayload, verifyJFSRequestBody } from "./verify.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
/** Default replay window per SPEC.md § Replay Protection (5 minutes). */
|
|
5
4
|
const DEFAULT_SNAP_POST_MAX_SKEW_SECONDS = 300;
|
|
6
5
|
const requestBodySchema = z.object({
|
|
7
6
|
header: z.string(),
|
|
@@ -29,7 +28,7 @@ export async function parseRequest(request, options = {}) {
|
|
|
29
28
|
action: { type: ACTION_TYPE_GET },
|
|
30
29
|
};
|
|
31
30
|
}
|
|
32
|
-
const maxSkew = DEFAULT_SNAP_POST_MAX_SKEW_SECONDS;
|
|
31
|
+
const maxSkew = options.maxSkewSeconds ?? DEFAULT_SNAP_POST_MAX_SKEW_SECONDS;
|
|
33
32
|
const nowSec = Math.floor(Date.now() / 1000);
|
|
34
33
|
const text = await request.text();
|
|
35
34
|
let jsonBody;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare const buttonGroupProps: z.ZodObject<{
|
|
2
|
+
export declare const buttonGroupProps: z.ZodPipe<z.ZodObject<{
|
|
3
3
|
name: z.ZodString;
|
|
4
4
|
options: z.ZodArray<z.ZodString>;
|
|
5
5
|
style: z.ZodOptional<z.ZodEnum<{
|
|
@@ -7,5 +7,13 @@ export declare const buttonGroupProps: z.ZodObject<{
|
|
|
7
7
|
stack: "stack";
|
|
8
8
|
grid: "grid";
|
|
9
9
|
}>>;
|
|
10
|
-
}, z.core.$strip
|
|
10
|
+
}, z.core.$strip>, z.ZodTransform<{
|
|
11
|
+
style: "row" | "stack" | "grid";
|
|
12
|
+
name: string;
|
|
13
|
+
options: string[];
|
|
14
|
+
}, {
|
|
15
|
+
name: string;
|
|
16
|
+
options: string[];
|
|
17
|
+
style?: "row" | "stack" | "grid" | undefined;
|
|
18
|
+
}>>;
|
|
11
19
|
export type ButtonGroupProps = z.infer<typeof buttonGroupProps>;
|
package/dist/ui/button-group.js
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { BUTTON_GROUP_STYLE_VALUES, LIMITS } from "../constants.js";
|
|
3
|
-
export const buttonGroupProps = z
|
|
2
|
+
import { BUTTON_GROUP_STYLE, BUTTON_GROUP_STYLE_VALUES, LIMITS, } from "../constants.js";
|
|
3
|
+
export const buttonGroupProps = z
|
|
4
|
+
.object({
|
|
4
5
|
name: z.string().min(1),
|
|
5
6
|
options: z
|
|
6
|
-
.array(z.string())
|
|
7
|
+
.array(z.string().max(LIMITS.maxButtonGroupOptionChars))
|
|
7
8
|
.min(LIMITS.minButtonGroupOptions)
|
|
8
9
|
.max(LIMITS.maxButtonGroupOptions),
|
|
9
10
|
style: z.enum(BUTTON_GROUP_STYLE_VALUES).optional(),
|
|
10
|
-
})
|
|
11
|
+
})
|
|
12
|
+
.transform((val) => ({
|
|
13
|
+
...val,
|
|
14
|
+
style: val.style ??
|
|
15
|
+
(val.options.length <= 3
|
|
16
|
+
? BUTTON_GROUP_STYLE.row
|
|
17
|
+
: BUTTON_GROUP_STYLE.stack),
|
|
18
|
+
}));
|
package/dist/ui/button.d.ts
CHANGED
|
@@ -5,9 +5,10 @@ export declare const actionButtonProps: z.ZodObject<{
|
|
|
5
5
|
post: "post";
|
|
6
6
|
link: "link";
|
|
7
7
|
mini_app: "mini_app";
|
|
8
|
-
|
|
8
|
+
client: "client";
|
|
9
9
|
}>;
|
|
10
|
-
target: z.ZodString
|
|
10
|
+
target: z.ZodOptional<z.ZodString>;
|
|
11
|
+
client_action: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
11
12
|
style: z.ZodOptional<z.ZodEnum<{
|
|
12
13
|
primary: "primary";
|
|
13
14
|
secondary: "secondary";
|
|
@@ -21,9 +22,10 @@ export declare const buttonProps: z.ZodObject<{
|
|
|
21
22
|
post: "post";
|
|
22
23
|
link: "link";
|
|
23
24
|
mini_app: "mini_app";
|
|
24
|
-
|
|
25
|
+
client: "client";
|
|
25
26
|
}>;
|
|
26
|
-
target: z.ZodString
|
|
27
|
+
target: z.ZodOptional<z.ZodString>;
|
|
28
|
+
client_action: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
27
29
|
style: z.ZodOptional<z.ZodEnum<{
|
|
28
30
|
primary: "primary";
|
|
29
31
|
secondary: "secondary";
|
package/dist/ui/button.js
CHANGED
|
@@ -3,7 +3,8 @@ import { BUTTON_ACTION_VALUES, BUTTON_STYLE_VALUES } from "../constants.js";
|
|
|
3
3
|
export const actionButtonProps = z.object({
|
|
4
4
|
label: z.string(),
|
|
5
5
|
action: z.enum(BUTTON_ACTION_VALUES),
|
|
6
|
-
target: z.string(),
|
|
6
|
+
target: z.string().optional(),
|
|
7
|
+
client_action: z.record(z.string(), z.unknown()).optional(),
|
|
7
8
|
style: z.enum(BUTTON_STYLE_VALUES).optional(),
|
|
8
9
|
});
|
|
9
10
|
/** Same schema as `actionButtonProps` (legacy export name). */
|
package/dist/ui/catalog.d.ts
CHANGED
|
@@ -69,7 +69,7 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
69
69
|
};
|
|
70
70
|
Spacer: {
|
|
71
71
|
props: z.ZodObject<{
|
|
72
|
-
size: z.
|
|
72
|
+
size: z.ZodDefault<z.ZodEnum<{
|
|
73
73
|
small: "small";
|
|
74
74
|
medium: "medium";
|
|
75
75
|
large: "large";
|
|
@@ -155,7 +155,7 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
155
155
|
description: string;
|
|
156
156
|
};
|
|
157
157
|
ButtonGroup: {
|
|
158
|
-
props: z.ZodObject<{
|
|
158
|
+
props: z.ZodPipe<z.ZodObject<{
|
|
159
159
|
name: z.ZodString;
|
|
160
160
|
options: z.ZodArray<z.ZodString>;
|
|
161
161
|
style: z.ZodOptional<z.ZodEnum<{
|
|
@@ -163,14 +163,22 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
163
163
|
stack: "stack";
|
|
164
164
|
grid: "grid";
|
|
165
165
|
}>>;
|
|
166
|
-
}, z.core.$strip
|
|
166
|
+
}, z.core.$strip>, z.ZodTransform<{
|
|
167
|
+
style: "row" | "stack" | "grid";
|
|
168
|
+
name: string;
|
|
169
|
+
options: string[];
|
|
170
|
+
}, {
|
|
171
|
+
name: string;
|
|
172
|
+
options: string[];
|
|
173
|
+
style?: "row" | "stack" | "grid" | undefined;
|
|
174
|
+
}>>;
|
|
167
175
|
description: string;
|
|
168
176
|
};
|
|
169
177
|
Toggle: {
|
|
170
178
|
props: z.ZodObject<{
|
|
171
179
|
name: z.ZodString;
|
|
172
180
|
label: z.ZodString;
|
|
173
|
-
value: z.
|
|
181
|
+
value: z.ZodDefault<z.ZodBoolean>;
|
|
174
182
|
}, z.core.$strip>;
|
|
175
183
|
description: string;
|
|
176
184
|
};
|
|
@@ -209,7 +217,6 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
209
217
|
props: z.ZodObject<{
|
|
210
218
|
layout: z.ZodEnum<{
|
|
211
219
|
row: "row";
|
|
212
|
-
grid: "grid";
|
|
213
220
|
}>;
|
|
214
221
|
}, z.core.$strip>;
|
|
215
222
|
description: string;
|
|
@@ -225,9 +232,10 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
225
232
|
post: "post";
|
|
226
233
|
link: "link";
|
|
227
234
|
mini_app: "mini_app";
|
|
228
|
-
|
|
235
|
+
client: "client";
|
|
229
236
|
}>;
|
|
230
|
-
target: z.ZodString
|
|
237
|
+
target: z.ZodOptional<z.ZodString>;
|
|
238
|
+
client_action: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
231
239
|
style: z.ZodOptional<z.ZodEnum<{
|
|
232
240
|
primary: "primary";
|
|
233
241
|
secondary: "secondary";
|
|
@@ -261,10 +269,10 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
261
269
|
target: z.ZodString;
|
|
262
270
|
}, z.core.$strip>;
|
|
263
271
|
};
|
|
264
|
-
|
|
272
|
+
snap_client: {
|
|
265
273
|
description: string;
|
|
266
274
|
params: z.ZodObject<{
|
|
267
|
-
|
|
275
|
+
client_action: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
268
276
|
}, z.core.$strip>;
|
|
269
277
|
};
|
|
270
278
|
};
|
package/dist/ui/catalog.js
CHANGED
|
@@ -26,6 +26,9 @@ const snapPostParams = z.object({
|
|
|
26
26
|
const snapTargetParams = z.object({
|
|
27
27
|
target: z.string(),
|
|
28
28
|
});
|
|
29
|
+
const snapClientParams = z.object({
|
|
30
|
+
client_action: z.record(z.string(), z.unknown()),
|
|
31
|
+
});
|
|
29
32
|
/**
|
|
30
33
|
* Basic catalog: one json-render component per snap element type, plus ActionButton for snap buttons.
|
|
31
34
|
* Does not validate cross-field rules (media count, height budget); snap JSON still goes through `@farcaster/snap` validation.
|
|
@@ -90,7 +93,7 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
90
93
|
},
|
|
91
94
|
ActionButton: {
|
|
92
95
|
props: actionButtonProps,
|
|
93
|
-
description: "Snap action button: post (next page), link (browser), mini_app,
|
|
96
|
+
description: "Snap action button: post (next page), link (browser), mini_app, client — target is HTTPS URL or client_action object.",
|
|
94
97
|
},
|
|
95
98
|
},
|
|
96
99
|
actions: {
|
|
@@ -106,9 +109,9 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
106
109
|
description: "Open `target` as an in-app Farcaster mini app.",
|
|
107
110
|
params: snapTargetParams,
|
|
108
111
|
},
|
|
109
|
-
|
|
110
|
-
description: "
|
|
111
|
-
params:
|
|
112
|
+
snap_client: {
|
|
113
|
+
description: "Trigger a Farcaster client action (view_cast, view_profile, compose_cast, …).",
|
|
114
|
+
params: snapClientParams,
|
|
112
115
|
},
|
|
113
116
|
},
|
|
114
117
|
});
|
package/dist/ui/group.d.ts
CHANGED
package/dist/ui/group.js
CHANGED
package/dist/ui/schema.js
CHANGED
|
@@ -26,6 +26,6 @@ export const snapJsonRenderSchema = defineSchema((s) => ({
|
|
|
26
26
|
defaultRules: [
|
|
27
27
|
"You are generating auxiliary UI for a Farcaster Snap. Prefer components matching snap element types (Text, Image, ButtonGroup, …).",
|
|
28
28
|
"Snap pages use a Stack root with at most 5 body children and 1 media element (Image or Grid); keep generated trees small.",
|
|
29
|
-
"Bottom-of-card snap buttons are ActionButton components; use actions post / link / mini_app /
|
|
29
|
+
"Bottom-of-card snap buttons are ActionButton components; use actions post / link / mini_app / client.",
|
|
30
30
|
],
|
|
31
31
|
});
|
package/dist/ui/spacer.d.ts
CHANGED
package/dist/ui/spacer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { SPACER_SIZE_VALUES } from "../constants.js";
|
|
2
|
+
import { SPACER_SIZE, SPACER_SIZE_VALUES } from "../constants.js";
|
|
3
3
|
export const spacerProps = z.object({
|
|
4
|
-
size: z.enum(SPACER_SIZE_VALUES).
|
|
4
|
+
size: z.enum(SPACER_SIZE_VALUES).default(SPACER_SIZE.medium),
|
|
5
5
|
});
|
package/dist/ui/toggle.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ import { z } from "zod";
|
|
|
2
2
|
export declare const toggleProps: z.ZodObject<{
|
|
3
3
|
name: z.ZodString;
|
|
4
4
|
label: z.ZodString;
|
|
5
|
-
value: z.
|
|
5
|
+
value: z.ZodDefault<z.ZodBoolean>;
|
|
6
6
|
}, z.core.$strip>;
|
|
7
7
|
export type ToggleProps = z.infer<typeof toggleProps>;
|
package/dist/ui/toggle.js
CHANGED
package/package.json
CHANGED
package/src/constants.ts
CHANGED
|
@@ -68,6 +68,8 @@ export const DEFAULT_LIST_STYLE = "ordered" as const;
|
|
|
68
68
|
|
|
69
69
|
export const GRID_CELL_SIZE_VALUES = ["auto", "square"] as const;
|
|
70
70
|
export const GRID_GAP_VALUES = ["none", "small", "medium"] as const;
|
|
71
|
+
export const DEFAULT_GRID_GAP =
|
|
72
|
+
"small" as const satisfies (typeof GRID_GAP_VALUES)[number];
|
|
71
73
|
|
|
72
74
|
export const BUTTON_GROUP_STYLE = {
|
|
73
75
|
row: "row",
|
|
@@ -85,14 +87,32 @@ export const BUTTON_ACTION = {
|
|
|
85
87
|
post: "post",
|
|
86
88
|
link: "link",
|
|
87
89
|
mini_app: "mini_app",
|
|
88
|
-
|
|
90
|
+
client: "client",
|
|
89
91
|
} as const;
|
|
90
92
|
|
|
91
93
|
export const BUTTON_ACTION_VALUES = [
|
|
92
94
|
BUTTON_ACTION.post,
|
|
93
95
|
BUTTON_ACTION.link,
|
|
94
96
|
BUTTON_ACTION.mini_app,
|
|
95
|
-
BUTTON_ACTION.
|
|
97
|
+
BUTTON_ACTION.client,
|
|
98
|
+
] as const;
|
|
99
|
+
|
|
100
|
+
export const CLIENT_ACTION = {
|
|
101
|
+
view_cast: "view_cast",
|
|
102
|
+
view_profile: "view_profile",
|
|
103
|
+
compose_cast: "compose_cast",
|
|
104
|
+
view_token: "view_token",
|
|
105
|
+
send_token: "send_token",
|
|
106
|
+
swap_token: "swap_token",
|
|
107
|
+
} as const;
|
|
108
|
+
|
|
109
|
+
export const CLIENT_ACTION_VALUES = [
|
|
110
|
+
CLIENT_ACTION.view_cast,
|
|
111
|
+
CLIENT_ACTION.view_profile,
|
|
112
|
+
CLIENT_ACTION.compose_cast,
|
|
113
|
+
CLIENT_ACTION.view_token,
|
|
114
|
+
CLIENT_ACTION.send_token,
|
|
115
|
+
CLIENT_ACTION.swap_token,
|
|
96
116
|
] as const;
|
|
97
117
|
|
|
98
118
|
export const BUTTON_STYLE = {
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,8 @@ export {
|
|
|
5
5
|
MEDIA_TYPE,
|
|
6
6
|
DEFAULT_LIST_STYLE,
|
|
7
7
|
DEFAULT_SLIDER_STEP,
|
|
8
|
+
CLIENT_ACTION,
|
|
9
|
+
CLIENT_ACTION_VALUES,
|
|
8
10
|
} from "./constants";
|
|
9
11
|
export {
|
|
10
12
|
DEFAULT_THEME_ACCENT,
|
|
@@ -21,11 +23,13 @@ export {
|
|
|
21
23
|
snapResponseSchema,
|
|
22
24
|
firstPageResponseSchema,
|
|
23
25
|
payloadSchema,
|
|
26
|
+
clientActionSchema,
|
|
24
27
|
createDefaultDataStore,
|
|
25
28
|
type Button,
|
|
26
29
|
type Element,
|
|
27
30
|
type Elements,
|
|
28
31
|
type GroupChildElement,
|
|
32
|
+
type ClientAction,
|
|
29
33
|
type SnapAction,
|
|
30
34
|
type SnapPageElementInput,
|
|
31
35
|
type SnapContext,
|
package/src/schemas.ts
CHANGED
|
@@ -2,11 +2,13 @@ import { z } from "zod";
|
|
|
2
2
|
import {
|
|
3
3
|
BUTTON_ACTION,
|
|
4
4
|
BUTTON_ACTION_VALUES,
|
|
5
|
+
CLIENT_ACTION,
|
|
5
6
|
BUTTON_GROUP_STYLE,
|
|
6
7
|
BUTTON_GROUP_STYLE_VALUES,
|
|
7
8
|
BUTTON_LAYOUT_VALUES,
|
|
8
9
|
BUTTON_STYLE_VALUES,
|
|
9
10
|
DEFAULT_BUTTON_LAYOUT,
|
|
11
|
+
DEFAULT_GRID_GAP,
|
|
10
12
|
DEFAULT_LIST_STYLE,
|
|
11
13
|
DEFAULT_SLIDER_STEP,
|
|
12
14
|
EFFECT_VALUES,
|
|
@@ -68,33 +70,10 @@ const themeSchema = z
|
|
|
68
70
|
})
|
|
69
71
|
.strict();
|
|
70
72
|
|
|
71
|
-
const
|
|
72
|
-
message: "URL must use HTTPS",
|
|
73
|
+
const imageUrlSchema = z.string().refine((s) => s.startsWith(HTTPS_PREFIX), {
|
|
74
|
+
message: "image URL must use HTTPS",
|
|
73
75
|
});
|
|
74
76
|
|
|
75
|
-
function hasAllowedMediaExtension(
|
|
76
|
-
urlString: string,
|
|
77
|
-
allowedExtensions: string[],
|
|
78
|
-
): boolean {
|
|
79
|
-
try {
|
|
80
|
-
const url = new URL(urlString);
|
|
81
|
-
if (url.protocol !== "https:") return false;
|
|
82
|
-
const lowerPathname = url.pathname.toLowerCase();
|
|
83
|
-
return allowedExtensions.some((extension) =>
|
|
84
|
-
lowerPathname.endsWith(`.${extension}`),
|
|
85
|
-
);
|
|
86
|
-
} catch {
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const imageUrlSchema = z
|
|
92
|
-
.string()
|
|
93
|
-
.refine((s) => hasAllowedMediaExtension(s, ["jpg", "png", "gif", "webp"]), {
|
|
94
|
-
message:
|
|
95
|
-
"image URL must use HTTPS and end with a supported extension (.jpg, .png, .gif, .webp)",
|
|
96
|
-
});
|
|
97
|
-
|
|
98
77
|
const textAlignSchema = z.enum(TEXT_ALIGN_VALUES);
|
|
99
78
|
|
|
100
79
|
const textElementSchema = z
|
|
@@ -208,7 +187,7 @@ const gridElementSchema = z
|
|
|
208
187
|
rows: z.number().int().min(LIMITS.minGridRows).max(LIMITS.maxGridRows),
|
|
209
188
|
cells: z.array(gridCellSchema),
|
|
210
189
|
cellSize: z.enum(GRID_CELL_SIZE_VALUES).optional(),
|
|
211
|
-
gap: z.enum(GRID_GAP_VALUES).
|
|
190
|
+
gap: z.enum(GRID_GAP_VALUES).default(DEFAULT_GRID_GAP),
|
|
212
191
|
interactive: z.boolean().optional(),
|
|
213
192
|
})
|
|
214
193
|
.superRefine((val, ctx) => {
|
|
@@ -370,26 +349,146 @@ const buttonActionSchema = z.enum(BUTTON_ACTION_VALUES);
|
|
|
370
349
|
|
|
371
350
|
const buttonStyleSchema = z.enum(BUTTON_STYLE_VALUES);
|
|
372
351
|
|
|
352
|
+
/* ------------------------------------------------------------------ */
|
|
353
|
+
/* Client action schemas */
|
|
354
|
+
/* ------------------------------------------------------------------ */
|
|
355
|
+
|
|
356
|
+
const viewCastClientActionSchema = z
|
|
357
|
+
.object({
|
|
358
|
+
type: z.literal(CLIENT_ACTION.view_cast),
|
|
359
|
+
hash: z.string().min(1),
|
|
360
|
+
})
|
|
361
|
+
.strict();
|
|
362
|
+
|
|
363
|
+
const viewProfileClientActionSchema = z
|
|
364
|
+
.object({
|
|
365
|
+
type: z.literal(CLIENT_ACTION.view_profile),
|
|
366
|
+
fid: z.number().int().nonnegative(),
|
|
367
|
+
})
|
|
368
|
+
.strict();
|
|
369
|
+
|
|
370
|
+
const composeCastClientActionSchema = z
|
|
371
|
+
.object({
|
|
372
|
+
type: z.literal(CLIENT_ACTION.compose_cast),
|
|
373
|
+
text: z.string().optional(),
|
|
374
|
+
embeds: z
|
|
375
|
+
.array(z.string())
|
|
376
|
+
.max(2, { message: "compose_cast embeds: max 2 URLs" })
|
|
377
|
+
.optional(),
|
|
378
|
+
parent: z
|
|
379
|
+
.object({
|
|
380
|
+
type: z.literal("cast"),
|
|
381
|
+
hash: z.string().min(1),
|
|
382
|
+
})
|
|
383
|
+
.strict()
|
|
384
|
+
.optional(),
|
|
385
|
+
channelKey: z.string().optional(),
|
|
386
|
+
})
|
|
387
|
+
.strict();
|
|
388
|
+
|
|
389
|
+
const viewTokenClientActionSchema = z
|
|
390
|
+
.object({
|
|
391
|
+
type: z.literal(CLIENT_ACTION.view_token),
|
|
392
|
+
/** CAIP-19 asset ID (e.g. "eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913") */
|
|
393
|
+
token: z.string().min(1),
|
|
394
|
+
})
|
|
395
|
+
.strict();
|
|
396
|
+
|
|
397
|
+
const sendTokenClientActionSchema = z
|
|
398
|
+
.object({
|
|
399
|
+
type: z.literal(CLIENT_ACTION.send_token),
|
|
400
|
+
/** CAIP-19 asset ID */
|
|
401
|
+
token: z.string().optional(),
|
|
402
|
+
/** Amount in raw token units (e.g. "1000000" for 1 USDC) */
|
|
403
|
+
amount: z.string().optional(),
|
|
404
|
+
recipientFid: z.number().int().nonnegative().optional(),
|
|
405
|
+
recipientAddress: z.string().optional(),
|
|
406
|
+
})
|
|
407
|
+
.strict();
|
|
408
|
+
|
|
409
|
+
const swapTokenClientActionSchema = z
|
|
410
|
+
.object({
|
|
411
|
+
type: z.literal(CLIENT_ACTION.swap_token),
|
|
412
|
+
/** CAIP-19 asset ID to sell */
|
|
413
|
+
sellToken: z.string().optional(),
|
|
414
|
+
/** CAIP-19 asset ID to buy */
|
|
415
|
+
buyToken: z.string().optional(),
|
|
416
|
+
/** Amount in raw token units */
|
|
417
|
+
sellAmount: z.string().optional(),
|
|
418
|
+
})
|
|
419
|
+
.strict();
|
|
420
|
+
|
|
421
|
+
export const clientActionSchema = z.discriminatedUnion("type", [
|
|
422
|
+
viewCastClientActionSchema,
|
|
423
|
+
viewProfileClientActionSchema,
|
|
424
|
+
composeCastClientActionSchema,
|
|
425
|
+
viewTokenClientActionSchema,
|
|
426
|
+
sendTokenClientActionSchema,
|
|
427
|
+
swapTokenClientActionSchema,
|
|
428
|
+
]);
|
|
429
|
+
|
|
430
|
+
export type ClientAction = z.infer<typeof clientActionSchema>;
|
|
431
|
+
|
|
432
|
+
/* ------------------------------------------------------------------ */
|
|
433
|
+
/* Button schema */
|
|
434
|
+
/* ------------------------------------------------------------------ */
|
|
435
|
+
|
|
373
436
|
const buttonSchema = z
|
|
374
437
|
.object({
|
|
375
438
|
label: z.string().min(1).max(LIMITS.maxButtonLabelChars),
|
|
376
439
|
action: buttonActionSchema,
|
|
377
|
-
/** URL
|
|
378
|
-
target: z.string().min(1),
|
|
440
|
+
/** URL target for post/link/mini_app buttons */
|
|
441
|
+
target: z.string().min(1).optional(),
|
|
442
|
+
/** Structured client action for client buttons */
|
|
443
|
+
client_action: clientActionSchema.optional(),
|
|
379
444
|
style: buttonStyleSchema.optional(),
|
|
380
445
|
})
|
|
381
446
|
.superRefine((val, ctx) => {
|
|
382
|
-
if (
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
447
|
+
if (val.action === BUTTON_ACTION.client) {
|
|
448
|
+
// client buttons require client_action, must not have target
|
|
449
|
+
if (val.client_action === undefined) {
|
|
450
|
+
ctx.addIssue({
|
|
451
|
+
code: "custom",
|
|
452
|
+
message: `button with action "client" must include a "client_action" object`,
|
|
453
|
+
path: ["client_action"],
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
if (val.target !== undefined) {
|
|
457
|
+
ctx.addIssue({
|
|
458
|
+
code: "custom",
|
|
459
|
+
message: `button with action "client" must not include "target"`,
|
|
460
|
+
path: ["target"],
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
} else {
|
|
464
|
+
// post/link/mini_app buttons require target, must not have client_action
|
|
465
|
+
if (val.target === undefined) {
|
|
466
|
+
ctx.addIssue({
|
|
467
|
+
code: "custom",
|
|
468
|
+
message: `button with action "${val.action}" must include a "target" URL`,
|
|
469
|
+
path: ["target"],
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
if (val.client_action !== undefined) {
|
|
473
|
+
ctx.addIssue({
|
|
474
|
+
code: "custom",
|
|
475
|
+
message: `button with action "${val.action}" must not include "client_action"`,
|
|
476
|
+
path: ["client_action"],
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
if (
|
|
480
|
+
val.target &&
|
|
481
|
+
(val.action === BUTTON_ACTION.post ||
|
|
482
|
+
val.action === BUTTON_ACTION.link ||
|
|
483
|
+
val.action === BUTTON_ACTION.mini_app) &&
|
|
484
|
+
!isSecureOrLoopbackHttpButtonTarget(val.target)
|
|
485
|
+
) {
|
|
486
|
+
ctx.addIssue({
|
|
487
|
+
code: "custom",
|
|
488
|
+
message: `button target must use HTTPS (or http:// on localhost / 127.0.0.1 for development) for action "${val.action}" (received: ${val.target})`,
|
|
489
|
+
path: ["target"],
|
|
490
|
+
});
|
|
491
|
+
}
|
|
393
492
|
}
|
|
394
493
|
});
|
|
395
494
|
|
|
@@ -608,7 +707,9 @@ export function createDefaultDataStore(): SnapDataStore {
|
|
|
608
707
|
set(_key: string, _value: DataStoreValue): Promise<never> {
|
|
609
708
|
return Promise.reject(err);
|
|
610
709
|
},
|
|
611
|
-
withLock<T>(
|
|
710
|
+
withLock<T>(
|
|
711
|
+
_fn: (store: SnapDataStoreOperations) => Promise<T>,
|
|
712
|
+
): Promise<never> {
|
|
612
713
|
return Promise.reject(err);
|
|
613
714
|
},
|
|
614
715
|
};
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
import { decodePayload, verifyJFSRequestBody } from "./verify";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
|
-
/** Default replay window per SPEC.md § Replay Protection (5 minutes). */
|
|
11
10
|
const DEFAULT_SNAP_POST_MAX_SKEW_SECONDS = 300 as const;
|
|
12
11
|
|
|
13
12
|
export type ParseRequestError =
|
|
@@ -37,6 +36,13 @@ export type ParseRequestOptions = {
|
|
|
37
36
|
* When true, skip {@link verifyJFSRequestBody} (signature checks).
|
|
38
37
|
*/
|
|
39
38
|
skipJFSVerification?: boolean;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Maximum allowed absolute difference between the request timestamp and the
|
|
42
|
+
* server clock, in seconds. Requests outside this window are rejected as
|
|
43
|
+
* potential replays. Defaults to 300 (5 minutes) when not provided.
|
|
44
|
+
*/
|
|
45
|
+
maxSkewSeconds?: number;
|
|
40
46
|
};
|
|
41
47
|
|
|
42
48
|
export type ParseRequestResult =
|
|
@@ -75,7 +81,7 @@ export async function parseRequest(
|
|
|
75
81
|
};
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
const maxSkew = DEFAULT_SNAP_POST_MAX_SKEW_SECONDS;
|
|
84
|
+
const maxSkew = options.maxSkewSeconds ?? DEFAULT_SNAP_POST_MAX_SKEW_SECONDS;
|
|
79
85
|
const nowSec = Math.floor(Date.now() / 1000);
|
|
80
86
|
|
|
81
87
|
const text = await request.text();
|
package/src/ui/button-group.ts
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
BUTTON_GROUP_STYLE,
|
|
4
|
+
BUTTON_GROUP_STYLE_VALUES,
|
|
5
|
+
LIMITS,
|
|
6
|
+
} from "../constants.js";
|
|
3
7
|
|
|
4
|
-
export const buttonGroupProps = z
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
export const buttonGroupProps = z
|
|
9
|
+
.object({
|
|
10
|
+
name: z.string().min(1),
|
|
11
|
+
options: z
|
|
12
|
+
.array(z.string().max(LIMITS.maxButtonGroupOptionChars))
|
|
13
|
+
.min(LIMITS.minButtonGroupOptions)
|
|
14
|
+
.max(LIMITS.maxButtonGroupOptions),
|
|
15
|
+
style: z.enum(BUTTON_GROUP_STYLE_VALUES).optional(),
|
|
16
|
+
})
|
|
17
|
+
.transform((val) => ({
|
|
18
|
+
...val,
|
|
19
|
+
style:
|
|
20
|
+
val.style ??
|
|
21
|
+
(val.options.length <= 3
|
|
22
|
+
? BUTTON_GROUP_STYLE.row
|
|
23
|
+
: BUTTON_GROUP_STYLE.stack),
|
|
24
|
+
}));
|
|
12
25
|
|
|
13
26
|
export type ButtonGroupProps = z.infer<typeof buttonGroupProps>;
|
package/src/ui/button.ts
CHANGED
|
@@ -4,7 +4,8 @@ import { BUTTON_ACTION_VALUES, BUTTON_STYLE_VALUES } from "../constants.js";
|
|
|
4
4
|
export const actionButtonProps = z.object({
|
|
5
5
|
label: z.string(),
|
|
6
6
|
action: z.enum(BUTTON_ACTION_VALUES),
|
|
7
|
-
target: z.string(),
|
|
7
|
+
target: z.string().optional(),
|
|
8
|
+
client_action: z.record(z.string(), z.unknown()).optional(),
|
|
8
9
|
style: z.enum(BUTTON_STYLE_VALUES).optional(),
|
|
9
10
|
});
|
|
10
11
|
|
package/src/ui/catalog.ts
CHANGED
|
@@ -29,6 +29,10 @@ const snapTargetParams = z.object({
|
|
|
29
29
|
target: z.string(),
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
const snapClientParams = z.object({
|
|
33
|
+
client_action: z.record(z.string(), z.unknown()),
|
|
34
|
+
});
|
|
35
|
+
|
|
32
36
|
/**
|
|
33
37
|
* Basic catalog: one json-render component per snap element type, plus ActionButton for snap buttons.
|
|
34
38
|
* Does not validate cross-field rules (media count, height budget); snap JSON still goes through `@farcaster/snap` validation.
|
|
@@ -102,7 +106,7 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
102
106
|
ActionButton: {
|
|
103
107
|
props: actionButtonProps,
|
|
104
108
|
description:
|
|
105
|
-
"Snap action button: post (next page), link (browser), mini_app,
|
|
109
|
+
"Snap action button: post (next page), link (browser), mini_app, client — target is HTTPS URL or client_action object.",
|
|
106
110
|
},
|
|
107
111
|
},
|
|
108
112
|
actions: {
|
|
@@ -119,10 +123,10 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
119
123
|
description: "Open `target` as an in-app Farcaster mini app.",
|
|
120
124
|
params: snapTargetParams,
|
|
121
125
|
},
|
|
122
|
-
|
|
126
|
+
snap_client: {
|
|
123
127
|
description:
|
|
124
|
-
"
|
|
125
|
-
params:
|
|
128
|
+
"Trigger a Farcaster client action (view_cast, view_profile, compose_cast, …).",
|
|
129
|
+
params: snapClientParams,
|
|
126
130
|
},
|
|
127
131
|
},
|
|
128
132
|
});
|
package/src/ui/group.ts
CHANGED
package/src/ui/schema.ts
CHANGED
|
@@ -31,7 +31,7 @@ export const snapJsonRenderSchema = defineSchema(
|
|
|
31
31
|
defaultRules: [
|
|
32
32
|
"You are generating auxiliary UI for a Farcaster Snap. Prefer components matching snap element types (Text, Image, ButtonGroup, …).",
|
|
33
33
|
"Snap pages use a Stack root with at most 5 body children and 1 media element (Image or Grid); keep generated trees small.",
|
|
34
|
-
"Bottom-of-card snap buttons are ActionButton components; use actions post / link / mini_app /
|
|
34
|
+
"Bottom-of-card snap buttons are ActionButton components; use actions post / link / mini_app / client.",
|
|
35
35
|
],
|
|
36
36
|
},
|
|
37
37
|
);
|
package/src/ui/spacer.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { SPACER_SIZE_VALUES } from "../constants.js";
|
|
2
|
+
import { SPACER_SIZE, SPACER_SIZE_VALUES } from "../constants.js";
|
|
3
3
|
|
|
4
4
|
export const spacerProps = z.object({
|
|
5
|
-
size: z.enum(SPACER_SIZE_VALUES).
|
|
5
|
+
size: z.enum(SPACER_SIZE_VALUES).default(SPACER_SIZE.medium),
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
export type SpacerProps = z.infer<typeof spacerProps>;
|
package/src/ui/toggle.ts
CHANGED