@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.cjs
CHANGED
|
@@ -1048,6 +1048,49 @@ function stockSnapshotSearchParams(params) {
|
|
|
1048
1048
|
}
|
|
1049
1049
|
|
|
1050
1050
|
// src/core/collection/http-client.ts
|
|
1051
|
+
function validateWhere(where) {
|
|
1052
|
+
if (where === void 0) return false;
|
|
1053
|
+
let hasNull = false;
|
|
1054
|
+
function visit(value, depth, key) {
|
|
1055
|
+
if (depth > 10) {
|
|
1056
|
+
throw createValidationError(
|
|
1057
|
+
"Collection where must not exceed nesting depth 10."
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
if (value === null && (key === "equals" || key === "not_equals")) {
|
|
1061
|
+
hasNull = true;
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
if (value == null) {
|
|
1065
|
+
throw createValidationError(
|
|
1066
|
+
"Collection where must not contain null or undefined values."
|
|
1067
|
+
);
|
|
1068
|
+
}
|
|
1069
|
+
if (Array.isArray(value)) {
|
|
1070
|
+
if (value.length === 0) {
|
|
1071
|
+
throw createValidationError(
|
|
1072
|
+
"Collection where must not contain empty arrays, including and/or."
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
if (value.length > 1e3) {
|
|
1076
|
+
throw createValidationError(
|
|
1077
|
+
"Collection where arrays must not contain more than 1000 elements."
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
for (const item of value) visit(item, depth + 1);
|
|
1081
|
+
} else if (typeof value === "object" && !(value instanceof Date)) {
|
|
1082
|
+
const values = Object.entries(value);
|
|
1083
|
+
if (values.length === 0) {
|
|
1084
|
+
throw createValidationError(
|
|
1085
|
+
"Collection where must not contain empty condition objects."
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
for (const [key2, item] of values) visit(item, depth + 1, key2);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
visit(where, 0);
|
|
1092
|
+
return hasNull;
|
|
1093
|
+
}
|
|
1051
1094
|
var HttpClient = class {
|
|
1052
1095
|
constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId, apiUrl) {
|
|
1053
1096
|
this.publishableKey = requirePublishableKeyForSecret(
|
|
@@ -1094,7 +1137,8 @@ var HttpClient = class {
|
|
|
1094
1137
|
...rest,
|
|
1095
1138
|
"fallback-locale": fallbackLocale === false ? "none" : fallbackLocale
|
|
1096
1139
|
};
|
|
1097
|
-
const
|
|
1140
|
+
const encodedOptions = validateWhere(queryOptions.where) ? { ...queryOptions, where: JSON.stringify(queryOptions.where) } : queryOptions;
|
|
1141
|
+
const queryString = (0, import_qs_esm.stringify)(encodedOptions, { addQueryPrefix: true });
|
|
1098
1142
|
return queryString ? `${endpoint}${queryString}` : endpoint;
|
|
1099
1143
|
}
|
|
1100
1144
|
assertJsonResponse(response) {
|
|
@@ -1396,6 +1440,11 @@ var COMMERCE_ENDPOINT_OPERATIONS = {
|
|
|
1396
1440
|
detail: strictCommerceOperation("checkouts", "post", "/detail"),
|
|
1397
1441
|
query: strictCommerceOperation("checkouts", "post", "/query")
|
|
1398
1442
|
},
|
|
1443
|
+
draftOrders: {
|
|
1444
|
+
calculate: operation("draft-orders", "post", "/:id/calculate"),
|
|
1445
|
+
complete: operation("draft-orders", "post", "/:id/complete"),
|
|
1446
|
+
finalize: operation("draft-orders", "post", "/:id/finalize")
|
|
1447
|
+
},
|
|
1399
1448
|
transactions: {
|
|
1400
1449
|
update: operation("transactions", "post", "/update")
|
|
1401
1450
|
},
|
|
@@ -1495,8 +1544,51 @@ var portOneStandardActionCapabilityOperations = /* @__PURE__ */ new Set([
|
|
|
1495
1544
|
]);
|
|
1496
1545
|
var MEMBERSHIP_ENDPOINT_OPERATIONS = {
|
|
1497
1546
|
subscribe: operation(null, "post", "/memberships/subscribe"),
|
|
1547
|
+
billingCustody: operation(null, "post", "/memberships/billing-custody"),
|
|
1498
1548
|
cancel: operation(null, "post", "/memberships/:id/cancel"),
|
|
1499
|
-
me: operation(null, "get", "/memberships/me")
|
|
1549
|
+
me: operation(null, "get", "/memberships/me"),
|
|
1550
|
+
chargeResult: operation(null, "post", "/memberships/:id/charge-result"),
|
|
1551
|
+
refundResult: operation(null, "post", "/memberships/:id/refund-result"),
|
|
1552
|
+
revoke: operation(null, "post", "/memberships/:id/revoke")
|
|
1553
|
+
};
|
|
1554
|
+
var EXTERNAL_EFFECT_CLAIM_ENDPOINT_OPERATIONS = {
|
|
1555
|
+
claim: operation(null, "post", "/external-effect-claims/claim"),
|
|
1556
|
+
get: operation(null, "post", "/external-effect-claims/get"),
|
|
1557
|
+
arm: operation(null, "post", "/external-effect-claims/arm"),
|
|
1558
|
+
release: operation(null, "post", "/external-effect-claims/release"),
|
|
1559
|
+
report: operation(null, "post", "/external-effect-claims/report"),
|
|
1560
|
+
recovery: operation(null, "post", "/external-effect-claims/recovery")
|
|
1561
|
+
};
|
|
1562
|
+
var INSTALLATION_CREDENTIAL_ENDPOINT_OPERATIONS = {
|
|
1563
|
+
create: operation(null, "post", "/installation-credentials/create"),
|
|
1564
|
+
get: operation(null, "post", "/installation-credentials/get"),
|
|
1565
|
+
replace: operation(null, "post", "/installation-credentials/replace"),
|
|
1566
|
+
dispose: operation(null, "post", "/installation-credentials/dispose")
|
|
1567
|
+
};
|
|
1568
|
+
var EXTERNAL_CUSTOMER_LINK_ENDPOINT_OPERATIONS = {
|
|
1569
|
+
connect: operation("customers", "post", "/external-links/connect"),
|
|
1570
|
+
get: operation("customers", "post", "/external-links/get"),
|
|
1571
|
+
list: operation("customers", "post", "/external-links/list"),
|
|
1572
|
+
unlink: operation("customers", "post", "/external-links/unlink"),
|
|
1573
|
+
capturePreviousState: operation(
|
|
1574
|
+
"customers",
|
|
1575
|
+
"post",
|
|
1576
|
+
"/external-links/capture-previous-state"
|
|
1577
|
+
),
|
|
1578
|
+
clearPreviousState: operation(
|
|
1579
|
+
"customers",
|
|
1580
|
+
"post",
|
|
1581
|
+
"/external-links/clear-previous-state"
|
|
1582
|
+
)
|
|
1583
|
+
};
|
|
1584
|
+
var CUSTOMER_GROUP_ENDPOINT_OPERATIONS = {
|
|
1585
|
+
members: operation("customer-groups", "get", "/:id/members"),
|
|
1586
|
+
grant: operation("customer-groups", "post", "/:id/members/grant"),
|
|
1587
|
+
revoke: operation("customer-groups", "post", "/:id/members/revoke")
|
|
1588
|
+
};
|
|
1589
|
+
var CUSTOMER_AUTH_ENDPOINT_OPERATIONS = {
|
|
1590
|
+
revokeRefresh: operation("customers", "post", "/revoke-refresh"),
|
|
1591
|
+
withdraw: operation("customers", "post", "/withdraw")
|
|
1500
1592
|
};
|
|
1501
1593
|
var EMBEDDED_ADMIN_ENDPOINT_OPERATIONS = {
|
|
1502
1594
|
show: operation("tenants", "get", "/embedded-admin-app"),
|
|
@@ -1521,6 +1613,14 @@ var COMMUNITY_ENDPOINT_OPERATIONS = {
|
|
|
1521
1613
|
detail: operation("posts", "get", "/detail")
|
|
1522
1614
|
}
|
|
1523
1615
|
};
|
|
1616
|
+
var GALLERY_ENDPOINT_OPERATIONS = {
|
|
1617
|
+
items: {
|
|
1618
|
+
storefront: operation("gallery-items", "get", "/storefront")
|
|
1619
|
+
}
|
|
1620
|
+
};
|
|
1621
|
+
var VIDEO_ENDPOINT_OPERATIONS = {
|
|
1622
|
+
playback: operation("videos", "post", "/:id/playback")
|
|
1623
|
+
};
|
|
1524
1624
|
function resolveOperationTemplate(descriptor) {
|
|
1525
1625
|
return descriptor.collection ? `/api/${descriptor.collection}${descriptor.path}` : `/api${descriptor.path}`;
|
|
1526
1626
|
}
|
|
@@ -2110,6 +2210,15 @@ var PublicContentApi = class extends RawCustomerScopedApi {
|
|
|
2110
2210
|
get(endpoint) {
|
|
2111
2211
|
return this.request(endpoint, { method: "GET" });
|
|
2112
2212
|
}
|
|
2213
|
+
galleryItems(options) {
|
|
2214
|
+
const operation2 = GALLERY_ENDPOINT_OPERATIONS.items.storefront;
|
|
2215
|
+
return this.request(
|
|
2216
|
+
resolveOperationPath(operation2, {
|
|
2217
|
+
query: galleryItemsListQuery(options)
|
|
2218
|
+
}),
|
|
2219
|
+
{ method: resolveOperationMethod(operation2) }
|
|
2220
|
+
);
|
|
2221
|
+
}
|
|
2113
2222
|
};
|
|
2114
2223
|
function appendCommonParams(search, options, defaultSort) {
|
|
2115
2224
|
search.set("sort", options?.sort ?? defaultSort);
|
|
@@ -2129,12 +2238,12 @@ function linksListPath(options) {
|
|
|
2129
2238
|
search.set("featured", String(options.featured));
|
|
2130
2239
|
return `/api/links/storefront?${search}`;
|
|
2131
2240
|
}
|
|
2132
|
-
function
|
|
2241
|
+
function galleryItemsListQuery(options) {
|
|
2133
2242
|
const search = new URLSearchParams();
|
|
2134
2243
|
appendCommonParams(search, options, "manual");
|
|
2135
2244
|
if (options?.galleryId) search.set("galleryId", options.galleryId);
|
|
2136
2245
|
if (options?.gallerySlug) search.set("gallerySlug", options.gallerySlug);
|
|
2137
|
-
return
|
|
2246
|
+
return search;
|
|
2138
2247
|
}
|
|
2139
2248
|
var ContentClient = class {
|
|
2140
2249
|
constructor(options) {
|
|
@@ -2148,9 +2257,7 @@ var ContentClient = class {
|
|
|
2148
2257
|
list: (params) => api.get(linksListPath(params))
|
|
2149
2258
|
};
|
|
2150
2259
|
this.galleryItems = {
|
|
2151
|
-
list: (params) => api.
|
|
2152
|
-
galleryItemsListPath(params)
|
|
2153
|
-
)
|
|
2260
|
+
list: (params) => api.galleryItems(params)
|
|
2154
2261
|
};
|
|
2155
2262
|
}
|
|
2156
2263
|
};
|
|
@@ -2206,7 +2313,10 @@ var ORDER_PRIMARY_DISPLAY_STATUSES = [
|
|
|
2206
2313
|
];
|
|
2207
2314
|
var CHECKOUT_STATUSES = ["open", "placed", "abandoned"];
|
|
2208
2315
|
var ABANDONED_REASONS = ["expired", "customer", "operator"];
|
|
2209
|
-
|
|
2316
|
+
function managedPaymentProviders(providers) {
|
|
2317
|
+
return providers;
|
|
2318
|
+
}
|
|
2319
|
+
var PAYMENT_PROVIDERS = managedPaymentProviders(["toss", "portone"]);
|
|
2210
2320
|
var PAYMENT_PROVIDER_STATUSES = [
|
|
2211
2321
|
"requires_payment_method",
|
|
2212
2322
|
"requires_action",
|
|
@@ -2254,6 +2364,26 @@ function invalidCanonicalResponse(resource, details) {
|
|
|
2254
2364
|
function fail(resource, path, expected) {
|
|
2255
2365
|
invalidCanonicalResponse(resource, { path, expected });
|
|
2256
2366
|
}
|
|
2367
|
+
function providerMismatch(resource, expected, actual, operation2) {
|
|
2368
|
+
if (operation2 === "cancel") {
|
|
2369
|
+
const method = {
|
|
2370
|
+
toss: "cancel()",
|
|
2371
|
+
portone: "cancelPortOne()"
|
|
2372
|
+
}[actual];
|
|
2373
|
+
throw createApiError(
|
|
2374
|
+
`Canonical ${resource} response provider mismatch: expected ${expected}, received ${actual}.`,
|
|
2375
|
+
502,
|
|
2376
|
+
{ resource, path: "$.provider", expected, actual },
|
|
2377
|
+
`The server applied the cancellation to a ${actual} PaymentSession, but this SDK method expects ${expected}.`,
|
|
2378
|
+
`Re-invoke ${method} with the same idempotencyKey; do not create a new key.`
|
|
2379
|
+
);
|
|
2380
|
+
}
|
|
2381
|
+
invalidCanonicalResponse(resource, {
|
|
2382
|
+
path: "$.provider",
|
|
2383
|
+
expected,
|
|
2384
|
+
actual
|
|
2385
|
+
});
|
|
2386
|
+
}
|
|
2257
2387
|
function record(value, resource, path) {
|
|
2258
2388
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2259
2389
|
fail(resource, path, "object");
|
|
@@ -3546,7 +3676,7 @@ function validateNextAction(value, provider, resource, path) {
|
|
|
3546
3676
|
if (action.customerEmail !== void 0)
|
|
3547
3677
|
validateCustomer({ email: action.customerEmail }, resource, path);
|
|
3548
3678
|
}
|
|
3549
|
-
function validatePublicPaymentSession(value, expectedProvider) {
|
|
3679
|
+
function validatePublicPaymentSession(value, expectedProvider, operation2) {
|
|
3550
3680
|
const resource = "PaymentSession";
|
|
3551
3681
|
const session = record(value, resource, "$");
|
|
3552
3682
|
const provider = enumValue(
|
|
@@ -3555,9 +3685,6 @@ function validatePublicPaymentSession(value, expectedProvider) {
|
|
|
3555
3685
|
resource,
|
|
3556
3686
|
"$.provider"
|
|
3557
3687
|
);
|
|
3558
|
-
if (expectedProvider !== void 0 && provider !== expectedProvider) {
|
|
3559
|
-
fail(resource, "$.provider", expectedProvider);
|
|
3560
|
-
}
|
|
3561
3688
|
const status = enumValue(
|
|
3562
3689
|
session.providerStatus,
|
|
3563
3690
|
PAYMENT_PROVIDER_STATUSES,
|
|
@@ -3761,44 +3888,128 @@ function validatePublicPaymentSession(value, expectedProvider) {
|
|
|
3761
3888
|
"$.cancellationReason"
|
|
3762
3889
|
);
|
|
3763
3890
|
}
|
|
3891
|
+
if (expectedProvider !== void 0 && provider !== expectedProvider) {
|
|
3892
|
+
if (operation2 === "cancel" && status !== "canceling" && status !== "canceled") {
|
|
3893
|
+
fail(
|
|
3894
|
+
resource,
|
|
3895
|
+
"$.providerStatus",
|
|
3896
|
+
"canceling or canceled after a successful cancellation"
|
|
3897
|
+
);
|
|
3898
|
+
}
|
|
3899
|
+
providerMismatch(resource, expectedProvider, provider, operation2);
|
|
3900
|
+
}
|
|
3764
3901
|
return value;
|
|
3765
3902
|
}
|
|
3766
3903
|
|
|
3767
3904
|
// src/core/customer/customer-auth.ts
|
|
3768
3905
|
var DEFAULT_TIMEOUT2 = 15e3;
|
|
3769
|
-
|
|
3906
|
+
var REFRESH_LOCK_TIMEOUT = DEFAULT_TIMEOUT2 * 3;
|
|
3907
|
+
var SESSION_RECORD_VERSION = 1;
|
|
3908
|
+
var STORAGE_UNUSABLE = /* @__PURE__ */ Symbol("storage-unusable");
|
|
3909
|
+
function canonicalApiIdentity(apiUrl) {
|
|
3770
3910
|
try {
|
|
3771
|
-
|
|
3911
|
+
const url = new URL(apiUrl);
|
|
3912
|
+
return `${url.protocol.toLowerCase()}//${url.host.toLowerCase()}${url.pathname.replace(/\/+$/, "")}`;
|
|
3913
|
+
} catch {
|
|
3914
|
+
return apiUrl.trim().toLowerCase().replace(/\/+$/, "");
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
function buildRefreshLockName(apiUrl, publishableKey, storageKey) {
|
|
3918
|
+
return `01software:customer-auth-refresh:${canonicalApiIdentity(apiUrl)}:${publishableKey}:${storageKey}`;
|
|
3919
|
+
}
|
|
3920
|
+
function getRefreshLockRequester() {
|
|
3921
|
+
try {
|
|
3922
|
+
if (typeof navigator === "undefined") return null;
|
|
3923
|
+
const locks = navigator.locks;
|
|
3924
|
+
if (!locks || typeof locks.request !== "function") return null;
|
|
3925
|
+
return (name, options, callback) => locks.request(
|
|
3926
|
+
name,
|
|
3927
|
+
options,
|
|
3928
|
+
() => callback()
|
|
3929
|
+
);
|
|
3772
3930
|
} catch {
|
|
3773
3931
|
return null;
|
|
3774
3932
|
}
|
|
3775
3933
|
}
|
|
3934
|
+
function parseSessionRecord(raw) {
|
|
3935
|
+
try {
|
|
3936
|
+
const parsed = JSON.parse(raw);
|
|
3937
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
3938
|
+
const candidate = parsed;
|
|
3939
|
+
if (candidate.v !== SESSION_RECORD_VERSION) return null;
|
|
3940
|
+
if (typeof candidate.rev !== "number" || !Number.isFinite(candidate.rev)) {
|
|
3941
|
+
return null;
|
|
3942
|
+
}
|
|
3943
|
+
const token = candidate.token;
|
|
3944
|
+
const refreshToken = candidate.refreshToken;
|
|
3945
|
+
if (token !== null && typeof token !== "string") return null;
|
|
3946
|
+
if (refreshToken !== null && typeof refreshToken !== "string") return null;
|
|
3947
|
+
return {
|
|
3948
|
+
v: SESSION_RECORD_VERSION,
|
|
3949
|
+
rev: candidate.rev,
|
|
3950
|
+
token,
|
|
3951
|
+
refreshToken
|
|
3952
|
+
};
|
|
3953
|
+
} catch {
|
|
3954
|
+
return null;
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
function readSessionRecord(recordKey, mirrorKey, mirrorRefreshKey) {
|
|
3958
|
+
let raw;
|
|
3959
|
+
try {
|
|
3960
|
+
raw = localStorage.getItem(recordKey);
|
|
3961
|
+
} catch {
|
|
3962
|
+
return STORAGE_UNUSABLE;
|
|
3963
|
+
}
|
|
3964
|
+
if (raw !== null) {
|
|
3965
|
+
const parsed = parseSessionRecord(raw);
|
|
3966
|
+
return parsed ? { record: parsed, stored: true } : STORAGE_UNUSABLE;
|
|
3967
|
+
}
|
|
3968
|
+
try {
|
|
3969
|
+
return {
|
|
3970
|
+
record: {
|
|
3971
|
+
v: SESSION_RECORD_VERSION,
|
|
3972
|
+
rev: 0,
|
|
3973
|
+
token: localStorage.getItem(mirrorKey),
|
|
3974
|
+
refreshToken: localStorage.getItem(mirrorRefreshKey)
|
|
3975
|
+
},
|
|
3976
|
+
stored: false
|
|
3977
|
+
};
|
|
3978
|
+
} catch {
|
|
3979
|
+
return STORAGE_UNUSABLE;
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3776
3982
|
var CustomerAuth = class {
|
|
3777
3983
|
constructor(publishableKey, options, apiUrl) {
|
|
3778
3984
|
this.refreshPromise = null;
|
|
3985
|
+
/** Canonical record key; null unless persistence is active in a browser. */
|
|
3986
|
+
this.persistRecordKey = null;
|
|
3987
|
+
/** Advisory single-value access mirror, plus the retired refresh mirror. */
|
|
3988
|
+
this.persistMirrorKey = null;
|
|
3989
|
+
this.persistMirrorRefreshKey = null;
|
|
3990
|
+
/** Cross-context refresh lock name; null when there is no shared storage. */
|
|
3991
|
+
this.refreshLockName = null;
|
|
3992
|
+
/** Revision of the canonical record this instance last observed. */
|
|
3993
|
+
this.sessionRev = 0;
|
|
3779
3994
|
this.publishableKey = publishableKey;
|
|
3780
3995
|
this.baseUrl = resolveApiUrl(apiUrl);
|
|
3781
3996
|
const persist = options?.persist ?? true;
|
|
3782
3997
|
if (persist) {
|
|
3783
3998
|
const key = typeof persist === "string" ? persist : "customer-token";
|
|
3784
|
-
const refreshKey = `${key}:refresh`;
|
|
3785
3999
|
const isBrowser = typeof window !== "undefined";
|
|
3786
|
-
this.token =
|
|
3787
|
-
this.refreshTokenValue =
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
} catch {
|
|
3800
|
-
}
|
|
3801
|
-
} : void 0;
|
|
4000
|
+
this.token = null;
|
|
4001
|
+
this.refreshTokenValue = null;
|
|
4002
|
+
if (isBrowser) {
|
|
4003
|
+
this.persistRecordKey = `${key}:session`;
|
|
4004
|
+
this.persistMirrorKey = key;
|
|
4005
|
+
this.persistMirrorRefreshKey = `${key}:refresh`;
|
|
4006
|
+
this.refreshLockName = buildRefreshLockName(
|
|
4007
|
+
this.baseUrl,
|
|
4008
|
+
publishableKey,
|
|
4009
|
+
key
|
|
4010
|
+
);
|
|
4011
|
+
this.syncFromPersistedStorage();
|
|
4012
|
+
}
|
|
3802
4013
|
} else {
|
|
3803
4014
|
this.token = options?.token ?? null;
|
|
3804
4015
|
this.refreshTokenValue = options?.refreshToken ?? null;
|
|
@@ -3826,66 +4037,334 @@ var CustomerAuth = class {
|
|
|
3826
4037
|
body: JSON.stringify(data)
|
|
3827
4038
|
}
|
|
3828
4039
|
);
|
|
3829
|
-
this.
|
|
3830
|
-
|
|
4040
|
+
if (result.refreshToken) this.setSession(result.token, result.refreshToken);
|
|
4041
|
+
else this.setToken(result.token);
|
|
3831
4042
|
return result;
|
|
3832
4043
|
}
|
|
3833
4044
|
/**
|
|
3834
4045
|
* Refresh the current token. Uses a refresh token when available, otherwise
|
|
3835
4046
|
* falls back to the legacy bearer-token compatibility path.
|
|
4047
|
+
*
|
|
4048
|
+
* Refresh tokens are single-use, so a persisted session shared by several
|
|
4049
|
+
* tabs or SDK instances must rotate at most once: concurrent rotations of the
|
|
4050
|
+
* same token are replay from the server's point of view and revoke the whole
|
|
4051
|
+
* family. Persisted browser sessions therefore serialize rotation across
|
|
4052
|
+
* contexts and re-read the canonical persisted credentials inside the lock.
|
|
3836
4053
|
*/
|
|
3837
4054
|
async refreshToken() {
|
|
4055
|
+
this.syncFromPersistedStorage();
|
|
3838
4056
|
if (!this.refreshTokenValue && !this.token) {
|
|
3839
4057
|
throw new ApiError("Not authenticated", 401);
|
|
3840
4058
|
}
|
|
3841
4059
|
if (this.refreshPromise) return this.refreshPromise;
|
|
3842
|
-
|
|
4060
|
+
const intended = this.currentFence();
|
|
4061
|
+
this.refreshPromise = this.rotateAcrossContexts(intended);
|
|
3843
4062
|
try {
|
|
3844
4063
|
return await this.refreshPromise;
|
|
3845
4064
|
} finally {
|
|
3846
4065
|
this.refreshPromise = null;
|
|
3847
4066
|
}
|
|
3848
4067
|
}
|
|
3849
|
-
|
|
3850
|
-
|
|
4068
|
+
/**
|
|
4069
|
+
* Re-read the canonical persisted session. In persisted mode the shared
|
|
4070
|
+
* record — not this instance's memory — is the source of truth, so another
|
|
4071
|
+
* context's rotation, login, or logout is observed before the next credential
|
|
4072
|
+
* use. Change callbacks are deliberately not invoked: this is a read.
|
|
4073
|
+
*/
|
|
4074
|
+
syncFromPersistedStorage() {
|
|
4075
|
+
const canonical = this.readCanonicalSession();
|
|
4076
|
+
if (canonical === STORAGE_UNUSABLE) {
|
|
4077
|
+
this.disablePersistence();
|
|
4078
|
+
return;
|
|
4079
|
+
}
|
|
4080
|
+
if (!canonical) return;
|
|
4081
|
+
this.sessionRev = canonical.record.rev;
|
|
4082
|
+
this.token = canonical.record.token;
|
|
4083
|
+
this.refreshTokenValue = canonical.record.refreshToken;
|
|
4084
|
+
}
|
|
4085
|
+
/** `null` when this instance does not persist. */
|
|
4086
|
+
readCanonicalSession() {
|
|
4087
|
+
if (!this.persistRecordKey || !this.persistMirrorKey || !this.persistMirrorRefreshKey) {
|
|
4088
|
+
return null;
|
|
4089
|
+
}
|
|
4090
|
+
return readSessionRecord(
|
|
4091
|
+
this.persistRecordKey,
|
|
4092
|
+
this.persistMirrorKey,
|
|
4093
|
+
this.persistMirrorRefreshKey
|
|
4094
|
+
);
|
|
4095
|
+
}
|
|
4096
|
+
/**
|
|
4097
|
+
* Publish a change to canonical storage.
|
|
4098
|
+
*
|
|
4099
|
+
* The canonical pair is re-read immediately before the single record write so
|
|
4100
|
+
* that a field this call does not change is taken from that read rather than
|
|
4101
|
+
* from this instance's possibly stale copy. That merge is BEST-EFFORT: it
|
|
4102
|
+
* removes the stale counterpart an instance would otherwise carry, but it is
|
|
4103
|
+
* not a compare-and-swap. Web Storage offers none, and the synchronous public
|
|
4104
|
+
* mutators cannot await the refresh lease, so a commit that lands between
|
|
4105
|
+
* this read and the store operation is lost by ordering — including the
|
|
4106
|
+
* counterpart just read, which is then republished stale. The window is one
|
|
4107
|
+
* store operation wide and the server remains authoritative; see
|
|
4108
|
+
* `CUST-AUTH-033` for the stated limit.
|
|
4109
|
+
*
|
|
4110
|
+
* Returns `false` only when `requireLiveSession` refused a write because the
|
|
4111
|
+
* canonical session had already been cleared.
|
|
4112
|
+
*/
|
|
4113
|
+
commitPersistedSession(change, options) {
|
|
4114
|
+
const recordKey = this.persistRecordKey;
|
|
4115
|
+
const mirrorKey = this.persistMirrorKey;
|
|
4116
|
+
const mirrorRefreshKey = this.persistMirrorRefreshKey;
|
|
4117
|
+
const canonical = this.readCanonicalSession();
|
|
4118
|
+
if (canonical === null || !recordKey || !mirrorKey || !mirrorRefreshKey) {
|
|
4119
|
+
return true;
|
|
4120
|
+
}
|
|
4121
|
+
if (canonical === STORAGE_UNUSABLE) {
|
|
4122
|
+
this.disablePersistence();
|
|
4123
|
+
return true;
|
|
4124
|
+
}
|
|
4125
|
+
const current = canonical.record;
|
|
4126
|
+
if (options?.requireLiveSession && !current.token && !current.refreshToken) {
|
|
4127
|
+
return false;
|
|
4128
|
+
}
|
|
4129
|
+
const next = {
|
|
4130
|
+
v: SESSION_RECORD_VERSION,
|
|
4131
|
+
rev: current.rev + 1,
|
|
4132
|
+
token: "token" in change ? change.token ?? null : current.token,
|
|
4133
|
+
refreshToken: "refreshToken" in change ? change.refreshToken ?? null : current.refreshToken
|
|
4134
|
+
};
|
|
4135
|
+
if (
|
|
4136
|
+
// Only a state that is genuinely persisted can already say what this
|
|
4137
|
+
// write would say. An absent record reads as an empty pair, and treating
|
|
4138
|
+
// that as a no-op would drop the very tombstone that stops a later
|
|
4139
|
+
// migration from resurrecting the session out of the earlier layout.
|
|
4140
|
+
canonical.stored && next.token === current.token && next.refreshToken === current.refreshToken
|
|
4141
|
+
) {
|
|
4142
|
+
this.sessionRev = current.rev;
|
|
4143
|
+
this.token = current.token;
|
|
4144
|
+
this.refreshTokenValue = current.refreshToken;
|
|
4145
|
+
return true;
|
|
4146
|
+
}
|
|
4147
|
+
try {
|
|
4148
|
+
localStorage.setItem(recordKey, JSON.stringify(next));
|
|
4149
|
+
} catch {
|
|
4150
|
+
this.disablePersistence();
|
|
4151
|
+
return true;
|
|
4152
|
+
}
|
|
4153
|
+
this.sessionRev = next.rev;
|
|
4154
|
+
this.token = next.token;
|
|
4155
|
+
this.refreshTokenValue = next.refreshToken;
|
|
4156
|
+
try {
|
|
4157
|
+
localStorage.removeItem(mirrorRefreshKey);
|
|
4158
|
+
} catch {
|
|
4159
|
+
return true;
|
|
4160
|
+
}
|
|
4161
|
+
try {
|
|
4162
|
+
if (next.token) localStorage.setItem(mirrorKey, next.token);
|
|
4163
|
+
else localStorage.removeItem(mirrorKey);
|
|
4164
|
+
} catch {
|
|
4165
|
+
}
|
|
4166
|
+
return true;
|
|
4167
|
+
}
|
|
4168
|
+
/**
|
|
4169
|
+
* Storage rejected a write or a read, so it is not a canonical store for this
|
|
4170
|
+
* instance. Fall back to the memory-only session and take no lease: there is
|
|
4171
|
+
* no shared state left to coordinate. Nothing is purged — the record commit
|
|
4172
|
+
* is atomic, so no inconsistent state was published, and another context's
|
|
4173
|
+
* valid session must not be destroyed by this instance's local failure.
|
|
4174
|
+
*/
|
|
4175
|
+
disablePersistence() {
|
|
4176
|
+
this.persistRecordKey = null;
|
|
4177
|
+
this.persistMirrorKey = null;
|
|
4178
|
+
this.persistMirrorRefreshKey = null;
|
|
4179
|
+
this.refreshLockName = null;
|
|
4180
|
+
}
|
|
4181
|
+
/**
|
|
4182
|
+
* Compare the canonical record with the fence this call captured before it
|
|
4183
|
+
* left. Every persisted decision — rotate, converge, or fail closed — goes
|
|
4184
|
+
* through this one comparison. The revision catches a logout, a fresh login,
|
|
4185
|
+
* or a manual setter even when token values coincide; the value comparison
|
|
4186
|
+
* additionally catches a mirror-only writer that cannot bump a revision.
|
|
4187
|
+
*/
|
|
4188
|
+
classifyCanonicalState(intended) {
|
|
4189
|
+
if (!this.persistRecordKey) return "current";
|
|
4190
|
+
this.syncFromPersistedStorage();
|
|
4191
|
+
if (!this.persistRecordKey) return "current";
|
|
4192
|
+
if (!this.token && !this.refreshTokenValue) return "cleared";
|
|
4193
|
+
if (intended.refreshToken !== null) {
|
|
4194
|
+
return this.refreshTokenValue === intended.refreshToken ? "current" : "superseded";
|
|
4195
|
+
}
|
|
4196
|
+
return this.token === intended.token && !this.refreshTokenValue ? "current" : "superseded";
|
|
4197
|
+
}
|
|
4198
|
+
/** The canonical persisted pair, as a refresh result. */
|
|
4199
|
+
canonicalPair(token) {
|
|
4200
|
+
return {
|
|
4201
|
+
token,
|
|
4202
|
+
...this.refreshTokenValue ? { refreshToken: this.refreshTokenValue } : {}
|
|
4203
|
+
};
|
|
4204
|
+
}
|
|
4205
|
+
/** Current credentials plus the revision they were observed at. */
|
|
4206
|
+
currentFence() {
|
|
4207
|
+
return { token: this.token, refreshToken: this.refreshTokenValue };
|
|
4208
|
+
}
|
|
4209
|
+
/**
|
|
4210
|
+
* Clear an access credential a 401 just proved unusable — but only while it
|
|
4211
|
+
* is still the canonical one. A rotation committed by another context while
|
|
4212
|
+
* the request was in flight is newer than this 401, so it must survive; the
|
|
4213
|
+
* instance adopts it instead. The refresh entry is never touched here: a
|
|
4214
|
+
* rejected access token says nothing about the refresh family.
|
|
4215
|
+
*/
|
|
4216
|
+
invalidateAccessToken(spentToken) {
|
|
4217
|
+
this.syncFromPersistedStorage();
|
|
4218
|
+
if (this.token !== spentToken) return;
|
|
4219
|
+
this.setToken(null);
|
|
4220
|
+
}
|
|
4221
|
+
/** Commit both credentials in one canonical write. */
|
|
4222
|
+
setSession(token, refreshToken, options) {
|
|
4223
|
+
this.token = token;
|
|
4224
|
+
this.refreshTokenValue = refreshToken;
|
|
4225
|
+
const committed = this.commitPersistedSession(
|
|
4226
|
+
{ token, refreshToken },
|
|
4227
|
+
options
|
|
4228
|
+
);
|
|
4229
|
+
if (!committed) return false;
|
|
4230
|
+
this.onTokenChange?.(this.token);
|
|
4231
|
+
this.onRefreshTokenChange?.(this.refreshTokenValue);
|
|
4232
|
+
return true;
|
|
4233
|
+
}
|
|
4234
|
+
/**
|
|
4235
|
+
* Serialize rotation against every other context sharing the same storage
|
|
4236
|
+
* key and API/tenant identity. Coordination is best-effort: when it is
|
|
4237
|
+
* unavailable or times out the rotation still happens, so server-side reuse
|
|
4238
|
+
* detection is never bypassed by a client that failed to coordinate.
|
|
4239
|
+
*/
|
|
4240
|
+
async rotateAcrossContexts(intended) {
|
|
4241
|
+
const lockName = this.refreshLockName;
|
|
4242
|
+
const requestLock = lockName ? getRefreshLockRequester() : null;
|
|
4243
|
+
if (!lockName || !requestLock) {
|
|
4244
|
+
return this.rotateWithCanonicalState(intended);
|
|
4245
|
+
}
|
|
4246
|
+
const controller = new AbortController();
|
|
4247
|
+
const timer = setTimeout(() => controller.abort(), REFRESH_LOCK_TIMEOUT);
|
|
4248
|
+
let entered = false;
|
|
4249
|
+
try {
|
|
4250
|
+
return await requestLock(
|
|
4251
|
+
lockName,
|
|
4252
|
+
{ signal: controller.signal },
|
|
4253
|
+
async () => {
|
|
4254
|
+
entered = true;
|
|
4255
|
+
return this.rotateWithCanonicalState(intended);
|
|
4256
|
+
}
|
|
4257
|
+
);
|
|
4258
|
+
} catch (error) {
|
|
4259
|
+
if (entered) throw error;
|
|
4260
|
+
return this.rotateWithCanonicalState(intended);
|
|
4261
|
+
} finally {
|
|
4262
|
+
clearTimeout(timer);
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
/**
|
|
4266
|
+
* Decide, against freshly re-read canonical state, whether this context still
|
|
4267
|
+
* owns the rotation.
|
|
4268
|
+
*/
|
|
4269
|
+
async rotateWithCanonicalState(intended) {
|
|
4270
|
+
let fence = intended;
|
|
4271
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
4272
|
+
const state = this.classifyCanonicalState(fence);
|
|
4273
|
+
if (state === "cleared") throw new ApiError("Not authenticated", 401);
|
|
4274
|
+
if (state === "current") return this._doRefreshToken(fence);
|
|
4275
|
+
if (this.token) return this.canonicalPair(this.token);
|
|
4276
|
+
fence = this.currentFence();
|
|
4277
|
+
}
|
|
4278
|
+
throw new ApiError("Not authenticated", 401);
|
|
4279
|
+
}
|
|
4280
|
+
async _doRefreshToken(intended) {
|
|
4281
|
+
const init = intended.refreshToken ? {
|
|
3851
4282
|
method: "POST",
|
|
3852
|
-
body: JSON.stringify({ refreshToken:
|
|
4283
|
+
body: JSON.stringify({ refreshToken: intended.refreshToken })
|
|
3853
4284
|
} : {
|
|
3854
4285
|
method: "POST",
|
|
3855
|
-
headers: { Authorization: `Bearer ${
|
|
4286
|
+
headers: { Authorization: `Bearer ${intended.token}` }
|
|
3856
4287
|
};
|
|
3857
4288
|
const result = await this.requestJson(
|
|
3858
4289
|
"/api/customers/refresh",
|
|
3859
4290
|
init
|
|
3860
4291
|
);
|
|
3861
|
-
this.
|
|
3862
|
-
if (
|
|
3863
|
-
|
|
4292
|
+
const state = this.classifyCanonicalState(intended);
|
|
4293
|
+
if (state === "cleared") throw new ApiError("Not authenticated", 401);
|
|
4294
|
+
if (state === "superseded") {
|
|
4295
|
+
if (this.token) return this.canonicalPair(this.token);
|
|
4296
|
+
throw new ApiError("Not authenticated", 401);
|
|
4297
|
+
}
|
|
4298
|
+
const replacedAccessToken = this.token !== null && this.token !== intended.token ? this.token : null;
|
|
4299
|
+
const nextToken = replacedAccessToken ?? result.token;
|
|
4300
|
+
const nextRefresh = result.refreshToken ?? null;
|
|
4301
|
+
if (!this.setSession(nextToken, nextRefresh, { requireLiveSession: true })) {
|
|
4302
|
+
throw new ApiError("Not authenticated", 401);
|
|
4303
|
+
}
|
|
4304
|
+
return {
|
|
4305
|
+
token: nextToken,
|
|
4306
|
+
...nextRefresh ? { refreshToken: nextRefresh } : {}
|
|
4307
|
+
};
|
|
3864
4308
|
}
|
|
3865
4309
|
/**
|
|
3866
|
-
* Clear
|
|
4310
|
+
* Clear local credentials synchronously. This compatibility API cannot
|
|
4311
|
+
* revoke the server-side refresh family; use logoutAsync() for that.
|
|
4312
|
+
* @deprecated Use logoutAsync() to end the direct server session.
|
|
3867
4313
|
*/
|
|
3868
4314
|
logout() {
|
|
3869
|
-
this.
|
|
3870
|
-
|
|
4315
|
+
this.setSession(null, null);
|
|
4316
|
+
}
|
|
4317
|
+
/**
|
|
4318
|
+
* End the current direct refresh family and clear local credentials even if
|
|
4319
|
+
* revocation fails. A legacy access-only session has no refresh family here.
|
|
4320
|
+
*/
|
|
4321
|
+
async logoutAsync() {
|
|
4322
|
+
this.syncFromPersistedStorage();
|
|
4323
|
+
const refreshToken = this.refreshTokenValue;
|
|
4324
|
+
this.logout();
|
|
4325
|
+
if (!refreshToken) return;
|
|
4326
|
+
const operation2 = CUSTOMER_AUTH_ENDPOINT_OPERATIONS.revokeRefresh;
|
|
4327
|
+
await this.requestJson(resolveOperationPath(operation2), {
|
|
4328
|
+
method: resolveOperationMethod(operation2),
|
|
4329
|
+
body: JSON.stringify({ refreshToken })
|
|
4330
|
+
});
|
|
4331
|
+
}
|
|
4332
|
+
/** Archive the current password account and clear this client's credentials on success. */
|
|
4333
|
+
async withdraw(currentPassword) {
|
|
4334
|
+
const token = this.getToken();
|
|
4335
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
4336
|
+
const operation2 = CUSTOMER_AUTH_ENDPOINT_OPERATIONS.withdraw;
|
|
4337
|
+
const result = await this.requestJson(
|
|
4338
|
+
resolveOperationPath(operation2),
|
|
4339
|
+
{
|
|
4340
|
+
method: resolveOperationMethod(operation2),
|
|
4341
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
4342
|
+
body: JSON.stringify({ currentPassword })
|
|
4343
|
+
}
|
|
4344
|
+
);
|
|
4345
|
+
if (!result || typeof result.withdrawnAt !== "string" || !Number.isFinite(Date.parse(result.withdrawnAt)))
|
|
4346
|
+
throw new ApiError("Invalid customer withdrawal response", 502);
|
|
4347
|
+
this.logout();
|
|
4348
|
+
return { withdrawnAt: result.withdrawnAt };
|
|
3871
4349
|
}
|
|
3872
4350
|
/**
|
|
3873
4351
|
* Get the current authenticated customer's profile
|
|
3874
4352
|
*/
|
|
3875
4353
|
async me() {
|
|
3876
|
-
|
|
4354
|
+
const token = this.getToken();
|
|
4355
|
+
if (!token) return null;
|
|
3877
4356
|
try {
|
|
3878
4357
|
const data = await this.requestJson(
|
|
3879
4358
|
"/api/customers/me",
|
|
3880
4359
|
{
|
|
3881
4360
|
method: "GET",
|
|
3882
|
-
headers: { Authorization: `Bearer ${
|
|
4361
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
3883
4362
|
}
|
|
3884
4363
|
);
|
|
3885
4364
|
return data.customer ?? null;
|
|
3886
4365
|
} catch (error) {
|
|
3887
4366
|
if (error instanceof SDKError && error.status === 401) {
|
|
3888
|
-
this.
|
|
4367
|
+
this.invalidateAccessToken(token);
|
|
3889
4368
|
return null;
|
|
3890
4369
|
}
|
|
3891
4370
|
throw error;
|
|
@@ -3913,12 +4392,13 @@ var CustomerAuth = class {
|
|
|
3913
4392
|
* Update the authenticated customer's profile (name, phone, marketingConsent)
|
|
3914
4393
|
*/
|
|
3915
4394
|
async updateProfile(data) {
|
|
3916
|
-
|
|
4395
|
+
const token = this.getToken();
|
|
4396
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
3917
4397
|
const result = await this.requestJson(
|
|
3918
4398
|
"/api/customers/me",
|
|
3919
4399
|
{
|
|
3920
4400
|
method: "PATCH",
|
|
3921
|
-
headers: { Authorization: `Bearer ${
|
|
4401
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
3922
4402
|
body: JSON.stringify(data)
|
|
3923
4403
|
}
|
|
3924
4404
|
);
|
|
@@ -3928,10 +4408,11 @@ var CustomerAuth = class {
|
|
|
3928
4408
|
* Change the password of the currently authenticated customer
|
|
3929
4409
|
*/
|
|
3930
4410
|
async changePassword(currentPassword, newPassword) {
|
|
3931
|
-
|
|
4411
|
+
const token = this.getToken();
|
|
4412
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
3932
4413
|
await this.requestJson("/api/customers/change-password", {
|
|
3933
4414
|
method: "POST",
|
|
3934
|
-
headers: { Authorization: `Bearer ${
|
|
4415
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
3935
4416
|
body: JSON.stringify({ currentPassword, newPassword })
|
|
3936
4417
|
});
|
|
3937
4418
|
}
|
|
@@ -3939,7 +4420,8 @@ var CustomerAuth = class {
|
|
|
3939
4420
|
* Get the authenticated customer's orders with pagination and optional status filter
|
|
3940
4421
|
*/
|
|
3941
4422
|
async getMyOrders(options) {
|
|
3942
|
-
|
|
4423
|
+
const token = this.getToken();
|
|
4424
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
3943
4425
|
const params = new URLSearchParams();
|
|
3944
4426
|
if (options?.page) params.set("page", String(options.page));
|
|
3945
4427
|
if (options?.limit) params.set("limit", String(options.limit));
|
|
@@ -3961,21 +4443,27 @@ var CustomerAuth = class {
|
|
|
3961
4443
|
`/api/customers/me/orders${qs ? `?${qs}` : ""}`,
|
|
3962
4444
|
{
|
|
3963
4445
|
method: "GET",
|
|
3964
|
-
headers: { Authorization: `Bearer ${
|
|
4446
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
3965
4447
|
}
|
|
3966
4448
|
);
|
|
3967
4449
|
return validatePublicOrderList(result);
|
|
3968
4450
|
}
|
|
3969
4451
|
/**
|
|
3970
|
-
* Get the current token (or null if not authenticated)
|
|
4452
|
+
* Get the current token (or null if not authenticated).
|
|
4453
|
+
*
|
|
4454
|
+
* In persisted mode this re-reads shared storage first, so a rotation or
|
|
4455
|
+
* logout performed by another tab is observed before the token is used.
|
|
3971
4456
|
*/
|
|
3972
4457
|
getToken() {
|
|
4458
|
+
this.syncFromPersistedStorage();
|
|
3973
4459
|
return this.token;
|
|
3974
4460
|
}
|
|
3975
4461
|
/**
|
|
3976
|
-
* Get the current refresh token (or null if unavailable)
|
|
4462
|
+
* Get the current refresh token (or null if unavailable). Persisted mode
|
|
4463
|
+
* re-reads shared storage first, like {@link CustomerAuth.getToken}.
|
|
3977
4464
|
*/
|
|
3978
4465
|
getRefreshToken() {
|
|
4466
|
+
this.syncFromPersistedStorage();
|
|
3979
4467
|
return this.refreshTokenValue;
|
|
3980
4468
|
}
|
|
3981
4469
|
/**
|
|
@@ -3983,6 +4471,7 @@ var CustomerAuth = class {
|
|
|
3983
4471
|
*/
|
|
3984
4472
|
setToken(token) {
|
|
3985
4473
|
this.token = token;
|
|
4474
|
+
this.commitPersistedSession({ token });
|
|
3986
4475
|
this.onTokenChange?.(token);
|
|
3987
4476
|
}
|
|
3988
4477
|
/**
|
|
@@ -3990,13 +4479,14 @@ var CustomerAuth = class {
|
|
|
3990
4479
|
*/
|
|
3991
4480
|
setRefreshToken(refreshToken) {
|
|
3992
4481
|
this.refreshTokenValue = refreshToken;
|
|
4482
|
+
this.commitPersistedSession({ refreshToken });
|
|
3993
4483
|
this.onRefreshTokenChange?.(refreshToken);
|
|
3994
4484
|
}
|
|
3995
4485
|
/**
|
|
3996
4486
|
* Check if the customer is currently authenticated
|
|
3997
4487
|
*/
|
|
3998
4488
|
isAuthenticated() {
|
|
3999
|
-
return this.
|
|
4489
|
+
return this.getToken() !== null;
|
|
4000
4490
|
}
|
|
4001
4491
|
/**
|
|
4002
4492
|
* Internal: make a request with timeout and error handling.
|
|
@@ -4342,8 +4832,12 @@ var PaymentSessionApi = class extends CustomerScopedApi {
|
|
|
4342
4832
|
...Object.keys(headers).length > 0 && { headers }
|
|
4343
4833
|
});
|
|
4344
4834
|
}
|
|
4345
|
-
async paymentSession(result, expectedProvider) {
|
|
4346
|
-
return validatePublicPaymentSession(
|
|
4835
|
+
async paymentSession(result, expectedProvider, operation2) {
|
|
4836
|
+
return validatePublicPaymentSession(
|
|
4837
|
+
await result,
|
|
4838
|
+
expectedProvider,
|
|
4839
|
+
operation2
|
|
4840
|
+
);
|
|
4347
4841
|
}
|
|
4348
4842
|
create(params) {
|
|
4349
4843
|
const { idempotencyKey, ...body } = params;
|
|
@@ -4390,7 +4884,8 @@ var PaymentSessionApi = class extends CustomerScopedApi {
|
|
|
4390
4884
|
const endpoint = this.serverTransport ? COMMERCE_ENDPOINTS.paymentSessions.server.cancel : COMMERCE_ENDPOINTS.paymentSessions.cancel;
|
|
4391
4885
|
return this.paymentSession(
|
|
4392
4886
|
this.post(endpoint, body, idempotencyKey),
|
|
4393
|
-
"toss"
|
|
4887
|
+
"toss",
|
|
4888
|
+
"cancel"
|
|
4394
4889
|
);
|
|
4395
4890
|
}
|
|
4396
4891
|
cancelPortOne(params) {
|
|
@@ -4399,7 +4894,8 @@ var PaymentSessionApi = class extends CustomerScopedApi {
|
|
|
4399
4894
|
const endpoint = this.serverTransport ? COMMERCE_ENDPOINTS.paymentSessions.server.cancel : COMMERCE_ENDPOINTS.paymentSessions.cancel;
|
|
4400
4895
|
return this.paymentSession(
|
|
4401
4896
|
this.post(endpoint, body, idempotencyKey),
|
|
4402
|
-
"portone"
|
|
4897
|
+
"portone",
|
|
4898
|
+
"cancel"
|
|
4403
4899
|
);
|
|
4404
4900
|
}
|
|
4405
4901
|
confirm(params) {
|