@swell/apps-sdk 1.0.152 → 1.0.154

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/dist/index.cjs CHANGED
@@ -860,18 +860,31 @@ var StorefrontResource = class {
860
860
  return this._compatibilityProps[prop];
861
861
  }
862
862
  };
863
+ var RESOURCE_CLONE_PROPS = Object.freeze([
864
+ "_defaultGetter",
865
+ "_getResourceObject",
866
+ "_collection",
867
+ "_swell",
868
+ "_query",
869
+ "_params",
870
+ "_id",
871
+ "_resourceName"
872
+ ]);
863
873
  function cloneStorefrontResource(input) {
864
- const resourceName = input._resourceName;
874
+ const resourceName = input._resourceName || input.constructor?.name;
865
875
  const ClonedClass = class extends StorefrontResource {
866
876
  };
867
877
  Object.defineProperty(ClonedClass, "name", {
868
878
  value: resourceName
869
879
  });
870
880
  const clone = new ClonedClass(input._getter);
871
- clone._params = input._params;
872
- Object.defineProperty(clone, "_resourceName", {
873
- value: resourceName
874
- });
881
+ for (const key of RESOURCE_CLONE_PROPS) {
882
+ if (input[key] !== void 0) {
883
+ Object.defineProperty(clone, key, {
884
+ value: input[key]
885
+ });
886
+ }
887
+ }
875
888
  return clone;
876
889
  }
877
890
  var SwellStorefrontResource = class extends StorefrontResource {
@@ -890,7 +903,7 @@ var SwellStorefrontResource = class extends StorefrontResource {
890
903
  _getProxy() {
891
904
  return super._getProxy();
892
905
  }
893
- getResourceObject() {
906
+ _getResourceObject() {
894
907
  const { _swell, _collection } = this;
895
908
  this._resource = (_swell?.storefront)[_collection];
896
909
  if (_swell && _collection.startsWith("content/")) {
@@ -947,7 +960,7 @@ var SwellStorefrontCollection = class _SwellStorefrontCollection extends SwellSt
947
960
  return properQuery;
948
961
  }
949
962
  _defaultGetter() {
950
- const resource = this.getResourceObject();
963
+ const resource = this._getResourceObject();
951
964
  async function defaultGetter() {
952
965
  return resource.list(this._query);
953
966
  }
@@ -1074,7 +1087,7 @@ var SwellStorefrontRecord = class extends SwellStorefrontResource {
1074
1087
  return super._getProxy();
1075
1088
  }
1076
1089
  _defaultGetter() {
1077
- const resource = this.getResourceObject();
1090
+ const resource = this._getResourceObject();
1078
1091
  async function defaultGetter() {
1079
1092
  return resource.get(this._id, this._query);
1080
1093
  }
@@ -1133,7 +1146,7 @@ var SwellStorefrontSingleton = class extends SwellStorefrontResource {
1133
1146
  _collection,
1134
1147
  _swell: { storefrontContext }
1135
1148
  } = this;
1136
- const resource = this.getResourceObject();
1149
+ const resource = this._getResourceObject();
1137
1150
  async function defaultGetter() {
1138
1151
  if (storefrontContext[_collection] !== void 0) {
1139
1152
  return storefrontContext[_collection];
@@ -7858,20 +7871,38 @@ var WorkerCacheProxy = class {
7858
7871
  var CACHE_NAME2 = "swell-html-v1";
7859
7872
  var CACHE_KEY_ORIGIN2 = "https://cache.swell.store";
7860
7873
  var TTL_CONFIG = {
7861
- DEFAULT: 300,
7862
- // 5 minutes
7863
- DEFAULT_SWR: 3600,
7864
- // 1 hour stale-while-revalidate
7865
- HOME: 300,
7866
- // 5 minutes
7867
- PRODUCT: 600,
7868
- // 10 minutes
7869
- COLLECTION: 900,
7870
- // 15 minutes
7871
- PAGE: 3600,
7872
- // 1 hour
7873
- BLOG: 1800
7874
- // 30 minutes
7874
+ LIVE: {
7875
+ DEFAULT: 300,
7876
+ // 5 minutes
7877
+ HOME: 300,
7878
+ // 5 minutes
7879
+ PRODUCT: 600,
7880
+ // 10 minutes
7881
+ COLLECTION: 900,
7882
+ // 15 minutes
7883
+ PAGE: 3600,
7884
+ // 1 hour
7885
+ BLOG: 1800,
7886
+ // 30 minutes
7887
+ SWR: 3600
7888
+ // 1 hour stale-while-revalidate
7889
+ },
7890
+ PREVIEW: {
7891
+ DEFAULT: 5,
7892
+ // 1 minute - faster updates in preview
7893
+ HOME: 5,
7894
+ // 1 minute
7895
+ PRODUCT: 5,
7896
+ // 2 minutes
7897
+ COLLECTION: 5,
7898
+ // 3 minutes
7899
+ PAGE: 5,
7900
+ // 5 minutes
7901
+ BLOG: 5,
7902
+ // 5 minutes
7903
+ SWR: 600
7904
+ // 10 minutes stale-while-revalidate
7905
+ }
7875
7906
  };
7876
7907
  var WorkerHtmlCache = class {
7877
7908
  epoch;
@@ -7898,7 +7929,7 @@ var WorkerHtmlCache = class {
7898
7929
  }
7899
7930
  const age = this.getResponseAge(cached);
7900
7931
  const ttl = parseInt(cached.headers.get("X-Original-TTL") || "") || this.getTTLForRequest(request);
7901
- const swr = parseInt(cached.headers.get("X-Original-SWR") || "") || TTL_CONFIG.DEFAULT_SWR;
7932
+ const swr = parseInt(cached.headers.get("X-Original-SWR") || "") || this.getSWRForRequest(request);
7902
7933
  const isStale = age >= ttl;
7903
7934
  const isExpired = age >= ttl + swr;
7904
7935
  if (!isExpired) {
@@ -7929,6 +7960,56 @@ var WorkerHtmlCache = class {
7929
7960
  return null;
7930
7961
  }
7931
7962
  }
7963
+ // 304 support
7964
+ async getWithConditionals(request) {
7965
+ const result = await this.get(request);
7966
+ if (!result?.found || result.stale) {
7967
+ return result;
7968
+ }
7969
+ const ifModifiedSince = request.headers.get("If-Modified-Since");
7970
+ const ifNoneMatch = request.headers.get("If-None-Match");
7971
+ if ((ifModifiedSince || ifNoneMatch) && result.response) {
7972
+ const lastModified = result.response.headers.get("Last-Modified");
7973
+ const etag = result.response.headers.get("ETag");
7974
+ if (this.checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag)) {
7975
+ result.notModified = true;
7976
+ result.conditional304 = new Response(null, {
7977
+ status: 304,
7978
+ headers: {
7979
+ "Last-Modified": lastModified || "",
7980
+ ETag: etag || "",
7981
+ "Cache-Control": result.response.headers.get("Cache-Control") || "",
7982
+ "Cloudflare-CDN-Cache-Control": result.response.headers.get("Cloudflare-CDN-Cache-Control") || "",
7983
+ "X-Cache-Status": "HIT-304"
7984
+ }
7985
+ });
7986
+ }
7987
+ }
7988
+ return result;
7989
+ }
7990
+ checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag) {
7991
+ if (ifNoneMatch && etag) {
7992
+ return ifNoneMatch === etag;
7993
+ }
7994
+ if (ifModifiedSince && lastModified) {
7995
+ const ifModDate = new Date(ifModifiedSince);
7996
+ const lastModDate = new Date(lastModified);
7997
+ return !isNaN(ifModDate.getTime()) && !isNaN(lastModDate.getTime()) && ifModDate >= lastModDate;
7998
+ }
7999
+ return false;
8000
+ }
8001
+ createRevalidationRequest(request) {
8002
+ const headers = new Headers(request.headers);
8003
+ headers.set("X-Cache-Bypass", "revalidation");
8004
+ headers.delete("If-None-Match");
8005
+ headers.delete("If-Modified-Since");
8006
+ headers.delete("Cache-Control");
8007
+ headers.delete("Pragma");
8008
+ return new Request(request.url, {
8009
+ method: "GET",
8010
+ headers
8011
+ });
8012
+ }
7932
8013
  async put(request, response) {
7933
8014
  const trace = createTraceId();
7934
8015
  if (request.method !== "GET" || !response.ok) {
@@ -7944,17 +8025,22 @@ var WorkerHtmlCache = class {
7944
8025
  try {
7945
8026
  const cache = await caches.open(CACHE_NAME2 + this.epoch);
7946
8027
  const cacheKey = this.buildCacheKey(request);
8028
+ await cache.delete(cacheKey);
7947
8029
  const ttl = this.getTTLForRequest(request);
7948
- const swr = TTL_CONFIG.DEFAULT_SWR;
8030
+ const swr = this.getSWRForRequest(request);
7949
8031
  const headers = new Headers(response.headers);
7950
8032
  const existingCacheControl = response.headers.get("Cache-Control");
7951
8033
  if (!existingCacheControl || existingCacheControl === "public") {
7952
8034
  const internalMaxAge = ttl + swr;
7953
8035
  headers.set("Cache-Control", `public, max-age=${internalMaxAge}`);
7954
8036
  }
7955
- headers.set("X-Cache-Time", (/* @__PURE__ */ new Date()).toISOString());
8037
+ const cacheTime = (/* @__PURE__ */ new Date()).toISOString();
8038
+ headers.set("X-Cache-Time", cacheTime);
7956
8039
  headers.set("X-Original-TTL", ttl.toString());
7957
8040
  headers.set("X-Original-SWR", swr.toString());
8041
+ if (!headers.get("Last-Modified")) {
8042
+ headers.set("Last-Modified", new Date(cacheTime).toUTCString());
8043
+ }
7958
8044
  const cacheableResponse = new Response(response.body, {
7959
8045
  status: response.status,
7960
8046
  statusText: response.statusText,
@@ -7966,28 +8052,32 @@ var WorkerHtmlCache = class {
7966
8052
  logger.warn("[SDK Html-cache] no put support", { trace });
7967
8053
  }
7968
8054
  }
7969
- /**
7970
- * Build client response with correct headers
7971
- */
7972
8055
  buildClientResponse(cachedResponse, ttl, swr, isStale, age) {
7973
8056
  const headers = new Headers(cachedResponse.headers);
7974
8057
  headers.set(
7975
8058
  "Cache-Control",
7976
8059
  `public, max-age=${ttl}, stale-while-revalidate=${swr}`
7977
8060
  );
8061
+ headers.set(
8062
+ "Cloudflare-CDN-Cache-Control",
8063
+ `public, s-maxage=${ttl}, stale-while-revalidate=${swr}, stale-if-error=60`
8064
+ );
8065
+ const cacheTime = headers.get("X-Cache-Time");
8066
+ if (cacheTime) {
8067
+ const lastModified = new Date(cacheTime).toUTCString();
8068
+ headers.set("Last-Modified", lastModified);
8069
+ }
7978
8070
  headers.set("X-Cache-Status", isStale ? "STALE" : "HIT");
7979
8071
  headers.set("X-Cache-Age", Math.floor(age).toString());
7980
8072
  headers.delete("X-Original-TTL");
7981
8073
  headers.delete("X-Original-SWR");
8074
+ headers.delete("X-Cache-Time");
7982
8075
  return new Response(cachedResponse.body, {
7983
8076
  status: cachedResponse.status,
7984
8077
  statusText: cachedResponse.statusText,
7985
8078
  headers
7986
8079
  });
7987
8080
  }
7988
- /**
7989
- * Build cache key from request using two-level structure
7990
- */
7991
8081
  buildCacheKey(request) {
7992
8082
  const url = new URL(request.url);
7993
8083
  const versionHash = this.generateVersionHash(request.headers);
@@ -8003,22 +8093,11 @@ var WorkerHtmlCache = class {
8003
8093
  headers: sanitizedHeaders
8004
8094
  });
8005
8095
  }
8006
- /**
8007
- * Sanitize headers for cache operations - minimal whitelist approach
8008
- */
8009
8096
  sanitizeHeaders(originalHeaders) {
8010
8097
  const CACHE_RELEVANT_HEADERS = [
8011
8098
  // Content negotiation (affects response format)
8012
8099
  "accept",
8013
- "accept-encoding",
8014
- "accept-language",
8015
- // Cache control directives from client/proxy
8016
- "cache-control",
8017
- "pragma",
8018
- // Legacy cache control
8019
- // Conditional request headers (for 304 responses)
8020
- "if-none-match",
8021
- "if-modified-since"
8100
+ "accept-language"
8022
8101
  ];
8023
8102
  const sanitized = new Headers();
8024
8103
  CACHE_RELEVANT_HEADERS.forEach((header) => {
@@ -8029,15 +8108,13 @@ var WorkerHtmlCache = class {
8029
8108
  });
8030
8109
  return sanitized;
8031
8110
  }
8032
- /**
8033
- * Generate version hash from headers and cookies
8034
- */
8035
8111
  generateVersionHash(headers) {
8036
8112
  const swellData = this.extractSwellData(headers);
8037
8113
  const versionFactors = {
8038
8114
  store: headers.get("swell-storefront-id") || "",
8039
8115
  auth: headers.get("swell-access-token") || "",
8040
8116
  theme: headers.get("swell-theme-version-hash") || "",
8117
+ modified: headers.get("swell-cache-modified") || "",
8041
8118
  currency: swellData["swell-currency"] || "USD",
8042
8119
  locale: headers.get("x-locale") || headers.get("accept-language")?.split(",")[0] || "default",
8043
8120
  context: headers.get("swell-storefront-context"),
@@ -8045,9 +8122,6 @@ var WorkerHtmlCache = class {
8045
8122
  };
8046
8123
  return md5(JSON.stringify(versionFactors));
8047
8124
  }
8048
- /**
8049
- * Extract swell-data from cookies
8050
- */
8051
8125
  extractSwellData(headers) {
8052
8126
  const cookie = headers.get("cookie");
8053
8127
  if (!cookie) return {};
@@ -8083,31 +8157,53 @@ var WorkerHtmlCache = class {
8083
8157
  if (!contentType?.includes("text/html")) {
8084
8158
  return false;
8085
8159
  }
8160
+ if (response.headers.get("set-cookie")) {
8161
+ return false;
8162
+ }
8086
8163
  const cacheControl = response.headers.get("cache-control");
8087
8164
  if (cacheControl?.includes("no-store") || cacheControl?.includes("private")) {
8088
8165
  return false;
8089
8166
  }
8090
8167
  return true;
8091
8168
  }
8169
+ getDeploymentMode(headers) {
8170
+ const mode = headers.get("swell-deployment-mode");
8171
+ if (mode === "preview" || mode === "editor") {
8172
+ return mode;
8173
+ }
8174
+ return "live";
8175
+ }
8092
8176
  getTTLForRequest(request) {
8093
8177
  const url = new URL(request.url);
8094
8178
  const path = url.pathname;
8179
+ const mode = this.getDeploymentMode(request.headers);
8180
+ if (mode === "editor") {
8181
+ return 0;
8182
+ }
8183
+ const config = mode === "preview" ? TTL_CONFIG.PREVIEW : TTL_CONFIG.LIVE;
8095
8184
  if (path === "/") {
8096
- return TTL_CONFIG.HOME;
8185
+ return config.HOME;
8097
8186
  }
8098
8187
  if (path.startsWith("/products/")) {
8099
- return TTL_CONFIG.PRODUCT;
8188
+ return config.PRODUCT;
8100
8189
  }
8101
8190
  if (path.startsWith("/categories/")) {
8102
- return TTL_CONFIG.COLLECTION;
8191
+ return config.COLLECTION;
8103
8192
  }
8104
8193
  if (path.startsWith("/pages/")) {
8105
- return TTL_CONFIG.PAGE;
8194
+ return config.PAGE;
8106
8195
  }
8107
8196
  if (path.startsWith("/blogs/")) {
8108
- return TTL_CONFIG.BLOG;
8197
+ return config.BLOG;
8109
8198
  }
8110
- return TTL_CONFIG.DEFAULT;
8199
+ return config.DEFAULT;
8200
+ }
8201
+ getSWRForRequest(request) {
8202
+ const mode = this.getDeploymentMode(request.headers);
8203
+ if (mode === "editor") {
8204
+ return 0;
8205
+ }
8206
+ return mode === "preview" ? TTL_CONFIG.PREVIEW.SWR : TTL_CONFIG.LIVE.SWR;
8111
8207
  }
8112
8208
  getResponseAge(response) {
8113
8209
  const cacheTime = response.headers.get("X-Cache-Time");
@@ -8121,31 +8217,6 @@ var WorkerHtmlCache = class {
8121
8217
  const age = (Date.now() - cacheDate.getTime()) / 1e3;
8122
8218
  return Math.max(0, age);
8123
8219
  }
8124
- getMaxAge(response) {
8125
- const cacheControl = response.headers.get("Cache-Control");
8126
- if (!cacheControl) {
8127
- return TTL_CONFIG.DEFAULT;
8128
- }
8129
- const maxAgeMatch = cacheControl.match(/max-age=(\d+)/);
8130
- if (maxAgeMatch) {
8131
- return parseInt(maxAgeMatch[1], 10);
8132
- }
8133
- return TTL_CONFIG.DEFAULT;
8134
- }
8135
- getStaleWindow(response) {
8136
- const cacheControl = response.headers.get("Cache-Control");
8137
- if (!cacheControl) {
8138
- return TTL_CONFIG.DEFAULT_SWR;
8139
- }
8140
- const swrMatch = cacheControl.match(/stale-while-revalidate=(\d+)/);
8141
- if (swrMatch) {
8142
- return parseInt(swrMatch[1], 10);
8143
- }
8144
- return TTL_CONFIG.DEFAULT_SWR;
8145
- }
8146
- /**
8147
- * Normalize search params for cache key
8148
- */
8149
8220
  normalizeSearchParams(searchParams) {
8150
8221
  const ignoredParams = [
8151
8222
  "utm_source",