@xrplrequest/sdk 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/README.md ADDED
@@ -0,0 +1,234 @@
1
+ # @xrplrequest/sdk
2
+
3
+ TypeScript SDK for [XRPL Request](https://xrplre.quest) — wallet-agnostic XRPL transaction signing.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @xrplrequest/sdk
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```typescript
14
+ import { XRPLRequest } from '@xrplrequest/sdk';
15
+
16
+ const client = new XRPLRequest({ apiKey: 'xrplr_live_...' });
17
+
18
+ const payload = await client.payloads.create({
19
+ type: 'signAndSubmit',
20
+ transaction: {
21
+ TransactionType: 'Payment',
22
+ Destination: 'rXXX...',
23
+ Amount: '1000000', // 1 XRP in drops
24
+ },
25
+ options: { expiresIn: 300 },
26
+ });
27
+
28
+ console.log(payload.signingUrl); // share with user
29
+ // https://xrplre.quest/sign/{uuid}
30
+
31
+ const result = await client.payloads.poll(payload.uuid, { timeout: 300_000 });
32
+
33
+ if (result.status === 'signed') {
34
+ console.log('tx hash:', result.txHash);
35
+ }
36
+ ```
37
+
38
+ ## API
39
+
40
+ ### `new XRPLRequest(config)`
41
+
42
+ | Option | Type | Required | Description |
43
+ |---|---|---|---|
44
+ | `apiKey` | `string` | ✓ | Your project API key (`xrplr_live_...`) |
45
+ | `baseUrl` | `string` | | Override the API base URL (default: `https://xrplre.quest`) |
46
+
47
+ ---
48
+
49
+ ### `client.payloads.create(options)`
50
+
51
+ Create a new signing payload. Returns a `Payload` with a `signingUrl` to share with the user.
52
+
53
+ ```typescript
54
+ const payload = await client.payloads.create({
55
+ type: 'signAndSubmit', // 'connect' | 'sign' | 'signAndSubmit' | 'signMessage'
56
+ transaction: { ... }, // required for sign / signAndSubmit
57
+ message: 'Hello XRPL', // required for signMessage
58
+ options: {
59
+ expiresIn: 300, // seconds (default 300, max depends on plan)
60
+ returnUrl: 'https://...', // optional redirect after signing
61
+ customInstructions: '...', // shown to user on sign page (max 200 chars)
62
+ },
63
+ });
64
+
65
+ payload.uuid // unique identifier
66
+ payload.signingUrl // https://xrplre.quest/sign/{uuid}
67
+ payload.status // 'pending'
68
+ payload.expiresAt // ISO timestamp
69
+ ```
70
+
71
+ ### `client.payloads.get(uuid)`
72
+
73
+ Fetch the current status of a payload.
74
+
75
+ ```typescript
76
+ const payload = await client.payloads.get(uuid);
77
+ // payload.status: 'pending' | 'signed' | 'rejected' | 'expired'
78
+ // payload.txHash — populated when signed + submitted
79
+ // payload.signerAddress — populated when signed
80
+ // payload.walletAdapter — which wallet was used
81
+ ```
82
+
83
+ ### `client.payloads.poll(uuid, options?)`
84
+
85
+ Poll until the payload leaves `pending` state or the timeout is reached.
86
+
87
+ ```typescript
88
+ const result = await client.payloads.poll(uuid, {
89
+ timeout: 300_000, // max wait in ms (default 300,000)
90
+ interval: 2_000, // poll interval in ms (default 2,000; min 3s on Free plan)
91
+ onUpdate: (payload) => {
92
+ console.log('still waiting:', payload.status);
93
+ },
94
+ });
95
+ ```
96
+
97
+ ### `client.payloads.cancel(uuid)`
98
+
99
+ Cancel a pending payload.
100
+
101
+ ### `client.payloads.list(options?)`
102
+
103
+ List payloads for your project.
104
+
105
+ ```typescript
106
+ const { payloads } = await client.payloads.list({ limit: 20, offset: 0 });
107
+ ```
108
+
109
+ ---
110
+
111
+ ### `client.webhooks.create(options)`
112
+
113
+ Register a webhook URL. The returned `secret` is used to verify signatures — save it, it won't be shown again.
114
+
115
+ ```typescript
116
+ const webhook = await client.webhooks.create({
117
+ url: 'https://yourapp.com/xrplrequest-webhook',
118
+ events: ['payload.signed', 'payload.rejected', 'payload.expired'],
119
+ });
120
+ console.log(webhook.secret); // save this
121
+ ```
122
+
123
+ ### `client.webhooks.list()`
124
+
125
+ List webhooks for your project.
126
+
127
+ ### `client.webhooks.delete(id)`
128
+
129
+ Remove a webhook.
130
+
131
+ ---
132
+
133
+ ### `verifyWebhookSignature(secret, rawBody, signature)`
134
+
135
+ Standalone function to verify an incoming webhook request. Also available as `WebhooksClient.verify`.
136
+
137
+ ```typescript
138
+ import { verifyWebhookSignature } from '@xrplrequest/sdk';
139
+
140
+ // Express example
141
+ app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
142
+ const sig = req.headers['x-xrpl-request-signature'] as string;
143
+ const valid = verifyWebhookSignature(process.env.WEBHOOK_SECRET!, req.body.toString(), sig);
144
+
145
+ if (!valid) return res.status(401).send('Invalid signature');
146
+
147
+ const { event, payload } = JSON.parse(req.body.toString());
148
+ console.log(event, payload.txHash);
149
+ res.sendStatus(200);
150
+ });
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Payload types
156
+
157
+ | Type | Description |
158
+ |---|---|
159
+ | `connect` | Request wallet address / identity verification. No transaction. |
160
+ | `sign` | Sign a transaction without submitting to the ledger. |
161
+ | `signAndSubmit` | Sign and broadcast to the XRP Ledger. Returns `txHash`. |
162
+ | `signMessage` | Sign an arbitrary UTF-8 message string. |
163
+
164
+ ## Webhook events
165
+
166
+ | Event | When |
167
+ |---|---|
168
+ | `payload.signed` | User signed (and optionally submitted) the transaction |
169
+ | `payload.rejected` | User rejected in their wallet |
170
+ | `payload.expired` | TTL elapsed without resolution |
171
+
172
+ Webhook POST body:
173
+ ```json
174
+ {
175
+ "event": "payload.signed",
176
+ "payload": {
177
+ "uuid": "...",
178
+ "type": "signAndSubmit",
179
+ "status": "signed",
180
+ "signerAddress": "rXXX...",
181
+ "txHash": "ABC123...",
182
+ "walletAdapter": "crossmark",
183
+ "createdAt": "2026-05-08T12:00:00.000Z",
184
+ "resolvedAt": "2026-05-08T12:01:23.000Z"
185
+ }
186
+ }
187
+ ```
188
+
189
+ Signature header: `X-XRPL-Request-Signature: sha256=<hmac>`
190
+
191
+ ## Discord bot example
192
+
193
+ ```typescript
194
+ import { Client, GatewayIntentBits } from 'discord.js';
195
+ import { XRPLRequest } from '@xrplrequest/sdk';
196
+
197
+ const xrpl = new XRPLRequest({ apiKey: process.env.XRPL_REQUEST_API_KEY! });
198
+ const discord = new Client({ intents: [GatewayIntentBits.Guilds] });
199
+
200
+ discord.on('interactionCreate', async (interaction) => {
201
+ if (!interaction.isChatInputCommand() || interaction.commandName !== 'tip') return;
202
+
203
+ const amount = interaction.options.getInteger('amount', true);
204
+ const destination = interaction.options.getString('destination', true);
205
+
206
+ const payload = await xrpl.payloads.create({
207
+ type: 'signAndSubmit',
208
+ transaction: {
209
+ TransactionType: 'Payment',
210
+ Destination: destination,
211
+ Amount: String(amount * 1_000_000), // XRP → drops
212
+ },
213
+ });
214
+
215
+ await interaction.reply({
216
+ content: `Sign this transaction to send ${amount} XRP:\n${payload.signingUrl}`,
217
+ ephemeral: true,
218
+ });
219
+
220
+ const result = await xrpl.payloads.poll(payload.uuid, { timeout: 300_000 });
221
+
222
+ if (result.status === 'signed') {
223
+ await interaction.editReply(`✅ Sent! Tx: \`${result.txHash}\``);
224
+ } else if (result.status === 'rejected') {
225
+ await interaction.editReply('❌ Transaction rejected.');
226
+ } else {
227
+ await interaction.editReply('⏱ Request expired.');
228
+ }
229
+ });
230
+ ```
231
+
232
+ ## License
233
+
234
+ MIT
@@ -0,0 +1,117 @@
1
+ type PayloadType = "connect" | "sign" | "signAndSubmit" | "signMessage";
2
+ type PayloadStatus = "pending" | "signed" | "rejected" | "expired";
3
+ interface CreatePayloadOptions {
4
+ type: PayloadType;
5
+ transaction?: Record<string, unknown>;
6
+ message?: string;
7
+ options?: {
8
+ expiresIn?: number;
9
+ returnUrl?: string;
10
+ customInstructions?: string;
11
+ };
12
+ }
13
+ interface Payload {
14
+ uuid: string;
15
+ type: PayloadType;
16
+ status: PayloadStatus;
17
+ network: "mainnet" | "testnet";
18
+ signingUrl: string;
19
+ expiresAt: string;
20
+ createdAt: string;
21
+ signerAddress?: string | null;
22
+ txHash?: string | null;
23
+ walletAdapter?: string | null;
24
+ resolvedAt?: string | null;
25
+ }
26
+ interface PollOptions {
27
+ timeout?: number;
28
+ interval?: number;
29
+ onUpdate?: (payload: Payload) => void;
30
+ }
31
+ interface XRPLRequestConfig {
32
+ apiKey: string;
33
+ baseUrl?: string;
34
+ }
35
+ interface CreateWebhookOptions {
36
+ url: string;
37
+ events?: Array<"payload.signed" | "payload.rejected" | "payload.expired">;
38
+ }
39
+ interface Webhook {
40
+ id: string;
41
+ url: string;
42
+ events: string[];
43
+ active: boolean;
44
+ createdAt: string;
45
+ }
46
+ interface Analytics {
47
+ period: string;
48
+ totals: {
49
+ total: number;
50
+ signed: number;
51
+ rejected: number;
52
+ expired: number;
53
+ pending: number;
54
+ };
55
+ walletBreakdown: Array<{
56
+ walletAdapter: string | null;
57
+ count: number;
58
+ }>;
59
+ }
60
+
61
+ declare class PayloadsClient {
62
+ private readonly apiKey;
63
+ private readonly baseUrl;
64
+ constructor(apiKey: string, baseUrl: string);
65
+ private get headers();
66
+ create(options: CreatePayloadOptions): Promise<Payload>;
67
+ get(uuid: string): Promise<Payload>;
68
+ cancel(uuid: string): Promise<Payload>;
69
+ list(options?: {
70
+ limit?: number;
71
+ offset?: number;
72
+ }): Promise<{
73
+ payloads: Payload[];
74
+ }>;
75
+ poll(uuid: string, options?: PollOptions): Promise<Payload>;
76
+ }
77
+ declare class XRPLRequestError extends Error {
78
+ readonly statusCode: number;
79
+ constructor(message: string, statusCode: number);
80
+ }
81
+
82
+ /**
83
+ * Verify an incoming webhook signature from XRPL Request.
84
+ * Use this in your webhook endpoint to confirm the request is authentic.
85
+ *
86
+ * @param secret - The webhook secret shown when the webhook was created
87
+ * @param rawBody - The raw request body string (before JSON.parse)
88
+ * @param signature - The value of the X-XRPL-Request-Signature header
89
+ */
90
+ declare function verifyWebhookSignature(secret: string, rawBody: string, signature: string): boolean;
91
+
92
+ declare class WebhooksClient {
93
+ private readonly apiKey;
94
+ private readonly baseUrl;
95
+ constructor(apiKey: string, baseUrl: string);
96
+ private get headers();
97
+ create(options: CreateWebhookOptions): Promise<Webhook & {
98
+ secret: string;
99
+ }>;
100
+ list(): Promise<{
101
+ webhooks: Webhook[];
102
+ }>;
103
+ delete(id: string): Promise<{
104
+ id: string;
105
+ deleted: boolean;
106
+ }>;
107
+ /** Verify an incoming webhook signature. Convenience alias for `verifyWebhookSignature`. */
108
+ static verify: typeof verifyWebhookSignature;
109
+ }
110
+
111
+ declare class XRPLRequest {
112
+ readonly payloads: PayloadsClient;
113
+ readonly webhooks: WebhooksClient;
114
+ constructor(config: XRPLRequestConfig);
115
+ }
116
+
117
+ export { type Analytics, type CreatePayloadOptions, type CreateWebhookOptions, type Payload, type PayloadStatus, type PayloadType, type PollOptions, type Webhook, WebhooksClient, XRPLRequest, type XRPLRequestConfig, XRPLRequestError, verifyWebhookSignature };
@@ -0,0 +1,117 @@
1
+ type PayloadType = "connect" | "sign" | "signAndSubmit" | "signMessage";
2
+ type PayloadStatus = "pending" | "signed" | "rejected" | "expired";
3
+ interface CreatePayloadOptions {
4
+ type: PayloadType;
5
+ transaction?: Record<string, unknown>;
6
+ message?: string;
7
+ options?: {
8
+ expiresIn?: number;
9
+ returnUrl?: string;
10
+ customInstructions?: string;
11
+ };
12
+ }
13
+ interface Payload {
14
+ uuid: string;
15
+ type: PayloadType;
16
+ status: PayloadStatus;
17
+ network: "mainnet" | "testnet";
18
+ signingUrl: string;
19
+ expiresAt: string;
20
+ createdAt: string;
21
+ signerAddress?: string | null;
22
+ txHash?: string | null;
23
+ walletAdapter?: string | null;
24
+ resolvedAt?: string | null;
25
+ }
26
+ interface PollOptions {
27
+ timeout?: number;
28
+ interval?: number;
29
+ onUpdate?: (payload: Payload) => void;
30
+ }
31
+ interface XRPLRequestConfig {
32
+ apiKey: string;
33
+ baseUrl?: string;
34
+ }
35
+ interface CreateWebhookOptions {
36
+ url: string;
37
+ events?: Array<"payload.signed" | "payload.rejected" | "payload.expired">;
38
+ }
39
+ interface Webhook {
40
+ id: string;
41
+ url: string;
42
+ events: string[];
43
+ active: boolean;
44
+ createdAt: string;
45
+ }
46
+ interface Analytics {
47
+ period: string;
48
+ totals: {
49
+ total: number;
50
+ signed: number;
51
+ rejected: number;
52
+ expired: number;
53
+ pending: number;
54
+ };
55
+ walletBreakdown: Array<{
56
+ walletAdapter: string | null;
57
+ count: number;
58
+ }>;
59
+ }
60
+
61
+ declare class PayloadsClient {
62
+ private readonly apiKey;
63
+ private readonly baseUrl;
64
+ constructor(apiKey: string, baseUrl: string);
65
+ private get headers();
66
+ create(options: CreatePayloadOptions): Promise<Payload>;
67
+ get(uuid: string): Promise<Payload>;
68
+ cancel(uuid: string): Promise<Payload>;
69
+ list(options?: {
70
+ limit?: number;
71
+ offset?: number;
72
+ }): Promise<{
73
+ payloads: Payload[];
74
+ }>;
75
+ poll(uuid: string, options?: PollOptions): Promise<Payload>;
76
+ }
77
+ declare class XRPLRequestError extends Error {
78
+ readonly statusCode: number;
79
+ constructor(message: string, statusCode: number);
80
+ }
81
+
82
+ /**
83
+ * Verify an incoming webhook signature from XRPL Request.
84
+ * Use this in your webhook endpoint to confirm the request is authentic.
85
+ *
86
+ * @param secret - The webhook secret shown when the webhook was created
87
+ * @param rawBody - The raw request body string (before JSON.parse)
88
+ * @param signature - The value of the X-XRPL-Request-Signature header
89
+ */
90
+ declare function verifyWebhookSignature(secret: string, rawBody: string, signature: string): boolean;
91
+
92
+ declare class WebhooksClient {
93
+ private readonly apiKey;
94
+ private readonly baseUrl;
95
+ constructor(apiKey: string, baseUrl: string);
96
+ private get headers();
97
+ create(options: CreateWebhookOptions): Promise<Webhook & {
98
+ secret: string;
99
+ }>;
100
+ list(): Promise<{
101
+ webhooks: Webhook[];
102
+ }>;
103
+ delete(id: string): Promise<{
104
+ id: string;
105
+ deleted: boolean;
106
+ }>;
107
+ /** Verify an incoming webhook signature. Convenience alias for `verifyWebhookSignature`. */
108
+ static verify: typeof verifyWebhookSignature;
109
+ }
110
+
111
+ declare class XRPLRequest {
112
+ readonly payloads: PayloadsClient;
113
+ readonly webhooks: WebhooksClient;
114
+ constructor(config: XRPLRequestConfig);
115
+ }
116
+
117
+ export { type Analytics, type CreatePayloadOptions, type CreateWebhookOptions, type Payload, type PayloadStatus, type PayloadType, type PollOptions, type Webhook, WebhooksClient, XRPLRequest, type XRPLRequestConfig, XRPLRequestError, verifyWebhookSignature };
package/dist/index.js ADDED
@@ -0,0 +1,171 @@
1
+ 'use strict';
2
+
3
+ var crypto = require('crypto');
4
+
5
+ // src/api-error.ts
6
+ function apiErrorMessage(body, fallback) {
7
+ if (typeof body === "object" && body !== null && "error" in body && typeof body.error === "string") {
8
+ return body.error;
9
+ }
10
+ return fallback;
11
+ }
12
+
13
+ // src/payloads.ts
14
+ var DEFAULT_POLL_INTERVAL = 2e3;
15
+ var DEFAULT_POLL_TIMEOUT = 3e5;
16
+ var PayloadsClient = class {
17
+ constructor(apiKey, baseUrl) {
18
+ this.apiKey = apiKey;
19
+ this.baseUrl = baseUrl;
20
+ }
21
+ get headers() {
22
+ return {
23
+ Authorization: `Bearer ${this.apiKey}`,
24
+ "Content-Type": "application/json"
25
+ };
26
+ }
27
+ async create(options) {
28
+ const res = await fetch(`${this.baseUrl}/api/v1/payloads`, {
29
+ method: "POST",
30
+ headers: this.headers,
31
+ body: JSON.stringify(options)
32
+ });
33
+ if (!res.ok) {
34
+ const body = await res.json().catch(() => null);
35
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to create payload"), res.status);
36
+ }
37
+ const data = await res.json();
38
+ return data;
39
+ }
40
+ async get(uuid) {
41
+ const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {
42
+ headers: this.headers
43
+ });
44
+ if (!res.ok) {
45
+ const body = await res.json().catch(() => null);
46
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to get payload"), res.status);
47
+ }
48
+ return res.json();
49
+ }
50
+ async cancel(uuid) {
51
+ const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {
52
+ method: "DELETE",
53
+ headers: this.headers
54
+ });
55
+ if (!res.ok) {
56
+ const body = await res.json().catch(() => null);
57
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to cancel payload"), res.status);
58
+ }
59
+ return res.json();
60
+ }
61
+ async list(options) {
62
+ const url = new URL(`${this.baseUrl}/api/v1/payloads`);
63
+ if (options?.limit) url.searchParams.set("limit", String(options.limit));
64
+ if (options?.offset) url.searchParams.set("offset", String(options.offset));
65
+ const res = await fetch(url.toString(), { headers: this.headers });
66
+ if (!res.ok) {
67
+ const body = await res.json().catch(() => null);
68
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to list payloads"), res.status);
69
+ }
70
+ return res.json();
71
+ }
72
+ async poll(uuid, options) {
73
+ const timeout = options?.timeout ?? DEFAULT_POLL_TIMEOUT;
74
+ const interval = options?.interval ?? DEFAULT_POLL_INTERVAL;
75
+ const deadline = Date.now() + timeout;
76
+ while (Date.now() < deadline) {
77
+ const payload = await this.get(uuid);
78
+ if (options?.onUpdate) options.onUpdate(payload);
79
+ if (payload.status !== "pending") return payload;
80
+ const remaining = deadline - Date.now();
81
+ if (remaining <= 0) break;
82
+ await sleep(Math.min(interval, remaining));
83
+ }
84
+ return this.get(uuid);
85
+ }
86
+ };
87
+ function sleep(ms) {
88
+ return new Promise((resolve) => setTimeout(resolve, ms));
89
+ }
90
+ var XRPLRequestError = class extends Error {
91
+ constructor(message, statusCode) {
92
+ super(message);
93
+ this.statusCode = statusCode;
94
+ this.name = "XRPLRequestError";
95
+ }
96
+ };
97
+ function verifyWebhookSignature(secret, rawBody, signature) {
98
+ if (typeof signature !== "string" || !signature.startsWith("sha256=")) return false;
99
+ const expected = `sha256=${crypto.createHmac("sha256", secret).update(rawBody).digest("hex")}`;
100
+ if (expected.length !== signature.length) return false;
101
+ let diff = 0;
102
+ for (let i = 0; i < expected.length; i++) {
103
+ diff |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
104
+ }
105
+ return diff === 0;
106
+ }
107
+
108
+ // src/webhooks.ts
109
+ var WebhooksClient = class {
110
+ constructor(apiKey, baseUrl) {
111
+ this.apiKey = apiKey;
112
+ this.baseUrl = baseUrl;
113
+ }
114
+ get headers() {
115
+ return {
116
+ Authorization: `Bearer ${this.apiKey}`,
117
+ "Content-Type": "application/json"
118
+ };
119
+ }
120
+ async create(options) {
121
+ const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, {
122
+ method: "POST",
123
+ headers: this.headers,
124
+ body: JSON.stringify(options)
125
+ });
126
+ if (!res.ok) {
127
+ const body = await res.json().catch(() => null);
128
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to create webhook"), res.status);
129
+ }
130
+ return res.json();
131
+ }
132
+ async list() {
133
+ const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, { headers: this.headers });
134
+ if (!res.ok) {
135
+ const body = await res.json().catch(() => null);
136
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to list webhooks"), res.status);
137
+ }
138
+ return res.json();
139
+ }
140
+ async delete(id) {
141
+ const res = await fetch(`${this.baseUrl}/api/v1/webhooks/${id}`, {
142
+ method: "DELETE",
143
+ headers: this.headers
144
+ });
145
+ if (!res.ok) {
146
+ const body = await res.json().catch(() => null);
147
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to delete webhook"), res.status);
148
+ }
149
+ return res.json();
150
+ }
151
+ };
152
+ /** Verify an incoming webhook signature. Convenience alias for `verifyWebhookSignature`. */
153
+ WebhooksClient.verify = verifyWebhookSignature;
154
+
155
+ // src/index.ts
156
+ var DEFAULT_BASE_URL = "https://xrplre.quest";
157
+ var XRPLRequest = class {
158
+ constructor(config) {
159
+ if (!config.apiKey) throw new Error("apiKey is required");
160
+ const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
161
+ this.payloads = new PayloadsClient(config.apiKey, baseUrl);
162
+ this.webhooks = new WebhooksClient(config.apiKey, baseUrl);
163
+ }
164
+ };
165
+
166
+ exports.WebhooksClient = WebhooksClient;
167
+ exports.XRPLRequest = XRPLRequest;
168
+ exports.XRPLRequestError = XRPLRequestError;
169
+ exports.verifyWebhookSignature = verifyWebhookSignature;
170
+ //# sourceMappingURL=index.js.map
171
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api-error.ts","../src/payloads.ts","../src/utils.ts","../src/webhooks.ts","../src/index.ts"],"names":["createHmac"],"mappings":";;;;;AAAO,SAAS,eAAA,CAAgB,MAAe,QAAA,EAA0B;AACvE,EAAA,IACE,OAAO,IAAA,KAAS,QAAA,IAChB,IAAA,KAAS,IAAA,IACT,WAAW,IAAA,IACX,OAAQ,IAAA,CAA4B,KAAA,KAAU,QAAA,EAC9C;AACA,IAAA,OAAQ,IAAA,CAA2B,KAAA;AAAA,EACrC;AACA,EAAA,OAAO,QAAA;AACT;;;ACNA,IAAM,qBAAA,GAAwB,GAAA;AAC9B,IAAM,oBAAA,GAAuB,GAAA;AAEtB,IAAM,iBAAN,MAAqB;AAAA,EAC1B,WAAA,CACmB,QACA,OAAA,EACjB;AAFiB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAChB;AAAA,EAEH,IAAY,OAAA,GAAU;AACpB,IAAA,OAAO;AAAA,MACL,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,MACpC,cAAA,EAAgB;AAAA,KAClB;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,OAAA,EAAiD;AAC5D,IAAA,MAAM,MAAM,MAAM,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,gBAAA,CAAA,EAAoB;AAAA,MACzD,MAAA,EAAQ,MAAA;AAAA,MACR,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,OAAO;AAAA,KAC7B,CAAA;AAED,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,OAAO,MAAM,GAAA,CAAI,MAAK,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA;AAC9C,MAAA,MAAM,IAAI,gBAAA,CAAiB,eAAA,CAAgB,MAAM,0BAA0B,CAAA,EAAG,IAAI,MAAM,CAAA;AAAA,IAC1F;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAM,IAAI,IAAA,EAAgC;AACxC,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,CAAA,EAAG,KAAK,OAAO,CAAA,iBAAA,EAAoB,IAAI,CAAA,CAAA,EAAI;AAAA,MACjE,SAAS,IAAA,CAAK;AAAA,KACf,CAAA;AAED,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,OAAO,MAAM,GAAA,CAAI,MAAK,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA;AAC9C,MAAA,MAAM,IAAI,gBAAA,CAAiB,eAAA,CAAgB,MAAM,uBAAuB,CAAA,EAAG,IAAI,MAAM,CAAA;AAAA,IACvF;AAEA,IAAA,OAAO,IAAI,IAAA,EAAK;AAAA,EAClB;AAAA,EAEA,MAAM,OAAO,IAAA,EAAgC;AAC3C,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,CAAA,EAAG,KAAK,OAAO,CAAA,iBAAA,EAAoB,IAAI,CAAA,CAAA,EAAI;AAAA,MACjE,MAAA,EAAQ,QAAA;AAAA,MACR,SAAS,IAAA,CAAK;AAAA,KACf,CAAA;AAED,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,OAAO,MAAM,GAAA,CAAI,MAAK,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA;AAC9C,MAAA,MAAM,IAAI,gBAAA,CAAiB,eAAA,CAAgB,MAAM,0BAA0B,CAAA,EAAG,IAAI,MAAM,CAAA;AAAA,IAC1F;AAEA,IAAA,OAAO,IAAI,IAAA,EAAK;AAAA,EAClB;AAAA,EAEA,MAAM,KAAK,OAAA,EAAiF;AAC1F,IAAA,MAAM,MAAM,IAAI,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,gBAAA,CAAkB,CAAA;AACrD,IAAA,IAAI,OAAA,EAAS,OAAO,GAAA,CAAI,YAAA,CAAa,IAAI,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAC,CAAA;AACvE,IAAA,IAAI,OAAA,EAAS,QAAQ,GAAA,CAAI,YAAA,CAAa,IAAI,QAAA,EAAU,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAC,CAAA;AAE1E,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,CAAI,QAAA,IAAY,EAAE,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,CAAA;AACjE,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,OAAO,MAAM,GAAA,CAAI,MAAK,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA;AAC9C,MAAA,MAAM,IAAI,gBAAA,CAAiB,eAAA,CAAgB,MAAM,yBAAyB,CAAA,EAAG,IAAI,MAAM,CAAA;AAAA,IACzF;AAEA,IAAA,OAAO,IAAI,IAAA,EAAK;AAAA,EAClB;AAAA,EAEA,MAAM,IAAA,CAAK,IAAA,EAAc,OAAA,EAAyC;AAChE,IAAA,MAAM,OAAA,GAAU,SAAS,OAAA,IAAW,oBAAA;AACpC,IAAA,MAAM,QAAA,GAAW,SAAS,QAAA,IAAY,qBAAA;AACtC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,OAAA;AAE9B,IAAA,OAAO,IAAA,CAAK,GAAA,EAAI,GAAI,QAAA,EAAU;AAC5B,MAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA;AAEnC,MAAA,IAAI,OAAA,EAAS,QAAA,EAAU,OAAA,CAAQ,QAAA,CAAS,OAAO,CAAA;AAE/C,MAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,SAAA,EAAW,OAAO,OAAA;AAEzC,MAAA,MAAM,SAAA,GAAY,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI;AACtC,MAAA,IAAI,aAAa,CAAA,EAAG;AAEpB,MAAA,MAAM,KAAA,CAAM,IAAA,CAAK,GAAA,CAAI,QAAA,EAAU,SAAS,CAAC,CAAA;AAAA,IAC3C;AAGA,IAAA,OAAO,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA,EACtB;AACF,CAAA;AAEA,SAAS,MAAM,EAAA,EAA2B;AACxC,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACzD;AAEO,IAAM,gBAAA,GAAN,cAA+B,KAAA,CAAM;AAAA,EAC1C,WAAA,CAAY,SAAiC,UAAA,EAAoB;AAC/D,IAAA,KAAA,CAAM,OAAO,CAAA;AAD8B,IAAA,IAAA,CAAA,UAAA,GAAA,UAAA;AAE3C,IAAA,IAAA,CAAK,IAAA,GAAO,kBAAA;AAAA,EACd;AACF;ACnGO,SAAS,sBAAA,CACd,MAAA,EACA,OAAA,EACA,SAAA,EACS;AACT,EAAA,IAAI,OAAO,cAAc,QAAA,IAAY,CAAC,UAAU,UAAA,CAAW,SAAS,GAAG,OAAO,KAAA;AAC9E,EAAA,MAAM,QAAA,GAAW,CAAA,OAAA,EAAUA,iBAAA,CAAW,QAAA,EAAU,MAAM,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,CAAE,MAAA,CAAO,KAAK,CAAC,CAAA,CAAA;AACrF,EAAA,IAAI,QAAA,CAAS,MAAA,KAAW,SAAA,CAAU,MAAA,EAAQ,OAAO,KAAA;AAEjD,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,QAAA,CAAS,QAAQ,CAAA,EAAA,EAAK;AACxC,IAAA,IAAA,IAAQ,SAAS,UAAA,CAAW,CAAC,CAAA,GAAI,SAAA,CAAU,WAAW,CAAC,CAAA;AAAA,EACzD;AACA,EAAA,OAAO,IAAA,KAAS,CAAA;AAClB;;;ACnBO,IAAM,iBAAN,MAAqB;AAAA,EAC1B,WAAA,CACmB,QACA,OAAA,EACjB;AAFiB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAChB;AAAA,EAEH,IAAY,OAAA,GAAU;AACpB,IAAA,OAAO;AAAA,MACL,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,MACpC,cAAA,EAAgB;AAAA,KAClB;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,OAAA,EAAsE;AACjF,IAAA,MAAM,MAAM,MAAM,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,gBAAA,CAAA,EAAoB;AAAA,MACzD,MAAA,EAAQ,MAAA;AAAA,MACR,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,OAAO;AAAA,KAC7B,CAAA;AACD,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,OAAO,MAAM,GAAA,CAAI,MAAK,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA;AAC9C,MAAA,MAAM,IAAI,gBAAA,CAAiB,eAAA,CAAgB,MAAM,0BAA0B,CAAA,EAAG,IAAI,MAAM,CAAA;AAAA,IAC1F;AACA,IAAA,OAAO,IAAI,IAAA,EAAK;AAAA,EAClB;AAAA,EAEA,MAAM,IAAA,GAAyC;AAC7C,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,gBAAA,CAAA,EAAoB,EAAE,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,CAAA;AACpF,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,OAAO,MAAM,GAAA,CAAI,MAAK,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA;AAC9C,MAAA,MAAM,IAAI,gBAAA,CAAiB,eAAA,CAAgB,MAAM,yBAAyB,CAAA,EAAG,IAAI,MAAM,CAAA;AAAA,IACzF;AACA,IAAA,OAAO,IAAI,IAAA,EAAK;AAAA,EAClB;AAAA,EAEA,MAAM,OAAO,EAAA,EAAuD;AAClE,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,CAAA,EAAG,KAAK,OAAO,CAAA,iBAAA,EAAoB,EAAE,CAAA,CAAA,EAAI;AAAA,MAC/D,MAAA,EAAQ,QAAA;AAAA,MACR,SAAS,IAAA,CAAK;AAAA,KACf,CAAA;AACD,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,OAAO,MAAM,GAAA,CAAI,MAAK,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA;AAC9C,MAAA,MAAM,IAAI,gBAAA,CAAiB,eAAA,CAAgB,MAAM,0BAA0B,CAAA,EAAG,IAAI,MAAM,CAAA;AAAA,IAC1F;AACA,IAAA,OAAO,IAAI,IAAA,EAAK;AAAA,EAClB;AAIF;AAAA;AAjDa,cAAA,CAgDJ,MAAA,GAAS,sBAAA;;;AC5ClB,IAAM,gBAAA,GAAmB,sBAAA;AAElB,IAAM,cAAN,MAAkB;AAAA,EAIvB,YAAY,MAAA,EAA2B;AACrC,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,EAAQ,MAAM,IAAI,MAAM,oBAAoB,CAAA;AACxD,IAAA,MAAM,WAAW,MAAA,CAAO,OAAA,IAAW,gBAAA,EAAkB,OAAA,CAAQ,OAAO,EAAE,CAAA;AACtE,IAAA,IAAA,CAAK,QAAA,GAAW,IAAI,cAAA,CAAe,MAAA,CAAO,QAAQ,OAAO,CAAA;AACzD,IAAA,IAAA,CAAK,QAAA,GAAW,IAAI,cAAA,CAAe,MAAA,CAAO,QAAQ,OAAO,CAAA;AAAA,EAC3D;AACF","file":"index.js","sourcesContent":["export function apiErrorMessage(body: unknown, fallback: string): string {\n if (\n typeof body === \"object\" &&\n body !== null &&\n \"error\" in body &&\n typeof (body as { error: unknown }).error === \"string\"\n ) {\n return (body as { error: string }).error;\n }\n return fallback;\n}\n","import { apiErrorMessage } from \"./api-error\";\nimport type { CreatePayloadOptions, Payload, PollOptions } from \"./types\";\n\nconst DEFAULT_BASE_URL = \"https://xrplre.quest\";\nconst DEFAULT_POLL_INTERVAL = 2000;\nconst DEFAULT_POLL_TIMEOUT = 300_000;\n\nexport class PayloadsClient {\n constructor(\n private readonly apiKey: string,\n private readonly baseUrl: string\n ) {}\n\n private get headers() {\n return {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n async create(options: CreatePayloadOptions): Promise<Payload> {\n const res = await fetch(`${this.baseUrl}/api/v1/payloads`, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(options),\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to create payload\"), res.status);\n }\n\n const data = await res.json();\n return data as Payload;\n }\n\n async get(uuid: string): Promise<Payload> {\n const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {\n headers: this.headers,\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to get payload\"), res.status);\n }\n\n return res.json() as Promise<Payload>;\n }\n\n async cancel(uuid: string): Promise<Payload> {\n const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {\n method: \"DELETE\",\n headers: this.headers,\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to cancel payload\"), res.status);\n }\n\n return res.json() as Promise<Payload>;\n }\n\n async list(options?: { limit?: number; offset?: number }): Promise<{ payloads: Payload[] }> {\n const url = new URL(`${this.baseUrl}/api/v1/payloads`);\n if (options?.limit) url.searchParams.set(\"limit\", String(options.limit));\n if (options?.offset) url.searchParams.set(\"offset\", String(options.offset));\n\n const res = await fetch(url.toString(), { headers: this.headers });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to list payloads\"), res.status);\n }\n\n return res.json() as Promise<{ payloads: Payload[] }>;\n }\n\n async poll(uuid: string, options?: PollOptions): Promise<Payload> {\n const timeout = options?.timeout ?? DEFAULT_POLL_TIMEOUT;\n const interval = options?.interval ?? DEFAULT_POLL_INTERVAL;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const payload = await this.get(uuid);\n\n if (options?.onUpdate) options.onUpdate(payload);\n\n if (payload.status !== \"pending\") return payload;\n\n const remaining = deadline - Date.now();\n if (remaining <= 0) break;\n\n await sleep(Math.min(interval, remaining));\n }\n\n // Final check\n return this.get(uuid);\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport class XRPLRequestError extends Error {\n constructor(message: string, public readonly statusCode: number) {\n super(message);\n this.name = \"XRPLRequestError\";\n }\n}\n","import { createHmac } from \"crypto\";\n\n/**\n * Verify an incoming webhook signature from XRPL Request.\n * Use this in your webhook endpoint to confirm the request is authentic.\n *\n * @param secret - The webhook secret shown when the webhook was created\n * @param rawBody - The raw request body string (before JSON.parse)\n * @param signature - The value of the X-XRPL-Request-Signature header\n */\nexport function verifyWebhookSignature(\n secret: string,\n rawBody: string,\n signature: string,\n): boolean {\n if (typeof signature !== \"string\" || !signature.startsWith(\"sha256=\")) return false;\n const expected = `sha256=${createHmac(\"sha256\", secret).update(rawBody).digest(\"hex\")}`;\n if (expected.length !== signature.length) return false;\n // Constant-time comparison\n let diff = 0;\n for (let i = 0; i < expected.length; i++) {\n diff |= expected.charCodeAt(i) ^ signature.charCodeAt(i);\n }\n return diff === 0;\n}\n","import { apiErrorMessage } from \"./api-error\";\nimport type { CreateWebhookOptions, Webhook } from \"./types\";\nimport { XRPLRequestError } from \"./payloads\";\nimport { verifyWebhookSignature } from \"./utils\";\n\nexport class WebhooksClient {\n constructor(\n private readonly apiKey: string,\n private readonly baseUrl: string,\n ) {}\n\n private get headers() {\n return {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n async create(options: CreateWebhookOptions): Promise<Webhook & { secret: string }> {\n const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(options),\n });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to create webhook\"), res.status);\n }\n return res.json() as Promise<Webhook & { secret: string }>;\n }\n\n async list(): Promise<{ webhooks: Webhook[] }> {\n const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, { headers: this.headers });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to list webhooks\"), res.status);\n }\n return res.json() as Promise<{ webhooks: Webhook[] }>;\n }\n\n async delete(id: string): Promise<{ id: string; deleted: boolean }> {\n const res = await fetch(`${this.baseUrl}/api/v1/webhooks/${id}`, {\n method: \"DELETE\",\n headers: this.headers,\n });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to delete webhook\"), res.status);\n }\n return res.json() as Promise<{ id: string; deleted: boolean }>;\n }\n\n /** Verify an incoming webhook signature. Convenience alias for `verifyWebhookSignature`. */\n static verify = verifyWebhookSignature;\n}\n","import { PayloadsClient, XRPLRequestError } from \"./payloads\";\nimport { WebhooksClient } from \"./webhooks\";\nimport type { XRPLRequestConfig } from \"./types\";\n\nexport * from \"./types\";\nexport { XRPLRequestError } from \"./payloads\";\nexport { WebhooksClient } from \"./webhooks\";\nexport { verifyWebhookSignature } from \"./utils\";\n\nconst DEFAULT_BASE_URL = \"https://xrplre.quest\";\n\nexport class XRPLRequest {\n public readonly payloads: PayloadsClient;\n public readonly webhooks: WebhooksClient;\n\n constructor(config: XRPLRequestConfig) {\n if (!config.apiKey) throw new Error(\"apiKey is required\");\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n this.payloads = new PayloadsClient(config.apiKey, baseUrl);\n this.webhooks = new WebhooksClient(config.apiKey, baseUrl);\n }\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,166 @@
1
+ import { createHmac } from 'crypto';
2
+
3
+ // src/api-error.ts
4
+ function apiErrorMessage(body, fallback) {
5
+ if (typeof body === "object" && body !== null && "error" in body && typeof body.error === "string") {
6
+ return body.error;
7
+ }
8
+ return fallback;
9
+ }
10
+
11
+ // src/payloads.ts
12
+ var DEFAULT_POLL_INTERVAL = 2e3;
13
+ var DEFAULT_POLL_TIMEOUT = 3e5;
14
+ var PayloadsClient = class {
15
+ constructor(apiKey, baseUrl) {
16
+ this.apiKey = apiKey;
17
+ this.baseUrl = baseUrl;
18
+ }
19
+ get headers() {
20
+ return {
21
+ Authorization: `Bearer ${this.apiKey}`,
22
+ "Content-Type": "application/json"
23
+ };
24
+ }
25
+ async create(options) {
26
+ const res = await fetch(`${this.baseUrl}/api/v1/payloads`, {
27
+ method: "POST",
28
+ headers: this.headers,
29
+ body: JSON.stringify(options)
30
+ });
31
+ if (!res.ok) {
32
+ const body = await res.json().catch(() => null);
33
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to create payload"), res.status);
34
+ }
35
+ const data = await res.json();
36
+ return data;
37
+ }
38
+ async get(uuid) {
39
+ const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {
40
+ headers: this.headers
41
+ });
42
+ if (!res.ok) {
43
+ const body = await res.json().catch(() => null);
44
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to get payload"), res.status);
45
+ }
46
+ return res.json();
47
+ }
48
+ async cancel(uuid) {
49
+ const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {
50
+ method: "DELETE",
51
+ headers: this.headers
52
+ });
53
+ if (!res.ok) {
54
+ const body = await res.json().catch(() => null);
55
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to cancel payload"), res.status);
56
+ }
57
+ return res.json();
58
+ }
59
+ async list(options) {
60
+ const url = new URL(`${this.baseUrl}/api/v1/payloads`);
61
+ if (options?.limit) url.searchParams.set("limit", String(options.limit));
62
+ if (options?.offset) url.searchParams.set("offset", String(options.offset));
63
+ const res = await fetch(url.toString(), { headers: this.headers });
64
+ if (!res.ok) {
65
+ const body = await res.json().catch(() => null);
66
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to list payloads"), res.status);
67
+ }
68
+ return res.json();
69
+ }
70
+ async poll(uuid, options) {
71
+ const timeout = options?.timeout ?? DEFAULT_POLL_TIMEOUT;
72
+ const interval = options?.interval ?? DEFAULT_POLL_INTERVAL;
73
+ const deadline = Date.now() + timeout;
74
+ while (Date.now() < deadline) {
75
+ const payload = await this.get(uuid);
76
+ if (options?.onUpdate) options.onUpdate(payload);
77
+ if (payload.status !== "pending") return payload;
78
+ const remaining = deadline - Date.now();
79
+ if (remaining <= 0) break;
80
+ await sleep(Math.min(interval, remaining));
81
+ }
82
+ return this.get(uuid);
83
+ }
84
+ };
85
+ function sleep(ms) {
86
+ return new Promise((resolve) => setTimeout(resolve, ms));
87
+ }
88
+ var XRPLRequestError = class extends Error {
89
+ constructor(message, statusCode) {
90
+ super(message);
91
+ this.statusCode = statusCode;
92
+ this.name = "XRPLRequestError";
93
+ }
94
+ };
95
+ function verifyWebhookSignature(secret, rawBody, signature) {
96
+ if (typeof signature !== "string" || !signature.startsWith("sha256=")) return false;
97
+ const expected = `sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`;
98
+ if (expected.length !== signature.length) return false;
99
+ let diff = 0;
100
+ for (let i = 0; i < expected.length; i++) {
101
+ diff |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
102
+ }
103
+ return diff === 0;
104
+ }
105
+
106
+ // src/webhooks.ts
107
+ var WebhooksClient = class {
108
+ constructor(apiKey, baseUrl) {
109
+ this.apiKey = apiKey;
110
+ this.baseUrl = baseUrl;
111
+ }
112
+ get headers() {
113
+ return {
114
+ Authorization: `Bearer ${this.apiKey}`,
115
+ "Content-Type": "application/json"
116
+ };
117
+ }
118
+ async create(options) {
119
+ const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, {
120
+ method: "POST",
121
+ headers: this.headers,
122
+ body: JSON.stringify(options)
123
+ });
124
+ if (!res.ok) {
125
+ const body = await res.json().catch(() => null);
126
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to create webhook"), res.status);
127
+ }
128
+ return res.json();
129
+ }
130
+ async list() {
131
+ const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, { headers: this.headers });
132
+ if (!res.ok) {
133
+ const body = await res.json().catch(() => null);
134
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to list webhooks"), res.status);
135
+ }
136
+ return res.json();
137
+ }
138
+ async delete(id) {
139
+ const res = await fetch(`${this.baseUrl}/api/v1/webhooks/${id}`, {
140
+ method: "DELETE",
141
+ headers: this.headers
142
+ });
143
+ if (!res.ok) {
144
+ const body = await res.json().catch(() => null);
145
+ throw new XRPLRequestError(apiErrorMessage(body, "Failed to delete webhook"), res.status);
146
+ }
147
+ return res.json();
148
+ }
149
+ };
150
+ /** Verify an incoming webhook signature. Convenience alias for `verifyWebhookSignature`. */
151
+ WebhooksClient.verify = verifyWebhookSignature;
152
+
153
+ // src/index.ts
154
+ var DEFAULT_BASE_URL = "https://xrplre.quest";
155
+ var XRPLRequest = class {
156
+ constructor(config) {
157
+ if (!config.apiKey) throw new Error("apiKey is required");
158
+ const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
159
+ this.payloads = new PayloadsClient(config.apiKey, baseUrl);
160
+ this.webhooks = new WebhooksClient(config.apiKey, baseUrl);
161
+ }
162
+ };
163
+
164
+ export { WebhooksClient, XRPLRequest, XRPLRequestError, verifyWebhookSignature };
165
+ //# sourceMappingURL=index.mjs.map
166
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api-error.ts","../src/payloads.ts","../src/utils.ts","../src/webhooks.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["export function apiErrorMessage(body: unknown, fallback: string): string {\n if (\n typeof body === \"object\" &&\n body !== null &&\n \"error\" in body &&\n typeof (body as { error: unknown }).error === \"string\"\n ) {\n return (body as { error: string }).error;\n }\n return fallback;\n}\n","import { apiErrorMessage } from \"./api-error\";\nimport type { CreatePayloadOptions, Payload, PollOptions } from \"./types\";\n\nconst DEFAULT_BASE_URL = \"https://xrplre.quest\";\nconst DEFAULT_POLL_INTERVAL = 2000;\nconst DEFAULT_POLL_TIMEOUT = 300_000;\n\nexport class PayloadsClient {\n constructor(\n private readonly apiKey: string,\n private readonly baseUrl: string\n ) {}\n\n private get headers() {\n return {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n async create(options: CreatePayloadOptions): Promise<Payload> {\n const res = await fetch(`${this.baseUrl}/api/v1/payloads`, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(options),\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to create payload\"), res.status);\n }\n\n const data = await res.json();\n return data as Payload;\n }\n\n async get(uuid: string): Promise<Payload> {\n const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {\n headers: this.headers,\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to get payload\"), res.status);\n }\n\n return res.json() as Promise<Payload>;\n }\n\n async cancel(uuid: string): Promise<Payload> {\n const res = await fetch(`${this.baseUrl}/api/v1/payloads/${uuid}`, {\n method: \"DELETE\",\n headers: this.headers,\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to cancel payload\"), res.status);\n }\n\n return res.json() as Promise<Payload>;\n }\n\n async list(options?: { limit?: number; offset?: number }): Promise<{ payloads: Payload[] }> {\n const url = new URL(`${this.baseUrl}/api/v1/payloads`);\n if (options?.limit) url.searchParams.set(\"limit\", String(options.limit));\n if (options?.offset) url.searchParams.set(\"offset\", String(options.offset));\n\n const res = await fetch(url.toString(), { headers: this.headers });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to list payloads\"), res.status);\n }\n\n return res.json() as Promise<{ payloads: Payload[] }>;\n }\n\n async poll(uuid: string, options?: PollOptions): Promise<Payload> {\n const timeout = options?.timeout ?? DEFAULT_POLL_TIMEOUT;\n const interval = options?.interval ?? DEFAULT_POLL_INTERVAL;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const payload = await this.get(uuid);\n\n if (options?.onUpdate) options.onUpdate(payload);\n\n if (payload.status !== \"pending\") return payload;\n\n const remaining = deadline - Date.now();\n if (remaining <= 0) break;\n\n await sleep(Math.min(interval, remaining));\n }\n\n // Final check\n return this.get(uuid);\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport class XRPLRequestError extends Error {\n constructor(message: string, public readonly statusCode: number) {\n super(message);\n this.name = \"XRPLRequestError\";\n }\n}\n","import { createHmac } from \"crypto\";\n\n/**\n * Verify an incoming webhook signature from XRPL Request.\n * Use this in your webhook endpoint to confirm the request is authentic.\n *\n * @param secret - The webhook secret shown when the webhook was created\n * @param rawBody - The raw request body string (before JSON.parse)\n * @param signature - The value of the X-XRPL-Request-Signature header\n */\nexport function verifyWebhookSignature(\n secret: string,\n rawBody: string,\n signature: string,\n): boolean {\n if (typeof signature !== \"string\" || !signature.startsWith(\"sha256=\")) return false;\n const expected = `sha256=${createHmac(\"sha256\", secret).update(rawBody).digest(\"hex\")}`;\n if (expected.length !== signature.length) return false;\n // Constant-time comparison\n let diff = 0;\n for (let i = 0; i < expected.length; i++) {\n diff |= expected.charCodeAt(i) ^ signature.charCodeAt(i);\n }\n return diff === 0;\n}\n","import { apiErrorMessage } from \"./api-error\";\nimport type { CreateWebhookOptions, Webhook } from \"./types\";\nimport { XRPLRequestError } from \"./payloads\";\nimport { verifyWebhookSignature } from \"./utils\";\n\nexport class WebhooksClient {\n constructor(\n private readonly apiKey: string,\n private readonly baseUrl: string,\n ) {}\n\n private get headers() {\n return {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n async create(options: CreateWebhookOptions): Promise<Webhook & { secret: string }> {\n const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(options),\n });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to create webhook\"), res.status);\n }\n return res.json() as Promise<Webhook & { secret: string }>;\n }\n\n async list(): Promise<{ webhooks: Webhook[] }> {\n const res = await fetch(`${this.baseUrl}/api/v1/webhooks`, { headers: this.headers });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to list webhooks\"), res.status);\n }\n return res.json() as Promise<{ webhooks: Webhook[] }>;\n }\n\n async delete(id: string): Promise<{ id: string; deleted: boolean }> {\n const res = await fetch(`${this.baseUrl}/api/v1/webhooks/${id}`, {\n method: \"DELETE\",\n headers: this.headers,\n });\n if (!res.ok) {\n const body = await res.json().catch(() => null);\n throw new XRPLRequestError(apiErrorMessage(body, \"Failed to delete webhook\"), res.status);\n }\n return res.json() as Promise<{ id: string; deleted: boolean }>;\n }\n\n /** Verify an incoming webhook signature. Convenience alias for `verifyWebhookSignature`. */\n static verify = verifyWebhookSignature;\n}\n","import { PayloadsClient, XRPLRequestError } from \"./payloads\";\nimport { WebhooksClient } from \"./webhooks\";\nimport type { XRPLRequestConfig } from \"./types\";\n\nexport * from \"./types\";\nexport { XRPLRequestError } from \"./payloads\";\nexport { WebhooksClient } from \"./webhooks\";\nexport { verifyWebhookSignature } from \"./utils\";\n\nconst DEFAULT_BASE_URL = \"https://xrplre.quest\";\n\nexport class XRPLRequest {\n public readonly payloads: PayloadsClient;\n public readonly webhooks: WebhooksClient;\n\n constructor(config: XRPLRequestConfig) {\n if (!config.apiKey) throw new Error(\"apiKey is required\");\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n this.payloads = new PayloadsClient(config.apiKey, baseUrl);\n this.webhooks = new WebhooksClient(config.apiKey, baseUrl);\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@xrplrequest/sdk",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript SDK for XRPL Request — wallet-agnostic XRPL transaction signing",
5
+ "author": "LedgerCraft <hello@xrplre.quest>",
6
+ "license": "MIT",
7
+ "homepage": "https://docs.xrplre.quest/sdk",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/LedgerCraft/XRPL-Request.git",
11
+ "directory": "packages/sdk"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/LedgerCraft/XRPL-Request/issues"
15
+ },
16
+ "main": "./dist/index.js",
17
+ "module": "./dist/index.mjs",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.mjs",
23
+ "require": "./dist/index.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md"
29
+ ],
30
+ "sideEffects": false,
31
+ "keywords": [
32
+ "xrpl",
33
+ "xrp",
34
+ "ripple",
35
+ "signing",
36
+ "wallet",
37
+ "xaman",
38
+ "crossmark",
39
+ "discord",
40
+ "bot",
41
+ "webhook",
42
+ "transaction"
43
+ ],
44
+ "engines": {
45
+ "node": ">=18"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public",
49
+ "registry": "https://registry.npmjs.org"
50
+ },
51
+ "dependencies": {},
52
+ "devDependencies": {
53
+ "@types/node": "^22.15.0",
54
+ "tsup": "^8.4.0",
55
+ "typescript": "^5.8.3"
56
+ },
57
+ "scripts": {
58
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
59
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
60
+ "type-check": "tsc --noEmit"
61
+ }
62
+ }