@4mica/x402 0.1.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/.eslintrc.cjs +29 -0
- package/.prettierignore +3 -0
- package/.prettierrc +6 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/README.md +389 -0
- package/demo/.env.example +8 -0
- package/demo/README.md +125 -0
- package/demo/package.json +26 -0
- package/demo/src/client.ts +54 -0
- package/demo/src/deposit.ts +39 -0
- package/demo/src/server.ts +74 -0
- package/demo/tsconfig.json +8 -0
- package/demo/yarn.lock +925 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/client/scheme.d.ts +11 -0
- package/dist/client/scheme.js +65 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/server/express/adapter.d.ts +71 -0
- package/dist/server/express/adapter.js +90 -0
- package/dist/server/express/index.d.ts +122 -0
- package/dist/server/express/index.js +340 -0
- package/dist/server/facilitator.d.ts +35 -0
- package/dist/server/facilitator.js +52 -0
- package/dist/server/index.d.ts +6 -0
- package/dist/server/index.js +4 -0
- package/dist/server/scheme.d.ts +93 -0
- package/dist/server/scheme.js +179 -0
- package/eslint.config.mjs +22 -0
- package/package.json +79 -0
- package/src/client/index.ts +1 -0
- package/src/client/scheme.ts +95 -0
- package/src/index.ts +7 -0
- package/src/server/express/adapter.ts +100 -0
- package/src/server/express/index.ts +466 -0
- package/src/server/facilitator.ts +90 -0
- package/src/server/index.ts +10 -0
- package/src/server/scheme.ts +223 -0
- package/tsconfig.build.json +5 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
export const SUPPORTED_NETWORKS = ['eip155:11155111', 'eip155:80002'];
|
|
2
|
+
/**
|
|
3
|
+
* EVM server implementation for the 4mica payment scheme.
|
|
4
|
+
*/
|
|
5
|
+
export class FourMicaEvmScheme {
|
|
6
|
+
constructor(advertisedTabEndpoint) {
|
|
7
|
+
this.advertisedTabEndpoint = advertisedTabEndpoint;
|
|
8
|
+
this.scheme = '4mica-credit';
|
|
9
|
+
this.moneyParsers = [];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Register a custom money parser in the parser chain.
|
|
13
|
+
* Multiple parsers can be registered - they will be tried in registration order.
|
|
14
|
+
* Each parser receives a decimal amount (e.g., 1.50 for $1.50).
|
|
15
|
+
* If a parser returns null, the next parser in the chain will be tried.
|
|
16
|
+
* The default parser is always the final fallback.
|
|
17
|
+
*
|
|
18
|
+
* @param parser - Custom function to convert amount to AssetAmount (or null to skip)
|
|
19
|
+
* @returns The server instance for chaining
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* evmServer.registerMoneyParser(async (amount, network) => {
|
|
23
|
+
* // Custom conversion logic
|
|
24
|
+
* if (amount > 100) {
|
|
25
|
+
* // Use different token for large amounts
|
|
26
|
+
* return { amount: (amount * 1e18).toString(), asset: "0xCustomToken" };
|
|
27
|
+
* }
|
|
28
|
+
* return null; // Use next parser
|
|
29
|
+
* });
|
|
30
|
+
*/
|
|
31
|
+
registerMoneyParser(parser) {
|
|
32
|
+
this.moneyParsers.push(parser);
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Parses a price into an asset amount.
|
|
37
|
+
* If price is already an AssetAmount, returns it directly.
|
|
38
|
+
* If price is Money (string | number), parses to decimal and tries custom parsers.
|
|
39
|
+
* Falls back to default conversion if all custom parsers return null.
|
|
40
|
+
*
|
|
41
|
+
* @param price - The price to parse
|
|
42
|
+
* @param network - The network to use
|
|
43
|
+
* @returns Promise that resolves to the parsed asset amount
|
|
44
|
+
*/
|
|
45
|
+
async parsePrice(price, network) {
|
|
46
|
+
// If already an AssetAmount, return it directly
|
|
47
|
+
if (typeof price === 'object' && price !== null && 'amount' in price) {
|
|
48
|
+
if (!price.asset) {
|
|
49
|
+
throw new Error(`Asset address must be specified for AssetAmount on network ${network}`);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
amount: price.amount,
|
|
53
|
+
asset: price.asset,
|
|
54
|
+
extra: price.extra || {},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// Parse Money to decimal number
|
|
58
|
+
const amount = this.parseMoneyToDecimal(price);
|
|
59
|
+
// Try each custom money parser in order
|
|
60
|
+
for (const parser of this.moneyParsers) {
|
|
61
|
+
const result = await parser(amount, network);
|
|
62
|
+
if (result !== null) {
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// All custom parsers returned null, use default conversion
|
|
67
|
+
return this.defaultMoneyConversion(amount, network);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build payment requirements for this scheme/network combination
|
|
71
|
+
*
|
|
72
|
+
* @param paymentRequirements - The base payment requirements
|
|
73
|
+
* @param supportedKind - The supported kind from facilitator (unused)
|
|
74
|
+
* @param supportedKind.x402Version - The x402 version
|
|
75
|
+
* @param supportedKind.scheme - The logical payment scheme
|
|
76
|
+
* @param supportedKind.network - The network identifier in CAIP-2 format
|
|
77
|
+
* @param supportedKind.extra - Optional extra metadata regarding scheme/network implementation details
|
|
78
|
+
* @param extensionKeys - Extension keys supported by the facilitator (unused)
|
|
79
|
+
* @returns Payment requirements ready to be sent to clients
|
|
80
|
+
*/
|
|
81
|
+
enhancePaymentRequirements(paymentRequirements, supportedKind, extensionKeys) {
|
|
82
|
+
// Mark unused parameters to satisfy linter
|
|
83
|
+
void supportedKind;
|
|
84
|
+
void extensionKeys;
|
|
85
|
+
if (!paymentRequirements.extra) {
|
|
86
|
+
paymentRequirements.extra = {};
|
|
87
|
+
}
|
|
88
|
+
paymentRequirements.extra.tabEndpoint = this.advertisedTabEndpoint;
|
|
89
|
+
return Promise.resolve(paymentRequirements);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Parse Money (string | number) to a decimal number.
|
|
93
|
+
* Handles formats like "$1.50", "1.50", 1.50, etc.
|
|
94
|
+
*
|
|
95
|
+
* @param money - The money value to parse
|
|
96
|
+
* @returns Decimal number
|
|
97
|
+
*/
|
|
98
|
+
parseMoneyToDecimal(money) {
|
|
99
|
+
if (typeof money === 'number') {
|
|
100
|
+
return money;
|
|
101
|
+
}
|
|
102
|
+
// Remove $ sign and whitespace, then parse
|
|
103
|
+
const cleanMoney = money.replace(/^\$/, '').trim();
|
|
104
|
+
const amount = parseFloat(cleanMoney);
|
|
105
|
+
if (isNaN(amount)) {
|
|
106
|
+
throw new Error(`Invalid money format: ${money}`);
|
|
107
|
+
}
|
|
108
|
+
return amount;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Default money conversion implementation.
|
|
112
|
+
* Converts decimal amount to the default stablecoin on the specified network.
|
|
113
|
+
*
|
|
114
|
+
* @param amount - The decimal amount (e.g., 1.50)
|
|
115
|
+
* @param network - The network to use
|
|
116
|
+
* @returns The parsed asset amount in the default stablecoin
|
|
117
|
+
*/
|
|
118
|
+
defaultMoneyConversion(amount, network) {
|
|
119
|
+
const assetInfo = this.getDefaultAsset(network);
|
|
120
|
+
const tokenAmount = this.convertToTokenAmount(amount.toString(), assetInfo.decimals);
|
|
121
|
+
return {
|
|
122
|
+
amount: tokenAmount,
|
|
123
|
+
asset: assetInfo.address,
|
|
124
|
+
extra: {
|
|
125
|
+
name: assetInfo.name,
|
|
126
|
+
version: assetInfo.version,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Convert decimal amount to token units (e.g., 0.10 -> 100000 for 6-decimal tokens)
|
|
132
|
+
*
|
|
133
|
+
* @param decimalAmount - The decimal amount to convert
|
|
134
|
+
* @param decimals - The number of decimals for the token
|
|
135
|
+
* @returns The token amount as a string
|
|
136
|
+
*/
|
|
137
|
+
convertToTokenAmount(decimalAmount, decimals) {
|
|
138
|
+
const amount = parseFloat(decimalAmount);
|
|
139
|
+
if (isNaN(amount)) {
|
|
140
|
+
throw new Error(`Invalid amount: ${decimalAmount}`);
|
|
141
|
+
}
|
|
142
|
+
// Convert to smallest unit (e.g., for USDC with 6 decimals: 0.10 * 10^6 = 100000)
|
|
143
|
+
const [intPart, decPart = ''] = String(amount).split('.');
|
|
144
|
+
const paddedDec = decPart.padEnd(decimals, '0').slice(0, decimals);
|
|
145
|
+
const tokenAmount = (intPart + paddedDec).replace(/^0+/, '') || '0';
|
|
146
|
+
return tokenAmount;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get the default asset info for a network (typically USDC)
|
|
150
|
+
*
|
|
151
|
+
* @param network - The network to get asset info for
|
|
152
|
+
* @returns The asset information including address, name, version, and decimals
|
|
153
|
+
*/
|
|
154
|
+
getDefaultAsset(network) {
|
|
155
|
+
// Map of network to USDC info including EIP-712 domain parameters
|
|
156
|
+
// Each network has the right to determine its own default stablecoin that can be expressed as a USD string by calling servers
|
|
157
|
+
// NOTE: Currently only EIP-3009 supporting stablecoins can be used with this scheme
|
|
158
|
+
// Generic ERC20 support via EIP-2612/permit2 is planned, but not yet implemented.
|
|
159
|
+
const stablecoins = {
|
|
160
|
+
'eip155:11155111': {
|
|
161
|
+
address: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',
|
|
162
|
+
name: 'USDC',
|
|
163
|
+
version: '2',
|
|
164
|
+
decimals: 6,
|
|
165
|
+
}, // Ethereum Sepolia USDC
|
|
166
|
+
'eip155:80002': {
|
|
167
|
+
address: '0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582',
|
|
168
|
+
name: 'USDC',
|
|
169
|
+
version: '2',
|
|
170
|
+
decimals: 6,
|
|
171
|
+
}, // Polygon PoS Amoy USDC
|
|
172
|
+
};
|
|
173
|
+
const assetInfo = stablecoins[network];
|
|
174
|
+
if (!assetInfo) {
|
|
175
|
+
throw new Error(`No default asset configured for network ${network}`);
|
|
176
|
+
}
|
|
177
|
+
return assetInfo;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import globals from "globals";
|
|
3
|
+
import tseslint from "typescript-eslint";
|
|
4
|
+
import pluginReact from "eslint-plugin-react";
|
|
5
|
+
import { defineConfig } from "eslint/config";
|
|
6
|
+
|
|
7
|
+
export default defineConfig([
|
|
8
|
+
{
|
|
9
|
+
ignores: ["dist/**", "coverage/**", "node_modules/**", "*.cjs"],
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
|
|
13
|
+
plugins: { js },
|
|
14
|
+
extends: ["js/recommended"],
|
|
15
|
+
languageOptions: { globals: globals.browser },
|
|
16
|
+
},
|
|
17
|
+
tseslint.configs.recommended,
|
|
18
|
+
pluginReact.configs.flat.recommended,
|
|
19
|
+
{
|
|
20
|
+
settings: { react: { version: "detect" } },
|
|
21
|
+
},
|
|
22
|
+
]);
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@4mica/x402",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "TypeScript x402 utilities for interacting with the 4Mica payment network",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/4mica-Network/x402-4mica.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/4mica-Network/x402-4mica#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/4mica-Network/x402-4mica/issues"
|
|
14
|
+
},
|
|
15
|
+
"main": "dist/index.js",
|
|
16
|
+
"types": "dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./server": {
|
|
23
|
+
"types": "./dist/server/index.d.ts",
|
|
24
|
+
"import": "./dist/server/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./server/express": {
|
|
27
|
+
"types": "./dist/server/express/index.d.ts",
|
|
28
|
+
"import": "./dist/server/express/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./client": {
|
|
31
|
+
"types": "./dist/client/index.d.ts",
|
|
32
|
+
"import": "./dist/client/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc -p tsconfig.build.json",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"lint": "eslint . --ext .ts",
|
|
39
|
+
"fmt": "prettier --check \"{src,tests}/**/*.{ts,js,json}\"",
|
|
40
|
+
"demo:server": "cd demo && yarn server",
|
|
41
|
+
"demo:client": "cd demo && yarn client",
|
|
42
|
+
"demo:dev": "cd demo && yarn dev"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@x402/core": "^2.2.0",
|
|
46
|
+
"@x402/extensions": "^2.2.0",
|
|
47
|
+
"@4mica/sdk": "^0.5.3",
|
|
48
|
+
"viem": "^2.45.1"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@arethetypeswrong/cli": "^0.18.2",
|
|
52
|
+
"@eslint/js": "^9.39.2",
|
|
53
|
+
"@types/express": "^5.0.6",
|
|
54
|
+
"@types/node": "^25.0.7",
|
|
55
|
+
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
|
56
|
+
"@typescript-eslint/parser": "^8.48.1",
|
|
57
|
+
"eslint": "^9.39.2",
|
|
58
|
+
"eslint-config-prettier": "^10.1.8",
|
|
59
|
+
"eslint-plugin-react": "^7.37.5",
|
|
60
|
+
"express": "^5.2.1",
|
|
61
|
+
"globals": "^17.0.0",
|
|
62
|
+
"prettier": "^3.7.4",
|
|
63
|
+
"typescript": "^5.3.3",
|
|
64
|
+
"typescript-eslint": "^8.48.1",
|
|
65
|
+
"vitest": "^4.0.17"
|
|
66
|
+
},
|
|
67
|
+
"peerDependencies": {
|
|
68
|
+
"@x402/paywall": "^2.2.0",
|
|
69
|
+
"express": "^4.0.0 || ^5.0.0"
|
|
70
|
+
},
|
|
71
|
+
"peerDependenciesMeta": {
|
|
72
|
+
"@x402/paywall": {
|
|
73
|
+
"optional": true
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"publishConfig": {
|
|
77
|
+
"access": "public"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './scheme.js'
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { SchemeNetworkClient, PaymentRequirements, PaymentPayload, Network } from '@x402/core/types'
|
|
2
|
+
import {
|
|
3
|
+
Client,
|
|
4
|
+
ConfigBuilder,
|
|
5
|
+
PaymentRequirementsV1,
|
|
6
|
+
X402Flow,
|
|
7
|
+
X402PaymentRequired,
|
|
8
|
+
} from '@4mica/sdk'
|
|
9
|
+
import { Account } from 'viem/accounts'
|
|
10
|
+
import { SUPPORTED_NETWORKS } from '../server/scheme.js'
|
|
11
|
+
|
|
12
|
+
const NETWORK_RPC_URLS: Record<Network, string> = {
|
|
13
|
+
'eip155:11155111': 'https://ethereum.sepolia.api.4mica.xyz',
|
|
14
|
+
'eip155:80002': 'https://api.4mica.xyz',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class FourMicaEvmScheme implements SchemeNetworkClient {
|
|
18
|
+
readonly scheme = '4mica-credit'
|
|
19
|
+
|
|
20
|
+
private constructor(
|
|
21
|
+
private readonly signer: Account,
|
|
22
|
+
// rpcUrl -> x402Flow
|
|
23
|
+
private readonly x402Flows: Map<string, X402Flow>
|
|
24
|
+
) {}
|
|
25
|
+
|
|
26
|
+
private static async createX402Flow(signer: Account, rpcUrl: string): Promise<X402Flow> {
|
|
27
|
+
const cfg = new ConfigBuilder().rpcUrl(rpcUrl).signer(signer).build()
|
|
28
|
+
const client = await Client.new(cfg)
|
|
29
|
+
|
|
30
|
+
return X402Flow.fromClient(client)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static async create(signer: Account): Promise<FourMicaEvmScheme> {
|
|
34
|
+
const x402Flows = new Map<string, X402Flow>()
|
|
35
|
+
|
|
36
|
+
for (const network of SUPPORTED_NETWORKS) {
|
|
37
|
+
const rpcUrl = NETWORK_RPC_URLS[network]
|
|
38
|
+
if (!rpcUrl) continue
|
|
39
|
+
|
|
40
|
+
x402Flows.set(rpcUrl, await FourMicaEvmScheme.createX402Flow(signer, rpcUrl))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return new FourMicaEvmScheme(signer, x402Flows)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async createPaymentPayload(
|
|
47
|
+
x402Version: number,
|
|
48
|
+
paymentRequirements: PaymentRequirements
|
|
49
|
+
): Promise<Pick<PaymentPayload, 'x402Version' | 'payload'>> {
|
|
50
|
+
const network = paymentRequirements.network as Network
|
|
51
|
+
if (!network) {
|
|
52
|
+
throw new Error('Network is required in PaymentRequirements')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const rpcUrl = (paymentRequirements.extra?.rpcUrl as string) ?? NETWORK_RPC_URLS[network]
|
|
56
|
+
if (!rpcUrl) {
|
|
57
|
+
throw new Error(`No RPC URL configured for network ${network}`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let x402Flow = this.x402Flows.get(rpcUrl)
|
|
61
|
+
if (!x402Flow) {
|
|
62
|
+
x402Flow = await FourMicaEvmScheme.createX402Flow(this.signer, rpcUrl)
|
|
63
|
+
this.x402Flows.set(rpcUrl, x402Flow)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (x402Version === 1) {
|
|
67
|
+
const signed = await x402Flow.signPayment(
|
|
68
|
+
paymentRequirements as unknown as PaymentRequirementsV1,
|
|
69
|
+
this.signer.address
|
|
70
|
+
)
|
|
71
|
+
return {
|
|
72
|
+
x402Version: 1,
|
|
73
|
+
payload: signed.payload as unknown as Record<string, unknown>,
|
|
74
|
+
}
|
|
75
|
+
} else if (x402Version === 2) {
|
|
76
|
+
const paymentRequired: X402PaymentRequired = {
|
|
77
|
+
x402Version: 2,
|
|
78
|
+
resource: { url: '', description: '', mimeType: '' },
|
|
79
|
+
accepts: [paymentRequirements],
|
|
80
|
+
}
|
|
81
|
+
const signed = await x402Flow.signPaymentV2(
|
|
82
|
+
paymentRequired,
|
|
83
|
+
paymentRequirements,
|
|
84
|
+
this.signer.address
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
x402Version: 2,
|
|
89
|
+
payload: signed.payload as unknown as Record<string, unknown>,
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
throw new Error(`Unsupported x402Version: ${x402Version}`)
|
|
94
|
+
}
|
|
95
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { HTTPAdapter } from '@x402/core/server'
|
|
2
|
+
import { Request } from 'express'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Express adapter implementation
|
|
6
|
+
*/
|
|
7
|
+
export class ExpressAdapter implements HTTPAdapter {
|
|
8
|
+
/**
|
|
9
|
+
* Creates a new ExpressAdapter instance.
|
|
10
|
+
*
|
|
11
|
+
* @param req - The Express request object
|
|
12
|
+
*/
|
|
13
|
+
constructor(private req: Request) {}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Gets a header value from the request.
|
|
17
|
+
*
|
|
18
|
+
* @param name - The header name
|
|
19
|
+
* @returns The header value or undefined
|
|
20
|
+
*/
|
|
21
|
+
getHeader(name: string): string | undefined {
|
|
22
|
+
const value = this.req.header(name)
|
|
23
|
+
return Array.isArray(value) ? value[0] : value
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Gets the HTTP method of the request.
|
|
28
|
+
*
|
|
29
|
+
* @returns The HTTP method
|
|
30
|
+
*/
|
|
31
|
+
getMethod(): string {
|
|
32
|
+
return this.req.method
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Gets the path of the request.
|
|
37
|
+
*
|
|
38
|
+
* @returns The request path
|
|
39
|
+
*/
|
|
40
|
+
getPath(): string {
|
|
41
|
+
return this.req.path
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Gets the full URL of the request.
|
|
46
|
+
*
|
|
47
|
+
* @returns The full request URL
|
|
48
|
+
*/
|
|
49
|
+
getUrl(): string {
|
|
50
|
+
return `${this.req.protocol}://${this.req.headers.host}${this.req.originalUrl}`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Gets the Accept header from the request.
|
|
55
|
+
*
|
|
56
|
+
* @returns The Accept header value or empty string
|
|
57
|
+
*/
|
|
58
|
+
getAcceptHeader(): string {
|
|
59
|
+
return this.req.header('Accept') || ''
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Gets the User-Agent header from the request.
|
|
64
|
+
*
|
|
65
|
+
* @returns The User-Agent header value or empty string
|
|
66
|
+
*/
|
|
67
|
+
getUserAgent(): string {
|
|
68
|
+
return this.req.header('User-Agent') || ''
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Gets all query parameters from the request URL.
|
|
73
|
+
*
|
|
74
|
+
* @returns Record of query parameter key-value pairs
|
|
75
|
+
*/
|
|
76
|
+
getQueryParams(): Record<string, string | string[]> {
|
|
77
|
+
return this.req.query as Record<string, string | string[]>
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Gets a specific query parameter by name.
|
|
82
|
+
*
|
|
83
|
+
* @param name - The query parameter name
|
|
84
|
+
* @returns The query parameter value(s) or undefined
|
|
85
|
+
*/
|
|
86
|
+
getQueryParam(name: string): string | string[] | undefined {
|
|
87
|
+
const value = this.req.query[name]
|
|
88
|
+
return value as string | string[] | undefined
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Gets the parsed request body.
|
|
93
|
+
* Requires express.json() or express.urlencoded() middleware.
|
|
94
|
+
*
|
|
95
|
+
* @returns The parsed request body
|
|
96
|
+
*/
|
|
97
|
+
getBody(): unknown {
|
|
98
|
+
return this.req.body
|
|
99
|
+
}
|
|
100
|
+
}
|