@alleyboss/micropay-solana-x402-paywall 1.0.1 → 2.0.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.
Files changed (62) hide show
  1. package/README.md +100 -167
  2. package/dist/client/index.cjs +99 -0
  3. package/dist/client/index.cjs.map +1 -0
  4. package/dist/client/index.d.cts +112 -0
  5. package/dist/client/index.d.ts +112 -0
  6. package/dist/client/index.js +95 -0
  7. package/dist/client/index.js.map +1 -0
  8. package/dist/client-CSZHI8o8.d.ts +32 -0
  9. package/dist/client-vRr48m2x.d.cts +32 -0
  10. package/dist/index.cjs +696 -15
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +11 -3
  13. package/dist/index.d.ts +11 -3
  14. package/dist/index.js +674 -16
  15. package/dist/index.js.map +1 -1
  16. package/dist/memory-Daxkczti.d.cts +29 -0
  17. package/dist/memory-Daxkczti.d.ts +29 -0
  18. package/dist/middleware/index.cjs +261 -0
  19. package/dist/middleware/index.cjs.map +1 -0
  20. package/dist/middleware/index.d.cts +90 -0
  21. package/dist/middleware/index.d.ts +90 -0
  22. package/dist/middleware/index.js +255 -0
  23. package/dist/middleware/index.js.map +1 -0
  24. package/dist/nextjs-BK0pVb9Y.d.ts +78 -0
  25. package/dist/nextjs-Bm272Jkj.d.cts +78 -0
  26. package/dist/{client-kfCr7G-P.d.cts → payment-CTxdtqmc.d.cts} +23 -34
  27. package/dist/{client-kfCr7G-P.d.ts → payment-CTxdtqmc.d.ts} +23 -34
  28. package/dist/pricing/index.cjs +142 -0
  29. package/dist/pricing/index.cjs.map +1 -0
  30. package/dist/pricing/index.d.cts +111 -0
  31. package/dist/pricing/index.d.ts +111 -0
  32. package/dist/pricing/index.js +133 -0
  33. package/dist/pricing/index.js.map +1 -0
  34. package/dist/session/index.d.cts +29 -1
  35. package/dist/session/index.d.ts +29 -1
  36. package/dist/{index-uxMb72hH.d.cts → session-D2IoWAWV.d.cts} +1 -27
  37. package/dist/{index-uxMb72hH.d.ts → session-D2IoWAWV.d.ts} +1 -27
  38. package/dist/solana/index.cjs +193 -0
  39. package/dist/solana/index.cjs.map +1 -1
  40. package/dist/solana/index.d.cts +60 -3
  41. package/dist/solana/index.d.ts +60 -3
  42. package/dist/solana/index.js +190 -1
  43. package/dist/solana/index.js.map +1 -1
  44. package/dist/store/index.cjs +99 -0
  45. package/dist/store/index.cjs.map +1 -0
  46. package/dist/store/index.d.cts +38 -0
  47. package/dist/store/index.d.ts +38 -0
  48. package/dist/store/index.js +96 -0
  49. package/dist/store/index.js.map +1 -0
  50. package/dist/utils/index.cjs +68 -0
  51. package/dist/utils/index.cjs.map +1 -0
  52. package/dist/utils/index.d.cts +30 -0
  53. package/dist/utils/index.d.ts +30 -0
  54. package/dist/utils/index.js +65 -0
  55. package/dist/utils/index.js.map +1 -0
  56. package/dist/x402/index.cjs +2 -1
  57. package/dist/x402/index.cjs.map +1 -1
  58. package/dist/x402/index.d.cts +2 -1
  59. package/dist/x402/index.d.ts +2 -1
  60. package/dist/x402/index.js +2 -1
  61. package/dist/x402/index.js.map +1 -1
  62. package/package.json +56 -3
@@ -0,0 +1,261 @@
1
+ 'use strict';
2
+
3
+ var jose = require('jose');
4
+ require('uuid');
5
+
6
+ // src/session/core.ts
7
+ var MIN_SECRET_LENGTH = 32;
8
+ function getSecretKey(secret) {
9
+ if (!secret || typeof secret !== "string") {
10
+ throw new Error("Session secret is required");
11
+ }
12
+ if (secret.length < MIN_SECRET_LENGTH) {
13
+ throw new Error(`Session secret must be at least ${MIN_SECRET_LENGTH} characters`);
14
+ }
15
+ return new TextEncoder().encode(secret);
16
+ }
17
+ function validateWalletAddress(address) {
18
+ if (!address || typeof address !== "string") return false;
19
+ const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
20
+ return base58Regex.test(address);
21
+ }
22
+ async function validateSession(token, secret) {
23
+ if (!token || typeof token !== "string") {
24
+ return { valid: false, reason: "Invalid token format" };
25
+ }
26
+ try {
27
+ const { payload } = await jose.jwtVerify(token, getSecretKey(secret));
28
+ const sessionPayload = payload;
29
+ if (!sessionPayload.sub || !sessionPayload.sid || !sessionPayload.exp) {
30
+ return { valid: false, reason: "Malformed session payload" };
31
+ }
32
+ const now = Math.floor(Date.now() / 1e3);
33
+ if (sessionPayload.exp < now) {
34
+ return { valid: false, reason: "Session expired" };
35
+ }
36
+ if (!validateWalletAddress(sessionPayload.sub)) {
37
+ return { valid: false, reason: "Invalid session data" };
38
+ }
39
+ const session = {
40
+ id: sessionPayload.sid,
41
+ walletAddress: sessionPayload.sub,
42
+ unlockedArticles: Array.isArray(sessionPayload.articles) ? sessionPayload.articles : [],
43
+ siteWideUnlock: Boolean(sessionPayload.siteWide),
44
+ createdAt: sessionPayload.iat ?? 0,
45
+ expiresAt: sessionPayload.exp
46
+ };
47
+ return { valid: true, session };
48
+ } catch (error) {
49
+ return { valid: false, reason: "Invalid session" };
50
+ }
51
+ }
52
+
53
+ // src/middleware/nextjs.ts
54
+ function matchesProtectedPath(path, patterns) {
55
+ for (const pattern of patterns) {
56
+ const regexPattern = pattern.replace(/\*\*/g, "{{DOUBLE_STAR}}").replace(/\*/g, "[^/]*").replace(/{{DOUBLE_STAR}}/g, ".*");
57
+ const regex = new RegExp(`^${regexPattern}$`);
58
+ if (regex.test(path)) {
59
+ return true;
60
+ }
61
+ }
62
+ return false;
63
+ }
64
+ async function checkPaywallAccess(path, sessionToken, config) {
65
+ if (!matchesProtectedPath(path, config.protectedPaths)) {
66
+ return { allowed: true };
67
+ }
68
+ if (!sessionToken) {
69
+ return {
70
+ allowed: false,
71
+ reason: "No session token",
72
+ requiresPayment: true
73
+ };
74
+ }
75
+ const validation = await validateSession(sessionToken, config.sessionSecret);
76
+ if (!validation.valid || !validation.session) {
77
+ return {
78
+ allowed: false,
79
+ reason: validation.reason || "Invalid session",
80
+ requiresPayment: true
81
+ };
82
+ }
83
+ return {
84
+ allowed: true,
85
+ session: validation.session
86
+ };
87
+ }
88
+ function createPaywallMiddleware(config) {
89
+ const { cookieName = "x402_session" } = config;
90
+ return async function middleware(request) {
91
+ const url = new URL(request.url);
92
+ const path = url.pathname;
93
+ const cookieHeader = request.headers.get("cookie") || "";
94
+ const cookies = Object.fromEntries(
95
+ cookieHeader.split(";").map((c) => {
96
+ const [key, ...vals] = c.trim().split("=");
97
+ return [key, vals.join("=")];
98
+ })
99
+ );
100
+ const sessionToken = cookies[cookieName];
101
+ const result = await checkPaywallAccess(path, sessionToken, config);
102
+ if (!result.allowed && result.requiresPayment) {
103
+ const body = config.custom402Response ? config.custom402Response(path) : {
104
+ error: "Payment Required",
105
+ message: "This resource requires payment to access",
106
+ path
107
+ };
108
+ return new Response(JSON.stringify(body), {
109
+ status: 402,
110
+ headers: {
111
+ "Content-Type": "application/json"
112
+ }
113
+ });
114
+ }
115
+ return null;
116
+ };
117
+ }
118
+ function withPaywall(handler, options) {
119
+ const { sessionSecret, cookieName = "x402_session", articleId } = options;
120
+ return async function protectedHandler(request) {
121
+ const cookieHeader = request.headers.get("cookie") || "";
122
+ const cookies = Object.fromEntries(
123
+ cookieHeader.split(";").map((c) => {
124
+ const [key, ...vals] = c.trim().split("=");
125
+ return [key, vals.join("=")];
126
+ })
127
+ );
128
+ const sessionToken = cookies[cookieName];
129
+ if (!sessionToken) {
130
+ return new Response(
131
+ JSON.stringify({ error: "Payment Required", message: "No session token" }),
132
+ { status: 402, headers: { "Content-Type": "application/json" } }
133
+ );
134
+ }
135
+ const validation = await validateSession(sessionToken, sessionSecret);
136
+ if (!validation.valid || !validation.session) {
137
+ return new Response(
138
+ JSON.stringify({ error: "Payment Required", message: validation.reason }),
139
+ { status: 402, headers: { "Content-Type": "application/json" } }
140
+ );
141
+ }
142
+ if (articleId) {
143
+ const { session } = validation;
144
+ const hasAccess = session.siteWideUnlock || session.unlockedArticles.includes(articleId);
145
+ if (!hasAccess) {
146
+ return new Response(
147
+ JSON.stringify({ error: "Payment Required", message: "Article not unlocked" }),
148
+ { status: 402, headers: { "Content-Type": "application/json" } }
149
+ );
150
+ }
151
+ }
152
+ return handler(request, validation.session);
153
+ };
154
+ }
155
+
156
+ // src/middleware/express.ts
157
+ function matchPath(path, patterns) {
158
+ for (const pattern of patterns) {
159
+ const regex = pattern.replace(/\*\*/g, "<<<GLOBSTAR>>>").replace(/\*/g, "[^/]*").replace(/<<<GLOBSTAR>>>/g, ".*");
160
+ if (new RegExp(`^${regex}$`).test(path)) {
161
+ return true;
162
+ }
163
+ }
164
+ return false;
165
+ }
166
+ function hasGetMethod(headers) {
167
+ return typeof headers.get === "function";
168
+ }
169
+ function getHeader(headers, name) {
170
+ if (hasGetMethod(headers)) {
171
+ return headers.get(name) ?? void 0;
172
+ }
173
+ const value = headers[name];
174
+ return typeof value === "string" ? value : void 0;
175
+ }
176
+ function extractToken(req, config) {
177
+ const { cookieName = "x402_session", headerName } = config;
178
+ if (headerName) {
179
+ const token = getHeader(req.headers, headerName.toLowerCase());
180
+ if (token) return token;
181
+ }
182
+ const authHeader = getHeader(req.headers, "authorization");
183
+ if (authHeader?.startsWith("Bearer ")) {
184
+ return authHeader.slice(7);
185
+ }
186
+ if (req.cookies?.[cookieName]) {
187
+ return req.cookies[cookieName];
188
+ }
189
+ const cookieHeader = getHeader(req.headers, "cookie");
190
+ if (cookieHeader) {
191
+ const match = cookieHeader.split(";").map((c) => c.trim()).find((c) => c.startsWith(`${cookieName}=`));
192
+ if (match) {
193
+ return match.slice(cookieName.length + 1);
194
+ }
195
+ }
196
+ return void 0;
197
+ }
198
+ function createExpressMiddleware(config) {
199
+ const { sessionSecret, protectedPaths, onPaymentRequired, onAccessGranted } = config;
200
+ return async function paywallMiddleware(req, res, next) {
201
+ const path = req.path || req.url?.split("?")[0] || "/";
202
+ if (!matchPath(path, protectedPaths)) {
203
+ next();
204
+ return;
205
+ }
206
+ const token = extractToken(req, config);
207
+ if (!token) {
208
+ if (onPaymentRequired) {
209
+ onPaymentRequired(req, res);
210
+ } else {
211
+ sendPaymentRequired(res, "No session token");
212
+ }
213
+ return;
214
+ }
215
+ const validation = await validateSession(token, sessionSecret);
216
+ if (!validation.valid || !validation.session) {
217
+ if (onPaymentRequired) {
218
+ onPaymentRequired(req, res);
219
+ } else {
220
+ sendPaymentRequired(res, validation.reason || "Invalid session");
221
+ }
222
+ return;
223
+ }
224
+ req.session = validation.session;
225
+ if (onAccessGranted) {
226
+ onAccessGranted(req, validation.session);
227
+ }
228
+ next();
229
+ };
230
+ }
231
+ function sendPaymentRequired(res, reason) {
232
+ const body = JSON.stringify({
233
+ error: "Payment Required",
234
+ message: reason
235
+ });
236
+ const statusFn = res.status;
237
+ const jsonFn = res.json;
238
+ if (statusFn && jsonFn) {
239
+ const chainedRes = statusFn.call(res, 402);
240
+ if (chainedRes?.json) {
241
+ chainedRes.json({ error: "Payment Required", message: reason });
242
+ }
243
+ } else if (res.statusCode !== void 0 && res.setHeader && res.end) {
244
+ res.statusCode = 402;
245
+ res.setHeader("Content-Type", "application/json");
246
+ res.end(body);
247
+ }
248
+ }
249
+ function createFastifyPlugin(config) {
250
+ return async function paywallPlugin(fastify) {
251
+ fastify.addHook("preHandler", createExpressMiddleware(config));
252
+ };
253
+ }
254
+
255
+ exports.checkPaywallAccess = checkPaywallAccess;
256
+ exports.createExpressMiddleware = createExpressMiddleware;
257
+ exports.createFastifyPlugin = createFastifyPlugin;
258
+ exports.createPaywallMiddleware = createPaywallMiddleware;
259
+ exports.withPaywall = withPaywall;
260
+ //# sourceMappingURL=index.cjs.map
261
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/session/core.ts","../../src/middleware/nextjs.ts","../../src/middleware/express.ts"],"names":["jwtVerify"],"mappings":"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","file":"index.cjs","sourcesContent":["// Session management with JWT (framework-agnostic core)\n// SECURITY: Uses jose library with HS256, constant-time validation, input sanitization\nimport { SignJWT, jwtVerify } from 'jose';\nimport { v4 as uuidv4 } from 'uuid';\nimport type { SessionData, SessionConfig, SessionValidation, SessionJWTPayload } from '../types';\n\n// Maximum articles per session to prevent unbounded growth\nconst MAX_ARTICLES_PER_SESSION = 100;\n\n// Minimum secret length for security\nconst MIN_SECRET_LENGTH = 32;\n\n/**\n * Get the secret key for JWT signing\n * SECURITY: Enforces minimum secret length\n */\nfunction getSecretKey(secret: string): Uint8Array {\n if (!secret || typeof secret !== 'string') {\n throw new Error('Session secret is required');\n }\n if (secret.length < MIN_SECRET_LENGTH) {\n throw new Error(`Session secret must be at least ${MIN_SECRET_LENGTH} characters`);\n }\n return new TextEncoder().encode(secret);\n}\n\n/**\n * Validate wallet address format (base58, 32-44 chars)\n * SECURITY: Prevents injection via wallet address field\n */\nfunction validateWalletAddress(address: string): boolean {\n if (!address || typeof address !== 'string') return false;\n // Solana addresses are base58, typically 32-44 characters\n const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;\n return base58Regex.test(address);\n}\n\n/**\n * Validate article ID format\n * SECURITY: Prevents injection via articleId field\n */\nfunction validateArticleId(articleId: string): boolean {\n if (!articleId || typeof articleId !== 'string') return false;\n // Allow alphanumeric, hyphens, underscores, max 128 chars\n if (articleId.length > 128) return false;\n const safeIdRegex = /^[a-zA-Z0-9_-]+$/;\n return safeIdRegex.test(articleId);\n}\n\n/**\n * Create a new session after successful payment\n * SECURITY: Validates inputs, enforces limits\n */\nexport async function createSession(\n walletAddress: string,\n articleId: string,\n config: SessionConfig,\n siteWide: boolean = false\n): Promise<{ token: string; session: SessionData }> {\n // Input validation\n if (!validateWalletAddress(walletAddress)) {\n throw new Error('Invalid wallet address format');\n }\n if (!validateArticleId(articleId)) {\n throw new Error('Invalid article ID format');\n }\n if (!config.durationHours || config.durationHours <= 0 || config.durationHours > 720) {\n throw new Error('Session duration must be between 1 and 720 hours');\n }\n\n const sessionId = uuidv4();\n const now = Math.floor(Date.now() / 1000);\n const expiresAt = now + (config.durationHours * 3600);\n\n const session: SessionData = {\n id: sessionId,\n walletAddress,\n unlockedArticles: [articleId],\n siteWideUnlock: Boolean(siteWide),\n createdAt: now,\n expiresAt,\n };\n\n const payload: SessionJWTPayload = {\n sub: walletAddress,\n sid: sessionId,\n articles: session.unlockedArticles,\n siteWide: session.siteWideUnlock,\n iat: now,\n exp: expiresAt,\n };\n\n const token = await new SignJWT(payload as unknown as Record<string, unknown>)\n .setProtectedHeader({ alg: 'HS256' })\n .setIssuedAt()\n .setExpirationTime(`${config.durationHours}h`)\n .sign(getSecretKey(config.secret));\n\n return { token, session };\n}\n\n/**\n * Validate an existing session token\n * SECURITY: jose library handles timing-safe comparison internally\n */\nexport async function validateSession(\n token: string,\n secret: string\n): Promise<SessionValidation> {\n // Input validation\n if (!token || typeof token !== 'string') {\n return { valid: false, reason: 'Invalid token format' };\n }\n\n try {\n const { payload } = await jwtVerify(token, getSecretKey(secret));\n const sessionPayload = payload as unknown as SessionJWTPayload;\n\n // Validate required fields exist\n if (!sessionPayload.sub || !sessionPayload.sid || !sessionPayload.exp) {\n return { valid: false, reason: 'Malformed session payload' };\n }\n\n // Check expiration (jose already checks, but double-check)\n const now = Math.floor(Date.now() / 1000);\n if (sessionPayload.exp < now) {\n return { valid: false, reason: 'Session expired' };\n }\n\n // Validate wallet address format from token\n if (!validateWalletAddress(sessionPayload.sub)) {\n return { valid: false, reason: 'Invalid session data' };\n }\n\n const session: SessionData = {\n id: sessionPayload.sid,\n walletAddress: sessionPayload.sub,\n unlockedArticles: Array.isArray(sessionPayload.articles) ? sessionPayload.articles : [],\n siteWideUnlock: Boolean(sessionPayload.siteWide),\n createdAt: sessionPayload.iat ?? 0,\n expiresAt: sessionPayload.exp,\n };\n\n return { valid: true, session };\n } catch (error) {\n // SECURITY: Don't expose internal error details\n return { valid: false, reason: 'Invalid session' };\n }\n}\n\n/**\n * Add an article to an existing session\n * SECURITY: Enforces article limit to prevent token bloat\n */\nexport async function addArticleToSession(\n token: string,\n articleId: string,\n secret: string\n): Promise<{ token: string; session: SessionData } | null> {\n // Validate article ID\n if (!validateArticleId(articleId)) {\n return null;\n }\n\n const validation = await validateSession(token, secret);\n if (!validation.valid || !validation.session) {\n return null;\n }\n\n const session = validation.session;\n\n // Already unlocked\n if (session.unlockedArticles.includes(articleId)) {\n return { token, session };\n }\n\n // SECURITY: Enforce maximum articles per session\n if (session.unlockedArticles.length >= MAX_ARTICLES_PER_SESSION) {\n return null;\n }\n\n const updatedArticles = [...session.unlockedArticles, articleId];\n\n const payload: SessionJWTPayload = {\n sub: session.walletAddress,\n sid: session.id,\n articles: updatedArticles,\n siteWide: session.siteWideUnlock,\n iat: session.createdAt,\n exp: session.expiresAt,\n };\n\n const newToken = await new SignJWT(payload as unknown as Record<string, unknown>)\n .setProtectedHeader({ alg: 'HS256' })\n .sign(getSecretKey(secret));\n\n return {\n token: newToken,\n session: { ...session, unlockedArticles: updatedArticles },\n };\n}\n\n/**\n * Check if an article is unlocked for a session\n */\nexport async function isArticleUnlocked(\n token: string,\n articleId: string,\n secret: string\n): Promise<boolean> {\n if (!validateArticleId(articleId)) {\n return false;\n }\n\n const validation = await validateSession(token, secret);\n if (!validation.valid || !validation.session) {\n return false;\n }\n\n if (validation.session.siteWideUnlock) {\n return true;\n }\n\n return validation.session.unlockedArticles.includes(articleId);\n}\n","// Next.js Middleware for Paywall\n// Zero-boilerplate integration for protected routes\n\nimport type { SignatureStore } from '../store';\nimport type { SessionData } from '../types';\nimport { validateSession } from '../session';\n\n/**\n * Configuration for paywall middleware\n */\nexport interface PaywallMiddlewareConfig {\n /** Session secret for JWT validation */\n sessionSecret: string;\n /** Protected path patterns (glob-like) */\n protectedPaths: string[];\n /** Cookie name for session token */\n cookieName?: string;\n /** Optional signature store for anti-replay */\n signatureStore?: SignatureStore;\n /** Custom 402 response body */\n custom402Response?: (path: string) => object;\n}\n\n/**\n * Result of middleware check\n */\nexport interface MiddlewareResult {\n /** Whether access is allowed */\n allowed: boolean;\n /** Session data if valid */\n session?: SessionData;\n /** Reason for denial */\n reason?: string;\n /** Should respond with 402 */\n requiresPayment?: boolean;\n}\n\n/**\n * Check if path matches any protected pattern\n */\nfunction matchesProtectedPath(path: string, patterns: string[]): boolean {\n for (const pattern of patterns) {\n // Simple glob matching: * matches anything, ** matches path segments\n const regexPattern = pattern\n .replace(/\\*\\*/g, '{{DOUBLE_STAR}}')\n .replace(/\\*/g, '[^/]*')\n .replace(/{{DOUBLE_STAR}}/g, '.*');\n\n const regex = new RegExp(`^${regexPattern}$`);\n if (regex.test(path)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check paywall access for a request\n * Framework-agnostic core logic\n */\nexport async function checkPaywallAccess(\n path: string,\n sessionToken: string | undefined,\n config: PaywallMiddlewareConfig\n): Promise<MiddlewareResult> {\n // Check if path is protected\n if (!matchesProtectedPath(path, config.protectedPaths)) {\n return { allowed: true };\n }\n\n // No session token\n if (!sessionToken) {\n return {\n allowed: false,\n reason: 'No session token',\n requiresPayment: true,\n };\n }\n\n // Validate session\n const validation = await validateSession(sessionToken, config.sessionSecret);\n\n if (!validation.valid || !validation.session) {\n return {\n allowed: false,\n reason: validation.reason || 'Invalid session',\n requiresPayment: true,\n };\n }\n\n return {\n allowed: true,\n session: validation.session,\n };\n}\n\n/**\n * Create Next.js middleware handler\n * \n * @example\n * ```typescript\n * // middleware.ts\n * import { createPaywallMiddleware } from '@alleyboss/micropay-solana-x402-paywall/middleware';\n * \n * export const middleware = createPaywallMiddleware({\n * sessionSecret: process.env.SESSION_SECRET!,\n * protectedPaths: ['/api/premium/*', '/api/content/*'],\n * });\n * \n * export const config = { matcher: ['/api/premium/:path*', '/api/content/:path*'] };\n * ```\n */\nexport function createPaywallMiddleware(config: PaywallMiddlewareConfig) {\n const { cookieName = 'x402_session' } = config;\n\n return async function middleware(request: Request): Promise<Response | null> {\n const url = new URL(request.url);\n const path = url.pathname;\n\n // Get session from cookie header\n const cookieHeader = request.headers.get('cookie') || '';\n const cookies = Object.fromEntries(\n cookieHeader.split(';').map(c => {\n const [key, ...vals] = c.trim().split('=');\n return [key, vals.join('=')];\n })\n );\n const sessionToken = cookies[cookieName];\n\n const result = await checkPaywallAccess(path, sessionToken, config);\n\n if (!result.allowed && result.requiresPayment) {\n // Return 402 Payment Required\n const body = config.custom402Response\n ? config.custom402Response(path)\n : {\n error: 'Payment Required',\n message: 'This resource requires payment to access',\n path,\n };\n\n return new Response(JSON.stringify(body), {\n status: 402,\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n }\n\n // Allow request to continue\n return null;\n };\n}\n\n/**\n * API route wrapper for Next.js App Router\n * \n * @example\n * ```typescript\n * // app/api/premium/route.ts\n * import { withPaywall } from '@alleyboss/micropay-solana-x402-paywall/middleware';\n * \n * async function handler(request: Request, session: SessionData) {\n * return Response.json({ content: 'Premium content', wallet: session.walletAddress });\n * }\n * \n * export const GET = withPaywall(handler, {\n * sessionSecret: process.env.SESSION_SECRET!,\n * articleId: 'article-123', // Optional: check specific article unlock\n * });\n * ```\n */\nexport function withPaywall<T>(\n handler: (request: Request, session: SessionData) => Promise<T>,\n options: {\n sessionSecret: string;\n cookieName?: string;\n articleId?: string;\n }\n): (request: Request) => Promise<Response | T> {\n const { sessionSecret, cookieName = 'x402_session', articleId } = options;\n\n return async function protectedHandler(request: Request): Promise<Response | T> {\n // Extract session token from cookie\n const cookieHeader = request.headers.get('cookie') || '';\n const cookies = Object.fromEntries(\n cookieHeader.split(';').map(c => {\n const [key, ...vals] = c.trim().split('=');\n return [key, vals.join('=')];\n })\n );\n const sessionToken = cookies[cookieName];\n\n if (!sessionToken) {\n return new Response(\n JSON.stringify({ error: 'Payment Required', message: 'No session token' }),\n { status: 402, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n // Validate session\n const validation = await validateSession(sessionToken, sessionSecret);\n\n if (!validation.valid || !validation.session) {\n return new Response(\n JSON.stringify({ error: 'Payment Required', message: validation.reason }),\n { status: 402, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n // Check article-specific unlock if required\n if (articleId) {\n const { session } = validation;\n const hasAccess = session.siteWideUnlock || session.unlockedArticles.includes(articleId);\n\n if (!hasAccess) {\n return new Response(\n JSON.stringify({ error: 'Payment Required', message: 'Article not unlocked' }),\n { status: 402, headers: { 'Content-Type': 'application/json' } }\n );\n }\n }\n\n // Call the actual handler with session\n return handler(request, validation.session);\n };\n}\n","// Express/Node.js Middleware\n// Universal middleware compatible with Express, Fastify, Koa adapters\n\nimport type { SignatureStore } from '../store';\nimport type { SessionData } from '../types';\nimport { validateSession } from '../session';\n\n/**\n * Generic HTTP request/response interfaces for framework compatibility\n * Works with Express, Fastify, raw Node.js http, etc.\n */\nexport interface GenericRequest {\n url?: string;\n path?: string;\n headers: Record<string, string | string[] | undefined> | { get(name: string): string | undefined };\n cookies?: Record<string, string>;\n}\n\nexport interface GenericResponse {\n status?(code: number): GenericResponse;\n statusCode?: number;\n json?(body: object): void;\n send?(body: string): void;\n setHeader?(name: string, value: string): void;\n end?(body?: string): void;\n}\n\nexport type NextFunction = (error?: Error) => void;\n\n/**\n * Express-style middleware configuration\n */\nexport interface ExpressPaywallConfig {\n /** Session secret for JWT validation */\n sessionSecret: string;\n /** Protected path patterns (glob-like) */\n protectedPaths: string[];\n /** Cookie name for session token (default: 'x402_session') */\n cookieName?: string;\n /** Optional signature store */\n signatureStore?: SignatureStore;\n /** Header name for session token (alternative to cookie) */\n headerName?: string;\n /** Custom 402 response */\n onPaymentRequired?: (req: GenericRequest, res: GenericResponse) => void;\n /** Called when access is granted */\n onAccessGranted?: (req: GenericRequest, session: SessionData) => void;\n}\n\n/**\n * Augmented request with session data\n */\nexport interface PaywallRequest extends GenericRequest {\n session?: SessionData;\n}\n\n/**\n * Simple glob pattern matching (supports * and **)\n */\nfunction matchPath(path: string, patterns: string[]): boolean {\n for (const pattern of patterns) {\n const regex = pattern\n .replace(/\\*\\*/g, '<<<GLOBSTAR>>>')\n .replace(/\\*/g, '[^/]*')\n .replace(/<<<GLOBSTAR>>>/g, '.*');\n\n if (new RegExp(`^${regex}$`).test(path)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if headers object has a get method (Fetch/Next.js style)\n */\nfunction hasGetMethod(headers: GenericRequest['headers']): headers is { get(name: string): string | undefined } {\n return typeof (headers as { get?: unknown }).get === 'function';\n}\n\n/**\n * Get header value from either style of headers object\n */\nfunction getHeader(headers: GenericRequest['headers'], name: string): string | undefined {\n if (hasGetMethod(headers)) {\n return headers.get(name) ?? undefined;\n }\n const value = (headers as Record<string, string | string[] | undefined>)[name];\n return typeof value === 'string' ? value : undefined;\n}\n\n/**\n * Extract session token from request\n */\nfunction extractToken(req: GenericRequest, config: ExpressPaywallConfig): string | undefined {\n const { cookieName = 'x402_session', headerName } = config;\n\n // Try custom header first (for API clients)\n if (headerName) {\n const token = getHeader(req.headers, headerName.toLowerCase());\n if (token) return token;\n }\n\n // Try Authorization header (Bearer token)\n const authHeader = getHeader(req.headers, 'authorization');\n if (authHeader?.startsWith('Bearer ')) {\n return authHeader.slice(7);\n }\n\n // Try cookies\n if (req.cookies?.[cookieName]) {\n return req.cookies[cookieName];\n }\n\n // Parse cookie header manually if cookies not pre-parsed\n const cookieHeader = getHeader(req.headers, 'cookie');\n if (cookieHeader) {\n const match = cookieHeader.split(';')\n .map(c => c.trim())\n .find(c => c.startsWith(`${cookieName}=`));\n\n if (match) {\n return match.slice(cookieName.length + 1);\n }\n }\n\n return undefined;\n}\n\n/**\n * Create Express-compatible paywall middleware\n * Also works with Connect, Polka, and similar frameworks\n * \n * @example\n * ```typescript\n * import express from 'express';\n * import { createExpressMiddleware } from '@alleyboss/micropay-solana-x402-paywall/middleware';\n * \n * const app = express();\n * \n * app.use('/api/premium', createExpressMiddleware({\n * sessionSecret: process.env.SESSION_SECRET!,\n * protectedPaths: ['/**'],\n * }));\n * \n * app.get('/api/premium/content', (req, res) => {\n * res.json({ content: 'Premium!', wallet: req.session?.walletAddress });\n * });\n * ```\n */\nexport function createExpressMiddleware(config: ExpressPaywallConfig) {\n const { sessionSecret, protectedPaths, onPaymentRequired, onAccessGranted } = config;\n\n return async function paywallMiddleware(\n req: PaywallRequest,\n res: GenericResponse,\n next: NextFunction\n ): Promise<void> {\n const path = req.path || req.url?.split('?')[0] || '/';\n\n // Check if path needs protection\n if (!matchPath(path, protectedPaths)) {\n next();\n return;\n }\n\n // Extract token\n const token = extractToken(req, config);\n\n if (!token) {\n if (onPaymentRequired) {\n onPaymentRequired(req, res);\n } else {\n sendPaymentRequired(res, 'No session token');\n }\n return;\n }\n\n // Validate session\n const validation = await validateSession(token, sessionSecret);\n\n if (!validation.valid || !validation.session) {\n if (onPaymentRequired) {\n onPaymentRequired(req, res);\n } else {\n sendPaymentRequired(res, validation.reason || 'Invalid session');\n }\n return;\n }\n\n // Attach session to request\n req.session = validation.session;\n\n if (onAccessGranted) {\n onAccessGranted(req, validation.session);\n }\n\n next();\n };\n}\n\n/**\n * Send 402 Payment Required response\n */\nfunction sendPaymentRequired(res: GenericResponse, reason: string): void {\n const body = JSON.stringify({\n error: 'Payment Required',\n message: reason,\n });\n\n // Check for Express-style response (has both status and json methods)\n const statusFn = res.status;\n const jsonFn = res.json;\n\n if (statusFn && jsonFn) {\n // Express-style: res.status(402).json(...)\n const chainedRes = statusFn.call(res, 402);\n if (chainedRes?.json) {\n chainedRes.json({ error: 'Payment Required', message: reason });\n }\n } else if (res.statusCode !== undefined && res.setHeader && res.end) {\n // Raw Node.js http\n res.statusCode = 402;\n res.setHeader('Content-Type', 'application/json');\n res.end(body);\n }\n}\n\n/**\n * Fastify-compatible plugin factory\n * \n * @example\n * ```typescript\n * import Fastify from 'fastify';\n * import { createFastifyPlugin } from '@alleyboss/micropay-solana-x402-paywall/middleware';\n * \n * const fastify = Fastify();\n * fastify.register(createFastifyPlugin({\n * sessionSecret: process.env.SESSION_SECRET!,\n * protectedPaths: ['/api/premium/*'],\n * }));\n * ```\n */\nexport function createFastifyPlugin(config: ExpressPaywallConfig) {\n return async function paywallPlugin(fastify: any) {\n fastify.addHook('preHandler', createExpressMiddleware(config));\n };\n}\n"]}
@@ -0,0 +1,90 @@
1
+ export { M as MiddlewareResult, P as PaywallMiddlewareConfig, a as checkPaywallAccess, c as createPaywallMiddleware, w as withPaywall } from '../nextjs-Bm272Jkj.cjs';
2
+ import { S as SignatureStore } from '../memory-Daxkczti.cjs';
3
+ import { S as SessionData } from '../session-D2IoWAWV.cjs';
4
+
5
+ /**
6
+ * Generic HTTP request/response interfaces for framework compatibility
7
+ * Works with Express, Fastify, raw Node.js http, etc.
8
+ */
9
+ interface GenericRequest {
10
+ url?: string;
11
+ path?: string;
12
+ headers: Record<string, string | string[] | undefined> | {
13
+ get(name: string): string | undefined;
14
+ };
15
+ cookies?: Record<string, string>;
16
+ }
17
+ interface GenericResponse {
18
+ status?(code: number): GenericResponse;
19
+ statusCode?: number;
20
+ json?(body: object): void;
21
+ send?(body: string): void;
22
+ setHeader?(name: string, value: string): void;
23
+ end?(body?: string): void;
24
+ }
25
+ type NextFunction = (error?: Error) => void;
26
+ /**
27
+ * Express-style middleware configuration
28
+ */
29
+ interface ExpressPaywallConfig {
30
+ /** Session secret for JWT validation */
31
+ sessionSecret: string;
32
+ /** Protected path patterns (glob-like) */
33
+ protectedPaths: string[];
34
+ /** Cookie name for session token (default: 'x402_session') */
35
+ cookieName?: string;
36
+ /** Optional signature store */
37
+ signatureStore?: SignatureStore;
38
+ /** Header name for session token (alternative to cookie) */
39
+ headerName?: string;
40
+ /** Custom 402 response */
41
+ onPaymentRequired?: (req: GenericRequest, res: GenericResponse) => void;
42
+ /** Called when access is granted */
43
+ onAccessGranted?: (req: GenericRequest, session: SessionData) => void;
44
+ }
45
+ /**
46
+ * Augmented request with session data
47
+ */
48
+ interface PaywallRequest extends GenericRequest {
49
+ session?: SessionData;
50
+ }
51
+ /**
52
+ * Create Express-compatible paywall middleware
53
+ * Also works with Connect, Polka, and similar frameworks
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * import express from 'express';
58
+ * import { createExpressMiddleware } from '@alleyboss/micropay-solana-x402-paywall/middleware';
59
+ *
60
+ * const app = express();
61
+ *
62
+ * app.use('/api/premium', createExpressMiddleware({
63
+ * sessionSecret: process.env.SESSION_SECRET!,
64
+ * protectedPaths: ['/**'],
65
+ * }));
66
+ *
67
+ * app.get('/api/premium/content', (req, res) => {
68
+ * res.json({ content: 'Premium!', wallet: req.session?.walletAddress });
69
+ * });
70
+ * ```
71
+ */
72
+ declare function createExpressMiddleware(config: ExpressPaywallConfig): (req: PaywallRequest, res: GenericResponse, next: NextFunction) => Promise<void>;
73
+ /**
74
+ * Fastify-compatible plugin factory
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * import Fastify from 'fastify';
79
+ * import { createFastifyPlugin } from '@alleyboss/micropay-solana-x402-paywall/middleware';
80
+ *
81
+ * const fastify = Fastify();
82
+ * fastify.register(createFastifyPlugin({
83
+ * sessionSecret: process.env.SESSION_SECRET!,
84
+ * protectedPaths: ['/api/premium/*'],
85
+ * }));
86
+ * ```
87
+ */
88
+ declare function createFastifyPlugin(config: ExpressPaywallConfig): (fastify: any) => Promise<void>;
89
+
90
+ export { type ExpressPaywallConfig, type GenericRequest, type GenericResponse, type NextFunction, type PaywallRequest, createExpressMiddleware, createFastifyPlugin };
@@ -0,0 +1,90 @@
1
+ export { M as MiddlewareResult, P as PaywallMiddlewareConfig, a as checkPaywallAccess, c as createPaywallMiddleware, w as withPaywall } from '../nextjs-BK0pVb9Y.js';
2
+ import { S as SignatureStore } from '../memory-Daxkczti.js';
3
+ import { S as SessionData } from '../session-D2IoWAWV.js';
4
+
5
+ /**
6
+ * Generic HTTP request/response interfaces for framework compatibility
7
+ * Works with Express, Fastify, raw Node.js http, etc.
8
+ */
9
+ interface GenericRequest {
10
+ url?: string;
11
+ path?: string;
12
+ headers: Record<string, string | string[] | undefined> | {
13
+ get(name: string): string | undefined;
14
+ };
15
+ cookies?: Record<string, string>;
16
+ }
17
+ interface GenericResponse {
18
+ status?(code: number): GenericResponse;
19
+ statusCode?: number;
20
+ json?(body: object): void;
21
+ send?(body: string): void;
22
+ setHeader?(name: string, value: string): void;
23
+ end?(body?: string): void;
24
+ }
25
+ type NextFunction = (error?: Error) => void;
26
+ /**
27
+ * Express-style middleware configuration
28
+ */
29
+ interface ExpressPaywallConfig {
30
+ /** Session secret for JWT validation */
31
+ sessionSecret: string;
32
+ /** Protected path patterns (glob-like) */
33
+ protectedPaths: string[];
34
+ /** Cookie name for session token (default: 'x402_session') */
35
+ cookieName?: string;
36
+ /** Optional signature store */
37
+ signatureStore?: SignatureStore;
38
+ /** Header name for session token (alternative to cookie) */
39
+ headerName?: string;
40
+ /** Custom 402 response */
41
+ onPaymentRequired?: (req: GenericRequest, res: GenericResponse) => void;
42
+ /** Called when access is granted */
43
+ onAccessGranted?: (req: GenericRequest, session: SessionData) => void;
44
+ }
45
+ /**
46
+ * Augmented request with session data
47
+ */
48
+ interface PaywallRequest extends GenericRequest {
49
+ session?: SessionData;
50
+ }
51
+ /**
52
+ * Create Express-compatible paywall middleware
53
+ * Also works with Connect, Polka, and similar frameworks
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * import express from 'express';
58
+ * import { createExpressMiddleware } from '@alleyboss/micropay-solana-x402-paywall/middleware';
59
+ *
60
+ * const app = express();
61
+ *
62
+ * app.use('/api/premium', createExpressMiddleware({
63
+ * sessionSecret: process.env.SESSION_SECRET!,
64
+ * protectedPaths: ['/**'],
65
+ * }));
66
+ *
67
+ * app.get('/api/premium/content', (req, res) => {
68
+ * res.json({ content: 'Premium!', wallet: req.session?.walletAddress });
69
+ * });
70
+ * ```
71
+ */
72
+ declare function createExpressMiddleware(config: ExpressPaywallConfig): (req: PaywallRequest, res: GenericResponse, next: NextFunction) => Promise<void>;
73
+ /**
74
+ * Fastify-compatible plugin factory
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * import Fastify from 'fastify';
79
+ * import { createFastifyPlugin } from '@alleyboss/micropay-solana-x402-paywall/middleware';
80
+ *
81
+ * const fastify = Fastify();
82
+ * fastify.register(createFastifyPlugin({
83
+ * sessionSecret: process.env.SESSION_SECRET!,
84
+ * protectedPaths: ['/api/premium/*'],
85
+ * }));
86
+ * ```
87
+ */
88
+ declare function createFastifyPlugin(config: ExpressPaywallConfig): (fastify: any) => Promise<void>;
89
+
90
+ export { type ExpressPaywallConfig, type GenericRequest, type GenericResponse, type NextFunction, type PaywallRequest, createExpressMiddleware, createFastifyPlugin };