@peaje/next 0.1.0 → 0.2.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.
- package/README.md +22 -4
- package/dist/{chunk-7KJLR3FN.js → chunk-WRT76472.js} +170 -27
- package/dist/head.cjs +57 -27
- package/dist/head.js +1 -1
- package/dist/index.cjs +57 -27
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/proxy.cjs +355 -0
- package/dist/proxy.d.cts +11 -0
- package/dist/proxy.d.ts +11 -0
- package/dist/proxy.js +60 -0
- package/package.json +23 -10
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# @peaje/next
|
|
2
2
|
|
|
3
|
-
Peaje for Next.js.
|
|
3
|
+
Peaje for Next.js. Three pieces:
|
|
4
4
|
|
|
5
|
-
- `
|
|
5
|
+
- `peajeProxy({ slug })` (from `@peaje/next/proxy`) is the middleware that answers HTTP 402 on your own domain for every priced route in your Peaje dashboard, including API routes you publish later. It reads the route list from your gateway at runtime, cached for a minute, so a new route needs no redeploy.
|
|
6
|
+
- `withPeaje(nextConfig, { slug })` adds the build-time rewrites for the fixed catalog paths (llms.txt, openapi.json, /developers, the MCP server, the `/r/` priced links).
|
|
6
7
|
- `<PeajeHead slug="..." />` renders the JSON-LD `WebAPI` block and the discovery `<link>` tags in your `<head>`.
|
|
7
8
|
|
|
8
9
|
Both are additive: your existing rewrites, config and head stay as they are.
|
|
@@ -30,9 +31,26 @@ export default withPeaje(nextConfig, { slug: 'cafe-andino' })
|
|
|
30
31
|
|
|
31
32
|
`withPeaje` prepends the Peaje rules to `rewrites().beforeFiles` and keeps what you had: an array becomes `afterFiles`, an object keeps its `beforeFiles`, `afterFiles` and `fallback`. It accepts sync or async `rewrites()` and a config written as a function `(phase, { defaultConfig }) => config`. Wrapping twice does not duplicate rules.
|
|
32
33
|
|
|
33
|
-
Options: `slug` (required, the one from your Peaje dashboard) and `gateway` (defaults to `https://
|
|
34
|
+
Options: `slug` (required, the one from your Peaje dashboard) and `gateway` (defaults to `https://api.usepeaje.com`, overridable with the `PEAJE_GATEWAY_URL` environment variable).
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
## 1b. Runtime proxy (priced API routes on your domain)
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
// proxy.ts (Next 16) or middleware.ts (Next 14/15), at the project root or in src/
|
|
40
|
+
import { peajeProxy } from '@peaje/next/proxy'
|
|
41
|
+
|
|
42
|
+
export default peajeProxy({ slug: 'cafe-andino' })
|
|
43
|
+
|
|
44
|
+
export const config = {
|
|
45
|
+
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Set `PEAJE_ORIGIN_SECRET` in your hosting environment variables (copy it from the Kit page of your Peaje dashboard). After an agent pays, the gateway forwards the request to your site with that secret; it is how the proxy tells the gateway apart from someone pretending to be it. Without it, priced API routes are not forwarded (they would be free to anyone who spoofs a header) and the proxy logs a warning; discovery files and `/r/` links work anyway. You can also pass it as `peajeProxy({ slug, secret })`.
|
|
50
|
+
|
|
51
|
+
The rewrites above are fixed at build time, so they cannot know about an API route you add to your dashboard tomorrow. `peajeProxy` fetches `/{slug}/kit/manifest.json` from the gateway, keeps it for 60 seconds, and rewrites any request that matches a priced route (`/api/forecast`, `/api/history/:city`, `/api/batch/*`) to the gateway, which answers 402 or, once paid, forwards to your origin. Catch-all patterns (`/*`, `/:id` at the root) are not forwarded from your domain: they would send the whole site to the gateway.
|
|
52
|
+
|
|
53
|
+
With your own middleware: `export default peajeProxy({ slug }, myMiddleware)`. Peaje runs first; when the request is not one of its routes, yours runs.
|
|
36
54
|
|
|
37
55
|
## 2. Head
|
|
38
56
|
|
|
@@ -91,6 +91,116 @@ var LINKS_HEAD = [
|
|
|
91
91
|
{ rel: "ard", type: "application/ai-catalog+json", href: "/.well-known/ard.json" }
|
|
92
92
|
];
|
|
93
93
|
|
|
94
|
+
// ../shared/src/kit/proxy-runtime.ts
|
|
95
|
+
var PEAJE_ORIGIN_HEADER = "x-peaje-origin";
|
|
96
|
+
function iguales(a, b) {
|
|
97
|
+
if (a.length !== b.length) return false;
|
|
98
|
+
let d = 0;
|
|
99
|
+
for (let i = 0; i < a.length; i++) d |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
100
|
+
return d === 0;
|
|
101
|
+
}
|
|
102
|
+
function coincidePatron(patron, pathname) {
|
|
103
|
+
const p = patron.split("/").filter(Boolean);
|
|
104
|
+
const parts = segmentosCanonicos(pathname);
|
|
105
|
+
for (let i = 0; i < p.length; i++) {
|
|
106
|
+
const seg = p[i];
|
|
107
|
+
if (seg === "*") return true;
|
|
108
|
+
const v = parts[i];
|
|
109
|
+
if (v === void 0) return false;
|
|
110
|
+
if (seg.startsWith(":")) continue;
|
|
111
|
+
if (seg.toLowerCase() !== v.toLowerCase()) return false;
|
|
112
|
+
}
|
|
113
|
+
return p.length === parts.length;
|
|
114
|
+
}
|
|
115
|
+
function segmentosCanonicos(pathname) {
|
|
116
|
+
const out = [];
|
|
117
|
+
for (const crudo of pathname.split(/[\\/]+/)) {
|
|
118
|
+
if (!crudo) continue;
|
|
119
|
+
let seg = crudo;
|
|
120
|
+
try {
|
|
121
|
+
seg = decodeURIComponent(crudo);
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
124
|
+
for (const s of seg.split(/[\\/]+/)) {
|
|
125
|
+
if (!s || s === ".") continue;
|
|
126
|
+
if (s === "..") out.pop();
|
|
127
|
+
else out.push(s);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
var pathCanonico = (pathname) => `/${segmentosCanonicos(pathname).join("/")}`;
|
|
133
|
+
function patronReenviable(patron) {
|
|
134
|
+
const segs = patron.split("/").filter(Boolean);
|
|
135
|
+
if (segs.length === 0) return false;
|
|
136
|
+
return !(segs.length === 1 && (segs[0] === "*" || segs[0].startsWith(":")));
|
|
137
|
+
}
|
|
138
|
+
function crearProxyRuntime(opciones) {
|
|
139
|
+
const base = opciones.base.replace(/\/+$/, "");
|
|
140
|
+
const ttl = opciones.ttlMs ?? 6e4;
|
|
141
|
+
const fetchImpl = opciones.fetchImpl ?? fetch;
|
|
142
|
+
let cache = {
|
|
143
|
+
at: 0,
|
|
144
|
+
rutas: opciones.respaldo?.rutas ?? [],
|
|
145
|
+
prefijos: opciones.respaldo?.prefijos ?? [],
|
|
146
|
+
patrones: opciones.respaldo?.patrones ?? []
|
|
147
|
+
};
|
|
148
|
+
let refrescando = null;
|
|
149
|
+
async function refrescar() {
|
|
150
|
+
try {
|
|
151
|
+
const res = await fetchImpl(`${base}/kit/manifest.json`, { signal: AbortSignal.timeout(5e3) });
|
|
152
|
+
if (!res.ok) throw new Error(String(res.status));
|
|
153
|
+
const m = await res.json();
|
|
154
|
+
cache = {
|
|
155
|
+
at: Date.now(),
|
|
156
|
+
rutas: Array.isArray(m.rutas) ? m.rutas : cache.rutas,
|
|
157
|
+
prefijos: Array.isArray(m.prefijos) ? m.prefijos : cache.prefijos,
|
|
158
|
+
patrones: Array.isArray(m.patrones) ? m.patrones : cache.patrones
|
|
159
|
+
};
|
|
160
|
+
} catch {
|
|
161
|
+
cache = { ...cache, at: Date.now() };
|
|
162
|
+
} finally {
|
|
163
|
+
refrescando = null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function listas() {
|
|
167
|
+
const vencido = Date.now() - cache.at >= ttl;
|
|
168
|
+
if (vencido) {
|
|
169
|
+
refrescando ??= refrescar();
|
|
170
|
+
if (cache.at === 0) await refrescando;
|
|
171
|
+
}
|
|
172
|
+
return cache;
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
/** Rutas de API con precio que este sitio no puede cobrar por falta de secreto. */
|
|
176
|
+
async sinSecreto() {
|
|
177
|
+
if (opciones.secreto) return [];
|
|
178
|
+
return (await listas()).patrones.filter((p) => patronReenviable(p.path));
|
|
179
|
+
},
|
|
180
|
+
/** Fuerza la carga del manifiesto (útil al arrancar o en tests). */
|
|
181
|
+
cargar: () => refrescando ??= refrescar(),
|
|
182
|
+
/**
|
|
183
|
+
* Decide si la request va al gateway. `method` y `pathname` de la request,
|
|
184
|
+
* `search` con el `?` incluido, y los headers para cortar bucles.
|
|
185
|
+
*/
|
|
186
|
+
async decidir(input) {
|
|
187
|
+
const secreto = opciones.secreto || null;
|
|
188
|
+
const prueba = input.headers.get(PEAJE_ORIGIN_HEADER);
|
|
189
|
+
if (secreto && prueba && iguales(prueba, secreto)) return null;
|
|
190
|
+
const { rutas, prefijos, patrones } = await listas();
|
|
191
|
+
const path = input.pathname;
|
|
192
|
+
const canon = pathCanonico(path).toLowerCase();
|
|
193
|
+
const esFetchDePeaje = input.headers.get(PEAJE_FETCH_HEADER) !== null;
|
|
194
|
+
const fija = (rutas.includes(path) || rutas.some((r) => r.toLowerCase() === canon)) && !(canon === "/llms.txt" && esFetchDePeaje);
|
|
195
|
+
const prefijo = prefijos.some((p) => path.startsWith(p) || canon.startsWith(p.toLowerCase()));
|
|
196
|
+
const method = input.method.toUpperCase();
|
|
197
|
+
const patron = secreto !== null && patrones.some((p) => p.method.toUpperCase() === method && patronReenviable(p.path) && coincidePatron(p.path, path));
|
|
198
|
+
if (!fija && !prefijo && !patron) return null;
|
|
199
|
+
return { destino: `${base}${path}${input.search ?? ""}` };
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
94
204
|
// ../shared/src/index.ts
|
|
95
205
|
var TEMPO = {
|
|
96
206
|
mainnet: {
|
|
@@ -110,32 +220,6 @@ var TOKENS = {
|
|
|
110
220
|
};
|
|
111
221
|
var DEFAULT_CURRENCY = TOKENS.pathUsd;
|
|
112
222
|
var NETWORKS = {
|
|
113
|
-
tempo: {
|
|
114
|
-
id: "tempo",
|
|
115
|
-
label: "Tempo",
|
|
116
|
-
token: TOKENS.pathUsd,
|
|
117
|
-
tokenSymbol: "pathUSD",
|
|
118
|
-
decimals: 6,
|
|
119
|
-
mainnet: TEMPO.mainnet,
|
|
120
|
-
testnet: TEMPO.testnet
|
|
121
|
-
},
|
|
122
|
-
arc: {
|
|
123
|
-
id: "arc",
|
|
124
|
-
label: "Arc",
|
|
125
|
-
// Interfaz ERC-20 del USDC nativo de Arc; el gas de la red se paga en ese mismo USDC.
|
|
126
|
-
token: "0x3600000000000000000000000000000000000000",
|
|
127
|
-
tokenSymbol: "USDC",
|
|
128
|
-
decimals: 6,
|
|
129
|
-
// Leído on-chain del contrato: name() = "USDC", version() = "2".
|
|
130
|
-
eip3009: { name: "USDC", version: "2" },
|
|
131
|
-
// Arc no publica direcciones de mainnet todavía (sept 2026).
|
|
132
|
-
mainnet: null,
|
|
133
|
-
testnet: {
|
|
134
|
-
chainId: 5042002,
|
|
135
|
-
rpcUrl: "https://rpc.testnet.arc.io",
|
|
136
|
-
explorerUrl: "https://testnet.arcscan.app"
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
223
|
arbitrum: {
|
|
140
224
|
id: "arbitrum",
|
|
141
225
|
label: "Arbitrum",
|
|
@@ -184,12 +268,68 @@ var NETWORKS = {
|
|
|
184
268
|
rpcUrl: "https://rpc.testnet.chain.robinhood.com",
|
|
185
269
|
explorerUrl: "https://explorer.testnet.chain.robinhood.com"
|
|
186
270
|
}
|
|
271
|
+
},
|
|
272
|
+
tempo: {
|
|
273
|
+
id: "tempo",
|
|
274
|
+
label: "Tempo",
|
|
275
|
+
token: TOKENS.pathUsd,
|
|
276
|
+
tokenSymbol: "pathUSD",
|
|
277
|
+
decimals: 6,
|
|
278
|
+
mainnet: TEMPO.mainnet,
|
|
279
|
+
testnet: TEMPO.testnet
|
|
280
|
+
},
|
|
281
|
+
arc: {
|
|
282
|
+
id: "arc",
|
|
283
|
+
label: "Arc",
|
|
284
|
+
// Interfaz ERC-20 del USDC nativo de Arc; el gas de la red se paga en ese mismo USDC.
|
|
285
|
+
token: "0x3600000000000000000000000000000000000000",
|
|
286
|
+
tokenSymbol: "USDC",
|
|
287
|
+
decimals: 6,
|
|
288
|
+
// Leído on-chain del contrato: name() = "USDC", version() = "2".
|
|
289
|
+
eip3009: { name: "USDC", version: "2" },
|
|
290
|
+
// Arc no publica direcciones de mainnet todavía (sept 2026).
|
|
291
|
+
mainnet: null,
|
|
292
|
+
testnet: {
|
|
293
|
+
chainId: 5042002,
|
|
294
|
+
rpcUrl: "https://rpc.testnet.arc.io",
|
|
295
|
+
explorerUrl: "https://testnet.arcscan.app"
|
|
296
|
+
}
|
|
187
297
|
}
|
|
188
298
|
};
|
|
189
299
|
var NETWORK_IDS = Object.keys(NETWORKS);
|
|
300
|
+
var CHAIN_LABELS = [
|
|
301
|
+
...new Map(NETWORK_IDS.map((id) => [NETWORKS[id].testnet.chainId, NETWORKS[id].label.split(" \xB7 ")[0] ?? NETWORKS[id].label])).values()
|
|
302
|
+
];
|
|
303
|
+
var GATEWAY_RAILS = {
|
|
304
|
+
"gateway-arbitrum": {
|
|
305
|
+
id: "gateway-arbitrum",
|
|
306
|
+
label: "Arbitrum \xB7 Nanopayments",
|
|
307
|
+
chainId: 421614,
|
|
308
|
+
rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
|
|
309
|
+
token: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
|
|
310
|
+
decimals: 6,
|
|
311
|
+
verifyingContract: "0x0077777d7EBA4688BDeF3E311b846F25870A19B9",
|
|
312
|
+
facilitatorUrl: "https://gateway-api-testnet.circle.com/v1/x402",
|
|
313
|
+
sdkChain: "arbitrumSepolia",
|
|
314
|
+
explorerUrl: "https://sepolia.arbiscan.io"
|
|
315
|
+
},
|
|
316
|
+
"gateway-arc": {
|
|
317
|
+
id: "gateway-arc",
|
|
318
|
+
label: "Arc \xB7 Nanopayments",
|
|
319
|
+
chainId: 5042002,
|
|
320
|
+
rpcUrl: "https://rpc.testnet.arc.network",
|
|
321
|
+
token: "0x3600000000000000000000000000000000000000",
|
|
322
|
+
decimals: 6,
|
|
323
|
+
verifyingContract: "0x0077777d7EBA4688BDeF3E311b846F25870A19B9",
|
|
324
|
+
facilitatorUrl: "https://gateway-api-testnet.circle.com/v1/x402",
|
|
325
|
+
sdkChain: "arcTestnet",
|
|
326
|
+
explorerUrl: "https://testnet.arcscan.app"
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
var GATEWAY_RAIL_IDS = Object.keys(GATEWAY_RAILS);
|
|
190
330
|
|
|
191
331
|
// src/index.ts
|
|
192
|
-
var GATEWAY_POR_DEFECTO = "https://
|
|
332
|
+
var GATEWAY_POR_DEFECTO = "https://api.usepeaje.com";
|
|
193
333
|
function baseDe({ slug, gateway }) {
|
|
194
334
|
const raiz = (gateway ?? process.env.PEAJE_GATEWAY_URL ?? GATEWAY_POR_DEFECTO).replace(/\/+$/, "");
|
|
195
335
|
return `${raiz}/${slug}`;
|
|
@@ -222,9 +362,12 @@ function withPeaje(nextConfig, opciones) {
|
|
|
222
362
|
var src_default = withPeaje;
|
|
223
363
|
|
|
224
364
|
export {
|
|
365
|
+
RUTAS_PROXY,
|
|
366
|
+
RUTAS_DINAMICAS,
|
|
225
367
|
jsonParaScript,
|
|
226
368
|
jsonLdWebApi,
|
|
227
369
|
LINKS_HEAD,
|
|
370
|
+
crearProxyRuntime,
|
|
228
371
|
GATEWAY_POR_DEFECTO,
|
|
229
372
|
baseDe,
|
|
230
373
|
fusionarRewrites,
|
package/dist/head.cjs
CHANGED
|
@@ -109,32 +109,6 @@ var TOKENS = {
|
|
|
109
109
|
};
|
|
110
110
|
var DEFAULT_CURRENCY = TOKENS.pathUsd;
|
|
111
111
|
var NETWORKS = {
|
|
112
|
-
tempo: {
|
|
113
|
-
id: "tempo",
|
|
114
|
-
label: "Tempo",
|
|
115
|
-
token: TOKENS.pathUsd,
|
|
116
|
-
tokenSymbol: "pathUSD",
|
|
117
|
-
decimals: 6,
|
|
118
|
-
mainnet: TEMPO.mainnet,
|
|
119
|
-
testnet: TEMPO.testnet
|
|
120
|
-
},
|
|
121
|
-
arc: {
|
|
122
|
-
id: "arc",
|
|
123
|
-
label: "Arc",
|
|
124
|
-
// Interfaz ERC-20 del USDC nativo de Arc; el gas de la red se paga en ese mismo USDC.
|
|
125
|
-
token: "0x3600000000000000000000000000000000000000",
|
|
126
|
-
tokenSymbol: "USDC",
|
|
127
|
-
decimals: 6,
|
|
128
|
-
// Leído on-chain del contrato: name() = "USDC", version() = "2".
|
|
129
|
-
eip3009: { name: "USDC", version: "2" },
|
|
130
|
-
// Arc no publica direcciones de mainnet todavía (sept 2026).
|
|
131
|
-
mainnet: null,
|
|
132
|
-
testnet: {
|
|
133
|
-
chainId: 5042002,
|
|
134
|
-
rpcUrl: "https://rpc.testnet.arc.io",
|
|
135
|
-
explorerUrl: "https://testnet.arcscan.app"
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
112
|
arbitrum: {
|
|
139
113
|
id: "arbitrum",
|
|
140
114
|
label: "Arbitrum",
|
|
@@ -183,12 +157,68 @@ var NETWORKS = {
|
|
|
183
157
|
rpcUrl: "https://rpc.testnet.chain.robinhood.com",
|
|
184
158
|
explorerUrl: "https://explorer.testnet.chain.robinhood.com"
|
|
185
159
|
}
|
|
160
|
+
},
|
|
161
|
+
tempo: {
|
|
162
|
+
id: "tempo",
|
|
163
|
+
label: "Tempo",
|
|
164
|
+
token: TOKENS.pathUsd,
|
|
165
|
+
tokenSymbol: "pathUSD",
|
|
166
|
+
decimals: 6,
|
|
167
|
+
mainnet: TEMPO.mainnet,
|
|
168
|
+
testnet: TEMPO.testnet
|
|
169
|
+
},
|
|
170
|
+
arc: {
|
|
171
|
+
id: "arc",
|
|
172
|
+
label: "Arc",
|
|
173
|
+
// Interfaz ERC-20 del USDC nativo de Arc; el gas de la red se paga en ese mismo USDC.
|
|
174
|
+
token: "0x3600000000000000000000000000000000000000",
|
|
175
|
+
tokenSymbol: "USDC",
|
|
176
|
+
decimals: 6,
|
|
177
|
+
// Leído on-chain del contrato: name() = "USDC", version() = "2".
|
|
178
|
+
eip3009: { name: "USDC", version: "2" },
|
|
179
|
+
// Arc no publica direcciones de mainnet todavía (sept 2026).
|
|
180
|
+
mainnet: null,
|
|
181
|
+
testnet: {
|
|
182
|
+
chainId: 5042002,
|
|
183
|
+
rpcUrl: "https://rpc.testnet.arc.io",
|
|
184
|
+
explorerUrl: "https://testnet.arcscan.app"
|
|
185
|
+
}
|
|
186
186
|
}
|
|
187
187
|
};
|
|
188
188
|
var NETWORK_IDS = Object.keys(NETWORKS);
|
|
189
|
+
var CHAIN_LABELS = [
|
|
190
|
+
...new Map(NETWORK_IDS.map((id) => [NETWORKS[id].testnet.chainId, NETWORKS[id].label.split(" \xB7 ")[0] ?? NETWORKS[id].label])).values()
|
|
191
|
+
];
|
|
192
|
+
var GATEWAY_RAILS = {
|
|
193
|
+
"gateway-arbitrum": {
|
|
194
|
+
id: "gateway-arbitrum",
|
|
195
|
+
label: "Arbitrum \xB7 Nanopayments",
|
|
196
|
+
chainId: 421614,
|
|
197
|
+
rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
|
|
198
|
+
token: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
|
|
199
|
+
decimals: 6,
|
|
200
|
+
verifyingContract: "0x0077777d7EBA4688BDeF3E311b846F25870A19B9",
|
|
201
|
+
facilitatorUrl: "https://gateway-api-testnet.circle.com/v1/x402",
|
|
202
|
+
sdkChain: "arbitrumSepolia",
|
|
203
|
+
explorerUrl: "https://sepolia.arbiscan.io"
|
|
204
|
+
},
|
|
205
|
+
"gateway-arc": {
|
|
206
|
+
id: "gateway-arc",
|
|
207
|
+
label: "Arc \xB7 Nanopayments",
|
|
208
|
+
chainId: 5042002,
|
|
209
|
+
rpcUrl: "https://rpc.testnet.arc.network",
|
|
210
|
+
token: "0x3600000000000000000000000000000000000000",
|
|
211
|
+
decimals: 6,
|
|
212
|
+
verifyingContract: "0x0077777d7EBA4688BDeF3E311b846F25870A19B9",
|
|
213
|
+
facilitatorUrl: "https://gateway-api-testnet.circle.com/v1/x402",
|
|
214
|
+
sdkChain: "arcTestnet",
|
|
215
|
+
explorerUrl: "https://testnet.arcscan.app"
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
var GATEWAY_RAIL_IDS = Object.keys(GATEWAY_RAILS);
|
|
189
219
|
|
|
190
220
|
// src/index.ts
|
|
191
|
-
var GATEWAY_POR_DEFECTO = "https://
|
|
221
|
+
var GATEWAY_POR_DEFECTO = "https://api.usepeaje.com";
|
|
192
222
|
function baseDe({ slug, gateway }) {
|
|
193
223
|
const raiz = (gateway ?? process.env.PEAJE_GATEWAY_URL ?? GATEWAY_POR_DEFECTO).replace(/\/+$/, "");
|
|
194
224
|
return `${raiz}/${slug}`;
|
package/dist/head.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -86,32 +86,6 @@ var TOKENS = {
|
|
|
86
86
|
};
|
|
87
87
|
var DEFAULT_CURRENCY = TOKENS.pathUsd;
|
|
88
88
|
var NETWORKS = {
|
|
89
|
-
tempo: {
|
|
90
|
-
id: "tempo",
|
|
91
|
-
label: "Tempo",
|
|
92
|
-
token: TOKENS.pathUsd,
|
|
93
|
-
tokenSymbol: "pathUSD",
|
|
94
|
-
decimals: 6,
|
|
95
|
-
mainnet: TEMPO.mainnet,
|
|
96
|
-
testnet: TEMPO.testnet
|
|
97
|
-
},
|
|
98
|
-
arc: {
|
|
99
|
-
id: "arc",
|
|
100
|
-
label: "Arc",
|
|
101
|
-
// Interfaz ERC-20 del USDC nativo de Arc; el gas de la red se paga en ese mismo USDC.
|
|
102
|
-
token: "0x3600000000000000000000000000000000000000",
|
|
103
|
-
tokenSymbol: "USDC",
|
|
104
|
-
decimals: 6,
|
|
105
|
-
// Leído on-chain del contrato: name() = "USDC", version() = "2".
|
|
106
|
-
eip3009: { name: "USDC", version: "2" },
|
|
107
|
-
// Arc no publica direcciones de mainnet todavía (sept 2026).
|
|
108
|
-
mainnet: null,
|
|
109
|
-
testnet: {
|
|
110
|
-
chainId: 5042002,
|
|
111
|
-
rpcUrl: "https://rpc.testnet.arc.io",
|
|
112
|
-
explorerUrl: "https://testnet.arcscan.app"
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
89
|
arbitrum: {
|
|
116
90
|
id: "arbitrum",
|
|
117
91
|
label: "Arbitrum",
|
|
@@ -160,12 +134,68 @@ var NETWORKS = {
|
|
|
160
134
|
rpcUrl: "https://rpc.testnet.chain.robinhood.com",
|
|
161
135
|
explorerUrl: "https://explorer.testnet.chain.robinhood.com"
|
|
162
136
|
}
|
|
137
|
+
},
|
|
138
|
+
tempo: {
|
|
139
|
+
id: "tempo",
|
|
140
|
+
label: "Tempo",
|
|
141
|
+
token: TOKENS.pathUsd,
|
|
142
|
+
tokenSymbol: "pathUSD",
|
|
143
|
+
decimals: 6,
|
|
144
|
+
mainnet: TEMPO.mainnet,
|
|
145
|
+
testnet: TEMPO.testnet
|
|
146
|
+
},
|
|
147
|
+
arc: {
|
|
148
|
+
id: "arc",
|
|
149
|
+
label: "Arc",
|
|
150
|
+
// Interfaz ERC-20 del USDC nativo de Arc; el gas de la red se paga en ese mismo USDC.
|
|
151
|
+
token: "0x3600000000000000000000000000000000000000",
|
|
152
|
+
tokenSymbol: "USDC",
|
|
153
|
+
decimals: 6,
|
|
154
|
+
// Leído on-chain del contrato: name() = "USDC", version() = "2".
|
|
155
|
+
eip3009: { name: "USDC", version: "2" },
|
|
156
|
+
// Arc no publica direcciones de mainnet todavía (sept 2026).
|
|
157
|
+
mainnet: null,
|
|
158
|
+
testnet: {
|
|
159
|
+
chainId: 5042002,
|
|
160
|
+
rpcUrl: "https://rpc.testnet.arc.io",
|
|
161
|
+
explorerUrl: "https://testnet.arcscan.app"
|
|
162
|
+
}
|
|
163
163
|
}
|
|
164
164
|
};
|
|
165
165
|
var NETWORK_IDS = Object.keys(NETWORKS);
|
|
166
|
+
var CHAIN_LABELS = [
|
|
167
|
+
...new Map(NETWORK_IDS.map((id) => [NETWORKS[id].testnet.chainId, NETWORKS[id].label.split(" \xB7 ")[0] ?? NETWORKS[id].label])).values()
|
|
168
|
+
];
|
|
169
|
+
var GATEWAY_RAILS = {
|
|
170
|
+
"gateway-arbitrum": {
|
|
171
|
+
id: "gateway-arbitrum",
|
|
172
|
+
label: "Arbitrum \xB7 Nanopayments",
|
|
173
|
+
chainId: 421614,
|
|
174
|
+
rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
|
|
175
|
+
token: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
|
|
176
|
+
decimals: 6,
|
|
177
|
+
verifyingContract: "0x0077777d7EBA4688BDeF3E311b846F25870A19B9",
|
|
178
|
+
facilitatorUrl: "https://gateway-api-testnet.circle.com/v1/x402",
|
|
179
|
+
sdkChain: "arbitrumSepolia",
|
|
180
|
+
explorerUrl: "https://sepolia.arbiscan.io"
|
|
181
|
+
},
|
|
182
|
+
"gateway-arc": {
|
|
183
|
+
id: "gateway-arc",
|
|
184
|
+
label: "Arc \xB7 Nanopayments",
|
|
185
|
+
chainId: 5042002,
|
|
186
|
+
rpcUrl: "https://rpc.testnet.arc.network",
|
|
187
|
+
token: "0x3600000000000000000000000000000000000000",
|
|
188
|
+
decimals: 6,
|
|
189
|
+
verifyingContract: "0x0077777d7EBA4688BDeF3E311b846F25870A19B9",
|
|
190
|
+
facilitatorUrl: "https://gateway-api-testnet.circle.com/v1/x402",
|
|
191
|
+
sdkChain: "arcTestnet",
|
|
192
|
+
explorerUrl: "https://testnet.arcscan.app"
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
var GATEWAY_RAIL_IDS = Object.keys(GATEWAY_RAILS);
|
|
166
196
|
|
|
167
197
|
// src/index.ts
|
|
168
|
-
var GATEWAY_POR_DEFECTO = "https://
|
|
198
|
+
var GATEWAY_POR_DEFECTO = "https://api.usepeaje.com";
|
|
169
199
|
function baseDe({ slug, gateway }) {
|
|
170
200
|
const raiz = (gateway ?? process.env.PEAJE_GATEWAY_URL ?? GATEWAY_POR_DEFECTO).replace(/\/+$/, "");
|
|
171
201
|
return `${raiz}/${slug}`;
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/proxy.cjs
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/proxy.ts
|
|
21
|
+
var proxy_exports = {};
|
|
22
|
+
__export(proxy_exports, {
|
|
23
|
+
default: () => proxy_default,
|
|
24
|
+
peajeProxy: () => peajeProxy
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(proxy_exports);
|
|
27
|
+
|
|
28
|
+
// ../shared/src/kit/rutas.ts
|
|
29
|
+
var RUTAS_PROXY = [
|
|
30
|
+
{ path: "/llms.txt", nota: "gu\xEDa para agentes (fusionada con la tuya)" },
|
|
31
|
+
{ path: "/pricing.md", nota: "precios por endpoint" },
|
|
32
|
+
{ path: "/auth.md", nota: 'c\xF3mo pagar (el "auth" ac\xE1 es el pago)' },
|
|
33
|
+
{ path: "/agents.md", nota: "qu\xE9 es y cu\xE1ndo usarte" },
|
|
34
|
+
{ path: "/openapi.json", nota: "discovery con precios" },
|
|
35
|
+
{ path: "/mcp", nota: "MCP server con tools pagas, en TU dominio" },
|
|
36
|
+
{ path: "/.well-known/mcp", nota: "el MCP tambi\xE9n ac\xE1: es donde lo buscan los auditores" },
|
|
37
|
+
{ path: "/.well-known/ard.json", nota: "cat\xE1logo ARD (path can\xF3nico)" },
|
|
38
|
+
{ path: "/.well-known/ai-catalog.json", nota: "cat\xE1logo ARD (alias legacy)" },
|
|
39
|
+
{ path: "/.well-known/api-catalog", nota: "linkset RFC 9727" },
|
|
40
|
+
{ path: "/.well-known/agent-card.json", nota: "agent card A2A" },
|
|
41
|
+
{ path: "/.well-known/mcp/server-card.json", nota: "server card MCP" },
|
|
42
|
+
{ path: "/discovery/resources", nota: "lista x402 Bazaar: lo que cobra 402 de verdad" },
|
|
43
|
+
{ path: "/.well-known/ucp", nota: "perfil UCP: qu\xE9 sabe hacer tu negocio" },
|
|
44
|
+
{ path: "/checkout_sessions", nota: "checkout ACP (el de OpenAI)" },
|
|
45
|
+
{ path: "/agentic_commerce/delegate_payment", nota: "delegate payment ACP" },
|
|
46
|
+
{ path: "/developers", nota: "portal de desarrolladores, renderizado con tus precios" },
|
|
47
|
+
{ path: "/.well-known/peaje-indexnow.txt", nota: "clave IndexNow: avisa a Bing y Copilot cuando cambian precios" },
|
|
48
|
+
// Los auditores prueban /docs y /developers; el gateway redirige uno al otro.
|
|
49
|
+
{ path: "/docs", nota: "alias del portal: los auditores prueban ambos" }
|
|
50
|
+
];
|
|
51
|
+
var RUTAS_DINAMICAS = [
|
|
52
|
+
{ path: "/r/:slug", prefijo: "/r/", nota: "links con precio: el 402 en tu dominio" },
|
|
53
|
+
{ path: "/checkout_sessions/:id", prefijo: "/checkout_sessions/", nota: "leer una sesi\xF3n de checkout ACP" }
|
|
54
|
+
];
|
|
55
|
+
var PEAJE_FETCH_HEADER = "x-peaje-fetch";
|
|
56
|
+
|
|
57
|
+
// ../shared/src/kit/proxy-runtime.ts
|
|
58
|
+
var PEAJE_ORIGIN_HEADER = "x-peaje-origin";
|
|
59
|
+
function iguales(a, b) {
|
|
60
|
+
if (a.length !== b.length) return false;
|
|
61
|
+
let d = 0;
|
|
62
|
+
for (let i = 0; i < a.length; i++) d |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
63
|
+
return d === 0;
|
|
64
|
+
}
|
|
65
|
+
function coincidePatron(patron, pathname) {
|
|
66
|
+
const p = patron.split("/").filter(Boolean);
|
|
67
|
+
const parts = segmentosCanonicos(pathname);
|
|
68
|
+
for (let i = 0; i < p.length; i++) {
|
|
69
|
+
const seg = p[i];
|
|
70
|
+
if (seg === "*") return true;
|
|
71
|
+
const v = parts[i];
|
|
72
|
+
if (v === void 0) return false;
|
|
73
|
+
if (seg.startsWith(":")) continue;
|
|
74
|
+
if (seg.toLowerCase() !== v.toLowerCase()) return false;
|
|
75
|
+
}
|
|
76
|
+
return p.length === parts.length;
|
|
77
|
+
}
|
|
78
|
+
function segmentosCanonicos(pathname) {
|
|
79
|
+
const out = [];
|
|
80
|
+
for (const crudo of pathname.split(/[\\/]+/)) {
|
|
81
|
+
if (!crudo) continue;
|
|
82
|
+
let seg = crudo;
|
|
83
|
+
try {
|
|
84
|
+
seg = decodeURIComponent(crudo);
|
|
85
|
+
} catch {
|
|
86
|
+
}
|
|
87
|
+
for (const s of seg.split(/[\\/]+/)) {
|
|
88
|
+
if (!s || s === ".") continue;
|
|
89
|
+
if (s === "..") out.pop();
|
|
90
|
+
else out.push(s);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
var pathCanonico = (pathname) => `/${segmentosCanonicos(pathname).join("/")}`;
|
|
96
|
+
function patronReenviable(patron) {
|
|
97
|
+
const segs = patron.split("/").filter(Boolean);
|
|
98
|
+
if (segs.length === 0) return false;
|
|
99
|
+
return !(segs.length === 1 && (segs[0] === "*" || segs[0].startsWith(":")));
|
|
100
|
+
}
|
|
101
|
+
function crearProxyRuntime(opciones) {
|
|
102
|
+
const base = opciones.base.replace(/\/+$/, "");
|
|
103
|
+
const ttl = opciones.ttlMs ?? 6e4;
|
|
104
|
+
const fetchImpl = opciones.fetchImpl ?? fetch;
|
|
105
|
+
let cache = {
|
|
106
|
+
at: 0,
|
|
107
|
+
rutas: opciones.respaldo?.rutas ?? [],
|
|
108
|
+
prefijos: opciones.respaldo?.prefijos ?? [],
|
|
109
|
+
patrones: opciones.respaldo?.patrones ?? []
|
|
110
|
+
};
|
|
111
|
+
let refrescando = null;
|
|
112
|
+
async function refrescar() {
|
|
113
|
+
try {
|
|
114
|
+
const res = await fetchImpl(`${base}/kit/manifest.json`, { signal: AbortSignal.timeout(5e3) });
|
|
115
|
+
if (!res.ok) throw new Error(String(res.status));
|
|
116
|
+
const m = await res.json();
|
|
117
|
+
cache = {
|
|
118
|
+
at: Date.now(),
|
|
119
|
+
rutas: Array.isArray(m.rutas) ? m.rutas : cache.rutas,
|
|
120
|
+
prefijos: Array.isArray(m.prefijos) ? m.prefijos : cache.prefijos,
|
|
121
|
+
patrones: Array.isArray(m.patrones) ? m.patrones : cache.patrones
|
|
122
|
+
};
|
|
123
|
+
} catch {
|
|
124
|
+
cache = { ...cache, at: Date.now() };
|
|
125
|
+
} finally {
|
|
126
|
+
refrescando = null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function listas() {
|
|
130
|
+
const vencido = Date.now() - cache.at >= ttl;
|
|
131
|
+
if (vencido) {
|
|
132
|
+
refrescando ??= refrescar();
|
|
133
|
+
if (cache.at === 0) await refrescando;
|
|
134
|
+
}
|
|
135
|
+
return cache;
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
/** Rutas de API con precio que este sitio no puede cobrar por falta de secreto. */
|
|
139
|
+
async sinSecreto() {
|
|
140
|
+
if (opciones.secreto) return [];
|
|
141
|
+
return (await listas()).patrones.filter((p) => patronReenviable(p.path));
|
|
142
|
+
},
|
|
143
|
+
/** Fuerza la carga del manifiesto (útil al arrancar o en tests). */
|
|
144
|
+
cargar: () => refrescando ??= refrescar(),
|
|
145
|
+
/**
|
|
146
|
+
* Decide si la request va al gateway. `method` y `pathname` de la request,
|
|
147
|
+
* `search` con el `?` incluido, y los headers para cortar bucles.
|
|
148
|
+
*/
|
|
149
|
+
async decidir(input) {
|
|
150
|
+
const secreto = opciones.secreto || null;
|
|
151
|
+
const prueba = input.headers.get(PEAJE_ORIGIN_HEADER);
|
|
152
|
+
if (secreto && prueba && iguales(prueba, secreto)) return null;
|
|
153
|
+
const { rutas, prefijos, patrones } = await listas();
|
|
154
|
+
const path = input.pathname;
|
|
155
|
+
const canon = pathCanonico(path).toLowerCase();
|
|
156
|
+
const esFetchDePeaje = input.headers.get(PEAJE_FETCH_HEADER) !== null;
|
|
157
|
+
const fija = (rutas.includes(path) || rutas.some((r) => r.toLowerCase() === canon)) && !(canon === "/llms.txt" && esFetchDePeaje);
|
|
158
|
+
const prefijo = prefijos.some((p) => path.startsWith(p) || canon.startsWith(p.toLowerCase()));
|
|
159
|
+
const method = input.method.toUpperCase();
|
|
160
|
+
const patron = secreto !== null && patrones.some((p) => p.method.toUpperCase() === method && patronReenviable(p.path) && coincidePatron(p.path, path));
|
|
161
|
+
if (!fija && !prefijo && !patron) return null;
|
|
162
|
+
return { destino: `${base}${path}${input.search ?? ""}` };
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ../shared/src/index.ts
|
|
168
|
+
var TEMPO = {
|
|
169
|
+
mainnet: {
|
|
170
|
+
chainId: 4217,
|
|
171
|
+
rpcUrl: "https://rpc.tempo.xyz",
|
|
172
|
+
explorerUrl: "https://explore.tempo.xyz"
|
|
173
|
+
},
|
|
174
|
+
testnet: {
|
|
175
|
+
chainId: 42431,
|
|
176
|
+
rpcUrl: "https://rpc.moderato.tempo.xyz",
|
|
177
|
+
explorerUrl: "https://explore.testnet.tempo.xyz"
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
var TOKENS = {
|
|
181
|
+
pathUsd: "0x20c0000000000000000000000000000000000000",
|
|
182
|
+
usdc: "0x20C000000000000000000000b9537d11c60E8b50"
|
|
183
|
+
};
|
|
184
|
+
var DEFAULT_CURRENCY = TOKENS.pathUsd;
|
|
185
|
+
var NETWORKS = {
|
|
186
|
+
arbitrum: {
|
|
187
|
+
id: "arbitrum",
|
|
188
|
+
label: "Arbitrum",
|
|
189
|
+
// USDC de Circle en Arbitrum Sepolia. En Arbitrum One vive en otra dirección,
|
|
190
|
+
// por eso mainnet queda en null hasta que el gateway opere allá.
|
|
191
|
+
token: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
|
|
192
|
+
tokenSymbol: "USDC",
|
|
193
|
+
decimals: 6,
|
|
194
|
+
// Leído on-chain: name() = "USD Coin", version() = "2" (distinto al USDC de Arc).
|
|
195
|
+
eip3009: { name: "USD Coin", version: "2" },
|
|
196
|
+
mainnet: null,
|
|
197
|
+
testnet: {
|
|
198
|
+
chainId: 421614,
|
|
199
|
+
rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
|
|
200
|
+
explorerUrl: "https://sepolia.arbiscan.io"
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"arbitrum-usdg": {
|
|
204
|
+
id: "arbitrum-usdg",
|
|
205
|
+
label: "Arbitrum \xB7 USDG",
|
|
206
|
+
// Mismo contrato y misma cadena que el riel USDC: cambia el stablecoin.
|
|
207
|
+
// Un riel es (cadena, token), y el saldo del negocio se lleva por token.
|
|
208
|
+
token: "0xFFC95faa3d63Cde504a05B567C600B78C0b41892",
|
|
209
|
+
tokenSymbol: "USDG",
|
|
210
|
+
decimals: 6,
|
|
211
|
+
eip3009: { name: "Global Dollar", version: "1" },
|
|
212
|
+
mainnet: null,
|
|
213
|
+
testnet: {
|
|
214
|
+
chainId: 421614,
|
|
215
|
+
rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
|
|
216
|
+
explorerUrl: "https://sepolia.arbiscan.io"
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
robinhood: {
|
|
220
|
+
id: "robinhood",
|
|
221
|
+
label: "Robinhood",
|
|
222
|
+
// USDG de Paxos en Robinhood Chain Testnet (el "USDC" de esa testnet es un
|
|
223
|
+
// mock sin EIP-3009). Leído on-chain: dominio "Global Dollar", versión "1".
|
|
224
|
+
token: "0x7E955252E15c84f5768B83c41a71F9eba181802F",
|
|
225
|
+
tokenSymbol: "USDG",
|
|
226
|
+
decimals: 6,
|
|
227
|
+
eip3009: { name: "Global Dollar", version: "1" },
|
|
228
|
+
mainnet: null,
|
|
229
|
+
testnet: {
|
|
230
|
+
chainId: 46630,
|
|
231
|
+
rpcUrl: "https://rpc.testnet.chain.robinhood.com",
|
|
232
|
+
explorerUrl: "https://explorer.testnet.chain.robinhood.com"
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
tempo: {
|
|
236
|
+
id: "tempo",
|
|
237
|
+
label: "Tempo",
|
|
238
|
+
token: TOKENS.pathUsd,
|
|
239
|
+
tokenSymbol: "pathUSD",
|
|
240
|
+
decimals: 6,
|
|
241
|
+
mainnet: TEMPO.mainnet,
|
|
242
|
+
testnet: TEMPO.testnet
|
|
243
|
+
},
|
|
244
|
+
arc: {
|
|
245
|
+
id: "arc",
|
|
246
|
+
label: "Arc",
|
|
247
|
+
// Interfaz ERC-20 del USDC nativo de Arc; el gas de la red se paga en ese mismo USDC.
|
|
248
|
+
token: "0x3600000000000000000000000000000000000000",
|
|
249
|
+
tokenSymbol: "USDC",
|
|
250
|
+
decimals: 6,
|
|
251
|
+
// Leído on-chain del contrato: name() = "USDC", version() = "2".
|
|
252
|
+
eip3009: { name: "USDC", version: "2" },
|
|
253
|
+
// Arc no publica direcciones de mainnet todavía (sept 2026).
|
|
254
|
+
mainnet: null,
|
|
255
|
+
testnet: {
|
|
256
|
+
chainId: 5042002,
|
|
257
|
+
rpcUrl: "https://rpc.testnet.arc.io",
|
|
258
|
+
explorerUrl: "https://testnet.arcscan.app"
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
var NETWORK_IDS = Object.keys(NETWORKS);
|
|
263
|
+
var CHAIN_LABELS = [
|
|
264
|
+
...new Map(NETWORK_IDS.map((id) => [NETWORKS[id].testnet.chainId, NETWORKS[id].label.split(" \xB7 ")[0] ?? NETWORKS[id].label])).values()
|
|
265
|
+
];
|
|
266
|
+
var GATEWAY_RAILS = {
|
|
267
|
+
"gateway-arbitrum": {
|
|
268
|
+
id: "gateway-arbitrum",
|
|
269
|
+
label: "Arbitrum \xB7 Nanopayments",
|
|
270
|
+
chainId: 421614,
|
|
271
|
+
rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
|
|
272
|
+
token: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
|
|
273
|
+
decimals: 6,
|
|
274
|
+
verifyingContract: "0x0077777d7EBA4688BDeF3E311b846F25870A19B9",
|
|
275
|
+
facilitatorUrl: "https://gateway-api-testnet.circle.com/v1/x402",
|
|
276
|
+
sdkChain: "arbitrumSepolia",
|
|
277
|
+
explorerUrl: "https://sepolia.arbiscan.io"
|
|
278
|
+
},
|
|
279
|
+
"gateway-arc": {
|
|
280
|
+
id: "gateway-arc",
|
|
281
|
+
label: "Arc \xB7 Nanopayments",
|
|
282
|
+
chainId: 5042002,
|
|
283
|
+
rpcUrl: "https://rpc.testnet.arc.network",
|
|
284
|
+
token: "0x3600000000000000000000000000000000000000",
|
|
285
|
+
decimals: 6,
|
|
286
|
+
verifyingContract: "0x0077777d7EBA4688BDeF3E311b846F25870A19B9",
|
|
287
|
+
facilitatorUrl: "https://gateway-api-testnet.circle.com/v1/x402",
|
|
288
|
+
sdkChain: "arcTestnet",
|
|
289
|
+
explorerUrl: "https://testnet.arcscan.app"
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
var GATEWAY_RAIL_IDS = Object.keys(GATEWAY_RAILS);
|
|
293
|
+
|
|
294
|
+
// src/proxy.ts
|
|
295
|
+
var import_server = require("next/server");
|
|
296
|
+
|
|
297
|
+
// src/index.ts
|
|
298
|
+
var GATEWAY_POR_DEFECTO = "https://api.usepeaje.com";
|
|
299
|
+
function baseDe({ slug, gateway }) {
|
|
300
|
+
const raiz = (gateway ?? process.env.PEAJE_GATEWAY_URL ?? GATEWAY_POR_DEFECTO).replace(/\/+$/, "");
|
|
301
|
+
return `${raiz}/${slug}`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// src/proxy.ts
|
|
305
|
+
var runtimes = /* @__PURE__ */ new Map();
|
|
306
|
+
function runtimeDe(base, secreto) {
|
|
307
|
+
const clave = `${base}|${secreto ? "con" : "sin"}`;
|
|
308
|
+
let r = runtimes.get(clave);
|
|
309
|
+
if (!r) {
|
|
310
|
+
r = crearProxyRuntime({
|
|
311
|
+
base,
|
|
312
|
+
secreto,
|
|
313
|
+
respaldo: { rutas: RUTAS_PROXY.map((x) => x.path), prefijos: RUTAS_DINAMICAS.map((x) => x.prefijo), patrones: [] }
|
|
314
|
+
});
|
|
315
|
+
runtimes.set(clave, r);
|
|
316
|
+
}
|
|
317
|
+
return r;
|
|
318
|
+
}
|
|
319
|
+
var avisado = false;
|
|
320
|
+
function peajeProxy(opciones, siguiente) {
|
|
321
|
+
if (!opciones?.slug) throw new Error("peajeProxy: `slug` is required (the business slug from your Peaje dashboard).");
|
|
322
|
+
const base = baseDe(opciones);
|
|
323
|
+
const secreto = opciones.secret ?? process.env.PEAJE_ORIGIN_SECRET ?? null;
|
|
324
|
+
return async (req, ev) => {
|
|
325
|
+
const runtime = runtimeDe(base, secreto);
|
|
326
|
+
if (!secreto && !avisado) {
|
|
327
|
+
avisado = true;
|
|
328
|
+
void runtime.sinSecreto().then((faltan) => {
|
|
329
|
+
if (faltan.length > 0) {
|
|
330
|
+
console.warn(
|
|
331
|
+
`[peaje] ${faltan.length} priced API route(s) are not charged on this domain because PEAJE_ORIGIN_SECRET is not set: ${faltan.map((p) => `${p.method} ${p.path}`).join(", ")}. Copy it from the Kit page of your Peaje dashboard into your hosting environment variables.`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
const decision = await runtime.decidir({
|
|
337
|
+
method: req.method,
|
|
338
|
+
pathname: req.nextUrl.pathname,
|
|
339
|
+
search: req.nextUrl.search,
|
|
340
|
+
headers: req.headers
|
|
341
|
+
});
|
|
342
|
+
if (decision) {
|
|
343
|
+
const headers = new Headers(req.headers);
|
|
344
|
+
headers.set("x-forwarded-host", req.nextUrl.host);
|
|
345
|
+
headers.set("x-forwarded-proto", req.nextUrl.protocol.replace(":", ""));
|
|
346
|
+
return import_server.NextResponse.rewrite(new URL(decision.destino), { request: { headers } });
|
|
347
|
+
}
|
|
348
|
+
return siguiente ? siguiente(req, ev) : import_server.NextResponse.next();
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
var proxy_default = peajeProxy;
|
|
352
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
353
|
+
0 && (module.exports = {
|
|
354
|
+
peajeProxy
|
|
355
|
+
});
|
package/dist/proxy.d.cts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { NextRequest, NextFetchEvent } from 'next/server';
|
|
2
|
+
import { OpcionesPeaje } from './index.cjs';
|
|
3
|
+
|
|
4
|
+
type Middleware = (req: NextRequest, ev: NextFetchEvent) => Response | undefined | void | Promise<Response | undefined | void>;
|
|
5
|
+
type OpcionesProxy = OpcionesPeaje & {
|
|
6
|
+
/** Por defecto `process.env.PEAJE_ORIGIN_SECRET`. */
|
|
7
|
+
secret?: string;
|
|
8
|
+
};
|
|
9
|
+
declare function peajeProxy(opciones: OpcionesProxy, siguiente?: Middleware): Middleware;
|
|
10
|
+
|
|
11
|
+
export { type OpcionesProxy, peajeProxy as default, peajeProxy };
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { NextRequest, NextFetchEvent } from 'next/server';
|
|
2
|
+
import { OpcionesPeaje } from './index.js';
|
|
3
|
+
|
|
4
|
+
type Middleware = (req: NextRequest, ev: NextFetchEvent) => Response | undefined | void | Promise<Response | undefined | void>;
|
|
5
|
+
type OpcionesProxy = OpcionesPeaje & {
|
|
6
|
+
/** Por defecto `process.env.PEAJE_ORIGIN_SECRET`. */
|
|
7
|
+
secret?: string;
|
|
8
|
+
};
|
|
9
|
+
declare function peajeProxy(opciones: OpcionesProxy, siguiente?: Middleware): Middleware;
|
|
10
|
+
|
|
11
|
+
export { type OpcionesProxy, peajeProxy as default, peajeProxy };
|
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RUTAS_DINAMICAS,
|
|
3
|
+
RUTAS_PROXY,
|
|
4
|
+
baseDe,
|
|
5
|
+
crearProxyRuntime
|
|
6
|
+
} from "./chunk-WRT76472.js";
|
|
7
|
+
|
|
8
|
+
// src/proxy.ts
|
|
9
|
+
import { NextResponse } from "next/server";
|
|
10
|
+
var runtimes = /* @__PURE__ */ new Map();
|
|
11
|
+
function runtimeDe(base, secreto) {
|
|
12
|
+
const clave = `${base}|${secreto ? "con" : "sin"}`;
|
|
13
|
+
let r = runtimes.get(clave);
|
|
14
|
+
if (!r) {
|
|
15
|
+
r = crearProxyRuntime({
|
|
16
|
+
base,
|
|
17
|
+
secreto,
|
|
18
|
+
respaldo: { rutas: RUTAS_PROXY.map((x) => x.path), prefijos: RUTAS_DINAMICAS.map((x) => x.prefijo), patrones: [] }
|
|
19
|
+
});
|
|
20
|
+
runtimes.set(clave, r);
|
|
21
|
+
}
|
|
22
|
+
return r;
|
|
23
|
+
}
|
|
24
|
+
var avisado = false;
|
|
25
|
+
function peajeProxy(opciones, siguiente) {
|
|
26
|
+
if (!opciones?.slug) throw new Error("peajeProxy: `slug` is required (the business slug from your Peaje dashboard).");
|
|
27
|
+
const base = baseDe(opciones);
|
|
28
|
+
const secreto = opciones.secret ?? process.env.PEAJE_ORIGIN_SECRET ?? null;
|
|
29
|
+
return async (req, ev) => {
|
|
30
|
+
const runtime = runtimeDe(base, secreto);
|
|
31
|
+
if (!secreto && !avisado) {
|
|
32
|
+
avisado = true;
|
|
33
|
+
void runtime.sinSecreto().then((faltan) => {
|
|
34
|
+
if (faltan.length > 0) {
|
|
35
|
+
console.warn(
|
|
36
|
+
`[peaje] ${faltan.length} priced API route(s) are not charged on this domain because PEAJE_ORIGIN_SECRET is not set: ${faltan.map((p) => `${p.method} ${p.path}`).join(", ")}. Copy it from the Kit page of your Peaje dashboard into your hosting environment variables.`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const decision = await runtime.decidir({
|
|
42
|
+
method: req.method,
|
|
43
|
+
pathname: req.nextUrl.pathname,
|
|
44
|
+
search: req.nextUrl.search,
|
|
45
|
+
headers: req.headers
|
|
46
|
+
});
|
|
47
|
+
if (decision) {
|
|
48
|
+
const headers = new Headers(req.headers);
|
|
49
|
+
headers.set("x-forwarded-host", req.nextUrl.host);
|
|
50
|
+
headers.set("x-forwarded-proto", req.nextUrl.protocol.replace(":", ""));
|
|
51
|
+
return NextResponse.rewrite(new URL(decision.destino), { request: { headers } });
|
|
52
|
+
}
|
|
53
|
+
return siguiente ? siguiente(req, ev) : NextResponse.next();
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
var proxy_default = peajeProxy;
|
|
57
|
+
export {
|
|
58
|
+
proxy_default as default,
|
|
59
|
+
peajeProxy
|
|
60
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peaje/next",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Peaje for Next.js: proxy rewrites and the head block that make your site
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Peaje for Next.js: runtime proxy, rewrites and the head block that make your site answer 402 (MPP, x402), MCP and OpenAPI on your own domain.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -31,13 +31,18 @@
|
|
|
31
31
|
"types": "./dist/head.d.cts",
|
|
32
32
|
"default": "./dist/head.cjs"
|
|
33
33
|
}
|
|
34
|
+
},
|
|
35
|
+
"./proxy": {
|
|
36
|
+
"import": {
|
|
37
|
+
"types": "./dist/proxy.d.ts",
|
|
38
|
+
"default": "./dist/proxy.js"
|
|
39
|
+
},
|
|
40
|
+
"require": {
|
|
41
|
+
"types": "./dist/proxy.d.cts",
|
|
42
|
+
"default": "./dist/proxy.cjs"
|
|
43
|
+
}
|
|
34
44
|
}
|
|
35
45
|
},
|
|
36
|
-
"scripts": {
|
|
37
|
-
"build": "tsup",
|
|
38
|
-
"typecheck": "tsc --noEmit",
|
|
39
|
-
"test": "tsx --test test/*.test.ts"
|
|
40
|
-
},
|
|
41
46
|
"keywords": [
|
|
42
47
|
"peaje",
|
|
43
48
|
"next",
|
|
@@ -59,13 +64,21 @@
|
|
|
59
64
|
}
|
|
60
65
|
},
|
|
61
66
|
"devDependencies": {
|
|
62
|
-
"@peaje/shared": "workspace:*",
|
|
63
67
|
"@types/node": "^20",
|
|
64
68
|
"@types/react": "^19",
|
|
65
69
|
"next": "16.3.1",
|
|
66
70
|
"react": "19.2.8",
|
|
67
71
|
"tsup": "^8.5.1",
|
|
68
72
|
"tsx": "^4.23.12",
|
|
69
|
-
"typescript": "^5.9.0"
|
|
73
|
+
"typescript": "^5.9.0",
|
|
74
|
+
"@peaje/shared": "0.0.0"
|
|
75
|
+
},
|
|
76
|
+
"publishConfig": {
|
|
77
|
+
"access": "public"
|
|
78
|
+
},
|
|
79
|
+
"scripts": {
|
|
80
|
+
"build": "tsup",
|
|
81
|
+
"typecheck": "tsc --noEmit",
|
|
82
|
+
"test": "tsx --test test/*.test.ts"
|
|
70
83
|
}
|
|
71
|
-
}
|
|
84
|
+
}
|