@swell/apps-sdk 1.0.159 → 1.0.161
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 +580 -417
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +576 -415
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +578 -416
- package/dist/index.mjs.map +4 -4
- package/dist/src/cache/html-cache/html-cache-backend.d.ts +64 -0
- package/dist/src/cache/html-cache/html-cache-factory.d.ts +8 -0
- package/dist/src/cache/html-cache/html-cache-kv.d.ts +38 -0
- package/dist/src/cache/html-cache/html-cache-worker.d.ts +8 -0
- package/dist/src/cache/html-cache/html-cache.d.ts +45 -0
- package/dist/src/cache/html-cache/index.d.ts +4 -0
- package/dist/src/cache/index.d.ts +2 -5
- package/dist/src/cache/{theme-file-storage.d.ts → theme-file-cache.d.ts} +2 -3
- package/dist/src/index.d.ts +1 -2
- package/dist/types/swell.d.ts +5 -0
- package/package.json +2 -3
- package/dist/src/cache/cache-api-stub.d.ts +0 -25
- package/dist/src/cache/worker-cache-proxy.d.ts +0 -31
- package/dist/src/cache/worker-html-cache.d.ts +0 -30
package/dist/index.cjs
CHANGED
|
@@ -46,6 +46,7 @@ __export(index_exports, {
|
|
|
46
46
|
DeferredShopifyResource: () => DeferredShopifyResource,
|
|
47
47
|
FILE_DATA_INCLUDE_QUERY: () => FILE_DATA_INCLUDE_QUERY,
|
|
48
48
|
GEO_DATA: () => GEO_DATA,
|
|
49
|
+
HtmlCache: () => HtmlCache,
|
|
49
50
|
LANG_TO_COUNTRY_CODES: () => LANG_TO_COUNTRY_CODES,
|
|
50
51
|
LiquidSwell: () => LiquidSwell30,
|
|
51
52
|
MAX_QUERY_PAGE_LIMIT: () => MAX_QUERY_PAGE_LIMIT,
|
|
@@ -93,7 +94,6 @@ __export(index_exports, {
|
|
|
93
94
|
ThemeForm: () => ThemeForm,
|
|
94
95
|
ThemeFormErrors: () => ThemeFormErrors,
|
|
95
96
|
VariantResource: () => VariantResource,
|
|
96
|
-
WorkerHtmlCache: () => WorkerHtmlCache,
|
|
97
97
|
adaptShopifyFontData: () => adaptShopifyFontData,
|
|
98
98
|
adaptShopifyFormData: () => adaptShopifyFormData,
|
|
99
99
|
adaptShopifyMenuData: () => adaptShopifyMenuData,
|
|
@@ -121,6 +121,7 @@ __export(index_exports, {
|
|
|
121
121
|
getEasyblocksComponentDefinitions: () => getEasyblocksComponentDefinitions,
|
|
122
122
|
getEasyblocksPagePropsWithConfigs: () => getEasyblocksPagePropsWithConfigs,
|
|
123
123
|
getEasyblocksPageTemplate: () => getEasyblocksPageTemplate,
|
|
124
|
+
getHtmlCache: () => getHtmlCache,
|
|
124
125
|
getKVFlavor: () => getKVFlavor,
|
|
125
126
|
getLayoutSectionGroups: () => getLayoutSectionGroups,
|
|
126
127
|
getMenuItemStorefrontUrl: () => getMenuItemStorefrontUrl,
|
|
@@ -866,7 +867,8 @@ var RESOURCE_CLONE_PROPS = Object.freeze([
|
|
|
866
867
|
"_swell",
|
|
867
868
|
"_params",
|
|
868
869
|
"_id",
|
|
869
|
-
"_resourceName"
|
|
870
|
+
"_resourceName",
|
|
871
|
+
"_result"
|
|
870
872
|
]);
|
|
871
873
|
function cloneStorefrontResource(input) {
|
|
872
874
|
const resourceName = input._resourceName || input.constructor?.name;
|
|
@@ -7436,12 +7438,7 @@ var ThemeCache = class extends Cache {
|
|
|
7436
7438
|
}
|
|
7437
7439
|
};
|
|
7438
7440
|
|
|
7439
|
-
// src/cache/theme-file-storage.ts
|
|
7440
|
-
var import_bluebird2 = __toESM(require("bluebird"), 1);
|
|
7441
|
-
|
|
7442
7441
|
// src/cache/kv-variety.ts
|
|
7443
|
-
var import_bluebird = __toESM(require("bluebird"), 1);
|
|
7444
|
-
var { Promise: Promise2 } = import_bluebird.default;
|
|
7445
7442
|
var CFKV = class {
|
|
7446
7443
|
constructor(kv) {
|
|
7447
7444
|
this.kv = kv;
|
|
@@ -7473,13 +7470,11 @@ var MiniflareKV = class {
|
|
|
7473
7470
|
return /* @__PURE__ */ new Map();
|
|
7474
7471
|
}
|
|
7475
7472
|
const result = /* @__PURE__ */ new Map();
|
|
7476
|
-
await
|
|
7477
|
-
keys
|
|
7478
|
-
async (key) => {
|
|
7473
|
+
await Promise.all(
|
|
7474
|
+
keys.map(async (key) => {
|
|
7479
7475
|
const value = await this.kv.get(key, "text");
|
|
7480
7476
|
result.set(key, value);
|
|
7481
|
-
}
|
|
7482
|
-
{ concurrency: 50 }
|
|
7477
|
+
})
|
|
7483
7478
|
);
|
|
7484
7479
|
return result;
|
|
7485
7480
|
}
|
|
@@ -7511,16 +7506,13 @@ function createClientKV(env, flavor = "cf") {
|
|
|
7511
7506
|
return new MemoryKV();
|
|
7512
7507
|
}
|
|
7513
7508
|
|
|
7514
|
-
// src/cache/theme-file-
|
|
7515
|
-
var
|
|
7516
|
-
var ThemeFileStorage = class {
|
|
7509
|
+
// src/cache/theme-file-cache.ts
|
|
7510
|
+
var ThemeFileCache = class {
|
|
7517
7511
|
kv;
|
|
7518
|
-
maxConcurrency;
|
|
7519
7512
|
maxBatchSize = 20 * 1024 * 1024;
|
|
7520
7513
|
// 20MB safety margin
|
|
7521
7514
|
constructor(env, flavor = "cf") {
|
|
7522
7515
|
this.kv = createClientKV(env, flavor);
|
|
7523
|
-
this.maxConcurrency = flavor === "miniflare" ? 50 : 6;
|
|
7524
7516
|
}
|
|
7525
7517
|
/**
|
|
7526
7518
|
* Build a KV storage key from a file hash
|
|
@@ -7613,10 +7605,8 @@ var ThemeFileStorage = class {
|
|
|
7613
7605
|
totalSize,
|
|
7614
7606
|
trace
|
|
7615
7607
|
});
|
|
7616
|
-
const results = await
|
|
7617
|
-
batches
|
|
7618
|
-
(batch) => this.loadBatch(batch),
|
|
7619
|
-
{ concurrency: Math.min(this.maxConcurrency, batches.length) }
|
|
7608
|
+
const results = await Promise.all(
|
|
7609
|
+
batches.map((batch) => this.loadBatch(batch))
|
|
7620
7610
|
);
|
|
7621
7611
|
const mergedConfigs = this.mergeResults(configs, results);
|
|
7622
7612
|
const loadedCount = mergedConfigs.filter((c) => c.file_data).length;
|
|
@@ -7681,10 +7671,8 @@ var ThemeFileStorage = class {
|
|
|
7681
7671
|
}
|
|
7682
7672
|
const existing = /* @__PURE__ */ new Set();
|
|
7683
7673
|
const batches = this.planGetBatches(configs);
|
|
7684
|
-
const results = await
|
|
7685
|
-
batches
|
|
7686
|
-
(batch) => this.kv.get(batch.keys),
|
|
7687
|
-
{ concurrency: this.maxConcurrency }
|
|
7674
|
+
const results = await Promise.all(
|
|
7675
|
+
batches.map((batch) => this.kv.get(batch.keys))
|
|
7688
7676
|
);
|
|
7689
7677
|
for (const batchResult of results) {
|
|
7690
7678
|
for (const [key, value] of batchResult.entries()) {
|
|
@@ -7748,15 +7736,13 @@ var ThemeFileStorage = class {
|
|
|
7748
7736
|
skippedExisting: existing.size,
|
|
7749
7737
|
trace
|
|
7750
7738
|
});
|
|
7751
|
-
await
|
|
7752
|
-
toWrite
|
|
7753
|
-
async (config) => {
|
|
7739
|
+
await Promise.all(
|
|
7740
|
+
toWrite.map(async (config) => {
|
|
7754
7741
|
const key = this.buildKey(config.hash);
|
|
7755
7742
|
const metadata = config.file?.content_type ? { content_type: config.file.content_type } : void 0;
|
|
7756
|
-
await this.kv.put(key, config.file_data, metadata);
|
|
7743
|
+
await this.kv.put(key, config.file_data, { metadata });
|
|
7757
7744
|
result.written++;
|
|
7758
|
-
}
|
|
7759
|
-
{ concurrency: this.maxConcurrency }
|
|
7745
|
+
})
|
|
7760
7746
|
);
|
|
7761
7747
|
}
|
|
7762
7748
|
logger.info("[ThemeFileStorage] Put files complete", {
|
|
@@ -7770,378 +7756,6 @@ var ThemeFileStorage = class {
|
|
|
7770
7756
|
}
|
|
7771
7757
|
};
|
|
7772
7758
|
|
|
7773
|
-
// src/cache/constants.ts
|
|
7774
|
-
var SECOND = 1e3;
|
|
7775
|
-
var MINUTE = 60 * SECOND;
|
|
7776
|
-
var HOUR = 60 * MINUTE;
|
|
7777
|
-
var DAY = 24 * HOUR;
|
|
7778
|
-
var YEAR = 365 * DAY;
|
|
7779
|
-
var SHORT_TTL = 5 * SECOND;
|
|
7780
|
-
|
|
7781
|
-
// src/cache/worker-html-cache.ts
|
|
7782
|
-
var CACHE_NAME = "swell-html-v0";
|
|
7783
|
-
var CACHE_KEY_ORIGIN = "https://cache.swell.store";
|
|
7784
|
-
var TTL_CONFIG = {
|
|
7785
|
-
LIVE: {
|
|
7786
|
-
DEFAULT: 20,
|
|
7787
|
-
HOME: 20,
|
|
7788
|
-
PRODUCT: 20,
|
|
7789
|
-
COLLECTION: 20,
|
|
7790
|
-
PAGE: 20,
|
|
7791
|
-
BLOG: 20,
|
|
7792
|
-
SWR: 180
|
|
7793
|
-
},
|
|
7794
|
-
PREVIEW: {
|
|
7795
|
-
DEFAULT: 20,
|
|
7796
|
-
HOME: 20,
|
|
7797
|
-
PRODUCT: 20,
|
|
7798
|
-
COLLECTION: 20,
|
|
7799
|
-
PAGE: 20,
|
|
7800
|
-
BLOG: 20,
|
|
7801
|
-
SWR: 180
|
|
7802
|
-
}
|
|
7803
|
-
};
|
|
7804
|
-
var WorkerHtmlCache = class {
|
|
7805
|
-
epoch;
|
|
7806
|
-
constructor(epoch) {
|
|
7807
|
-
this.epoch = epoch;
|
|
7808
|
-
}
|
|
7809
|
-
async get(request) {
|
|
7810
|
-
const trace = createTraceId();
|
|
7811
|
-
if (request.method !== "GET") {
|
|
7812
|
-
logger.debug("[SDK Html-cache] non-cacheable", { trace });
|
|
7813
|
-
return { found: false, cacheable: false };
|
|
7814
|
-
}
|
|
7815
|
-
if (!this.isCacheable(request)) {
|
|
7816
|
-
logger.debug("[SDK Html-cache] non-cacheable", { trace });
|
|
7817
|
-
return { found: false, cacheable: false };
|
|
7818
|
-
}
|
|
7819
|
-
try {
|
|
7820
|
-
const cache = await caches.open(CACHE_NAME + this.epoch);
|
|
7821
|
-
const cacheKey = this.buildCacheKey(request);
|
|
7822
|
-
const cached = await cache.match(cacheKey);
|
|
7823
|
-
if (!cached) {
|
|
7824
|
-
logger.debug("[SDK Html-cache] cacheable, MISS", { trace });
|
|
7825
|
-
return { found: false, cacheable: true };
|
|
7826
|
-
}
|
|
7827
|
-
const age = this.getResponseAge(cached);
|
|
7828
|
-
const ttl = parseInt(cached.headers.get("X-Original-TTL") || "") || this.getTTLForRequest(request);
|
|
7829
|
-
const swr = parseInt(cached.headers.get("X-Original-SWR") || "") || this.getSWRForRequest(request);
|
|
7830
|
-
const isStale = age >= ttl;
|
|
7831
|
-
const isExpired = age >= ttl + swr;
|
|
7832
|
-
if (!isExpired) {
|
|
7833
|
-
logger.debug("[SDK Html-cache] cacheable, HIT", {
|
|
7834
|
-
stale: isStale,
|
|
7835
|
-
age,
|
|
7836
|
-
trace
|
|
7837
|
-
});
|
|
7838
|
-
const clientResponse = this.buildClientResponse(
|
|
7839
|
-
cached,
|
|
7840
|
-
ttl,
|
|
7841
|
-
swr,
|
|
7842
|
-
isStale,
|
|
7843
|
-
age
|
|
7844
|
-
);
|
|
7845
|
-
return {
|
|
7846
|
-
found: true,
|
|
7847
|
-
stale: isStale,
|
|
7848
|
-
response: clientResponse,
|
|
7849
|
-
cacheable: true,
|
|
7850
|
-
age: Math.floor(age)
|
|
7851
|
-
};
|
|
7852
|
-
}
|
|
7853
|
-
logger.debug("[SDK Html-cache] cacheable, hit, expired", { trace });
|
|
7854
|
-
return { found: false, cacheable: true };
|
|
7855
|
-
} catch (_) {
|
|
7856
|
-
logger.warn("[SDK Html-cache] no get support", { trace });
|
|
7857
|
-
return null;
|
|
7858
|
-
}
|
|
7859
|
-
}
|
|
7860
|
-
// 304 support
|
|
7861
|
-
async getWithConditionals(request) {
|
|
7862
|
-
const result = await this.get(request);
|
|
7863
|
-
if (!result?.found || result.stale) {
|
|
7864
|
-
return result;
|
|
7865
|
-
}
|
|
7866
|
-
const ifModifiedSince = request.headers.get("If-Modified-Since");
|
|
7867
|
-
const ifNoneMatch = request.headers.get("If-None-Match");
|
|
7868
|
-
if ((ifModifiedSince || ifNoneMatch) && result.response) {
|
|
7869
|
-
const lastModified = result.response.headers.get("Last-Modified");
|
|
7870
|
-
const etag = result.response.headers.get("ETag");
|
|
7871
|
-
if (this.checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag)) {
|
|
7872
|
-
result.notModified = true;
|
|
7873
|
-
result.conditional304 = new Response(null, {
|
|
7874
|
-
status: 304,
|
|
7875
|
-
headers: {
|
|
7876
|
-
"Last-Modified": lastModified || "",
|
|
7877
|
-
ETag: etag || "",
|
|
7878
|
-
"Cache-Control": result.response.headers.get("Cache-Control") || "",
|
|
7879
|
-
"Cloudflare-CDN-Cache-Control": result.response.headers.get("Cloudflare-CDN-Cache-Control") || "",
|
|
7880
|
-
"X-Cache-Status": "HIT-304"
|
|
7881
|
-
}
|
|
7882
|
-
});
|
|
7883
|
-
}
|
|
7884
|
-
}
|
|
7885
|
-
return result;
|
|
7886
|
-
}
|
|
7887
|
-
checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag) {
|
|
7888
|
-
if (ifNoneMatch && etag) {
|
|
7889
|
-
return ifNoneMatch === etag;
|
|
7890
|
-
}
|
|
7891
|
-
if (ifModifiedSince && lastModified) {
|
|
7892
|
-
const ifModDate = new Date(ifModifiedSince);
|
|
7893
|
-
const lastModDate = new Date(lastModified);
|
|
7894
|
-
return !isNaN(ifModDate.getTime()) && !isNaN(lastModDate.getTime()) && ifModDate >= lastModDate;
|
|
7895
|
-
}
|
|
7896
|
-
return false;
|
|
7897
|
-
}
|
|
7898
|
-
createRevalidationRequest(request) {
|
|
7899
|
-
const headers = new Headers(request.headers);
|
|
7900
|
-
headers.set("X-Cache-Bypass", "revalidation");
|
|
7901
|
-
headers.delete("If-None-Match");
|
|
7902
|
-
headers.delete("If-Modified-Since");
|
|
7903
|
-
headers.delete("Cache-Control");
|
|
7904
|
-
headers.delete("Pragma");
|
|
7905
|
-
return new Request(request.url, {
|
|
7906
|
-
method: "GET",
|
|
7907
|
-
headers
|
|
7908
|
-
});
|
|
7909
|
-
}
|
|
7910
|
-
async put(request, response) {
|
|
7911
|
-
const trace = createTraceId();
|
|
7912
|
-
if (request.method !== "GET" || !response.ok) {
|
|
7913
|
-
logger.debug("[SDK Html-cache] put skipped", { trace });
|
|
7914
|
-
return;
|
|
7915
|
-
}
|
|
7916
|
-
if (!this.isCacheable(request) || !this.isResponseCacheable(response)) {
|
|
7917
|
-
logger.debug("[SDK Html-cache] put skipped, non-cacheable", {
|
|
7918
|
-
trace
|
|
7919
|
-
});
|
|
7920
|
-
return;
|
|
7921
|
-
}
|
|
7922
|
-
try {
|
|
7923
|
-
const cache = await caches.open(CACHE_NAME + this.epoch);
|
|
7924
|
-
const cacheKey = this.buildCacheKey(request);
|
|
7925
|
-
await cache.delete(cacheKey);
|
|
7926
|
-
const ttl = this.getTTLForRequest(request);
|
|
7927
|
-
const swr = this.getSWRForRequest(request);
|
|
7928
|
-
const headers = new Headers(response.headers);
|
|
7929
|
-
const existingCacheControl = response.headers.get("Cache-Control");
|
|
7930
|
-
if (!existingCacheControl || existingCacheControl === "public") {
|
|
7931
|
-
const internalMaxAge = ttl + swr;
|
|
7932
|
-
headers.set("Cache-Control", `public, max-age=${internalMaxAge}`);
|
|
7933
|
-
}
|
|
7934
|
-
const cacheTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
7935
|
-
headers.set("X-Cache-Time", cacheTime);
|
|
7936
|
-
headers.set("X-Original-TTL", ttl.toString());
|
|
7937
|
-
headers.set("X-Original-SWR", swr.toString());
|
|
7938
|
-
if (!headers.get("Last-Modified")) {
|
|
7939
|
-
headers.set("Last-Modified", new Date(cacheTime).toUTCString());
|
|
7940
|
-
}
|
|
7941
|
-
const cacheableResponse = new Response(response.body, {
|
|
7942
|
-
status: response.status,
|
|
7943
|
-
statusText: response.statusText,
|
|
7944
|
-
headers
|
|
7945
|
-
});
|
|
7946
|
-
await cache.put(cacheKey, cacheableResponse);
|
|
7947
|
-
logger.debug("[SDK Html-cache] put done", { trace });
|
|
7948
|
-
} catch (_) {
|
|
7949
|
-
logger.warn("[SDK Html-cache] no put support", { trace });
|
|
7950
|
-
}
|
|
7951
|
-
}
|
|
7952
|
-
buildClientResponse(cachedResponse, ttl, swr, isStale, age) {
|
|
7953
|
-
const headers = new Headers(cachedResponse.headers);
|
|
7954
|
-
headers.set(
|
|
7955
|
-
"Cache-Control",
|
|
7956
|
-
`public, max-age=${ttl}, stale-while-revalidate=${swr}`
|
|
7957
|
-
);
|
|
7958
|
-
headers.set(
|
|
7959
|
-
"Cloudflare-CDN-Cache-Control",
|
|
7960
|
-
`public, s-maxage=${ttl}, stale-while-revalidate=${swr}, stale-if-error=60`
|
|
7961
|
-
);
|
|
7962
|
-
const cacheTime = headers.get("X-Cache-Time");
|
|
7963
|
-
if (cacheTime) {
|
|
7964
|
-
const lastModified = new Date(cacheTime).toUTCString();
|
|
7965
|
-
headers.set("Last-Modified", lastModified);
|
|
7966
|
-
}
|
|
7967
|
-
headers.set("X-Cache-Status", isStale ? "STALE" : "HIT");
|
|
7968
|
-
headers.set("X-Cache-Age", Math.floor(age).toString());
|
|
7969
|
-
headers.delete("X-Original-TTL");
|
|
7970
|
-
headers.delete("X-Original-SWR");
|
|
7971
|
-
headers.delete("X-Cache-Time");
|
|
7972
|
-
return new Response(cachedResponse.body, {
|
|
7973
|
-
status: cachedResponse.status,
|
|
7974
|
-
statusText: cachedResponse.statusText,
|
|
7975
|
-
headers
|
|
7976
|
-
});
|
|
7977
|
-
}
|
|
7978
|
-
buildCacheKey(request) {
|
|
7979
|
-
const url = new URL(request.url);
|
|
7980
|
-
const versionHash = this.generateVersionHash(request.headers);
|
|
7981
|
-
const normalizedQuery = this.normalizeSearchParams(url.searchParams);
|
|
7982
|
-
const cacheKeyPath = `${versionHash}${url.pathname}`;
|
|
7983
|
-
const keyUrl = new URL(`${CACHE_KEY_ORIGIN}${cacheKeyPath}`);
|
|
7984
|
-
if (normalizedQuery) {
|
|
7985
|
-
keyUrl.search = `?${normalizedQuery}`;
|
|
7986
|
-
}
|
|
7987
|
-
const sanitizedHeaders = this.sanitizeHeaders(request.headers);
|
|
7988
|
-
return new Request(keyUrl.toString(), {
|
|
7989
|
-
method: "GET",
|
|
7990
|
-
headers: sanitizedHeaders
|
|
7991
|
-
});
|
|
7992
|
-
}
|
|
7993
|
-
sanitizeHeaders(originalHeaders) {
|
|
7994
|
-
const CACHE_RELEVANT_HEADERS = [
|
|
7995
|
-
// Content negotiation (affects response format)
|
|
7996
|
-
"accept",
|
|
7997
|
-
"accept-language"
|
|
7998
|
-
];
|
|
7999
|
-
const sanitized = new Headers();
|
|
8000
|
-
CACHE_RELEVANT_HEADERS.forEach((header) => {
|
|
8001
|
-
const value = originalHeaders.get(header);
|
|
8002
|
-
if (value) {
|
|
8003
|
-
sanitized.set(header, value);
|
|
8004
|
-
}
|
|
8005
|
-
});
|
|
8006
|
-
return sanitized;
|
|
8007
|
-
}
|
|
8008
|
-
generateVersionHash(headers) {
|
|
8009
|
-
const swellData = this.extractSwellData(headers);
|
|
8010
|
-
const versionFactors = {
|
|
8011
|
-
store: headers.get("swell-storefront-id") || "",
|
|
8012
|
-
auth: headers.get("swell-access-token") || "",
|
|
8013
|
-
theme: headers.get("swell-theme-version-hash") || "",
|
|
8014
|
-
modified: headers.get("swell-cache-modified") || "",
|
|
8015
|
-
currency: swellData["swell-currency"] || "USD",
|
|
8016
|
-
locale: headers.get("x-locale") || headers.get("accept-language")?.split(",")[0] || "default",
|
|
8017
|
-
context: headers.get("swell-storefront-context"),
|
|
8018
|
-
epoch: this.epoch
|
|
8019
|
-
};
|
|
8020
|
-
return md5(JSON.stringify(versionFactors));
|
|
8021
|
-
}
|
|
8022
|
-
extractSwellData(headers) {
|
|
8023
|
-
const cookie = headers.get("cookie");
|
|
8024
|
-
if (!cookie) return {};
|
|
8025
|
-
const swellDataMatch = cookie.match(/swell-data=([^;]+)/);
|
|
8026
|
-
if (!swellDataMatch) return {};
|
|
8027
|
-
try {
|
|
8028
|
-
const parsed = JSON.parse(decodeURIComponent(swellDataMatch[1]));
|
|
8029
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
8030
|
-
return parsed;
|
|
8031
|
-
}
|
|
8032
|
-
return {};
|
|
8033
|
-
} catch {
|
|
8034
|
-
return {};
|
|
8035
|
-
}
|
|
8036
|
-
}
|
|
8037
|
-
isCacheable(request) {
|
|
8038
|
-
const url = new URL(request.url);
|
|
8039
|
-
const headers = request.headers;
|
|
8040
|
-
if (headers.get("swell-deployment-mode") === "editor") {
|
|
8041
|
-
return false;
|
|
8042
|
-
}
|
|
8043
|
-
const skipPaths = ["/checkout"];
|
|
8044
|
-
if (skipPaths.some((path) => url.pathname.startsWith(path))) {
|
|
8045
|
-
return false;
|
|
8046
|
-
}
|
|
8047
|
-
if (headers.get("cache-control")?.includes("no-cache")) {
|
|
8048
|
-
return false;
|
|
8049
|
-
}
|
|
8050
|
-
return true;
|
|
8051
|
-
}
|
|
8052
|
-
isResponseCacheable(response) {
|
|
8053
|
-
const contentType = response.headers.get("content-type");
|
|
8054
|
-
if (!contentType?.includes("text/html")) {
|
|
8055
|
-
return false;
|
|
8056
|
-
}
|
|
8057
|
-
if (response.headers.get("set-cookie")) {
|
|
8058
|
-
return false;
|
|
8059
|
-
}
|
|
8060
|
-
const cacheControl = response.headers.get("cache-control");
|
|
8061
|
-
if (cacheControl?.includes("no-store") || cacheControl?.includes("private")) {
|
|
8062
|
-
return false;
|
|
8063
|
-
}
|
|
8064
|
-
return true;
|
|
8065
|
-
}
|
|
8066
|
-
getDeploymentMode(headers) {
|
|
8067
|
-
const mode = headers.get("swell-deployment-mode");
|
|
8068
|
-
if (mode === "preview" || mode === "editor") {
|
|
8069
|
-
return mode;
|
|
8070
|
-
}
|
|
8071
|
-
return "live";
|
|
8072
|
-
}
|
|
8073
|
-
getTTLForRequest(request) {
|
|
8074
|
-
const url = new URL(request.url);
|
|
8075
|
-
const path = url.pathname;
|
|
8076
|
-
const mode = this.getDeploymentMode(request.headers);
|
|
8077
|
-
if (mode === "editor") {
|
|
8078
|
-
return 0;
|
|
8079
|
-
}
|
|
8080
|
-
const config = mode === "preview" ? TTL_CONFIG.PREVIEW : TTL_CONFIG.LIVE;
|
|
8081
|
-
if (path === "/") {
|
|
8082
|
-
return config.HOME;
|
|
8083
|
-
}
|
|
8084
|
-
if (path.startsWith("/products/")) {
|
|
8085
|
-
return config.PRODUCT;
|
|
8086
|
-
}
|
|
8087
|
-
if (path.startsWith("/categories/")) {
|
|
8088
|
-
return config.COLLECTION;
|
|
8089
|
-
}
|
|
8090
|
-
if (path.startsWith("/pages/")) {
|
|
8091
|
-
return config.PAGE;
|
|
8092
|
-
}
|
|
8093
|
-
if (path.startsWith("/blogs/")) {
|
|
8094
|
-
return config.BLOG;
|
|
8095
|
-
}
|
|
8096
|
-
return config.DEFAULT;
|
|
8097
|
-
}
|
|
8098
|
-
getSWRForRequest(request) {
|
|
8099
|
-
const mode = this.getDeploymentMode(request.headers);
|
|
8100
|
-
if (mode === "editor") {
|
|
8101
|
-
return 0;
|
|
8102
|
-
}
|
|
8103
|
-
return mode === "preview" ? TTL_CONFIG.PREVIEW.SWR : TTL_CONFIG.LIVE.SWR;
|
|
8104
|
-
}
|
|
8105
|
-
getResponseAge(response) {
|
|
8106
|
-
const cacheTime = response.headers.get("X-Cache-Time");
|
|
8107
|
-
if (!cacheTime) {
|
|
8108
|
-
return Infinity;
|
|
8109
|
-
}
|
|
8110
|
-
const cacheDate = new Date(cacheTime);
|
|
8111
|
-
if (isNaN(cacheDate.getTime())) {
|
|
8112
|
-
return Infinity;
|
|
8113
|
-
}
|
|
8114
|
-
const age = (Date.now() - cacheDate.getTime()) / 1e3;
|
|
8115
|
-
return Math.max(0, age);
|
|
8116
|
-
}
|
|
8117
|
-
normalizeSearchParams(searchParams) {
|
|
8118
|
-
const ignoredParams = [
|
|
8119
|
-
"utm_source",
|
|
8120
|
-
"utm_medium",
|
|
8121
|
-
"utm_campaign",
|
|
8122
|
-
"utm_content",
|
|
8123
|
-
"utm_term",
|
|
8124
|
-
"fbclid",
|
|
8125
|
-
"gclid",
|
|
8126
|
-
"gbraid",
|
|
8127
|
-
"wbraid",
|
|
8128
|
-
"ref",
|
|
8129
|
-
"source",
|
|
8130
|
-
"mc_cid",
|
|
8131
|
-
"mc_eid"
|
|
8132
|
-
];
|
|
8133
|
-
const relevantParams = [];
|
|
8134
|
-
searchParams.forEach((value, key) => {
|
|
8135
|
-
if (!ignoredParams.includes(key)) {
|
|
8136
|
-
relevantParams.push(
|
|
8137
|
-
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
|
8138
|
-
);
|
|
8139
|
-
}
|
|
8140
|
-
});
|
|
8141
|
-
return relevantParams.sort().join("&");
|
|
8142
|
-
}
|
|
8143
|
-
};
|
|
8144
|
-
|
|
8145
7759
|
// src/resources/addresses.ts
|
|
8146
7760
|
var SwellAddresses = class extends SwellStorefrontCollection {
|
|
8147
7761
|
constructor(swell, query) {
|
|
@@ -16283,9 +15897,16 @@ function convertToShopifySorting(value) {
|
|
|
16283
15897
|
function getProducts(instance, object, mapper) {
|
|
16284
15898
|
return deferWith(object, (object2) => {
|
|
16285
15899
|
const { page, limit: limit2 } = instance.swell.queryParams;
|
|
16286
|
-
const
|
|
16287
|
-
|
|
16288
|
-
|
|
15900
|
+
const productQuery = {
|
|
15901
|
+
$variants: true
|
|
15902
|
+
};
|
|
15903
|
+
if (typeof object2.id === "string" && object2.id !== "all") {
|
|
15904
|
+
productQuery.category = object2.id;
|
|
15905
|
+
}
|
|
15906
|
+
if (object2.performed && typeof object2.query === "string") {
|
|
15907
|
+
productQuery.search = object2.query;
|
|
15908
|
+
}
|
|
15909
|
+
const filterQuery = productQueryWithFilters(instance.swell, productQuery);
|
|
16289
15910
|
const products = new SwellStorefrontCollection(
|
|
16290
15911
|
instance.swell,
|
|
16291
15912
|
"products",
|
|
@@ -16400,7 +16021,7 @@ function ShopifyAddress(instance, address, account) {
|
|
|
16400
16021
|
function joinAddressLines(...props) {
|
|
16401
16022
|
return props.filter(Boolean).join("\n");
|
|
16402
16023
|
}
|
|
16403
|
-
function ShopifyCountry(
|
|
16024
|
+
function ShopifyCountry(_instance2, countryCode) {
|
|
16404
16025
|
const currencyCode = getCurrencyByCountry(countryCode) || "USD";
|
|
16405
16026
|
return new ShopifyResource(
|
|
16406
16027
|
{
|
|
@@ -16733,7 +16354,7 @@ async function resolveLastOrder(instance, account) {
|
|
|
16733
16354
|
}
|
|
16734
16355
|
|
|
16735
16356
|
// src/compatibility/shopify-objects/font.ts
|
|
16736
|
-
function ShopifyFont(
|
|
16357
|
+
function ShopifyFont(_instance2, font) {
|
|
16737
16358
|
if (font instanceof ShopifyResource) {
|
|
16738
16359
|
return font.clone();
|
|
16739
16360
|
}
|
|
@@ -16744,7 +16365,7 @@ function ShopifyFont(_instance, font) {
|
|
|
16744
16365
|
family: font.family,
|
|
16745
16366
|
style: font.style,
|
|
16746
16367
|
"system?": font.system,
|
|
16747
|
-
variants: font.variants.map((variant) => ShopifyFont(
|
|
16368
|
+
variants: font.variants.map((variant) => ShopifyFont(_instance2, variant)),
|
|
16748
16369
|
weight: font.weight
|
|
16749
16370
|
});
|
|
16750
16371
|
}
|
|
@@ -16757,7 +16378,7 @@ var SHOPIFY_FORMS = {
|
|
|
16757
16378
|
})
|
|
16758
16379
|
}
|
|
16759
16380
|
};
|
|
16760
|
-
function ShopifyForm(
|
|
16381
|
+
function ShopifyForm(_instance2, form) {
|
|
16761
16382
|
if (form instanceof ShopifyResource) {
|
|
16762
16383
|
return form.clone();
|
|
16763
16384
|
}
|
|
@@ -17054,7 +16675,7 @@ function ShopifyRecommendations(instance, product) {
|
|
|
17054
16675
|
}
|
|
17055
16676
|
|
|
17056
16677
|
// src/compatibility/shopify-objects/page.ts
|
|
17057
|
-
function ShopifyPage(
|
|
16678
|
+
function ShopifyPage(_instance2, page) {
|
|
17058
16679
|
if (page instanceof ShopifyResource) {
|
|
17059
16680
|
return page.clone();
|
|
17060
16681
|
}
|
|
@@ -17100,11 +16721,11 @@ function ShopifySearch(instance, search) {
|
|
|
17100
16721
|
(filter) => ShopifyFilter(instance, filter)
|
|
17101
16722
|
);
|
|
17102
16723
|
}),
|
|
17103
|
-
performed:
|
|
16724
|
+
performed: deferWith(search, (search2) => search2.performed),
|
|
17104
16725
|
results: defer(async () => (await resolveProducts())?.results ?? []),
|
|
17105
16726
|
results_count: defer(async () => (await resolveProducts())?.count || 0),
|
|
17106
16727
|
sort_by: defer(() => search.sort),
|
|
17107
|
-
sort_options:
|
|
16728
|
+
sort_options: deferWith(search, (search2) => search2.sort_options),
|
|
17108
16729
|
terms: defer(() => search.query),
|
|
17109
16730
|
types: ["product"]
|
|
17110
16731
|
});
|
|
@@ -18266,7 +17887,7 @@ ${injects.join("\n")}</script>`;
|
|
|
18266
17887
|
};
|
|
18267
17888
|
|
|
18268
17889
|
// src/compatibility/shopify-objects/template.ts
|
|
18269
|
-
function ShopifyTemplate(
|
|
17890
|
+
function ShopifyTemplate(_instance2, template) {
|
|
18270
17891
|
return new ShopifyResource(
|
|
18271
17892
|
{
|
|
18272
17893
|
directory: template.path,
|
|
@@ -20578,7 +20199,7 @@ var ThemeLoader = class _ThemeLoader {
|
|
|
20578
20199
|
flavor,
|
|
20579
20200
|
trace
|
|
20580
20201
|
});
|
|
20581
|
-
const storage = new
|
|
20202
|
+
const storage = new ThemeFileCache(this.swell.workerEnv, flavor);
|
|
20582
20203
|
const result = await storage.putFiles(configs);
|
|
20583
20204
|
if (result.warnings.length > 0) {
|
|
20584
20205
|
logger.warn("[ThemeLoader] Theme cache updated with warnings", {
|
|
@@ -20616,7 +20237,7 @@ var ThemeLoader = class _ThemeLoader {
|
|
|
20616
20237
|
total: configMetadata.length
|
|
20617
20238
|
});
|
|
20618
20239
|
const flavor = getKVFlavor(this.swell.workerEnv);
|
|
20619
|
-
const storage = new
|
|
20240
|
+
const storage = new ThemeFileCache(this.swell.workerEnv, flavor);
|
|
20620
20241
|
const kvHydrated = await storage.getFiles(configMetadata);
|
|
20621
20242
|
const completeConfigs = await this.ensureConfigsHaveData(kvHydrated);
|
|
20622
20243
|
for (const config of completeConfigs) {
|
|
@@ -22838,6 +22459,547 @@ function getResourceQuery(slug, query) {
|
|
|
22838
22459
|
...query
|
|
22839
22460
|
};
|
|
22840
22461
|
}
|
|
22462
|
+
|
|
22463
|
+
// src/cache/html-cache/html-cache.ts
|
|
22464
|
+
var CACHE_KEY_ORIGIN = "https://cache.swell.store";
|
|
22465
|
+
var TTL_CONFIG = {
|
|
22466
|
+
LIVE: {
|
|
22467
|
+
DEFAULT: 20,
|
|
22468
|
+
HOME: 20,
|
|
22469
|
+
PRODUCT: 20,
|
|
22470
|
+
COLLECTION: 20,
|
|
22471
|
+
PAGE: 20,
|
|
22472
|
+
BLOG: 20,
|
|
22473
|
+
SWR: 60 * 60 * 24 * 7
|
|
22474
|
+
// 1 week
|
|
22475
|
+
},
|
|
22476
|
+
PREVIEW: {
|
|
22477
|
+
DEFAULT: 10,
|
|
22478
|
+
HOME: 10,
|
|
22479
|
+
PRODUCT: 10,
|
|
22480
|
+
COLLECTION: 10,
|
|
22481
|
+
PAGE: 10,
|
|
22482
|
+
BLOG: 10,
|
|
22483
|
+
SWR: 60 * 60 * 24 * 7
|
|
22484
|
+
// 1 week
|
|
22485
|
+
}
|
|
22486
|
+
};
|
|
22487
|
+
var HtmlCache = class {
|
|
22488
|
+
epoch;
|
|
22489
|
+
backend;
|
|
22490
|
+
constructor(epoch, backend) {
|
|
22491
|
+
this.epoch = epoch;
|
|
22492
|
+
this.backend = backend;
|
|
22493
|
+
}
|
|
22494
|
+
async get(request) {
|
|
22495
|
+
const trace = createTraceId();
|
|
22496
|
+
if (request.method !== "GET") {
|
|
22497
|
+
logger.debug("[SDK Html-cache] non-cacheable method", { trace });
|
|
22498
|
+
return { found: false, cacheable: false };
|
|
22499
|
+
}
|
|
22500
|
+
if (!this.isCacheable(request)) {
|
|
22501
|
+
logger.debug("[SDK Html-cache] non-cacheable request", { trace });
|
|
22502
|
+
return { found: false, cacheable: false };
|
|
22503
|
+
}
|
|
22504
|
+
try {
|
|
22505
|
+
const cacheKey = this.buildCacheKey(request);
|
|
22506
|
+
const entry = await this.backend.read(cacheKey);
|
|
22507
|
+
if (!entry) {
|
|
22508
|
+
logger.debug("[SDK Html-cache] cacheable, MISS", { trace });
|
|
22509
|
+
return { found: false, cacheable: true };
|
|
22510
|
+
}
|
|
22511
|
+
const age = this.getEntryAge(entry);
|
|
22512
|
+
const { ttl, swr } = entry;
|
|
22513
|
+
const isStale = age >= ttl;
|
|
22514
|
+
const isExpired = age >= ttl + swr;
|
|
22515
|
+
if (!isExpired) {
|
|
22516
|
+
logger.debug("[SDK Html-cache] cacheable, HIT", {
|
|
22517
|
+
stale: isStale,
|
|
22518
|
+
age,
|
|
22519
|
+
trace
|
|
22520
|
+
});
|
|
22521
|
+
const clientResponse = this.buildClientResponse(entry, isStale, age);
|
|
22522
|
+
return {
|
|
22523
|
+
found: true,
|
|
22524
|
+
stale: isStale,
|
|
22525
|
+
response: clientResponse,
|
|
22526
|
+
cacheable: true,
|
|
22527
|
+
age: Math.floor(age)
|
|
22528
|
+
};
|
|
22529
|
+
}
|
|
22530
|
+
logger.debug("[SDK Html-cache] cacheable, hit, expired", { trace });
|
|
22531
|
+
return { found: false, cacheable: true };
|
|
22532
|
+
} catch (e) {
|
|
22533
|
+
logger.warn("[SDK Html-cache] get failed", {
|
|
22534
|
+
trace,
|
|
22535
|
+
error: e instanceof Error ? e.message : String(e)
|
|
22536
|
+
});
|
|
22537
|
+
return null;
|
|
22538
|
+
}
|
|
22539
|
+
}
|
|
22540
|
+
async getWithConditionals(request) {
|
|
22541
|
+
const result = await this.get(request);
|
|
22542
|
+
if (!result?.found || result.stale) {
|
|
22543
|
+
return result;
|
|
22544
|
+
}
|
|
22545
|
+
const ifModifiedSince = request.headers.get("If-Modified-Since");
|
|
22546
|
+
const ifNoneMatch = request.headers.get("If-None-Match");
|
|
22547
|
+
if ((ifModifiedSince || ifNoneMatch) && result.response) {
|
|
22548
|
+
const lastModified = result.response.headers.get("Last-Modified");
|
|
22549
|
+
const etag = result.response.headers.get("ETag");
|
|
22550
|
+
if (this.checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag)) {
|
|
22551
|
+
result.notModified = true;
|
|
22552
|
+
result.conditional304 = new Response(null, {
|
|
22553
|
+
status: 304,
|
|
22554
|
+
headers: {
|
|
22555
|
+
"Last-Modified": lastModified || "",
|
|
22556
|
+
ETag: etag || "",
|
|
22557
|
+
"Cache-Control": result.response.headers.get("Cache-Control") || "",
|
|
22558
|
+
"Cloudflare-CDN-Cache-Control": result.response.headers.get("Cloudflare-CDN-Cache-Control") || "",
|
|
22559
|
+
"X-Cache-Status": "HIT-304"
|
|
22560
|
+
}
|
|
22561
|
+
});
|
|
22562
|
+
}
|
|
22563
|
+
}
|
|
22564
|
+
return result;
|
|
22565
|
+
}
|
|
22566
|
+
async put(request, response) {
|
|
22567
|
+
const trace = createTraceId();
|
|
22568
|
+
if (request.method !== "GET" || !response.ok) {
|
|
22569
|
+
logger.debug("[SDK Html-cache] put skipped, invalid method or response", {
|
|
22570
|
+
trace
|
|
22571
|
+
});
|
|
22572
|
+
return;
|
|
22573
|
+
}
|
|
22574
|
+
if (!this.isCacheable(request) || !this.isResponseCacheable(response)) {
|
|
22575
|
+
logger.debug("[SDK Html-cache] put skipped, non-cacheable", { trace });
|
|
22576
|
+
return;
|
|
22577
|
+
}
|
|
22578
|
+
try {
|
|
22579
|
+
let lowercaseHeaders2 = function(headers2) {
|
|
22580
|
+
const out = {};
|
|
22581
|
+
headers2.forEach((value, key) => {
|
|
22582
|
+
out[key.toLowerCase()] = value;
|
|
22583
|
+
});
|
|
22584
|
+
return out;
|
|
22585
|
+
};
|
|
22586
|
+
var lowercaseHeaders = lowercaseHeaders2;
|
|
22587
|
+
const cacheKey = this.buildCacheKey(request);
|
|
22588
|
+
const ttl = this.getTTLForRequest(request);
|
|
22589
|
+
const swr = this.getSWRForRequest(request);
|
|
22590
|
+
const body = await response.text();
|
|
22591
|
+
const cacheTimeISO = (/* @__PURE__ */ new Date()).toISOString();
|
|
22592
|
+
const headers = lowercaseHeaders2(response.headers);
|
|
22593
|
+
const entry = {
|
|
22594
|
+
status: response.status,
|
|
22595
|
+
statusText: response.statusText,
|
|
22596
|
+
headers,
|
|
22597
|
+
body,
|
|
22598
|
+
cacheTimeISO,
|
|
22599
|
+
ttl,
|
|
22600
|
+
swr,
|
|
22601
|
+
etag: this.quoteETag(headers["etag"] || md5(body)),
|
|
22602
|
+
lastModifiedUTC: headers["last-modified"] || new Date(cacheTimeISO).toUTCString()
|
|
22603
|
+
};
|
|
22604
|
+
const hardExpireSeconds = ttl + swr;
|
|
22605
|
+
await this.backend.write(cacheKey, entry, hardExpireSeconds);
|
|
22606
|
+
logger.debug("[SDK Html-cache] put done", { trace });
|
|
22607
|
+
} catch (e) {
|
|
22608
|
+
logger.warn("[SDK Html-cache] put failed", {
|
|
22609
|
+
trace,
|
|
22610
|
+
error: e instanceof Error ? e.message : String(e)
|
|
22611
|
+
});
|
|
22612
|
+
}
|
|
22613
|
+
}
|
|
22614
|
+
async delete(requestOrKey) {
|
|
22615
|
+
try {
|
|
22616
|
+
const key = typeof requestOrKey === "string" ? requestOrKey : this.buildCacheKey(requestOrKey);
|
|
22617
|
+
if (this.backend.delete) {
|
|
22618
|
+
await this.backend.delete(key);
|
|
22619
|
+
}
|
|
22620
|
+
} catch (e) {
|
|
22621
|
+
logger.warn("[SDK Html-cache] delete failed", {
|
|
22622
|
+
error: e instanceof Error ? e.message : String(e)
|
|
22623
|
+
});
|
|
22624
|
+
}
|
|
22625
|
+
}
|
|
22626
|
+
isReadCacheCandidate(request) {
|
|
22627
|
+
const m = request.method.toUpperCase();
|
|
22628
|
+
return (m === "GET" || m === "HEAD") && this.isCacheable(request);
|
|
22629
|
+
}
|
|
22630
|
+
isWriteCacheCandidate(request, response) {
|
|
22631
|
+
if (request.method.toUpperCase() !== "GET") return false;
|
|
22632
|
+
if (!this.isCacheable(request)) return false;
|
|
22633
|
+
return this.isResponseCacheable(response);
|
|
22634
|
+
}
|
|
22635
|
+
createRevalidationRequest(request) {
|
|
22636
|
+
const headers = new Headers(request.headers);
|
|
22637
|
+
headers.set("X-Cache-Bypass", "revalidation");
|
|
22638
|
+
headers.delete("If-None-Match");
|
|
22639
|
+
headers.delete("If-Modified-Since");
|
|
22640
|
+
headers.delete("Cache-Control");
|
|
22641
|
+
headers.delete("Pragma");
|
|
22642
|
+
return new Request(request.url, {
|
|
22643
|
+
method: "GET",
|
|
22644
|
+
headers
|
|
22645
|
+
});
|
|
22646
|
+
}
|
|
22647
|
+
buildClientResponse(entry, isStale, age) {
|
|
22648
|
+
const headers = new Headers(entry.headers);
|
|
22649
|
+
headers.set("Cache-Control", "public, max-age=0, must-revalidate");
|
|
22650
|
+
headers.set(
|
|
22651
|
+
"Cloudflare-CDN-Cache-Control",
|
|
22652
|
+
`public, s-maxage=${entry.ttl}, stale-while-revalidate=${entry.swr}, stale-if-error=60`
|
|
22653
|
+
);
|
|
22654
|
+
if (entry.lastModifiedUTC) {
|
|
22655
|
+
headers.set("Last-Modified", entry.lastModifiedUTC);
|
|
22656
|
+
}
|
|
22657
|
+
if (entry.etag) {
|
|
22658
|
+
headers.set("ETag", entry.etag);
|
|
22659
|
+
}
|
|
22660
|
+
headers.set("X-Cache-Status", isStale ? "STALE" : "HIT");
|
|
22661
|
+
headers.set("X-Cache-Age", Math.floor(age).toString());
|
|
22662
|
+
this.sanitizeClientHeaders(headers);
|
|
22663
|
+
return new Response(entry.body, {
|
|
22664
|
+
status: entry.status,
|
|
22665
|
+
statusText: entry.statusText,
|
|
22666
|
+
headers
|
|
22667
|
+
});
|
|
22668
|
+
}
|
|
22669
|
+
buildCacheKey(request) {
|
|
22670
|
+
const url = new URL(request.url);
|
|
22671
|
+
const versionHash = this.generateVersionHash(request.headers);
|
|
22672
|
+
const normalizedQuery = this.normalizeSearchParams(url.searchParams);
|
|
22673
|
+
const cacheKeyPath = `${versionHash}${url.pathname}`;
|
|
22674
|
+
const keyUrl = new URL(`${CACHE_KEY_ORIGIN}${cacheKeyPath}`);
|
|
22675
|
+
if (normalizedQuery) {
|
|
22676
|
+
keyUrl.search = `?${normalizedQuery}`;
|
|
22677
|
+
}
|
|
22678
|
+
return keyUrl.toString();
|
|
22679
|
+
}
|
|
22680
|
+
checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag) {
|
|
22681
|
+
if (this.ifNoneMatchMatches(ifNoneMatch, etag)) {
|
|
22682
|
+
return true;
|
|
22683
|
+
}
|
|
22684
|
+
if (ifModifiedSince && lastModified) {
|
|
22685
|
+
try {
|
|
22686
|
+
const ifModDate = new Date(ifModifiedSince);
|
|
22687
|
+
const lastModDate = new Date(lastModified);
|
|
22688
|
+
if (isNaN(ifModDate.getTime()) || isNaN(lastModDate.getTime())) {
|
|
22689
|
+
return false;
|
|
22690
|
+
}
|
|
22691
|
+
return ifModDate >= lastModDate;
|
|
22692
|
+
} catch {
|
|
22693
|
+
return false;
|
|
22694
|
+
}
|
|
22695
|
+
}
|
|
22696
|
+
return false;
|
|
22697
|
+
}
|
|
22698
|
+
ifNoneMatchMatches(ifNoneMatch, etag) {
|
|
22699
|
+
if (!ifNoneMatch || !etag) return false;
|
|
22700
|
+
const header = ifNoneMatch.trim();
|
|
22701
|
+
if (header === "*") return true;
|
|
22702
|
+
const tokens = header.split(",").map((t) => t.trim());
|
|
22703
|
+
const normalizedEtag = etag.replace(/^W\//, "");
|
|
22704
|
+
for (const token of tokens) {
|
|
22705
|
+
if (token === etag) return true;
|
|
22706
|
+
const normalizedToken = token.replace(/^W\//, "");
|
|
22707
|
+
if (normalizedToken === normalizedEtag) return true;
|
|
22708
|
+
}
|
|
22709
|
+
return false;
|
|
22710
|
+
}
|
|
22711
|
+
quoteETag(value) {
|
|
22712
|
+
if (!value) return value;
|
|
22713
|
+
if (value.startsWith('"') || value.startsWith('W/"')) return value;
|
|
22714
|
+
if (value.startsWith("W/")) return `W/"${value.slice(2)}"`;
|
|
22715
|
+
return `"${value}"`;
|
|
22716
|
+
}
|
|
22717
|
+
sanitizeClientHeaders(headers) {
|
|
22718
|
+
const HOP_BY_HOP = [
|
|
22719
|
+
"connection",
|
|
22720
|
+
"proxy-connection",
|
|
22721
|
+
"keep-alive",
|
|
22722
|
+
"transfer-encoding",
|
|
22723
|
+
"upgrade",
|
|
22724
|
+
"proxy-authenticate",
|
|
22725
|
+
"proxy-authorization",
|
|
22726
|
+
"te",
|
|
22727
|
+
"trailers",
|
|
22728
|
+
"via",
|
|
22729
|
+
"alt-svc",
|
|
22730
|
+
"content-length"
|
|
22731
|
+
];
|
|
22732
|
+
for (const h of HOP_BY_HOP) headers.delete(h);
|
|
22733
|
+
headers.delete("content-encoding");
|
|
22734
|
+
headers.delete("x-original-ttl");
|
|
22735
|
+
headers.delete("x-original-swr");
|
|
22736
|
+
headers.delete("x-cache-time");
|
|
22737
|
+
}
|
|
22738
|
+
generateVersionHash(headers) {
|
|
22739
|
+
const swellData = this.extractSwellData(headers);
|
|
22740
|
+
const acceptLang = headers.get("accept-language") || "";
|
|
22741
|
+
const accept = headers.get("accept") || "";
|
|
22742
|
+
const versionFactors = {
|
|
22743
|
+
store: headers.get("swell-storefront-id") || "",
|
|
22744
|
+
auth: headers.get("swell-access-token") || "",
|
|
22745
|
+
theme: headers.get("swell-theme-version-hash") || "",
|
|
22746
|
+
modified: headers.get("swell-cache-modified") || "",
|
|
22747
|
+
currency: swellData["swell-currency"] || "USD",
|
|
22748
|
+
locale: headers.get("x-locale") || acceptLang.split(",")[0].trim().toLowerCase() || "default",
|
|
22749
|
+
context: headers.get("swell-storefront-context"),
|
|
22750
|
+
accept,
|
|
22751
|
+
epoch: this.epoch
|
|
22752
|
+
};
|
|
22753
|
+
return md5(JSON.stringify(versionFactors));
|
|
22754
|
+
}
|
|
22755
|
+
extractSwellData(headers) {
|
|
22756
|
+
const cookie = headers.get("cookie");
|
|
22757
|
+
if (!cookie) return {};
|
|
22758
|
+
const swellDataMatch = cookie.match(/swell-data=([^;]+)/);
|
|
22759
|
+
if (!swellDataMatch) return {};
|
|
22760
|
+
try {
|
|
22761
|
+
return JSON.parse(decodeURIComponent(swellDataMatch[1])) || {};
|
|
22762
|
+
} catch {
|
|
22763
|
+
return {};
|
|
22764
|
+
}
|
|
22765
|
+
}
|
|
22766
|
+
isCacheable(request) {
|
|
22767
|
+
const url = new URL(request.url);
|
|
22768
|
+
if (request.headers.get("swell-deployment-mode") === "editor") return false;
|
|
22769
|
+
const skipPaths = ["/checkout"];
|
|
22770
|
+
if (skipPaths.some((path) => url.pathname.startsWith(path))) return false;
|
|
22771
|
+
if (request.headers.get("cache-control")?.includes("no-cache"))
|
|
22772
|
+
return false;
|
|
22773
|
+
return true;
|
|
22774
|
+
}
|
|
22775
|
+
isResponseCacheable(response) {
|
|
22776
|
+
if (!response.headers.get("content-type")?.includes("text/html"))
|
|
22777
|
+
return false;
|
|
22778
|
+
if (response.headers.get("set-cookie")) return false;
|
|
22779
|
+
const cacheControl = response.headers.get("cache-control");
|
|
22780
|
+
if (cacheControl?.includes("no-store") || cacheControl?.includes("private"))
|
|
22781
|
+
return false;
|
|
22782
|
+
return true;
|
|
22783
|
+
}
|
|
22784
|
+
getDeploymentMode(headers) {
|
|
22785
|
+
const mode = headers.get("swell-deployment-mode");
|
|
22786
|
+
return mode === "preview" || mode === "editor" ? mode : "live";
|
|
22787
|
+
}
|
|
22788
|
+
getTTLForRequest(request) {
|
|
22789
|
+
const url = new URL(request.url);
|
|
22790
|
+
const mode = this.getDeploymentMode(request.headers);
|
|
22791
|
+
if (mode === "editor") return 0;
|
|
22792
|
+
const config = mode === "preview" ? TTL_CONFIG.PREVIEW : TTL_CONFIG.LIVE;
|
|
22793
|
+
if (url.pathname === "/") return config.HOME;
|
|
22794
|
+
if (url.pathname.startsWith("/products/")) return config.PRODUCT;
|
|
22795
|
+
if (url.pathname.startsWith("/categories/")) return config.COLLECTION;
|
|
22796
|
+
if (url.pathname.startsWith("/pages/")) return config.PAGE;
|
|
22797
|
+
if (url.pathname.startsWith("/blogs/")) return config.BLOG;
|
|
22798
|
+
return config.DEFAULT;
|
|
22799
|
+
}
|
|
22800
|
+
getSWRForRequest(request) {
|
|
22801
|
+
const mode = this.getDeploymentMode(request.headers);
|
|
22802
|
+
if (mode === "editor") return 0;
|
|
22803
|
+
return mode === "preview" ? TTL_CONFIG.PREVIEW.SWR : TTL_CONFIG.LIVE.SWR;
|
|
22804
|
+
}
|
|
22805
|
+
getEntryAge(entry) {
|
|
22806
|
+
const t = Date.parse(entry.cacheTimeISO);
|
|
22807
|
+
if (Number.isNaN(t)) return Infinity;
|
|
22808
|
+
const age = (Date.now() - t) / 1e3;
|
|
22809
|
+
return age < 0 ? 0 : age;
|
|
22810
|
+
}
|
|
22811
|
+
normalizeSearchParams(searchParams) {
|
|
22812
|
+
const ignoredParams = [
|
|
22813
|
+
"utm_source",
|
|
22814
|
+
"utm_medium",
|
|
22815
|
+
"utm_campaign",
|
|
22816
|
+
"utm_content",
|
|
22817
|
+
"utm_term",
|
|
22818
|
+
"fbclid",
|
|
22819
|
+
"gclid",
|
|
22820
|
+
"gbraid",
|
|
22821
|
+
"wbraid",
|
|
22822
|
+
"ref",
|
|
22823
|
+
"source",
|
|
22824
|
+
"mc_cid",
|
|
22825
|
+
"mc_eid"
|
|
22826
|
+
];
|
|
22827
|
+
const relevantParams = [];
|
|
22828
|
+
searchParams.forEach((value, key) => {
|
|
22829
|
+
if (!ignoredParams.includes(key)) {
|
|
22830
|
+
relevantParams.push(
|
|
22831
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
|
22832
|
+
);
|
|
22833
|
+
}
|
|
22834
|
+
});
|
|
22835
|
+
return relevantParams.sort().join("&");
|
|
22836
|
+
}
|
|
22837
|
+
};
|
|
22838
|
+
|
|
22839
|
+
// src/cache/html-cache/html-cache-kv.ts
|
|
22840
|
+
var KVCacheBackend = class {
|
|
22841
|
+
kv;
|
|
22842
|
+
prefix;
|
|
22843
|
+
hashKeys;
|
|
22844
|
+
maxValueBytes;
|
|
22845
|
+
constructor(kv, opts = {}) {
|
|
22846
|
+
this.kv = kv;
|
|
22847
|
+
this.prefix = opts.prefix;
|
|
22848
|
+
this.hashKeys = opts.hashKeys !== false;
|
|
22849
|
+
this.maxValueBytes = opts.maxValueBytes ?? Math.floor(24.5 * 1024 * 1024);
|
|
22850
|
+
}
|
|
22851
|
+
async read(key) {
|
|
22852
|
+
const kvKey = this.makeKey(key);
|
|
22853
|
+
const value = await this.kv.get(kvKey, "json");
|
|
22854
|
+
if (!value) return null;
|
|
22855
|
+
const entry = {
|
|
22856
|
+
status: value.status,
|
|
22857
|
+
statusText: value.statusText,
|
|
22858
|
+
headers: value.headers,
|
|
22859
|
+
body: value.body,
|
|
22860
|
+
cacheTimeISO: value.cacheTimeISO,
|
|
22861
|
+
ttl: value.ttl,
|
|
22862
|
+
swr: value.swr,
|
|
22863
|
+
etag: value.etag,
|
|
22864
|
+
lastModifiedUTC: value.lastModifiedUTC
|
|
22865
|
+
};
|
|
22866
|
+
return entry;
|
|
22867
|
+
}
|
|
22868
|
+
async write(key, entry, hardExpireSeconds) {
|
|
22869
|
+
const kvKey = this.makeKey(key);
|
|
22870
|
+
const payload = {
|
|
22871
|
+
v: 1,
|
|
22872
|
+
status: entry.status,
|
|
22873
|
+
statusText: entry.statusText,
|
|
22874
|
+
headers: entry.headers,
|
|
22875
|
+
body: entry.body,
|
|
22876
|
+
cacheTimeISO: entry.cacheTimeISO,
|
|
22877
|
+
ttl: entry.ttl,
|
|
22878
|
+
swr: entry.swr,
|
|
22879
|
+
etag: entry.etag,
|
|
22880
|
+
lastModifiedUTC: entry.lastModifiedUTC
|
|
22881
|
+
};
|
|
22882
|
+
const json = JSON.stringify(payload);
|
|
22883
|
+
this.assertSize(json);
|
|
22884
|
+
const metadata = {
|
|
22885
|
+
v: 1,
|
|
22886
|
+
cacheTimeISO: entry.cacheTimeISO,
|
|
22887
|
+
ttl: entry.ttl,
|
|
22888
|
+
swr: entry.swr,
|
|
22889
|
+
etag: entry.etag,
|
|
22890
|
+
lastModifiedUTC: entry.lastModifiedUTC
|
|
22891
|
+
};
|
|
22892
|
+
await this.kv.put(kvKey, json, {
|
|
22893
|
+
expirationTtl: hardExpireSeconds + 60,
|
|
22894
|
+
// natural hard expiry after SWR + grace period
|
|
22895
|
+
metadata
|
|
22896
|
+
});
|
|
22897
|
+
}
|
|
22898
|
+
async delete(key) {
|
|
22899
|
+
const kvKey = this.makeKey(key);
|
|
22900
|
+
await this.kv.delete(kvKey);
|
|
22901
|
+
}
|
|
22902
|
+
// ---- private helpers ----
|
|
22903
|
+
makeKey(raw) {
|
|
22904
|
+
const core = this.hashKeys ? md5(raw) : raw;
|
|
22905
|
+
return this.prefix ? `${this.prefix}:${core}` : core;
|
|
22906
|
+
}
|
|
22907
|
+
assertSize(json) {
|
|
22908
|
+
const bytes = typeof TextEncoder !== "undefined" ? new TextEncoder().encode(json).length : this.approxUtf8Bytes(json);
|
|
22909
|
+
if (bytes > this.maxValueBytes) {
|
|
22910
|
+
throw new Error(
|
|
22911
|
+
`KV value too large: ${bytes} bytes exceeds limit ${this.maxValueBytes} bytes`
|
|
22912
|
+
);
|
|
22913
|
+
}
|
|
22914
|
+
}
|
|
22915
|
+
approxUtf8Bytes(str) {
|
|
22916
|
+
let count = 0;
|
|
22917
|
+
for (let i = 0; i < str.length; i++) {
|
|
22918
|
+
const code = str.charCodeAt(i);
|
|
22919
|
+
if (code <= 127) count += 1;
|
|
22920
|
+
else if (code <= 2047) count += 2;
|
|
22921
|
+
else count += 3;
|
|
22922
|
+
}
|
|
22923
|
+
return count;
|
|
22924
|
+
}
|
|
22925
|
+
};
|
|
22926
|
+
|
|
22927
|
+
// src/cache/html-cache/html-cache-worker.ts
|
|
22928
|
+
var CACHE_NAME_PREFIX = "swell-html-v0";
|
|
22929
|
+
var WorkerCacheBackend = class {
|
|
22930
|
+
cacheName;
|
|
22931
|
+
constructor(epoch) {
|
|
22932
|
+
this.cacheName = CACHE_NAME_PREFIX + epoch;
|
|
22933
|
+
}
|
|
22934
|
+
async read(key) {
|
|
22935
|
+
const cache = await caches.open(this.cacheName);
|
|
22936
|
+
const request = new Request(key);
|
|
22937
|
+
const response = await cache.match(request);
|
|
22938
|
+
if (!response) return null;
|
|
22939
|
+
const headers = {};
|
|
22940
|
+
response.headers.forEach((value, name) => {
|
|
22941
|
+
headers[name.toLowerCase()] = value;
|
|
22942
|
+
});
|
|
22943
|
+
return {
|
|
22944
|
+
status: response.status,
|
|
22945
|
+
statusText: response.statusText,
|
|
22946
|
+
headers,
|
|
22947
|
+
body: await response.text(),
|
|
22948
|
+
cacheTimeISO: response.headers.get("x-cache-time") || (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
22949
|
+
ttl: parseInt(response.headers.get("x-original-ttl") || "0", 10),
|
|
22950
|
+
swr: parseInt(response.headers.get("x-original-swr") || "0", 10),
|
|
22951
|
+
etag: response.headers.get("etag") || void 0,
|
|
22952
|
+
lastModifiedUTC: response.headers.get("last-modified") || void 0
|
|
22953
|
+
};
|
|
22954
|
+
}
|
|
22955
|
+
async write(key, entry, _hardExpireSeconds) {
|
|
22956
|
+
const cache = await caches.open(this.cacheName);
|
|
22957
|
+
const request = new Request(key);
|
|
22958
|
+
const headers = new Headers(entry.headers);
|
|
22959
|
+
if (entry.lastModifiedUTC && !headers.get("Last-Modified")) {
|
|
22960
|
+
headers.set("Last-Modified", entry.lastModifiedUTC);
|
|
22961
|
+
}
|
|
22962
|
+
if (entry.etag && !headers.get("ETag")) {
|
|
22963
|
+
headers.set("ETag", entry.etag);
|
|
22964
|
+
}
|
|
22965
|
+
headers.set("X-Cache-Time", entry.cacheTimeISO);
|
|
22966
|
+
headers.set("X-Original-TTL", String(entry.ttl));
|
|
22967
|
+
headers.set("X-Original-SWR", String(entry.swr));
|
|
22968
|
+
headers.delete("content-encoding");
|
|
22969
|
+
headers.delete("content-length");
|
|
22970
|
+
const existing = headers.get("Cache-Control");
|
|
22971
|
+
if (!existing || existing.trim().toLowerCase() === "public") {
|
|
22972
|
+
headers.set("Cache-Control", `public, max-age=${entry.ttl + entry.swr}`);
|
|
22973
|
+
}
|
|
22974
|
+
const response = new Response(entry.body, {
|
|
22975
|
+
status: entry.status,
|
|
22976
|
+
statusText: entry.statusText,
|
|
22977
|
+
headers
|
|
22978
|
+
});
|
|
22979
|
+
await cache.delete(request);
|
|
22980
|
+
await cache.put(request, response);
|
|
22981
|
+
}
|
|
22982
|
+
async delete(key) {
|
|
22983
|
+
const cache = await caches.open(this.cacheName);
|
|
22984
|
+
const request = new Request(key);
|
|
22985
|
+
await cache.delete(request);
|
|
22986
|
+
}
|
|
22987
|
+
};
|
|
22988
|
+
|
|
22989
|
+
// src/cache/html-cache/html-cache-factory.ts
|
|
22990
|
+
var _instance = null;
|
|
22991
|
+
function getHtmlCache(env) {
|
|
22992
|
+
const epoch = env?.HTML_CACHE_EPOCH;
|
|
22993
|
+
if (typeof epoch !== "string" || !epoch) return null;
|
|
22994
|
+
if (_instance) return _instance;
|
|
22995
|
+
const kv = env?.NAMESPACE;
|
|
22996
|
+
if (env?.HTML_CACHE_BACKEND !== "worker" && kv) {
|
|
22997
|
+
_instance = new HtmlCache(epoch, new KVCacheBackend(kv));
|
|
22998
|
+
return _instance;
|
|
22999
|
+
}
|
|
23000
|
+
_instance = new HtmlCache(epoch, new WorkerCacheBackend(epoch));
|
|
23001
|
+
return _instance;
|
|
23002
|
+
}
|
|
22841
23003
|
// Annotate the CommonJS export names for ESM import in node:
|
|
22842
23004
|
0 && (module.exports = {
|
|
22843
23005
|
AccountAddressesResource,
|
|
@@ -22856,6 +23018,7 @@ function getResourceQuery(slug, query) {
|
|
|
22856
23018
|
DeferredShopifyResource,
|
|
22857
23019
|
FILE_DATA_INCLUDE_QUERY,
|
|
22858
23020
|
GEO_DATA,
|
|
23021
|
+
HtmlCache,
|
|
22859
23022
|
LANG_TO_COUNTRY_CODES,
|
|
22860
23023
|
LiquidSwell,
|
|
22861
23024
|
MAX_QUERY_PAGE_LIMIT,
|
|
@@ -22903,7 +23066,6 @@ function getResourceQuery(slug, query) {
|
|
|
22903
23066
|
ThemeForm,
|
|
22904
23067
|
ThemeFormErrors,
|
|
22905
23068
|
VariantResource,
|
|
22906
|
-
WorkerHtmlCache,
|
|
22907
23069
|
adaptShopifyFontData,
|
|
22908
23070
|
adaptShopifyFormData,
|
|
22909
23071
|
adaptShopifyMenuData,
|
|
@@ -22931,6 +23093,7 @@ function getResourceQuery(slug, query) {
|
|
|
22931
23093
|
getEasyblocksComponentDefinitions,
|
|
22932
23094
|
getEasyblocksPagePropsWithConfigs,
|
|
22933
23095
|
getEasyblocksPageTemplate,
|
|
23096
|
+
getHtmlCache,
|
|
22934
23097
|
getKVFlavor,
|
|
22935
23098
|
getLayoutSectionGroups,
|
|
22936
23099
|
getMenuItemStorefrontUrl,
|