@swell/apps-sdk 1.0.160 → 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 +566 -411
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +562 -409
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +564 -410
- 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/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,
|
|
@@ -7437,12 +7438,7 @@ var ThemeCache = class extends Cache {
|
|
|
7437
7438
|
}
|
|
7438
7439
|
};
|
|
7439
7440
|
|
|
7440
|
-
// src/cache/theme-file-storage.ts
|
|
7441
|
-
var import_bluebird2 = __toESM(require("bluebird"), 1);
|
|
7442
|
-
|
|
7443
7441
|
// src/cache/kv-variety.ts
|
|
7444
|
-
var import_bluebird = __toESM(require("bluebird"), 1);
|
|
7445
|
-
var { Promise: Promise2 } = import_bluebird.default;
|
|
7446
7442
|
var CFKV = class {
|
|
7447
7443
|
constructor(kv) {
|
|
7448
7444
|
this.kv = kv;
|
|
@@ -7474,13 +7470,11 @@ var MiniflareKV = class {
|
|
|
7474
7470
|
return /* @__PURE__ */ new Map();
|
|
7475
7471
|
}
|
|
7476
7472
|
const result = /* @__PURE__ */ new Map();
|
|
7477
|
-
await
|
|
7478
|
-
keys
|
|
7479
|
-
async (key) => {
|
|
7473
|
+
await Promise.all(
|
|
7474
|
+
keys.map(async (key) => {
|
|
7480
7475
|
const value = await this.kv.get(key, "text");
|
|
7481
7476
|
result.set(key, value);
|
|
7482
|
-
}
|
|
7483
|
-
{ concurrency: 50 }
|
|
7477
|
+
})
|
|
7484
7478
|
);
|
|
7485
7479
|
return result;
|
|
7486
7480
|
}
|
|
@@ -7512,16 +7506,13 @@ function createClientKV(env, flavor = "cf") {
|
|
|
7512
7506
|
return new MemoryKV();
|
|
7513
7507
|
}
|
|
7514
7508
|
|
|
7515
|
-
// src/cache/theme-file-
|
|
7516
|
-
var
|
|
7517
|
-
var ThemeFileStorage = class {
|
|
7509
|
+
// src/cache/theme-file-cache.ts
|
|
7510
|
+
var ThemeFileCache = class {
|
|
7518
7511
|
kv;
|
|
7519
|
-
maxConcurrency;
|
|
7520
7512
|
maxBatchSize = 20 * 1024 * 1024;
|
|
7521
7513
|
// 20MB safety margin
|
|
7522
7514
|
constructor(env, flavor = "cf") {
|
|
7523
7515
|
this.kv = createClientKV(env, flavor);
|
|
7524
|
-
this.maxConcurrency = flavor === "miniflare" ? 50 : 6;
|
|
7525
7516
|
}
|
|
7526
7517
|
/**
|
|
7527
7518
|
* Build a KV storage key from a file hash
|
|
@@ -7614,10 +7605,8 @@ var ThemeFileStorage = class {
|
|
|
7614
7605
|
totalSize,
|
|
7615
7606
|
trace
|
|
7616
7607
|
});
|
|
7617
|
-
const results = await
|
|
7618
|
-
batches
|
|
7619
|
-
(batch) => this.loadBatch(batch),
|
|
7620
|
-
{ concurrency: Math.min(this.maxConcurrency, batches.length) }
|
|
7608
|
+
const results = await Promise.all(
|
|
7609
|
+
batches.map((batch) => this.loadBatch(batch))
|
|
7621
7610
|
);
|
|
7622
7611
|
const mergedConfigs = this.mergeResults(configs, results);
|
|
7623
7612
|
const loadedCount = mergedConfigs.filter((c) => c.file_data).length;
|
|
@@ -7682,10 +7671,8 @@ var ThemeFileStorage = class {
|
|
|
7682
7671
|
}
|
|
7683
7672
|
const existing = /* @__PURE__ */ new Set();
|
|
7684
7673
|
const batches = this.planGetBatches(configs);
|
|
7685
|
-
const results = await
|
|
7686
|
-
batches
|
|
7687
|
-
(batch) => this.kv.get(batch.keys),
|
|
7688
|
-
{ concurrency: this.maxConcurrency }
|
|
7674
|
+
const results = await Promise.all(
|
|
7675
|
+
batches.map((batch) => this.kv.get(batch.keys))
|
|
7689
7676
|
);
|
|
7690
7677
|
for (const batchResult of results) {
|
|
7691
7678
|
for (const [key, value] of batchResult.entries()) {
|
|
@@ -7749,15 +7736,13 @@ var ThemeFileStorage = class {
|
|
|
7749
7736
|
skippedExisting: existing.size,
|
|
7750
7737
|
trace
|
|
7751
7738
|
});
|
|
7752
|
-
await
|
|
7753
|
-
toWrite
|
|
7754
|
-
async (config) => {
|
|
7739
|
+
await Promise.all(
|
|
7740
|
+
toWrite.map(async (config) => {
|
|
7755
7741
|
const key = this.buildKey(config.hash);
|
|
7756
7742
|
const metadata = config.file?.content_type ? { content_type: config.file.content_type } : void 0;
|
|
7757
|
-
await this.kv.put(key, config.file_data, metadata);
|
|
7743
|
+
await this.kv.put(key, config.file_data, { metadata });
|
|
7758
7744
|
result.written++;
|
|
7759
|
-
}
|
|
7760
|
-
{ concurrency: this.maxConcurrency }
|
|
7745
|
+
})
|
|
7761
7746
|
);
|
|
7762
7747
|
}
|
|
7763
7748
|
logger.info("[ThemeFileStorage] Put files complete", {
|
|
@@ -7771,378 +7756,6 @@ var ThemeFileStorage = class {
|
|
|
7771
7756
|
}
|
|
7772
7757
|
};
|
|
7773
7758
|
|
|
7774
|
-
// src/cache/constants.ts
|
|
7775
|
-
var SECOND = 1e3;
|
|
7776
|
-
var MINUTE = 60 * SECOND;
|
|
7777
|
-
var HOUR = 60 * MINUTE;
|
|
7778
|
-
var DAY = 24 * HOUR;
|
|
7779
|
-
var YEAR = 365 * DAY;
|
|
7780
|
-
var SHORT_TTL = 5 * SECOND;
|
|
7781
|
-
|
|
7782
|
-
// src/cache/worker-html-cache.ts
|
|
7783
|
-
var CACHE_NAME = "swell-html-v0";
|
|
7784
|
-
var CACHE_KEY_ORIGIN = "https://cache.swell.store";
|
|
7785
|
-
var TTL_CONFIG = {
|
|
7786
|
-
LIVE: {
|
|
7787
|
-
DEFAULT: 20,
|
|
7788
|
-
HOME: 20,
|
|
7789
|
-
PRODUCT: 20,
|
|
7790
|
-
COLLECTION: 20,
|
|
7791
|
-
PAGE: 20,
|
|
7792
|
-
BLOG: 20,
|
|
7793
|
-
SWR: 180
|
|
7794
|
-
},
|
|
7795
|
-
PREVIEW: {
|
|
7796
|
-
DEFAULT: 20,
|
|
7797
|
-
HOME: 20,
|
|
7798
|
-
PRODUCT: 20,
|
|
7799
|
-
COLLECTION: 20,
|
|
7800
|
-
PAGE: 20,
|
|
7801
|
-
BLOG: 20,
|
|
7802
|
-
SWR: 180
|
|
7803
|
-
}
|
|
7804
|
-
};
|
|
7805
|
-
var WorkerHtmlCache = class {
|
|
7806
|
-
epoch;
|
|
7807
|
-
constructor(epoch) {
|
|
7808
|
-
this.epoch = epoch;
|
|
7809
|
-
}
|
|
7810
|
-
async get(request) {
|
|
7811
|
-
const trace = createTraceId();
|
|
7812
|
-
if (request.method !== "GET") {
|
|
7813
|
-
logger.debug("[SDK Html-cache] non-cacheable", { trace });
|
|
7814
|
-
return { found: false, cacheable: false };
|
|
7815
|
-
}
|
|
7816
|
-
if (!this.isCacheable(request)) {
|
|
7817
|
-
logger.debug("[SDK Html-cache] non-cacheable", { trace });
|
|
7818
|
-
return { found: false, cacheable: false };
|
|
7819
|
-
}
|
|
7820
|
-
try {
|
|
7821
|
-
const cache = await caches.open(CACHE_NAME + this.epoch);
|
|
7822
|
-
const cacheKey = this.buildCacheKey(request);
|
|
7823
|
-
const cached = await cache.match(cacheKey);
|
|
7824
|
-
if (!cached) {
|
|
7825
|
-
logger.debug("[SDK Html-cache] cacheable, MISS", { trace });
|
|
7826
|
-
return { found: false, cacheable: true };
|
|
7827
|
-
}
|
|
7828
|
-
const age = this.getResponseAge(cached);
|
|
7829
|
-
const ttl = parseInt(cached.headers.get("X-Original-TTL") || "") || this.getTTLForRequest(request);
|
|
7830
|
-
const swr = parseInt(cached.headers.get("X-Original-SWR") || "") || this.getSWRForRequest(request);
|
|
7831
|
-
const isStale = age >= ttl;
|
|
7832
|
-
const isExpired = age >= ttl + swr;
|
|
7833
|
-
if (!isExpired) {
|
|
7834
|
-
logger.debug("[SDK Html-cache] cacheable, HIT", {
|
|
7835
|
-
stale: isStale,
|
|
7836
|
-
age,
|
|
7837
|
-
trace
|
|
7838
|
-
});
|
|
7839
|
-
const clientResponse = this.buildClientResponse(
|
|
7840
|
-
cached,
|
|
7841
|
-
ttl,
|
|
7842
|
-
swr,
|
|
7843
|
-
isStale,
|
|
7844
|
-
age
|
|
7845
|
-
);
|
|
7846
|
-
return {
|
|
7847
|
-
found: true,
|
|
7848
|
-
stale: isStale,
|
|
7849
|
-
response: clientResponse,
|
|
7850
|
-
cacheable: true,
|
|
7851
|
-
age: Math.floor(age)
|
|
7852
|
-
};
|
|
7853
|
-
}
|
|
7854
|
-
logger.debug("[SDK Html-cache] cacheable, hit, expired", { trace });
|
|
7855
|
-
return { found: false, cacheable: true };
|
|
7856
|
-
} catch (_) {
|
|
7857
|
-
logger.warn("[SDK Html-cache] no get support", { trace });
|
|
7858
|
-
return null;
|
|
7859
|
-
}
|
|
7860
|
-
}
|
|
7861
|
-
// 304 support
|
|
7862
|
-
async getWithConditionals(request) {
|
|
7863
|
-
const result = await this.get(request);
|
|
7864
|
-
if (!result?.found || result.stale) {
|
|
7865
|
-
return result;
|
|
7866
|
-
}
|
|
7867
|
-
const ifModifiedSince = request.headers.get("If-Modified-Since");
|
|
7868
|
-
const ifNoneMatch = request.headers.get("If-None-Match");
|
|
7869
|
-
if ((ifModifiedSince || ifNoneMatch) && result.response) {
|
|
7870
|
-
const lastModified = result.response.headers.get("Last-Modified");
|
|
7871
|
-
const etag = result.response.headers.get("ETag");
|
|
7872
|
-
if (this.checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag)) {
|
|
7873
|
-
result.notModified = true;
|
|
7874
|
-
result.conditional304 = new Response(null, {
|
|
7875
|
-
status: 304,
|
|
7876
|
-
headers: {
|
|
7877
|
-
"Last-Modified": lastModified || "",
|
|
7878
|
-
ETag: etag || "",
|
|
7879
|
-
"Cache-Control": result.response.headers.get("Cache-Control") || "",
|
|
7880
|
-
"Cloudflare-CDN-Cache-Control": result.response.headers.get("Cloudflare-CDN-Cache-Control") || "",
|
|
7881
|
-
"X-Cache-Status": "HIT-304"
|
|
7882
|
-
}
|
|
7883
|
-
});
|
|
7884
|
-
}
|
|
7885
|
-
}
|
|
7886
|
-
return result;
|
|
7887
|
-
}
|
|
7888
|
-
checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag) {
|
|
7889
|
-
if (ifNoneMatch && etag) {
|
|
7890
|
-
return ifNoneMatch === etag;
|
|
7891
|
-
}
|
|
7892
|
-
if (ifModifiedSince && lastModified) {
|
|
7893
|
-
const ifModDate = new Date(ifModifiedSince);
|
|
7894
|
-
const lastModDate = new Date(lastModified);
|
|
7895
|
-
return !isNaN(ifModDate.getTime()) && !isNaN(lastModDate.getTime()) && ifModDate >= lastModDate;
|
|
7896
|
-
}
|
|
7897
|
-
return false;
|
|
7898
|
-
}
|
|
7899
|
-
createRevalidationRequest(request) {
|
|
7900
|
-
const headers = new Headers(request.headers);
|
|
7901
|
-
headers.set("X-Cache-Bypass", "revalidation");
|
|
7902
|
-
headers.delete("If-None-Match");
|
|
7903
|
-
headers.delete("If-Modified-Since");
|
|
7904
|
-
headers.delete("Cache-Control");
|
|
7905
|
-
headers.delete("Pragma");
|
|
7906
|
-
return new Request(request.url, {
|
|
7907
|
-
method: "GET",
|
|
7908
|
-
headers
|
|
7909
|
-
});
|
|
7910
|
-
}
|
|
7911
|
-
async put(request, response) {
|
|
7912
|
-
const trace = createTraceId();
|
|
7913
|
-
if (request.method !== "GET" || !response.ok) {
|
|
7914
|
-
logger.debug("[SDK Html-cache] put skipped", { trace });
|
|
7915
|
-
return;
|
|
7916
|
-
}
|
|
7917
|
-
if (!this.isCacheable(request) || !this.isResponseCacheable(response)) {
|
|
7918
|
-
logger.debug("[SDK Html-cache] put skipped, non-cacheable", {
|
|
7919
|
-
trace
|
|
7920
|
-
});
|
|
7921
|
-
return;
|
|
7922
|
-
}
|
|
7923
|
-
try {
|
|
7924
|
-
const cache = await caches.open(CACHE_NAME + this.epoch);
|
|
7925
|
-
const cacheKey = this.buildCacheKey(request);
|
|
7926
|
-
await cache.delete(cacheKey);
|
|
7927
|
-
const ttl = this.getTTLForRequest(request);
|
|
7928
|
-
const swr = this.getSWRForRequest(request);
|
|
7929
|
-
const headers = new Headers(response.headers);
|
|
7930
|
-
const existingCacheControl = response.headers.get("Cache-Control");
|
|
7931
|
-
if (!existingCacheControl || existingCacheControl === "public") {
|
|
7932
|
-
const internalMaxAge = ttl + swr;
|
|
7933
|
-
headers.set("Cache-Control", `public, max-age=${internalMaxAge}`);
|
|
7934
|
-
}
|
|
7935
|
-
const cacheTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
7936
|
-
headers.set("X-Cache-Time", cacheTime);
|
|
7937
|
-
headers.set("X-Original-TTL", ttl.toString());
|
|
7938
|
-
headers.set("X-Original-SWR", swr.toString());
|
|
7939
|
-
if (!headers.get("Last-Modified")) {
|
|
7940
|
-
headers.set("Last-Modified", new Date(cacheTime).toUTCString());
|
|
7941
|
-
}
|
|
7942
|
-
const cacheableResponse = new Response(response.body, {
|
|
7943
|
-
status: response.status,
|
|
7944
|
-
statusText: response.statusText,
|
|
7945
|
-
headers
|
|
7946
|
-
});
|
|
7947
|
-
await cache.put(cacheKey, cacheableResponse);
|
|
7948
|
-
logger.debug("[SDK Html-cache] put done", { trace });
|
|
7949
|
-
} catch (_) {
|
|
7950
|
-
logger.warn("[SDK Html-cache] no put support", { trace });
|
|
7951
|
-
}
|
|
7952
|
-
}
|
|
7953
|
-
buildClientResponse(cachedResponse, ttl, swr, isStale, age) {
|
|
7954
|
-
const headers = new Headers(cachedResponse.headers);
|
|
7955
|
-
headers.set(
|
|
7956
|
-
"Cache-Control",
|
|
7957
|
-
`public, max-age=${ttl}, stale-while-revalidate=${swr}`
|
|
7958
|
-
);
|
|
7959
|
-
headers.set(
|
|
7960
|
-
"Cloudflare-CDN-Cache-Control",
|
|
7961
|
-
`public, s-maxage=${ttl}, stale-while-revalidate=${swr}, stale-if-error=60`
|
|
7962
|
-
);
|
|
7963
|
-
const cacheTime = headers.get("X-Cache-Time");
|
|
7964
|
-
if (cacheTime) {
|
|
7965
|
-
const lastModified = new Date(cacheTime).toUTCString();
|
|
7966
|
-
headers.set("Last-Modified", lastModified);
|
|
7967
|
-
}
|
|
7968
|
-
headers.set("X-Cache-Status", isStale ? "STALE" : "HIT");
|
|
7969
|
-
headers.set("X-Cache-Age", Math.floor(age).toString());
|
|
7970
|
-
headers.delete("X-Original-TTL");
|
|
7971
|
-
headers.delete("X-Original-SWR");
|
|
7972
|
-
headers.delete("X-Cache-Time");
|
|
7973
|
-
return new Response(cachedResponse.body, {
|
|
7974
|
-
status: cachedResponse.status,
|
|
7975
|
-
statusText: cachedResponse.statusText,
|
|
7976
|
-
headers
|
|
7977
|
-
});
|
|
7978
|
-
}
|
|
7979
|
-
buildCacheKey(request) {
|
|
7980
|
-
const url = new URL(request.url);
|
|
7981
|
-
const versionHash = this.generateVersionHash(request.headers);
|
|
7982
|
-
const normalizedQuery = this.normalizeSearchParams(url.searchParams);
|
|
7983
|
-
const cacheKeyPath = `${versionHash}${url.pathname}`;
|
|
7984
|
-
const keyUrl = new URL(`${CACHE_KEY_ORIGIN}${cacheKeyPath}`);
|
|
7985
|
-
if (normalizedQuery) {
|
|
7986
|
-
keyUrl.search = `?${normalizedQuery}`;
|
|
7987
|
-
}
|
|
7988
|
-
const sanitizedHeaders = this.sanitizeHeaders(request.headers);
|
|
7989
|
-
return new Request(keyUrl.toString(), {
|
|
7990
|
-
method: "GET",
|
|
7991
|
-
headers: sanitizedHeaders
|
|
7992
|
-
});
|
|
7993
|
-
}
|
|
7994
|
-
sanitizeHeaders(originalHeaders) {
|
|
7995
|
-
const CACHE_RELEVANT_HEADERS = [
|
|
7996
|
-
// Content negotiation (affects response format)
|
|
7997
|
-
"accept",
|
|
7998
|
-
"accept-language"
|
|
7999
|
-
];
|
|
8000
|
-
const sanitized = new Headers();
|
|
8001
|
-
CACHE_RELEVANT_HEADERS.forEach((header) => {
|
|
8002
|
-
const value = originalHeaders.get(header);
|
|
8003
|
-
if (value) {
|
|
8004
|
-
sanitized.set(header, value);
|
|
8005
|
-
}
|
|
8006
|
-
});
|
|
8007
|
-
return sanitized;
|
|
8008
|
-
}
|
|
8009
|
-
generateVersionHash(headers) {
|
|
8010
|
-
const swellData = this.extractSwellData(headers);
|
|
8011
|
-
const versionFactors = {
|
|
8012
|
-
store: headers.get("swell-storefront-id") || "",
|
|
8013
|
-
auth: headers.get("swell-access-token") || "",
|
|
8014
|
-
theme: headers.get("swell-theme-version-hash") || "",
|
|
8015
|
-
modified: headers.get("swell-cache-modified") || "",
|
|
8016
|
-
currency: swellData["swell-currency"] || "USD",
|
|
8017
|
-
locale: headers.get("x-locale") || headers.get("accept-language")?.split(",")[0] || "default",
|
|
8018
|
-
context: headers.get("swell-storefront-context"),
|
|
8019
|
-
epoch: this.epoch
|
|
8020
|
-
};
|
|
8021
|
-
return md5(JSON.stringify(versionFactors));
|
|
8022
|
-
}
|
|
8023
|
-
extractSwellData(headers) {
|
|
8024
|
-
const cookie = headers.get("cookie");
|
|
8025
|
-
if (!cookie) return {};
|
|
8026
|
-
const swellDataMatch = cookie.match(/swell-data=([^;]+)/);
|
|
8027
|
-
if (!swellDataMatch) return {};
|
|
8028
|
-
try {
|
|
8029
|
-
const parsed = JSON.parse(decodeURIComponent(swellDataMatch[1]));
|
|
8030
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
8031
|
-
return parsed;
|
|
8032
|
-
}
|
|
8033
|
-
return {};
|
|
8034
|
-
} catch {
|
|
8035
|
-
return {};
|
|
8036
|
-
}
|
|
8037
|
-
}
|
|
8038
|
-
isCacheable(request) {
|
|
8039
|
-
const url = new URL(request.url);
|
|
8040
|
-
const headers = request.headers;
|
|
8041
|
-
if (headers.get("swell-deployment-mode") === "editor") {
|
|
8042
|
-
return false;
|
|
8043
|
-
}
|
|
8044
|
-
const skipPaths = ["/checkout"];
|
|
8045
|
-
if (skipPaths.some((path) => url.pathname.startsWith(path))) {
|
|
8046
|
-
return false;
|
|
8047
|
-
}
|
|
8048
|
-
if (headers.get("cache-control")?.includes("no-cache")) {
|
|
8049
|
-
return false;
|
|
8050
|
-
}
|
|
8051
|
-
return true;
|
|
8052
|
-
}
|
|
8053
|
-
isResponseCacheable(response) {
|
|
8054
|
-
const contentType = response.headers.get("content-type");
|
|
8055
|
-
if (!contentType?.includes("text/html")) {
|
|
8056
|
-
return false;
|
|
8057
|
-
}
|
|
8058
|
-
if (response.headers.get("set-cookie")) {
|
|
8059
|
-
return false;
|
|
8060
|
-
}
|
|
8061
|
-
const cacheControl = response.headers.get("cache-control");
|
|
8062
|
-
if (cacheControl?.includes("no-store") || cacheControl?.includes("private")) {
|
|
8063
|
-
return false;
|
|
8064
|
-
}
|
|
8065
|
-
return true;
|
|
8066
|
-
}
|
|
8067
|
-
getDeploymentMode(headers) {
|
|
8068
|
-
const mode = headers.get("swell-deployment-mode");
|
|
8069
|
-
if (mode === "preview" || mode === "editor") {
|
|
8070
|
-
return mode;
|
|
8071
|
-
}
|
|
8072
|
-
return "live";
|
|
8073
|
-
}
|
|
8074
|
-
getTTLForRequest(request) {
|
|
8075
|
-
const url = new URL(request.url);
|
|
8076
|
-
const path = url.pathname;
|
|
8077
|
-
const mode = this.getDeploymentMode(request.headers);
|
|
8078
|
-
if (mode === "editor") {
|
|
8079
|
-
return 0;
|
|
8080
|
-
}
|
|
8081
|
-
const config = mode === "preview" ? TTL_CONFIG.PREVIEW : TTL_CONFIG.LIVE;
|
|
8082
|
-
if (path === "/") {
|
|
8083
|
-
return config.HOME;
|
|
8084
|
-
}
|
|
8085
|
-
if (path.startsWith("/products/")) {
|
|
8086
|
-
return config.PRODUCT;
|
|
8087
|
-
}
|
|
8088
|
-
if (path.startsWith("/categories/")) {
|
|
8089
|
-
return config.COLLECTION;
|
|
8090
|
-
}
|
|
8091
|
-
if (path.startsWith("/pages/")) {
|
|
8092
|
-
return config.PAGE;
|
|
8093
|
-
}
|
|
8094
|
-
if (path.startsWith("/blogs/")) {
|
|
8095
|
-
return config.BLOG;
|
|
8096
|
-
}
|
|
8097
|
-
return config.DEFAULT;
|
|
8098
|
-
}
|
|
8099
|
-
getSWRForRequest(request) {
|
|
8100
|
-
const mode = this.getDeploymentMode(request.headers);
|
|
8101
|
-
if (mode === "editor") {
|
|
8102
|
-
return 0;
|
|
8103
|
-
}
|
|
8104
|
-
return mode === "preview" ? TTL_CONFIG.PREVIEW.SWR : TTL_CONFIG.LIVE.SWR;
|
|
8105
|
-
}
|
|
8106
|
-
getResponseAge(response) {
|
|
8107
|
-
const cacheTime = response.headers.get("X-Cache-Time");
|
|
8108
|
-
if (!cacheTime) {
|
|
8109
|
-
return Infinity;
|
|
8110
|
-
}
|
|
8111
|
-
const cacheDate = new Date(cacheTime);
|
|
8112
|
-
if (isNaN(cacheDate.getTime())) {
|
|
8113
|
-
return Infinity;
|
|
8114
|
-
}
|
|
8115
|
-
const age = (Date.now() - cacheDate.getTime()) / 1e3;
|
|
8116
|
-
return Math.max(0, age);
|
|
8117
|
-
}
|
|
8118
|
-
normalizeSearchParams(searchParams) {
|
|
8119
|
-
const ignoredParams = [
|
|
8120
|
-
"utm_source",
|
|
8121
|
-
"utm_medium",
|
|
8122
|
-
"utm_campaign",
|
|
8123
|
-
"utm_content",
|
|
8124
|
-
"utm_term",
|
|
8125
|
-
"fbclid",
|
|
8126
|
-
"gclid",
|
|
8127
|
-
"gbraid",
|
|
8128
|
-
"wbraid",
|
|
8129
|
-
"ref",
|
|
8130
|
-
"source",
|
|
8131
|
-
"mc_cid",
|
|
8132
|
-
"mc_eid"
|
|
8133
|
-
];
|
|
8134
|
-
const relevantParams = [];
|
|
8135
|
-
searchParams.forEach((value, key) => {
|
|
8136
|
-
if (!ignoredParams.includes(key)) {
|
|
8137
|
-
relevantParams.push(
|
|
8138
|
-
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
|
8139
|
-
);
|
|
8140
|
-
}
|
|
8141
|
-
});
|
|
8142
|
-
return relevantParams.sort().join("&");
|
|
8143
|
-
}
|
|
8144
|
-
};
|
|
8145
|
-
|
|
8146
7759
|
// src/resources/addresses.ts
|
|
8147
7760
|
var SwellAddresses = class extends SwellStorefrontCollection {
|
|
8148
7761
|
constructor(swell, query) {
|
|
@@ -16408,7 +16021,7 @@ function ShopifyAddress(instance, address, account) {
|
|
|
16408
16021
|
function joinAddressLines(...props) {
|
|
16409
16022
|
return props.filter(Boolean).join("\n");
|
|
16410
16023
|
}
|
|
16411
|
-
function ShopifyCountry(
|
|
16024
|
+
function ShopifyCountry(_instance2, countryCode) {
|
|
16412
16025
|
const currencyCode = getCurrencyByCountry(countryCode) || "USD";
|
|
16413
16026
|
return new ShopifyResource(
|
|
16414
16027
|
{
|
|
@@ -16741,7 +16354,7 @@ async function resolveLastOrder(instance, account) {
|
|
|
16741
16354
|
}
|
|
16742
16355
|
|
|
16743
16356
|
// src/compatibility/shopify-objects/font.ts
|
|
16744
|
-
function ShopifyFont(
|
|
16357
|
+
function ShopifyFont(_instance2, font) {
|
|
16745
16358
|
if (font instanceof ShopifyResource) {
|
|
16746
16359
|
return font.clone();
|
|
16747
16360
|
}
|
|
@@ -16752,7 +16365,7 @@ function ShopifyFont(_instance, font) {
|
|
|
16752
16365
|
family: font.family,
|
|
16753
16366
|
style: font.style,
|
|
16754
16367
|
"system?": font.system,
|
|
16755
|
-
variants: font.variants.map((variant) => ShopifyFont(
|
|
16368
|
+
variants: font.variants.map((variant) => ShopifyFont(_instance2, variant)),
|
|
16756
16369
|
weight: font.weight
|
|
16757
16370
|
});
|
|
16758
16371
|
}
|
|
@@ -16765,7 +16378,7 @@ var SHOPIFY_FORMS = {
|
|
|
16765
16378
|
})
|
|
16766
16379
|
}
|
|
16767
16380
|
};
|
|
16768
|
-
function ShopifyForm(
|
|
16381
|
+
function ShopifyForm(_instance2, form) {
|
|
16769
16382
|
if (form instanceof ShopifyResource) {
|
|
16770
16383
|
return form.clone();
|
|
16771
16384
|
}
|
|
@@ -17062,7 +16675,7 @@ function ShopifyRecommendations(instance, product) {
|
|
|
17062
16675
|
}
|
|
17063
16676
|
|
|
17064
16677
|
// src/compatibility/shopify-objects/page.ts
|
|
17065
|
-
function ShopifyPage(
|
|
16678
|
+
function ShopifyPage(_instance2, page) {
|
|
17066
16679
|
if (page instanceof ShopifyResource) {
|
|
17067
16680
|
return page.clone();
|
|
17068
16681
|
}
|
|
@@ -18274,7 +17887,7 @@ ${injects.join("\n")}</script>`;
|
|
|
18274
17887
|
};
|
|
18275
17888
|
|
|
18276
17889
|
// src/compatibility/shopify-objects/template.ts
|
|
18277
|
-
function ShopifyTemplate(
|
|
17890
|
+
function ShopifyTemplate(_instance2, template) {
|
|
18278
17891
|
return new ShopifyResource(
|
|
18279
17892
|
{
|
|
18280
17893
|
directory: template.path,
|
|
@@ -20586,7 +20199,7 @@ var ThemeLoader = class _ThemeLoader {
|
|
|
20586
20199
|
flavor,
|
|
20587
20200
|
trace
|
|
20588
20201
|
});
|
|
20589
|
-
const storage = new
|
|
20202
|
+
const storage = new ThemeFileCache(this.swell.workerEnv, flavor);
|
|
20590
20203
|
const result = await storage.putFiles(configs);
|
|
20591
20204
|
if (result.warnings.length > 0) {
|
|
20592
20205
|
logger.warn("[ThemeLoader] Theme cache updated with warnings", {
|
|
@@ -20624,7 +20237,7 @@ var ThemeLoader = class _ThemeLoader {
|
|
|
20624
20237
|
total: configMetadata.length
|
|
20625
20238
|
});
|
|
20626
20239
|
const flavor = getKVFlavor(this.swell.workerEnv);
|
|
20627
|
-
const storage = new
|
|
20240
|
+
const storage = new ThemeFileCache(this.swell.workerEnv, flavor);
|
|
20628
20241
|
const kvHydrated = await storage.getFiles(configMetadata);
|
|
20629
20242
|
const completeConfigs = await this.ensureConfigsHaveData(kvHydrated);
|
|
20630
20243
|
for (const config of completeConfigs) {
|
|
@@ -22846,6 +22459,547 @@ function getResourceQuery(slug, query) {
|
|
|
22846
22459
|
...query
|
|
22847
22460
|
};
|
|
22848
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
|
+
}
|
|
22849
23003
|
// Annotate the CommonJS export names for ESM import in node:
|
|
22850
23004
|
0 && (module.exports = {
|
|
22851
23005
|
AccountAddressesResource,
|
|
@@ -22864,6 +23018,7 @@ function getResourceQuery(slug, query) {
|
|
|
22864
23018
|
DeferredShopifyResource,
|
|
22865
23019
|
FILE_DATA_INCLUDE_QUERY,
|
|
22866
23020
|
GEO_DATA,
|
|
23021
|
+
HtmlCache,
|
|
22867
23022
|
LANG_TO_COUNTRY_CODES,
|
|
22868
23023
|
LiquidSwell,
|
|
22869
23024
|
MAX_QUERY_PAGE_LIMIT,
|
|
@@ -22911,7 +23066,6 @@ function getResourceQuery(slug, query) {
|
|
|
22911
23066
|
ThemeForm,
|
|
22912
23067
|
ThemeFormErrors,
|
|
22913
23068
|
VariantResource,
|
|
22914
|
-
WorkerHtmlCache,
|
|
22915
23069
|
adaptShopifyFontData,
|
|
22916
23070
|
adaptShopifyFormData,
|
|
22917
23071
|
adaptShopifyMenuData,
|
|
@@ -22939,6 +23093,7 @@ function getResourceQuery(slug, query) {
|
|
|
22939
23093
|
getEasyblocksComponentDefinitions,
|
|
22940
23094
|
getEasyblocksPagePropsWithConfigs,
|
|
22941
23095
|
getEasyblocksPageTemplate,
|
|
23096
|
+
getHtmlCache,
|
|
22942
23097
|
getKVFlavor,
|
|
22943
23098
|
getLayoutSectionGroups,
|
|
22944
23099
|
getMenuItemStorefrontUrl,
|