@mixrpay/agent-sdk 0.1.1 → 0.2.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 +185 -222
- package/dist/index.cjs +411 -0
- package/dist/index.d.cts +244 -1
- package/dist/index.d.ts +244 -1
- package/dist/index.js +413 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -204,6 +204,13 @@ var SessionKey = class _SessionKey {
|
|
|
204
204
|
this.address = this.account.address;
|
|
205
205
|
this.isTest = isTest;
|
|
206
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Get the private key as hex string (without 0x prefix).
|
|
209
|
+
* Used for API authentication headers.
|
|
210
|
+
*/
|
|
211
|
+
get privateKeyHex() {
|
|
212
|
+
return this.privateKey.slice(2);
|
|
213
|
+
}
|
|
207
214
|
/**
|
|
208
215
|
* Parse a session key string into a SessionKey object.
|
|
209
216
|
*
|
|
@@ -244,6 +251,18 @@ var SessionKey = class _SessionKey {
|
|
|
244
251
|
message
|
|
245
252
|
});
|
|
246
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* Sign a plain message (EIP-191 personal sign).
|
|
256
|
+
*
|
|
257
|
+
* @param message - Message to sign
|
|
258
|
+
* @returns Hex-encoded signature
|
|
259
|
+
*/
|
|
260
|
+
async signMessage(message) {
|
|
261
|
+
return (0, import_accounts.signMessage)({
|
|
262
|
+
privateKey: this.privateKey,
|
|
263
|
+
message
|
|
264
|
+
});
|
|
265
|
+
}
|
|
247
266
|
/**
|
|
248
267
|
* Get the chain ID based on whether this is a test key.
|
|
249
268
|
*/
|
|
@@ -276,6 +295,19 @@ function generateNonce() {
|
|
|
276
295
|
crypto.getRandomValues(bytes);
|
|
277
296
|
return `0x${Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
278
297
|
}
|
|
298
|
+
function buildSessionAuthMessage(timestamp, address) {
|
|
299
|
+
return `MixrPay:${timestamp}:${address.toLowerCase()}`;
|
|
300
|
+
}
|
|
301
|
+
async function createSessionAuthPayload(sessionKey) {
|
|
302
|
+
const timestamp = Date.now();
|
|
303
|
+
const message = buildSessionAuthMessage(timestamp, sessionKey.address);
|
|
304
|
+
const signature = await sessionKey.signMessage(message);
|
|
305
|
+
return {
|
|
306
|
+
address: sessionKey.address,
|
|
307
|
+
timestamp,
|
|
308
|
+
signature
|
|
309
|
+
};
|
|
310
|
+
}
|
|
279
311
|
|
|
280
312
|
// src/x402.ts
|
|
281
313
|
async function parse402Response(response) {
|
|
@@ -953,6 +985,385 @@ var AgentWallet = class {
|
|
|
953
985
|
setLogLevel(level) {
|
|
954
986
|
this.logger.setLevel(level);
|
|
955
987
|
}
|
|
988
|
+
// ===========================================================================
|
|
989
|
+
// Session Authorization Methods (for MixrPay Merchants)
|
|
990
|
+
// ===========================================================================
|
|
991
|
+
/**
|
|
992
|
+
* Create the X-Session-Auth header for secure API authentication.
|
|
993
|
+
* Uses signature-based authentication - private key is NEVER transmitted.
|
|
994
|
+
*
|
|
995
|
+
* @returns Headers object with X-Session-Auth
|
|
996
|
+
*/
|
|
997
|
+
async getSessionAuthHeaders() {
|
|
998
|
+
const payload = await createSessionAuthPayload(this.sessionKey);
|
|
999
|
+
return {
|
|
1000
|
+
"X-Session-Auth": JSON.stringify(payload)
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Get an existing session or create a new one with a MixrPay merchant.
|
|
1005
|
+
*
|
|
1006
|
+
* This is the recommended way to interact with MixrPay-enabled APIs.
|
|
1007
|
+
* If an active session exists, it will be returned. Otherwise, a new
|
|
1008
|
+
* session authorization request will be created and confirmed.
|
|
1009
|
+
*
|
|
1010
|
+
* @param options - Session creation options
|
|
1011
|
+
* @returns Active session authorization
|
|
1012
|
+
*
|
|
1013
|
+
* @throws {MixrPayError} If merchant is not found or session creation fails
|
|
1014
|
+
*
|
|
1015
|
+
* @example
|
|
1016
|
+
* ```typescript
|
|
1017
|
+
* const session = await wallet.getOrCreateSession({
|
|
1018
|
+
* merchantPublicKey: 'pk_live_abc123...',
|
|
1019
|
+
* spendingLimitUsd: 25.00,
|
|
1020
|
+
* durationDays: 7,
|
|
1021
|
+
* });
|
|
1022
|
+
*
|
|
1023
|
+
* console.log(`Session active: $${session.remainingLimitUsd} remaining`);
|
|
1024
|
+
* ```
|
|
1025
|
+
*/
|
|
1026
|
+
async getOrCreateSession(options) {
|
|
1027
|
+
this.logger.debug("getOrCreateSession called", options);
|
|
1028
|
+
const { merchantPublicKey, spendingLimitUsd = 25, durationDays = 7 } = options;
|
|
1029
|
+
try {
|
|
1030
|
+
const existingSession = await this.getSessionByMerchant(merchantPublicKey);
|
|
1031
|
+
if (existingSession && existingSession.status === "active") {
|
|
1032
|
+
this.logger.debug("Found existing active session", existingSession.id);
|
|
1033
|
+
return existingSession;
|
|
1034
|
+
}
|
|
1035
|
+
} catch {
|
|
1036
|
+
}
|
|
1037
|
+
this.logger.info(`Creating new session with merchant ${merchantPublicKey.slice(0, 20)}...`);
|
|
1038
|
+
const authHeaders = await this.getSessionAuthHeaders();
|
|
1039
|
+
const authorizeResponse = await fetch(`${this.baseUrl}/api/v2/session/authorize`, {
|
|
1040
|
+
method: "POST",
|
|
1041
|
+
headers: {
|
|
1042
|
+
"Content-Type": "application/json",
|
|
1043
|
+
...authHeaders
|
|
1044
|
+
},
|
|
1045
|
+
body: JSON.stringify({
|
|
1046
|
+
merchant_public_key: merchantPublicKey,
|
|
1047
|
+
spending_limit_usd: spendingLimitUsd,
|
|
1048
|
+
duration_days: durationDays
|
|
1049
|
+
})
|
|
1050
|
+
});
|
|
1051
|
+
if (!authorizeResponse.ok) {
|
|
1052
|
+
const error = await authorizeResponse.json().catch(() => ({}));
|
|
1053
|
+
throw new MixrPayError(
|
|
1054
|
+
error.message || error.error || `Failed to create session: ${authorizeResponse.status}`
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
const authorizeData = await authorizeResponse.json();
|
|
1058
|
+
const sessionId = authorizeData.session_id;
|
|
1059
|
+
const messageToSign = authorizeData.message_to_sign;
|
|
1060
|
+
if (!sessionId || !messageToSign) {
|
|
1061
|
+
throw new MixrPayError("Invalid authorize response: missing session_id or message_to_sign");
|
|
1062
|
+
}
|
|
1063
|
+
this.logger.debug("Signing session authorization message...");
|
|
1064
|
+
const signature = await this.sessionKey.signMessage(messageToSign);
|
|
1065
|
+
const confirmResponse = await fetch(`${this.baseUrl}/api/v2/session/confirm`, {
|
|
1066
|
+
method: "POST",
|
|
1067
|
+
headers: {
|
|
1068
|
+
"Content-Type": "application/json"
|
|
1069
|
+
},
|
|
1070
|
+
body: JSON.stringify({
|
|
1071
|
+
session_id: sessionId,
|
|
1072
|
+
signature,
|
|
1073
|
+
wallet_address: this.walletAddress
|
|
1074
|
+
})
|
|
1075
|
+
});
|
|
1076
|
+
if (!confirmResponse.ok) {
|
|
1077
|
+
const error = await confirmResponse.json().catch(() => ({}));
|
|
1078
|
+
throw new MixrPayError(
|
|
1079
|
+
error.message || `Failed to confirm session: ${confirmResponse.status}`
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
const confirmData = await confirmResponse.json();
|
|
1083
|
+
this.logger.info(`Session created: ${confirmData.session?.id || sessionId}`);
|
|
1084
|
+
return this.parseSessionResponse(confirmData.session || confirmData);
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Get session status for a specific merchant.
|
|
1088
|
+
*
|
|
1089
|
+
* @param merchantPublicKey - The merchant's public key
|
|
1090
|
+
* @returns Session authorization or null if not found
|
|
1091
|
+
*/
|
|
1092
|
+
async getSessionByMerchant(merchantPublicKey) {
|
|
1093
|
+
this.logger.debug("getSessionByMerchant", merchantPublicKey);
|
|
1094
|
+
const authHeaders = await this.getSessionAuthHeaders();
|
|
1095
|
+
const response = await fetch(`${this.baseUrl}/api/v2/session/status?merchant_public_key=${encodeURIComponent(merchantPublicKey)}`, {
|
|
1096
|
+
headers: authHeaders
|
|
1097
|
+
});
|
|
1098
|
+
if (response.status === 404) {
|
|
1099
|
+
return null;
|
|
1100
|
+
}
|
|
1101
|
+
if (!response.ok) {
|
|
1102
|
+
const error = await response.json().catch(() => ({}));
|
|
1103
|
+
throw new MixrPayError(error.message || `Failed to get session: ${response.status}`);
|
|
1104
|
+
}
|
|
1105
|
+
const data = await response.json();
|
|
1106
|
+
return data.session ? this.parseSessionResponse(data.session) : null;
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* List all session authorizations for this wallet.
|
|
1110
|
+
*
|
|
1111
|
+
* @returns Array of session authorizations
|
|
1112
|
+
*
|
|
1113
|
+
* @example
|
|
1114
|
+
* ```typescript
|
|
1115
|
+
* const sessions = await wallet.listSessions();
|
|
1116
|
+
* for (const session of sessions) {
|
|
1117
|
+
* console.log(`${session.merchantName}: $${session.remainingLimitUsd} remaining`);
|
|
1118
|
+
* }
|
|
1119
|
+
* ```
|
|
1120
|
+
*/
|
|
1121
|
+
async listSessions() {
|
|
1122
|
+
this.logger.debug("listSessions");
|
|
1123
|
+
const authHeaders = await this.getSessionAuthHeaders();
|
|
1124
|
+
const response = await fetch(`${this.baseUrl}/api/v2/session/list`, {
|
|
1125
|
+
headers: authHeaders
|
|
1126
|
+
});
|
|
1127
|
+
if (!response.ok) {
|
|
1128
|
+
const error = await response.json().catch(() => ({}));
|
|
1129
|
+
throw new MixrPayError(error.message || `Failed to list sessions: ${response.status}`);
|
|
1130
|
+
}
|
|
1131
|
+
const data = await response.json();
|
|
1132
|
+
return (data.sessions || []).map((s) => this.parseSessionResponse(s));
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Revoke a session authorization.
|
|
1136
|
+
*
|
|
1137
|
+
* After revocation, no further charges can be made against this session.
|
|
1138
|
+
*
|
|
1139
|
+
* @param sessionId - The session ID to revoke
|
|
1140
|
+
* @returns true if revoked successfully
|
|
1141
|
+
*
|
|
1142
|
+
* @example
|
|
1143
|
+
* ```typescript
|
|
1144
|
+
* const revoked = await wallet.revokeSession('sess_abc123');
|
|
1145
|
+
* if (revoked) {
|
|
1146
|
+
* console.log('Session revoked successfully');
|
|
1147
|
+
* }
|
|
1148
|
+
* ```
|
|
1149
|
+
*/
|
|
1150
|
+
async revokeSession(sessionId) {
|
|
1151
|
+
this.logger.debug("revokeSession", sessionId);
|
|
1152
|
+
const authHeaders = await this.getSessionAuthHeaders();
|
|
1153
|
+
const response = await fetch(`${this.baseUrl}/api/v2/session/revoke`, {
|
|
1154
|
+
method: "POST",
|
|
1155
|
+
headers: {
|
|
1156
|
+
"Content-Type": "application/json",
|
|
1157
|
+
...authHeaders
|
|
1158
|
+
},
|
|
1159
|
+
body: JSON.stringify({ session_id: sessionId })
|
|
1160
|
+
});
|
|
1161
|
+
if (!response.ok) {
|
|
1162
|
+
const error = await response.json().catch(() => ({}));
|
|
1163
|
+
this.logger.error("Failed to revoke session:", error);
|
|
1164
|
+
return false;
|
|
1165
|
+
}
|
|
1166
|
+
this.logger.info(`Session ${sessionId} revoked`);
|
|
1167
|
+
return true;
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Charge against an active session authorization.
|
|
1171
|
+
*
|
|
1172
|
+
* This is useful when you need to manually charge a session outside of
|
|
1173
|
+
* the `callMerchantApi()` flow.
|
|
1174
|
+
*
|
|
1175
|
+
* @param sessionId - The session ID to charge
|
|
1176
|
+
* @param amountUsd - Amount to charge in USD
|
|
1177
|
+
* @param options - Additional charge options
|
|
1178
|
+
* @returns Charge result
|
|
1179
|
+
*
|
|
1180
|
+
* @example
|
|
1181
|
+
* ```typescript
|
|
1182
|
+
* const result = await wallet.chargeSession('sess_abc123', 0.05, {
|
|
1183
|
+
* feature: 'ai-generation',
|
|
1184
|
+
* idempotencyKey: 'unique-key-123',
|
|
1185
|
+
* });
|
|
1186
|
+
*
|
|
1187
|
+
* console.log(`Charged $${result.amountUsd}, remaining: $${result.remainingSessionBalanceUsd}`);
|
|
1188
|
+
* ```
|
|
1189
|
+
*/
|
|
1190
|
+
async chargeSession(sessionId, amountUsd, options = {}) {
|
|
1191
|
+
this.logger.debug("chargeSession", { sessionId, amountUsd, options });
|
|
1192
|
+
const authHeaders = await this.getSessionAuthHeaders();
|
|
1193
|
+
const response = await fetch(`${this.baseUrl}/api/v2/charge`, {
|
|
1194
|
+
method: "POST",
|
|
1195
|
+
headers: {
|
|
1196
|
+
"Content-Type": "application/json",
|
|
1197
|
+
...authHeaders
|
|
1198
|
+
},
|
|
1199
|
+
body: JSON.stringify({
|
|
1200
|
+
session_id: sessionId,
|
|
1201
|
+
price_usd: amountUsd,
|
|
1202
|
+
feature: options.feature,
|
|
1203
|
+
idempotency_key: options.idempotencyKey,
|
|
1204
|
+
metadata: options.metadata
|
|
1205
|
+
})
|
|
1206
|
+
});
|
|
1207
|
+
if (!response.ok) {
|
|
1208
|
+
const error = await response.json().catch(() => ({}));
|
|
1209
|
+
if (response.status === 402) {
|
|
1210
|
+
if (error.error === "session_limit_exceeded") {
|
|
1211
|
+
throw new SpendingLimitExceededError(
|
|
1212
|
+
"session",
|
|
1213
|
+
error.sessionLimitUsd || error.session_limit_usd || 0,
|
|
1214
|
+
amountUsd
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
if (error.error === "insufficient_balance") {
|
|
1218
|
+
throw new InsufficientBalanceError(
|
|
1219
|
+
amountUsd,
|
|
1220
|
+
error.availableUsd || error.available_usd || 0
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
throw new MixrPayError(error.message || `Charge failed: ${response.status}`);
|
|
1225
|
+
}
|
|
1226
|
+
const data = await response.json();
|
|
1227
|
+
this.logger.payment(amountUsd, sessionId, options.feature);
|
|
1228
|
+
return {
|
|
1229
|
+
success: true,
|
|
1230
|
+
chargeId: data.chargeId || data.charge_id,
|
|
1231
|
+
amountUsd: data.amountUsd || data.amount_usd || amountUsd,
|
|
1232
|
+
txHash: data.txHash || data.tx_hash,
|
|
1233
|
+
remainingSessionBalanceUsd: data.remainingSessionBalanceUsd || data.remaining_session_balance_usd || 0
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Call a MixrPay merchant's API with automatic session management.
|
|
1238
|
+
*
|
|
1239
|
+
* This is the recommended way to interact with MixrPay-enabled APIs.
|
|
1240
|
+
* It automatically:
|
|
1241
|
+
* 1. Gets or creates a session authorization
|
|
1242
|
+
* 2. Adds the `X-Mixr-Session` header to the request
|
|
1243
|
+
* 3. Handles payment errors and session expiration
|
|
1244
|
+
*
|
|
1245
|
+
* @param options - API call options
|
|
1246
|
+
* @returns Response from the merchant API
|
|
1247
|
+
*
|
|
1248
|
+
* @example
|
|
1249
|
+
* ```typescript
|
|
1250
|
+
* const response = await wallet.callMerchantApi({
|
|
1251
|
+
* url: 'https://api.merchant.com/generate',
|
|
1252
|
+
* merchantPublicKey: 'pk_live_abc123...',
|
|
1253
|
+
* method: 'POST',
|
|
1254
|
+
* body: { prompt: 'Hello world' },
|
|
1255
|
+
* priceUsd: 0.05,
|
|
1256
|
+
* });
|
|
1257
|
+
*
|
|
1258
|
+
* const data = await response.json();
|
|
1259
|
+
* ```
|
|
1260
|
+
*/
|
|
1261
|
+
async callMerchantApi(options) {
|
|
1262
|
+
const {
|
|
1263
|
+
url,
|
|
1264
|
+
method = "POST",
|
|
1265
|
+
body,
|
|
1266
|
+
headers: customHeaders = {},
|
|
1267
|
+
merchantPublicKey,
|
|
1268
|
+
priceUsd,
|
|
1269
|
+
feature
|
|
1270
|
+
} = options;
|
|
1271
|
+
this.logger.debug("callMerchantApi", { url, method, merchantPublicKey, priceUsd });
|
|
1272
|
+
if (priceUsd !== void 0 && this.maxPaymentUsd !== void 0 && priceUsd > this.maxPaymentUsd) {
|
|
1273
|
+
throw new SpendingLimitExceededError("client_max", this.maxPaymentUsd, priceUsd);
|
|
1274
|
+
}
|
|
1275
|
+
const session = await this.getOrCreateSession({
|
|
1276
|
+
merchantPublicKey,
|
|
1277
|
+
spendingLimitUsd: 25,
|
|
1278
|
+
// Default limit
|
|
1279
|
+
durationDays: 7
|
|
1280
|
+
});
|
|
1281
|
+
const headers = {
|
|
1282
|
+
"Content-Type": "application/json",
|
|
1283
|
+
"X-Mixr-Session": session.id,
|
|
1284
|
+
...customHeaders
|
|
1285
|
+
};
|
|
1286
|
+
if (feature) {
|
|
1287
|
+
headers["X-Mixr-Feature"] = feature;
|
|
1288
|
+
}
|
|
1289
|
+
const requestBody = body !== void 0 ? typeof body === "string" ? body : JSON.stringify(body) : void 0;
|
|
1290
|
+
const response = await fetch(url, {
|
|
1291
|
+
method,
|
|
1292
|
+
headers,
|
|
1293
|
+
body: requestBody,
|
|
1294
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
1295
|
+
});
|
|
1296
|
+
const chargedAmount = response.headers.get("X-Mixr-Charged");
|
|
1297
|
+
if (chargedAmount) {
|
|
1298
|
+
const amountUsd = parseFloat(chargedAmount);
|
|
1299
|
+
if (!isNaN(amountUsd)) {
|
|
1300
|
+
const payment = {
|
|
1301
|
+
amountUsd,
|
|
1302
|
+
recipient: merchantPublicKey,
|
|
1303
|
+
txHash: response.headers.get("X-Payment-TxHash"),
|
|
1304
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1305
|
+
description: feature || "API call",
|
|
1306
|
+
url
|
|
1307
|
+
};
|
|
1308
|
+
this.payments.push(payment);
|
|
1309
|
+
this.totalSpentUsd += amountUsd;
|
|
1310
|
+
this.logger.payment(amountUsd, merchantPublicKey, feature);
|
|
1311
|
+
if (this.onPayment) {
|
|
1312
|
+
this.onPayment(payment);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
if (response.status === 402) {
|
|
1317
|
+
const errorData = await response.json().catch(() => ({}));
|
|
1318
|
+
if (errorData.error === "session_expired") {
|
|
1319
|
+
this.logger.info("Session expired, creating new one...");
|
|
1320
|
+
const newSession = await this.getOrCreateSession({
|
|
1321
|
+
merchantPublicKey,
|
|
1322
|
+
spendingLimitUsd: 25,
|
|
1323
|
+
durationDays: 7
|
|
1324
|
+
});
|
|
1325
|
+
headers["X-Mixr-Session"] = newSession.id;
|
|
1326
|
+
return fetch(url, {
|
|
1327
|
+
method,
|
|
1328
|
+
headers,
|
|
1329
|
+
body: requestBody,
|
|
1330
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
if (errorData.error === "session_limit_exceeded") {
|
|
1334
|
+
throw new SpendingLimitExceededError(
|
|
1335
|
+
"session",
|
|
1336
|
+
errorData.sessionLimitUsd || 0,
|
|
1337
|
+
priceUsd || 0
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
if (errorData.error === "insufficient_balance") {
|
|
1341
|
+
throw new InsufficientBalanceError(
|
|
1342
|
+
priceUsd || 0,
|
|
1343
|
+
errorData.availableUsd || 0
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
return response;
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Parse session response data into SessionAuthorization object.
|
|
1351
|
+
*/
|
|
1352
|
+
parseSessionResponse(data) {
|
|
1353
|
+
return {
|
|
1354
|
+
id: data.id || data.session_id || data.sessionId,
|
|
1355
|
+
merchantId: data.merchantId || data.merchant_id,
|
|
1356
|
+
merchantName: data.merchantName || data.merchant_name || "Unknown",
|
|
1357
|
+
status: data.status || "active",
|
|
1358
|
+
spendingLimitUsd: Number(data.spendingLimitUsd || data.spending_limit_usd || data.spendingLimit || 0),
|
|
1359
|
+
amountUsedUsd: Number(data.amountUsedUsd || data.amount_used_usd || data.amountUsed || 0),
|
|
1360
|
+
remainingLimitUsd: Number(
|
|
1361
|
+
data.remainingLimitUsd || data.remaining_limit_usd || data.remainingLimit || Number(data.spendingLimitUsd || data.spending_limit_usd || 0) - Number(data.amountUsedUsd || data.amount_used_usd || 0)
|
|
1362
|
+
),
|
|
1363
|
+
expiresAt: new Date(data.expiresAt || data.expires_at),
|
|
1364
|
+
createdAt: new Date(data.createdAt || data.created_at)
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
956
1367
|
};
|
|
957
1368
|
// Annotate the CommonJS export names for ESM import in node:
|
|
958
1369
|
0 && (module.exports = {
|