@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.
Files changed (67) hide show
  1. package/CHANGELOG.md +313 -0
  2. package/README.md +228 -102
  3. package/dist/client.cjs +556 -60
  4. package/dist/client.cjs.map +1 -1
  5. package/dist/client.d.cts +6 -6
  6. package/dist/client.d.ts +6 -6
  7. package/dist/client.js +556 -60
  8. package/dist/client.js.map +1 -1
  9. package/dist/{collection-client-CWAeuzV4.d.cts → collection-client-B7rsTtVj.d.cts} +6 -6
  10. package/dist/{collection-client-hYvVfHIm.d.ts → collection-client-C2sMvfme.d.ts} +6 -6
  11. package/dist/{const-JD8obaKg.d.ts → const-CRxPTDGt.d.ts} +2 -2
  12. package/dist/{const-D8LixhAJ.d.cts → const-D6ZRQLxI.d.cts} +2 -2
  13. package/dist/{index-B0Hjh3Ey.d.ts → index-1auUEYrl.d.ts} +24 -7
  14. package/dist/{index-Bu0kCc6o.d.cts → index-Bbpf5t-l.d.cts} +24 -7
  15. package/dist/index.cjs +573 -260
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +19 -12
  18. package/dist/index.d.ts +19 -12
  19. package/dist/index.js +573 -260
  20. package/dist/index.js.map +1 -1
  21. package/dist/{payload-types-BOsckg8B.d.cts → payload-types-BnIET0xU.d.cts} +567 -438
  22. package/dist/{payload-types-BOsckg8B.d.ts → payload-types-BnIET0xU.d.ts} +567 -438
  23. package/dist/query.cjs +62 -3
  24. package/dist/query.cjs.map +1 -1
  25. package/dist/query.d.cts +5 -5
  26. package/dist/query.d.ts +5 -5
  27. package/dist/query.js +62 -3
  28. package/dist/query.js.map +1 -1
  29. package/dist/realtime.d.cts +2 -2
  30. package/dist/realtime.d.ts +2 -2
  31. package/dist/server.cjs +557 -20
  32. package/dist/server.cjs.map +1 -1
  33. package/dist/server.d.cts +14 -8
  34. package/dist/server.d.ts +14 -8
  35. package/dist/server.js +557 -20
  36. package/dist/server.js.map +1 -1
  37. package/dist/{types-5ZlcAFuU.d.ts → types-D1y4TgQm.d.ts} +2 -2
  38. package/dist/{types-Bj8j7VHU.d.ts → types-DnRC3nLG.d.ts} +464 -16
  39. package/dist/{types-B14sEt1z.d.cts → types-Dp1oYpD3.d.cts} +2 -2
  40. package/dist/{types-CM5qiR2k.d.cts → types-Dzn0LD2N.d.cts} +464 -16
  41. package/dist/ui/form.d.cts +1 -1
  42. package/dist/ui/form.d.ts +1 -1
  43. package/dist/ui/video.cjs +4 -2
  44. package/dist/ui/video.cjs.map +1 -1
  45. package/dist/ui/video.d.cts +4 -2
  46. package/dist/ui/video.d.ts +4 -2
  47. package/dist/ui/video.js +4 -2
  48. package/dist/ui/video.js.map +1 -1
  49. package/dist/webhook.cjs +13 -2
  50. package/dist/webhook.cjs.map +1 -1
  51. package/dist/webhook.d.cts +5 -5
  52. package/dist/webhook.d.ts +5 -5
  53. package/dist/webhook.js +13 -2
  54. package/dist/webhook.js.map +1 -1
  55. package/package.json +9 -28
  56. package/dist/analytics/react.cjs +0 -344
  57. package/dist/analytics/react.cjs.map +0 -1
  58. package/dist/analytics/react.d.cts +0 -24
  59. package/dist/analytics/react.d.ts +0 -24
  60. package/dist/analytics/react.js +0 -319
  61. package/dist/analytics/react.js.map +0 -1
  62. package/dist/analytics.cjs +0 -238
  63. package/dist/analytics.cjs.map +0 -1
  64. package/dist/analytics.d.cts +0 -39
  65. package/dist/analytics.d.ts +0 -39
  66. package/dist/analytics.js +0 -215
  67. package/dist/analytics.js.map +0 -1
package/dist/index.js CHANGED
@@ -1005,6 +1005,49 @@ function stockSnapshotSearchParams(params) {
1005
1005
  }
1006
1006
 
1007
1007
  // src/core/collection/http-client.ts
1008
+ function validateWhere(where) {
1009
+ if (where === void 0) return false;
1010
+ let hasNull = false;
1011
+ function visit(value, depth, key) {
1012
+ if (depth > 10) {
1013
+ throw createValidationError(
1014
+ "Collection where must not exceed nesting depth 10."
1015
+ );
1016
+ }
1017
+ if (value === null && (key === "equals" || key === "not_equals")) {
1018
+ hasNull = true;
1019
+ return;
1020
+ }
1021
+ if (value == null) {
1022
+ throw createValidationError(
1023
+ "Collection where must not contain null or undefined values."
1024
+ );
1025
+ }
1026
+ if (Array.isArray(value)) {
1027
+ if (value.length === 0) {
1028
+ throw createValidationError(
1029
+ "Collection where must not contain empty arrays, including and/or."
1030
+ );
1031
+ }
1032
+ if (value.length > 1e3) {
1033
+ throw createValidationError(
1034
+ "Collection where arrays must not contain more than 1000 elements."
1035
+ );
1036
+ }
1037
+ for (const item of value) visit(item, depth + 1);
1038
+ } else if (typeof value === "object" && !(value instanceof Date)) {
1039
+ const values = Object.entries(value);
1040
+ if (values.length === 0) {
1041
+ throw createValidationError(
1042
+ "Collection where must not contain empty condition objects."
1043
+ );
1044
+ }
1045
+ for (const [key2, item] of values) visit(item, depth + 1, key2);
1046
+ }
1047
+ }
1048
+ visit(where, 0);
1049
+ return hasNull;
1050
+ }
1008
1051
  var HttpClient = class {
1009
1052
  constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId, apiUrl) {
1010
1053
  this.publishableKey = requirePublishableKeyForSecret(
@@ -1051,7 +1094,8 @@ var HttpClient = class {
1051
1094
  ...rest,
1052
1095
  "fallback-locale": fallbackLocale === false ? "none" : fallbackLocale
1053
1096
  };
1054
- const queryString = stringify(queryOptions, { addQueryPrefix: true });
1097
+ const encodedOptions = validateWhere(queryOptions.where) ? { ...queryOptions, where: JSON.stringify(queryOptions.where) } : queryOptions;
1098
+ const queryString = stringify(encodedOptions, { addQueryPrefix: true });
1055
1099
  return queryString ? `${endpoint}${queryString}` : endpoint;
1056
1100
  }
1057
1101
  assertJsonResponse(response) {
@@ -1360,6 +1404,11 @@ var COMMERCE_ENDPOINT_OPERATIONS = {
1360
1404
  detail: strictCommerceOperation("checkouts", "post", "/detail"),
1361
1405
  query: strictCommerceOperation("checkouts", "post", "/query")
1362
1406
  },
1407
+ draftOrders: {
1408
+ calculate: operation("draft-orders", "post", "/:id/calculate"),
1409
+ complete: operation("draft-orders", "post", "/:id/complete"),
1410
+ finalize: operation("draft-orders", "post", "/:id/finalize")
1411
+ },
1363
1412
  transactions: {
1364
1413
  update: operation("transactions", "post", "/update")
1365
1414
  },
@@ -1459,8 +1508,51 @@ var portOneStandardActionCapabilityOperations = /* @__PURE__ */ new Set([
1459
1508
  ]);
1460
1509
  var MEMBERSHIP_ENDPOINT_OPERATIONS = {
1461
1510
  subscribe: operation(null, "post", "/memberships/subscribe"),
1511
+ billingCustody: operation(null, "post", "/memberships/billing-custody"),
1462
1512
  cancel: operation(null, "post", "/memberships/:id/cancel"),
1463
- me: operation(null, "get", "/memberships/me")
1513
+ me: operation(null, "get", "/memberships/me"),
1514
+ chargeResult: operation(null, "post", "/memberships/:id/charge-result"),
1515
+ refundResult: operation(null, "post", "/memberships/:id/refund-result"),
1516
+ revoke: operation(null, "post", "/memberships/:id/revoke")
1517
+ };
1518
+ var EXTERNAL_EFFECT_CLAIM_ENDPOINT_OPERATIONS = {
1519
+ claim: operation(null, "post", "/external-effect-claims/claim"),
1520
+ get: operation(null, "post", "/external-effect-claims/get"),
1521
+ arm: operation(null, "post", "/external-effect-claims/arm"),
1522
+ release: operation(null, "post", "/external-effect-claims/release"),
1523
+ report: operation(null, "post", "/external-effect-claims/report"),
1524
+ recovery: operation(null, "post", "/external-effect-claims/recovery")
1525
+ };
1526
+ var INSTALLATION_CREDENTIAL_ENDPOINT_OPERATIONS = {
1527
+ create: operation(null, "post", "/installation-credentials/create"),
1528
+ get: operation(null, "post", "/installation-credentials/get"),
1529
+ replace: operation(null, "post", "/installation-credentials/replace"),
1530
+ dispose: operation(null, "post", "/installation-credentials/dispose")
1531
+ };
1532
+ var EXTERNAL_CUSTOMER_LINK_ENDPOINT_OPERATIONS = {
1533
+ connect: operation("customers", "post", "/external-links/connect"),
1534
+ get: operation("customers", "post", "/external-links/get"),
1535
+ list: operation("customers", "post", "/external-links/list"),
1536
+ unlink: operation("customers", "post", "/external-links/unlink"),
1537
+ capturePreviousState: operation(
1538
+ "customers",
1539
+ "post",
1540
+ "/external-links/capture-previous-state"
1541
+ ),
1542
+ clearPreviousState: operation(
1543
+ "customers",
1544
+ "post",
1545
+ "/external-links/clear-previous-state"
1546
+ )
1547
+ };
1548
+ var CUSTOMER_GROUP_ENDPOINT_OPERATIONS = {
1549
+ members: operation("customer-groups", "get", "/:id/members"),
1550
+ grant: operation("customer-groups", "post", "/:id/members/grant"),
1551
+ revoke: operation("customer-groups", "post", "/:id/members/revoke")
1552
+ };
1553
+ var CUSTOMER_AUTH_ENDPOINT_OPERATIONS = {
1554
+ revokeRefresh: operation("customers", "post", "/revoke-refresh"),
1555
+ withdraw: operation("customers", "post", "/withdraw")
1464
1556
  };
1465
1557
  var EMBEDDED_ADMIN_ENDPOINT_OPERATIONS = {
1466
1558
  show: operation("tenants", "get", "/embedded-admin-app"),
@@ -1485,6 +1577,14 @@ var COMMUNITY_ENDPOINT_OPERATIONS = {
1485
1577
  detail: operation("posts", "get", "/detail")
1486
1578
  }
1487
1579
  };
1580
+ var GALLERY_ENDPOINT_OPERATIONS = {
1581
+ items: {
1582
+ storefront: operation("gallery-items", "get", "/storefront")
1583
+ }
1584
+ };
1585
+ var VIDEO_ENDPOINT_OPERATIONS = {
1586
+ playback: operation("videos", "post", "/:id/playback")
1587
+ };
1488
1588
  function resolveOperationTemplate(descriptor) {
1489
1589
  return descriptor.collection ? `/api/${descriptor.collection}${descriptor.path}` : `/api${descriptor.path}`;
1490
1590
  }
@@ -2074,6 +2174,15 @@ var PublicContentApi = class extends RawCustomerScopedApi {
2074
2174
  get(endpoint) {
2075
2175
  return this.request(endpoint, { method: "GET" });
2076
2176
  }
2177
+ galleryItems(options) {
2178
+ const operation2 = GALLERY_ENDPOINT_OPERATIONS.items.storefront;
2179
+ return this.request(
2180
+ resolveOperationPath(operation2, {
2181
+ query: galleryItemsListQuery(options)
2182
+ }),
2183
+ { method: resolveOperationMethod(operation2) }
2184
+ );
2185
+ }
2077
2186
  };
2078
2187
  function appendCommonParams(search, options, defaultSort) {
2079
2188
  search.set("sort", options?.sort ?? defaultSort);
@@ -2093,12 +2202,12 @@ function linksListPath(options) {
2093
2202
  search.set("featured", String(options.featured));
2094
2203
  return `/api/links/storefront?${search}`;
2095
2204
  }
2096
- function galleryItemsListPath(options) {
2205
+ function galleryItemsListQuery(options) {
2097
2206
  const search = new URLSearchParams();
2098
2207
  appendCommonParams(search, options, "manual");
2099
2208
  if (options?.galleryId) search.set("galleryId", options.galleryId);
2100
2209
  if (options?.gallerySlug) search.set("gallerySlug", options.gallerySlug);
2101
- return `/api/gallery-items/storefront?${search}`;
2210
+ return search;
2102
2211
  }
2103
2212
  var ContentClient = class {
2104
2213
  constructor(options) {
@@ -2112,9 +2221,7 @@ var ContentClient = class {
2112
2221
  list: (params) => api.get(linksListPath(params))
2113
2222
  };
2114
2223
  this.galleryItems = {
2115
- list: (params) => api.get(
2116
- galleryItemsListPath(params)
2117
- )
2224
+ list: (params) => api.galleryItems(params)
2118
2225
  };
2119
2226
  }
2120
2227
  };
@@ -2170,7 +2277,10 @@ var ORDER_PRIMARY_DISPLAY_STATUSES = [
2170
2277
  ];
2171
2278
  var CHECKOUT_STATUSES = ["open", "placed", "abandoned"];
2172
2279
  var ABANDONED_REASONS = ["expired", "customer", "operator"];
2173
- var PAYMENT_PROVIDERS = ["toss", "portone"];
2280
+ function managedPaymentProviders(providers) {
2281
+ return providers;
2282
+ }
2283
+ var PAYMENT_PROVIDERS = managedPaymentProviders(["toss", "portone"]);
2174
2284
  var PAYMENT_PROVIDER_STATUSES = [
2175
2285
  "requires_payment_method",
2176
2286
  "requires_action",
@@ -2218,6 +2328,26 @@ function invalidCanonicalResponse(resource, details) {
2218
2328
  function fail(resource, path, expected) {
2219
2329
  invalidCanonicalResponse(resource, { path, expected });
2220
2330
  }
2331
+ function providerMismatch(resource, expected, actual, operation2) {
2332
+ if (operation2 === "cancel") {
2333
+ const method = {
2334
+ toss: "cancel()",
2335
+ portone: "cancelPortOne()"
2336
+ }[actual];
2337
+ throw createApiError(
2338
+ `Canonical ${resource} response provider mismatch: expected ${expected}, received ${actual}.`,
2339
+ 502,
2340
+ { resource, path: "$.provider", expected, actual },
2341
+ `The server applied the cancellation to a ${actual} PaymentSession, but this SDK method expects ${expected}.`,
2342
+ `Re-invoke ${method} with the same idempotencyKey; do not create a new key.`
2343
+ );
2344
+ }
2345
+ invalidCanonicalResponse(resource, {
2346
+ path: "$.provider",
2347
+ expected,
2348
+ actual
2349
+ });
2350
+ }
2221
2351
  function record(value, resource, path) {
2222
2352
  if (!value || typeof value !== "object" || Array.isArray(value)) {
2223
2353
  fail(resource, path, "object");
@@ -3510,7 +3640,7 @@ function validateNextAction(value, provider, resource, path) {
3510
3640
  if (action.customerEmail !== void 0)
3511
3641
  validateCustomer({ email: action.customerEmail }, resource, path);
3512
3642
  }
3513
- function validatePublicPaymentSession(value, expectedProvider) {
3643
+ function validatePublicPaymentSession(value, expectedProvider, operation2) {
3514
3644
  const resource = "PaymentSession";
3515
3645
  const session = record(value, resource, "$");
3516
3646
  const provider = enumValue(
@@ -3519,9 +3649,6 @@ function validatePublicPaymentSession(value, expectedProvider) {
3519
3649
  resource,
3520
3650
  "$.provider"
3521
3651
  );
3522
- if (expectedProvider !== void 0 && provider !== expectedProvider) {
3523
- fail(resource, "$.provider", expectedProvider);
3524
- }
3525
3652
  const status = enumValue(
3526
3653
  session.providerStatus,
3527
3654
  PAYMENT_PROVIDER_STATUSES,
@@ -3725,44 +3852,128 @@ function validatePublicPaymentSession(value, expectedProvider) {
3725
3852
  "$.cancellationReason"
3726
3853
  );
3727
3854
  }
3855
+ if (expectedProvider !== void 0 && provider !== expectedProvider) {
3856
+ if (operation2 === "cancel" && status !== "canceling" && status !== "canceled") {
3857
+ fail(
3858
+ resource,
3859
+ "$.providerStatus",
3860
+ "canceling or canceled after a successful cancellation"
3861
+ );
3862
+ }
3863
+ providerMismatch(resource, expectedProvider, provider, operation2);
3864
+ }
3728
3865
  return value;
3729
3866
  }
3730
3867
 
3731
3868
  // src/core/customer/customer-auth.ts
3732
3869
  var DEFAULT_TIMEOUT2 = 15e3;
3733
- function safeGetItem(key) {
3870
+ var REFRESH_LOCK_TIMEOUT = DEFAULT_TIMEOUT2 * 3;
3871
+ var SESSION_RECORD_VERSION = 1;
3872
+ var STORAGE_UNUSABLE = /* @__PURE__ */ Symbol("storage-unusable");
3873
+ function canonicalApiIdentity(apiUrl) {
3734
3874
  try {
3735
- return localStorage.getItem(key);
3875
+ const url = new URL(apiUrl);
3876
+ return `${url.protocol.toLowerCase()}//${url.host.toLowerCase()}${url.pathname.replace(/\/+$/, "")}`;
3877
+ } catch {
3878
+ return apiUrl.trim().toLowerCase().replace(/\/+$/, "");
3879
+ }
3880
+ }
3881
+ function buildRefreshLockName(apiUrl, publishableKey, storageKey) {
3882
+ return `01software:customer-auth-refresh:${canonicalApiIdentity(apiUrl)}:${publishableKey}:${storageKey}`;
3883
+ }
3884
+ function getRefreshLockRequester() {
3885
+ try {
3886
+ if (typeof navigator === "undefined") return null;
3887
+ const locks = navigator.locks;
3888
+ if (!locks || typeof locks.request !== "function") return null;
3889
+ return (name, options, callback) => locks.request(
3890
+ name,
3891
+ options,
3892
+ () => callback()
3893
+ );
3894
+ } catch {
3895
+ return null;
3896
+ }
3897
+ }
3898
+ function parseSessionRecord(raw) {
3899
+ try {
3900
+ const parsed = JSON.parse(raw);
3901
+ if (!parsed || typeof parsed !== "object") return null;
3902
+ const candidate = parsed;
3903
+ if (candidate.v !== SESSION_RECORD_VERSION) return null;
3904
+ if (typeof candidate.rev !== "number" || !Number.isFinite(candidate.rev)) {
3905
+ return null;
3906
+ }
3907
+ const token = candidate.token;
3908
+ const refreshToken = candidate.refreshToken;
3909
+ if (token !== null && typeof token !== "string") return null;
3910
+ if (refreshToken !== null && typeof refreshToken !== "string") return null;
3911
+ return {
3912
+ v: SESSION_RECORD_VERSION,
3913
+ rev: candidate.rev,
3914
+ token,
3915
+ refreshToken
3916
+ };
3736
3917
  } catch {
3737
3918
  return null;
3738
3919
  }
3739
3920
  }
3921
+ function readSessionRecord(recordKey, mirrorKey, mirrorRefreshKey) {
3922
+ let raw;
3923
+ try {
3924
+ raw = localStorage.getItem(recordKey);
3925
+ } catch {
3926
+ return STORAGE_UNUSABLE;
3927
+ }
3928
+ if (raw !== null) {
3929
+ const parsed = parseSessionRecord(raw);
3930
+ return parsed ? { record: parsed, stored: true } : STORAGE_UNUSABLE;
3931
+ }
3932
+ try {
3933
+ return {
3934
+ record: {
3935
+ v: SESSION_RECORD_VERSION,
3936
+ rev: 0,
3937
+ token: localStorage.getItem(mirrorKey),
3938
+ refreshToken: localStorage.getItem(mirrorRefreshKey)
3939
+ },
3940
+ stored: false
3941
+ };
3942
+ } catch {
3943
+ return STORAGE_UNUSABLE;
3944
+ }
3945
+ }
3740
3946
  var CustomerAuth = class {
3741
3947
  constructor(publishableKey, options, apiUrl) {
3742
3948
  this.refreshPromise = null;
3949
+ /** Canonical record key; null unless persistence is active in a browser. */
3950
+ this.persistRecordKey = null;
3951
+ /** Advisory single-value access mirror, plus the retired refresh mirror. */
3952
+ this.persistMirrorKey = null;
3953
+ this.persistMirrorRefreshKey = null;
3954
+ /** Cross-context refresh lock name; null when there is no shared storage. */
3955
+ this.refreshLockName = null;
3956
+ /** Revision of the canonical record this instance last observed. */
3957
+ this.sessionRev = 0;
3743
3958
  this.publishableKey = publishableKey;
3744
3959
  this.baseUrl = resolveApiUrl(apiUrl);
3745
3960
  const persist = options?.persist ?? true;
3746
3961
  if (persist) {
3747
3962
  const key = typeof persist === "string" ? persist : "customer-token";
3748
- const refreshKey = `${key}:refresh`;
3749
3963
  const isBrowser = typeof window !== "undefined";
3750
- this.token = isBrowser ? safeGetItem(key) : null;
3751
- this.refreshTokenValue = isBrowser ? safeGetItem(refreshKey) : null;
3752
- this.onTokenChange = isBrowser ? (token) => {
3753
- try {
3754
- if (token) localStorage.setItem(key, token);
3755
- else localStorage.removeItem(key);
3756
- } catch {
3757
- }
3758
- } : void 0;
3759
- this.onRefreshTokenChange = isBrowser ? (refreshToken) => {
3760
- try {
3761
- if (refreshToken) localStorage.setItem(refreshKey, refreshToken);
3762
- else localStorage.removeItem(refreshKey);
3763
- } catch {
3764
- }
3765
- } : void 0;
3964
+ this.token = null;
3965
+ this.refreshTokenValue = null;
3966
+ if (isBrowser) {
3967
+ this.persistRecordKey = `${key}:session`;
3968
+ this.persistMirrorKey = key;
3969
+ this.persistMirrorRefreshKey = `${key}:refresh`;
3970
+ this.refreshLockName = buildRefreshLockName(
3971
+ this.baseUrl,
3972
+ publishableKey,
3973
+ key
3974
+ );
3975
+ this.syncFromPersistedStorage();
3976
+ }
3766
3977
  } else {
3767
3978
  this.token = options?.token ?? null;
3768
3979
  this.refreshTokenValue = options?.refreshToken ?? null;
@@ -3790,66 +4001,334 @@ var CustomerAuth = class {
3790
4001
  body: JSON.stringify(data)
3791
4002
  }
3792
4003
  );
3793
- this.setToken(result.token);
3794
- if (result.refreshToken) this.setRefreshToken(result.refreshToken);
4004
+ if (result.refreshToken) this.setSession(result.token, result.refreshToken);
4005
+ else this.setToken(result.token);
3795
4006
  return result;
3796
4007
  }
3797
4008
  /**
3798
4009
  * Refresh the current token. Uses a refresh token when available, otherwise
3799
4010
  * falls back to the legacy bearer-token compatibility path.
4011
+ *
4012
+ * Refresh tokens are single-use, so a persisted session shared by several
4013
+ * tabs or SDK instances must rotate at most once: concurrent rotations of the
4014
+ * same token are replay from the server's point of view and revoke the whole
4015
+ * family. Persisted browser sessions therefore serialize rotation across
4016
+ * contexts and re-read the canonical persisted credentials inside the lock.
3800
4017
  */
3801
4018
  async refreshToken() {
4019
+ this.syncFromPersistedStorage();
3802
4020
  if (!this.refreshTokenValue && !this.token) {
3803
4021
  throw new ApiError("Not authenticated", 401);
3804
4022
  }
3805
4023
  if (this.refreshPromise) return this.refreshPromise;
3806
- this.refreshPromise = this._doRefreshToken();
4024
+ const intended = this.currentFence();
4025
+ this.refreshPromise = this.rotateAcrossContexts(intended);
3807
4026
  try {
3808
4027
  return await this.refreshPromise;
3809
4028
  } finally {
3810
4029
  this.refreshPromise = null;
3811
4030
  }
3812
4031
  }
3813
- async _doRefreshToken() {
3814
- const init = this.refreshTokenValue ? {
4032
+ /**
4033
+ * Re-read the canonical persisted session. In persisted mode the shared
4034
+ * record — not this instance's memory — is the source of truth, so another
4035
+ * context's rotation, login, or logout is observed before the next credential
4036
+ * use. Change callbacks are deliberately not invoked: this is a read.
4037
+ */
4038
+ syncFromPersistedStorage() {
4039
+ const canonical = this.readCanonicalSession();
4040
+ if (canonical === STORAGE_UNUSABLE) {
4041
+ this.disablePersistence();
4042
+ return;
4043
+ }
4044
+ if (!canonical) return;
4045
+ this.sessionRev = canonical.record.rev;
4046
+ this.token = canonical.record.token;
4047
+ this.refreshTokenValue = canonical.record.refreshToken;
4048
+ }
4049
+ /** `null` when this instance does not persist. */
4050
+ readCanonicalSession() {
4051
+ if (!this.persistRecordKey || !this.persistMirrorKey || !this.persistMirrorRefreshKey) {
4052
+ return null;
4053
+ }
4054
+ return readSessionRecord(
4055
+ this.persistRecordKey,
4056
+ this.persistMirrorKey,
4057
+ this.persistMirrorRefreshKey
4058
+ );
4059
+ }
4060
+ /**
4061
+ * Publish a change to canonical storage.
4062
+ *
4063
+ * The canonical pair is re-read immediately before the single record write so
4064
+ * that a field this call does not change is taken from that read rather than
4065
+ * from this instance's possibly stale copy. That merge is BEST-EFFORT: it
4066
+ * removes the stale counterpart an instance would otherwise carry, but it is
4067
+ * not a compare-and-swap. Web Storage offers none, and the synchronous public
4068
+ * mutators cannot await the refresh lease, so a commit that lands between
4069
+ * this read and the store operation is lost by ordering — including the
4070
+ * counterpart just read, which is then republished stale. The window is one
4071
+ * store operation wide and the server remains authoritative; see
4072
+ * `CUST-AUTH-033` for the stated limit.
4073
+ *
4074
+ * Returns `false` only when `requireLiveSession` refused a write because the
4075
+ * canonical session had already been cleared.
4076
+ */
4077
+ commitPersistedSession(change, options) {
4078
+ const recordKey = this.persistRecordKey;
4079
+ const mirrorKey = this.persistMirrorKey;
4080
+ const mirrorRefreshKey = this.persistMirrorRefreshKey;
4081
+ const canonical = this.readCanonicalSession();
4082
+ if (canonical === null || !recordKey || !mirrorKey || !mirrorRefreshKey) {
4083
+ return true;
4084
+ }
4085
+ if (canonical === STORAGE_UNUSABLE) {
4086
+ this.disablePersistence();
4087
+ return true;
4088
+ }
4089
+ const current = canonical.record;
4090
+ if (options?.requireLiveSession && !current.token && !current.refreshToken) {
4091
+ return false;
4092
+ }
4093
+ const next = {
4094
+ v: SESSION_RECORD_VERSION,
4095
+ rev: current.rev + 1,
4096
+ token: "token" in change ? change.token ?? null : current.token,
4097
+ refreshToken: "refreshToken" in change ? change.refreshToken ?? null : current.refreshToken
4098
+ };
4099
+ if (
4100
+ // Only a state that is genuinely persisted can already say what this
4101
+ // write would say. An absent record reads as an empty pair, and treating
4102
+ // that as a no-op would drop the very tombstone that stops a later
4103
+ // migration from resurrecting the session out of the earlier layout.
4104
+ canonical.stored && next.token === current.token && next.refreshToken === current.refreshToken
4105
+ ) {
4106
+ this.sessionRev = current.rev;
4107
+ this.token = current.token;
4108
+ this.refreshTokenValue = current.refreshToken;
4109
+ return true;
4110
+ }
4111
+ try {
4112
+ localStorage.setItem(recordKey, JSON.stringify(next));
4113
+ } catch {
4114
+ this.disablePersistence();
4115
+ return true;
4116
+ }
4117
+ this.sessionRev = next.rev;
4118
+ this.token = next.token;
4119
+ this.refreshTokenValue = next.refreshToken;
4120
+ try {
4121
+ localStorage.removeItem(mirrorRefreshKey);
4122
+ } catch {
4123
+ return true;
4124
+ }
4125
+ try {
4126
+ if (next.token) localStorage.setItem(mirrorKey, next.token);
4127
+ else localStorage.removeItem(mirrorKey);
4128
+ } catch {
4129
+ }
4130
+ return true;
4131
+ }
4132
+ /**
4133
+ * Storage rejected a write or a read, so it is not a canonical store for this
4134
+ * instance. Fall back to the memory-only session and take no lease: there is
4135
+ * no shared state left to coordinate. Nothing is purged — the record commit
4136
+ * is atomic, so no inconsistent state was published, and another context's
4137
+ * valid session must not be destroyed by this instance's local failure.
4138
+ */
4139
+ disablePersistence() {
4140
+ this.persistRecordKey = null;
4141
+ this.persistMirrorKey = null;
4142
+ this.persistMirrorRefreshKey = null;
4143
+ this.refreshLockName = null;
4144
+ }
4145
+ /**
4146
+ * Compare the canonical record with the fence this call captured before it
4147
+ * left. Every persisted decision — rotate, converge, or fail closed — goes
4148
+ * through this one comparison. The revision catches a logout, a fresh login,
4149
+ * or a manual setter even when token values coincide; the value comparison
4150
+ * additionally catches a mirror-only writer that cannot bump a revision.
4151
+ */
4152
+ classifyCanonicalState(intended) {
4153
+ if (!this.persistRecordKey) return "current";
4154
+ this.syncFromPersistedStorage();
4155
+ if (!this.persistRecordKey) return "current";
4156
+ if (!this.token && !this.refreshTokenValue) return "cleared";
4157
+ if (intended.refreshToken !== null) {
4158
+ return this.refreshTokenValue === intended.refreshToken ? "current" : "superseded";
4159
+ }
4160
+ return this.token === intended.token && !this.refreshTokenValue ? "current" : "superseded";
4161
+ }
4162
+ /** The canonical persisted pair, as a refresh result. */
4163
+ canonicalPair(token) {
4164
+ return {
4165
+ token,
4166
+ ...this.refreshTokenValue ? { refreshToken: this.refreshTokenValue } : {}
4167
+ };
4168
+ }
4169
+ /** Current credentials plus the revision they were observed at. */
4170
+ currentFence() {
4171
+ return { token: this.token, refreshToken: this.refreshTokenValue };
4172
+ }
4173
+ /**
4174
+ * Clear an access credential a 401 just proved unusable — but only while it
4175
+ * is still the canonical one. A rotation committed by another context while
4176
+ * the request was in flight is newer than this 401, so it must survive; the
4177
+ * instance adopts it instead. The refresh entry is never touched here: a
4178
+ * rejected access token says nothing about the refresh family.
4179
+ */
4180
+ invalidateAccessToken(spentToken) {
4181
+ this.syncFromPersistedStorage();
4182
+ if (this.token !== spentToken) return;
4183
+ this.setToken(null);
4184
+ }
4185
+ /** Commit both credentials in one canonical write. */
4186
+ setSession(token, refreshToken, options) {
4187
+ this.token = token;
4188
+ this.refreshTokenValue = refreshToken;
4189
+ const committed = this.commitPersistedSession(
4190
+ { token, refreshToken },
4191
+ options
4192
+ );
4193
+ if (!committed) return false;
4194
+ this.onTokenChange?.(this.token);
4195
+ this.onRefreshTokenChange?.(this.refreshTokenValue);
4196
+ return true;
4197
+ }
4198
+ /**
4199
+ * Serialize rotation against every other context sharing the same storage
4200
+ * key and API/tenant identity. Coordination is best-effort: when it is
4201
+ * unavailable or times out the rotation still happens, so server-side reuse
4202
+ * detection is never bypassed by a client that failed to coordinate.
4203
+ */
4204
+ async rotateAcrossContexts(intended) {
4205
+ const lockName = this.refreshLockName;
4206
+ const requestLock = lockName ? getRefreshLockRequester() : null;
4207
+ if (!lockName || !requestLock) {
4208
+ return this.rotateWithCanonicalState(intended);
4209
+ }
4210
+ const controller = new AbortController();
4211
+ const timer = setTimeout(() => controller.abort(), REFRESH_LOCK_TIMEOUT);
4212
+ let entered = false;
4213
+ try {
4214
+ return await requestLock(
4215
+ lockName,
4216
+ { signal: controller.signal },
4217
+ async () => {
4218
+ entered = true;
4219
+ return this.rotateWithCanonicalState(intended);
4220
+ }
4221
+ );
4222
+ } catch (error) {
4223
+ if (entered) throw error;
4224
+ return this.rotateWithCanonicalState(intended);
4225
+ } finally {
4226
+ clearTimeout(timer);
4227
+ }
4228
+ }
4229
+ /**
4230
+ * Decide, against freshly re-read canonical state, whether this context still
4231
+ * owns the rotation.
4232
+ */
4233
+ async rotateWithCanonicalState(intended) {
4234
+ let fence = intended;
4235
+ for (let attempt = 0; attempt < 2; attempt += 1) {
4236
+ const state = this.classifyCanonicalState(fence);
4237
+ if (state === "cleared") throw new ApiError("Not authenticated", 401);
4238
+ if (state === "current") return this._doRefreshToken(fence);
4239
+ if (this.token) return this.canonicalPair(this.token);
4240
+ fence = this.currentFence();
4241
+ }
4242
+ throw new ApiError("Not authenticated", 401);
4243
+ }
4244
+ async _doRefreshToken(intended) {
4245
+ const init = intended.refreshToken ? {
3815
4246
  method: "POST",
3816
- body: JSON.stringify({ refreshToken: this.refreshTokenValue })
4247
+ body: JSON.stringify({ refreshToken: intended.refreshToken })
3817
4248
  } : {
3818
4249
  method: "POST",
3819
- headers: { Authorization: `Bearer ${this.token}` }
4250
+ headers: { Authorization: `Bearer ${intended.token}` }
3820
4251
  };
3821
4252
  const result = await this.requestJson(
3822
4253
  "/api/customers/refresh",
3823
4254
  init
3824
4255
  );
3825
- this.setToken(result.token);
3826
- if (result.refreshToken) this.setRefreshToken(result.refreshToken);
3827
- return result;
4256
+ const state = this.classifyCanonicalState(intended);
4257
+ if (state === "cleared") throw new ApiError("Not authenticated", 401);
4258
+ if (state === "superseded") {
4259
+ if (this.token) return this.canonicalPair(this.token);
4260
+ throw new ApiError("Not authenticated", 401);
4261
+ }
4262
+ const replacedAccessToken = this.token !== null && this.token !== intended.token ? this.token : null;
4263
+ const nextToken = replacedAccessToken ?? result.token;
4264
+ const nextRefresh = result.refreshToken ?? null;
4265
+ if (!this.setSession(nextToken, nextRefresh, { requireLiveSession: true })) {
4266
+ throw new ApiError("Not authenticated", 401);
4267
+ }
4268
+ return {
4269
+ token: nextToken,
4270
+ ...nextRefresh ? { refreshToken: nextRefresh } : {}
4271
+ };
3828
4272
  }
3829
4273
  /**
3830
- * Clear the stored token
4274
+ * Clear local credentials synchronously. This compatibility API cannot
4275
+ * revoke the server-side refresh family; use logoutAsync() for that.
4276
+ * @deprecated Use logoutAsync() to end the direct server session.
3831
4277
  */
3832
4278
  logout() {
3833
- this.setToken(null);
3834
- this.setRefreshToken(null);
4279
+ this.setSession(null, null);
4280
+ }
4281
+ /**
4282
+ * End the current direct refresh family and clear local credentials even if
4283
+ * revocation fails. A legacy access-only session has no refresh family here.
4284
+ */
4285
+ async logoutAsync() {
4286
+ this.syncFromPersistedStorage();
4287
+ const refreshToken = this.refreshTokenValue;
4288
+ this.logout();
4289
+ if (!refreshToken) return;
4290
+ const operation2 = CUSTOMER_AUTH_ENDPOINT_OPERATIONS.revokeRefresh;
4291
+ await this.requestJson(resolveOperationPath(operation2), {
4292
+ method: resolveOperationMethod(operation2),
4293
+ body: JSON.stringify({ refreshToken })
4294
+ });
4295
+ }
4296
+ /** Archive the current password account and clear this client's credentials on success. */
4297
+ async withdraw(currentPassword) {
4298
+ const token = this.getToken();
4299
+ if (!token) throw new ApiError("Not authenticated", 401);
4300
+ const operation2 = CUSTOMER_AUTH_ENDPOINT_OPERATIONS.withdraw;
4301
+ const result = await this.requestJson(
4302
+ resolveOperationPath(operation2),
4303
+ {
4304
+ method: resolveOperationMethod(operation2),
4305
+ headers: { Authorization: `Bearer ${token}` },
4306
+ body: JSON.stringify({ currentPassword })
4307
+ }
4308
+ );
4309
+ if (!result || typeof result.withdrawnAt !== "string" || !Number.isFinite(Date.parse(result.withdrawnAt)))
4310
+ throw new ApiError("Invalid customer withdrawal response", 502);
4311
+ this.logout();
4312
+ return { withdrawnAt: result.withdrawnAt };
3835
4313
  }
3836
4314
  /**
3837
4315
  * Get the current authenticated customer's profile
3838
4316
  */
3839
4317
  async me() {
3840
- if (!this.token) return null;
4318
+ const token = this.getToken();
4319
+ if (!token) return null;
3841
4320
  try {
3842
4321
  const data = await this.requestJson(
3843
4322
  "/api/customers/me",
3844
4323
  {
3845
4324
  method: "GET",
3846
- headers: { Authorization: `Bearer ${this.token}` }
4325
+ headers: { Authorization: `Bearer ${token}` }
3847
4326
  }
3848
4327
  );
3849
4328
  return data.customer ?? null;
3850
4329
  } catch (error) {
3851
4330
  if (error instanceof SDKError && error.status === 401) {
3852
- this.setToken(null);
4331
+ this.invalidateAccessToken(token);
3853
4332
  return null;
3854
4333
  }
3855
4334
  throw error;
@@ -3877,12 +4356,13 @@ var CustomerAuth = class {
3877
4356
  * Update the authenticated customer's profile (name, phone, marketingConsent)
3878
4357
  */
3879
4358
  async updateProfile(data) {
3880
- if (!this.token) throw new ApiError("Not authenticated", 401);
4359
+ const token = this.getToken();
4360
+ if (!token) throw new ApiError("Not authenticated", 401);
3881
4361
  const result = await this.requestJson(
3882
4362
  "/api/customers/me",
3883
4363
  {
3884
4364
  method: "PATCH",
3885
- headers: { Authorization: `Bearer ${this.token}` },
4365
+ headers: { Authorization: `Bearer ${token}` },
3886
4366
  body: JSON.stringify(data)
3887
4367
  }
3888
4368
  );
@@ -3892,10 +4372,11 @@ var CustomerAuth = class {
3892
4372
  * Change the password of the currently authenticated customer
3893
4373
  */
3894
4374
  async changePassword(currentPassword, newPassword) {
3895
- if (!this.token) throw new ApiError("Not authenticated", 401);
4375
+ const token = this.getToken();
4376
+ if (!token) throw new ApiError("Not authenticated", 401);
3896
4377
  await this.requestJson("/api/customers/change-password", {
3897
4378
  method: "POST",
3898
- headers: { Authorization: `Bearer ${this.token}` },
4379
+ headers: { Authorization: `Bearer ${token}` },
3899
4380
  body: JSON.stringify({ currentPassword, newPassword })
3900
4381
  });
3901
4382
  }
@@ -3903,7 +4384,8 @@ var CustomerAuth = class {
3903
4384
  * Get the authenticated customer's orders with pagination and optional status filter
3904
4385
  */
3905
4386
  async getMyOrders(options) {
3906
- if (!this.token) throw new ApiError("Not authenticated", 401);
4387
+ const token = this.getToken();
4388
+ if (!token) throw new ApiError("Not authenticated", 401);
3907
4389
  const params = new URLSearchParams();
3908
4390
  if (options?.page) params.set("page", String(options.page));
3909
4391
  if (options?.limit) params.set("limit", String(options.limit));
@@ -3925,21 +4407,27 @@ var CustomerAuth = class {
3925
4407
  `/api/customers/me/orders${qs ? `?${qs}` : ""}`,
3926
4408
  {
3927
4409
  method: "GET",
3928
- headers: { Authorization: `Bearer ${this.token}` }
4410
+ headers: { Authorization: `Bearer ${token}` }
3929
4411
  }
3930
4412
  );
3931
4413
  return validatePublicOrderList(result);
3932
4414
  }
3933
4415
  /**
3934
- * Get the current token (or null if not authenticated)
4416
+ * Get the current token (or null if not authenticated).
4417
+ *
4418
+ * In persisted mode this re-reads shared storage first, so a rotation or
4419
+ * logout performed by another tab is observed before the token is used.
3935
4420
  */
3936
4421
  getToken() {
4422
+ this.syncFromPersistedStorage();
3937
4423
  return this.token;
3938
4424
  }
3939
4425
  /**
3940
- * Get the current refresh token (or null if unavailable)
4426
+ * Get the current refresh token (or null if unavailable). Persisted mode
4427
+ * re-reads shared storage first, like {@link CustomerAuth.getToken}.
3941
4428
  */
3942
4429
  getRefreshToken() {
4430
+ this.syncFromPersistedStorage();
3943
4431
  return this.refreshTokenValue;
3944
4432
  }
3945
4433
  /**
@@ -3947,6 +4435,7 @@ var CustomerAuth = class {
3947
4435
  */
3948
4436
  setToken(token) {
3949
4437
  this.token = token;
4438
+ this.commitPersistedSession({ token });
3950
4439
  this.onTokenChange?.(token);
3951
4440
  }
3952
4441
  /**
@@ -3954,13 +4443,14 @@ var CustomerAuth = class {
3954
4443
  */
3955
4444
  setRefreshToken(refreshToken) {
3956
4445
  this.refreshTokenValue = refreshToken;
4446
+ this.commitPersistedSession({ refreshToken });
3957
4447
  this.onRefreshTokenChange?.(refreshToken);
3958
4448
  }
3959
4449
  /**
3960
4450
  * Check if the customer is currently authenticated
3961
4451
  */
3962
4452
  isAuthenticated() {
3963
- return this.token !== null;
4453
+ return this.getToken() !== null;
3964
4454
  }
3965
4455
  /**
3966
4456
  * Internal: make a request with timeout and error handling.
@@ -4310,8 +4800,12 @@ var PaymentSessionApi = class extends CustomerScopedApi {
4310
4800
  ...Object.keys(headers).length > 0 && { headers }
4311
4801
  });
4312
4802
  }
4313
- async paymentSession(result, expectedProvider) {
4314
- return validatePublicPaymentSession(await result, expectedProvider);
4803
+ async paymentSession(result, expectedProvider, operation2) {
4804
+ return validatePublicPaymentSession(
4805
+ await result,
4806
+ expectedProvider,
4807
+ operation2
4808
+ );
4315
4809
  }
4316
4810
  create(params) {
4317
4811
  const { idempotencyKey, ...body } = params;
@@ -4358,7 +4852,8 @@ var PaymentSessionApi = class extends CustomerScopedApi {
4358
4852
  const endpoint = this.serverTransport ? COMMERCE_ENDPOINTS.paymentSessions.server.cancel : COMMERCE_ENDPOINTS.paymentSessions.cancel;
4359
4853
  return this.paymentSession(
4360
4854
  this.post(endpoint, body, idempotencyKey),
4361
- "toss"
4855
+ "toss",
4856
+ "cancel"
4362
4857
  );
4363
4858
  }
4364
4859
  cancelPortOne(params) {
@@ -4367,7 +4862,8 @@ var PaymentSessionApi = class extends CustomerScopedApi {
4367
4862
  const endpoint = this.serverTransport ? COMMERCE_ENDPOINTS.paymentSessions.server.cancel : COMMERCE_ENDPOINTS.paymentSessions.cancel;
4368
4863
  return this.paymentSession(
4369
4864
  this.post(endpoint, body, idempotencyKey),
4370
- "portone"
4865
+ "portone",
4866
+ "cancel"
4371
4867
  );
4372
4868
  }
4373
4869
  confirm(params) {
@@ -6808,7 +7304,11 @@ var COMMERCE_NOTIFICATION_EVENTS = [
6808
7304
  "returnCanceled",
6809
7305
  "returnCompleted",
6810
7306
  "paymentReconciliationRequired",
6811
- "paymentRefundRequiresAttention"
7307
+ "paymentRefundRequiresAttention",
7308
+ "membershipActivated",
7309
+ "membershipPastDue",
7310
+ "membershipExpired",
7311
+ "membershipCancelled"
6812
7312
  ];
6813
7313
  function isStringOrNumber(value) {
6814
7314
  return typeof value === "string" || typeof value === "number";
@@ -6838,7 +7338,7 @@ function isCommerceNotificationEventName(value) {
6838
7338
  return typeof value === "string" && COMMERCE_NOTIFICATION_EVENTS.includes(value);
6839
7339
  }
6840
7340
  function isCommerceNotificationSourceCollection(value) {
6841
- return value === "orders" || value === "fulfillments" || value === "returns" || value === "payment-sessions" || value === "payment-refund-attempts";
7341
+ return value === "orders" || value === "fulfillments" || value === "returns" || value === "payment-sessions" || value === "payment-refund-attempts" || value === "customer-memberships";
6842
7342
  }
6843
7343
  function isCommerceNotificationData(value) {
6844
7344
  if (!isRecord(value)) return false;
@@ -6858,6 +7358,10 @@ function isPaymentIncidentNotificationData(value, event) {
6858
7358
  const expectedReason = reconciliation ? "orderless_capture" : "local_commit_ambiguous";
6859
7359
  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);
6860
7360
  }
7361
+ function isMembershipNotificationData(value, event) {
7362
+ const expectedStatus = event === "membershipActivated" ? "active" : event === "membershipPastDue" ? "past_due" : event === "membershipExpired" ? "expired" : "cancelled";
7363
+ 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));
7364
+ }
6861
7365
  function isWebhookCommerceNotificationChange(value) {
6862
7366
  if (!isRecord(value)) return false;
6863
7367
  return value.type === "notification" && value.source === "commerce-notifications" && isCommerceNotificationEventName(value.event) && isCommerceNotificationSourceCollection(value.sourceCollection) && isStringOrNumber(value.sourceId);
@@ -6906,6 +7410,9 @@ function isCommerceNotificationWebhookEvent(event) {
6906
7410
  const notificationSourceId = notification.event === "paymentReconciliationRequired" ? notification.paymentSessionId : notification.refundAttemptId;
6907
7411
  return sourceCollection === expectedCollection && typeof notification.paymentSessionId === "string" && notification.paymentSessionId === incidentData.paymentSessionId && typeof notificationSourceId === "string" && notificationSourceId === incidentData.source.id && matchesOptionalId(changeSourceId, incidentData.source.id);
6908
7412
  }
7413
+ if (notification.event === "membershipActivated" || notification.event === "membershipPastDue" || notification.event === "membershipExpired" || notification.event === "membershipCancelled") {
7414
+ return sourceCollection === "customer-memberships" && isMembershipNotificationData(data, notification.event) && typeof notification.membershipId === "string" && notification.membershipId === data.membershipId && matchesOptionalId(changeSourceId, data.membershipId);
7415
+ }
6909
7416
  if (notification.event === "orderPaid" || notification.event === "orderCanceled" || notification.event === "orderDelivered") {
6910
7417
  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);
6911
7418
  }
@@ -7002,9 +7509,7 @@ var INTERNAL_COLLECTIONS = [
7002
7509
  "audiences",
7003
7510
  "email-logs",
7004
7511
  "api-usage",
7005
- "tenant-analytics-daily",
7006
- "tenant-web-analytics-config",
7007
- "analytics-event-schemas",
7512
+ "tenant-mcp-usage-daily",
7008
7513
  "subscriptions",
7009
7514
  "billing-history",
7010
7515
  "usage-rate-cards",
@@ -7023,6 +7528,8 @@ var INTERNAL_COLLECTIONS = [
7023
7528
  "fulfillment-events",
7024
7529
  "tenant-commerce-config",
7025
7530
  "customer-oauth-clients",
7531
+ "customer-consent-receipts",
7532
+ "customer-group-grants",
7026
7533
  "api-keys",
7027
7534
  "personal-access-tokens",
7028
7535
  "tenant-entitlements",
@@ -7040,6 +7547,7 @@ var INTERNAL_COLLECTIONS = [
7040
7547
  "event-admission-types",
7041
7548
  "customer-payment-methods",
7042
7549
  "tenant-payment-credentials",
7550
+ "membership-unapplied-paid-evidence",
7043
7551
  "commerce-payment-accounts",
7044
7552
  "payment-provider-customer-aliases"
7045
7553
  ];
@@ -7422,200 +7930,6 @@ function getVideoMp4Url(playbackId, resolution = "high") {
7422
7930
  return `${MUX_STREAM_BASE}/${playbackId}/${resolution}.mp4`;
7423
7931
  }
7424
7932
 
7425
- // src/analytics.ts
7426
- function createAnalytics(config) {
7427
- if (typeof window === "undefined") {
7428
- return { pageview() {
7429
- }, track() {
7430
- }, destroy() {
7431
- } };
7432
- }
7433
- const endpoint = config.endpoint ?? `${resolveApiUrl()}/api/analytics/collect`;
7434
- const respectDnt = config.respectDnt !== false;
7435
- function isDntActive() {
7436
- if (!respectDnt) return false;
7437
- const nav = navigator;
7438
- return nav.doNotTrack === "1" || nav.globalPrivacyControl === true;
7439
- }
7440
- const isLocalHost = (() => {
7441
- try {
7442
- const h = location.hostname;
7443
- return h === "localhost" || h === "127.0.0.1" || h.endsWith(".local");
7444
- } catch {
7445
- return false;
7446
- }
7447
- })();
7448
- const mode = config.mode ?? "auto";
7449
- const sendSuppressed = mode === "development" || mode === "auto" && isLocalHost;
7450
- let suppressNoticeShown = false;
7451
- function devSuppressNotice() {
7452
- if (suppressNoticeShown) return;
7453
- suppressNoticeShown = true;
7454
- try {
7455
- console.info(
7456
- `[01 analytics] events disabled on local host (mode: '${mode}'). Pass mode: 'production' to send while developing.`
7457
- );
7458
- } catch {
7459
- }
7460
- }
7461
- let lastPath = null;
7462
- let lastAt = 0;
7463
- const autoTrack = config.autoTrack !== false;
7464
- const originalPushState = history.pushState;
7465
- const originalReplaceState = history.replaceState;
7466
- let destroyed = false;
7467
- function newEventId() {
7468
- return typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : String(Date.now()) + String(Math.random());
7469
- }
7470
- function sendBeaconOrFetch(body) {
7471
- try {
7472
- if (typeof navigator.sendBeacon === "function") {
7473
- const blob = new Blob([body], { type: "text/plain" });
7474
- const sent = navigator.sendBeacon(endpoint, blob);
7475
- if (sent) return;
7476
- }
7477
- fetch(endpoint, {
7478
- method: "POST",
7479
- keepalive: true,
7480
- headers: { "Content-Type": "application/json" },
7481
- body
7482
- }).catch(() => {
7483
- });
7484
- } catch {
7485
- }
7486
- }
7487
- function sendPageview(pathname) {
7488
- if (isDntActive()) return;
7489
- const doc = document;
7490
- if (doc.prerendering === true || document.visibilityState === "prerender") return;
7491
- if (sendSuppressed) {
7492
- devSuppressNotice();
7493
- return;
7494
- }
7495
- const now = Date.now();
7496
- if (pathname === lastPath && now - lastAt < 500) return;
7497
- lastPath = pathname;
7498
- lastAt = now;
7499
- const body = JSON.stringify({
7500
- publishableKey: config.publishableKey,
7501
- pathname,
7502
- referrer: document.referrer || "",
7503
- eventId: newEventId(),
7504
- eventTs: Date.now()
7505
- });
7506
- sendBeaconOrFetch(body);
7507
- }
7508
- function trackCurrentPath() {
7509
- if (destroyed) return;
7510
- sendPageview(location.pathname);
7511
- }
7512
- function patchedPushState(data, unused, url) {
7513
- originalPushState.apply(this, [data, unused, url]);
7514
- if (!destroyed) setTimeout(trackCurrentPath, 0);
7515
- }
7516
- function patchedReplaceState(data, unused, url) {
7517
- originalReplaceState.apply(this, [data, unused, url]);
7518
- if (!destroyed) setTimeout(trackCurrentPath, 0);
7519
- }
7520
- if (autoTrack) {
7521
- history.pushState = patchedPushState;
7522
- history.replaceState = patchedReplaceState;
7523
- window.addEventListener("popstate", trackCurrentPath);
7524
- if (document.readyState === "complete") {
7525
- trackCurrentPath();
7526
- } else {
7527
- window.addEventListener("load", trackCurrentPath, { once: true });
7528
- }
7529
- }
7530
- const isProduction = !isLocalHost;
7531
- const warnedReasons = /* @__PURE__ */ new Set();
7532
- function devWarn(name, reason) {
7533
- if (isProduction) return;
7534
- if (warnedReasons.has(reason)) return;
7535
- warnedReasons.add(reason);
7536
- console.warn(`[01 analytics] dropped event ${name}: ${reason}`);
7537
- }
7538
- const EVENT_NAME_RE = /^[a-zA-Z][a-zA-Z0-9_:-]{0,49}$/;
7539
- const RESERVED_PREFIXES = ["__", "_pv_"];
7540
- function validateEventName(name) {
7541
- if (!name || typeof name !== "string") return "name-empty";
7542
- for (const prefix of RESERVED_PREFIXES) {
7543
- if (name.startsWith(prefix)) return "name-reserved";
7544
- }
7545
- if (!EVENT_NAME_RE.test(name)) return "name-regex";
7546
- return null;
7547
- }
7548
- const PROP_KEY_RE = /^[a-zA-Z_][a-zA-Z0-9_]{0,31}$/;
7549
- function validateEventProps(props) {
7550
- if (props === void 0 || props === null) return null;
7551
- if (typeof props !== "object" || Array.isArray(props)) return "props-value-type";
7552
- const keys = Object.keys(props);
7553
- if (keys.length > 10) return "props-too-many-keys";
7554
- for (const k of keys) {
7555
- const v = props[k];
7556
- if (!PROP_KEY_RE.test(k)) return "props-key-regex";
7557
- if (typeof v === "string") {
7558
- if (v.length > 80) return "props-value-too-long";
7559
- } else if (typeof v === "number") {
7560
- if (!isFinite(v)) return "props-value-not-finite";
7561
- } else if (typeof v === "boolean") {
7562
- } else {
7563
- return "props-value-type";
7564
- }
7565
- }
7566
- return null;
7567
- }
7568
- return {
7569
- pageview(path) {
7570
- if (destroyed) return;
7571
- sendPageview(path ?? location.pathname);
7572
- },
7573
- track(name, props) {
7574
- if (destroyed) return;
7575
- if (isDntActive()) return;
7576
- const doc = document;
7577
- if (doc.prerendering === true || document.visibilityState === "prerender") return;
7578
- const nameErr = validateEventName(name);
7579
- if (nameErr) {
7580
- devWarn(name, nameErr);
7581
- return;
7582
- }
7583
- if (props !== void 0) {
7584
- const propsErr = validateEventProps(props);
7585
- if (propsErr) {
7586
- devWarn(name, propsErr);
7587
- return;
7588
- }
7589
- }
7590
- if (sendSuppressed) {
7591
- devSuppressNotice();
7592
- return;
7593
- }
7594
- const body = JSON.stringify({
7595
- publishableKey: config.publishableKey,
7596
- pathname: location.pathname,
7597
- referrer: document.referrer || "",
7598
- eventId: newEventId(),
7599
- eventName: name,
7600
- eventProps: props,
7601
- eventTs: Date.now()
7602
- });
7603
- sendBeaconOrFetch(body);
7604
- },
7605
- destroy() {
7606
- if (destroyed) return;
7607
- destroyed = true;
7608
- if (autoTrack) {
7609
- history.pushState = originalPushState;
7610
- history.replaceState = originalReplaceState;
7611
- window.removeEventListener("popstate", trackCurrentPath);
7612
- }
7613
- lastPath = null;
7614
- lastAt = 0;
7615
- }
7616
- };
7617
- }
7618
-
7619
7933
  // src/index.ts
7620
7934
  function createClient2(options) {
7621
7935
  return createClient(options);
@@ -7666,7 +7980,6 @@ export {
7666
7980
  buildProductListingGroupsByOption,
7667
7981
  buildProductOptionMatrix,
7668
7982
  buildProductOptionMatrixFromDetail,
7669
- createAnalytics,
7670
7983
  createAuthError,
7671
7984
  createClient2 as createClient,
7672
7985
  createCommerceEmailWebhookHandler,