@foldset/cloudflare 0.0.2 → 0.0.3
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/ai-crawlers/index.d.ts +15 -0
- package/dist/ai-crawlers/index.d.ts.map +1 -0
- package/dist/ai-crawlers/index.js +26 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +1 -0
- package/dist/facilitators/index.d.ts +16 -0
- package/dist/facilitators/index.d.ts.map +1 -0
- package/dist/facilitators/index.js +35 -0
- package/dist/hono.d.ts +2 -0
- package/dist/hono.d.ts.map +1 -0
- package/dist/hono.js +1 -0
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -163
- package/dist/payment/adapter.d.ts +16 -0
- package/dist/payment/adapter.d.ts.map +1 -0
- package/dist/payment/adapter.js +43 -0
- package/dist/payment/handler.d.ts +6 -0
- package/dist/payment/handler.d.ts.map +1 -0
- package/dist/payment/handler.js +86 -0
- package/dist/payment/paywall.d.ts +4 -0
- package/dist/payment/paywall.d.ts.map +1 -0
- package/dist/payment/paywall.js +99 -0
- package/dist/payment/routes.d.ts +5 -0
- package/dist/payment/routes.d.ts.map +1 -0
- package/dist/payment/routes.js +31 -0
- package/dist/payment/settlement.d.ts +8 -0
- package/dist/payment/settlement.d.ts.map +1 -0
- package/dist/payment/settlement.js +27 -0
- package/dist/payment/setup.d.ts +7 -0
- package/dist/payment/setup.d.ts.map +1 -0
- package/dist/payment/setup.js +85 -0
- package/dist/payment-methods/index.d.ts +18 -0
- package/dist/payment-methods/index.d.ts.map +1 -0
- package/dist/payment-methods/index.js +18 -0
- package/dist/restrictions/index.d.ts +16 -0
- package/dist/restrictions/index.d.ts.map +1 -0
- package/dist/restrictions/index.js +18 -0
- package/dist/telemetry/logging.d.ts +18 -0
- package/dist/telemetry/logging.d.ts.map +1 -0
- package/dist/telemetry/logging.js +47 -0
- package/dist/telemetry/sentry.d.ts +8 -0
- package/dist/telemetry/sentry.d.ts.map +1 -0
- package/dist/telemetry/sentry.js +19 -0
- package/dist/types.d.ts +5 -9
- package/dist/types.d.ts.map +1 -0
- package/dist/webhooks/index.d.ts +23 -0
- package/dist/webhooks/index.d.ts.map +1 -0
- package/dist/webhooks/index.js +50 -0
- package/package.json +21 -18
- package/README.md +0 -1
- package/dist/adapter.d.ts +0 -71
- package/dist/adapter.js +0 -99
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as Sentry from "@sentry/cloudflare";
|
|
2
|
+
export async function handleSettlement(c, httpServer, paymentPayload, paymentRequirements, res) {
|
|
3
|
+
if (res.status >= 400) {
|
|
4
|
+
return res;
|
|
5
|
+
}
|
|
6
|
+
try {
|
|
7
|
+
const settleResult = await httpServer.processSettlement(paymentPayload, paymentRequirements);
|
|
8
|
+
if (!settleResult.success) {
|
|
9
|
+
Sentry.captureException(new Error(`Settlement failed: ${settleResult.errorReason}`));
|
|
10
|
+
return c.json({
|
|
11
|
+
error: "Settlement failed",
|
|
12
|
+
details: settleResult.errorReason,
|
|
13
|
+
}, 402);
|
|
14
|
+
}
|
|
15
|
+
Object.entries(settleResult.headers).forEach(([key, value]) => {
|
|
16
|
+
res.headers.set(key, value);
|
|
17
|
+
});
|
|
18
|
+
return res;
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
Sentry.captureException(error);
|
|
22
|
+
return c.json({
|
|
23
|
+
error: "Settlement failed",
|
|
24
|
+
details: error instanceof Error ? error.message : "Unknown error",
|
|
25
|
+
}, 402);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
import { x402HTTPResourceServer } from "@x402/hono";
|
|
3
|
+
import type { Env } from "../types";
|
|
4
|
+
export declare function getHttpServer(c: Context<{
|
|
5
|
+
Bindings: Env;
|
|
6
|
+
}>): Promise<x402HTTPResourceServer | null>;
|
|
7
|
+
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/payment/setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAEL,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAMpB,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AA+DpC,wBAAsB,aAAa,CACjC,CAAC,EAAE,OAAO,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAA;CAAE,CAAC,GAC5B,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CA0CxC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { x402ResourceServer, x402HTTPResourceServer, } from "@x402/hono";
|
|
2
|
+
import { registerExactEvmScheme } from "@x402/evm/exact/server";
|
|
3
|
+
import { registerExactSvmScheme } from "@x402/svm/exact/server";
|
|
4
|
+
import { getRestrictions } from "../restrictions";
|
|
5
|
+
import { getPaymentMethods } from "../payment-methods";
|
|
6
|
+
import { buildRoutesConfig } from "./routes";
|
|
7
|
+
import { getFacilitator } from "../facilitators";
|
|
8
|
+
import { generateHtml } from "./paywall";
|
|
9
|
+
const paywallProvider = {
|
|
10
|
+
generateHtml
|
|
11
|
+
};
|
|
12
|
+
let cachedHttpServer = null;
|
|
13
|
+
let cachedRestrictions = null;
|
|
14
|
+
let cachedPaymentMethods = null;
|
|
15
|
+
/**
|
|
16
|
+
* Custom createHTTPResponse implementation for Foldset
|
|
17
|
+
* Monkey-patched onto x402HTTPResourceServer instances
|
|
18
|
+
*/
|
|
19
|
+
function createFoldsetHTTPResponse(paymentRequired, isWebBrowser, paywallConfig, customHtml, unpaidResponse) {
|
|
20
|
+
// @ts-expect-error - accessing private method
|
|
21
|
+
const html = this.generatePaywallHTML(paymentRequired, paywallConfig, customHtml);
|
|
22
|
+
// Always generate and provide
|
|
23
|
+
// if (isWebBrowser) {
|
|
24
|
+
// // @ts-expect-error - accessing private method
|
|
25
|
+
// const html = this.generatePaywallHTML(paymentRequired, paywallConfig, customHtml);
|
|
26
|
+
// return {
|
|
27
|
+
// status: 402,
|
|
28
|
+
// headers: { "Content-Type": "text/html" },
|
|
29
|
+
// body: html,
|
|
30
|
+
// isHtml: true,
|
|
31
|
+
// };
|
|
32
|
+
// }
|
|
33
|
+
// @ts-expect-error - accessing private method
|
|
34
|
+
const response = this.createHTTPPaymentRequiredResponse(paymentRequired);
|
|
35
|
+
// We don't provide a callback
|
|
36
|
+
// Use callback result if provided, otherwise default to JSON with empty object
|
|
37
|
+
// const contentType = unpaidResponse ? unpaidResponse.contentType : "application/json";
|
|
38
|
+
// const body = unpaidResponse ? unpaidResponse.body : {};
|
|
39
|
+
// Status should be 402 but we return 200 so AI crawlers view the payment instructions
|
|
40
|
+
return {
|
|
41
|
+
status: 200,
|
|
42
|
+
headers: {
|
|
43
|
+
"Content-Type": "text/html",
|
|
44
|
+
...response.headers,
|
|
45
|
+
},
|
|
46
|
+
body: html,
|
|
47
|
+
isHtml: true,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Maybe this function is never needed and can just updated cachedHttpServer on changes to upstream configs
|
|
51
|
+
// ie consider changing to a chained pattern
|
|
52
|
+
export async function getHttpServer(c) {
|
|
53
|
+
const restrictions = await getRestrictions(c);
|
|
54
|
+
const paymentMethods = await getPaymentMethods(c);
|
|
55
|
+
if (restrictions === null || paymentMethods === null) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (cachedHttpServer &&
|
|
59
|
+
restrictions === cachedRestrictions &&
|
|
60
|
+
paymentMethods === cachedPaymentMethods) {
|
|
61
|
+
return cachedHttpServer;
|
|
62
|
+
}
|
|
63
|
+
const facilitator = await getFacilitator(c);
|
|
64
|
+
if (facilitator === null) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
// This could be pulled out
|
|
68
|
+
const server = new x402ResourceServer(facilitator);
|
|
69
|
+
registerExactEvmScheme(server);
|
|
70
|
+
registerExactSvmScheme(server);
|
|
71
|
+
const routesConfig = buildRoutesConfig(restrictions, paymentMethods);
|
|
72
|
+
const httpServer = new x402HTTPResourceServer(server, routesConfig);
|
|
73
|
+
// Monkey-patch createHTTPResponse with our custom implementation
|
|
74
|
+
// @ts-expect-error - overriding private method
|
|
75
|
+
httpServer.createHTTPResponse = createFoldsetHTTPResponse;
|
|
76
|
+
// TODO rfradkin: This is probably the slowest part of the cold start. Figure out how to speed this up.
|
|
77
|
+
// Also this is slow on every request where its updated.
|
|
78
|
+
// Consider looking into creating a serverless facilitator so don't have to request outside the serverless environment
|
|
79
|
+
await httpServer.initialize();
|
|
80
|
+
httpServer.registerPaywallProvider(paywallProvider);
|
|
81
|
+
cachedHttpServer = httpServer;
|
|
82
|
+
cachedRestrictions = restrictions;
|
|
83
|
+
cachedPaymentMethods = paymentMethods;
|
|
84
|
+
return httpServer;
|
|
85
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
import type { Env } from "../types";
|
|
3
|
+
export interface PaymentMethod {
|
|
4
|
+
caip2_id: string;
|
|
5
|
+
decimals: number;
|
|
6
|
+
contract_address: string;
|
|
7
|
+
circle_wallet_address: string;
|
|
8
|
+
extra?: Record<string, string>;
|
|
9
|
+
chain_display_name: string;
|
|
10
|
+
asset_display_name: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function getPaymentMethods(c: Context<{
|
|
13
|
+
Bindings: Env;
|
|
14
|
+
}>): Promise<PaymentMethod[] | null>;
|
|
15
|
+
export declare function storePaymentMethods(c: Context<{
|
|
16
|
+
Bindings: Env;
|
|
17
|
+
}>, paymentMethods: PaymentMethod[]): Promise<void>;
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/payment-methods/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAOD,wBAAsB,iBAAiB,CACrC,CAAC,EAAE,OAAO,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAA;CAAE,CAAC,GAC5B,OAAO,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,CAUjC;AAED,wBAAsB,mBAAmB,CACvC,CAAC,EAAE,OAAO,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAA;CAAE,CAAC,EAC7B,cAAc,EAAE,aAAa,EAAE,GAC9B,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const CACHE_TTL_MS = 30_000;
|
|
2
|
+
let cachedPaymentMethods = null;
|
|
3
|
+
let cacheTimestamp = 0;
|
|
4
|
+
export async function getPaymentMethods(c) {
|
|
5
|
+
const now = Date.now();
|
|
6
|
+
if (cachedPaymentMethods !== null && now - cacheTimestamp < CACHE_TTL_MS) {
|
|
7
|
+
return cachedPaymentMethods;
|
|
8
|
+
}
|
|
9
|
+
const response = await c.env.FOLDSET_CONFIG.get("payment-methods");
|
|
10
|
+
cachedPaymentMethods = response ? JSON.parse(response) : null;
|
|
11
|
+
cacheTimestamp = now;
|
|
12
|
+
return cachedPaymentMethods;
|
|
13
|
+
}
|
|
14
|
+
export async function storePaymentMethods(c, paymentMethods) {
|
|
15
|
+
await c.env.FOLDSET_CONFIG.put("payment-methods", JSON.stringify(paymentMethods), {
|
|
16
|
+
expirationTtl: 60 * 60 * 3 + 60 * 30, // 3 hours + 30 minutes
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
import type { Env } from "../types";
|
|
3
|
+
export interface Restriction {
|
|
4
|
+
host: string;
|
|
5
|
+
path: string;
|
|
6
|
+
description: string;
|
|
7
|
+
price: number;
|
|
8
|
+
scheme: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function getRestrictions(c: Context<{
|
|
11
|
+
Bindings: Env;
|
|
12
|
+
}>): Promise<Restriction[] | null>;
|
|
13
|
+
export declare function storeRestrictions(c: Context<{
|
|
14
|
+
Bindings: Env;
|
|
15
|
+
}>, restrictions: Restriction[]): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/restrictions/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAOD,wBAAsB,eAAe,CACnC,CAAC,EAAE,OAAO,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAA;CAAE,CAAC,GAC5B,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,CAU/B;AAED,wBAAsB,iBAAiB,CACrC,CAAC,EAAE,OAAO,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAA;CAAE,CAAC,EAC7B,YAAY,EAAE,WAAW,EAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const CACHE_TTL_MS = 30_000;
|
|
2
|
+
let cachedRestrictions = null;
|
|
3
|
+
let cacheTimestamp = 0;
|
|
4
|
+
export async function getRestrictions(c) {
|
|
5
|
+
const now = Date.now();
|
|
6
|
+
if (cachedRestrictions !== null && now - cacheTimestamp < CACHE_TTL_MS) {
|
|
7
|
+
return cachedRestrictions;
|
|
8
|
+
}
|
|
9
|
+
const response = await c.env.FOLDSET_CONFIG.get("restrictions");
|
|
10
|
+
cachedRestrictions = response ? JSON.parse(response) : null;
|
|
11
|
+
cacheTimestamp = now;
|
|
12
|
+
return cachedRestrictions;
|
|
13
|
+
}
|
|
14
|
+
export async function storeRestrictions(c, restrictions) {
|
|
15
|
+
await c.env.FOLDSET_CONFIG.put("restrictions", JSON.stringify(restrictions), {
|
|
16
|
+
expirationTtl: 60 * 60 * 3 + 60 * 30, // 3 hours + 30 minutes
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
import type { Env } from "../types";
|
|
3
|
+
export type EventPayload = {
|
|
4
|
+
method: string;
|
|
5
|
+
status_code: number;
|
|
6
|
+
user_agent: string | null;
|
|
7
|
+
referer?: string | null;
|
|
8
|
+
href: string;
|
|
9
|
+
hostname: string;
|
|
10
|
+
pathname: string;
|
|
11
|
+
search: string;
|
|
12
|
+
ip_address?: string | null;
|
|
13
|
+
payment_response?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function logVisitEvent(c: Context<{
|
|
16
|
+
Bindings: Env;
|
|
17
|
+
}>, response: Response): void;
|
|
18
|
+
//# sourceMappingURL=logging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../../src/telemetry/logging.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAIpC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAIpC,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAA;CAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,QA8C9E"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as Sentry from "@sentry/cloudflare";
|
|
2
|
+
import { API_BASE_URL } from "../config";
|
|
3
|
+
const PAYMENT_RESPONSE_HEADER = "PAYMENT-RESPONSE";
|
|
4
|
+
export function logVisitEvent(c, response) {
|
|
5
|
+
const url = new URL(c.req.url);
|
|
6
|
+
const ipAddressHeader = c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for");
|
|
7
|
+
const ipAddress = ipAddressHeader?.split(",")[0]?.trim() || null;
|
|
8
|
+
const paymentResponse = response.headers.get(PAYMENT_RESPONSE_HEADER) || undefined;
|
|
9
|
+
const payload = {
|
|
10
|
+
method: c.req.method,
|
|
11
|
+
status_code: response.status,
|
|
12
|
+
user_agent: c.req.header("user-agent") || null,
|
|
13
|
+
referer: c.req.header("referer") || null,
|
|
14
|
+
href: url.href,
|
|
15
|
+
hostname: url.hostname,
|
|
16
|
+
pathname: url.pathname,
|
|
17
|
+
search: url.search,
|
|
18
|
+
ip_address: ipAddress,
|
|
19
|
+
...(paymentResponse ? { payment_response: paymentResponse } : {}),
|
|
20
|
+
};
|
|
21
|
+
const requestPromise = fetch(`${API_BASE_URL}/events`, {
|
|
22
|
+
method: "POST",
|
|
23
|
+
headers: {
|
|
24
|
+
Authorization: `Bearer ${c.env.FOLDSET_API_KEY}`,
|
|
25
|
+
"Content-Type": "application/json",
|
|
26
|
+
Accept: "application/json",
|
|
27
|
+
},
|
|
28
|
+
body: JSON.stringify(payload),
|
|
29
|
+
}).catch((error) => {
|
|
30
|
+
Sentry.captureException(error, {
|
|
31
|
+
extra: {
|
|
32
|
+
method: "POST",
|
|
33
|
+
url: `${API_BASE_URL}/events`,
|
|
34
|
+
headers: {
|
|
35
|
+
Authorization: `Bearer ${c.env.FOLDSET_API_KEY}`,
|
|
36
|
+
"Content-Type": "application/json",
|
|
37
|
+
Accept: "application/json",
|
|
38
|
+
},
|
|
39
|
+
body: JSON.stringify(payload),
|
|
40
|
+
},
|
|
41
|
+
tags: {
|
|
42
|
+
endpoint: "/events",
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
c.executionCtx.waitUntil(requestPromise);
|
|
47
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ExecutionContext } from "@cloudflare/workers-types";
|
|
2
|
+
type SentryWrapperArgs = {
|
|
3
|
+
request: Request;
|
|
4
|
+
context: ExecutionContext;
|
|
5
|
+
};
|
|
6
|
+
export declare const wrappedPaymentHandler: (wrapperArgs: SentryWrapperArgs, handler: () => Promise<Response>) => Promise<Response>;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=sentry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../../src/telemetry/sentry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAIlE,KAAK,iBAAiB,GAAG;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,gBAAgB,CAAC;CAC3B,CAAC;AAuBF,eAAO,MAAM,qBAAqB,gBAnBX,iBAAiB,WAAW,MAAM,OAAO,CAAC,QAAQ,CAAC,sBAmBE,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as Sentry from "@sentry/cloudflare";
|
|
2
|
+
const SENTRY_DSN = "https://f68380669974dab9bbcf5aec9414bcb8@o4510648631296000.ingest.us.sentry.io/4510718170038272";
|
|
3
|
+
// TODO rfradkin: Add webhook support for updating this
|
|
4
|
+
function createWrappedPaymentHandler(dsn) {
|
|
5
|
+
return (wrapperArgs, handler) => {
|
|
6
|
+
if (!dsn) {
|
|
7
|
+
return handler();
|
|
8
|
+
}
|
|
9
|
+
return Sentry.wrapRequestHandler({
|
|
10
|
+
options: {
|
|
11
|
+
dsn,
|
|
12
|
+
sendDefaultPii: true,
|
|
13
|
+
},
|
|
14
|
+
request: wrapperArgs.request,
|
|
15
|
+
context: wrapperArgs.context,
|
|
16
|
+
}, handler);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export const wrappedPaymentHandler = createWrappedPaymentHandler(SENTRY_DSN);
|
package/dist/types.d.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
pathname: Pathname;
|
|
6
|
-
};
|
|
7
|
-
export type Restrictions = Record<Pathname, Price>;
|
|
8
|
-
export type AddressResponse = {
|
|
9
|
-
address: string;
|
|
1
|
+
import type { KVNamespace } from "@cloudflare/workers-types";
|
|
2
|
+
export type Env = {
|
|
3
|
+
FOLDSET_API_KEY: string;
|
|
4
|
+
FOLDSET_CONFIG: KVNamespace;
|
|
10
5
|
};
|
|
6
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,MAAM,MAAM,GAAG,GAAG;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,WAAW,CAAC;CAC7B,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
import type { Env } from "../types";
|
|
3
|
+
import { type Restriction } from "../restrictions";
|
|
4
|
+
import { type PaymentMethod } from "../payment-methods";
|
|
5
|
+
import { type AiCrawler } from "../ai-crawlers";
|
|
6
|
+
import { type FacilitatorConfig } from "../facilitators";
|
|
7
|
+
export type FoldsetWebhook = {
|
|
8
|
+
event_type: "restrictions";
|
|
9
|
+
event_object: Restriction[];
|
|
10
|
+
} | {
|
|
11
|
+
event_type: "payment-methods";
|
|
12
|
+
event_object: PaymentMethod[];
|
|
13
|
+
} | {
|
|
14
|
+
event_type: "ai-crawlers";
|
|
15
|
+
event_object: AiCrawler[];
|
|
16
|
+
} | {
|
|
17
|
+
event_type: "facilitator";
|
|
18
|
+
event_object: FacilitatorConfig;
|
|
19
|
+
};
|
|
20
|
+
export declare function handleWebhook(c: Context<{
|
|
21
|
+
Bindings: Env;
|
|
22
|
+
}>): Promise<Response>;
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webhooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,KAAK,aAAa,EAAuB,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,KAAK,SAAS,EAAmB,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,KAAK,iBAAiB,EAAoB,MAAM,iBAAiB,CAAC;AAE3E,MAAM,MAAM,cAAc,GACtB;IAAE,UAAU,EAAE,cAAc,CAAC;IAAC,YAAY,EAAE,WAAW,EAAE,CAAA;CAAE,GAC3D;IAAE,UAAU,EAAE,iBAAiB,CAAC;IAAC,YAAY,EAAE,aAAa,EAAE,CAAA;CAAE,GAChE;IAAE,UAAU,EAAE,aAAa,CAAC;IAAC,YAAY,EAAE,SAAS,EAAE,CAAA;CAAE,GACxD;IAAE,UAAU,EAAE,aAAa,CAAC;IAAC,YAAY,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAgCnE,wBAAsB,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAA;CAAE,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CA0BpF"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { storeRestrictions } from "../restrictions";
|
|
2
|
+
import { storePaymentMethods } from "../payment-methods";
|
|
3
|
+
import { storeAiCrawlers } from "../ai-crawlers";
|
|
4
|
+
import { storeFacilitator } from "../facilitators";
|
|
5
|
+
async function verifySignature(body, signature, apiKey) {
|
|
6
|
+
const encoder = new TextEncoder();
|
|
7
|
+
const keyHashBuffer = await crypto.subtle.digest("SHA-256", encoder.encode(apiKey));
|
|
8
|
+
const hashedKeyHex = Array.from(new Uint8Array(keyHashBuffer))
|
|
9
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
10
|
+
.join("");
|
|
11
|
+
const hmacKey = await crypto.subtle.importKey("raw", encoder.encode(hashedKeyHex), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
12
|
+
const expectedSig = await crypto.subtle.sign("HMAC", hmacKey, encoder.encode(body));
|
|
13
|
+
const expectedHex = Array.from(new Uint8Array(expectedSig))
|
|
14
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
15
|
+
.join("");
|
|
16
|
+
// Timing-safe comparison
|
|
17
|
+
if (signature.length !== expectedHex.length)
|
|
18
|
+
return false;
|
|
19
|
+
let result = 0;
|
|
20
|
+
for (let i = 0; i < signature.length; i++) {
|
|
21
|
+
result |= signature.charCodeAt(i) ^ expectedHex.charCodeAt(i);
|
|
22
|
+
}
|
|
23
|
+
return result === 0;
|
|
24
|
+
}
|
|
25
|
+
export async function handleWebhook(c) {
|
|
26
|
+
const signature = c.req.header("X-Foldset-Signature");
|
|
27
|
+
if (!signature) {
|
|
28
|
+
return new Response("Missing signature", { status: 401 });
|
|
29
|
+
}
|
|
30
|
+
const body = await c.req.text();
|
|
31
|
+
const isValid = await verifySignature(body, signature, c.env.FOLDSET_API_KEY);
|
|
32
|
+
if (!isValid) {
|
|
33
|
+
return new Response("Invalid signature", { status: 401 });
|
|
34
|
+
}
|
|
35
|
+
const webhook = JSON.parse(body);
|
|
36
|
+
// Fails the entire webhook if the put fails. Not standard but think it makes sense for now
|
|
37
|
+
if (webhook.event_type === "restrictions") {
|
|
38
|
+
await storeRestrictions(c, webhook.event_object);
|
|
39
|
+
}
|
|
40
|
+
else if (webhook.event_type === "payment-methods") {
|
|
41
|
+
await storePaymentMethods(c, webhook.event_object);
|
|
42
|
+
}
|
|
43
|
+
else if (webhook.event_type === "ai-crawlers") {
|
|
44
|
+
await storeAiCrawlers(c, webhook.event_object);
|
|
45
|
+
}
|
|
46
|
+
else if (webhook.event_type === "facilitator") {
|
|
47
|
+
await storeFacilitator(c, webhook.event_object);
|
|
48
|
+
}
|
|
49
|
+
return new Response("Ok", { status: 200 });
|
|
50
|
+
}
|
package/package.json
CHANGED
|
@@ -1,34 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foldset/cloudflare",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"type": "module",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"private": false,
|
|
6
5
|
"main": "./dist/index.js",
|
|
7
6
|
"types": "./dist/index.d.ts",
|
|
8
7
|
"exports": {
|
|
9
8
|
".": {
|
|
10
9
|
"types": "./dist/index.d.ts",
|
|
11
|
-
"
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./hono": {
|
|
13
|
+
"types": "./dist/hono.d.ts",
|
|
14
|
+
"default": "./dist/hono.js"
|
|
12
15
|
}
|
|
13
16
|
},
|
|
14
17
|
"files": [
|
|
15
18
|
"dist"
|
|
16
19
|
],
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
"x402"
|
|
22
|
-
],
|
|
23
|
-
"license": "MIT",
|
|
20
|
+
"scripts": {
|
|
21
|
+
"typegen": "wrangler types --path types/worker-configuration.d.ts",
|
|
22
|
+
"build": "tsc -p tsconfig.json --noEmit false --outDir dist --rootDir src"
|
|
23
|
+
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@
|
|
26
|
-
"@
|
|
27
|
-
"@x402/
|
|
28
|
-
"
|
|
29
|
-
"
|
|
25
|
+
"@cloudflare/workers-types": "^4.20260117.0",
|
|
26
|
+
"@sentry/cloudflare": "^10.34.0",
|
|
27
|
+
"@x402/core": "^2.2.0",
|
|
28
|
+
"@x402/evm": "^2.2.0",
|
|
29
|
+
"@x402/hono": "^2.2.0",
|
|
30
|
+
"@x402/svm": "^2.2.0",
|
|
31
|
+
"hono": "^4.11.4"
|
|
30
32
|
},
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"typescript": "^5.9.3",
|
|
35
|
+
"wrangler": "^4.59.1"
|
|
33
36
|
}
|
|
34
37
|
}
|
package/README.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# Typescript library for foldset?
|
package/dist/adapter.d.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { HTTPAdapter } from "@x402/core/server";
|
|
2
|
-
import { Context } from "hono";
|
|
3
|
-
/**
|
|
4
|
-
* Hono adapter implementation
|
|
5
|
-
*/
|
|
6
|
-
export declare class HonoAdapter implements HTTPAdapter {
|
|
7
|
-
private c;
|
|
8
|
-
/**
|
|
9
|
-
* Creates a new HonoAdapter instance.
|
|
10
|
-
*
|
|
11
|
-
* @param c - The Hono context object
|
|
12
|
-
*/
|
|
13
|
-
constructor(c: Context);
|
|
14
|
-
/**
|
|
15
|
-
* Gets a header value from the request.
|
|
16
|
-
*
|
|
17
|
-
* @param name - The header name
|
|
18
|
-
* @returns The header value or undefined
|
|
19
|
-
*/
|
|
20
|
-
getHeader(name: string): string | undefined;
|
|
21
|
-
/**
|
|
22
|
-
* Gets the HTTP method of the request.
|
|
23
|
-
*
|
|
24
|
-
* @returns The HTTP method
|
|
25
|
-
*/
|
|
26
|
-
getMethod(): string;
|
|
27
|
-
/**
|
|
28
|
-
* Gets the path of the request.
|
|
29
|
-
*
|
|
30
|
-
* @returns The request path
|
|
31
|
-
*/
|
|
32
|
-
getPath(): string;
|
|
33
|
-
/**
|
|
34
|
-
* Gets the full URL of the request.
|
|
35
|
-
*
|
|
36
|
-
* @returns The full request URL
|
|
37
|
-
*/
|
|
38
|
-
getUrl(): string;
|
|
39
|
-
/**
|
|
40
|
-
* Gets the Accept header from the request.
|
|
41
|
-
*
|
|
42
|
-
* @returns The Accept header value or empty string
|
|
43
|
-
*/
|
|
44
|
-
getAcceptHeader(): string;
|
|
45
|
-
/**
|
|
46
|
-
* Gets the User-Agent header from the request.
|
|
47
|
-
*
|
|
48
|
-
* @returns The User-Agent header value or empty string
|
|
49
|
-
*/
|
|
50
|
-
getUserAgent(): string;
|
|
51
|
-
/**
|
|
52
|
-
* Gets all query parameters from the request URL.
|
|
53
|
-
*
|
|
54
|
-
* @returns Record of query parameter key-value pairs
|
|
55
|
-
*/
|
|
56
|
-
getQueryParams(): Record<string, string | string[]>;
|
|
57
|
-
/**
|
|
58
|
-
* Gets a specific query parameter by name.
|
|
59
|
-
*
|
|
60
|
-
* @param name - The query parameter name
|
|
61
|
-
* @returns The query parameter value(s) or undefined
|
|
62
|
-
*/
|
|
63
|
-
getQueryParam(name: string): string | string[] | undefined;
|
|
64
|
-
/**
|
|
65
|
-
* Gets the parsed request body.
|
|
66
|
-
* Requires appropriate body parsing middleware.
|
|
67
|
-
*
|
|
68
|
-
* @returns The parsed request body
|
|
69
|
-
*/
|
|
70
|
-
getBody(): Promise<unknown>;
|
|
71
|
-
}
|
package/dist/adapter.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hono adapter implementation
|
|
3
|
-
*/
|
|
4
|
-
export class HonoAdapter {
|
|
5
|
-
/**
|
|
6
|
-
* Creates a new HonoAdapter instance.
|
|
7
|
-
*
|
|
8
|
-
* @param c - The Hono context object
|
|
9
|
-
*/
|
|
10
|
-
constructor(c) {
|
|
11
|
-
this.c = c;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Gets a header value from the request.
|
|
15
|
-
*
|
|
16
|
-
* @param name - The header name
|
|
17
|
-
* @returns The header value or undefined
|
|
18
|
-
*/
|
|
19
|
-
getHeader(name) {
|
|
20
|
-
return this.c.req.header(name);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Gets the HTTP method of the request.
|
|
24
|
-
*
|
|
25
|
-
* @returns The HTTP method
|
|
26
|
-
*/
|
|
27
|
-
getMethod() {
|
|
28
|
-
return this.c.req.method;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Gets the path of the request.
|
|
32
|
-
*
|
|
33
|
-
* @returns The request path
|
|
34
|
-
*/
|
|
35
|
-
getPath() {
|
|
36
|
-
return this.c.req.path;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Gets the full URL of the request.
|
|
40
|
-
*
|
|
41
|
-
* @returns The full request URL
|
|
42
|
-
*/
|
|
43
|
-
getUrl() {
|
|
44
|
-
return this.c.req.url;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Gets the Accept header from the request.
|
|
48
|
-
*
|
|
49
|
-
* @returns The Accept header value or empty string
|
|
50
|
-
*/
|
|
51
|
-
getAcceptHeader() {
|
|
52
|
-
return this.c.req.header("Accept") || "";
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Gets the User-Agent header from the request.
|
|
56
|
-
*
|
|
57
|
-
* @returns The User-Agent header value or empty string
|
|
58
|
-
*/
|
|
59
|
-
getUserAgent() {
|
|
60
|
-
return this.c.req.header("User-Agent") || "";
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Gets all query parameters from the request URL.
|
|
64
|
-
*
|
|
65
|
-
* @returns Record of query parameter key-value pairs
|
|
66
|
-
*/
|
|
67
|
-
getQueryParams() {
|
|
68
|
-
const query = this.c.req.query();
|
|
69
|
-
// Convert single values to match the interface
|
|
70
|
-
const result = {};
|
|
71
|
-
for (const [key, value] of Object.entries(query)) {
|
|
72
|
-
result[key] = value;
|
|
73
|
-
}
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Gets a specific query parameter by name.
|
|
78
|
-
*
|
|
79
|
-
* @param name - The query parameter name
|
|
80
|
-
* @returns The query parameter value(s) or undefined
|
|
81
|
-
*/
|
|
82
|
-
getQueryParam(name) {
|
|
83
|
-
return this.c.req.query(name);
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Gets the parsed request body.
|
|
87
|
-
* Requires appropriate body parsing middleware.
|
|
88
|
-
*
|
|
89
|
-
* @returns The parsed request body
|
|
90
|
-
*/
|
|
91
|
-
async getBody() {
|
|
92
|
-
try {
|
|
93
|
-
return await this.c.req.json();
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
return undefined;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|