@farcaster/snap 1.1.0 → 1.2.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/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/schemas.d.ts +1 -1
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +3 -0
- package/dist/{http.d.ts → server/parseRequest.d.ts} +1 -12
- package/dist/{http.js → server/parseRequest.js} +1 -56
- package/dist/verify.test.js +1 -1
- package/package.json +5 -5
- package/src/constants.ts +1 -1
- package/src/index.ts +0 -9
- package/src/{hubs.ts → server/hubs.ts} +3 -1
- package/src/server/index.ts +11 -0
- package/src/{http.ts → server/parseRequest.ts} +1 -79
- package/src/verify.test.ts +1 -1
- /package/dist/{hubs.d.ts → server/hubs.d.ts} +0 -0
- /package/dist/{hubs.js → server/hubs.js} +0 -0
- /package/dist/{verify.d.ts → server/verify.d.ts} +0 -0
- /package/dist/{verify.js → server/verify.js} +0 -0
- /package/src/{verify.ts → server/verify.ts} +0 -0
package/dist/constants.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const POST_GRID_TAP_KEY: "grid_tap";
|
|
2
2
|
export declare const SPEC_VERSION: "1.0";
|
|
3
|
-
export declare const MEDIA_TYPE: "application/
|
|
3
|
+
export declare const MEDIA_TYPE: "application/vnd.farcaster.snap+json";
|
|
4
4
|
export declare const LIMITS: {
|
|
5
5
|
readonly maxElementsPerPage: 5;
|
|
6
6
|
readonly maxButtonsPerPage: 4;
|
package/dist/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const POST_GRID_TAP_KEY = "grid_tap";
|
|
2
2
|
export const SPEC_VERSION = "1.0";
|
|
3
|
-
export const MEDIA_TYPE = "application/
|
|
3
|
+
export const MEDIA_TYPE = "application/vnd.farcaster.snap+json";
|
|
4
4
|
export const LIMITS = {
|
|
5
5
|
maxElementsPerPage: 5,
|
|
6
6
|
maxButtonsPerPage: 4,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export { verifyJFSRequestBody } from "./verify.js";
|
|
2
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";
|
|
3
2
|
export { rootSchema, firstPageRootSchema, payloadSchema, type SnapAction, type SnapContext, type SnapResponse, type SnapFunction, type SnapPayload, } from "./schemas.js";
|
|
4
|
-
export { parseRequest, sendResponse, type ParseRequestError, type ParseRequestOptions, type ParseRequestResult, type SendResponseOptions, } from "./http.js";
|
|
5
3
|
export { validatePage, validateFirstPage, type ValidationResult, } from "./validator.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export { verifyJFSRequestBody } from "./verify.js";
|
|
2
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";
|
|
3
2
|
export { rootSchema, firstPageRootSchema, payloadSchema, } from "./schemas.js";
|
|
4
|
-
export { parseRequest, sendResponse, } from "./http.js";
|
|
5
3
|
export { validatePage, validateFirstPage, } from "./validator.js";
|
package/dist/schemas.d.ts
CHANGED
|
@@ -1405,11 +1405,11 @@ export type SnapResponse = z.infer<typeof rootSchema>;
|
|
|
1405
1405
|
export type SnapPage = SnapResponse["page"];
|
|
1406
1406
|
export type SnapPayload = z.infer<typeof payloadSchema>;
|
|
1407
1407
|
declare const snapPostActionSchema: z.ZodObject<{
|
|
1408
|
+
fid: z.ZodNumber;
|
|
1408
1409
|
inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodObject<{
|
|
1409
1410
|
row: z.ZodNumber;
|
|
1410
1411
|
col: z.ZodNumber;
|
|
1411
1412
|
}, z.core.$strict>]>>>;
|
|
1412
|
-
fid: z.ZodNumber;
|
|
1413
1413
|
timestamp: z.ZodNumber;
|
|
1414
1414
|
type: z.ZodLiteral<"post">;
|
|
1415
1415
|
buttonIndex: z.ZodNumber;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { verifyJFSRequestBody, decodePayload } from "./verify.js";
|
|
2
|
+
export { DEFAULT_SNAP_HUB_HTTP_BASE_URL, getActiveEd25519SignerKeysFromHubHttp, } from "./hubs.js";
|
|
3
|
+
export { parseRequest, type ParseRequestError, type ParseRequestOptions, type ParseRequestResult, } from "./parseRequest.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type SnapAction
|
|
1
|
+
import { type SnapAction } from "../schemas.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
export type ParseRequestError = {
|
|
4
4
|
type: "method_not_allowed";
|
|
@@ -35,14 +35,3 @@ export type ParseRequestResult = {
|
|
|
35
35
|
* - `POST`: the body must be JSON in JFS form (`header` / `payload` / `signature`) even if JFS verification is skipped.
|
|
36
36
|
*/
|
|
37
37
|
export declare function parseRequest(request: Request, options?: ParseRequestOptions): Promise<ParseRequestResult>;
|
|
38
|
-
export type SendResponseOptions = ResponseInit & {
|
|
39
|
-
/** When false, invalid pages return 500 instead of 400. Default true. */
|
|
40
|
-
clientErrorOnInvalid?: boolean;
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* Validate a snap root or bare `page` object, then return a JSON Response for the client.
|
|
44
|
-
* Sets `Content-Type: application/json+farcaster-snap`.
|
|
45
|
-
*
|
|
46
|
-
* On validation failure returns JSON `{ "error": "...", "issues": [...] }` with status 400 or 500.
|
|
47
|
-
*/
|
|
48
|
-
export declare function sendResponse(payload: SnapResponse | SnapPage, init?: SendResponseOptions): Response;
|
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { payloadSchema } from "../schemas.js";
|
|
2
2
|
import { decodePayload, verifyJFSRequestBody } from "./verify.js";
|
|
3
|
-
import { payloadSchema, rootSchema, } from "./schemas.js";
|
|
4
|
-
import { validatePage } from "./validator.js";
|
|
5
3
|
import { z } from "zod";
|
|
6
4
|
/** Default replay window per SPEC.md § Replay Protection (5 minutes). */
|
|
7
5
|
const DEFAULT_SNAP_POST_MAX_SKEW_SECONDS = 300;
|
|
8
|
-
function isRecord(v) {
|
|
9
|
-
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
10
|
-
}
|
|
11
|
-
function normalizeToRoot(payload) {
|
|
12
|
-
if (isRecord(payload) && "version" in payload && "page" in payload) {
|
|
13
|
-
return payload;
|
|
14
|
-
}
|
|
15
|
-
return { version: SPEC_VERSION, page: payload };
|
|
16
|
-
}
|
|
17
6
|
const requestBodySchema = z.object({
|
|
18
7
|
header: z.string(),
|
|
19
8
|
payload: z.string(),
|
|
@@ -100,47 +89,3 @@ export async function parseRequest(request, options = {}) {
|
|
|
100
89
|
},
|
|
101
90
|
};
|
|
102
91
|
}
|
|
103
|
-
/**
|
|
104
|
-
* Validate a snap root or bare `page` object, then return a JSON Response for the client.
|
|
105
|
-
* Sets `Content-Type: application/json+farcaster-snap`.
|
|
106
|
-
*
|
|
107
|
-
* On validation failure returns JSON `{ "error": "...", "issues": [...] }` with status 400 or 500.
|
|
108
|
-
*/
|
|
109
|
-
export function sendResponse(payload, init) {
|
|
110
|
-
const rootUnknown = normalizeToRoot(payload);
|
|
111
|
-
const validation = validatePage(rootUnknown);
|
|
112
|
-
const clientError = init?.clientErrorOnInvalid !== false;
|
|
113
|
-
const status = init?.status;
|
|
114
|
-
if (!validation.valid) {
|
|
115
|
-
const errStatus = clientError ? 400 : 500;
|
|
116
|
-
return new Response(JSON.stringify({
|
|
117
|
-
error: clientError
|
|
118
|
-
? "invalid snap page"
|
|
119
|
-
: "server produced an invalid snap page",
|
|
120
|
-
issues: validation.issues,
|
|
121
|
-
}), {
|
|
122
|
-
status: status ?? errStatus,
|
|
123
|
-
headers: {
|
|
124
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
125
|
-
...normalizeHeaders(init?.headers),
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
const finalized = rootSchema.parse(rootUnknown);
|
|
130
|
-
const headers = new Headers(init?.headers);
|
|
131
|
-
headers.set("Content-Type", `${MEDIA_TYPE}; charset=utf-8`);
|
|
132
|
-
return new Response(JSON.stringify(finalized), {
|
|
133
|
-
...init,
|
|
134
|
-
status: status ?? init?.status ?? 200,
|
|
135
|
-
headers,
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
function normalizeHeaders(h) {
|
|
139
|
-
if (h === undefined)
|
|
140
|
-
return {};
|
|
141
|
-
const out = {};
|
|
142
|
-
new Headers(h).forEach((value, key) => {
|
|
143
|
-
out[key] = value;
|
|
144
|
-
});
|
|
145
|
-
return out;
|
|
146
|
-
}
|
package/dist/verify.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { verifyJFSRequestBody } from "./verify.js";
|
|
2
|
+
import { verifyJFSRequestBody } from "./server/verify.js";
|
|
3
3
|
const validRequestBody = `{
|
|
4
4
|
"header":"eyJmaWQiOjI2MTMxOSwidHlwZSI6ImFwcF9rZXkiLCJrZXkiOiIweGY0ZGQyNjczYTUzMjEwYzQ3ZGYzZjFmNTk0NjZlZTdhMTM3ZmQxOGQ5NTVjMmU2OGExMmQwOTE2MGE2NmMyMTUifQ",
|
|
5
5
|
"payload":"eyJmaWQiOjI2MTMxOSwiaW5wdXRzIjp7ImRpc3BsYXkiOiJJU08gKFVUQykifSwiYnV0dG9uX2luZGV4IjowLCJ0aW1lc3RhbXAiOjE3NzQ2OTMyMTN9",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farcaster/snap",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Farcaster Snaps 🫰",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"import": "./dist/index.js",
|
|
17
17
|
"default": "./dist/index.js"
|
|
18
18
|
},
|
|
19
|
-
"./
|
|
20
|
-
"types": "./dist/
|
|
21
|
-
"import": "./dist/
|
|
22
|
-
"default": "./dist/
|
|
19
|
+
"./server": {
|
|
20
|
+
"types": "./dist/server/index.d.ts",
|
|
21
|
+
"import": "./dist/server/index.js",
|
|
22
|
+
"default": "./dist/server/index.js"
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
package/src/constants.ts
CHANGED
|
@@ -2,7 +2,7 @@ export const POST_GRID_TAP_KEY = "grid_tap" as const;
|
|
|
2
2
|
|
|
3
3
|
export const SPEC_VERSION = "1.0" as const;
|
|
4
4
|
|
|
5
|
-
export const MEDIA_TYPE = "application/
|
|
5
|
+
export const MEDIA_TYPE = "application/vnd.farcaster.snap+json" as const;
|
|
6
6
|
|
|
7
7
|
export const LIMITS = {
|
|
8
8
|
maxElementsPerPage: 5,
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export { verifyJFSRequestBody } from "./verify";
|
|
2
1
|
export {
|
|
3
2
|
POST_GRID_TAP_KEY,
|
|
4
3
|
PAGE_ROOT_TYPE,
|
|
@@ -23,14 +22,6 @@ export {
|
|
|
23
22
|
type SnapFunction,
|
|
24
23
|
type SnapPayload,
|
|
25
24
|
} from "./schemas";
|
|
26
|
-
export {
|
|
27
|
-
parseRequest,
|
|
28
|
-
sendResponse,
|
|
29
|
-
type ParseRequestError,
|
|
30
|
-
type ParseRequestOptions,
|
|
31
|
-
type ParseRequestResult,
|
|
32
|
-
type SendResponseOptions,
|
|
33
|
-
} from "./http";
|
|
34
25
|
export {
|
|
35
26
|
validatePage,
|
|
36
27
|
validateFirstPage,
|
|
@@ -304,7 +304,9 @@ export async function getActiveEd25519SignerKeysFromHubHttp(
|
|
|
304
304
|
options?: FetchSignerKeysFromHubHttpOptions,
|
|
305
305
|
): Promise<FetchSignerKeysResult> {
|
|
306
306
|
const base = stripHubHttpBaseUrlTrailingSlash(httpBaseUrl);
|
|
307
|
-
const url = `${base}/v1/onChainSignersByFid?fid=${encodeURIComponent(
|
|
307
|
+
const url = `${base}/v1/onChainSignersByFid?fid=${encodeURIComponent(
|
|
308
|
+
String(fid),
|
|
309
|
+
)}`;
|
|
308
310
|
const fetchImpl = options?.fetchFn ?? globalThis.fetch;
|
|
309
311
|
if (typeof fetchImpl !== "function") {
|
|
310
312
|
return {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { verifyJFSRequestBody, decodePayload } from "./verify";
|
|
2
|
+
export {
|
|
3
|
+
DEFAULT_SNAP_HUB_HTTP_BASE_URL,
|
|
4
|
+
getActiveEd25519SignerKeysFromHubHttp,
|
|
5
|
+
} from "./hubs";
|
|
6
|
+
export {
|
|
7
|
+
parseRequest,
|
|
8
|
+
type ParseRequestError,
|
|
9
|
+
type ParseRequestOptions,
|
|
10
|
+
type ParseRequestResult,
|
|
11
|
+
} from "./parseRequest";
|
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { payloadSchema, type SnapAction } from "../schemas";
|
|
2
2
|
import { decodePayload, verifyJFSRequestBody } from "./verify";
|
|
3
|
-
import {
|
|
4
|
-
payloadSchema,
|
|
5
|
-
rootSchema,
|
|
6
|
-
type SnapAction,
|
|
7
|
-
type SnapResponse,
|
|
8
|
-
type SnapPage,
|
|
9
|
-
} from "./schemas";
|
|
10
|
-
import { validatePage } from "./validator";
|
|
11
3
|
import { z } from "zod";
|
|
12
4
|
|
|
13
5
|
/** Default replay window per SPEC.md § Replay Protection (5 minutes). */
|
|
@@ -46,17 +38,6 @@ export type ParseRequestResult =
|
|
|
46
38
|
| { success: true; action: SnapAction }
|
|
47
39
|
| { success: false; error: ParseRequestError };
|
|
48
40
|
|
|
49
|
-
function isRecord(v: unknown): v is Record<string, unknown> {
|
|
50
|
-
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function normalizeToRoot(payload: unknown): unknown {
|
|
54
|
-
if (isRecord(payload) && "version" in payload && "page" in payload) {
|
|
55
|
-
return payload;
|
|
56
|
-
}
|
|
57
|
-
return { version: SPEC_VERSION, page: payload };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
41
|
const requestBodySchema = z.object({
|
|
61
42
|
header: z.string(),
|
|
62
43
|
payload: z.string(),
|
|
@@ -156,62 +137,3 @@ export async function parseRequest(
|
|
|
156
137
|
},
|
|
157
138
|
};
|
|
158
139
|
}
|
|
159
|
-
|
|
160
|
-
export type SendResponseOptions = ResponseInit & {
|
|
161
|
-
/** When false, invalid pages return 500 instead of 400. Default true. */
|
|
162
|
-
clientErrorOnInvalid?: boolean;
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Validate a snap root or bare `page` object, then return a JSON Response for the client.
|
|
167
|
-
* Sets `Content-Type: application/json+farcaster-snap`.
|
|
168
|
-
*
|
|
169
|
-
* On validation failure returns JSON `{ "error": "...", "issues": [...] }` with status 400 or 500.
|
|
170
|
-
*/
|
|
171
|
-
export function sendResponse(
|
|
172
|
-
payload: SnapResponse | SnapPage,
|
|
173
|
-
init?: SendResponseOptions,
|
|
174
|
-
): Response {
|
|
175
|
-
const rootUnknown = normalizeToRoot(payload);
|
|
176
|
-
const validation = validatePage(rootUnknown);
|
|
177
|
-
const clientError = init?.clientErrorOnInvalid !== false;
|
|
178
|
-
const status = init?.status;
|
|
179
|
-
|
|
180
|
-
if (!validation.valid) {
|
|
181
|
-
const errStatus = clientError ? 400 : 500;
|
|
182
|
-
return new Response(
|
|
183
|
-
JSON.stringify({
|
|
184
|
-
error: clientError
|
|
185
|
-
? "invalid snap page"
|
|
186
|
-
: "server produced an invalid snap page",
|
|
187
|
-
issues: validation.issues,
|
|
188
|
-
}),
|
|
189
|
-
{
|
|
190
|
-
status: status ?? errStatus,
|
|
191
|
-
headers: {
|
|
192
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
193
|
-
...normalizeHeaders(init?.headers),
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const finalized = rootSchema.parse(rootUnknown);
|
|
200
|
-
const headers = new Headers(init?.headers);
|
|
201
|
-
headers.set("Content-Type", `${MEDIA_TYPE}; charset=utf-8`);
|
|
202
|
-
|
|
203
|
-
return new Response(JSON.stringify(finalized), {
|
|
204
|
-
...init,
|
|
205
|
-
status: status ?? init?.status ?? 200,
|
|
206
|
-
headers,
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function normalizeHeaders(h: HeadersInit | undefined): Record<string, string> {
|
|
211
|
-
if (h === undefined) return {};
|
|
212
|
-
const out: Record<string, string> = {};
|
|
213
|
-
new Headers(h).forEach((value, key) => {
|
|
214
|
-
out[key] = value;
|
|
215
|
-
});
|
|
216
|
-
return out;
|
|
217
|
-
}
|
package/src/verify.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { verifyJFSRequestBody } from "./verify";
|
|
2
|
+
import { verifyJFSRequestBody } from "./server/verify";
|
|
3
3
|
|
|
4
4
|
const validRequestBody = `{
|
|
5
5
|
"header":"eyJmaWQiOjI2MTMxOSwidHlwZSI6ImFwcF9rZXkiLCJrZXkiOiIweGY0ZGQyNjczYTUzMjEwYzQ3ZGYzZjFmNTk0NjZlZTdhMTM3ZmQxOGQ5NTVjMmU2OGExMmQwOTE2MGE2NmMyMTUifQ",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|