@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/index.cjs
CHANGED
|
@@ -65,7 +65,6 @@ __export(src_exports, {
|
|
|
65
65
|
buildProductListingGroupsByOption: () => buildProductListingGroupsByOption,
|
|
66
66
|
buildProductOptionMatrix: () => buildProductOptionMatrix,
|
|
67
67
|
buildProductOptionMatrixFromDetail: () => buildProductOptionMatrixFromDetail,
|
|
68
|
-
createAnalytics: () => createAnalytics,
|
|
69
68
|
createAuthError: () => createAuthError,
|
|
70
69
|
createClient: () => createClient2,
|
|
71
70
|
createCommerceEmailWebhookHandler: () => createCommerceEmailWebhookHandler,
|
|
@@ -1149,6 +1148,49 @@ function stockSnapshotSearchParams(params) {
|
|
|
1149
1148
|
}
|
|
1150
1149
|
|
|
1151
1150
|
// src/core/collection/http-client.ts
|
|
1151
|
+
function validateWhere(where) {
|
|
1152
|
+
if (where === void 0) return false;
|
|
1153
|
+
let hasNull = false;
|
|
1154
|
+
function visit(value, depth, key) {
|
|
1155
|
+
if (depth > 10) {
|
|
1156
|
+
throw createValidationError(
|
|
1157
|
+
"Collection where must not exceed nesting depth 10."
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
if (value === null && (key === "equals" || key === "not_equals")) {
|
|
1161
|
+
hasNull = true;
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
if (value == null) {
|
|
1165
|
+
throw createValidationError(
|
|
1166
|
+
"Collection where must not contain null or undefined values."
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1169
|
+
if (Array.isArray(value)) {
|
|
1170
|
+
if (value.length === 0) {
|
|
1171
|
+
throw createValidationError(
|
|
1172
|
+
"Collection where must not contain empty arrays, including and/or."
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
if (value.length > 1e3) {
|
|
1176
|
+
throw createValidationError(
|
|
1177
|
+
"Collection where arrays must not contain more than 1000 elements."
|
|
1178
|
+
);
|
|
1179
|
+
}
|
|
1180
|
+
for (const item of value) visit(item, depth + 1);
|
|
1181
|
+
} else if (typeof value === "object" && !(value instanceof Date)) {
|
|
1182
|
+
const values = Object.entries(value);
|
|
1183
|
+
if (values.length === 0) {
|
|
1184
|
+
throw createValidationError(
|
|
1185
|
+
"Collection where must not contain empty condition objects."
|
|
1186
|
+
);
|
|
1187
|
+
}
|
|
1188
|
+
for (const [key2, item] of values) visit(item, depth + 1, key2);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
visit(where, 0);
|
|
1192
|
+
return hasNull;
|
|
1193
|
+
}
|
|
1152
1194
|
var HttpClient = class {
|
|
1153
1195
|
constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId, apiUrl) {
|
|
1154
1196
|
this.publishableKey = requirePublishableKeyForSecret(
|
|
@@ -1195,7 +1237,8 @@ var HttpClient = class {
|
|
|
1195
1237
|
...rest,
|
|
1196
1238
|
"fallback-locale": fallbackLocale === false ? "none" : fallbackLocale
|
|
1197
1239
|
};
|
|
1198
|
-
const
|
|
1240
|
+
const encodedOptions = validateWhere(queryOptions.where) ? { ...queryOptions, where: JSON.stringify(queryOptions.where) } : queryOptions;
|
|
1241
|
+
const queryString = (0, import_qs_esm.stringify)(encodedOptions, { addQueryPrefix: true });
|
|
1199
1242
|
return queryString ? `${endpoint}${queryString}` : endpoint;
|
|
1200
1243
|
}
|
|
1201
1244
|
assertJsonResponse(response) {
|
|
@@ -1504,6 +1547,11 @@ var COMMERCE_ENDPOINT_OPERATIONS = {
|
|
|
1504
1547
|
detail: strictCommerceOperation("checkouts", "post", "/detail"),
|
|
1505
1548
|
query: strictCommerceOperation("checkouts", "post", "/query")
|
|
1506
1549
|
},
|
|
1550
|
+
draftOrders: {
|
|
1551
|
+
calculate: operation("draft-orders", "post", "/:id/calculate"),
|
|
1552
|
+
complete: operation("draft-orders", "post", "/:id/complete"),
|
|
1553
|
+
finalize: operation("draft-orders", "post", "/:id/finalize")
|
|
1554
|
+
},
|
|
1507
1555
|
transactions: {
|
|
1508
1556
|
update: operation("transactions", "post", "/update")
|
|
1509
1557
|
},
|
|
@@ -1603,8 +1651,51 @@ var portOneStandardActionCapabilityOperations = /* @__PURE__ */ new Set([
|
|
|
1603
1651
|
]);
|
|
1604
1652
|
var MEMBERSHIP_ENDPOINT_OPERATIONS = {
|
|
1605
1653
|
subscribe: operation(null, "post", "/memberships/subscribe"),
|
|
1654
|
+
billingCustody: operation(null, "post", "/memberships/billing-custody"),
|
|
1606
1655
|
cancel: operation(null, "post", "/memberships/:id/cancel"),
|
|
1607
|
-
me: operation(null, "get", "/memberships/me")
|
|
1656
|
+
me: operation(null, "get", "/memberships/me"),
|
|
1657
|
+
chargeResult: operation(null, "post", "/memberships/:id/charge-result"),
|
|
1658
|
+
refundResult: operation(null, "post", "/memberships/:id/refund-result"),
|
|
1659
|
+
revoke: operation(null, "post", "/memberships/:id/revoke")
|
|
1660
|
+
};
|
|
1661
|
+
var EXTERNAL_EFFECT_CLAIM_ENDPOINT_OPERATIONS = {
|
|
1662
|
+
claim: operation(null, "post", "/external-effect-claims/claim"),
|
|
1663
|
+
get: operation(null, "post", "/external-effect-claims/get"),
|
|
1664
|
+
arm: operation(null, "post", "/external-effect-claims/arm"),
|
|
1665
|
+
release: operation(null, "post", "/external-effect-claims/release"),
|
|
1666
|
+
report: operation(null, "post", "/external-effect-claims/report"),
|
|
1667
|
+
recovery: operation(null, "post", "/external-effect-claims/recovery")
|
|
1668
|
+
};
|
|
1669
|
+
var INSTALLATION_CREDENTIAL_ENDPOINT_OPERATIONS = {
|
|
1670
|
+
create: operation(null, "post", "/installation-credentials/create"),
|
|
1671
|
+
get: operation(null, "post", "/installation-credentials/get"),
|
|
1672
|
+
replace: operation(null, "post", "/installation-credentials/replace"),
|
|
1673
|
+
dispose: operation(null, "post", "/installation-credentials/dispose")
|
|
1674
|
+
};
|
|
1675
|
+
var EXTERNAL_CUSTOMER_LINK_ENDPOINT_OPERATIONS = {
|
|
1676
|
+
connect: operation("customers", "post", "/external-links/connect"),
|
|
1677
|
+
get: operation("customers", "post", "/external-links/get"),
|
|
1678
|
+
list: operation("customers", "post", "/external-links/list"),
|
|
1679
|
+
unlink: operation("customers", "post", "/external-links/unlink"),
|
|
1680
|
+
capturePreviousState: operation(
|
|
1681
|
+
"customers",
|
|
1682
|
+
"post",
|
|
1683
|
+
"/external-links/capture-previous-state"
|
|
1684
|
+
),
|
|
1685
|
+
clearPreviousState: operation(
|
|
1686
|
+
"customers",
|
|
1687
|
+
"post",
|
|
1688
|
+
"/external-links/clear-previous-state"
|
|
1689
|
+
)
|
|
1690
|
+
};
|
|
1691
|
+
var CUSTOMER_GROUP_ENDPOINT_OPERATIONS = {
|
|
1692
|
+
members: operation("customer-groups", "get", "/:id/members"),
|
|
1693
|
+
grant: operation("customer-groups", "post", "/:id/members/grant"),
|
|
1694
|
+
revoke: operation("customer-groups", "post", "/:id/members/revoke")
|
|
1695
|
+
};
|
|
1696
|
+
var CUSTOMER_AUTH_ENDPOINT_OPERATIONS = {
|
|
1697
|
+
revokeRefresh: operation("customers", "post", "/revoke-refresh"),
|
|
1698
|
+
withdraw: operation("customers", "post", "/withdraw")
|
|
1608
1699
|
};
|
|
1609
1700
|
var EMBEDDED_ADMIN_ENDPOINT_OPERATIONS = {
|
|
1610
1701
|
show: operation("tenants", "get", "/embedded-admin-app"),
|
|
@@ -1629,6 +1720,14 @@ var COMMUNITY_ENDPOINT_OPERATIONS = {
|
|
|
1629
1720
|
detail: operation("posts", "get", "/detail")
|
|
1630
1721
|
}
|
|
1631
1722
|
};
|
|
1723
|
+
var GALLERY_ENDPOINT_OPERATIONS = {
|
|
1724
|
+
items: {
|
|
1725
|
+
storefront: operation("gallery-items", "get", "/storefront")
|
|
1726
|
+
}
|
|
1727
|
+
};
|
|
1728
|
+
var VIDEO_ENDPOINT_OPERATIONS = {
|
|
1729
|
+
playback: operation("videos", "post", "/:id/playback")
|
|
1730
|
+
};
|
|
1632
1731
|
function resolveOperationTemplate(descriptor) {
|
|
1633
1732
|
return descriptor.collection ? `/api/${descriptor.collection}${descriptor.path}` : `/api${descriptor.path}`;
|
|
1634
1733
|
}
|
|
@@ -2218,6 +2317,15 @@ var PublicContentApi = class extends RawCustomerScopedApi {
|
|
|
2218
2317
|
get(endpoint) {
|
|
2219
2318
|
return this.request(endpoint, { method: "GET" });
|
|
2220
2319
|
}
|
|
2320
|
+
galleryItems(options) {
|
|
2321
|
+
const operation2 = GALLERY_ENDPOINT_OPERATIONS.items.storefront;
|
|
2322
|
+
return this.request(
|
|
2323
|
+
resolveOperationPath(operation2, {
|
|
2324
|
+
query: galleryItemsListQuery(options)
|
|
2325
|
+
}),
|
|
2326
|
+
{ method: resolveOperationMethod(operation2) }
|
|
2327
|
+
);
|
|
2328
|
+
}
|
|
2221
2329
|
};
|
|
2222
2330
|
function appendCommonParams(search, options, defaultSort) {
|
|
2223
2331
|
search.set("sort", options?.sort ?? defaultSort);
|
|
@@ -2237,12 +2345,12 @@ function linksListPath(options) {
|
|
|
2237
2345
|
search.set("featured", String(options.featured));
|
|
2238
2346
|
return `/api/links/storefront?${search}`;
|
|
2239
2347
|
}
|
|
2240
|
-
function
|
|
2348
|
+
function galleryItemsListQuery(options) {
|
|
2241
2349
|
const search = new URLSearchParams();
|
|
2242
2350
|
appendCommonParams(search, options, "manual");
|
|
2243
2351
|
if (options?.galleryId) search.set("galleryId", options.galleryId);
|
|
2244
2352
|
if (options?.gallerySlug) search.set("gallerySlug", options.gallerySlug);
|
|
2245
|
-
return
|
|
2353
|
+
return search;
|
|
2246
2354
|
}
|
|
2247
2355
|
var ContentClient = class {
|
|
2248
2356
|
constructor(options) {
|
|
@@ -2256,9 +2364,7 @@ var ContentClient = class {
|
|
|
2256
2364
|
list: (params) => api.get(linksListPath(params))
|
|
2257
2365
|
};
|
|
2258
2366
|
this.galleryItems = {
|
|
2259
|
-
list: (params) => api.
|
|
2260
|
-
galleryItemsListPath(params)
|
|
2261
|
-
)
|
|
2367
|
+
list: (params) => api.galleryItems(params)
|
|
2262
2368
|
};
|
|
2263
2369
|
}
|
|
2264
2370
|
};
|
|
@@ -2314,7 +2420,10 @@ var ORDER_PRIMARY_DISPLAY_STATUSES = [
|
|
|
2314
2420
|
];
|
|
2315
2421
|
var CHECKOUT_STATUSES = ["open", "placed", "abandoned"];
|
|
2316
2422
|
var ABANDONED_REASONS = ["expired", "customer", "operator"];
|
|
2317
|
-
|
|
2423
|
+
function managedPaymentProviders(providers) {
|
|
2424
|
+
return providers;
|
|
2425
|
+
}
|
|
2426
|
+
var PAYMENT_PROVIDERS = managedPaymentProviders(["toss", "portone"]);
|
|
2318
2427
|
var PAYMENT_PROVIDER_STATUSES = [
|
|
2319
2428
|
"requires_payment_method",
|
|
2320
2429
|
"requires_action",
|
|
@@ -2362,6 +2471,26 @@ function invalidCanonicalResponse(resource, details) {
|
|
|
2362
2471
|
function fail(resource, path, expected) {
|
|
2363
2472
|
invalidCanonicalResponse(resource, { path, expected });
|
|
2364
2473
|
}
|
|
2474
|
+
function providerMismatch(resource, expected, actual, operation2) {
|
|
2475
|
+
if (operation2 === "cancel") {
|
|
2476
|
+
const method = {
|
|
2477
|
+
toss: "cancel()",
|
|
2478
|
+
portone: "cancelPortOne()"
|
|
2479
|
+
}[actual];
|
|
2480
|
+
throw createApiError(
|
|
2481
|
+
`Canonical ${resource} response provider mismatch: expected ${expected}, received ${actual}.`,
|
|
2482
|
+
502,
|
|
2483
|
+
{ resource, path: "$.provider", expected, actual },
|
|
2484
|
+
`The server applied the cancellation to a ${actual} PaymentSession, but this SDK method expects ${expected}.`,
|
|
2485
|
+
`Re-invoke ${method} with the same idempotencyKey; do not create a new key.`
|
|
2486
|
+
);
|
|
2487
|
+
}
|
|
2488
|
+
invalidCanonicalResponse(resource, {
|
|
2489
|
+
path: "$.provider",
|
|
2490
|
+
expected,
|
|
2491
|
+
actual
|
|
2492
|
+
});
|
|
2493
|
+
}
|
|
2365
2494
|
function record(value, resource, path) {
|
|
2366
2495
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2367
2496
|
fail(resource, path, "object");
|
|
@@ -3654,7 +3783,7 @@ function validateNextAction(value, provider, resource, path) {
|
|
|
3654
3783
|
if (action.customerEmail !== void 0)
|
|
3655
3784
|
validateCustomer({ email: action.customerEmail }, resource, path);
|
|
3656
3785
|
}
|
|
3657
|
-
function validatePublicPaymentSession(value, expectedProvider) {
|
|
3786
|
+
function validatePublicPaymentSession(value, expectedProvider, operation2) {
|
|
3658
3787
|
const resource = "PaymentSession";
|
|
3659
3788
|
const session = record(value, resource, "$");
|
|
3660
3789
|
const provider = enumValue(
|
|
@@ -3663,9 +3792,6 @@ function validatePublicPaymentSession(value, expectedProvider) {
|
|
|
3663
3792
|
resource,
|
|
3664
3793
|
"$.provider"
|
|
3665
3794
|
);
|
|
3666
|
-
if (expectedProvider !== void 0 && provider !== expectedProvider) {
|
|
3667
|
-
fail(resource, "$.provider", expectedProvider);
|
|
3668
|
-
}
|
|
3669
3795
|
const status = enumValue(
|
|
3670
3796
|
session.providerStatus,
|
|
3671
3797
|
PAYMENT_PROVIDER_STATUSES,
|
|
@@ -3869,44 +3995,128 @@ function validatePublicPaymentSession(value, expectedProvider) {
|
|
|
3869
3995
|
"$.cancellationReason"
|
|
3870
3996
|
);
|
|
3871
3997
|
}
|
|
3998
|
+
if (expectedProvider !== void 0 && provider !== expectedProvider) {
|
|
3999
|
+
if (operation2 === "cancel" && status !== "canceling" && status !== "canceled") {
|
|
4000
|
+
fail(
|
|
4001
|
+
resource,
|
|
4002
|
+
"$.providerStatus",
|
|
4003
|
+
"canceling or canceled after a successful cancellation"
|
|
4004
|
+
);
|
|
4005
|
+
}
|
|
4006
|
+
providerMismatch(resource, expectedProvider, provider, operation2);
|
|
4007
|
+
}
|
|
3872
4008
|
return value;
|
|
3873
4009
|
}
|
|
3874
4010
|
|
|
3875
4011
|
// src/core/customer/customer-auth.ts
|
|
3876
4012
|
var DEFAULT_TIMEOUT2 = 15e3;
|
|
3877
|
-
|
|
4013
|
+
var REFRESH_LOCK_TIMEOUT = DEFAULT_TIMEOUT2 * 3;
|
|
4014
|
+
var SESSION_RECORD_VERSION = 1;
|
|
4015
|
+
var STORAGE_UNUSABLE = /* @__PURE__ */ Symbol("storage-unusable");
|
|
4016
|
+
function canonicalApiIdentity(apiUrl) {
|
|
3878
4017
|
try {
|
|
3879
|
-
|
|
4018
|
+
const url = new URL(apiUrl);
|
|
4019
|
+
return `${url.protocol.toLowerCase()}//${url.host.toLowerCase()}${url.pathname.replace(/\/+$/, "")}`;
|
|
4020
|
+
} catch {
|
|
4021
|
+
return apiUrl.trim().toLowerCase().replace(/\/+$/, "");
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
function buildRefreshLockName(apiUrl, publishableKey, storageKey) {
|
|
4025
|
+
return `01software:customer-auth-refresh:${canonicalApiIdentity(apiUrl)}:${publishableKey}:${storageKey}`;
|
|
4026
|
+
}
|
|
4027
|
+
function getRefreshLockRequester() {
|
|
4028
|
+
try {
|
|
4029
|
+
if (typeof navigator === "undefined") return null;
|
|
4030
|
+
const locks = navigator.locks;
|
|
4031
|
+
if (!locks || typeof locks.request !== "function") return null;
|
|
4032
|
+
return (name, options, callback) => locks.request(
|
|
4033
|
+
name,
|
|
4034
|
+
options,
|
|
4035
|
+
() => callback()
|
|
4036
|
+
);
|
|
4037
|
+
} catch {
|
|
4038
|
+
return null;
|
|
4039
|
+
}
|
|
4040
|
+
}
|
|
4041
|
+
function parseSessionRecord(raw) {
|
|
4042
|
+
try {
|
|
4043
|
+
const parsed = JSON.parse(raw);
|
|
4044
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
4045
|
+
const candidate = parsed;
|
|
4046
|
+
if (candidate.v !== SESSION_RECORD_VERSION) return null;
|
|
4047
|
+
if (typeof candidate.rev !== "number" || !Number.isFinite(candidate.rev)) {
|
|
4048
|
+
return null;
|
|
4049
|
+
}
|
|
4050
|
+
const token = candidate.token;
|
|
4051
|
+
const refreshToken = candidate.refreshToken;
|
|
4052
|
+
if (token !== null && typeof token !== "string") return null;
|
|
4053
|
+
if (refreshToken !== null && typeof refreshToken !== "string") return null;
|
|
4054
|
+
return {
|
|
4055
|
+
v: SESSION_RECORD_VERSION,
|
|
4056
|
+
rev: candidate.rev,
|
|
4057
|
+
token,
|
|
4058
|
+
refreshToken
|
|
4059
|
+
};
|
|
3880
4060
|
} catch {
|
|
3881
4061
|
return null;
|
|
3882
4062
|
}
|
|
3883
4063
|
}
|
|
4064
|
+
function readSessionRecord(recordKey, mirrorKey, mirrorRefreshKey) {
|
|
4065
|
+
let raw;
|
|
4066
|
+
try {
|
|
4067
|
+
raw = localStorage.getItem(recordKey);
|
|
4068
|
+
} catch {
|
|
4069
|
+
return STORAGE_UNUSABLE;
|
|
4070
|
+
}
|
|
4071
|
+
if (raw !== null) {
|
|
4072
|
+
const parsed = parseSessionRecord(raw);
|
|
4073
|
+
return parsed ? { record: parsed, stored: true } : STORAGE_UNUSABLE;
|
|
4074
|
+
}
|
|
4075
|
+
try {
|
|
4076
|
+
return {
|
|
4077
|
+
record: {
|
|
4078
|
+
v: SESSION_RECORD_VERSION,
|
|
4079
|
+
rev: 0,
|
|
4080
|
+
token: localStorage.getItem(mirrorKey),
|
|
4081
|
+
refreshToken: localStorage.getItem(mirrorRefreshKey)
|
|
4082
|
+
},
|
|
4083
|
+
stored: false
|
|
4084
|
+
};
|
|
4085
|
+
} catch {
|
|
4086
|
+
return STORAGE_UNUSABLE;
|
|
4087
|
+
}
|
|
4088
|
+
}
|
|
3884
4089
|
var CustomerAuth = class {
|
|
3885
4090
|
constructor(publishableKey, options, apiUrl) {
|
|
3886
4091
|
this.refreshPromise = null;
|
|
4092
|
+
/** Canonical record key; null unless persistence is active in a browser. */
|
|
4093
|
+
this.persistRecordKey = null;
|
|
4094
|
+
/** Advisory single-value access mirror, plus the retired refresh mirror. */
|
|
4095
|
+
this.persistMirrorKey = null;
|
|
4096
|
+
this.persistMirrorRefreshKey = null;
|
|
4097
|
+
/** Cross-context refresh lock name; null when there is no shared storage. */
|
|
4098
|
+
this.refreshLockName = null;
|
|
4099
|
+
/** Revision of the canonical record this instance last observed. */
|
|
4100
|
+
this.sessionRev = 0;
|
|
3887
4101
|
this.publishableKey = publishableKey;
|
|
3888
4102
|
this.baseUrl = resolveApiUrl(apiUrl);
|
|
3889
4103
|
const persist = options?.persist ?? true;
|
|
3890
4104
|
if (persist) {
|
|
3891
4105
|
const key = typeof persist === "string" ? persist : "customer-token";
|
|
3892
|
-
const refreshKey = `${key}:refresh`;
|
|
3893
4106
|
const isBrowser = typeof window !== "undefined";
|
|
3894
|
-
this.token =
|
|
3895
|
-
this.refreshTokenValue =
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
} catch {
|
|
3908
|
-
}
|
|
3909
|
-
} : void 0;
|
|
4107
|
+
this.token = null;
|
|
4108
|
+
this.refreshTokenValue = null;
|
|
4109
|
+
if (isBrowser) {
|
|
4110
|
+
this.persistRecordKey = `${key}:session`;
|
|
4111
|
+
this.persistMirrorKey = key;
|
|
4112
|
+
this.persistMirrorRefreshKey = `${key}:refresh`;
|
|
4113
|
+
this.refreshLockName = buildRefreshLockName(
|
|
4114
|
+
this.baseUrl,
|
|
4115
|
+
publishableKey,
|
|
4116
|
+
key
|
|
4117
|
+
);
|
|
4118
|
+
this.syncFromPersistedStorage();
|
|
4119
|
+
}
|
|
3910
4120
|
} else {
|
|
3911
4121
|
this.token = options?.token ?? null;
|
|
3912
4122
|
this.refreshTokenValue = options?.refreshToken ?? null;
|
|
@@ -3934,66 +4144,334 @@ var CustomerAuth = class {
|
|
|
3934
4144
|
body: JSON.stringify(data)
|
|
3935
4145
|
}
|
|
3936
4146
|
);
|
|
3937
|
-
this.
|
|
3938
|
-
|
|
4147
|
+
if (result.refreshToken) this.setSession(result.token, result.refreshToken);
|
|
4148
|
+
else this.setToken(result.token);
|
|
3939
4149
|
return result;
|
|
3940
4150
|
}
|
|
3941
4151
|
/**
|
|
3942
4152
|
* Refresh the current token. Uses a refresh token when available, otherwise
|
|
3943
4153
|
* falls back to the legacy bearer-token compatibility path.
|
|
4154
|
+
*
|
|
4155
|
+
* Refresh tokens are single-use, so a persisted session shared by several
|
|
4156
|
+
* tabs or SDK instances must rotate at most once: concurrent rotations of the
|
|
4157
|
+
* same token are replay from the server's point of view and revoke the whole
|
|
4158
|
+
* family. Persisted browser sessions therefore serialize rotation across
|
|
4159
|
+
* contexts and re-read the canonical persisted credentials inside the lock.
|
|
3944
4160
|
*/
|
|
3945
4161
|
async refreshToken() {
|
|
4162
|
+
this.syncFromPersistedStorage();
|
|
3946
4163
|
if (!this.refreshTokenValue && !this.token) {
|
|
3947
4164
|
throw new ApiError("Not authenticated", 401);
|
|
3948
4165
|
}
|
|
3949
4166
|
if (this.refreshPromise) return this.refreshPromise;
|
|
3950
|
-
|
|
4167
|
+
const intended = this.currentFence();
|
|
4168
|
+
this.refreshPromise = this.rotateAcrossContexts(intended);
|
|
3951
4169
|
try {
|
|
3952
4170
|
return await this.refreshPromise;
|
|
3953
4171
|
} finally {
|
|
3954
4172
|
this.refreshPromise = null;
|
|
3955
4173
|
}
|
|
3956
4174
|
}
|
|
3957
|
-
|
|
3958
|
-
|
|
4175
|
+
/**
|
|
4176
|
+
* Re-read the canonical persisted session. In persisted mode the shared
|
|
4177
|
+
* record — not this instance's memory — is the source of truth, so another
|
|
4178
|
+
* context's rotation, login, or logout is observed before the next credential
|
|
4179
|
+
* use. Change callbacks are deliberately not invoked: this is a read.
|
|
4180
|
+
*/
|
|
4181
|
+
syncFromPersistedStorage() {
|
|
4182
|
+
const canonical = this.readCanonicalSession();
|
|
4183
|
+
if (canonical === STORAGE_UNUSABLE) {
|
|
4184
|
+
this.disablePersistence();
|
|
4185
|
+
return;
|
|
4186
|
+
}
|
|
4187
|
+
if (!canonical) return;
|
|
4188
|
+
this.sessionRev = canonical.record.rev;
|
|
4189
|
+
this.token = canonical.record.token;
|
|
4190
|
+
this.refreshTokenValue = canonical.record.refreshToken;
|
|
4191
|
+
}
|
|
4192
|
+
/** `null` when this instance does not persist. */
|
|
4193
|
+
readCanonicalSession() {
|
|
4194
|
+
if (!this.persistRecordKey || !this.persistMirrorKey || !this.persistMirrorRefreshKey) {
|
|
4195
|
+
return null;
|
|
4196
|
+
}
|
|
4197
|
+
return readSessionRecord(
|
|
4198
|
+
this.persistRecordKey,
|
|
4199
|
+
this.persistMirrorKey,
|
|
4200
|
+
this.persistMirrorRefreshKey
|
|
4201
|
+
);
|
|
4202
|
+
}
|
|
4203
|
+
/**
|
|
4204
|
+
* Publish a change to canonical storage.
|
|
4205
|
+
*
|
|
4206
|
+
* The canonical pair is re-read immediately before the single record write so
|
|
4207
|
+
* that a field this call does not change is taken from that read rather than
|
|
4208
|
+
* from this instance's possibly stale copy. That merge is BEST-EFFORT: it
|
|
4209
|
+
* removes the stale counterpart an instance would otherwise carry, but it is
|
|
4210
|
+
* not a compare-and-swap. Web Storage offers none, and the synchronous public
|
|
4211
|
+
* mutators cannot await the refresh lease, so a commit that lands between
|
|
4212
|
+
* this read and the store operation is lost by ordering — including the
|
|
4213
|
+
* counterpart just read, which is then republished stale. The window is one
|
|
4214
|
+
* store operation wide and the server remains authoritative; see
|
|
4215
|
+
* `CUST-AUTH-033` for the stated limit.
|
|
4216
|
+
*
|
|
4217
|
+
* Returns `false` only when `requireLiveSession` refused a write because the
|
|
4218
|
+
* canonical session had already been cleared.
|
|
4219
|
+
*/
|
|
4220
|
+
commitPersistedSession(change, options) {
|
|
4221
|
+
const recordKey = this.persistRecordKey;
|
|
4222
|
+
const mirrorKey = this.persistMirrorKey;
|
|
4223
|
+
const mirrorRefreshKey = this.persistMirrorRefreshKey;
|
|
4224
|
+
const canonical = this.readCanonicalSession();
|
|
4225
|
+
if (canonical === null || !recordKey || !mirrorKey || !mirrorRefreshKey) {
|
|
4226
|
+
return true;
|
|
4227
|
+
}
|
|
4228
|
+
if (canonical === STORAGE_UNUSABLE) {
|
|
4229
|
+
this.disablePersistence();
|
|
4230
|
+
return true;
|
|
4231
|
+
}
|
|
4232
|
+
const current = canonical.record;
|
|
4233
|
+
if (options?.requireLiveSession && !current.token && !current.refreshToken) {
|
|
4234
|
+
return false;
|
|
4235
|
+
}
|
|
4236
|
+
const next = {
|
|
4237
|
+
v: SESSION_RECORD_VERSION,
|
|
4238
|
+
rev: current.rev + 1,
|
|
4239
|
+
token: "token" in change ? change.token ?? null : current.token,
|
|
4240
|
+
refreshToken: "refreshToken" in change ? change.refreshToken ?? null : current.refreshToken
|
|
4241
|
+
};
|
|
4242
|
+
if (
|
|
4243
|
+
// Only a state that is genuinely persisted can already say what this
|
|
4244
|
+
// write would say. An absent record reads as an empty pair, and treating
|
|
4245
|
+
// that as a no-op would drop the very tombstone that stops a later
|
|
4246
|
+
// migration from resurrecting the session out of the earlier layout.
|
|
4247
|
+
canonical.stored && next.token === current.token && next.refreshToken === current.refreshToken
|
|
4248
|
+
) {
|
|
4249
|
+
this.sessionRev = current.rev;
|
|
4250
|
+
this.token = current.token;
|
|
4251
|
+
this.refreshTokenValue = current.refreshToken;
|
|
4252
|
+
return true;
|
|
4253
|
+
}
|
|
4254
|
+
try {
|
|
4255
|
+
localStorage.setItem(recordKey, JSON.stringify(next));
|
|
4256
|
+
} catch {
|
|
4257
|
+
this.disablePersistence();
|
|
4258
|
+
return true;
|
|
4259
|
+
}
|
|
4260
|
+
this.sessionRev = next.rev;
|
|
4261
|
+
this.token = next.token;
|
|
4262
|
+
this.refreshTokenValue = next.refreshToken;
|
|
4263
|
+
try {
|
|
4264
|
+
localStorage.removeItem(mirrorRefreshKey);
|
|
4265
|
+
} catch {
|
|
4266
|
+
return true;
|
|
4267
|
+
}
|
|
4268
|
+
try {
|
|
4269
|
+
if (next.token) localStorage.setItem(mirrorKey, next.token);
|
|
4270
|
+
else localStorage.removeItem(mirrorKey);
|
|
4271
|
+
} catch {
|
|
4272
|
+
}
|
|
4273
|
+
return true;
|
|
4274
|
+
}
|
|
4275
|
+
/**
|
|
4276
|
+
* Storage rejected a write or a read, so it is not a canonical store for this
|
|
4277
|
+
* instance. Fall back to the memory-only session and take no lease: there is
|
|
4278
|
+
* no shared state left to coordinate. Nothing is purged — the record commit
|
|
4279
|
+
* is atomic, so no inconsistent state was published, and another context's
|
|
4280
|
+
* valid session must not be destroyed by this instance's local failure.
|
|
4281
|
+
*/
|
|
4282
|
+
disablePersistence() {
|
|
4283
|
+
this.persistRecordKey = null;
|
|
4284
|
+
this.persistMirrorKey = null;
|
|
4285
|
+
this.persistMirrorRefreshKey = null;
|
|
4286
|
+
this.refreshLockName = null;
|
|
4287
|
+
}
|
|
4288
|
+
/**
|
|
4289
|
+
* Compare the canonical record with the fence this call captured before it
|
|
4290
|
+
* left. Every persisted decision — rotate, converge, or fail closed — goes
|
|
4291
|
+
* through this one comparison. The revision catches a logout, a fresh login,
|
|
4292
|
+
* or a manual setter even when token values coincide; the value comparison
|
|
4293
|
+
* additionally catches a mirror-only writer that cannot bump a revision.
|
|
4294
|
+
*/
|
|
4295
|
+
classifyCanonicalState(intended) {
|
|
4296
|
+
if (!this.persistRecordKey) return "current";
|
|
4297
|
+
this.syncFromPersistedStorage();
|
|
4298
|
+
if (!this.persistRecordKey) return "current";
|
|
4299
|
+
if (!this.token && !this.refreshTokenValue) return "cleared";
|
|
4300
|
+
if (intended.refreshToken !== null) {
|
|
4301
|
+
return this.refreshTokenValue === intended.refreshToken ? "current" : "superseded";
|
|
4302
|
+
}
|
|
4303
|
+
return this.token === intended.token && !this.refreshTokenValue ? "current" : "superseded";
|
|
4304
|
+
}
|
|
4305
|
+
/** The canonical persisted pair, as a refresh result. */
|
|
4306
|
+
canonicalPair(token) {
|
|
4307
|
+
return {
|
|
4308
|
+
token,
|
|
4309
|
+
...this.refreshTokenValue ? { refreshToken: this.refreshTokenValue } : {}
|
|
4310
|
+
};
|
|
4311
|
+
}
|
|
4312
|
+
/** Current credentials plus the revision they were observed at. */
|
|
4313
|
+
currentFence() {
|
|
4314
|
+
return { token: this.token, refreshToken: this.refreshTokenValue };
|
|
4315
|
+
}
|
|
4316
|
+
/**
|
|
4317
|
+
* Clear an access credential a 401 just proved unusable — but only while it
|
|
4318
|
+
* is still the canonical one. A rotation committed by another context while
|
|
4319
|
+
* the request was in flight is newer than this 401, so it must survive; the
|
|
4320
|
+
* instance adopts it instead. The refresh entry is never touched here: a
|
|
4321
|
+
* rejected access token says nothing about the refresh family.
|
|
4322
|
+
*/
|
|
4323
|
+
invalidateAccessToken(spentToken) {
|
|
4324
|
+
this.syncFromPersistedStorage();
|
|
4325
|
+
if (this.token !== spentToken) return;
|
|
4326
|
+
this.setToken(null);
|
|
4327
|
+
}
|
|
4328
|
+
/** Commit both credentials in one canonical write. */
|
|
4329
|
+
setSession(token, refreshToken, options) {
|
|
4330
|
+
this.token = token;
|
|
4331
|
+
this.refreshTokenValue = refreshToken;
|
|
4332
|
+
const committed = this.commitPersistedSession(
|
|
4333
|
+
{ token, refreshToken },
|
|
4334
|
+
options
|
|
4335
|
+
);
|
|
4336
|
+
if (!committed) return false;
|
|
4337
|
+
this.onTokenChange?.(this.token);
|
|
4338
|
+
this.onRefreshTokenChange?.(this.refreshTokenValue);
|
|
4339
|
+
return true;
|
|
4340
|
+
}
|
|
4341
|
+
/**
|
|
4342
|
+
* Serialize rotation against every other context sharing the same storage
|
|
4343
|
+
* key and API/tenant identity. Coordination is best-effort: when it is
|
|
4344
|
+
* unavailable or times out the rotation still happens, so server-side reuse
|
|
4345
|
+
* detection is never bypassed by a client that failed to coordinate.
|
|
4346
|
+
*/
|
|
4347
|
+
async rotateAcrossContexts(intended) {
|
|
4348
|
+
const lockName = this.refreshLockName;
|
|
4349
|
+
const requestLock = lockName ? getRefreshLockRequester() : null;
|
|
4350
|
+
if (!lockName || !requestLock) {
|
|
4351
|
+
return this.rotateWithCanonicalState(intended);
|
|
4352
|
+
}
|
|
4353
|
+
const controller = new AbortController();
|
|
4354
|
+
const timer = setTimeout(() => controller.abort(), REFRESH_LOCK_TIMEOUT);
|
|
4355
|
+
let entered = false;
|
|
4356
|
+
try {
|
|
4357
|
+
return await requestLock(
|
|
4358
|
+
lockName,
|
|
4359
|
+
{ signal: controller.signal },
|
|
4360
|
+
async () => {
|
|
4361
|
+
entered = true;
|
|
4362
|
+
return this.rotateWithCanonicalState(intended);
|
|
4363
|
+
}
|
|
4364
|
+
);
|
|
4365
|
+
} catch (error) {
|
|
4366
|
+
if (entered) throw error;
|
|
4367
|
+
return this.rotateWithCanonicalState(intended);
|
|
4368
|
+
} finally {
|
|
4369
|
+
clearTimeout(timer);
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
4372
|
+
/**
|
|
4373
|
+
* Decide, against freshly re-read canonical state, whether this context still
|
|
4374
|
+
* owns the rotation.
|
|
4375
|
+
*/
|
|
4376
|
+
async rotateWithCanonicalState(intended) {
|
|
4377
|
+
let fence = intended;
|
|
4378
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
4379
|
+
const state = this.classifyCanonicalState(fence);
|
|
4380
|
+
if (state === "cleared") throw new ApiError("Not authenticated", 401);
|
|
4381
|
+
if (state === "current") return this._doRefreshToken(fence);
|
|
4382
|
+
if (this.token) return this.canonicalPair(this.token);
|
|
4383
|
+
fence = this.currentFence();
|
|
4384
|
+
}
|
|
4385
|
+
throw new ApiError("Not authenticated", 401);
|
|
4386
|
+
}
|
|
4387
|
+
async _doRefreshToken(intended) {
|
|
4388
|
+
const init = intended.refreshToken ? {
|
|
3959
4389
|
method: "POST",
|
|
3960
|
-
body: JSON.stringify({ refreshToken:
|
|
4390
|
+
body: JSON.stringify({ refreshToken: intended.refreshToken })
|
|
3961
4391
|
} : {
|
|
3962
4392
|
method: "POST",
|
|
3963
|
-
headers: { Authorization: `Bearer ${
|
|
4393
|
+
headers: { Authorization: `Bearer ${intended.token}` }
|
|
3964
4394
|
};
|
|
3965
4395
|
const result = await this.requestJson(
|
|
3966
4396
|
"/api/customers/refresh",
|
|
3967
4397
|
init
|
|
3968
4398
|
);
|
|
3969
|
-
this.
|
|
3970
|
-
if (
|
|
3971
|
-
|
|
4399
|
+
const state = this.classifyCanonicalState(intended);
|
|
4400
|
+
if (state === "cleared") throw new ApiError("Not authenticated", 401);
|
|
4401
|
+
if (state === "superseded") {
|
|
4402
|
+
if (this.token) return this.canonicalPair(this.token);
|
|
4403
|
+
throw new ApiError("Not authenticated", 401);
|
|
4404
|
+
}
|
|
4405
|
+
const replacedAccessToken = this.token !== null && this.token !== intended.token ? this.token : null;
|
|
4406
|
+
const nextToken = replacedAccessToken ?? result.token;
|
|
4407
|
+
const nextRefresh = result.refreshToken ?? null;
|
|
4408
|
+
if (!this.setSession(nextToken, nextRefresh, { requireLiveSession: true })) {
|
|
4409
|
+
throw new ApiError("Not authenticated", 401);
|
|
4410
|
+
}
|
|
4411
|
+
return {
|
|
4412
|
+
token: nextToken,
|
|
4413
|
+
...nextRefresh ? { refreshToken: nextRefresh } : {}
|
|
4414
|
+
};
|
|
3972
4415
|
}
|
|
3973
4416
|
/**
|
|
3974
|
-
* Clear
|
|
4417
|
+
* Clear local credentials synchronously. This compatibility API cannot
|
|
4418
|
+
* revoke the server-side refresh family; use logoutAsync() for that.
|
|
4419
|
+
* @deprecated Use logoutAsync() to end the direct server session.
|
|
3975
4420
|
*/
|
|
3976
4421
|
logout() {
|
|
3977
|
-
this.
|
|
3978
|
-
|
|
4422
|
+
this.setSession(null, null);
|
|
4423
|
+
}
|
|
4424
|
+
/**
|
|
4425
|
+
* End the current direct refresh family and clear local credentials even if
|
|
4426
|
+
* revocation fails. A legacy access-only session has no refresh family here.
|
|
4427
|
+
*/
|
|
4428
|
+
async logoutAsync() {
|
|
4429
|
+
this.syncFromPersistedStorage();
|
|
4430
|
+
const refreshToken = this.refreshTokenValue;
|
|
4431
|
+
this.logout();
|
|
4432
|
+
if (!refreshToken) return;
|
|
4433
|
+
const operation2 = CUSTOMER_AUTH_ENDPOINT_OPERATIONS.revokeRefresh;
|
|
4434
|
+
await this.requestJson(resolveOperationPath(operation2), {
|
|
4435
|
+
method: resolveOperationMethod(operation2),
|
|
4436
|
+
body: JSON.stringify({ refreshToken })
|
|
4437
|
+
});
|
|
4438
|
+
}
|
|
4439
|
+
/** Archive the current password account and clear this client's credentials on success. */
|
|
4440
|
+
async withdraw(currentPassword) {
|
|
4441
|
+
const token = this.getToken();
|
|
4442
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
4443
|
+
const operation2 = CUSTOMER_AUTH_ENDPOINT_OPERATIONS.withdraw;
|
|
4444
|
+
const result = await this.requestJson(
|
|
4445
|
+
resolveOperationPath(operation2),
|
|
4446
|
+
{
|
|
4447
|
+
method: resolveOperationMethod(operation2),
|
|
4448
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
4449
|
+
body: JSON.stringify({ currentPassword })
|
|
4450
|
+
}
|
|
4451
|
+
);
|
|
4452
|
+
if (!result || typeof result.withdrawnAt !== "string" || !Number.isFinite(Date.parse(result.withdrawnAt)))
|
|
4453
|
+
throw new ApiError("Invalid customer withdrawal response", 502);
|
|
4454
|
+
this.logout();
|
|
4455
|
+
return { withdrawnAt: result.withdrawnAt };
|
|
3979
4456
|
}
|
|
3980
4457
|
/**
|
|
3981
4458
|
* Get the current authenticated customer's profile
|
|
3982
4459
|
*/
|
|
3983
4460
|
async me() {
|
|
3984
|
-
|
|
4461
|
+
const token = this.getToken();
|
|
4462
|
+
if (!token) return null;
|
|
3985
4463
|
try {
|
|
3986
4464
|
const data = await this.requestJson(
|
|
3987
4465
|
"/api/customers/me",
|
|
3988
4466
|
{
|
|
3989
4467
|
method: "GET",
|
|
3990
|
-
headers: { Authorization: `Bearer ${
|
|
4468
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
3991
4469
|
}
|
|
3992
4470
|
);
|
|
3993
4471
|
return data.customer ?? null;
|
|
3994
4472
|
} catch (error) {
|
|
3995
4473
|
if (error instanceof SDKError && error.status === 401) {
|
|
3996
|
-
this.
|
|
4474
|
+
this.invalidateAccessToken(token);
|
|
3997
4475
|
return null;
|
|
3998
4476
|
}
|
|
3999
4477
|
throw error;
|
|
@@ -4021,12 +4499,13 @@ var CustomerAuth = class {
|
|
|
4021
4499
|
* Update the authenticated customer's profile (name, phone, marketingConsent)
|
|
4022
4500
|
*/
|
|
4023
4501
|
async updateProfile(data) {
|
|
4024
|
-
|
|
4502
|
+
const token = this.getToken();
|
|
4503
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
4025
4504
|
const result = await this.requestJson(
|
|
4026
4505
|
"/api/customers/me",
|
|
4027
4506
|
{
|
|
4028
4507
|
method: "PATCH",
|
|
4029
|
-
headers: { Authorization: `Bearer ${
|
|
4508
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
4030
4509
|
body: JSON.stringify(data)
|
|
4031
4510
|
}
|
|
4032
4511
|
);
|
|
@@ -4036,10 +4515,11 @@ var CustomerAuth = class {
|
|
|
4036
4515
|
* Change the password of the currently authenticated customer
|
|
4037
4516
|
*/
|
|
4038
4517
|
async changePassword(currentPassword, newPassword) {
|
|
4039
|
-
|
|
4518
|
+
const token = this.getToken();
|
|
4519
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
4040
4520
|
await this.requestJson("/api/customers/change-password", {
|
|
4041
4521
|
method: "POST",
|
|
4042
|
-
headers: { Authorization: `Bearer ${
|
|
4522
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
4043
4523
|
body: JSON.stringify({ currentPassword, newPassword })
|
|
4044
4524
|
});
|
|
4045
4525
|
}
|
|
@@ -4047,7 +4527,8 @@ var CustomerAuth = class {
|
|
|
4047
4527
|
* Get the authenticated customer's orders with pagination and optional status filter
|
|
4048
4528
|
*/
|
|
4049
4529
|
async getMyOrders(options) {
|
|
4050
|
-
|
|
4530
|
+
const token = this.getToken();
|
|
4531
|
+
if (!token) throw new ApiError("Not authenticated", 401);
|
|
4051
4532
|
const params = new URLSearchParams();
|
|
4052
4533
|
if (options?.page) params.set("page", String(options.page));
|
|
4053
4534
|
if (options?.limit) params.set("limit", String(options.limit));
|
|
@@ -4069,21 +4550,27 @@ var CustomerAuth = class {
|
|
|
4069
4550
|
`/api/customers/me/orders${qs ? `?${qs}` : ""}`,
|
|
4070
4551
|
{
|
|
4071
4552
|
method: "GET",
|
|
4072
|
-
headers: { Authorization: `Bearer ${
|
|
4553
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
4073
4554
|
}
|
|
4074
4555
|
);
|
|
4075
4556
|
return validatePublicOrderList(result);
|
|
4076
4557
|
}
|
|
4077
4558
|
/**
|
|
4078
|
-
* Get the current token (or null if not authenticated)
|
|
4559
|
+
* Get the current token (or null if not authenticated).
|
|
4560
|
+
*
|
|
4561
|
+
* In persisted mode this re-reads shared storage first, so a rotation or
|
|
4562
|
+
* logout performed by another tab is observed before the token is used.
|
|
4079
4563
|
*/
|
|
4080
4564
|
getToken() {
|
|
4565
|
+
this.syncFromPersistedStorage();
|
|
4081
4566
|
return this.token;
|
|
4082
4567
|
}
|
|
4083
4568
|
/**
|
|
4084
|
-
* Get the current refresh token (or null if unavailable)
|
|
4569
|
+
* Get the current refresh token (or null if unavailable). Persisted mode
|
|
4570
|
+
* re-reads shared storage first, like {@link CustomerAuth.getToken}.
|
|
4085
4571
|
*/
|
|
4086
4572
|
getRefreshToken() {
|
|
4573
|
+
this.syncFromPersistedStorage();
|
|
4087
4574
|
return this.refreshTokenValue;
|
|
4088
4575
|
}
|
|
4089
4576
|
/**
|
|
@@ -4091,6 +4578,7 @@ var CustomerAuth = class {
|
|
|
4091
4578
|
*/
|
|
4092
4579
|
setToken(token) {
|
|
4093
4580
|
this.token = token;
|
|
4581
|
+
this.commitPersistedSession({ token });
|
|
4094
4582
|
this.onTokenChange?.(token);
|
|
4095
4583
|
}
|
|
4096
4584
|
/**
|
|
@@ -4098,13 +4586,14 @@ var CustomerAuth = class {
|
|
|
4098
4586
|
*/
|
|
4099
4587
|
setRefreshToken(refreshToken) {
|
|
4100
4588
|
this.refreshTokenValue = refreshToken;
|
|
4589
|
+
this.commitPersistedSession({ refreshToken });
|
|
4101
4590
|
this.onRefreshTokenChange?.(refreshToken);
|
|
4102
4591
|
}
|
|
4103
4592
|
/**
|
|
4104
4593
|
* Check if the customer is currently authenticated
|
|
4105
4594
|
*/
|
|
4106
4595
|
isAuthenticated() {
|
|
4107
|
-
return this.
|
|
4596
|
+
return this.getToken() !== null;
|
|
4108
4597
|
}
|
|
4109
4598
|
/**
|
|
4110
4599
|
* Internal: make a request with timeout and error handling.
|
|
@@ -4454,8 +4943,12 @@ var PaymentSessionApi = class extends CustomerScopedApi {
|
|
|
4454
4943
|
...Object.keys(headers).length > 0 && { headers }
|
|
4455
4944
|
});
|
|
4456
4945
|
}
|
|
4457
|
-
async paymentSession(result, expectedProvider) {
|
|
4458
|
-
return validatePublicPaymentSession(
|
|
4946
|
+
async paymentSession(result, expectedProvider, operation2) {
|
|
4947
|
+
return validatePublicPaymentSession(
|
|
4948
|
+
await result,
|
|
4949
|
+
expectedProvider,
|
|
4950
|
+
operation2
|
|
4951
|
+
);
|
|
4459
4952
|
}
|
|
4460
4953
|
create(params) {
|
|
4461
4954
|
const { idempotencyKey, ...body } = params;
|
|
@@ -4502,7 +4995,8 @@ var PaymentSessionApi = class extends CustomerScopedApi {
|
|
|
4502
4995
|
const endpoint = this.serverTransport ? COMMERCE_ENDPOINTS.paymentSessions.server.cancel : COMMERCE_ENDPOINTS.paymentSessions.cancel;
|
|
4503
4996
|
return this.paymentSession(
|
|
4504
4997
|
this.post(endpoint, body, idempotencyKey),
|
|
4505
|
-
"toss"
|
|
4998
|
+
"toss",
|
|
4999
|
+
"cancel"
|
|
4506
5000
|
);
|
|
4507
5001
|
}
|
|
4508
5002
|
cancelPortOne(params) {
|
|
@@ -4511,7 +5005,8 @@ var PaymentSessionApi = class extends CustomerScopedApi {
|
|
|
4511
5005
|
const endpoint = this.serverTransport ? COMMERCE_ENDPOINTS.paymentSessions.server.cancel : COMMERCE_ENDPOINTS.paymentSessions.cancel;
|
|
4512
5006
|
return this.paymentSession(
|
|
4513
5007
|
this.post(endpoint, body, idempotencyKey),
|
|
4514
|
-
"portone"
|
|
5008
|
+
"portone",
|
|
5009
|
+
"cancel"
|
|
4515
5010
|
);
|
|
4516
5011
|
}
|
|
4517
5012
|
confirm(params) {
|
|
@@ -6952,7 +7447,11 @@ var COMMERCE_NOTIFICATION_EVENTS = [
|
|
|
6952
7447
|
"returnCanceled",
|
|
6953
7448
|
"returnCompleted",
|
|
6954
7449
|
"paymentReconciliationRequired",
|
|
6955
|
-
"paymentRefundRequiresAttention"
|
|
7450
|
+
"paymentRefundRequiresAttention",
|
|
7451
|
+
"membershipActivated",
|
|
7452
|
+
"membershipPastDue",
|
|
7453
|
+
"membershipExpired",
|
|
7454
|
+
"membershipCancelled"
|
|
6956
7455
|
];
|
|
6957
7456
|
function isStringOrNumber(value) {
|
|
6958
7457
|
return typeof value === "string" || typeof value === "number";
|
|
@@ -6982,7 +7481,7 @@ function isCommerceNotificationEventName(value) {
|
|
|
6982
7481
|
return typeof value === "string" && COMMERCE_NOTIFICATION_EVENTS.includes(value);
|
|
6983
7482
|
}
|
|
6984
7483
|
function isCommerceNotificationSourceCollection(value) {
|
|
6985
|
-
return value === "orders" || value === "fulfillments" || value === "returns" || value === "payment-sessions" || value === "payment-refund-attempts";
|
|
7484
|
+
return value === "orders" || value === "fulfillments" || value === "returns" || value === "payment-sessions" || value === "payment-refund-attempts" || value === "customer-memberships";
|
|
6986
7485
|
}
|
|
6987
7486
|
function isCommerceNotificationData(value) {
|
|
6988
7487
|
if (!isRecord(value)) return false;
|
|
@@ -7002,6 +7501,10 @@ function isPaymentIncidentNotificationData(value, event) {
|
|
|
7002
7501
|
const expectedReason = reconciliation ? "orderless_capture" : "local_commit_ambiguous";
|
|
7003
7502
|
return value.source?.collection === expectedCollection && typeof value.source.id === "string" && value.source.id.length > 0 && typeof value.paymentSessionId === "string" && value.paymentSessionId.length > 0 && isRecord(value.providerObject) && typeof value.providerObject.provider === "string" && value.providerObject.provider.length > 0 && (value.providerObject.kind === "payment" || value.providerObject.kind === "refund") && typeof value.providerObject.id === "string" && value.providerObject.id.length > 0 && isRecord(value.money) && Number.isSafeInteger(value.money.amountMinor) && Number(value.money.amountMinor) > 0 && typeof value.money.currency === "string" && value.money.currency.length > 0 && value.status === expectedStatus && value.reason === expectedReason && typeof value.observedAt === "string" && value.observedAt.length > 0 && !Number.isNaN(Date.parse(value.observedAt)) && (value.checkoutId === void 0 || typeof value.checkoutId === "string" && value.checkoutId.length > 0) && (value.orderId === void 0 || typeof value.orderId === "string" && value.orderId.length > 0) && (reconciliation ? value.source.id === value.paymentSessionId && value.providerObject.kind === "payment" && value.refundAttemptId === void 0 : typeof value.refundAttemptId === "string" && value.refundAttemptId.length > 0 && value.source.id === value.refundAttemptId);
|
|
7004
7503
|
}
|
|
7504
|
+
function isMembershipNotificationData(value, event) {
|
|
7505
|
+
const expectedStatus = event === "membershipActivated" ? "active" : event === "membershipPastDue" ? "past_due" : event === "membershipExpired" ? "expired" : "cancelled";
|
|
7506
|
+
return typeof value.membershipId === "string" && value.membershipId.length > 0 && typeof value.planId === "string" && value.planId.length > 0 && typeof value.customerId === "string" && value.customerId.length > 0 && typeof value.fromStatus === "string" && value.fromStatus.length > 0 && value.fromStatus !== value.toStatus && value.toStatus === expectedStatus && typeof value.occurredAt === "string" && !Number.isNaN(Date.parse(value.occurredAt));
|
|
7507
|
+
}
|
|
7005
7508
|
function isWebhookCommerceNotificationChange(value) {
|
|
7006
7509
|
if (!isRecord(value)) return false;
|
|
7007
7510
|
return value.type === "notification" && value.source === "commerce-notifications" && isCommerceNotificationEventName(value.event) && isCommerceNotificationSourceCollection(value.sourceCollection) && isStringOrNumber(value.sourceId);
|
|
@@ -7050,6 +7553,9 @@ function isCommerceNotificationWebhookEvent(event) {
|
|
|
7050
7553
|
const notificationSourceId = notification.event === "paymentReconciliationRequired" ? notification.paymentSessionId : notification.refundAttemptId;
|
|
7051
7554
|
return sourceCollection === expectedCollection && typeof notification.paymentSessionId === "string" && notification.paymentSessionId === incidentData.paymentSessionId && typeof notificationSourceId === "string" && notificationSourceId === incidentData.source.id && matchesOptionalId(changeSourceId, incidentData.source.id);
|
|
7052
7555
|
}
|
|
7556
|
+
if (notification.event === "membershipActivated" || notification.event === "membershipPastDue" || notification.event === "membershipExpired" || notification.event === "membershipCancelled") {
|
|
7557
|
+
return sourceCollection === "customer-memberships" && isMembershipNotificationData(data, notification.event) && typeof notification.membershipId === "string" && notification.membershipId === data.membershipId && matchesOptionalId(changeSourceId, data.membershipId);
|
|
7558
|
+
}
|
|
7053
7559
|
if (notification.event === "orderPaid" || notification.event === "orderCanceled" || notification.event === "orderDelivered") {
|
|
7054
7560
|
return sourceCollection === "orders" && typeof notification.orderId === "string" && notification.orderId.length > 0 && matchesOptionalId(data.orderId, sourceId) && matchesOptionalId(notification.orderId, sourceId) && matchesOptionalId(data.orderId, notification.orderId) && matchesOptionalId(changeSourceId, data.orderId ?? notification.orderId);
|
|
7055
7561
|
}
|
|
@@ -7146,9 +7652,7 @@ var INTERNAL_COLLECTIONS = [
|
|
|
7146
7652
|
"audiences",
|
|
7147
7653
|
"email-logs",
|
|
7148
7654
|
"api-usage",
|
|
7149
|
-
"tenant-
|
|
7150
|
-
"tenant-web-analytics-config",
|
|
7151
|
-
"analytics-event-schemas",
|
|
7655
|
+
"tenant-mcp-usage-daily",
|
|
7152
7656
|
"subscriptions",
|
|
7153
7657
|
"billing-history",
|
|
7154
7658
|
"usage-rate-cards",
|
|
@@ -7167,6 +7671,8 @@ var INTERNAL_COLLECTIONS = [
|
|
|
7167
7671
|
"fulfillment-events",
|
|
7168
7672
|
"tenant-commerce-config",
|
|
7169
7673
|
"customer-oauth-clients",
|
|
7674
|
+
"customer-consent-receipts",
|
|
7675
|
+
"customer-group-grants",
|
|
7170
7676
|
"api-keys",
|
|
7171
7677
|
"personal-access-tokens",
|
|
7172
7678
|
"tenant-entitlements",
|
|
@@ -7184,6 +7690,7 @@ var INTERNAL_COLLECTIONS = [
|
|
|
7184
7690
|
"event-admission-types",
|
|
7185
7691
|
"customer-payment-methods",
|
|
7186
7692
|
"tenant-payment-credentials",
|
|
7693
|
+
"membership-unapplied-paid-evidence",
|
|
7187
7694
|
"commerce-payment-accounts",
|
|
7188
7695
|
"payment-provider-customer-aliases"
|
|
7189
7696
|
];
|
|
@@ -7566,200 +8073,6 @@ function getVideoMp4Url(playbackId, resolution = "high") {
|
|
|
7566
8073
|
return `${MUX_STREAM_BASE}/${playbackId}/${resolution}.mp4`;
|
|
7567
8074
|
}
|
|
7568
8075
|
|
|
7569
|
-
// src/analytics.ts
|
|
7570
|
-
function createAnalytics(config) {
|
|
7571
|
-
if (typeof window === "undefined") {
|
|
7572
|
-
return { pageview() {
|
|
7573
|
-
}, track() {
|
|
7574
|
-
}, destroy() {
|
|
7575
|
-
} };
|
|
7576
|
-
}
|
|
7577
|
-
const endpoint = config.endpoint ?? `${resolveApiUrl()}/api/analytics/collect`;
|
|
7578
|
-
const respectDnt = config.respectDnt !== false;
|
|
7579
|
-
function isDntActive() {
|
|
7580
|
-
if (!respectDnt) return false;
|
|
7581
|
-
const nav = navigator;
|
|
7582
|
-
return nav.doNotTrack === "1" || nav.globalPrivacyControl === true;
|
|
7583
|
-
}
|
|
7584
|
-
const isLocalHost = (() => {
|
|
7585
|
-
try {
|
|
7586
|
-
const h = location.hostname;
|
|
7587
|
-
return h === "localhost" || h === "127.0.0.1" || h.endsWith(".local");
|
|
7588
|
-
} catch {
|
|
7589
|
-
return false;
|
|
7590
|
-
}
|
|
7591
|
-
})();
|
|
7592
|
-
const mode = config.mode ?? "auto";
|
|
7593
|
-
const sendSuppressed = mode === "development" || mode === "auto" && isLocalHost;
|
|
7594
|
-
let suppressNoticeShown = false;
|
|
7595
|
-
function devSuppressNotice() {
|
|
7596
|
-
if (suppressNoticeShown) return;
|
|
7597
|
-
suppressNoticeShown = true;
|
|
7598
|
-
try {
|
|
7599
|
-
console.info(
|
|
7600
|
-
`[01 analytics] events disabled on local host (mode: '${mode}'). Pass mode: 'production' to send while developing.`
|
|
7601
|
-
);
|
|
7602
|
-
} catch {
|
|
7603
|
-
}
|
|
7604
|
-
}
|
|
7605
|
-
let lastPath = null;
|
|
7606
|
-
let lastAt = 0;
|
|
7607
|
-
const autoTrack = config.autoTrack !== false;
|
|
7608
|
-
const originalPushState = history.pushState;
|
|
7609
|
-
const originalReplaceState = history.replaceState;
|
|
7610
|
-
let destroyed = false;
|
|
7611
|
-
function newEventId() {
|
|
7612
|
-
return typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : String(Date.now()) + String(Math.random());
|
|
7613
|
-
}
|
|
7614
|
-
function sendBeaconOrFetch(body) {
|
|
7615
|
-
try {
|
|
7616
|
-
if (typeof navigator.sendBeacon === "function") {
|
|
7617
|
-
const blob = new Blob([body], { type: "text/plain" });
|
|
7618
|
-
const sent = navigator.sendBeacon(endpoint, blob);
|
|
7619
|
-
if (sent) return;
|
|
7620
|
-
}
|
|
7621
|
-
fetch(endpoint, {
|
|
7622
|
-
method: "POST",
|
|
7623
|
-
keepalive: true,
|
|
7624
|
-
headers: { "Content-Type": "application/json" },
|
|
7625
|
-
body
|
|
7626
|
-
}).catch(() => {
|
|
7627
|
-
});
|
|
7628
|
-
} catch {
|
|
7629
|
-
}
|
|
7630
|
-
}
|
|
7631
|
-
function sendPageview(pathname) {
|
|
7632
|
-
if (isDntActive()) return;
|
|
7633
|
-
const doc = document;
|
|
7634
|
-
if (doc.prerendering === true || document.visibilityState === "prerender") return;
|
|
7635
|
-
if (sendSuppressed) {
|
|
7636
|
-
devSuppressNotice();
|
|
7637
|
-
return;
|
|
7638
|
-
}
|
|
7639
|
-
const now = Date.now();
|
|
7640
|
-
if (pathname === lastPath && now - lastAt < 500) return;
|
|
7641
|
-
lastPath = pathname;
|
|
7642
|
-
lastAt = now;
|
|
7643
|
-
const body = JSON.stringify({
|
|
7644
|
-
publishableKey: config.publishableKey,
|
|
7645
|
-
pathname,
|
|
7646
|
-
referrer: document.referrer || "",
|
|
7647
|
-
eventId: newEventId(),
|
|
7648
|
-
eventTs: Date.now()
|
|
7649
|
-
});
|
|
7650
|
-
sendBeaconOrFetch(body);
|
|
7651
|
-
}
|
|
7652
|
-
function trackCurrentPath() {
|
|
7653
|
-
if (destroyed) return;
|
|
7654
|
-
sendPageview(location.pathname);
|
|
7655
|
-
}
|
|
7656
|
-
function patchedPushState(data, unused, url) {
|
|
7657
|
-
originalPushState.apply(this, [data, unused, url]);
|
|
7658
|
-
if (!destroyed) setTimeout(trackCurrentPath, 0);
|
|
7659
|
-
}
|
|
7660
|
-
function patchedReplaceState(data, unused, url) {
|
|
7661
|
-
originalReplaceState.apply(this, [data, unused, url]);
|
|
7662
|
-
if (!destroyed) setTimeout(trackCurrentPath, 0);
|
|
7663
|
-
}
|
|
7664
|
-
if (autoTrack) {
|
|
7665
|
-
history.pushState = patchedPushState;
|
|
7666
|
-
history.replaceState = patchedReplaceState;
|
|
7667
|
-
window.addEventListener("popstate", trackCurrentPath);
|
|
7668
|
-
if (document.readyState === "complete") {
|
|
7669
|
-
trackCurrentPath();
|
|
7670
|
-
} else {
|
|
7671
|
-
window.addEventListener("load", trackCurrentPath, { once: true });
|
|
7672
|
-
}
|
|
7673
|
-
}
|
|
7674
|
-
const isProduction = !isLocalHost;
|
|
7675
|
-
const warnedReasons = /* @__PURE__ */ new Set();
|
|
7676
|
-
function devWarn(name, reason) {
|
|
7677
|
-
if (isProduction) return;
|
|
7678
|
-
if (warnedReasons.has(reason)) return;
|
|
7679
|
-
warnedReasons.add(reason);
|
|
7680
|
-
console.warn(`[01 analytics] dropped event ${name}: ${reason}`);
|
|
7681
|
-
}
|
|
7682
|
-
const EVENT_NAME_RE = /^[a-zA-Z][a-zA-Z0-9_:-]{0,49}$/;
|
|
7683
|
-
const RESERVED_PREFIXES = ["__", "_pv_"];
|
|
7684
|
-
function validateEventName(name) {
|
|
7685
|
-
if (!name || typeof name !== "string") return "name-empty";
|
|
7686
|
-
for (const prefix of RESERVED_PREFIXES) {
|
|
7687
|
-
if (name.startsWith(prefix)) return "name-reserved";
|
|
7688
|
-
}
|
|
7689
|
-
if (!EVENT_NAME_RE.test(name)) return "name-regex";
|
|
7690
|
-
return null;
|
|
7691
|
-
}
|
|
7692
|
-
const PROP_KEY_RE = /^[a-zA-Z_][a-zA-Z0-9_]{0,31}$/;
|
|
7693
|
-
function validateEventProps(props) {
|
|
7694
|
-
if (props === void 0 || props === null) return null;
|
|
7695
|
-
if (typeof props !== "object" || Array.isArray(props)) return "props-value-type";
|
|
7696
|
-
const keys = Object.keys(props);
|
|
7697
|
-
if (keys.length > 10) return "props-too-many-keys";
|
|
7698
|
-
for (const k of keys) {
|
|
7699
|
-
const v = props[k];
|
|
7700
|
-
if (!PROP_KEY_RE.test(k)) return "props-key-regex";
|
|
7701
|
-
if (typeof v === "string") {
|
|
7702
|
-
if (v.length > 80) return "props-value-too-long";
|
|
7703
|
-
} else if (typeof v === "number") {
|
|
7704
|
-
if (!isFinite(v)) return "props-value-not-finite";
|
|
7705
|
-
} else if (typeof v === "boolean") {
|
|
7706
|
-
} else {
|
|
7707
|
-
return "props-value-type";
|
|
7708
|
-
}
|
|
7709
|
-
}
|
|
7710
|
-
return null;
|
|
7711
|
-
}
|
|
7712
|
-
return {
|
|
7713
|
-
pageview(path) {
|
|
7714
|
-
if (destroyed) return;
|
|
7715
|
-
sendPageview(path ?? location.pathname);
|
|
7716
|
-
},
|
|
7717
|
-
track(name, props) {
|
|
7718
|
-
if (destroyed) return;
|
|
7719
|
-
if (isDntActive()) return;
|
|
7720
|
-
const doc = document;
|
|
7721
|
-
if (doc.prerendering === true || document.visibilityState === "prerender") return;
|
|
7722
|
-
const nameErr = validateEventName(name);
|
|
7723
|
-
if (nameErr) {
|
|
7724
|
-
devWarn(name, nameErr);
|
|
7725
|
-
return;
|
|
7726
|
-
}
|
|
7727
|
-
if (props !== void 0) {
|
|
7728
|
-
const propsErr = validateEventProps(props);
|
|
7729
|
-
if (propsErr) {
|
|
7730
|
-
devWarn(name, propsErr);
|
|
7731
|
-
return;
|
|
7732
|
-
}
|
|
7733
|
-
}
|
|
7734
|
-
if (sendSuppressed) {
|
|
7735
|
-
devSuppressNotice();
|
|
7736
|
-
return;
|
|
7737
|
-
}
|
|
7738
|
-
const body = JSON.stringify({
|
|
7739
|
-
publishableKey: config.publishableKey,
|
|
7740
|
-
pathname: location.pathname,
|
|
7741
|
-
referrer: document.referrer || "",
|
|
7742
|
-
eventId: newEventId(),
|
|
7743
|
-
eventName: name,
|
|
7744
|
-
eventProps: props,
|
|
7745
|
-
eventTs: Date.now()
|
|
7746
|
-
});
|
|
7747
|
-
sendBeaconOrFetch(body);
|
|
7748
|
-
},
|
|
7749
|
-
destroy() {
|
|
7750
|
-
if (destroyed) return;
|
|
7751
|
-
destroyed = true;
|
|
7752
|
-
if (autoTrack) {
|
|
7753
|
-
history.pushState = originalPushState;
|
|
7754
|
-
history.replaceState = originalReplaceState;
|
|
7755
|
-
window.removeEventListener("popstate", trackCurrentPath);
|
|
7756
|
-
}
|
|
7757
|
-
lastPath = null;
|
|
7758
|
-
lastAt = 0;
|
|
7759
|
-
}
|
|
7760
|
-
};
|
|
7761
|
-
}
|
|
7762
|
-
|
|
7763
8076
|
// src/index.ts
|
|
7764
8077
|
function createClient2(options) {
|
|
7765
8078
|
return createClient(options);
|