@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.
@@ -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/json+farcaster-snap";
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/json+farcaster-snap";
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";
@@ -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, } from "./parseRequest.js";
@@ -1,4 +1,4 @@
1
- import { type SnapAction, type SnapResponse, type SnapPage } from "./schemas.js";
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 { MEDIA_TYPE, SPEC_VERSION } from "./constants.js";
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
- }
@@ -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.1.0",
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
- "./constants": {
20
- "types": "./dist/constants.d.ts",
21
- "import": "./dist/constants.js",
22
- "default": "./dist/constants.js"
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/json+farcaster-snap" as const;
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(String(fid))}`;
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 { MEDIA_TYPE, SPEC_VERSION } from "./constants";
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
- }
@@ -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