@01.software/sdk 0.51.0 → 0.65.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 +313 -0
- package/README.md +228 -102
- package/dist/client.cjs +556 -60
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +6 -6
- package/dist/client.d.ts +6 -6
- package/dist/client.js +556 -60
- package/dist/client.js.map +1 -1
- package/dist/{collection-client-CWAeuzV4.d.cts → collection-client-B7rsTtVj.d.cts} +6 -6
- package/dist/{collection-client-hYvVfHIm.d.ts → collection-client-C2sMvfme.d.ts} +6 -6
- package/dist/{const-JD8obaKg.d.ts → const-CRxPTDGt.d.ts} +2 -2
- package/dist/{const-D8LixhAJ.d.cts → const-D6ZRQLxI.d.cts} +2 -2
- package/dist/{index-B0Hjh3Ey.d.ts → index-1auUEYrl.d.ts} +24 -7
- package/dist/{index-Bu0kCc6o.d.cts → index-Bbpf5t-l.d.cts} +24 -7
- package/dist/index.cjs +573 -260
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -12
- package/dist/index.d.ts +19 -12
- package/dist/index.js +573 -260
- package/dist/index.js.map +1 -1
- package/dist/{payload-types-BOsckg8B.d.cts → payload-types-BnIET0xU.d.cts} +567 -438
- package/dist/{payload-types-BOsckg8B.d.ts → payload-types-BnIET0xU.d.ts} +567 -438
- package/dist/query.cjs +62 -3
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.cts +5 -5
- package/dist/query.d.ts +5 -5
- package/dist/query.js +62 -3
- package/dist/query.js.map +1 -1
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/server.cjs +557 -20
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +14 -8
- package/dist/server.d.ts +14 -8
- package/dist/server.js +557 -20
- package/dist/server.js.map +1 -1
- package/dist/{types-5ZlcAFuU.d.ts → types-D1y4TgQm.d.ts} +2 -2
- package/dist/{types-Bj8j7VHU.d.ts → types-DnRC3nLG.d.ts} +464 -16
- package/dist/{types-B14sEt1z.d.cts → types-Dp1oYpD3.d.cts} +2 -2
- package/dist/{types-CM5qiR2k.d.cts → types-Dzn0LD2N.d.cts} +464 -16
- package/dist/ui/form.d.cts +1 -1
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/video.cjs +4 -2
- package/dist/ui/video.cjs.map +1 -1
- package/dist/ui/video.d.cts +4 -2
- package/dist/ui/video.d.ts +4 -2
- package/dist/ui/video.js +4 -2
- package/dist/ui/video.js.map +1 -1
- package/dist/webhook.cjs +13 -2
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +5 -5
- package/dist/webhook.d.ts +5 -5
- package/dist/webhook.js +13 -2
- package/dist/webhook.js.map +1 -1
- package/package.json +9 -28
- package/dist/analytics/react.cjs +0 -344
- package/dist/analytics/react.cjs.map +0 -1
- package/dist/analytics/react.d.cts +0 -24
- package/dist/analytics/react.d.ts +0 -24
- package/dist/analytics/react.js +0 -319
- package/dist/analytics/react.js.map +0 -1
- package/dist/analytics.cjs +0 -238
- package/dist/analytics.cjs.map +0 -1
- package/dist/analytics.d.cts +0 -39
- package/dist/analytics.d.ts +0 -39
- package/dist/analytics.js +0 -215
- package/dist/analytics.js.map +0 -1
package/dist/client.js
CHANGED
|
@@ -1005,6 +1005,49 @@ function stockSnapshotSearchParams(params) {
|
|
|
1005
1005
|
}
|
|
1006
1006
|
|
|
1007
1007
|
// src/core/collection/http-client.ts
|
|
1008
|
+
function validateWhere(where) {
|
|
1009
|
+
if (where === void 0) return false;
|
|
1010
|
+
let hasNull = false;
|
|
1011
|
+
function visit(value, depth, key) {
|
|
1012
|
+
if (depth > 10) {
|
|
1013
|
+
throw createValidationError(
|
|
1014
|
+
"Collection where must not exceed nesting depth 10."
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
if (value === null && (key === "equals" || key === "not_equals")) {
|
|
1018
|
+
hasNull = true;
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
if (value == null) {
|
|
1022
|
+
throw createValidationError(
|
|
1023
|
+
"Collection where must not contain null or undefined values."
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
if (Array.isArray(value)) {
|
|
1027
|
+
if (value.length === 0) {
|
|
1028
|
+
throw createValidationError(
|
|
1029
|
+
"Collection where must not contain empty arrays, including and/or."
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
if (value.length > 1e3) {
|
|
1033
|
+
throw createValidationError(
|
|
1034
|
+
"Collection where arrays must not contain more than 1000 elements."
|
|
1035
|
+
);
|
|
1036
|
+
}
|
|
1037
|
+
for (const item of value) visit(item, depth + 1);
|
|
1038
|
+
} else if (typeof value === "object" && !(value instanceof Date)) {
|
|
1039
|
+
const values = Object.entries(value);
|
|
1040
|
+
if (values.length === 0) {
|
|
1041
|
+
throw createValidationError(
|
|
1042
|
+
"Collection where must not contain empty condition objects."
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
for (const [key2, item] of values) visit(item, depth + 1, key2);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
visit(where, 0);
|
|
1049
|
+
return hasNull;
|
|
1050
|
+
}
|
|
1008
1051
|
var HttpClient = class {
|
|
1009
1052
|
constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId, apiUrl) {
|
|
1010
1053
|
this.publishableKey = requirePublishableKeyForSecret(
|
|
@@ -1051,7 +1094,8 @@ var HttpClient = class {
|
|
|
1051
1094
|
...rest,
|
|
1052
1095
|
"fallback-locale": fallbackLocale === false ? "none" : fallbackLocale
|
|
1053
1096
|
};
|
|
1054
|
-
const
|
|
1097
|
+
const encodedOptions = validateWhere(queryOptions.where) ? { ...queryOptions, where: JSON.stringify(queryOptions.where) } : queryOptions;
|
|
1098
|
+
const queryString = stringify(encodedOptions, { addQueryPrefix: true });
|
|
1055
1099
|
return queryString ? `${endpoint}${queryString}` : endpoint;
|
|
1056
1100
|
}
|
|
1057
1101
|
assertJsonResponse(response) {
|
|
@@ -1353,6 +1397,11 @@ var COMMERCE_ENDPOINT_OPERATIONS = {
|
|
|
1353
1397
|
detail: strictCommerceOperation("checkouts", "post", "/detail"),
|
|
1354
1398
|
query: strictCommerceOperation("checkouts", "post", "/query")
|
|
1355
1399
|
},
|
|
1400
|
+
draftOrders: {
|
|
1401
|
+
calculate: operation("draft-orders", "post", "/:id/calculate"),
|
|
1402
|
+
complete: operation("draft-orders", "post", "/:id/complete"),
|
|
1403
|
+
finalize: operation("draft-orders", "post", "/:id/finalize")
|
|
1404
|
+
},
|
|
1356
1405
|
transactions: {
|
|
1357
1406
|
update: operation("transactions", "post", "/update")
|
|
1358
1407
|
},
|
|
@@ -1452,8 +1501,51 @@ var portOneStandardActionCapabilityOperations = /* @__PURE__ */ new Set([
|
|
|
1452
1501
|
]);
|
|
1453
1502
|
var MEMBERSHIP_ENDPOINT_OPERATIONS = {
|
|
1454
1503
|
subscribe: operation(null, "post", "/memberships/subscribe"),
|
|
1504
|
+
billingCustody: operation(null, "post", "/memberships/billing-custody"),
|
|
1455
1505
|
cancel: operation(null, "post", "/memberships/:id/cancel"),
|
|
1456
|
-
me: operation(null, "get", "/memberships/me")
|
|
1506
|
+
me: operation(null, "get", "/memberships/me"),
|
|
1507
|
+
chargeResult: operation(null, "post", "/memberships/:id/charge-result"),
|
|
1508
|
+
refundResult: operation(null, "post", "/memberships/:id/refund-result"),
|
|
1509
|
+
revoke: operation(null, "post", "/memberships/:id/revoke")
|
|
1510
|
+
};
|
|
1511
|
+
var EXTERNAL_EFFECT_CLAIM_ENDPOINT_OPERATIONS = {
|
|
1512
|
+
claim: operation(null, "post", "/external-effect-claims/claim"),
|
|
1513
|
+
get: operation(null, "post", "/external-effect-claims/get"),
|
|
1514
|
+
arm: operation(null, "post", "/external-effect-claims/arm"),
|
|
1515
|
+
release: operation(null, "post", "/external-effect-claims/release"),
|
|
1516
|
+
report: operation(null, "post", "/external-effect-claims/report"),
|
|
1517
|
+
recovery: operation(null, "post", "/external-effect-claims/recovery")
|
|
1518
|
+
};
|
|
1519
|
+
var INSTALLATION_CREDENTIAL_ENDPOINT_OPERATIONS = {
|
|
1520
|
+
create: operation(null, "post", "/installation-credentials/create"),
|
|
1521
|
+
get: operation(null, "post", "/installation-credentials/get"),
|
|
1522
|
+
replace: operation(null, "post", "/installation-credentials/replace"),
|
|
1523
|
+
dispose: operation(null, "post", "/installation-credentials/dispose")
|
|
1524
|
+
};
|
|
1525
|
+
var EXTERNAL_CUSTOMER_LINK_ENDPOINT_OPERATIONS = {
|
|
1526
|
+
connect: operation("customers", "post", "/external-links/connect"),
|
|
1527
|
+
get: operation("customers", "post", "/external-links/get"),
|
|
1528
|
+
list: operation("customers", "post", "/external-links/list"),
|
|
1529
|
+
unlink: operation("customers", "post", "/external-links/unlink"),
|
|
1530
|
+
capturePreviousState: operation(
|
|
1531
|
+
"customers",
|
|
1532
|
+
"post",
|
|
1533
|
+
"/external-links/capture-previous-state"
|
|
1534
|
+
),
|
|
1535
|
+
clearPreviousState: operation(
|
|
1536
|
+
"customers",
|
|
1537
|
+
"post",
|
|
1538
|
+
"/external-links/clear-previous-state"
|
|
1539
|
+
)
|
|
1540
|
+
};
|
|
1541
|
+
var CUSTOMER_GROUP_ENDPOINT_OPERATIONS = {
|
|
1542
|
+
members: operation("customer-groups", "get", "/:id/members"),
|
|
1543
|
+
grant: operation("customer-groups", "post", "/:id/members/grant"),
|
|
1544
|
+
revoke: operation("customer-groups", "post", "/:id/members/revoke")
|
|
1545
|
+
};
|
|
1546
|
+
var CUSTOMER_AUTH_ENDPOINT_OPERATIONS = {
|
|
1547
|
+
revokeRefresh: operation("customers", "post", "/revoke-refresh"),
|
|
1548
|
+
withdraw: operation("customers", "post", "/withdraw")
|
|
1457
1549
|
};
|
|
1458
1550
|
var EMBEDDED_ADMIN_ENDPOINT_OPERATIONS = {
|
|
1459
1551
|
show: operation("tenants", "get", "/embedded-admin-app"),
|
|
@@ -1478,6 +1570,14 @@ var COMMUNITY_ENDPOINT_OPERATIONS = {
|
|
|
1478
1570
|
detail: operation("posts", "get", "/detail")
|
|
1479
1571
|
}
|
|
1480
1572
|
};
|
|
1573
|
+
var GALLERY_ENDPOINT_OPERATIONS = {
|
|
1574
|
+
items: {
|
|
1575
|
+
storefront: operation("gallery-items", "get", "/storefront")
|
|
1576
|
+
}
|
|
1577
|
+
};
|
|
1578
|
+
var VIDEO_ENDPOINT_OPERATIONS = {
|
|
1579
|
+
playback: operation("videos", "post", "/:id/playback")
|
|
1580
|
+
};
|
|
1481
1581
|
function resolveOperationTemplate(descriptor) {
|
|
1482
1582
|
return descriptor.collection ? `/api/${descriptor.collection}${descriptor.path}` : `/api${descriptor.path}`;
|
|
1483
1583
|
}
|
|
@@ -2067,6 +2167,15 @@ var PublicContentApi = class extends RawCustomerScopedApi {
|
|
|
2067
2167
|
get(endpoint) {
|
|
2068
2168
|
return this.request(endpoint, { method: "GET" });
|
|
2069
2169
|
}
|
|
2170
|
+
galleryItems(options) {
|
|
2171
|
+
const operation2 = GALLERY_ENDPOINT_OPERATIONS.items.storefront;
|
|
2172
|
+
return this.request(
|
|
2173
|
+
resolveOperationPath(operation2, {
|
|
2174
|
+
query: galleryItemsListQuery(options)
|
|
2175
|
+
}),
|
|
2176
|
+
{ method: resolveOperationMethod(operation2) }
|
|
2177
|
+
);
|
|
2178
|
+
}
|
|
2070
2179
|
};
|
|
2071
2180
|
function appendCommonParams(search, options, defaultSort) {
|
|
2072
2181
|
search.set("sort", options?.sort ?? defaultSort);
|
|
@@ -2086,12 +2195,12 @@ function linksListPath(options) {
|
|
|
2086
2195
|
search.set("featured", String(options.featured));
|
|
2087
2196
|
return `/api/links/storefront?${search}`;
|
|
2088
2197
|
}
|
|
2089
|
-
function
|
|
2198
|
+
function galleryItemsListQuery(options) {
|
|
2090
2199
|
const search = new URLSearchParams();
|
|
2091
2200
|
appendCommonParams(search, options, "manual");
|
|
2092
2201
|
if (options?.galleryId) search.set("galleryId", options.galleryId);
|
|
2093
2202
|
if (options?.gallerySlug) search.set("gallerySlug", options.gallerySlug);
|
|
2094
|
-
return
|
|
2203
|
+
return search;
|
|
2095
2204
|
}
|
|
2096
2205
|
var ContentClient = class {
|
|
2097
2206
|
constructor(options) {
|
|
@@ -2105,9 +2214,7 @@ var ContentClient = class {
|
|
|
2105
2214
|
list: (params) => api.get(linksListPath(params))
|
|
2106
2215
|
};
|
|
2107
2216
|
this.galleryItems = {
|
|
2108
|
-
list: (params) => api.
|
|
2109
|
-
galleryItemsListPath(params)
|
|
2110
|
-
)
|
|
2217
|
+
list: (params) => api.galleryItems(params)
|
|
2111
2218
|
};
|
|
2112
2219
|
}
|
|
2113
2220
|
};
|
|
@@ -2163,7 +2270,10 @@ var ORDER_PRIMARY_DISPLAY_STATUSES = [
|
|
|
2163
2270
|
];
|
|
2164
2271
|
var CHECKOUT_STATUSES = ["open", "placed", "abandoned"];
|
|
2165
2272
|
var ABANDONED_REASONS = ["expired", "customer", "operator"];
|
|
2166
|
-
|
|
2273
|
+
function managedPaymentProviders(providers) {
|
|
2274
|
+
return providers;
|
|
2275
|
+
}
|
|
2276
|
+
var PAYMENT_PROVIDERS = managedPaymentProviders(["toss", "portone"]);
|
|
2167
2277
|
var PAYMENT_PROVIDER_STATUSES = [
|
|
2168
2278
|
"requires_payment_method",
|
|
2169
2279
|
"requires_action",
|
|
@@ -2211,6 +2321,26 @@ function invalidCanonicalResponse(resource, details) {
|
|
|
2211
2321
|
function fail(resource, path, expected) {
|
|
2212
2322
|
invalidCanonicalResponse(resource, { path, expected });
|
|
2213
2323
|
}
|
|
2324
|
+
function providerMismatch(resource, expected, actual, operation2) {
|
|
2325
|
+
if (operation2 === "cancel") {
|
|
2326
|
+
const method = {
|
|
2327
|
+
toss: "cancel()",
|
|
2328
|
+
portone: "cancelPortOne()"
|
|
2329
|
+
}[actual];
|
|
2330
|
+
throw createApiError(
|
|
2331
|
+
`Canonical ${resource} response provider mismatch: expected ${expected}, received ${actual}.`,
|
|
2332
|
+
502,
|
|
2333
|
+
{ resource, path: "$.provider", expected, actual },
|
|
2334
|
+
`The server applied the cancellation to a ${actual} PaymentSession, but this SDK method expects ${expected}.`,
|
|
2335
|
+
`Re-invoke ${method} with the same idempotencyKey; do not create a new key.`
|
|
2336
|
+
);
|
|
2337
|
+
}
|
|
2338
|
+
invalidCanonicalResponse(resource, {
|
|
2339
|
+
path: "$.provider",
|
|
2340
|
+
expected,
|
|
2341
|
+
actual
|
|
2342
|
+
});
|
|
2343
|
+
}
|
|
2214
2344
|
function record(value, resource, path) {
|
|
2215
2345
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2216
2346
|
fail(resource, path, "object");
|
|
@@ -3503,7 +3633,7 @@ function validateNextAction(value, provider, resource, path) {
|
|
|
3503
3633
|
if (action.customerEmail !== void 0)
|
|
3504
3634
|
validateCustomer({ email: action.customerEmail }, resource, path);
|
|
3505
3635
|
}
|
|
3506
|
-
function validatePublicPaymentSession(value, expectedProvider) {
|
|
3636
|
+
function validatePublicPaymentSession(value, expectedProvider, operation2) {
|
|
3507
3637
|
const resource = "PaymentSession";
|
|
3508
3638
|
const session = record(value, resource, "$");
|
|
3509
3639
|
const provider = enumValue(
|
|
@@ -3512,9 +3642,6 @@ function validatePublicPaymentSession(value, expectedProvider) {
|
|
|
3512
3642
|
resource,
|
|
3513
3643
|
"$.provider"
|
|
3514
3644
|
);
|
|
3515
|
-
if (expectedProvider !== void 0 && provider !== expectedProvider) {
|
|
3516
|
-
fail(resource, "$.provider", expectedProvider);
|
|
3517
|
-
}
|
|
3518
3645
|
const status = enumValue(
|
|
3519
3646
|
session.providerStatus,
|
|
3520
3647
|
PAYMENT_PROVIDER_STATUSES,
|
|
@@ -3718,44 +3845,128 @@ function validatePublicPaymentSession(value, expectedProvider) {
|
|
|
3718
3845
|
"$.cancellationReason"
|
|
3719
3846
|
);
|
|
3720
3847
|
}
|
|
3848
|
+
if (expectedProvider !== void 0 && provider !== expectedProvider) {
|
|
3849
|
+
if (operation2 === "cancel" && status !== "canceling" && status !== "canceled") {
|
|
3850
|
+
fail(
|
|
3851
|
+
resource,
|
|
3852
|
+
"$.providerStatus",
|
|
3853
|
+
"canceling or canceled after a successful cancellation"
|
|
3854
|
+
);
|
|
3855
|
+
}
|
|
3856
|
+
providerMismatch(resource, expectedProvider, provider, operation2);
|
|
3857
|
+
}
|
|
3721
3858
|
return value;
|
|
3722
3859
|
}
|
|
3723
3860
|
|
|
3724
3861
|
// src/core/customer/customer-auth.ts
|
|
3725
3862
|
var DEFAULT_TIMEOUT2 = 15e3;
|
|
3726
|
-
|
|
3863
|
+
var REFRESH_LOCK_TIMEOUT = DEFAULT_TIMEOUT2 * 3;
|
|
3864
|
+
var SESSION_RECORD_VERSION = 1;
|
|
3865
|
+
var STORAGE_UNUSABLE = /* @__PURE__ */ Symbol("storage-unusable");
|
|
3866
|
+
function canonicalApiIdentity(apiUrl) {
|
|
3727
3867
|
try {
|
|
3728
|
-
|
|
3868
|
+
const url = new URL(apiUrl);
|
|
3869
|
+
return `${url.protocol.toLowerCase()}//${url.host.toLowerCase()}${url.pathname.replace(/\/+$/, "")}`;
|
|
3870
|
+
} catch {
|
|
3871
|
+
return apiUrl.trim().toLowerCase().replace(/\/+$/, "");
|
|
3872
|
+
}
|
|
3873
|
+
}
|
|
3874
|
+
function buildRefreshLockName(apiUrl, publishableKey, storageKey) {
|
|
3875
|
+
return `01software:customer-auth-refresh:${canonicalApiIdentity(apiUrl)}:${publishableKey}:${storageKey}`;
|
|
3876
|
+
}
|
|
3877
|
+
function getRefreshLockRequester() {
|
|
3878
|
+
try {
|
|
3879
|
+
if (typeof navigator === "undefined") return null;
|
|
3880
|
+
const locks = navigator.locks;
|
|
3881
|
+
if (!locks || typeof locks.request !== "function") return null;
|
|
3882
|
+
return (name, options, callback) => locks.request(
|
|
3883
|
+
name,
|
|
3884
|
+
options,
|
|
3885
|
+
() => callback()
|
|
3886
|
+
);
|
|
3729
3887
|
} catch {
|
|
3730
3888
|
return null;
|
|
3731
3889
|
}
|
|
3732
3890
|
}
|
|
3891
|
+
function parseSessionRecord(raw) {
|
|
3892
|
+
try {
|
|
3893
|
+
const parsed = JSON.parse(raw);
|
|
3894
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
3895
|
+
const candidate = parsed;
|
|
3896
|
+
if (candidate.v !== SESSION_RECORD_VERSION) return null;
|
|
3897
|
+
if (typeof candidate.rev !== "number" || !Number.isFinite(candidate.rev)) {
|
|
3898
|
+
return null;
|
|
3899
|
+
}
|
|
3900
|
+
const token = candidate.token;
|
|
3901
|
+
const refreshToken = candidate.refreshToken;
|
|
3902
|
+
if (token !== null && typeof token !== "string") return null;
|
|
3903
|
+
if (refreshToken !== null && typeof refreshToken !== "string") return null;
|
|
3904
|
+
return {
|
|
3905
|
+
v: SESSION_RECORD_VERSION,
|
|
3906
|
+
rev: candidate.rev,
|
|
3907
|
+
token,
|
|
3908
|
+
refreshToken
|
|
3909
|
+
};
|
|
3910
|
+
} catch {
|
|
3911
|
+
return null;
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
function readSessionRecord(recordKey, mirrorKey, mirrorRefreshKey) {
|
|
3915
|
+
let raw;
|
|
3916
|
+
try {
|
|
3917
|
+
raw = localStorage.getItem(recordKey);
|
|
3918
|
+
} catch {
|
|
3919
|
+
return STORAGE_UNUSABLE;
|
|
3920
|
+
}
|
|
3921
|
+
if (raw !== null) {
|
|
3922
|
+
const parsed = parseSessionRecord(raw);
|
|
3923
|
+
return parsed ? { record: parsed, stored: true } : STORAGE_UNUSABLE;
|
|
3924
|
+
}
|
|
3925
|
+
try {
|
|
3926
|
+
return {
|
|
3927
|
+
record: {
|
|
3928
|
+
v: SESSION_RECORD_VERSION,
|
|
3929
|
+
rev: 0,
|
|
3930
|
+
token: localStorage.getItem(mirrorKey),
|
|
3931
|
+
refreshToken: localStorage.getItem(mirrorRefreshKey)
|
|
3932
|
+
},
|
|
3933
|
+
stored: false
|
|
3934
|
+
};
|
|
3935
|
+
} catch {
|
|
3936
|
+
return STORAGE_UNUSABLE;
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3733
3939
|
var CustomerAuth = class {
|
|
3734
3940
|
constructor(publishableKey, options, apiUrl) {
|
|
3735
3941
|
this.refreshPromise = null;
|
|
3942
|
+
/** Canonical record key; null unless persistence is active in a browser. */
|
|
3943
|
+
this.persistRecordKey = null;
|
|
3944
|
+
/** Advisory single-value access mirror, plus the retired refresh mirror. */
|
|
3945
|
+
this.persistMirrorKey = null;
|
|
3946
|
+
this.persistMirrorRefreshKey = null;
|
|
3947
|
+
/** Cross-context refresh lock name; null when there is no shared storage. */
|
|
3948
|
+
this.refreshLockName = null;
|
|
3949
|
+
/** Revision of the canonical record this instance last observed. */
|
|
3950
|
+
this.sessionRev = 0;
|
|
3736
3951
|
this.publishableKey = publishableKey;
|
|
3737
3952
|
this.baseUrl = resolveApiUrl(apiUrl);
|
|
3738
3953
|
const persist = options?.persist ?? true;
|
|
3739
3954
|
if (persist) {
|
|
3740
3955
|
const key = typeof persist === "string" ? persist : "customer-token";
|
|
3741
|
-
const refreshKey = `${key}:refresh`;
|
|
3742
3956
|
const isBrowser = typeof window !== "undefined";
|
|
3743
|
-
this.token =
|
|
3744
|
-
this.refreshTokenValue =
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
} catch {
|
|
3757
|
-
}
|
|
3758
|
-
} : void 0;
|
|
3957
|
+
this.token = null;
|
|
3958
|
+
this.refreshTokenValue = null;
|
|
3959
|
+
if (isBrowser) {
|
|
3960
|
+
this.persistRecordKey = `${key}:session`;
|
|
3961
|
+
this.persistMirrorKey = key;
|
|
3962
|
+
this.persistMirrorRefreshKey = `${key}:refresh`;
|
|
3963
|
+
this.refreshLockName = buildRefreshLockName(
|
|
3964
|
+
this.baseUrl,
|
|
3965
|
+
publishableKey,
|
|
3966
|
+
key
|
|
3967
|
+
);
|
|
3968
|
+
this.syncFromPersistedStorage();
|
|
3969
|
+
}
|
|
3759
3970
|
} else {
|
|
3760
3971
|
this.token = options?.token ?? null;
|
|
3761
3972
|
this.refreshTokenValue = options?.refreshToken ?? null;
|
|
@@ -3783,66 +3994,334 @@ var CustomerAuth = class {
|
|
|
3783
3994
|
body: JSON.stringify(data)
|
|
3784
3995
|
}
|
|
3785
3996
|
);
|
|
3786
|
-
this.
|
|
3787
|
-
|
|
3997
|
+
if (result.refreshToken) this.setSession(result.token, result.refreshToken);
|
|
3998
|
+
else this.setToken(result.token);
|
|
3788
3999
|
return result;
|
|
3789
4000
|
}
|
|
3790
4001
|
/**
|
|
3791
4002
|
* Refresh the current token. Uses a refresh token when available, otherwise
|
|
3792
4003
|
* falls back to the legacy bearer-token compatibility path.
|
|
4004
|
+
*
|
|
4005
|
+
* Refresh tokens are single-use, so a persisted session shared by several
|
|
4006
|
+
* tabs or SDK instances must rotate at most once: concurrent rotations of the
|
|
4007
|
+
* same token are replay from the server's point of view and revoke the whole
|
|
4008
|
+
* family. Persisted browser sessions therefore serialize rotation across
|
|
4009
|
+
* contexts and re-read the canonical persisted credentials inside the lock.
|
|
3793
4010
|
*/
|
|
3794
4011
|
async refreshToken() {
|
|
4012
|
+
this.syncFromPersistedStorage();
|
|
3795
4013
|
if (!this.refreshTokenValue && !this.token) {
|
|
3796
4014
|
throw new ApiError("Not authenticated", 401);
|
|
3797
4015
|
}
|
|
3798
4016
|
if (this.refreshPromise) return this.refreshPromise;
|
|
3799
|
-
|
|
4017
|
+
const intended = this.currentFence();
|
|
4018
|
+
this.refreshPromise = this.rotateAcrossContexts(intended);
|
|
3800
4019
|
try {
|
|
3801
4020
|
return await this.refreshPromise;
|
|
3802
4021
|
} finally {
|
|
3803
4022
|
this.refreshPromise = null;
|
|
3804
4023
|
}
|
|
3805
4024
|
}
|
|
3806
|
-
|
|
3807
|
-
|
|
4025
|
+
/**
|
|
4026
|
+
* Re-read the canonical persisted session. In persisted mode the shared
|
|
4027
|
+
* record — not this instance's memory — is the source of truth, so another
|
|
4028
|
+
* context's rotation, login, or logout is observed before the next credential
|
|
4029
|
+
* use. Change callbacks are deliberately not invoked: this is a read.
|
|
4030
|
+
*/
|
|
4031
|
+
syncFromPersistedStorage() {
|
|
4032
|
+
const canonical = this.readCanonicalSession();
|
|
4033
|
+
if (canonical === STORAGE_UNUSABLE) {
|
|
4034
|
+
this.disablePersistence();
|
|
4035
|
+
return;
|
|
4036
|
+
}
|
|
4037
|
+
if (!canonical) return;
|
|
4038
|
+
this.sessionRev = canonical.record.rev;
|
|
4039
|
+
this.token = canonical.record.token;
|
|
4040
|
+
this.refreshTokenValue = canonical.record.refreshToken;
|
|
4041
|
+
}
|
|
4042
|
+
/** `null` when this instance does not persist. */
|
|
4043
|
+
readCanonicalSession() {
|
|
4044
|
+
if (!this.persistRecordKey || !this.persistMirrorKey || !this.persistMirrorRefreshKey) {
|
|
4045
|
+
return null;
|
|
4046
|
+
}
|
|
4047
|
+
return readSessionRecord(
|
|
4048
|
+
this.persistRecordKey,
|
|
4049
|
+
this.persistMirrorKey,
|
|
4050
|
+
this.persistMirrorRefreshKey
|
|
4051
|
+
);
|
|
4052
|
+
}
|
|
4053
|
+
/**
|
|
4054
|
+
* Publish a change to canonical storage.
|
|
4055
|
+
*
|
|
4056
|
+
* The canonical pair is re-read immediately before the single record write so
|
|
4057
|
+
* that a field this call does not change is taken from that read rather than
|
|
4058
|
+
* from this instance's possibly stale copy. That merge is BEST-EFFORT: it
|
|
4059
|
+
* removes the stale counterpart an instance would otherwise carry, but it is
|
|
4060
|
+
* not a compare-and-swap. Web Storage offers none, and the synchronous public
|
|
4061
|
+
* mutators cannot await the refresh lease, so a commit that lands between
|
|
4062
|
+
* this read and the store operation is lost by ordering — including the
|
|
4063
|
+
* counterpart just read, which is then republished stale. The window is one
|
|
4064
|
+
* store operation wide and the server remains authoritative; see
|
|
4065
|
+
* `CUST-AUTH-033` for the stated limit.
|
|
4066
|
+
*
|
|
4067
|
+
* Returns `false` only when `requireLiveSession` refused a write because the
|
|
4068
|
+
* canonical session had already been cleared.
|
|
4069
|
+
*/
|
|
4070
|
+
commitPersistedSession(change, options) {
|
|
4071
|
+
const recordKey = this.persistRecordKey;
|
|
4072
|
+
const mirrorKey = this.persistMirrorKey;
|
|
4073
|
+
const mirrorRefreshKey = this.persistMirrorRefreshKey;
|
|
4074
|
+
const canonical = this.readCanonicalSession();
|
|
4075
|
+
if (canonical === null || !recordKey || !mirrorKey || !mirrorRefreshKey) {
|
|
4076
|
+
return true;
|
|
4077
|
+
}
|
|
4078
|
+
if (canonical === STORAGE_UNUSABLE) {
|
|
4079
|
+
this.disablePersistence();
|
|
4080
|
+
return true;
|
|
4081
|
+
}
|
|
4082
|
+
const current = canonical.record;
|
|
4083
|
+
if (options?.requireLiveSession && !current.token && !current.refreshToken) {
|
|
4084
|
+
return false;
|
|
4085
|
+
}
|
|
4086
|
+
const next = {
|
|
4087
|
+
v: SESSION_RECORD_VERSION,
|
|
4088
|
+
rev: current.rev + 1,
|
|
4089
|
+
token: "token" in change ? change.token ?? null : current.token,
|
|
4090
|
+
refreshToken: "refreshToken" in change ? change.refreshToken ?? null : current.refreshToken
|
|
4091
|
+
};
|
|
4092
|
+
if (
|
|
4093
|
+
// Only a state that is genuinely persisted can already say what this
|
|
4094
|
+
// write would say. An absent record reads as an empty pair, and treating
|
|
4095
|
+
// that as a no-op would drop the very tombstone that stops a later
|
|
4096
|
+
// migration from resurrecting the session out of the earlier layout.
|
|
4097
|
+
canonical.stored && next.token === current.token && next.refreshToken === current.refreshToken
|
|
4098
|
+
) {
|
|
4099
|
+
this.sessionRev = current.rev;
|
|
4100
|
+
this.token = current.token;
|
|
4101
|
+
this.refreshTokenValue = current.refreshToken;
|
|
4102
|
+
return true;
|
|
4103
|
+
}
|
|
4104
|
+
try {
|
|
4105
|
+
localStorage.setItem(recordKey, JSON.stringify(next));
|
|
4106
|
+
} catch {
|
|
4107
|
+
this.disablePersistence();
|
|
4108
|
+
return true;
|
|
4109
|
+
}
|
|
4110
|
+
this.sessionRev = next.rev;
|
|
4111
|
+
this.token = next.token;
|
|
4112
|
+
this.refreshTokenValue = next.refreshToken;
|
|
4113
|
+
try {
|
|
4114
|
+
localStorage.removeItem(mirrorRefreshKey);
|
|
4115
|
+
} catch {
|
|
4116
|
+
return true;
|
|
4117
|
+
}
|
|
4118
|
+
try {
|
|
4119
|
+
if (next.token) localStorage.setItem(mirrorKey, next.token);
|
|
4120
|
+
else localStorage.removeItem(mirrorKey);
|
|
4121
|
+
} catch {
|
|
4122
|
+
}
|
|
4123
|
+
return true;
|
|
4124
|
+
}
|
|
4125
|
+
/**
|
|
4126
|
+
* Storage rejected a write or a read, so it is not a canonical store for this
|
|
4127
|
+
* instance. Fall back to the memory-only session and take no lease: there is
|
|
4128
|
+
* no shared state left to coordinate. Nothing is purged — the record commit
|
|
4129
|
+
* is atomic, so no inconsistent state was published, and another context's
|
|
4130
|
+
* valid session must not be destroyed by this instance's local failure.
|
|
4131
|
+
*/
|
|
4132
|
+
disablePersistence() {
|
|
4133
|
+
this.persistRecordKey = null;
|
|
4134
|
+
this.persistMirrorKey = null;
|
|
4135
|
+
this.persistMirrorRefreshKey = null;
|
|
4136
|
+
this.refreshLockName = null;
|
|
4137
|
+
}
|
|
4138
|
+
/**
|
|
4139
|
+
* Compare the canonical record with the fence this call captured before it
|
|
4140
|
+
* left. Every persisted decision — rotate, converge, or fail closed — goes
|
|
4141
|
+
* through this one comparison. The revision catches a logout, a fresh login,
|
|
4142
|
+
* or a manual setter even when token values coincide; the value comparison
|
|
4143
|
+
* additionally catches a mirror-only writer that cannot bump a revision.
|
|
4144
|
+
*/
|
|
4145
|
+
classifyCanonicalState(intended) {
|
|
4146
|
+
if (!this.persistRecordKey) return "current";
|
|
4147
|
+
this.syncFromPersistedStorage();
|
|
4148
|
+
if (!this.persistRecordKey) return "current";
|
|
4149
|
+
if (!this.token && !this.refreshTokenValue) return "cleared";
|
|
4150
|
+
if (intended.refreshToken !== null) {
|
|
4151
|
+
return this.refreshTokenValue === intended.refreshToken ? "current" : "superseded";
|
|
4152
|
+
}
|
|
4153
|
+
return this.token === intended.token && !this.refreshTokenValue ? "current" : "superseded";
|
|
4154
|
+
}
|
|
4155
|
+
/** The canonical persisted pair, as a refresh result. */
|
|
4156
|
+
canonicalPair(token) {
|
|
4157
|
+
return {
|
|
4158
|
+
token,
|
|
4159
|
+
...this.refreshTokenValue ? { refreshToken: this.refreshTokenValue } : {}
|
|
4160
|
+
};
|
|
4161
|
+
}
|
|
4162
|
+
/** Current credentials plus the revision they were observed at. */
|
|
4163
|
+
currentFence() {
|
|
4164
|
+
return { token: this.token, refreshToken: this.refreshTokenValue };
|
|
4165
|
+
}
|
|
4166
|
+
/**
|
|
4167
|
+
* Clear an access credential a 401 just proved unusable — but only while it
|
|
4168
|
+
* is still the canonical one. A rotation committed by another context while
|
|
4169
|
+
* the request was in flight is newer than this 401, so it must survive; the
|
|
4170
|
+
* instance adopts it instead. The refresh entry is never touched here: a
|
|
4171
|
+
* rejected access token says nothing about the refresh family.
|
|
4172
|
+
*/
|
|
4173
|
+
invalidateAccessToken(spentToken) {
|
|
4174
|
+
this.syncFromPersistedStorage();
|
|
4175
|
+
if (this.token !== spentToken) return;
|
|
4176
|
+
this.setToken(null);
|
|
4177
|
+
}
|
|
4178
|
+
/** Commit both credentials in one canonical write. */
|
|
4179
|
+
setSession(token, refreshToken, options) {
|
|
4180
|
+
this.token = token;
|
|
4181
|
+
this.refreshTokenValue = refreshToken;
|
|
4182
|
+
const committed = this.commitPersistedSession(
|
|
4183
|
+
{ token, refreshToken },
|
|
4184
|
+
options
|
|
4185
|
+
);
|
|
4186
|
+
if (!committed) return false;
|
|
4187
|
+
this.onTokenChange?.(this.token);
|
|
4188
|
+
this.onRefreshTokenChange?.(this.refreshTokenValue);
|
|
4189
|
+
return true;
|
|
4190
|
+
}
|
|
4191
|
+
/**
|
|
4192
|
+
* Serialize rotation against every other context sharing the same storage
|
|
4193
|
+
* key and API/tenant identity. Coordination is best-effort: when it is
|
|
4194
|
+
* unavailable or times out the rotation still happens, so server-side reuse
|
|
4195
|
+
* detection is never bypassed by a client that failed to coordinate.
|
|
4196
|
+
*/
|
|
4197
|
+
async rotateAcrossContexts(intended) {
|
|
4198
|
+
const lockName = this.refreshLockName;
|
|
4199
|
+
const requestLock = lockName ? getRefreshLockRequester() : null;
|
|
4200
|
+
if (!lockName || !requestLock) {
|
|
4201
|
+
return this.rotateWithCanonicalState(intended);
|
|
4202
|
+
}
|
|
4203
|
+
const controller = new AbortController();
|
|
4204
|
+
const timer = setTimeout(() => controller.abort(), REFRESH_LOCK_TIMEOUT);
|
|
4205
|
+
let entered = false;
|
|
4206
|
+
try {
|
|
4207
|
+
return await requestLock(
|
|
4208
|
+
lockName,
|
|
4209
|
+
{ signal: controller.signal },
|
|
4210
|
+
async () => {
|
|
4211
|
+
entered = true;
|
|
4212
|
+
return this.rotateWithCanonicalState(intended);
|
|
4213
|
+
}
|
|
4214
|
+
);
|
|
4215
|
+
} catch (error) {
|
|
4216
|
+
if (entered) throw error;
|
|
4217
|
+
return this.rotateWithCanonicalState(intended);
|
|
4218
|
+
} finally {
|
|
4219
|
+
clearTimeout(timer);
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
/**
|
|
4223
|
+
* Decide, against freshly re-read canonical state, whether this context still
|
|
4224
|
+
* owns the rotation.
|
|
4225
|
+
*/
|
|
4226
|
+
async rotateWithCanonicalState(intended) {
|
|
4227
|
+
let fence = intended;
|
|
4228
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
4229
|
+
const state = this.classifyCanonicalState(fence);
|
|
4230
|
+
if (state === "cleared") throw new ApiError("Not authenticated", 401);
|
|
4231
|
+
if (state === "current") return this._doRefreshToken(fence);
|
|
4232
|
+
if (this.token) return this.canonicalPair(this.token);
|
|
4233
|
+
fence = this.currentFence();
|
|
4234
|
+
}
|
|
4235
|
+
throw new ApiError("Not authenticated", 401);
|
|
4236
|
+
}
|
|
4237
|
+
async _doRefreshToken(intended) {
|
|
4238
|
+
const init = intended.refreshToken ? {
|
|
3808
4239
|
method: "POST",
|
|
3809
|
-
body: JSON.stringify({ refreshToken:
|
|
4240
|
+
body: JSON.stringify({ refreshToken: intended.refreshToken })
|
|
3810
4241
|
} : {
|
|
3811
4242
|
method: "POST",
|
|
3812
|
-
headers: { Authorization: `Bearer ${
|
|
4243
|
+
headers: { Authorization: `Bearer ${intended.token}` }
|
|
3813
4244
|
};
|
|
3814
4245
|
const result = await this.requestJson(
|
|
3815
4246
|
"/api/customers/refresh",
|
|
3816
4247
|
init
|
|
3817
4248
|
);
|
|
3818
|
-
this.
|
|
3819
|
-
if (
|
|
3820
|
-
|
|
4249
|
+
const state = this.classifyCanonicalState(intended);
|
|
4250
|
+
if (state === "cleared") throw new ApiError("Not authenticated", 401);
|
|
4251
|
+
if (state === "superseded") {
|
|
4252
|
+
if (this.token) return this.canonicalPair(this.token);
|
|
4253
|
+
throw new ApiError("Not authenticated", 401);
|
|
4254
|
+
}
|
|
4255
|
+
const replacedAccessToken = this.token !== null && this.token !== intended.token ? this.token : null;
|
|
4256
|
+
const nextToken = replacedAccessToken ?? result.token;
|
|
4257
|
+
const nextRefresh = result.refreshToken ?? null;
|
|
4258
|
+
if (!this.setSession(nextToken, nextRefresh, { requireLiveSession: true })) {
|
|
4259
|
+
throw new ApiError("Not authenticated", 401);
|
|
4260
|
+
}
|
|
4261
|
+
return {
|
|
4262
|
+
token: nextToken,
|
|
4263
|
+
...nextRefresh ? { refreshToken: nextRefresh } : {}
|
|
4264
|
+
};
|
|
3821
4265
|
}
|
|
3822
4266
|
/**
|
|
3823
|
-
* Clear
|
|
4267
|
+
* Clear local credentials synchronously. This compatibility API cannot
|
|
4268
|
+
* revoke the server-side refresh family; use logoutAsync() for that.
|
|
4269
|
+
* @deprecated Use logoutAsync() to end the direct server session.
|
|
3824
4270
|
*/
|
|
3825
4271
|
logout() {
|
|
3826
|
-
this.
|
|
3827
|
-
|
|
4272
|
+
this.setSession(null, null);
|
|
4273
|
+
}
|
|
4274
|
+
/**
|
|
4275
|
+
* End the current direct refresh family and clear local credentials even if
|
|
4276
|
+
* revocation fails. A legacy access-only session has no refresh family here.
|
|
4277
|
+
*/
|
|
4278
|
+
async logoutAsync() {
|
|
4279
|
+
this.syncFromPersistedStorage();
|
|
4280
|
+
const refreshToken = this.refreshTokenValue;
|
|
4281
|
+
this.logout();
|
|
4282
|
+
if (!refreshToken) return;
|
|
4283
|
+
const operation2 = CUSTOMER_AUTH_ENDPOINT_OPERATIONS.revokeRefresh;
|
|
4284
|
+
await this.requestJson(resolveOperationPath(operation2), {
|
|
4285
|
+
method: resolveOperationMethod(operation2),
|
|
4286
|
+
body: JSON.stringify({ refreshToken })
|
|
4287
|
+
});
|
|
4288
|
+
}
|
|
4289
|
+
/** Archive the current password account and clear this client's credentials on success. */
|
|
4290
|
+
async withdraw(currentPassword) {
|
|
4291
|
+
const token = this.getToken();
|
|
4292
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
4293
|
+
const operation2 = CUSTOMER_AUTH_ENDPOINT_OPERATIONS.withdraw;
|
|
4294
|
+
const result = await this.requestJson(
|
|
4295
|
+
resolveOperationPath(operation2),
|
|
4296
|
+
{
|
|
4297
|
+
method: resolveOperationMethod(operation2),
|
|
4298
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
4299
|
+
body: JSON.stringify({ currentPassword })
|
|
4300
|
+
}
|
|
4301
|
+
);
|
|
4302
|
+
if (!result || typeof result.withdrawnAt !== "string" || !Number.isFinite(Date.parse(result.withdrawnAt)))
|
|
4303
|
+
throw new ApiError("Invalid customer withdrawal response", 502);
|
|
4304
|
+
this.logout();
|
|
4305
|
+
return { withdrawnAt: result.withdrawnAt };
|
|
3828
4306
|
}
|
|
3829
4307
|
/**
|
|
3830
4308
|
* Get the current authenticated customer's profile
|
|
3831
4309
|
*/
|
|
3832
4310
|
async me() {
|
|
3833
|
-
|
|
4311
|
+
const token = this.getToken();
|
|
4312
|
+
if (!token) return null;
|
|
3834
4313
|
try {
|
|
3835
4314
|
const data = await this.requestJson(
|
|
3836
4315
|
"/api/customers/me",
|
|
3837
4316
|
{
|
|
3838
4317
|
method: "GET",
|
|
3839
|
-
headers: { Authorization: `Bearer ${
|
|
4318
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
3840
4319
|
}
|
|
3841
4320
|
);
|
|
3842
4321
|
return data.customer ?? null;
|
|
3843
4322
|
} catch (error) {
|
|
3844
4323
|
if (error instanceof SDKError && error.status === 401) {
|
|
3845
|
-
this.
|
|
4324
|
+
this.invalidateAccessToken(token);
|
|
3846
4325
|
return null;
|
|
3847
4326
|
}
|
|
3848
4327
|
throw error;
|
|
@@ -3870,12 +4349,13 @@ var CustomerAuth = class {
|
|
|
3870
4349
|
* Update the authenticated customer's profile (name, phone, marketingConsent)
|
|
3871
4350
|
*/
|
|
3872
4351
|
async updateProfile(data) {
|
|
3873
|
-
|
|
4352
|
+
const token = this.getToken();
|
|
4353
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
3874
4354
|
const result = await this.requestJson(
|
|
3875
4355
|
"/api/customers/me",
|
|
3876
4356
|
{
|
|
3877
4357
|
method: "PATCH",
|
|
3878
|
-
headers: { Authorization: `Bearer ${
|
|
4358
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
3879
4359
|
body: JSON.stringify(data)
|
|
3880
4360
|
}
|
|
3881
4361
|
);
|
|
@@ -3885,10 +4365,11 @@ var CustomerAuth = class {
|
|
|
3885
4365
|
* Change the password of the currently authenticated customer
|
|
3886
4366
|
*/
|
|
3887
4367
|
async changePassword(currentPassword, newPassword) {
|
|
3888
|
-
|
|
4368
|
+
const token = this.getToken();
|
|
4369
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
3889
4370
|
await this.requestJson("/api/customers/change-password", {
|
|
3890
4371
|
method: "POST",
|
|
3891
|
-
headers: { Authorization: `Bearer ${
|
|
4372
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
3892
4373
|
body: JSON.stringify({ currentPassword, newPassword })
|
|
3893
4374
|
});
|
|
3894
4375
|
}
|
|
@@ -3896,7 +4377,8 @@ var CustomerAuth = class {
|
|
|
3896
4377
|
* Get the authenticated customer's orders with pagination and optional status filter
|
|
3897
4378
|
*/
|
|
3898
4379
|
async getMyOrders(options) {
|
|
3899
|
-
|
|
4380
|
+
const token = this.getToken();
|
|
4381
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
3900
4382
|
const params = new URLSearchParams();
|
|
3901
4383
|
if (options?.page) params.set("page", String(options.page));
|
|
3902
4384
|
if (options?.limit) params.set("limit", String(options.limit));
|
|
@@ -3918,21 +4400,27 @@ var CustomerAuth = class {
|
|
|
3918
4400
|
`/api/customers/me/orders${qs ? `?${qs}` : ""}`,
|
|
3919
4401
|
{
|
|
3920
4402
|
method: "GET",
|
|
3921
|
-
headers: { Authorization: `Bearer ${
|
|
4403
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
3922
4404
|
}
|
|
3923
4405
|
);
|
|
3924
4406
|
return validatePublicOrderList(result);
|
|
3925
4407
|
}
|
|
3926
4408
|
/**
|
|
3927
|
-
* Get the current token (or null if not authenticated)
|
|
4409
|
+
* Get the current token (or null if not authenticated).
|
|
4410
|
+
*
|
|
4411
|
+
* In persisted mode this re-reads shared storage first, so a rotation or
|
|
4412
|
+
* logout performed by another tab is observed before the token is used.
|
|
3928
4413
|
*/
|
|
3929
4414
|
getToken() {
|
|
4415
|
+
this.syncFromPersistedStorage();
|
|
3930
4416
|
return this.token;
|
|
3931
4417
|
}
|
|
3932
4418
|
/**
|
|
3933
|
-
* Get the current refresh token (or null if unavailable)
|
|
4419
|
+
* Get the current refresh token (or null if unavailable). Persisted mode
|
|
4420
|
+
* re-reads shared storage first, like {@link CustomerAuth.getToken}.
|
|
3934
4421
|
*/
|
|
3935
4422
|
getRefreshToken() {
|
|
4423
|
+
this.syncFromPersistedStorage();
|
|
3936
4424
|
return this.refreshTokenValue;
|
|
3937
4425
|
}
|
|
3938
4426
|
/**
|
|
@@ -3940,6 +4428,7 @@ var CustomerAuth = class {
|
|
|
3940
4428
|
*/
|
|
3941
4429
|
setToken(token) {
|
|
3942
4430
|
this.token = token;
|
|
4431
|
+
this.commitPersistedSession({ token });
|
|
3943
4432
|
this.onTokenChange?.(token);
|
|
3944
4433
|
}
|
|
3945
4434
|
/**
|
|
@@ -3947,13 +4436,14 @@ var CustomerAuth = class {
|
|
|
3947
4436
|
*/
|
|
3948
4437
|
setRefreshToken(refreshToken) {
|
|
3949
4438
|
this.refreshTokenValue = refreshToken;
|
|
4439
|
+
this.commitPersistedSession({ refreshToken });
|
|
3950
4440
|
this.onRefreshTokenChange?.(refreshToken);
|
|
3951
4441
|
}
|
|
3952
4442
|
/**
|
|
3953
4443
|
* Check if the customer is currently authenticated
|
|
3954
4444
|
*/
|
|
3955
4445
|
isAuthenticated() {
|
|
3956
|
-
return this.
|
|
4446
|
+
return this.getToken() !== null;
|
|
3957
4447
|
}
|
|
3958
4448
|
/**
|
|
3959
4449
|
* Internal: make a request with timeout and error handling.
|
|
@@ -4299,8 +4789,12 @@ var PaymentSessionApi = class extends CustomerScopedApi {
|
|
|
4299
4789
|
...Object.keys(headers).length > 0 && { headers }
|
|
4300
4790
|
});
|
|
4301
4791
|
}
|
|
4302
|
-
async paymentSession(result, expectedProvider) {
|
|
4303
|
-
return validatePublicPaymentSession(
|
|
4792
|
+
async paymentSession(result, expectedProvider, operation2) {
|
|
4793
|
+
return validatePublicPaymentSession(
|
|
4794
|
+
await result,
|
|
4795
|
+
expectedProvider,
|
|
4796
|
+
operation2
|
|
4797
|
+
);
|
|
4304
4798
|
}
|
|
4305
4799
|
create(params) {
|
|
4306
4800
|
const { idempotencyKey, ...body } = params;
|
|
@@ -4347,7 +4841,8 @@ var PaymentSessionApi = class extends CustomerScopedApi {
|
|
|
4347
4841
|
const endpoint = this.serverTransport ? COMMERCE_ENDPOINTS.paymentSessions.server.cancel : COMMERCE_ENDPOINTS.paymentSessions.cancel;
|
|
4348
4842
|
return this.paymentSession(
|
|
4349
4843
|
this.post(endpoint, body, idempotencyKey),
|
|
4350
|
-
"toss"
|
|
4844
|
+
"toss",
|
|
4845
|
+
"cancel"
|
|
4351
4846
|
);
|
|
4352
4847
|
}
|
|
4353
4848
|
cancelPortOne(params) {
|
|
@@ -4356,7 +4851,8 @@ var PaymentSessionApi = class extends CustomerScopedApi {
|
|
|
4356
4851
|
const endpoint = this.serverTransport ? COMMERCE_ENDPOINTS.paymentSessions.server.cancel : COMMERCE_ENDPOINTS.paymentSessions.cancel;
|
|
4357
4852
|
return this.paymentSession(
|
|
4358
4853
|
this.post(endpoint, body, idempotencyKey),
|
|
4359
|
-
"portone"
|
|
4854
|
+
"portone",
|
|
4855
|
+
"cancel"
|
|
4360
4856
|
);
|
|
4361
4857
|
}
|
|
4362
4858
|
confirm(params) {
|