@cryptonly/x402 0.1.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -2
- package/dist/client.d.ts +15 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +20 -5
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +174 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +281 -0
- package/dist/config.js.map +1 -0
- package/dist/express.d.ts +100 -0
- package/dist/express.d.ts.map +1 -0
- package/dist/express.js +213 -0
- package/dist/express.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/package.json +51 -5
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
Facilitator client for x402 payments.
|
|
8
8
|
|
|
9
|
-
[Website](https://cryptonly.net) · [Documentation](https://cryptonly.
|
|
9
|
+
[Website](https://cryptonly.net) · [Documentation](https://docs.cryptonly.net/api-reference/openapi/operations/x402supported/) · [Contact](https://cryptonly.net/contact/)
|
|
10
10
|
|
|
11
11
|
</div>
|
|
12
12
|
|
|
@@ -35,10 +35,105 @@ const server = new x402ResourceServer(facilitator).register(
|
|
|
35
35
|
);
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
## Options
|
|
39
|
+
|
|
40
|
+
| Option | Default | |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| `apiKey` | required | Your Cryptonly API key, with the `x402.verify` and `x402.settle` scopes. Sent as `x-tenant-api-key` on every facilitator call. |
|
|
43
|
+
| `sandbox` | `false` | Use the sandbox facilitator, which settles on Base Sepolia (`eip155:84532`). |
|
|
44
|
+
| `baseUrl` | — | Merchant API origin, for example a local API. `/x402` is appended. Takes precedence over `sandbox`. |
|
|
39
45
|
|
|
40
46
|
```ts
|
|
47
|
+
// Sandbox: Base Sepolia, with test USDC from the Circle faucet.
|
|
41
48
|
const facilitator = new CryptonlyFacilitatorClient({
|
|
42
49
|
apiKey: process.env.CRYPTONLY_API_KEY!,
|
|
50
|
+
sandbox: true,
|
|
43
51
|
});
|
|
52
|
+
const server = new x402ResourceServer(facilitator).register(
|
|
53
|
+
'eip155:84532',
|
|
54
|
+
new ExactEvmScheme(),
|
|
55
|
+
);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Dashboard-configured routes
|
|
59
|
+
|
|
60
|
+
`@cryptonly/x402/express` reads your paid resources, prices and payout address from the Cryptonly dashboard, so you do not write routes in code. A price change in the dashboard reaches production within a minute, without a deploy.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm install @cryptonly/x402 @x402/core @x402/express @x402/evm express
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import express from 'express';
|
|
68
|
+
import { cryptonlyPaymentMiddleware } from '@cryptonly/x402/express';
|
|
69
|
+
|
|
70
|
+
const app = express();
|
|
71
|
+
|
|
72
|
+
app.get('/health', (_req, res) => res.send('ok')); // free routes first
|
|
73
|
+
|
|
74
|
+
const payments = cryptonlyPaymentMiddleware({
|
|
75
|
+
apiKey: process.env.CRYPTONLY_API_KEY!, // scopes x402.verify and x402.settle
|
|
76
|
+
});
|
|
77
|
+
app.use(payments);
|
|
78
|
+
|
|
79
|
+
// Priced in the dashboard as `GET /weather/:city`.
|
|
80
|
+
app.get('/weather/:city', (req, res) => {
|
|
81
|
+
res.json({ city: req.params.city, temperatureC: 21 });
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await payments.ready(); // optional: fail at startup if the config cannot load
|
|
85
|
+
app.listen(4021);
|
|
44
86
|
```
|
|
87
|
+
|
|
88
|
+
How it works:
|
|
89
|
+
|
|
90
|
+
- It loads `GET /x402/config` with your API key on the first request (or on `ready()`), then every minute.
|
|
91
|
+
- It turns each resource into a stock `@x402/express` route: `METHOD /path` (or just `/path` for method `*`), the price in atomic units of the chosen asset, your `payTo`, and the token's EIP-712 domain in `extra`. A Permit2-only token gets `assetTransferMethod: 'permit2'` and EIP-2612 gas sponsoring.
|
|
92
|
+
- Resources marked discoverable get the bazaar discovery extension, with their input and output schemas, when `@x402/extensions` is installed.
|
|
93
|
+
- It rebuilds the stock middleware only when the routes change.
|
|
94
|
+
|
|
95
|
+
It fails closed, so paid endpoints are never served for free:
|
|
96
|
+
|
|
97
|
+
- If the first load fails, every request through the middleware answers `503` with `Retry-After` until a load succeeds. This covers a bad key, an account that is not active, or no payout address. Mount the middleware after your free routes.
|
|
98
|
+
- If a refresh fails, the last good routes stay in use and a warning is logged.
|
|
99
|
+
|
|
100
|
+
| Option | Default | |
|
|
101
|
+
| --- | --- | --- |
|
|
102
|
+
| `apiKey` | required | Same key as for `CryptonlyFacilitatorClient`. It also needs `x402.verify` for `GET /x402/config`. |
|
|
103
|
+
| `sandbox`, `baseUrl` | production | As for `CryptonlyFacilitatorClient`. |
|
|
104
|
+
| `refreshIntervalMs` | `60000` | How often to reload the configuration. `0` turns refreshing off. |
|
|
105
|
+
| `schemes` | `[new ExactEvmScheme()]` | Scheme servers registered on the account's Base network. Resources with another scheme are skipped. |
|
|
106
|
+
| `svmSchemes` | `[new ExactSvmScheme()]` when `@x402/svm` is installed, else none | Scheme servers registered on the account's Solana network. Without one, Solana-priced resources are skipped. |
|
|
107
|
+
| `paywallConfig` | — | Passed to the stock `paymentMiddleware`. |
|
|
108
|
+
| `logger` | `console` | Receives refresh failures and skipped resources. |
|
|
109
|
+
| `createMiddleware` | stock `paymentMiddleware` | Builds the inner middleware for a set of routes, for example to add hooks. |
|
|
110
|
+
|
|
111
|
+
The returned middleware also has these methods:
|
|
112
|
+
|
|
113
|
+
- `ready()`: load now.
|
|
114
|
+
- `refresh()`: reload now. It resolves `true` when the routes changed.
|
|
115
|
+
- `routes()`: the routes in use.
|
|
116
|
+
- `close()`: stop refreshing. The refresh timer is `unref()`ed and never keeps the process alive.
|
|
117
|
+
|
|
118
|
+
For another framework, build the routes yourself with `fetchX402Config()` and `routesFromConfig()` from `@cryptonly/x402`, and pass them to its stock x402 middleware.
|
|
119
|
+
|
|
120
|
+
### Discovery
|
|
121
|
+
|
|
122
|
+
`CryptonlyFacilitatorClient` also sends the key on bazaar calls:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { withBazaar } from '@x402/extensions/bazaar';
|
|
126
|
+
|
|
127
|
+
const client = withBazaar(new CryptonlyFacilitatorClient({ apiKey }));
|
|
128
|
+
const { items } = await client.extensions.bazaar.listResources();
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Networks and assets
|
|
132
|
+
|
|
133
|
+
Production settles on Base (`eip155:8453`) and Solana (`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp`); sandbox on Base Sepolia (`eip155:84532`) and Solana Devnet (`solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1`). On Base, USDC and EURC settle with EIP-3009, and allowlisted tokens also through Permit2. On Solana, USDC and USDT settle with the `exact` SVM scheme: the buyer signs a token transfer and Cryptonly co-signs it as fee payer (`extra.feePayer` in `GET /x402/supported`). The buyer never needs ETH or SOL. `GET /x402/supported` lists what the facilitator accepts.
|
|
134
|
+
|
|
135
|
+
To take Solana payments with `cryptonlyPaymentMiddleware`, install `@x402/svm` and `@solana/kit` next to it and price resources in a Solana asset in the dashboard. Payouts are set up per network, so the account also needs a Solana `payTo`.
|
|
136
|
+
|
|
137
|
+
## Settlement results
|
|
138
|
+
|
|
139
|
+
USDC goes straight from the buyer to your `payTo` address; Cryptonly never holds it. If the transaction has not mined when `/settle` answers, the answer is `settlement_pending` with the transaction hash. List your settlements, including their final status, with `GET /x402/settlements` (scope `x402.list`) or `client.x402Settlements.list()` in `@cryptonly/sdk`.
|
package/dist/client.d.ts
CHANGED
|
@@ -5,15 +5,29 @@ export type CryptonlyFacilitatorConfig = {
|
|
|
5
5
|
* Cryptonly API key.
|
|
6
6
|
*/
|
|
7
7
|
apiKey: string;
|
|
8
|
+
/**
|
|
9
|
+
* Merchant API origin, with no path (as `baseUrl` in `@cryptonly/sdk`).
|
|
10
|
+
* `/x402` is appended. Takes precedence over {@link sandbox}.
|
|
11
|
+
*/
|
|
12
|
+
baseUrl?: string;
|
|
8
13
|
/**
|
|
9
14
|
* When `true`, uses the sandbox facilitator. Defaults to production.
|
|
15
|
+
* Ignored when {@link baseUrl} is set.
|
|
10
16
|
*/
|
|
11
17
|
sandbox?: boolean;
|
|
12
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* Merchant API origin for a config, with no trailing slash.
|
|
21
|
+
*
|
|
22
|
+
* Same priority as `new Cryptonly(...)`: `baseUrl`, then `sandbox`, then
|
|
23
|
+
* production.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveMerchantApiOrigin(config: Pick<CryptonlyFacilitatorConfig, 'baseUrl' | 'sandbox'>): string;
|
|
13
26
|
/**
|
|
14
27
|
* Cryptonly facilitator client for the x402 protocol.
|
|
15
28
|
*
|
|
16
|
-
* Extends `HTTPFacilitatorClient` from `@x402/core/server`.
|
|
29
|
+
* Extends `HTTPFacilitatorClient` from `@x402/core/server`. The API key goes
|
|
30
|
+
* on `/verify`, `/settle`, `/supported` and the bazaar discovery calls.
|
|
17
31
|
*
|
|
18
32
|
* @example
|
|
19
33
|
* ```ts
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAO1D,4DAA4D;AAC5D,MAAM,MAAM,0BAA0B,GAAG;IACvC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAO1D,4DAA4D;AAC5D,MAAM,MAAM,0BAA0B,GAAG;IACvC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,IAAI,CAAC,0BAA0B,EAAE,SAAS,GAAG,SAAS,CAAC,GAC9D,MAAM,CAQR;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,0BAA2B,SAAQ,qBAAqB;gBACvD,MAAM,EAAE,0BAA0B;CAgB/C"}
|
package/dist/client.js
CHANGED
|
@@ -1,18 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CryptonlyFacilitatorClient = void 0;
|
|
4
|
+
exports.resolveMerchantApiOrigin = resolveMerchantApiOrigin;
|
|
4
5
|
const server_1 = require("@x402/core/server");
|
|
5
6
|
const constants_js_1 = require("./constants.js");
|
|
7
|
+
/**
|
|
8
|
+
* Merchant API origin for a config, with no trailing slash.
|
|
9
|
+
*
|
|
10
|
+
* Same priority as `new Cryptonly(...)`: `baseUrl`, then `sandbox`, then
|
|
11
|
+
* production.
|
|
12
|
+
*/
|
|
13
|
+
function resolveMerchantApiOrigin(config) {
|
|
14
|
+
const origin = typeof config.baseUrl === 'string' && config.baseUrl.length > 0
|
|
15
|
+
? config.baseUrl
|
|
16
|
+
: config.sandbox
|
|
17
|
+
? constants_js_1.SANDBOX_MERCHANT_API_BASE_URL
|
|
18
|
+
: constants_js_1.MERCHANT_API_BASE_URL;
|
|
19
|
+
return origin.replace(/\/+$/, '');
|
|
20
|
+
}
|
|
6
21
|
function resolveFacilitatorUrl(config) {
|
|
7
|
-
|
|
8
|
-
? constants_js_1.SANDBOX_MERCHANT_API_BASE_URL
|
|
9
|
-
: constants_js_1.MERCHANT_API_BASE_URL;
|
|
10
|
-
return `${origin}/x402`;
|
|
22
|
+
return `${resolveMerchantApiOrigin(config)}/x402`;
|
|
11
23
|
}
|
|
12
24
|
/**
|
|
13
25
|
* Cryptonly facilitator client for the x402 protocol.
|
|
14
26
|
*
|
|
15
|
-
* Extends `HTTPFacilitatorClient` from `@x402/core/server`.
|
|
27
|
+
* Extends `HTTPFacilitatorClient` from `@x402/core/server`. The API key goes
|
|
28
|
+
* on `/verify`, `/settle`, `/supported` and the bazaar discovery calls.
|
|
16
29
|
*
|
|
17
30
|
* @example
|
|
18
31
|
* ```ts
|
|
@@ -38,6 +51,8 @@ class CryptonlyFacilitatorClient extends server_1.HTTPFacilitatorClient {
|
|
|
38
51
|
verify: headers,
|
|
39
52
|
settle: headers,
|
|
40
53
|
supported: headers,
|
|
54
|
+
// `withBazaar(client).extensions.bazaar.listResources()`.
|
|
55
|
+
bazaar: headers,
|
|
41
56
|
}),
|
|
42
57
|
});
|
|
43
58
|
}
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";;;AA+BA,4DAUC;AAzCD,8CAA0D;AAC1D,iDAIwB;AAoBxB;;;;;GAKG;AACH,SAAgB,wBAAwB,CACtC,MAA+D;IAE/D,MAAM,MAAM,GACV,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;QAC7D,CAAC,CAAC,MAAM,CAAC,OAAO;QAChB,CAAC,CAAC,MAAM,CAAC,OAAO;YACd,CAAC,CAAC,4CAA6B;YAC/B,CAAC,CAAC,oCAAqB,CAAC;IAC9B,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAkC;IAC/D,OAAO,GAAG,wBAAwB,CAAC,MAAM,CAAC,OAAO,CAAC;AACpD,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAa,0BAA2B,SAAQ,8BAAqB;IACnE,YAAY,MAAkC;QAC5C,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;YACpB,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAC;QAC1E,CAAC;QACD,MAAM,OAAO,GAAG,EAAE,CAAC,sCAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;QAC7D,KAAK,CAAC;YACJ,GAAG,EAAE,qBAAqB,CAAC,MAAM,CAAC;YAClC,iBAAiB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;gBAC9B,MAAM,EAAE,OAAO;gBACf,MAAM,EAAE,OAAO;gBACf,SAAS,EAAE,OAAO;gBAClB,0DAA0D;gBAC1D,MAAM,EAAE,OAAO;aAChB,CAAC;SACH,CAAC,CAAC;IACL,CAAC;CACF;AAjBD,gEAiBC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { RouteConfig } from '@x402/core/server';
|
|
2
|
+
import type { Network } from '@x402/core/types';
|
|
3
|
+
import { type CryptonlyFacilitatorConfig } from './client.js';
|
|
4
|
+
/**
|
|
5
|
+
* `account.status` in `GET /x402/config`. Only `active` can be paid: x402 is
|
|
6
|
+
* on for every account unless Cryptonly suspended it or production still
|
|
7
|
+
* needs the business verification.
|
|
8
|
+
*/
|
|
9
|
+
export type X402AccountStatus = 'active' | 'suspended' | 'tenant_not_verified';
|
|
10
|
+
/** An asset the account accepts, from `GET /x402/config`. */
|
|
11
|
+
export type X402ConfigAsset = {
|
|
12
|
+
symbol: string;
|
|
13
|
+
currencyCode: string;
|
|
14
|
+
/** Token contract address (EVM) or SPL mint (Solana, base58). */
|
|
15
|
+
address: string;
|
|
16
|
+
decimals: number;
|
|
17
|
+
/**
|
|
18
|
+
* Transfer methods the facilitator settles for it: `eip3009`, `permit2`
|
|
19
|
+
* (EVM) or `spl` (Solana).
|
|
20
|
+
*/
|
|
21
|
+
methods: string[];
|
|
22
|
+
/** Smallest price, in atomic units. */
|
|
23
|
+
minAmount: string;
|
|
24
|
+
/** CAIP-2 network of the asset. Older APIs omit it: {@link X402Config.network}. */
|
|
25
|
+
network?: Network;
|
|
26
|
+
/** `payTo` on the asset's network. Older APIs omit it: {@link X402Config.payTo}. */
|
|
27
|
+
payTo?: string | null;
|
|
28
|
+
/** EVM: EIP-712 domain of the token; goes into `extra`. */
|
|
29
|
+
eip712?: {
|
|
30
|
+
name: string;
|
|
31
|
+
version: string;
|
|
32
|
+
};
|
|
33
|
+
/** Solana: the facilitator's fee payer; goes into `extra.feePayer`. */
|
|
34
|
+
feePayer?: string;
|
|
35
|
+
};
|
|
36
|
+
/** A paid resource configured in the dashboard. */
|
|
37
|
+
export type X402ConfigResource = {
|
|
38
|
+
id: string;
|
|
39
|
+
/** HTTP method, or `*` for any method. */
|
|
40
|
+
method: string;
|
|
41
|
+
/** Full URL as entered in the dashboard; may hold `:param` segments. */
|
|
42
|
+
url: string;
|
|
43
|
+
/** Path of {@link url}, the route pattern (`/weather/:city`). */
|
|
44
|
+
path: string;
|
|
45
|
+
price: {
|
|
46
|
+
/** Atomic units. */
|
|
47
|
+
amount: string;
|
|
48
|
+
/** Token contract address or SPL mint. */
|
|
49
|
+
asset: string;
|
|
50
|
+
currencyCode: string;
|
|
51
|
+
/** CAIP-2 network of the asset. */
|
|
52
|
+
network?: Network;
|
|
53
|
+
};
|
|
54
|
+
scheme: string;
|
|
55
|
+
maxTimeoutSeconds: number;
|
|
56
|
+
description: string | null;
|
|
57
|
+
mimeType: string | null;
|
|
58
|
+
/** Listed in the public discovery feed (bazaar). */
|
|
59
|
+
discoverable: boolean;
|
|
60
|
+
/** JSON Schema of the query (GET/HEAD/DELETE) or the JSON body. */
|
|
61
|
+
inputSchema: Record<string, unknown> | null;
|
|
62
|
+
outputSchema: Record<string, unknown> | null;
|
|
63
|
+
};
|
|
64
|
+
/** Response of `GET /x402/config`: the x402 setup of the key's account. */
|
|
65
|
+
export type X402Config = {
|
|
66
|
+
account: {
|
|
67
|
+
id: string;
|
|
68
|
+
name: string;
|
|
69
|
+
status: X402AccountStatus;
|
|
70
|
+
};
|
|
71
|
+
environment: 'production' | 'sandbox';
|
|
72
|
+
/** CAIP-2 network of Base, such as `eip155:84532`. */
|
|
73
|
+
network: Network;
|
|
74
|
+
/** `payTo` on {@link network}. `balance` (Cryptonly balance) is Base only. */
|
|
75
|
+
payTo: {
|
|
76
|
+
mode: 'wallet' | 'balance';
|
|
77
|
+
address: string | null;
|
|
78
|
+
};
|
|
79
|
+
/** Every network the account can be paid on, with its `payTo`. */
|
|
80
|
+
networks?: Array<{
|
|
81
|
+
network: Network;
|
|
82
|
+
payTo: {
|
|
83
|
+
mode: 'wallet' | 'balance';
|
|
84
|
+
address: string | null;
|
|
85
|
+
};
|
|
86
|
+
}>;
|
|
87
|
+
assets: X402ConfigAsset[];
|
|
88
|
+
resources: X402ConfigResource[];
|
|
89
|
+
fees: Record<string, unknown>;
|
|
90
|
+
limits: {
|
|
91
|
+
dailyGasUsd: number;
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
/** Why {@link fetchX402Config} or {@link routesFromConfig} failed. */
|
|
95
|
+
export type X402ConfigErrorCode = 'network_error' | 'http_error' | 'invalid_response' | 'account_not_active' | 'pay_to_missing';
|
|
96
|
+
/** Thrown by {@link fetchX402Config} and {@link routesFromConfig}. */
|
|
97
|
+
export declare class X402ConfigError extends Error {
|
|
98
|
+
readonly code: X402ConfigErrorCode;
|
|
99
|
+
/** HTTP status, for `http_error`. */
|
|
100
|
+
readonly status?: number;
|
|
101
|
+
constructor(code: X402ConfigErrorCode, message: string, options?: {
|
|
102
|
+
status?: number;
|
|
103
|
+
cause?: unknown;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/** Options for {@link fetchX402Config}. */
|
|
107
|
+
export type FetchX402ConfigOptions = CryptonlyFacilitatorConfig & {
|
|
108
|
+
/** `fetch` implementation. Defaults to the global `fetch`. */
|
|
109
|
+
fetch?: typeof fetch;
|
|
110
|
+
/** Request timeout. Defaults to 10 s. */
|
|
111
|
+
timeoutMs?: number;
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Loads `GET <base>/x402/config` with the API key (scope `x402.verify`).
|
|
115
|
+
*
|
|
116
|
+
* @throws {X402ConfigError} `network_error`, `http_error` or `invalid_response`.
|
|
117
|
+
*/
|
|
118
|
+
export declare function fetchX402Config(options: FetchX402ConfigOptions): Promise<X402Config>;
|
|
119
|
+
/**
|
|
120
|
+
* Checks the shape of a `GET /x402/config` body.
|
|
121
|
+
*
|
|
122
|
+
* @throws {X402ConfigError} `invalid_response`.
|
|
123
|
+
*/
|
|
124
|
+
export declare function parseX402Config(body: unknown): X402Config;
|
|
125
|
+
/**
|
|
126
|
+
* The parts of `@x402/extensions` that {@link routesFromConfig} uses.
|
|
127
|
+
* Typed structurally so that the package stays optional.
|
|
128
|
+
*/
|
|
129
|
+
export type X402ExtensionsModule = {
|
|
130
|
+
declareDiscoveryExtension(config: {
|
|
131
|
+
inputSchema?: Record<string, unknown>;
|
|
132
|
+
bodyType?: 'json';
|
|
133
|
+
}): Record<string, unknown>;
|
|
134
|
+
declareEip2612GasSponsoringExtension?(): Record<string, unknown>;
|
|
135
|
+
};
|
|
136
|
+
/** `@x402/extensions` when installed, else `null`. */
|
|
137
|
+
export declare function loadX402Extensions(): X402ExtensionsModule | null;
|
|
138
|
+
/** Options for {@link routesFromConfig}. */
|
|
139
|
+
export type RoutesFromConfigOptions = {
|
|
140
|
+
/**
|
|
141
|
+
* Schemes the resource server has registered. Resources with another
|
|
142
|
+
* scheme are skipped (a 402 for them could not be verified). Default: all.
|
|
143
|
+
*/
|
|
144
|
+
schemes?: readonly string[];
|
|
145
|
+
/**
|
|
146
|
+
* `@x402/extensions`, for the bazaar discovery and EIP-2612 extensions.
|
|
147
|
+
* Default: loaded when installed. `null` leaves extensions out.
|
|
148
|
+
*/
|
|
149
|
+
extensions?: X402ExtensionsModule | null;
|
|
150
|
+
/** Called for each resource left out, with the reason. */
|
|
151
|
+
onSkip?: (resource: X402ConfigResource, reason: string) => void;
|
|
152
|
+
};
|
|
153
|
+
/** Route key of a resource: `METHOD /path`, or just `/path` for method `*`. */
|
|
154
|
+
export declare function routeKey(resource: Pick<X402ConfigResource, 'method' | 'path'>): string;
|
|
155
|
+
/** Solana CAIP-2 networks (`solana:<genesis>`). */
|
|
156
|
+
export declare function isSolanaNetwork(network: string): boolean;
|
|
157
|
+
/**
|
|
158
|
+
* Throws unless payments to this account can settle: the account is
|
|
159
|
+
* `active` and has a `payTo` address on at least one network.
|
|
160
|
+
*
|
|
161
|
+
* @throws {X402ConfigError} `account_not_active` or `pay_to_missing`.
|
|
162
|
+
*/
|
|
163
|
+
export declare function assertPayableConfig(config: X402Config): void;
|
|
164
|
+
/**
|
|
165
|
+
* Builds stock `@x402/core` routes (for `paymentMiddleware` and
|
|
166
|
+
* `x402HTTPResourceServer`) from the dashboard configuration.
|
|
167
|
+
*
|
|
168
|
+
* Fails closed: an account that cannot be paid throws instead of yielding
|
|
169
|
+
* no routes, since no routes would serve paid endpoints for free.
|
|
170
|
+
*
|
|
171
|
+
* @throws {X402ConfigError} `account_not_active` or `pay_to_missing`.
|
|
172
|
+
*/
|
|
173
|
+
export declare function routesFromConfig(config: X402Config, options?: RoutesFromConfigOptions): Record<string, RouteConfig>;
|
|
174
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.X402ConfigError = void 0;
|
|
4
|
+
exports.fetchX402Config = fetchX402Config;
|
|
5
|
+
exports.parseX402Config = parseX402Config;
|
|
6
|
+
exports.loadX402Extensions = loadX402Extensions;
|
|
7
|
+
exports.routeKey = routeKey;
|
|
8
|
+
exports.isSolanaNetwork = isSolanaNetwork;
|
|
9
|
+
exports.assertPayableConfig = assertPayableConfig;
|
|
10
|
+
exports.routesFromConfig = routesFromConfig;
|
|
11
|
+
const client_js_1 = require("./client.js");
|
|
12
|
+
const constants_js_1 = require("./constants.js");
|
|
13
|
+
/** Thrown by {@link fetchX402Config} and {@link routesFromConfig}. */
|
|
14
|
+
class X402ConfigError extends Error {
|
|
15
|
+
code;
|
|
16
|
+
/** HTTP status, for `http_error`. */
|
|
17
|
+
status;
|
|
18
|
+
constructor(code, message, options) {
|
|
19
|
+
super(message, options?.cause === undefined ? undefined : { cause: options.cause });
|
|
20
|
+
this.name = 'X402ConfigError';
|
|
21
|
+
this.code = code;
|
|
22
|
+
if (options?.status !== undefined)
|
|
23
|
+
this.status = options.status;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.X402ConfigError = X402ConfigError;
|
|
27
|
+
const DEFAULT_CONFIG_TIMEOUT_MS = 10_000;
|
|
28
|
+
/**
|
|
29
|
+
* Loads `GET <base>/x402/config` with the API key (scope `x402.verify`).
|
|
30
|
+
*
|
|
31
|
+
* @throws {X402ConfigError} `network_error`, `http_error` or `invalid_response`.
|
|
32
|
+
*/
|
|
33
|
+
async function fetchX402Config(options) {
|
|
34
|
+
if (!options?.apiKey) {
|
|
35
|
+
throw new TypeError('fetchX402Config: `apiKey` is required');
|
|
36
|
+
}
|
|
37
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
38
|
+
if (typeof fetchImpl !== 'function') {
|
|
39
|
+
throw new TypeError('fetchX402Config: no global `fetch`; pass `fetch`');
|
|
40
|
+
}
|
|
41
|
+
const url = `${(0, client_js_1.resolveMerchantApiOrigin)(options)}/x402/config`;
|
|
42
|
+
let response;
|
|
43
|
+
try {
|
|
44
|
+
response = await fetchImpl(url, {
|
|
45
|
+
method: 'GET',
|
|
46
|
+
headers: {
|
|
47
|
+
accept: 'application/json',
|
|
48
|
+
[constants_js_1.MERCHANT_API_KEY_HEADER]: options.apiKey,
|
|
49
|
+
},
|
|
50
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_CONFIG_TIMEOUT_MS),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (cause) {
|
|
54
|
+
throw new X402ConfigError('network_error', `GET ${url} failed: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
55
|
+
}
|
|
56
|
+
const text = await response.text().catch(() => '');
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
throw new X402ConfigError('http_error', `GET ${url} answered ${response.status}${describeErrorBody(text)}`, { status: response.status });
|
|
59
|
+
}
|
|
60
|
+
let body;
|
|
61
|
+
try {
|
|
62
|
+
body = JSON.parse(text);
|
|
63
|
+
}
|
|
64
|
+
catch (cause) {
|
|
65
|
+
throw new X402ConfigError('invalid_response', `GET ${url} returned non-JSON`, {
|
|
66
|
+
cause,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return parseX402Config(body);
|
|
70
|
+
}
|
|
71
|
+
function describeErrorBody(text) {
|
|
72
|
+
if (!text)
|
|
73
|
+
return '';
|
|
74
|
+
try {
|
|
75
|
+
const parsed = JSON.parse(text);
|
|
76
|
+
const message = Array.isArray(parsed.message) ? parsed.message.join('; ') : parsed.message;
|
|
77
|
+
const parts = [parsed.code, message].filter((part) => typeof part === 'string' && part.length > 0);
|
|
78
|
+
if (parts.length > 0)
|
|
79
|
+
return `: ${parts.join(': ')}`;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Not JSON; fall through.
|
|
83
|
+
}
|
|
84
|
+
return `: ${text.slice(0, 200)}`;
|
|
85
|
+
}
|
|
86
|
+
function isObject(value) {
|
|
87
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Checks the shape of a `GET /x402/config` body.
|
|
91
|
+
*
|
|
92
|
+
* @throws {X402ConfigError} `invalid_response`.
|
|
93
|
+
*/
|
|
94
|
+
function parseX402Config(body) {
|
|
95
|
+
const invalid = (what) => new X402ConfigError('invalid_response', `x402 config: ${what}`);
|
|
96
|
+
if (!isObject(body))
|
|
97
|
+
throw invalid('not an object');
|
|
98
|
+
const { account, network, payTo, assets, resources } = body;
|
|
99
|
+
if (!isObject(account) || typeof account.status !== 'string') {
|
|
100
|
+
throw invalid('`account.status` missing');
|
|
101
|
+
}
|
|
102
|
+
if (typeof network !== 'string' || !/^[^:\s]+:\S+$/.test(network)) {
|
|
103
|
+
throw invalid('`network` is not a CAIP-2 id');
|
|
104
|
+
}
|
|
105
|
+
if (!isObject(payTo) || !(payTo.address === null || typeof payTo.address === 'string')) {
|
|
106
|
+
throw invalid('`payTo` missing');
|
|
107
|
+
}
|
|
108
|
+
if (!Array.isArray(assets))
|
|
109
|
+
throw invalid('`assets` is not an array');
|
|
110
|
+
if (!Array.isArray(resources))
|
|
111
|
+
throw invalid('`resources` is not an array');
|
|
112
|
+
for (const resource of resources) {
|
|
113
|
+
if (!isObject(resource) ||
|
|
114
|
+
typeof resource.method !== 'string' ||
|
|
115
|
+
typeof resource.path !== 'string' ||
|
|
116
|
+
!isObject(resource.price) ||
|
|
117
|
+
typeof resource.price.amount !== 'string' ||
|
|
118
|
+
typeof resource.price.asset !== 'string' ||
|
|
119
|
+
typeof resource.scheme !== 'string') {
|
|
120
|
+
throw invalid('a resource lacks `method`, `path`, `price` or `scheme`');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return body;
|
|
124
|
+
}
|
|
125
|
+
let extensionsModule;
|
|
126
|
+
/** `@x402/extensions` when installed, else `null`. */
|
|
127
|
+
function loadX402Extensions() {
|
|
128
|
+
if (extensionsModule === undefined) {
|
|
129
|
+
try {
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
131
|
+
extensionsModule = require('@x402/extensions');
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
extensionsModule = null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return extensionsModule;
|
|
138
|
+
}
|
|
139
|
+
const BODY_METHODS = new Set(['POST', 'PUT', 'PATCH']);
|
|
140
|
+
/** Route key of a resource: `METHOD /path`, or just `/path` for method `*`. */
|
|
141
|
+
function routeKey(resource) {
|
|
142
|
+
const method = resource.method.trim().toUpperCase();
|
|
143
|
+
return method === '*' || method === '' ? resource.path : `${method} ${resource.path}`;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Fits a dashboard resource's declaration (`{ bazaar: { info, schema } }`) to
|
|
147
|
+
* what the dashboard knows: the method and schemas, but no example values.
|
|
148
|
+
*
|
|
149
|
+
* - `declareDiscoveryExtension` puts an empty `queryParams` or `body` example
|
|
150
|
+
* in `info`, which fails the merchant's schema when it requires fields, so
|
|
151
|
+
* the example goes and the body stops being required.
|
|
152
|
+
* - It also drops `output` unless an example is given, so the output schema
|
|
153
|
+
* is added here, under `output.example` as the package declares it.
|
|
154
|
+
*/
|
|
155
|
+
function withDashboardSchemas(method, declared, outputSchema) {
|
|
156
|
+
const bazaar = declared.bazaar;
|
|
157
|
+
const input = bazaar?.info?.input;
|
|
158
|
+
const properties = bazaar?.schema?.properties;
|
|
159
|
+
if (!bazaar?.info || !input || !properties)
|
|
160
|
+
return declared;
|
|
161
|
+
// Known here; the bazaar server extension would add it per request.
|
|
162
|
+
input.method = method;
|
|
163
|
+
delete input.queryParams;
|
|
164
|
+
delete input.body;
|
|
165
|
+
if (properties.input?.required) {
|
|
166
|
+
properties.input.required = properties.input.required.filter((key) => key !== 'body');
|
|
167
|
+
}
|
|
168
|
+
if (outputSchema) {
|
|
169
|
+
bazaar.info.output = { type: 'json' };
|
|
170
|
+
properties.output = {
|
|
171
|
+
type: 'object',
|
|
172
|
+
properties: {
|
|
173
|
+
type: { type: 'string' },
|
|
174
|
+
example: { type: 'object', ...outputSchema },
|
|
175
|
+
},
|
|
176
|
+
required: ['type'],
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return declared;
|
|
180
|
+
}
|
|
181
|
+
/** Solana CAIP-2 networks (`solana:<genesis>`). */
|
|
182
|
+
function isSolanaNetwork(network) {
|
|
183
|
+
return network.startsWith('solana:');
|
|
184
|
+
}
|
|
185
|
+
/** EVM addresses compare case-insensitively; base58 never does. */
|
|
186
|
+
function sameAsset(network, a, b) {
|
|
187
|
+
return isSolanaNetwork(network) ? a === b : a.toLowerCase() === b.toLowerCase();
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Throws unless payments to this account can settle: the account is
|
|
191
|
+
* `active` and has a `payTo` address on at least one network.
|
|
192
|
+
*
|
|
193
|
+
* @throws {X402ConfigError} `account_not_active` or `pay_to_missing`.
|
|
194
|
+
*/
|
|
195
|
+
function assertPayableConfig(config) {
|
|
196
|
+
if (config.account.status !== 'active') {
|
|
197
|
+
throw new X402ConfigError('account_not_active', `x402 account "${config.account.name}" is ${config.account.status}; ` +
|
|
198
|
+
'see Settings › General in the dashboard');
|
|
199
|
+
}
|
|
200
|
+
const payable = config.payTo.address || (config.networks ?? []).some((network) => network.payTo.address);
|
|
201
|
+
if (!payable) {
|
|
202
|
+
throw new X402ConfigError('pay_to_missing', `x402 account "${config.account.name}" has no payTo address; ` +
|
|
203
|
+
'set a payout wallet in the dashboard');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Builds stock `@x402/core` routes (for `paymentMiddleware` and
|
|
208
|
+
* `x402HTTPResourceServer`) from the dashboard configuration.
|
|
209
|
+
*
|
|
210
|
+
* Fails closed: an account that cannot be paid throws instead of yielding
|
|
211
|
+
* no routes, since no routes would serve paid endpoints for free.
|
|
212
|
+
*
|
|
213
|
+
* @throws {X402ConfigError} `account_not_active` or `pay_to_missing`.
|
|
214
|
+
*/
|
|
215
|
+
function routesFromConfig(config, options = {}) {
|
|
216
|
+
assertPayableConfig(config);
|
|
217
|
+
const extensions = options.extensions === undefined ? loadX402Extensions() : options.extensions;
|
|
218
|
+
const skip = (resource, reason) => options.onSkip?.(resource, reason);
|
|
219
|
+
const routes = {};
|
|
220
|
+
for (const resource of config.resources) {
|
|
221
|
+
if (options.schemes && !options.schemes.includes(resource.scheme)) {
|
|
222
|
+
skip(resource, `scheme "${resource.scheme}" is not registered`);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const network = resource.price.network ?? config.network;
|
|
226
|
+
const asset = config.assets.find((candidate) => (candidate.network ?? config.network) === network &&
|
|
227
|
+
sameAsset(network, candidate.address, resource.price.asset));
|
|
228
|
+
if (!asset) {
|
|
229
|
+
skip(resource, `asset ${resource.price.asset} is not accepted`);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const payTo = asset.network ? asset.payTo : config.payTo.address;
|
|
233
|
+
if (!payTo) {
|
|
234
|
+
skip(resource, `no payTo on ${network}; set one in the dashboard`);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const key = routeKey(resource);
|
|
238
|
+
if (routes[key]) {
|
|
239
|
+
skip(resource, `duplicate route "${key}"`);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
const permit2Only = asset.methods.includes('permit2') && !asset.methods.includes('eip3009');
|
|
243
|
+
const method = resource.method.trim().toUpperCase();
|
|
244
|
+
const routeExtensions = {};
|
|
245
|
+
if (permit2Only && extensions?.declareEip2612GasSponsoringExtension) {
|
|
246
|
+
// A buyer without a Permit2 approval signs an EIP-2612 permit too.
|
|
247
|
+
Object.assign(routeExtensions, extensions.declareEip2612GasSponsoringExtension());
|
|
248
|
+
}
|
|
249
|
+
if (resource.discoverable && extensions) {
|
|
250
|
+
Object.assign(routeExtensions, withDashboardSchemas(method, extensions.declareDiscoveryExtension({
|
|
251
|
+
...(resource.inputSchema ? { inputSchema: resource.inputSchema } : {}),
|
|
252
|
+
...(BODY_METHODS.has(method) ? { bodyType: 'json' } : {}),
|
|
253
|
+
}), resource.outputSchema));
|
|
254
|
+
}
|
|
255
|
+
routes[key] = {
|
|
256
|
+
accepts: {
|
|
257
|
+
scheme: resource.scheme,
|
|
258
|
+
network: network,
|
|
259
|
+
payTo,
|
|
260
|
+
price: {
|
|
261
|
+
asset: resource.price.asset,
|
|
262
|
+
amount: resource.price.amount,
|
|
263
|
+
extra: isSolanaNetwork(network)
|
|
264
|
+
? { ...(asset.feePayer ? { feePayer: asset.feePayer } : {}) }
|
|
265
|
+
: {
|
|
266
|
+
name: asset.eip712?.name,
|
|
267
|
+
version: asset.eip712?.version,
|
|
268
|
+
...(permit2Only ? { assetTransferMethod: 'permit2' } : {}),
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
maxTimeoutSeconds: resource.maxTimeoutSeconds,
|
|
272
|
+
},
|
|
273
|
+
...(resource.description ? { description: resource.description } : {}),
|
|
274
|
+
...(resource.mimeType ? { mimeType: resource.mimeType } : {}),
|
|
275
|
+
...(resource.discoverable && config.account.name ? { serviceName: config.account.name } : {}),
|
|
276
|
+
...(Object.keys(routeExtensions).length > 0 ? { extensions: routeExtensions } : {}),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
return routes;
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
+
import type { RoutesConfig } from '@x402/core/server';
|
|
3
|
+
import { type PaywallConfig, type SchemeNetworkServer } from '@x402/express';
|
|
4
|
+
import { CryptonlyFacilitatorClient, type CryptonlyFacilitatorConfig } from './client.js';
|
|
5
|
+
import { type X402Config, type X402ExtensionsModule } from './config.js';
|
|
6
|
+
/** An Express middleware. */
|
|
7
|
+
export type ExpressMiddleware = (req: Request, res: Response, next: NextFunction) => void | Promise<void>;
|
|
8
|
+
/** What {@link InnerMiddlewareFactory} gets besides the routes. */
|
|
9
|
+
export type InnerMiddlewareContext = {
|
|
10
|
+
config: X402Config;
|
|
11
|
+
facilitator: CryptonlyFacilitatorClient;
|
|
12
|
+
/** Registered on the account's EVM network. */
|
|
13
|
+
schemes: readonly SchemeNetworkServer[];
|
|
14
|
+
/** Registered on each Solana network of the account. */
|
|
15
|
+
svmSchemes: readonly SchemeNetworkServer[];
|
|
16
|
+
paywallConfig?: PaywallConfig;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Builds the payment middleware for one set of routes. The default is the
|
|
20
|
+
* stock `paymentMiddleware` from `@x402/express` over a fresh
|
|
21
|
+
* `x402ResourceServer`; replace it to add hooks, or in tests.
|
|
22
|
+
*/
|
|
23
|
+
export type InnerMiddlewareFactory = (routes: RoutesConfig, context: InnerMiddlewareContext) => ExpressMiddleware;
|
|
24
|
+
/** Logger used for refresh failures and skipped resources. */
|
|
25
|
+
export type CryptonlyMiddlewareLogger = Pick<Console, 'info' | 'warn' | 'error'>;
|
|
26
|
+
/** Options for {@link cryptonlyPaymentMiddleware}. */
|
|
27
|
+
export type CryptonlyPaymentMiddlewareOptions = CryptonlyFacilitatorConfig & {
|
|
28
|
+
/**
|
|
29
|
+
* How often to reload `GET /x402/config`. Defaults to 60 s; `0` turns
|
|
30
|
+
* refreshing off.
|
|
31
|
+
*/
|
|
32
|
+
refreshIntervalMs?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Scheme servers registered on the account's network. Defaults to
|
|
35
|
+
* `[new ExactEvmScheme()]`. Resources with another scheme are skipped.
|
|
36
|
+
*/
|
|
37
|
+
schemes?: readonly SchemeNetworkServer[];
|
|
38
|
+
/**
|
|
39
|
+
* Scheme servers registered on the account's Solana networks. Defaults
|
|
40
|
+
* to `[new ExactSvmScheme()]` from `@x402/svm` when it is installed, and
|
|
41
|
+
* to none otherwise (Solana resources are then skipped).
|
|
42
|
+
*/
|
|
43
|
+
svmSchemes?: readonly SchemeNetworkServer[];
|
|
44
|
+
/** Passed to the stock `paymentMiddleware` for the browser paywall. */
|
|
45
|
+
paywallConfig?: PaywallConfig;
|
|
46
|
+
/** `fetch` for `GET /x402/config`. Defaults to the global `fetch`. */
|
|
47
|
+
fetch?: typeof fetch;
|
|
48
|
+
/**
|
|
49
|
+
* After a failed first load, how long requests answer 503 before the
|
|
50
|
+
* next attempt. Defaults to 5 s.
|
|
51
|
+
*/
|
|
52
|
+
retryAfterMs?: number;
|
|
53
|
+
/** Defaults to `console`. */
|
|
54
|
+
logger?: CryptonlyMiddlewareLogger;
|
|
55
|
+
/** `@x402/extensions` override; `null` leaves extensions out. */
|
|
56
|
+
extensions?: X402ExtensionsModule | null;
|
|
57
|
+
/** Builds the inner middleware. See {@link InnerMiddlewareFactory}. */
|
|
58
|
+
createMiddleware?: InnerMiddlewareFactory;
|
|
59
|
+
};
|
|
60
|
+
/** The middleware returned by {@link cryptonlyPaymentMiddleware}. */
|
|
61
|
+
export type CryptonlyPaymentMiddleware = ((req: Request, res: Response, next: NextFunction) => Promise<void>) & {
|
|
62
|
+
/**
|
|
63
|
+
* Loads the configuration now (if not loaded yet) and starts refreshing.
|
|
64
|
+
* Rejects when the first load fails. Await it before `listen` to fail
|
|
65
|
+
* fast at startup.
|
|
66
|
+
*/
|
|
67
|
+
ready(): Promise<void>;
|
|
68
|
+
/** Reloads the configuration now. Resolves `true` when the routes changed. */
|
|
69
|
+
refresh(): Promise<boolean>;
|
|
70
|
+
/** The routes in use, or `undefined` before the first successful load. */
|
|
71
|
+
routes(): RoutesConfig | undefined;
|
|
72
|
+
/** Stops refreshing. The last routes stay in use. */
|
|
73
|
+
close(): void;
|
|
74
|
+
};
|
|
75
|
+
/** The default {@link InnerMiddlewareFactory}: stock `@x402/express`. */
|
|
76
|
+
export declare const stockPaymentMiddleware: InnerMiddlewareFactory;
|
|
77
|
+
/**
|
|
78
|
+
* Express middleware whose paid routes and prices come from the Cryptonly
|
|
79
|
+
* dashboard (`GET /x402/config`), refreshed every minute, so a price change
|
|
80
|
+
* reaches production without a deploy.
|
|
81
|
+
*
|
|
82
|
+
* - The configuration loads on the first request (or on `ready()`), once.
|
|
83
|
+
* - Each refresh rebuilds the stock `@x402/express` middleware only when the
|
|
84
|
+
* routes changed. A failed refresh keeps the last good routes.
|
|
85
|
+
* - Until a first load succeeds, every request through it answers 503
|
|
86
|
+
* (fail closed): paid endpoints are never served for free. Mount it after
|
|
87
|
+
* health checks and other free routes.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* import { cryptonlyPaymentMiddleware } from '@cryptonly/x402/express';
|
|
92
|
+
*
|
|
93
|
+
* app.use(cryptonlyPaymentMiddleware({ apiKey: process.env.CRYPTONLY_API_KEY! }));
|
|
94
|
+
* app.get('/weather', (req, res) => res.json({ temperatureC: 21 }));
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export declare function cryptonlyPaymentMiddleware(options: CryptonlyPaymentMiddlewareOptions): CryptonlyPaymentMiddleware;
|
|
98
|
+
export { fetchX402Config, routesFromConfig } from './config.js';
|
|
99
|
+
export type { X402Config } from './config.js';
|
|
100
|
+
//# sourceMappingURL=express.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,0BAA0B,EAAE,KAAK,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAC1F,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,oBAAoB,EAC1B,MAAM,aAAa,CAAC;AAErB,6BAA6B;AAC7B,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,KACf,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,mEAAmE;AACnE,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,EAAE,0BAA0B,CAAC;IACxC,+CAA+C;IAC/C,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACxC,wDAAwD;IACxD,UAAU,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC3C,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,CACnC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,sBAAsB,KAC5B,iBAAiB,CAAC;AAEvB,8DAA8D;AAC9D,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAEjF,sDAAsD;AACtD,MAAM,MAAM,iCAAiC,GAAG,0BAA0B,GAAG;IAC3E;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACzC;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC5C,uEAAuE;IACvE,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,sEAAsE;IACtE,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,iEAAiE;IACjE,UAAU,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACzC,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;CAC3C,CAAC;AAEF,qEAAqE;AACrE,MAAM,MAAM,0BAA0B,GAAG,CAAC,CACxC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,YAAY,KACf,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG;IACpB;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,8EAA8E;IAC9E,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5B,0EAA0E;IAC1E,MAAM,IAAI,YAAY,GAAG,SAAS,CAAC;IACnC,qDAAqD;IACrD,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAMF,yEAAyE;AACzE,eAAO,MAAM,sBAAsB,EAAE,sBAgBpC,CAAC;AA6BF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,iCAAiC,GACzC,0BAA0B,CA+I5B;AAMD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAChE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/express.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.routesFromConfig = exports.fetchX402Config = exports.stockPaymentMiddleware = void 0;
|
|
4
|
+
exports.cryptonlyPaymentMiddleware = cryptonlyPaymentMiddleware;
|
|
5
|
+
const server_1 = require("@x402/evm/exact/server");
|
|
6
|
+
const express_1 = require("@x402/express");
|
|
7
|
+
const client_js_1 = require("./client.js");
|
|
8
|
+
const config_js_1 = require("./config.js");
|
|
9
|
+
const DEFAULT_REFRESH_INTERVAL_MS = 60_000;
|
|
10
|
+
const DEFAULT_RETRY_AFTER_MS = 5_000;
|
|
11
|
+
const LOG_PREFIX = '[@cryptonly/x402]';
|
|
12
|
+
/** The default {@link InnerMiddlewareFactory}: stock `@x402/express`. */
|
|
13
|
+
const stockPaymentMiddleware = (routes, { config, facilitator, schemes, svmSchemes, paywallConfig }) => {
|
|
14
|
+
// A fresh resource server per route set: `initialize()` on a shared one
|
|
15
|
+
// would clear its facilitator data under requests still in flight.
|
|
16
|
+
const server = new express_1.x402ResourceServer(facilitator);
|
|
17
|
+
for (const scheme of schemes) {
|
|
18
|
+
server.register(config.network, scheme);
|
|
19
|
+
}
|
|
20
|
+
for (const network of solanaNetworksOf(config)) {
|
|
21
|
+
for (const scheme of svmSchemes) {
|
|
22
|
+
server.register(network, scheme);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return (0, express_1.paymentMiddleware)(routes, server, paywallConfig);
|
|
26
|
+
};
|
|
27
|
+
exports.stockPaymentMiddleware = stockPaymentMiddleware;
|
|
28
|
+
function solanaNetworksOf(config) {
|
|
29
|
+
const networks = new Set();
|
|
30
|
+
for (const entry of config.networks ?? []) {
|
|
31
|
+
if ((0, config_js_1.isSolanaNetwork)(entry.network))
|
|
32
|
+
networks.add(entry.network);
|
|
33
|
+
}
|
|
34
|
+
for (const asset of config.assets) {
|
|
35
|
+
if (asset.network && (0, config_js_1.isSolanaNetwork)(asset.network))
|
|
36
|
+
networks.add(asset.network);
|
|
37
|
+
}
|
|
38
|
+
return [...networks];
|
|
39
|
+
}
|
|
40
|
+
let svmModule;
|
|
41
|
+
/** `@x402/svm`'s exact server scheme when installed (optional peer). */
|
|
42
|
+
function defaultSvmSchemes() {
|
|
43
|
+
if (svmModule === undefined) {
|
|
44
|
+
try {
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
46
|
+
svmModule = require('@x402/svm/exact/server');
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
svmModule = null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return svmModule ? [new svmModule.ExactSvmScheme()] : [];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Express middleware whose paid routes and prices come from the Cryptonly
|
|
56
|
+
* dashboard (`GET /x402/config`), refreshed every minute, so a price change
|
|
57
|
+
* reaches production without a deploy.
|
|
58
|
+
*
|
|
59
|
+
* - The configuration loads on the first request (or on `ready()`), once.
|
|
60
|
+
* - Each refresh rebuilds the stock `@x402/express` middleware only when the
|
|
61
|
+
* routes changed. A failed refresh keeps the last good routes.
|
|
62
|
+
* - Until a first load succeeds, every request through it answers 503
|
|
63
|
+
* (fail closed): paid endpoints are never served for free. Mount it after
|
|
64
|
+
* health checks and other free routes.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { cryptonlyPaymentMiddleware } from '@cryptonly/x402/express';
|
|
69
|
+
*
|
|
70
|
+
* app.use(cryptonlyPaymentMiddleware({ apiKey: process.env.CRYPTONLY_API_KEY! }));
|
|
71
|
+
* app.get('/weather', (req, res) => res.json({ temperatureC: 21 }));
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
function cryptonlyPaymentMiddleware(options) {
|
|
75
|
+
const facilitator = new client_js_1.CryptonlyFacilitatorClient(options);
|
|
76
|
+
const schemes = options.schemes ?? [new server_1.ExactEvmScheme()];
|
|
77
|
+
const svmSchemes = options.svmSchemes ?? defaultSvmSchemes();
|
|
78
|
+
const createMiddleware = options.createMiddleware ?? exports.stockPaymentMiddleware;
|
|
79
|
+
const logger = options.logger ?? console;
|
|
80
|
+
const refreshIntervalMs = options.refreshIntervalMs ?? DEFAULT_REFRESH_INTERVAL_MS;
|
|
81
|
+
const retryAfterMs = options.retryAfterMs ?? DEFAULT_RETRY_AFTER_MS;
|
|
82
|
+
let current;
|
|
83
|
+
let inflight;
|
|
84
|
+
let firstLoad;
|
|
85
|
+
let lastFailureAt;
|
|
86
|
+
let lastFailure;
|
|
87
|
+
let timer;
|
|
88
|
+
let started = false;
|
|
89
|
+
let closed = false;
|
|
90
|
+
async function load() {
|
|
91
|
+
const config = await (0, config_js_1.fetchX402Config)({
|
|
92
|
+
apiKey: options.apiKey,
|
|
93
|
+
baseUrl: options.baseUrl,
|
|
94
|
+
sandbox: options.sandbox,
|
|
95
|
+
fetch: options.fetch,
|
|
96
|
+
});
|
|
97
|
+
const skipped = [];
|
|
98
|
+
// A route is servable when a scheme is registered for its network.
|
|
99
|
+
const evmNames = schemes.map((scheme) => scheme.scheme);
|
|
100
|
+
const svmNames = svmSchemes.map((scheme) => scheme.scheme);
|
|
101
|
+
const routes = (0, config_js_1.routesFromConfig)(config, {
|
|
102
|
+
schemes: [...new Set([...evmNames, ...svmNames])],
|
|
103
|
+
...(options.extensions !== undefined ? { extensions: options.extensions } : {}),
|
|
104
|
+
onSkip: (resource, reason) => skipped.push(`${resource.method} ${resource.path}: ${reason}`),
|
|
105
|
+
});
|
|
106
|
+
for (const [key, route] of Object.entries(routes)) {
|
|
107
|
+
const accepts = Array.isArray(route.accepts) ? route.accepts[0] : route.accepts;
|
|
108
|
+
const names = (0, config_js_1.isSolanaNetwork)(accepts.network) ? svmNames : evmNames;
|
|
109
|
+
if (!names.includes(accepts.scheme)) {
|
|
110
|
+
skipped.push(`${key}: no ${accepts.scheme} scheme for ${accepts.network}`);
|
|
111
|
+
delete routes[key];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const key = JSON.stringify([config.network, routes]);
|
|
115
|
+
if (current?.key === key)
|
|
116
|
+
return false;
|
|
117
|
+
for (const line of skipped)
|
|
118
|
+
logger.warn(`${LOG_PREFIX} skipped ${line}`);
|
|
119
|
+
const handler = createMiddleware(routes, {
|
|
120
|
+
config,
|
|
121
|
+
facilitator,
|
|
122
|
+
schemes,
|
|
123
|
+
svmSchemes,
|
|
124
|
+
...(options.paywallConfig ? { paywallConfig: options.paywallConfig } : {}),
|
|
125
|
+
});
|
|
126
|
+
const previous = current;
|
|
127
|
+
current = { key, routes, handler };
|
|
128
|
+
if (previous) {
|
|
129
|
+
logger.info(`${LOG_PREFIX} x402 routes updated: ${Object.keys(routes).join(', ') || '(none)'}`);
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
function refresh() {
|
|
134
|
+
inflight ??= load().finally(() => {
|
|
135
|
+
inflight = undefined;
|
|
136
|
+
});
|
|
137
|
+
return inflight;
|
|
138
|
+
}
|
|
139
|
+
function startRefreshing() {
|
|
140
|
+
if (started || closed)
|
|
141
|
+
return;
|
|
142
|
+
started = true;
|
|
143
|
+
if (!(refreshIntervalMs > 0) || !Number.isFinite(refreshIntervalMs))
|
|
144
|
+
return;
|
|
145
|
+
timer = setInterval(() => {
|
|
146
|
+
refresh().catch((error) => {
|
|
147
|
+
logger.warn(current
|
|
148
|
+
? `${LOG_PREFIX} config refresh failed; keeping the last routes: ${describe(error)}`
|
|
149
|
+
: `${LOG_PREFIX} config load failed: ${describe(error)}`);
|
|
150
|
+
});
|
|
151
|
+
}, refreshIntervalMs);
|
|
152
|
+
timer.unref?.();
|
|
153
|
+
}
|
|
154
|
+
function ready() {
|
|
155
|
+
startRefreshing();
|
|
156
|
+
if (current)
|
|
157
|
+
return Promise.resolve();
|
|
158
|
+
if (!firstLoad) {
|
|
159
|
+
if (lastFailureAt !== undefined && Date.now() - lastFailureAt < retryAfterMs) {
|
|
160
|
+
return Promise.reject(lastFailure);
|
|
161
|
+
}
|
|
162
|
+
firstLoad = refresh().then(() => {
|
|
163
|
+
firstLoad = undefined;
|
|
164
|
+
}, (error) => {
|
|
165
|
+
firstLoad = undefined;
|
|
166
|
+
lastFailureAt = Date.now();
|
|
167
|
+
lastFailure = error;
|
|
168
|
+
logger.error(`${LOG_PREFIX} x402 config unavailable: ${describe(error)}`);
|
|
169
|
+
throw error;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return firstLoad;
|
|
173
|
+
}
|
|
174
|
+
const middleware = async (req, res, next) => {
|
|
175
|
+
if (!current) {
|
|
176
|
+
try {
|
|
177
|
+
await ready();
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
if (!res.headersSent) {
|
|
181
|
+
res.setHeader('Retry-After', String(Math.ceil(retryAfterMs / 1000)));
|
|
182
|
+
res.status(503).json({
|
|
183
|
+
error: 'x402_config_unavailable',
|
|
184
|
+
message: 'Payment configuration is unavailable; retry shortly.',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
await current.handler(req, res, next);
|
|
191
|
+
};
|
|
192
|
+
return Object.assign(middleware, {
|
|
193
|
+
ready,
|
|
194
|
+
refresh: () => {
|
|
195
|
+
startRefreshing();
|
|
196
|
+
return refresh();
|
|
197
|
+
},
|
|
198
|
+
routes: () => current?.routes,
|
|
199
|
+
close: () => {
|
|
200
|
+
closed = true;
|
|
201
|
+
if (timer)
|
|
202
|
+
clearInterval(timer);
|
|
203
|
+
timer = undefined;
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function describe(error) {
|
|
208
|
+
return error instanceof Error ? error.message : String(error);
|
|
209
|
+
}
|
|
210
|
+
var config_js_2 = require("./config.js");
|
|
211
|
+
Object.defineProperty(exports, "fetchX402Config", { enumerable: true, get: function () { return config_js_2.fetchX402Config; } });
|
|
212
|
+
Object.defineProperty(exports, "routesFromConfig", { enumerable: true, get: function () { return config_js_2.routesFromConfig; } });
|
|
213
|
+
//# sourceMappingURL=express.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.js","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":";;;AA8KA,gEAiJC;AA7TD,mDAAwD;AACxD,2CAKuB;AACvB,2CAA0F;AAC1F,2CAMqB;AAwFrB,MAAM,2BAA2B,GAAG,MAAM,CAAC;AAC3C,MAAM,sBAAsB,GAAG,KAAK,CAAC;AACrC,MAAM,UAAU,GAAG,mBAAmB,CAAC;AAEvC,yEAAyE;AAClE,MAAM,sBAAsB,GAA2B,CAC5D,MAAM,EACN,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,EAC3D,EAAE;IACF,wEAAwE;IACxE,mEAAmE;IACnE,MAAM,MAAM,GAAG,IAAI,4BAAkB,CAAC,WAAW,CAAC,CAAC;IACnD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IACD,OAAO,IAAA,2BAAiB,EAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;AAC1D,CAAC,CAAC;AAhBW,QAAA,sBAAsB,0BAgBjC;AAEF,SAAS,gBAAgB,CAAC,MAAkB;IAC1C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IAClD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QAC1C,IAAI,IAAA,2BAAe,EAAC,KAAK,CAAC,OAAO,CAAC;YAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClE,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,IAAI,KAAK,CAAC,OAAO,IAAI,IAAA,2BAAe,EAAC,KAAK,CAAC,OAAO,CAAC;YAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC;AACvB,CAAC;AAGD,IAAI,SAA6C,CAAC;AAElD,wEAAwE;AACxE,SAAS,iBAAiB;IACxB,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,iEAAiE;YACjE,SAAS,GAAG,OAAO,CAAC,wBAAwB,CAAoB,CAAC;QACnE,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC3D,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAgB,0BAA0B,CACxC,OAA0C;IAE1C,MAAM,WAAW,GAAG,IAAI,sCAA0B,CAAC,OAAO,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,IAAI,uBAAc,EAAE,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,iBAAiB,EAAE,CAAC;IAC7D,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,8BAAsB,CAAC;IAC5E,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC;IACzC,MAAM,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,IAAI,2BAA2B,CAAC;IACnF,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,sBAAsB,CAAC;IAEpE,IAAI,OAES,CAAC;IACd,IAAI,QAAsC,CAAC;IAC3C,IAAI,SAAoC,CAAC;IACzC,IAAI,aAAiC,CAAC;IACtC,IAAI,WAAoB,CAAC;IACzB,IAAI,KAAiD,CAAC;IACtD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,KAAK,UAAU,IAAI;QACjB,MAAM,MAAM,GAAG,MAAM,IAAA,2BAAe,EAAC;YACnC,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,KAAK,EAAE,OAAO,CAAC,KAAK;SACrB,CAAC,CAAC;QACH,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,mEAAmE;QACnE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxD,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3D,MAAM,MAAM,GAAG,IAAA,4BAAgB,EAAC,MAAM,EAAE;YACtC,OAAO,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC;YACjD,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/E,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAC3B,OAAO,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;SACjE,CAAC,CAAC;QACH,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAClD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;YAChF,MAAM,KAAK,GAAG,IAAA,2BAAe,EAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;YACrE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBACpC,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,QAAQ,OAAO,CAAC,MAAM,eAAe,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC3E,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;QACrD,IAAI,OAAO,EAAE,GAAG,KAAK,GAAG;YAAE,OAAO,KAAK,CAAC;QAEvC,KAAK,MAAM,IAAI,IAAI,OAAO;YAAE,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,YAAY,IAAI,EAAE,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,EAAE;YACvC,MAAM;YACN,WAAW;YACX,OAAO;YACP,UAAU;YACV,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3E,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,OAAO,CAAC;QACzB,OAAO,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QACnC,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CACT,GAAG,UAAU,yBAAyB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,EAAE,CACnF,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,SAAS,OAAO;QACd,QAAQ,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YAC/B,QAAQ,GAAG,SAAS,CAAC;QACvB,CAAC,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,SAAS,eAAe;QACtB,IAAI,OAAO,IAAI,MAAM;YAAE,OAAO;QAC9B,OAAO,GAAG,IAAI,CAAC;QACf,IAAI,CAAC,CAAC,iBAAiB,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YAAE,OAAO;QAC5E,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YACvB,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;gBACjC,MAAM,CAAC,IAAI,CACT,OAAO;oBACL,CAAC,CAAC,GAAG,UAAU,oDAAoD,QAAQ,CAAC,KAAK,CAAC,EAAE;oBACpF,CAAC,CAAC,GAAG,UAAU,wBAAwB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAC3D,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,iBAAiB,CAAC,CAAC;QACtB,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;IAClB,CAAC;IAED,SAAS,KAAK;QACZ,eAAe,EAAE,CAAC;QAClB,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QACtC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,IAAI,aAAa,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,aAAa,GAAG,YAAY,EAAE,CAAC;gBAC7E,OAAO,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YACrC,CAAC;YACD,SAAS,GAAG,OAAO,EAAE,CAAC,IAAI,CACxB,GAAG,EAAE;gBACH,SAAS,GAAG,SAAS,CAAC;YACxB,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;gBACjB,SAAS,GAAG,SAAS,CAAC;gBACtB,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC3B,WAAW,GAAG,KAAK,CAAC;gBACpB,MAAM,CAAC,KAAK,CAAC,GAAG,UAAU,6BAA6B,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAC1E,MAAM,KAAK,CAAC;YACd,CAAC,CACF,CAAC;QACJ,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QAC3E,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,MAAM,KAAK,EAAE,CAAC;YAChB,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;oBACrB,GAAG,CAAC,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;oBACrE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;wBACnB,KAAK,EAAE,yBAAyB;wBAChC,OAAO,EAAE,sDAAsD;qBAChE,CAAC,CAAC;gBACL,CAAC;gBACD,OAAO;YACT,CAAC;QACH,CAAC;QACD,MAAM,OAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC,CAAC;IAEF,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE;QAC/B,KAAK;QACL,OAAO,EAAE,GAAG,EAAE;YACZ,eAAe,EAAE,CAAC;YAClB,OAAO,OAAO,EAAE,CAAC;QACnB,CAAC;QACD,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM;QAC7B,KAAK,EAAE,GAAG,EAAE;YACV,MAAM,GAAG,IAAI,CAAC;YACd,IAAI,KAAK;gBAAE,aAAa,CAAC,KAAK,CAAC,CAAC;YAChC,KAAK,GAAG,SAAS,CAAC;QACpB,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC;AAED,yCAAgE;AAAvD,4GAAA,eAAe,OAAA;AAAE,6GAAA,gBAAgB,OAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export { CryptonlyFacilitatorClient } from './client.js';
|
|
1
|
+
export { CryptonlyFacilitatorClient, resolveMerchantApiOrigin, } from './client.js';
|
|
2
2
|
export type { CryptonlyFacilitatorConfig } from './client.js';
|
|
3
|
+
export { X402ConfigError, assertPayableConfig, fetchX402Config, loadX402Extensions, parseX402Config, routeKey, routesFromConfig, } from './config.js';
|
|
4
|
+
export type { FetchX402ConfigOptions, RoutesFromConfigOptions, X402AccountStatus, X402Config, X402ConfigAsset, X402ConfigErrorCode, X402ConfigResource, X402ExtensionsModule, } from './config.js';
|
|
3
5
|
export { MERCHANT_API_BASE_URL, MERCHANT_API_KEY_HEADER, SANDBOX_MERCHANT_API_BASE_URL, } from './constants.js';
|
|
4
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,QAAQ,EACR,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,EACjB,UAAU,EACV,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SANDBOX_MERCHANT_API_BASE_URL = exports.MERCHANT_API_KEY_HEADER = exports.MERCHANT_API_BASE_URL = exports.CryptonlyFacilitatorClient = void 0;
|
|
3
|
+
exports.SANDBOX_MERCHANT_API_BASE_URL = exports.MERCHANT_API_KEY_HEADER = exports.MERCHANT_API_BASE_URL = exports.routesFromConfig = exports.routeKey = exports.parseX402Config = exports.loadX402Extensions = exports.fetchX402Config = exports.assertPayableConfig = exports.X402ConfigError = exports.resolveMerchantApiOrigin = exports.CryptonlyFacilitatorClient = void 0;
|
|
4
4
|
var client_js_1 = require("./client.js");
|
|
5
5
|
Object.defineProperty(exports, "CryptonlyFacilitatorClient", { enumerable: true, get: function () { return client_js_1.CryptonlyFacilitatorClient; } });
|
|
6
|
+
Object.defineProperty(exports, "resolveMerchantApiOrigin", { enumerable: true, get: function () { return client_js_1.resolveMerchantApiOrigin; } });
|
|
7
|
+
var config_js_1 = require("./config.js");
|
|
8
|
+
Object.defineProperty(exports, "X402ConfigError", { enumerable: true, get: function () { return config_js_1.X402ConfigError; } });
|
|
9
|
+
Object.defineProperty(exports, "assertPayableConfig", { enumerable: true, get: function () { return config_js_1.assertPayableConfig; } });
|
|
10
|
+
Object.defineProperty(exports, "fetchX402Config", { enumerable: true, get: function () { return config_js_1.fetchX402Config; } });
|
|
11
|
+
Object.defineProperty(exports, "loadX402Extensions", { enumerable: true, get: function () { return config_js_1.loadX402Extensions; } });
|
|
12
|
+
Object.defineProperty(exports, "parseX402Config", { enumerable: true, get: function () { return config_js_1.parseX402Config; } });
|
|
13
|
+
Object.defineProperty(exports, "routeKey", { enumerable: true, get: function () { return config_js_1.routeKey; } });
|
|
14
|
+
Object.defineProperty(exports, "routesFromConfig", { enumerable: true, get: function () { return config_js_1.routesFromConfig; } });
|
|
6
15
|
var constants_js_1 = require("./constants.js");
|
|
7
16
|
Object.defineProperty(exports, "MERCHANT_API_BASE_URL", { enumerable: true, get: function () { return constants_js_1.MERCHANT_API_BASE_URL; } });
|
|
8
17
|
Object.defineProperty(exports, "MERCHANT_API_KEY_HEADER", { enumerable: true, get: function () { return constants_js_1.MERCHANT_API_KEY_HEADER; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAGqB;AAFnB,uHAAA,0BAA0B,OAAA;AAC1B,qHAAA,wBAAwB,OAAA;AAG1B,yCAQqB;AAPnB,4GAAA,eAAe,OAAA;AACf,gHAAA,mBAAmB,OAAA;AACnB,4GAAA,eAAe,OAAA;AACf,+GAAA,kBAAkB,OAAA;AAClB,4GAAA,eAAe,OAAA;AACf,qGAAA,QAAQ,OAAA;AACR,6GAAA,gBAAgB,OAAA;AAYlB,+CAIwB;AAHtB,qHAAA,qBAAqB,OAAA;AACrB,uHAAA,uBAAuB,OAAA;AACvB,6HAAA,6BAA6B,OAAA"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cryptonly/x402",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Cryptonly facilitator client for the x402
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Cryptonly facilitator client and dashboard-configured payment middleware for the x402 protocol.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
7
7
|
"type": "commonjs",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
},
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
14
|
-
"url": "git+https://github.com/cryptonly-net/x402.git"
|
|
14
|
+
"url": "git+https://github.com/cryptonly-net/x402.git",
|
|
15
|
+
"directory": "typescript"
|
|
15
16
|
},
|
|
16
17
|
"homepage": "https://github.com/cryptonly-net/x402#readme",
|
|
17
18
|
"bugs": {
|
|
@@ -23,7 +24,9 @@
|
|
|
23
24
|
"facilitator",
|
|
24
25
|
"payments",
|
|
25
26
|
"client",
|
|
26
|
-
"typescript"
|
|
27
|
+
"typescript",
|
|
28
|
+
"express",
|
|
29
|
+
"middleware"
|
|
27
30
|
],
|
|
28
31
|
"main": "./dist/index.js",
|
|
29
32
|
"types": "./dist/index.d.ts",
|
|
@@ -33,6 +36,20 @@
|
|
|
33
36
|
"import": "./dist/index.js",
|
|
34
37
|
"require": "./dist/index.js",
|
|
35
38
|
"default": "./dist/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./express": {
|
|
41
|
+
"types": "./dist/express.d.ts",
|
|
42
|
+
"import": "./dist/express.js",
|
|
43
|
+
"require": "./dist/express.js",
|
|
44
|
+
"default": "./dist/express.js"
|
|
45
|
+
},
|
|
46
|
+
"./package.json": "./package.json"
|
|
47
|
+
},
|
|
48
|
+
"typesVersions": {
|
|
49
|
+
"*": {
|
|
50
|
+
"express": [
|
|
51
|
+
"./dist/express.d.ts"
|
|
52
|
+
]
|
|
36
53
|
}
|
|
37
54
|
},
|
|
38
55
|
"files": [
|
|
@@ -49,11 +66,40 @@
|
|
|
49
66
|
"access": "public"
|
|
50
67
|
},
|
|
51
68
|
"peerDependencies": {
|
|
52
|
-
"@x402/core": "^2"
|
|
69
|
+
"@x402/core": "^2",
|
|
70
|
+
"@x402/evm": "^2",
|
|
71
|
+
"@x402/express": "^2",
|
|
72
|
+
"@x402/extensions": "^2",
|
|
73
|
+
"@x402/svm": "^2",
|
|
74
|
+
"express": "^4 || ^5"
|
|
75
|
+
},
|
|
76
|
+
"peerDependenciesMeta": {
|
|
77
|
+
"@x402/evm": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"@x402/express": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"@x402/extensions": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"express": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"@x402/svm": {
|
|
90
|
+
"optional": true
|
|
91
|
+
}
|
|
53
92
|
},
|
|
54
93
|
"devDependencies": {
|
|
94
|
+
"@types/express": "^5.0.6",
|
|
55
95
|
"@types/jest": "^29.5.14",
|
|
96
|
+
"@types/node": "^22.20.5",
|
|
56
97
|
"@x402/core": "^2.28.0",
|
|
98
|
+
"@x402/evm": "^2.28.0",
|
|
99
|
+
"@x402/express": "^2.28.0",
|
|
100
|
+
"@x402/extensions": "^2.28.0",
|
|
101
|
+
"@x402/svm": "^2.28.0",
|
|
102
|
+
"express": "^5.3.0",
|
|
57
103
|
"jest": "^29.7.0",
|
|
58
104
|
"ts-jest": "^29.2.0",
|
|
59
105
|
"typescript": "^5.9.3"
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) Cryptonly
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|