spaps-sdk 1.6.8 → 1.7.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/CHANGELOG.md +7 -1
- package/README.md +107 -3
- package/dist/index.d.mts +241 -3
- package/dist/index.d.ts +241 -3
- package/dist/index.js +349 -11
- package/dist/index.mjs +341 -8
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -190,6 +190,10 @@ var init_permissions = __esm({
|
|
|
190
190
|
init_permissions();
|
|
191
191
|
import crypto from "crypto";
|
|
192
192
|
import axios from "axios";
|
|
193
|
+
import {
|
|
194
|
+
isX402PaymentRequired,
|
|
195
|
+
isX402ResourceStatus
|
|
196
|
+
} from "spaps-types";
|
|
193
197
|
import {
|
|
194
198
|
createSecureMessageRequestSchema,
|
|
195
199
|
secureMessageSchema,
|
|
@@ -480,12 +484,23 @@ function appendSupportedIssueReportScope(q, scope) {
|
|
|
480
484
|
}
|
|
481
485
|
q.append("scope", scope);
|
|
482
486
|
}
|
|
483
|
-
var
|
|
487
|
+
var X402PaymentRequiredSDKError = class extends Error {
|
|
488
|
+
paymentRequiredHeader;
|
|
489
|
+
response;
|
|
490
|
+
constructor(paymentRequiredHeader, response) {
|
|
491
|
+
super("x402 payment required");
|
|
492
|
+
this.name = "X402PaymentRequiredSDKError";
|
|
493
|
+
this.paymentRequiredHeader = paymentRequiredHeader;
|
|
494
|
+
this.response = response;
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
var SPAPSClient = class _SPAPSClient {
|
|
484
498
|
client;
|
|
485
499
|
apiKey;
|
|
486
500
|
accessToken;
|
|
487
501
|
refreshToken;
|
|
488
502
|
_isLocalMode = false;
|
|
503
|
+
headerProvider;
|
|
489
504
|
unwrapApiResponse(response, fallback) {
|
|
490
505
|
if (!response) {
|
|
491
506
|
throw new Error(fallback);
|
|
@@ -502,6 +517,13 @@ var SPAPSClient = class {
|
|
|
502
517
|
}
|
|
503
518
|
return payload;
|
|
504
519
|
}
|
|
520
|
+
skillEvalMutationConfig(options) {
|
|
521
|
+
const ifMatch = options?.ifMatch ?? options?.caseVersion;
|
|
522
|
+
if (ifMatch === void 0 || ifMatch === null || ifMatch === "") {
|
|
523
|
+
return void 0;
|
|
524
|
+
}
|
|
525
|
+
return { headers: { "If-Match": String(ifMatch) } };
|
|
526
|
+
}
|
|
505
527
|
isAxiosResponse(value) {
|
|
506
528
|
if (!value || typeof value !== "object") {
|
|
507
529
|
return false;
|
|
@@ -521,6 +543,41 @@ var SPAPSClient = class {
|
|
|
521
543
|
const record = value;
|
|
522
544
|
return "success" in record && typeof record.success === "boolean";
|
|
523
545
|
}
|
|
546
|
+
static isSdkManagedHeader(name) {
|
|
547
|
+
const normalized = name.toLowerCase();
|
|
548
|
+
return normalized === "authorization" || normalized === "x-api-key";
|
|
549
|
+
}
|
|
550
|
+
static hasHeader(headers, name) {
|
|
551
|
+
if (!headers) return false;
|
|
552
|
+
if (typeof headers.has === "function") {
|
|
553
|
+
return headers.has(name);
|
|
554
|
+
}
|
|
555
|
+
const normalized = name.toLowerCase();
|
|
556
|
+
return Object.keys(headers).some((key) => key.toLowerCase() === normalized);
|
|
557
|
+
}
|
|
558
|
+
static setHeader(headers, name, value) {
|
|
559
|
+
if (typeof headers.set === "function") {
|
|
560
|
+
headers.set(name, value);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
headers[name] = value;
|
|
564
|
+
}
|
|
565
|
+
static assertSafeHeaderValue(name, value) {
|
|
566
|
+
if (typeof value === "string" && /[\r\n]/.test(value)) {
|
|
567
|
+
throw new Error(`Invalid header value for ${name}: control characters not allowed`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
applyCustomHeaders(headers) {
|
|
571
|
+
const customHeaders = this.headerProvider?.();
|
|
572
|
+
if (!customHeaders) return;
|
|
573
|
+
for (const [key, value] of Object.entries(customHeaders)) {
|
|
574
|
+
if (_SPAPSClient.isSdkManagedHeader(key) || _SPAPSClient.hasHeader(headers, key)) {
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
_SPAPSClient.assertSafeHeaderValue(key, value);
|
|
578
|
+
_SPAPSClient.setHeader(headers, key, value);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
524
581
|
// Admin namespace for cleaner API
|
|
525
582
|
admin = {
|
|
526
583
|
createProduct: (productData) => this.createProduct(productData),
|
|
@@ -695,8 +752,43 @@ var SPAPSClient = class {
|
|
|
695
752
|
return this.unwrapApiResponse(res, "Failed to reply to issue report");
|
|
696
753
|
}
|
|
697
754
|
};
|
|
755
|
+
/**
|
|
756
|
+
* Application-scoped short links for browser apps that need stable public URLs.
|
|
757
|
+
*/
|
|
758
|
+
appLinks = {
|
|
759
|
+
/**
|
|
760
|
+
* Create a short link owned by the authenticated user.
|
|
761
|
+
*/
|
|
762
|
+
create: async (payload) => {
|
|
763
|
+
const res = await this.client.post(
|
|
764
|
+
"/api/v1/app-links",
|
|
765
|
+
payload,
|
|
766
|
+
this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0
|
|
767
|
+
);
|
|
768
|
+
return this.unwrapApiResponse(res, "Failed to create app link");
|
|
769
|
+
},
|
|
770
|
+
/**
|
|
771
|
+
* Resolve a public short link for the active application.
|
|
772
|
+
*/
|
|
773
|
+
get: async (username, slug, options) => {
|
|
774
|
+
const q = new URLSearchParams();
|
|
775
|
+
if (options?.track) q.set("track", "true");
|
|
776
|
+
const qs = q.toString();
|
|
777
|
+
const res = await this.client.get(
|
|
778
|
+
`/api/v1/app-links/${encodeURIComponent(username)}/${encodeURIComponent(slug)}${qs ? `?${qs}` : ""}`,
|
|
779
|
+
this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0
|
|
780
|
+
);
|
|
781
|
+
return this.unwrapApiResponse(res, "Failed to get app link");
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
static envVar(name) {
|
|
785
|
+
if (typeof process !== "undefined" && process.env) {
|
|
786
|
+
return process.env[name];
|
|
787
|
+
}
|
|
788
|
+
return void 0;
|
|
789
|
+
}
|
|
698
790
|
constructor(config = {}) {
|
|
699
|
-
const apiUrl = config.apiUrl ||
|
|
791
|
+
const apiUrl = config.apiUrl || _SPAPSClient.envVar("SPAPS_API_URL") || _SPAPSClient.envVar("NEXT_PUBLIC_SPAPS_API_URL");
|
|
700
792
|
const isBrowser = typeof window !== "undefined";
|
|
701
793
|
let effectiveApiKey;
|
|
702
794
|
if (config.publishableKey) {
|
|
@@ -709,7 +801,7 @@ var SPAPSClient = class {
|
|
|
709
801
|
} else if (config.apiKey) {
|
|
710
802
|
effectiveApiKey = config.apiKey;
|
|
711
803
|
} else {
|
|
712
|
-
effectiveApiKey =
|
|
804
|
+
effectiveApiKey = _SPAPSClient.envVar("SPAPS_API_KEY") || _SPAPSClient.envVar("NEXT_PUBLIC_SPAPS_API_KEY");
|
|
713
805
|
}
|
|
714
806
|
if (!apiUrl || apiUrl.includes("localhost") || apiUrl.includes("127.0.0.1")) {
|
|
715
807
|
this._isLocalMode = true;
|
|
@@ -718,6 +810,7 @@ var SPAPSClient = class {
|
|
|
718
810
|
if (!this.apiKey && !this._isLocalMode) {
|
|
719
811
|
console.warn("\u26A0\uFE0F SPAPS: No API key provided. Some features may not work.");
|
|
720
812
|
}
|
|
813
|
+
this.headerProvider = config.headerProvider;
|
|
721
814
|
this.client = axios.create({
|
|
722
815
|
baseURL: apiUrl || "http://localhost:3301",
|
|
723
816
|
timeout: config.timeout || 1e4,
|
|
@@ -727,11 +820,13 @@ var SPAPSClient = class {
|
|
|
727
820
|
}
|
|
728
821
|
});
|
|
729
822
|
this.client.interceptors.request.use((config2) => {
|
|
730
|
-
|
|
731
|
-
|
|
823
|
+
config2.headers = config2.headers || {};
|
|
824
|
+
this.applyCustomHeaders(config2.headers);
|
|
825
|
+
if (this.apiKey && !_SPAPSClient.hasHeader(config2.headers, "X-API-Key")) {
|
|
826
|
+
_SPAPSClient.setHeader(config2.headers, "X-API-Key", this.apiKey);
|
|
732
827
|
}
|
|
733
|
-
if (this.accessToken && !config2.headers
|
|
734
|
-
config2.headers
|
|
828
|
+
if (this.accessToken && !_SPAPSClient.hasHeader(config2.headers, "Authorization")) {
|
|
829
|
+
_SPAPSClient.setHeader(config2.headers, "Authorization", `Bearer ${this.accessToken}`);
|
|
735
830
|
}
|
|
736
831
|
return config2;
|
|
737
832
|
});
|
|
@@ -1033,6 +1128,7 @@ var SPAPSClient = class {
|
|
|
1033
1128
|
reconcile: async (options = {}) => {
|
|
1034
1129
|
const headers = {};
|
|
1035
1130
|
if (options.reconToken) {
|
|
1131
|
+
_SPAPSClient.assertSafeHeaderValue("X-Recon-Token", options.reconToken);
|
|
1036
1132
|
headers["X-Recon-Token"] = options.reconToken;
|
|
1037
1133
|
}
|
|
1038
1134
|
const payload = {};
|
|
@@ -1258,6 +1354,176 @@ var SPAPSClient = class {
|
|
|
1258
1354
|
return this.unwrapApiResponse(res, "Failed to disconnect");
|
|
1259
1355
|
}
|
|
1260
1356
|
};
|
|
1357
|
+
/**
|
|
1358
|
+
* x402 paid-resource namespace.
|
|
1359
|
+
* Handles resource status checks, payment-gated actions, receipts, and handoff authorization.
|
|
1360
|
+
*/
|
|
1361
|
+
x402 = {
|
|
1362
|
+
getResourceStatus: async (resourceKey) => {
|
|
1363
|
+
const res = await this.client.get(
|
|
1364
|
+
`/api/x402/resources/${encodeURIComponent(resourceKey)}/status`
|
|
1365
|
+
);
|
|
1366
|
+
return this.unwrapApiResponse(res, "Failed to get x402 resource status");
|
|
1367
|
+
},
|
|
1368
|
+
executeAction: async (resourceKey, actionKey, options) => {
|
|
1369
|
+
const headers = {};
|
|
1370
|
+
if (this.accessToken) {
|
|
1371
|
+
headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
1372
|
+
}
|
|
1373
|
+
if (options?.paymentSignature) {
|
|
1374
|
+
headers["PAYMENT-SIGNATURE"] = options.paymentSignature;
|
|
1375
|
+
}
|
|
1376
|
+
const body = {};
|
|
1377
|
+
if (options?.target) body.target = options.target;
|
|
1378
|
+
const bridgeToken = options?.bridgeToken ?? options?.bridge_token;
|
|
1379
|
+
if (bridgeToken !== void 0) body.bridge_token = bridgeToken;
|
|
1380
|
+
const res = await this.client.post(
|
|
1381
|
+
`/api/x402/resources/${encodeURIComponent(resourceKey)}/actions/${encodeURIComponent(actionKey)}`,
|
|
1382
|
+
body,
|
|
1383
|
+
{ headers, validateStatus: (s) => s < 500 }
|
|
1384
|
+
);
|
|
1385
|
+
const paymentRequiredHeader = res.headers?.["payment-required"] || res.headers?.["PAYMENT-REQUIRED"] || "";
|
|
1386
|
+
if (res.status === 402 && paymentRequiredHeader) {
|
|
1387
|
+
throw new X402PaymentRequiredSDKError(
|
|
1388
|
+
paymentRequiredHeader,
|
|
1389
|
+
res.data
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
return this.unwrapApiResponse(res, "Failed to execute x402 action");
|
|
1393
|
+
},
|
|
1394
|
+
getReceipt: async (receiptId) => {
|
|
1395
|
+
const res = await this.client.get(`/api/x402/receipts/${encodeURIComponent(receiptId)}`);
|
|
1396
|
+
return this.unwrapApiResponse(res, "Failed to get x402 receipt");
|
|
1397
|
+
},
|
|
1398
|
+
listReceipts: async (params) => {
|
|
1399
|
+
const q = new URLSearchParams();
|
|
1400
|
+
if (params?.resourceKey) q.append("resource_key", params.resourceKey);
|
|
1401
|
+
if (params?.limit !== void 0) q.append("limit", String(params.limit));
|
|
1402
|
+
if (params?.offset !== void 0) q.append("offset", String(params.offset));
|
|
1403
|
+
const qs = q.toString();
|
|
1404
|
+
const res = await this.client.get(`/api/x402/receipts${qs ? `?${qs}` : ""}`);
|
|
1405
|
+
return this.unwrapApiResponse(res, "Failed to list x402 receipts");
|
|
1406
|
+
},
|
|
1407
|
+
verifyHandoff: async (token, target, bridgeToken, options) => {
|
|
1408
|
+
const body = {
|
|
1409
|
+
token,
|
|
1410
|
+
resource_key: options.resourceKey,
|
|
1411
|
+
action_key: options.actionKey,
|
|
1412
|
+
target,
|
|
1413
|
+
bridge_token: bridgeToken
|
|
1414
|
+
};
|
|
1415
|
+
const res = await this.client.post("/api/x402/handoff/verify", body);
|
|
1416
|
+
return this.unwrapApiResponse(res, "Failed to verify x402 handoff");
|
|
1417
|
+
}
|
|
1418
|
+
};
|
|
1419
|
+
/**
|
|
1420
|
+
* Blind comparative skill-eval namespace.
|
|
1421
|
+
*/
|
|
1422
|
+
skillEvals = {
|
|
1423
|
+
createCase: async (payload, options) => {
|
|
1424
|
+
const headers = {};
|
|
1425
|
+
if (this.accessToken) {
|
|
1426
|
+
headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
1427
|
+
}
|
|
1428
|
+
if (options?.paymentSignature) {
|
|
1429
|
+
headers["PAYMENT-SIGNATURE"] = options.paymentSignature;
|
|
1430
|
+
}
|
|
1431
|
+
const res = await this.client.post("/api/skill-evals/cases", payload, {
|
|
1432
|
+
headers,
|
|
1433
|
+
validateStatus: (s) => s < 500
|
|
1434
|
+
});
|
|
1435
|
+
const paymentRequiredHeader = res.headers?.["payment-required"] || res.headers?.["PAYMENT-REQUIRED"] || "";
|
|
1436
|
+
if (res.status === 402 && paymentRequiredHeader) {
|
|
1437
|
+
throw new X402PaymentRequiredSDKError(paymentRequiredHeader, res.data);
|
|
1438
|
+
}
|
|
1439
|
+
return this.unwrapApiResponse(
|
|
1440
|
+
res,
|
|
1441
|
+
"Failed to create skill eval case"
|
|
1442
|
+
);
|
|
1443
|
+
},
|
|
1444
|
+
getCase: async (caseId) => {
|
|
1445
|
+
const res = await this.client.get(
|
|
1446
|
+
`/api/skill-evals/cases/${encodeURIComponent(caseId)}`
|
|
1447
|
+
);
|
|
1448
|
+
return this.unwrapApiResponse(res, "Failed to get skill eval case");
|
|
1449
|
+
},
|
|
1450
|
+
getReviewRoom: async (caseId) => {
|
|
1451
|
+
const res = await this.client.get(
|
|
1452
|
+
`/api/skill-evals/cases/${encodeURIComponent(caseId)}/room`
|
|
1453
|
+
);
|
|
1454
|
+
return this.unwrapApiResponse(res, "Failed to get skill eval room");
|
|
1455
|
+
},
|
|
1456
|
+
submitReview: async (caseId, payload, options) => {
|
|
1457
|
+
const config = this.skillEvalMutationConfig(options);
|
|
1458
|
+
const res = await this.client.post(
|
|
1459
|
+
`/api/skill-evals/cases/${encodeURIComponent(caseId)}/reviews`,
|
|
1460
|
+
payload,
|
|
1461
|
+
...config ? [config] : []
|
|
1462
|
+
);
|
|
1463
|
+
return this.unwrapApiResponse(
|
|
1464
|
+
res,
|
|
1465
|
+
"Failed to submit skill eval review"
|
|
1466
|
+
);
|
|
1467
|
+
},
|
|
1468
|
+
respondToReview: async (caseId, reviewId, payload, options) => {
|
|
1469
|
+
const config = this.skillEvalMutationConfig(options);
|
|
1470
|
+
const res = await this.client.post(
|
|
1471
|
+
`/api/skill-evals/cases/${encodeURIComponent(caseId)}/reviews/${encodeURIComponent(reviewId)}/response`,
|
|
1472
|
+
payload,
|
|
1473
|
+
...config ? [config] : []
|
|
1474
|
+
);
|
|
1475
|
+
return this.unwrapApiResponse(
|
|
1476
|
+
res,
|
|
1477
|
+
"Failed to respond to skill eval review"
|
|
1478
|
+
);
|
|
1479
|
+
},
|
|
1480
|
+
lockReviews: async (caseId, options) => {
|
|
1481
|
+
const config = this.skillEvalMutationConfig(options);
|
|
1482
|
+
const res = await this.client.post(
|
|
1483
|
+
`/api/skill-evals/cases/${encodeURIComponent(caseId)}/lock`,
|
|
1484
|
+
{},
|
|
1485
|
+
...config ? [config] : []
|
|
1486
|
+
);
|
|
1487
|
+
return this.unwrapApiResponse(
|
|
1488
|
+
res,
|
|
1489
|
+
"Failed to lock skill eval reviews"
|
|
1490
|
+
);
|
|
1491
|
+
},
|
|
1492
|
+
revealEvidence: async (caseId, payload, options) => {
|
|
1493
|
+
const config = this.skillEvalMutationConfig(options);
|
|
1494
|
+
const res = await this.client.post(
|
|
1495
|
+
`/api/skill-evals/cases/${encodeURIComponent(caseId)}/reveal`,
|
|
1496
|
+
payload,
|
|
1497
|
+
...config ? [config] : []
|
|
1498
|
+
);
|
|
1499
|
+
return this.unwrapApiResponse(
|
|
1500
|
+
res,
|
|
1501
|
+
"Failed to reveal skill eval evidence"
|
|
1502
|
+
);
|
|
1503
|
+
},
|
|
1504
|
+
createGovernanceSnapshot: async (caseId, payload, options) => {
|
|
1505
|
+
const config = this.skillEvalMutationConfig(options);
|
|
1506
|
+
const res = await this.client.post(
|
|
1507
|
+
`/api/skill-evals/cases/${encodeURIComponent(caseId)}/governance-snapshots`,
|
|
1508
|
+
payload,
|
|
1509
|
+
...config ? [config] : []
|
|
1510
|
+
);
|
|
1511
|
+
return this.unwrapApiResponse(
|
|
1512
|
+
res,
|
|
1513
|
+
"Failed to create skill eval governance snapshot"
|
|
1514
|
+
);
|
|
1515
|
+
},
|
|
1516
|
+
importGovernanceOutcome: async (snapshotId, payload) => {
|
|
1517
|
+
const res = await this.client.post(
|
|
1518
|
+
`/api/skill-evals/governance-snapshots/${encodeURIComponent(snapshotId)}/outcome`,
|
|
1519
|
+
payload
|
|
1520
|
+
);
|
|
1521
|
+
return this.unwrapApiResponse(
|
|
1522
|
+
res,
|
|
1523
|
+
"Failed to import skill eval governance outcome"
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
};
|
|
1261
1527
|
/**
|
|
1262
1528
|
* DayRate (Dynamic Scheduling) namespace
|
|
1263
1529
|
* For booking half-day sessions with dynamic pricing
|
|
@@ -1286,6 +1552,21 @@ var SPAPSClient = class {
|
|
|
1286
1552
|
createMultiBooking: async (payload) => {
|
|
1287
1553
|
const res = await this.client.post("/api/dayrate/book-multi", payload);
|
|
1288
1554
|
return this.unwrapApiResponse(res, "Failed to create multi-booking");
|
|
1555
|
+
},
|
|
1556
|
+
/**
|
|
1557
|
+
* Create a single-slot booking hold backed by an x402 paid-resource action.
|
|
1558
|
+
*/
|
|
1559
|
+
createX402Booking: async (payload) => {
|
|
1560
|
+
const res = await this.client.post("/api/dayrate/book-x402", payload);
|
|
1561
|
+
return this.unwrapApiResponse(res, "Failed to create x402 booking");
|
|
1562
|
+
},
|
|
1563
|
+
/**
|
|
1564
|
+
* Get guest-safe checkout confirmation state for a Stripe session.
|
|
1565
|
+
*/
|
|
1566
|
+
getCheckoutStatus: async (sessionId) => {
|
|
1567
|
+
const query = new URLSearchParams({ sessionId }).toString();
|
|
1568
|
+
const res = await this.client.get(`/api/dayrate/checkout-status?${query}`);
|
|
1569
|
+
return this.unwrapApiResponse(res, "Failed to get checkout status");
|
|
1289
1570
|
}
|
|
1290
1571
|
};
|
|
1291
1572
|
// Stripe Methods
|
|
@@ -1574,13 +1855,24 @@ var TokenManager = class _TokenManager {
|
|
|
1574
1855
|
s.removeItem(_TokenManager.USER_KEY);
|
|
1575
1856
|
}
|
|
1576
1857
|
}
|
|
1858
|
+
static decodePayload(token) {
|
|
1859
|
+
try {
|
|
1860
|
+
const parts = token.split(".");
|
|
1861
|
+
if (parts.length !== 3 || !parts[1]) return null;
|
|
1862
|
+
const decoded = JSON.parse(_TokenManager.base64Decode(parts[1]));
|
|
1863
|
+
if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) return null;
|
|
1864
|
+
return decoded;
|
|
1865
|
+
} catch {
|
|
1866
|
+
return null;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1577
1869
|
static isTokenExpired(token) {
|
|
1578
1870
|
try {
|
|
1579
1871
|
const parts = token.split(".");
|
|
1580
1872
|
if (parts.length !== 3 || !parts[1]) return true;
|
|
1581
1873
|
const payload = JSON.parse(_TokenManager.base64Decode(parts[1]));
|
|
1582
1874
|
const now = Math.floor(Date.now() / 1e3);
|
|
1583
|
-
return payload.exp < now;
|
|
1875
|
+
return typeof payload?.exp !== "number" || payload.exp < now;
|
|
1584
1876
|
} catch {
|
|
1585
1877
|
return true;
|
|
1586
1878
|
}
|
|
@@ -1695,6 +1987,39 @@ var WalletUtils = class _WalletUtils {
|
|
|
1695
1987
|
}
|
|
1696
1988
|
}
|
|
1697
1989
|
};
|
|
1990
|
+
function isEnvelope(value) {
|
|
1991
|
+
if (!value || typeof value !== "object") return false;
|
|
1992
|
+
const r = value;
|
|
1993
|
+
return "success" in r && typeof r.success === "boolean";
|
|
1994
|
+
}
|
|
1995
|
+
function isSuccessEnvelope(value) {
|
|
1996
|
+
return isEnvelope(value) && value.success === true;
|
|
1997
|
+
}
|
|
1998
|
+
function isErrorEnvelope(value) {
|
|
1999
|
+
if (!isEnvelope(value) || value.success !== false) return false;
|
|
2000
|
+
const error = value.error;
|
|
2001
|
+
if (!error || typeof error !== "object") return false;
|
|
2002
|
+
const r = error;
|
|
2003
|
+
return typeof r.code === "string" && typeof r.message === "string";
|
|
2004
|
+
}
|
|
2005
|
+
function unwrapEnvelope(value, fallbackMessage) {
|
|
2006
|
+
if (!isEnvelope(value)) return value;
|
|
2007
|
+
if (value.success === false) {
|
|
2008
|
+
const message = isErrorEnvelope(value) ? value.error.message : void 0;
|
|
2009
|
+
throw new Error(message || fallbackMessage || "SPAPS request failed");
|
|
2010
|
+
}
|
|
2011
|
+
return value.data;
|
|
2012
|
+
}
|
|
2013
|
+
function unwrapNestedData(value) {
|
|
2014
|
+
if (isEnvelope(value) && value.success !== false) {
|
|
2015
|
+
const inner = value.data;
|
|
2016
|
+
if (inner && typeof inner === "object" && "data" in inner) {
|
|
2017
|
+
return inner.data;
|
|
2018
|
+
}
|
|
2019
|
+
return inner;
|
|
2020
|
+
}
|
|
2021
|
+
return value;
|
|
2022
|
+
}
|
|
1698
2023
|
function createBrowserClient(publishableKey, options) {
|
|
1699
2024
|
if (!publishableKey.startsWith("spaps_pub_")) {
|
|
1700
2025
|
console.warn("\u26A0\uFE0F SPAPS: Expected a publishable key (spaps_pub_xxx). Using a secret key in browser is not recommended.");
|
|
@@ -1728,6 +2053,7 @@ export {
|
|
|
1728
2053
|
TokenManager,
|
|
1729
2054
|
WalletUtils,
|
|
1730
2055
|
WebSocketAuthHelper,
|
|
2056
|
+
X402PaymentRequiredSDKError,
|
|
1731
2057
|
canAccessAdmin,
|
|
1732
2058
|
createBrowserClient,
|
|
1733
2059
|
createPermissionChecker,
|
|
@@ -1741,7 +2067,14 @@ export {
|
|
|
1741
2067
|
getUserRole,
|
|
1742
2068
|
hasPermission,
|
|
1743
2069
|
isAdminAccount,
|
|
2070
|
+
isEnvelope,
|
|
2071
|
+
isErrorEnvelope,
|
|
2072
|
+
isSuccessEnvelope,
|
|
2073
|
+
isX402PaymentRequired,
|
|
2074
|
+
isX402ResourceStatus,
|
|
1744
2075
|
secureMessageMetadataSchema,
|
|
1745
2076
|
secureMessageSchema,
|
|
2077
|
+
unwrapEnvelope,
|
|
2078
|
+
unwrapNestedData,
|
|
1746
2079
|
verifyCryptoWebhookSignature
|
|
1747
2080
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spaps-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Sweet Potato Authentication & Payment Service SDK - Zero-config client with built-in permission checking, role-based access control, and dayrate scheduling",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"build": "tsup --tsconfig tsconfig.json src/index.ts --format cjs,esm --dts --clean",
|
|
21
21
|
"dev": "tsup --tsconfig tsconfig.json src/index.ts --format cjs,esm --dts --watch",
|
|
22
22
|
"test": "vitest run",
|
|
23
|
+
"test:cov": "vitest run --coverage --coverage.reporter=lcov --coverage.reporter=text",
|
|
23
24
|
"test:readme": "vitest run test/readme-snippets.test.ts",
|
|
24
25
|
"typecheck:readme": "tsc -p tsconfig.readme.json --noEmit",
|
|
25
26
|
"smoke-test": "npm run build && node smoke-test.js",
|
|
@@ -54,6 +55,7 @@
|
|
|
54
55
|
},
|
|
55
56
|
"devDependencies": {
|
|
56
57
|
"@types/node": "^20.10.0",
|
|
58
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
57
59
|
"tsup": "^8.0.1",
|
|
58
60
|
"typescript": "^5.3.2",
|
|
59
61
|
"vitest": "^4.0.18"
|