@farcaster/snap 1.3.2 → 1.4.0
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/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/schemas.d.ts +28 -11
- package/dist/schemas.js +16 -9
- package/dist/server/parseRequest.js +4 -7
- package/dist/ui/catalog.d.ts +1 -1
- package/dist/ui/catalog.js +1 -1
- package/dist/validator.d.ts +2 -2
- package/dist/validator.js +5 -5
- package/package.json +1 -1
- package/src/index.ts +5 -4
- package/src/schemas.ts +62 -43
- package/src/server/parseRequest.ts +9 -7
- package/src/ui/catalog.ts +1 -1
- package/src/validator.ts +7 -7
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { POST_GRID_TAP_KEY, PAGE_ROOT_TYPE, ELEMENT_TYPE, MEDIA_TYPE, DEFAULT_THEME_ACCENT, DEFAULT_LIST_STYLE, DEFAULT_SLIDER_STEP, PALETTE_COLOR, PALETTE_COLOR_VALUES, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, type PaletteColor, } from "./constants.js";
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
2
|
+
export { snapResponseSchema, firstPageResponseSchema, payloadSchema, type SnapAction, type SnapContext, type SnapResponse, type SnapHandlerResult, type SnapFunction, type SnapPayload, } from "./schemas.js";
|
|
3
|
+
export { validateSnapResponse, validateFirstPageResponse, type ValidationResult, } from "./validator.js";
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { POST_GRID_TAP_KEY, PAGE_ROOT_TYPE, ELEMENT_TYPE, MEDIA_TYPE, DEFAULT_THEME_ACCENT, DEFAULT_LIST_STYLE, DEFAULT_SLIDER_STEP, PALETTE_COLOR, PALETTE_COLOR_VALUES, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, } from "./constants.js";
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
2
|
+
export { snapResponseSchema, firstPageResponseSchema, payloadSchema, } from "./schemas.js";
|
|
3
|
+
export { validateSnapResponse, validateFirstPageResponse, } from "./validator.js";
|
package/dist/schemas.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ declare const buttonSchema: z.ZodObject<{
|
|
|
13
13
|
secondary: "secondary";
|
|
14
14
|
}>>;
|
|
15
15
|
}, z.core.$strip>;
|
|
16
|
+
export type Button = z.infer<typeof buttonSchema>;
|
|
16
17
|
/** Any single page element, including media and `group` */
|
|
17
18
|
declare const elementSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
18
19
|
type: z.ZodLiteral<"text">;
|
|
@@ -326,6 +327,7 @@ declare const elementSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
326
327
|
accent: "accent";
|
|
327
328
|
}>>;
|
|
328
329
|
}, z.core.$strip>], "type">;
|
|
330
|
+
export type Element = z.infer<typeof elementSchema>;
|
|
329
331
|
declare const elementsSchema: z.ZodObject<{
|
|
330
332
|
type: z.ZodLiteral<"stack">;
|
|
331
333
|
children: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -641,10 +643,8 @@ declare const elementsSchema: z.ZodObject<{
|
|
|
641
643
|
}>>;
|
|
642
644
|
}, z.core.$strip>], "type">>;
|
|
643
645
|
}, z.core.$strict>;
|
|
644
|
-
export type Button = z.infer<typeof buttonSchema>;
|
|
645
|
-
export type Element = z.infer<typeof elementSchema>;
|
|
646
646
|
export type Elements = z.infer<typeof elementsSchema>;
|
|
647
|
-
export declare const
|
|
647
|
+
export declare const snapResponseSchema: z.ZodObject<{
|
|
648
648
|
version: z.ZodLiteral<"1.0">;
|
|
649
649
|
page: z.ZodObject<{
|
|
650
650
|
theme: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
@@ -998,7 +998,9 @@ export declare const rootSchema: z.ZodObject<{
|
|
|
998
998
|
}, z.core.$strip>>>;
|
|
999
999
|
}, z.core.$strict>;
|
|
1000
1000
|
}, z.core.$strict>;
|
|
1001
|
-
export
|
|
1001
|
+
export type SnapResponse = z.infer<typeof snapResponseSchema>;
|
|
1002
|
+
export type SnapHandlerResult = z.input<typeof snapResponseSchema>;
|
|
1003
|
+
export declare const firstPageResponseSchema: z.ZodObject<{
|
|
1002
1004
|
version: z.ZodLiteral<"1.0">;
|
|
1003
1005
|
page: z.ZodObject<{
|
|
1004
1006
|
theme: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
@@ -1352,6 +1354,7 @@ export declare const firstPageRootSchema: z.ZodObject<{
|
|
|
1352
1354
|
}, z.core.$strip>>>;
|
|
1353
1355
|
}, z.core.$strict>;
|
|
1354
1356
|
}, z.core.$strict>;
|
|
1357
|
+
export type FirstPageResponse = z.infer<typeof firstPageResponseSchema>;
|
|
1355
1358
|
export declare const payloadSchema: z.ZodObject<{
|
|
1356
1359
|
fid: z.ZodNumber;
|
|
1357
1360
|
inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodObject<{
|
|
@@ -1361,26 +1364,40 @@ export declare const payloadSchema: z.ZodObject<{
|
|
|
1361
1364
|
button_index: z.ZodNumber;
|
|
1362
1365
|
timestamp: z.ZodNumber;
|
|
1363
1366
|
}, z.core.$strict>;
|
|
1364
|
-
export type SnapResponse = z.infer<typeof rootSchema>;
|
|
1365
|
-
export type SnapPage = SnapResponse["page"];
|
|
1366
1367
|
export type SnapPayload = z.infer<typeof payloadSchema>;
|
|
1368
|
+
export declare const ACTION_TYPE_GET: "get";
|
|
1369
|
+
export declare const ACTION_TYPE_POST: "post";
|
|
1370
|
+
declare const snapGetActionSchema: z.ZodObject<{
|
|
1371
|
+
type: z.ZodLiteral<"get">;
|
|
1372
|
+
}, z.core.$strip>;
|
|
1373
|
+
export type SnapGetAction = z.infer<typeof snapGetActionSchema>;
|
|
1367
1374
|
declare const snapPostActionSchema: z.ZodObject<{
|
|
1368
1375
|
fid: z.ZodNumber;
|
|
1369
1376
|
inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodObject<{
|
|
1370
1377
|
row: z.ZodNumber;
|
|
1371
1378
|
col: z.ZodNumber;
|
|
1372
1379
|
}, z.core.$strict>]>>>;
|
|
1380
|
+
button_index: z.ZodNumber;
|
|
1373
1381
|
timestamp: z.ZodNumber;
|
|
1374
1382
|
type: z.ZodLiteral<"post">;
|
|
1375
|
-
buttonIndex: z.ZodNumber;
|
|
1376
1383
|
}, z.core.$strict>;
|
|
1377
1384
|
export type SnapPostAction = z.infer<typeof snapPostActionSchema>;
|
|
1378
|
-
export
|
|
1379
|
-
type: "get"
|
|
1380
|
-
}
|
|
1385
|
+
export declare const snapActionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1386
|
+
type: z.ZodLiteral<"get">;
|
|
1387
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1388
|
+
fid: z.ZodNumber;
|
|
1389
|
+
inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodObject<{
|
|
1390
|
+
row: z.ZodNumber;
|
|
1391
|
+
col: z.ZodNumber;
|
|
1392
|
+
}, z.core.$strict>]>>>;
|
|
1393
|
+
button_index: z.ZodNumber;
|
|
1394
|
+
timestamp: z.ZodNumber;
|
|
1395
|
+
type: z.ZodLiteral<"post">;
|
|
1396
|
+
}, z.core.$strict>], "type">;
|
|
1397
|
+
export type SnapAction = z.infer<typeof snapActionSchema>;
|
|
1381
1398
|
export type SnapContext = {
|
|
1382
1399
|
action: SnapAction;
|
|
1383
1400
|
request: Request;
|
|
1384
1401
|
};
|
|
1385
|
-
export type SnapFunction = (ctx: SnapContext) => Promise<
|
|
1402
|
+
export type SnapFunction = (ctx: SnapContext) => Promise<SnapHandlerResult>;
|
|
1386
1403
|
export {};
|
package/dist/schemas.js
CHANGED
|
@@ -368,7 +368,7 @@ const elementsSchema = z
|
|
|
368
368
|
}),
|
|
369
369
|
})
|
|
370
370
|
.strict();
|
|
371
|
-
export const
|
|
371
|
+
export const snapResponseSchema = z
|
|
372
372
|
.object({
|
|
373
373
|
version: z.literal(SPEC_VERSION),
|
|
374
374
|
page: z
|
|
@@ -402,9 +402,9 @@ export const rootSchema = z
|
|
|
402
402
|
})
|
|
403
403
|
.strict();
|
|
404
404
|
// extra constraints for the first page to make it look nicer
|
|
405
|
-
export const
|
|
406
|
-
const
|
|
407
|
-
const hasTextTitleOrBody =
|
|
405
|
+
export const firstPageResponseSchema = snapResponseSchema.superRefine((response, ctx) => {
|
|
406
|
+
const elements = response.page.elements.children;
|
|
407
|
+
const hasTextTitleOrBody = elements.some((el) => el.type === ELEMENT_TYPE.text &&
|
|
408
408
|
(el.style === TEXT_STYLE.title || el.style === TEXT_STYLE.body));
|
|
409
409
|
if (!hasTextTitleOrBody) {
|
|
410
410
|
ctx.addIssue({
|
|
@@ -413,8 +413,8 @@ export const firstPageRootSchema = rootSchema.superRefine((root, ctx) => {
|
|
|
413
413
|
path: ["page", "elements", "children"],
|
|
414
414
|
});
|
|
415
415
|
}
|
|
416
|
-
const hasInteractive =
|
|
417
|
-
const hasMedia =
|
|
416
|
+
const hasInteractive = elements.some((el) => INTERACTIVE_ELEMENT_TYPES.includes(el.type));
|
|
417
|
+
const hasMedia = elements.some((el) => MEDIA_ELEMENT_TYPES.includes(el.type));
|
|
418
418
|
if (!hasInteractive && !hasMedia) {
|
|
419
419
|
ctx.addIssue({
|
|
420
420
|
code: "custom",
|
|
@@ -443,10 +443,17 @@ export const payloadSchema = z
|
|
|
443
443
|
timestamp: z.number().int(),
|
|
444
444
|
})
|
|
445
445
|
.strict();
|
|
446
|
+
export const ACTION_TYPE_GET = "get";
|
|
447
|
+
export const ACTION_TYPE_POST = "post";
|
|
448
|
+
const snapGetActionSchema = z.object({
|
|
449
|
+
type: z.literal(ACTION_TYPE_GET),
|
|
450
|
+
});
|
|
446
451
|
const snapPostActionSchema = payloadSchema
|
|
447
|
-
.omit({ button_index: true })
|
|
448
452
|
.extend({
|
|
449
|
-
type: z.literal(
|
|
450
|
-
buttonIndex: payloadSchema.shape.button_index,
|
|
453
|
+
type: z.literal(ACTION_TYPE_POST),
|
|
451
454
|
})
|
|
452
455
|
.strict();
|
|
456
|
+
export const snapActionSchema = z.discriminatedUnion("type", [
|
|
457
|
+
snapGetActionSchema,
|
|
458
|
+
snapPostActionSchema,
|
|
459
|
+
]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { payloadSchema } from "../schemas.js";
|
|
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
4
|
/** Default replay window per SPEC.md § Replay Protection (5 minutes). */
|
|
@@ -26,7 +26,7 @@ export async function parseRequest(request, options = {}) {
|
|
|
26
26
|
if (request.method === "GET") {
|
|
27
27
|
return {
|
|
28
28
|
success: true,
|
|
29
|
-
action: { type:
|
|
29
|
+
action: { type: ACTION_TYPE_GET },
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
const maxSkew = DEFAULT_SNAP_POST_MAX_SKEW_SECONDS;
|
|
@@ -81,11 +81,8 @@ export async function parseRequest(request, options = {}) {
|
|
|
81
81
|
return {
|
|
82
82
|
success: true,
|
|
83
83
|
action: {
|
|
84
|
-
type:
|
|
85
|
-
|
|
86
|
-
inputs: body.inputs,
|
|
87
|
-
buttonIndex: body.button_index,
|
|
88
|
-
timestamp: body.timestamp,
|
|
84
|
+
type: ACTION_TYPE_POST,
|
|
85
|
+
...body,
|
|
89
86
|
},
|
|
90
87
|
};
|
|
91
88
|
}
|
package/dist/ui/catalog.d.ts
CHANGED
|
@@ -240,7 +240,7 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
240
240
|
snap_post: {
|
|
241
241
|
description: string;
|
|
242
242
|
params: z.ZodObject<{
|
|
243
|
-
|
|
243
|
+
button_index: z.ZodNumber;
|
|
244
244
|
target: z.ZodString;
|
|
245
245
|
label: z.ZodOptional<z.ZodString>;
|
|
246
246
|
style: z.ZodOptional<z.ZodEnum<{
|
package/dist/ui/catalog.js
CHANGED
|
@@ -18,7 +18,7 @@ import { groupProps } from "./group.js";
|
|
|
18
18
|
import { stackProps } from "./stack.js";
|
|
19
19
|
import { actionButtonProps } from "./button.js";
|
|
20
20
|
const snapPostParams = z.object({
|
|
21
|
-
|
|
21
|
+
button_index: z.number().int().nonnegative(),
|
|
22
22
|
target: z.string(),
|
|
23
23
|
label: z.string().optional(),
|
|
24
24
|
style: z.enum(BUTTON_STYLE_VALUES).optional(),
|
package/dist/validator.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ export type ValidationResult = {
|
|
|
3
3
|
valid: boolean;
|
|
4
4
|
issues: z.core.$ZodIssue[];
|
|
5
5
|
};
|
|
6
|
-
export declare function
|
|
7
|
-
export declare function
|
|
6
|
+
export declare function validateSnapResponse(json: unknown): ValidationResult;
|
|
7
|
+
export declare function validateFirstPageResponse(json: unknown): ValidationResult;
|
package/dist/validator.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BUTTON_GROUP_STYLE, DEFAULT_BUTTON_LAYOUT, ELEMENT_TYPE, LIMITS, SPACER_SIZE, TEXT_STYLE, } from "./constants.js";
|
|
2
|
-
import {
|
|
2
|
+
import { firstPageResponseSchema, snapResponseSchema, } from "./schemas.js";
|
|
3
3
|
/** Not in SPEC — rough px used only for {@link measureHeightBudget}. */
|
|
4
4
|
const PAGE_HEIGHT_HEURISTIC = {
|
|
5
5
|
baseChromePx: 32,
|
|
@@ -116,8 +116,8 @@ function heightBudgetValidationErrorForRoot(root) {
|
|
|
116
116
|
: DEFAULT_BUTTON_LAYOUT;
|
|
117
117
|
return heightBudgetValidationError(root.page.elements.children, root.page.buttons ?? [], layout);
|
|
118
118
|
}
|
|
119
|
-
export function
|
|
120
|
-
const parsed =
|
|
119
|
+
export function validateSnapResponse(json) {
|
|
120
|
+
const parsed = snapResponseSchema.safeParse(json);
|
|
121
121
|
if (!parsed.success) {
|
|
122
122
|
return {
|
|
123
123
|
valid: false,
|
|
@@ -130,8 +130,8 @@ export function validatePage(json) {
|
|
|
130
130
|
}
|
|
131
131
|
return { valid: true, issues: [] };
|
|
132
132
|
}
|
|
133
|
-
export function
|
|
134
|
-
const parsed =
|
|
133
|
+
export function validateFirstPageResponse(json) {
|
|
134
|
+
const parsed = firstPageResponseSchema.safeParse(json);
|
|
135
135
|
if (!parsed.success) {
|
|
136
136
|
return {
|
|
137
137
|
valid: false,
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -13,17 +13,18 @@ export {
|
|
|
13
13
|
type PaletteColor,
|
|
14
14
|
} from "./constants";
|
|
15
15
|
export {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
snapResponseSchema,
|
|
17
|
+
firstPageResponseSchema,
|
|
18
18
|
payloadSchema,
|
|
19
19
|
type SnapAction,
|
|
20
20
|
type SnapContext,
|
|
21
21
|
type SnapResponse,
|
|
22
|
+
type SnapHandlerResult,
|
|
22
23
|
type SnapFunction,
|
|
23
24
|
type SnapPayload,
|
|
24
25
|
} from "./schemas";
|
|
25
26
|
export {
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
validateSnapResponse,
|
|
28
|
+
validateFirstPageResponse,
|
|
28
29
|
type ValidationResult,
|
|
29
30
|
} from "./validator";
|
package/src/schemas.ts
CHANGED
|
@@ -391,6 +391,8 @@ const buttonSchema = z
|
|
|
391
391
|
}
|
|
392
392
|
});
|
|
393
393
|
|
|
394
|
+
export type Button = z.infer<typeof buttonSchema>;
|
|
395
|
+
|
|
394
396
|
/** Child elements allowed inside `group` (no media, no nested group) */
|
|
395
397
|
const groupChildElementSchema = z.discriminatedUnion("type", [
|
|
396
398
|
textElementSchema,
|
|
@@ -431,6 +433,8 @@ const elementSchema = z.discriminatedUnion("type", [
|
|
|
431
433
|
barChartElementSchema,
|
|
432
434
|
]);
|
|
433
435
|
|
|
436
|
+
export type Element = z.infer<typeof elementSchema>;
|
|
437
|
+
|
|
434
438
|
const elementsSchema = z
|
|
435
439
|
.object({
|
|
436
440
|
type: z.literal(PAGE_ROOT_TYPE.stack),
|
|
@@ -443,11 +447,9 @@ const elementsSchema = z
|
|
|
443
447
|
})
|
|
444
448
|
.strict();
|
|
445
449
|
|
|
446
|
-
export type Button = z.infer<typeof buttonSchema>;
|
|
447
|
-
export type Element = z.infer<typeof elementSchema>;
|
|
448
450
|
export type Elements = z.infer<typeof elementsSchema>;
|
|
449
451
|
|
|
450
|
-
export const
|
|
452
|
+
export const snapResponseSchema = z
|
|
451
453
|
.object({
|
|
452
454
|
version: z.literal(SPEC_VERSION),
|
|
453
455
|
page: z
|
|
@@ -481,37 +483,48 @@ export const rootSchema = z
|
|
|
481
483
|
})
|
|
482
484
|
.strict();
|
|
483
485
|
|
|
486
|
+
// canonical snap response type
|
|
487
|
+
export type SnapResponse = z.infer<typeof snapResponseSchema>;
|
|
488
|
+
// what snap handlers may return (keeps optional fields optional)
|
|
489
|
+
export type SnapHandlerResult = z.input<typeof snapResponseSchema>;
|
|
490
|
+
|
|
484
491
|
// extra constraints for the first page to make it look nicer
|
|
485
|
-
export const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
(
|
|
490
|
-
el
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
492
|
+
export const firstPageResponseSchema = snapResponseSchema.superRefine(
|
|
493
|
+
(response, ctx) => {
|
|
494
|
+
const elements = response.page.elements.children;
|
|
495
|
+
|
|
496
|
+
const hasTextTitleOrBody = elements.some(
|
|
497
|
+
(el) =>
|
|
498
|
+
el.type === ELEMENT_TYPE.text &&
|
|
499
|
+
(el.style === TEXT_STYLE.title || el.style === TEXT_STYLE.body),
|
|
500
|
+
);
|
|
501
|
+
if (!hasTextTitleOrBody) {
|
|
502
|
+
ctx.addIssue({
|
|
503
|
+
code: "custom",
|
|
504
|
+
message:
|
|
505
|
+
'first page must have at least one text element with style "title" or "body"',
|
|
506
|
+
path: ["page", "elements", "children"],
|
|
507
|
+
});
|
|
508
|
+
}
|
|
501
509
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
});
|
|
510
|
+
const hasInteractive = elements.some((el) =>
|
|
511
|
+
INTERACTIVE_ELEMENT_TYPES.includes(el.type),
|
|
512
|
+
);
|
|
513
|
+
const hasMedia = elements.some((el) =>
|
|
514
|
+
MEDIA_ELEMENT_TYPES.includes(el.type),
|
|
515
|
+
);
|
|
516
|
+
if (!hasInteractive && !hasMedia) {
|
|
517
|
+
ctx.addIssue({
|
|
518
|
+
code: "custom",
|
|
519
|
+
message:
|
|
520
|
+
"first page must have at least one interactive element (button_group, slider, text_input, toggle) or media element (image, grid)",
|
|
521
|
+
path: ["page", "elements", "children"],
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
export type FirstPageResponse = z.infer<typeof firstPageResponseSchema>;
|
|
515
528
|
|
|
516
529
|
const postInputValueSchema = z.union([
|
|
517
530
|
z.string(),
|
|
@@ -535,29 +548,35 @@ export const payloadSchema = z
|
|
|
535
548
|
})
|
|
536
549
|
.strict();
|
|
537
550
|
|
|
538
|
-
export type SnapResponse = z.infer<typeof rootSchema>;
|
|
539
|
-
export type SnapPage = SnapResponse["page"];
|
|
540
551
|
export type SnapPayload = z.infer<typeof payloadSchema>;
|
|
541
552
|
|
|
553
|
+
export const ACTION_TYPE_GET = "get" as const;
|
|
554
|
+
export const ACTION_TYPE_POST = "post" as const;
|
|
555
|
+
|
|
556
|
+
const snapGetActionSchema = z.object({
|
|
557
|
+
type: z.literal(ACTION_TYPE_GET),
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
export type SnapGetAction = z.infer<typeof snapGetActionSchema>;
|
|
561
|
+
|
|
542
562
|
const snapPostActionSchema = payloadSchema
|
|
543
|
-
.omit({ button_index: true })
|
|
544
563
|
.extend({
|
|
545
|
-
type: z.literal(
|
|
546
|
-
buttonIndex: payloadSchema.shape.button_index,
|
|
564
|
+
type: z.literal(ACTION_TYPE_POST),
|
|
547
565
|
})
|
|
548
566
|
.strict();
|
|
549
567
|
|
|
550
568
|
export type SnapPostAction = z.infer<typeof snapPostActionSchema>;
|
|
551
569
|
|
|
552
|
-
export
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
570
|
+
export const snapActionSchema = z.discriminatedUnion("type", [
|
|
571
|
+
snapGetActionSchema,
|
|
572
|
+
snapPostActionSchema,
|
|
573
|
+
]);
|
|
574
|
+
|
|
575
|
+
export type SnapAction = z.infer<typeof snapActionSchema>;
|
|
557
576
|
|
|
558
577
|
export type SnapContext = {
|
|
559
578
|
action: SnapAction;
|
|
560
579
|
request: Request;
|
|
561
580
|
};
|
|
562
581
|
|
|
563
|
-
export type SnapFunction = (ctx: SnapContext) => Promise<
|
|
582
|
+
export type SnapFunction = (ctx: SnapContext) => Promise<SnapHandlerResult>;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ACTION_TYPE_GET,
|
|
3
|
+
ACTION_TYPE_POST,
|
|
4
|
+
payloadSchema,
|
|
5
|
+
type SnapAction,
|
|
6
|
+
} from "../schemas";
|
|
2
7
|
import { decodePayload, verifyJFSRequestBody } from "./verify";
|
|
3
8
|
import { z } from "zod";
|
|
4
9
|
|
|
@@ -66,7 +71,7 @@ export async function parseRequest(
|
|
|
66
71
|
if (request.method === "GET") {
|
|
67
72
|
return {
|
|
68
73
|
success: true,
|
|
69
|
-
action: { type:
|
|
74
|
+
action: { type: ACTION_TYPE_GET },
|
|
70
75
|
};
|
|
71
76
|
}
|
|
72
77
|
|
|
@@ -129,11 +134,8 @@ export async function parseRequest(
|
|
|
129
134
|
return {
|
|
130
135
|
success: true,
|
|
131
136
|
action: {
|
|
132
|
-
type:
|
|
133
|
-
|
|
134
|
-
inputs: body.inputs,
|
|
135
|
-
buttonIndex: body.button_index,
|
|
136
|
-
timestamp: body.timestamp,
|
|
137
|
+
type: ACTION_TYPE_POST,
|
|
138
|
+
...body,
|
|
137
139
|
},
|
|
138
140
|
};
|
|
139
141
|
}
|
package/src/ui/catalog.ts
CHANGED
|
@@ -19,7 +19,7 @@ import { stackProps } from "./stack.js";
|
|
|
19
19
|
import { actionButtonProps } from "./button.js";
|
|
20
20
|
|
|
21
21
|
const snapPostParams = z.object({
|
|
22
|
-
|
|
22
|
+
button_index: z.number().int().nonnegative(),
|
|
23
23
|
target: z.string(),
|
|
24
24
|
label: z.string().optional(),
|
|
25
25
|
style: z.enum(BUTTON_STYLE_VALUES).optional(),
|
package/src/validator.ts
CHANGED
|
@@ -8,13 +8,13 @@ import {
|
|
|
8
8
|
TEXT_STYLE,
|
|
9
9
|
} from "./constants";
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
firstPageResponseSchema,
|
|
12
|
+
snapResponseSchema,
|
|
13
13
|
type Button,
|
|
14
14
|
type Element,
|
|
15
15
|
} from "./schemas";
|
|
16
16
|
|
|
17
|
-
type SnapRoot = z.infer<typeof
|
|
17
|
+
type SnapRoot = z.infer<typeof snapResponseSchema>;
|
|
18
18
|
|
|
19
19
|
export type ValidationResult = {
|
|
20
20
|
valid: boolean;
|
|
@@ -158,8 +158,8 @@ function heightBudgetValidationErrorForRoot(
|
|
|
158
158
|
);
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
export function
|
|
162
|
-
const parsed =
|
|
161
|
+
export function validateSnapResponse(json: unknown): ValidationResult {
|
|
162
|
+
const parsed = snapResponseSchema.safeParse(json);
|
|
163
163
|
if (!parsed.success) {
|
|
164
164
|
return {
|
|
165
165
|
valid: false,
|
|
@@ -175,8 +175,8 @@ export function validatePage(json: unknown): ValidationResult {
|
|
|
175
175
|
return { valid: true, issues: [] };
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
export function
|
|
179
|
-
const parsed =
|
|
178
|
+
export function validateFirstPageResponse(json: unknown): ValidationResult {
|
|
179
|
+
const parsed = firstPageResponseSchema.safeParse(json);
|
|
180
180
|
if (!parsed.success) {
|
|
181
181
|
return {
|
|
182
182
|
valid: false,
|