@swell/apps-sdk 1.0.160 → 1.0.162
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 +573 -417
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +567 -415
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +570 -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 +9 -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 +81 -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
|
@@ -42,10 +42,12 @@ __export(index_exports, {
|
|
|
42
42
|
CartResource: () => CartResource,
|
|
43
43
|
CategoriesResource: () => CategoriesResource,
|
|
44
44
|
CategoryResource: () => CategoryResource,
|
|
45
|
+
DEFAULT_CACHE_RULES: () => DEFAULT_CACHE_RULES,
|
|
45
46
|
DEFAULT_QUERY_PAGE_LIMIT: () => DEFAULT_QUERY_PAGE_LIMIT,
|
|
46
47
|
DeferredShopifyResource: () => DeferredShopifyResource,
|
|
47
48
|
FILE_DATA_INCLUDE_QUERY: () => FILE_DATA_INCLUDE_QUERY,
|
|
48
49
|
GEO_DATA: () => GEO_DATA,
|
|
50
|
+
HtmlCache: () => HtmlCache,
|
|
49
51
|
LANG_TO_COUNTRY_CODES: () => LANG_TO_COUNTRY_CODES,
|
|
50
52
|
LiquidSwell: () => LiquidSwell30,
|
|
51
53
|
MAX_QUERY_PAGE_LIMIT: () => MAX_QUERY_PAGE_LIMIT,
|
|
@@ -93,7 +95,6 @@ __export(index_exports, {
|
|
|
93
95
|
ThemeForm: () => ThemeForm,
|
|
94
96
|
ThemeFormErrors: () => ThemeFormErrors,
|
|
95
97
|
VariantResource: () => VariantResource,
|
|
96
|
-
WorkerHtmlCache: () => WorkerHtmlCache,
|
|
97
98
|
adaptShopifyFontData: () => adaptShopifyFontData,
|
|
98
99
|
adaptShopifyFormData: () => adaptShopifyFormData,
|
|
99
100
|
adaptShopifyMenuData: () => adaptShopifyMenuData,
|
|
@@ -121,6 +122,7 @@ __export(index_exports, {
|
|
|
121
122
|
getEasyblocksComponentDefinitions: () => getEasyblocksComponentDefinitions,
|
|
122
123
|
getEasyblocksPagePropsWithConfigs: () => getEasyblocksPagePropsWithConfigs,
|
|
123
124
|
getEasyblocksPageTemplate: () => getEasyblocksPageTemplate,
|
|
125
|
+
getHtmlCache: () => getHtmlCache,
|
|
124
126
|
getKVFlavor: () => getKVFlavor,
|
|
125
127
|
getLayoutSectionGroups: () => getLayoutSectionGroups,
|
|
126
128
|
getMenuItemStorefrontUrl: () => getMenuItemStorefrontUrl,
|
|
@@ -7437,12 +7439,7 @@ var ThemeCache = class extends Cache {
|
|
|
7437
7439
|
}
|
|
7438
7440
|
};
|
|
7439
7441
|
|
|
7440
|
-
// src/cache/theme-file-storage.ts
|
|
7441
|
-
var import_bluebird2 = __toESM(require("bluebird"), 1);
|
|
7442
|
-
|
|
7443
7442
|
// src/cache/kv-variety.ts
|
|
7444
|
-
var import_bluebird = __toESM(require("bluebird"), 1);
|
|
7445
|
-
var { Promise: Promise2 } = import_bluebird.default;
|
|
7446
7443
|
var CFKV = class {
|
|
7447
7444
|
constructor(kv) {
|
|
7448
7445
|
this.kv = kv;
|
|
@@ -7474,13 +7471,11 @@ var MiniflareKV = class {
|
|
|
7474
7471
|
return /* @__PURE__ */ new Map();
|
|
7475
7472
|
}
|
|
7476
7473
|
const result = /* @__PURE__ */ new Map();
|
|
7477
|
-
await
|
|
7478
|
-
keys
|
|
7479
|
-
async (key) => {
|
|
7474
|
+
await Promise.all(
|
|
7475
|
+
keys.map(async (key) => {
|
|
7480
7476
|
const value = await this.kv.get(key, "text");
|
|
7481
7477
|
result.set(key, value);
|
|
7482
|
-
}
|
|
7483
|
-
{ concurrency: 50 }
|
|
7478
|
+
})
|
|
7484
7479
|
);
|
|
7485
7480
|
return result;
|
|
7486
7481
|
}
|
|
@@ -7512,16 +7507,13 @@ function createClientKV(env, flavor = "cf") {
|
|
|
7512
7507
|
return new MemoryKV();
|
|
7513
7508
|
}
|
|
7514
7509
|
|
|
7515
|
-
// src/cache/theme-file-
|
|
7516
|
-
var
|
|
7517
|
-
var ThemeFileStorage = class {
|
|
7510
|
+
// src/cache/theme-file-cache.ts
|
|
7511
|
+
var ThemeFileCache = class {
|
|
7518
7512
|
kv;
|
|
7519
|
-
maxConcurrency;
|
|
7520
7513
|
maxBatchSize = 20 * 1024 * 1024;
|
|
7521
7514
|
// 20MB safety margin
|
|
7522
7515
|
constructor(env, flavor = "cf") {
|
|
7523
7516
|
this.kv = createClientKV(env, flavor);
|
|
7524
|
-
this.maxConcurrency = flavor === "miniflare" ? 50 : 6;
|
|
7525
7517
|
}
|
|
7526
7518
|
/**
|
|
7527
7519
|
* Build a KV storage key from a file hash
|
|
@@ -7614,10 +7606,8 @@ var ThemeFileStorage = class {
|
|
|
7614
7606
|
totalSize,
|
|
7615
7607
|
trace
|
|
7616
7608
|
});
|
|
7617
|
-
const results = await
|
|
7618
|
-
batches
|
|
7619
|
-
(batch) => this.loadBatch(batch),
|
|
7620
|
-
{ concurrency: Math.min(this.maxConcurrency, batches.length) }
|
|
7609
|
+
const results = await Promise.all(
|
|
7610
|
+
batches.map((batch) => this.loadBatch(batch))
|
|
7621
7611
|
);
|
|
7622
7612
|
const mergedConfigs = this.mergeResults(configs, results);
|
|
7623
7613
|
const loadedCount = mergedConfigs.filter((c) => c.file_data).length;
|
|
@@ -7682,10 +7672,8 @@ var ThemeFileStorage = class {
|
|
|
7682
7672
|
}
|
|
7683
7673
|
const existing = /* @__PURE__ */ new Set();
|
|
7684
7674
|
const batches = this.planGetBatches(configs);
|
|
7685
|
-
const results = await
|
|
7686
|
-
batches
|
|
7687
|
-
(batch) => this.kv.get(batch.keys),
|
|
7688
|
-
{ concurrency: this.maxConcurrency }
|
|
7675
|
+
const results = await Promise.all(
|
|
7676
|
+
batches.map((batch) => this.kv.get(batch.keys))
|
|
7689
7677
|
);
|
|
7690
7678
|
for (const batchResult of results) {
|
|
7691
7679
|
for (const [key, value] of batchResult.entries()) {
|
|
@@ -7749,15 +7737,13 @@ var ThemeFileStorage = class {
|
|
|
7749
7737
|
skippedExisting: existing.size,
|
|
7750
7738
|
trace
|
|
7751
7739
|
});
|
|
7752
|
-
await
|
|
7753
|
-
toWrite
|
|
7754
|
-
async (config) => {
|
|
7740
|
+
await Promise.all(
|
|
7741
|
+
toWrite.map(async (config) => {
|
|
7755
7742
|
const key = this.buildKey(config.hash);
|
|
7756
7743
|
const metadata = config.file?.content_type ? { content_type: config.file.content_type } : void 0;
|
|
7757
|
-
await this.kv.put(key, config.file_data, metadata);
|
|
7744
|
+
await this.kv.put(key, config.file_data, { metadata });
|
|
7758
7745
|
result.written++;
|
|
7759
|
-
}
|
|
7760
|
-
{ concurrency: this.maxConcurrency }
|
|
7746
|
+
})
|
|
7761
7747
|
);
|
|
7762
7748
|
}
|
|
7763
7749
|
logger.info("[ThemeFileStorage] Put files complete", {
|
|
@@ -7771,378 +7757,6 @@ var ThemeFileStorage = class {
|
|
|
7771
7757
|
}
|
|
7772
7758
|
};
|
|
7773
7759
|
|
|
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
7760
|
// src/resources/addresses.ts
|
|
8147
7761
|
var SwellAddresses = class extends SwellStorefrontCollection {
|
|
8148
7762
|
constructor(swell, query) {
|
|
@@ -16408,7 +16022,7 @@ function ShopifyAddress(instance, address, account) {
|
|
|
16408
16022
|
function joinAddressLines(...props) {
|
|
16409
16023
|
return props.filter(Boolean).join("\n");
|
|
16410
16024
|
}
|
|
16411
|
-
function ShopifyCountry(
|
|
16025
|
+
function ShopifyCountry(_instance2, countryCode) {
|
|
16412
16026
|
const currencyCode = getCurrencyByCountry(countryCode) || "USD";
|
|
16413
16027
|
return new ShopifyResource(
|
|
16414
16028
|
{
|
|
@@ -16741,7 +16355,7 @@ async function resolveLastOrder(instance, account) {
|
|
|
16741
16355
|
}
|
|
16742
16356
|
|
|
16743
16357
|
// src/compatibility/shopify-objects/font.ts
|
|
16744
|
-
function ShopifyFont(
|
|
16358
|
+
function ShopifyFont(_instance2, font) {
|
|
16745
16359
|
if (font instanceof ShopifyResource) {
|
|
16746
16360
|
return font.clone();
|
|
16747
16361
|
}
|
|
@@ -16752,7 +16366,7 @@ function ShopifyFont(_instance, font) {
|
|
|
16752
16366
|
family: font.family,
|
|
16753
16367
|
style: font.style,
|
|
16754
16368
|
"system?": font.system,
|
|
16755
|
-
variants: font.variants.map((variant) => ShopifyFont(
|
|
16369
|
+
variants: font.variants.map((variant) => ShopifyFont(_instance2, variant)),
|
|
16756
16370
|
weight: font.weight
|
|
16757
16371
|
});
|
|
16758
16372
|
}
|
|
@@ -16765,7 +16379,7 @@ var SHOPIFY_FORMS = {
|
|
|
16765
16379
|
})
|
|
16766
16380
|
}
|
|
16767
16381
|
};
|
|
16768
|
-
function ShopifyForm(
|
|
16382
|
+
function ShopifyForm(_instance2, form) {
|
|
16769
16383
|
if (form instanceof ShopifyResource) {
|
|
16770
16384
|
return form.clone();
|
|
16771
16385
|
}
|
|
@@ -17062,7 +16676,7 @@ function ShopifyRecommendations(instance, product) {
|
|
|
17062
16676
|
}
|
|
17063
16677
|
|
|
17064
16678
|
// src/compatibility/shopify-objects/page.ts
|
|
17065
|
-
function ShopifyPage(
|
|
16679
|
+
function ShopifyPage(_instance2, page) {
|
|
17066
16680
|
if (page instanceof ShopifyResource) {
|
|
17067
16681
|
return page.clone();
|
|
17068
16682
|
}
|
|
@@ -18274,7 +17888,7 @@ ${injects.join("\n")}</script>`;
|
|
|
18274
17888
|
};
|
|
18275
17889
|
|
|
18276
17890
|
// src/compatibility/shopify-objects/template.ts
|
|
18277
|
-
function ShopifyTemplate(
|
|
17891
|
+
function ShopifyTemplate(_instance2, template) {
|
|
18278
17892
|
return new ShopifyResource(
|
|
18279
17893
|
{
|
|
18280
17894
|
directory: template.path,
|
|
@@ -20586,7 +20200,7 @@ var ThemeLoader = class _ThemeLoader {
|
|
|
20586
20200
|
flavor,
|
|
20587
20201
|
trace
|
|
20588
20202
|
});
|
|
20589
|
-
const storage = new
|
|
20203
|
+
const storage = new ThemeFileCache(this.swell.workerEnv, flavor);
|
|
20590
20204
|
const result = await storage.putFiles(configs);
|
|
20591
20205
|
if (result.warnings.length > 0) {
|
|
20592
20206
|
logger.warn("[ThemeLoader] Theme cache updated with warnings", {
|
|
@@ -20624,7 +20238,7 @@ var ThemeLoader = class _ThemeLoader {
|
|
|
20624
20238
|
total: configMetadata.length
|
|
20625
20239
|
});
|
|
20626
20240
|
const flavor = getKVFlavor(this.swell.workerEnv);
|
|
20627
|
-
const storage = new
|
|
20241
|
+
const storage = new ThemeFileCache(this.swell.workerEnv, flavor);
|
|
20628
20242
|
const kvHydrated = await storage.getFiles(configMetadata);
|
|
20629
20243
|
const completeConfigs = await this.ensureConfigsHaveData(kvHydrated);
|
|
20630
20244
|
for (const config of completeConfigs) {
|
|
@@ -21206,12 +20820,7 @@ var SwellTheme3 = class {
|
|
|
21206
20820
|
// Default value (always StorefrontResource)
|
|
21207
20821
|
() => this.fetchCart()
|
|
21208
20822
|
),
|
|
21209
|
-
this.
|
|
21210
|
-
"account",
|
|
21211
|
-
() => this.fetchAccount(),
|
|
21212
|
-
() => null,
|
|
21213
|
-
false
|
|
21214
|
-
)
|
|
20823
|
+
this.fetchAccount()
|
|
21215
20824
|
]);
|
|
21216
20825
|
if (!cart) {
|
|
21217
20826
|
throw new Error("Failed to fetch cart");
|
|
@@ -22846,6 +22455,551 @@ function getResourceQuery(slug, query) {
|
|
|
22846
22455
|
...query
|
|
22847
22456
|
};
|
|
22848
22457
|
}
|
|
22458
|
+
|
|
22459
|
+
// src/cache/html-cache/html-cache.ts
|
|
22460
|
+
var CACHE_KEY_ORIGIN = "https://cache.swell.store";
|
|
22461
|
+
var DEFAULT_CACHE_RULES = {
|
|
22462
|
+
defaults: {
|
|
22463
|
+
live: { ttl: 20, swr: 60 * 60 * 24 * 7 },
|
|
22464
|
+
// 20s TTL, 1 week SWR
|
|
22465
|
+
preview: { ttl: 10, swr: 60 * 60 * 24 * 7 }
|
|
22466
|
+
// 10s TTL, 1 week SWR
|
|
22467
|
+
},
|
|
22468
|
+
pathRules: [
|
|
22469
|
+
{ path: "/checkout/*", skip: true }
|
|
22470
|
+
]
|
|
22471
|
+
};
|
|
22472
|
+
var HtmlCache = class {
|
|
22473
|
+
epoch;
|
|
22474
|
+
backend;
|
|
22475
|
+
cacheRules;
|
|
22476
|
+
constructor(epoch, backend, cacheRules = DEFAULT_CACHE_RULES) {
|
|
22477
|
+
this.epoch = epoch;
|
|
22478
|
+
this.backend = backend;
|
|
22479
|
+
this.cacheRules = cacheRules;
|
|
22480
|
+
}
|
|
22481
|
+
async get(request) {
|
|
22482
|
+
const trace = createTraceId();
|
|
22483
|
+
if (!this.canReadFromCache(request)) {
|
|
22484
|
+
logger.debug("[SDK Html-cache] non-cacheable request", { trace });
|
|
22485
|
+
return { found: false, cacheable: false };
|
|
22486
|
+
}
|
|
22487
|
+
try {
|
|
22488
|
+
const cacheKey = this.buildCacheKey(request);
|
|
22489
|
+
const entry = await this.backend.read(cacheKey);
|
|
22490
|
+
if (!entry) {
|
|
22491
|
+
logger.debug("[SDK Html-cache] cacheable, MISS", { trace });
|
|
22492
|
+
return { found: false, cacheable: true };
|
|
22493
|
+
}
|
|
22494
|
+
const age = this.getEntryAge(entry);
|
|
22495
|
+
const { ttl, swr } = entry;
|
|
22496
|
+
const isStale = age >= ttl;
|
|
22497
|
+
const isExpired = age >= ttl + swr;
|
|
22498
|
+
if (!isExpired) {
|
|
22499
|
+
logger.debug("[SDK Html-cache] cacheable, HIT", {
|
|
22500
|
+
stale: isStale,
|
|
22501
|
+
age,
|
|
22502
|
+
trace
|
|
22503
|
+
});
|
|
22504
|
+
const clientResponse = this.buildClientResponse(entry, isStale, age);
|
|
22505
|
+
return {
|
|
22506
|
+
found: true,
|
|
22507
|
+
stale: isStale,
|
|
22508
|
+
response: clientResponse,
|
|
22509
|
+
cacheable: true,
|
|
22510
|
+
age: Math.floor(age)
|
|
22511
|
+
};
|
|
22512
|
+
}
|
|
22513
|
+
logger.debug("[SDK Html-cache] cacheable, hit, expired", { trace });
|
|
22514
|
+
return { found: false, cacheable: true };
|
|
22515
|
+
} catch (e) {
|
|
22516
|
+
logger.warn("[SDK Html-cache] get failed", {
|
|
22517
|
+
trace,
|
|
22518
|
+
error: e instanceof Error ? e.message : String(e)
|
|
22519
|
+
});
|
|
22520
|
+
return null;
|
|
22521
|
+
}
|
|
22522
|
+
}
|
|
22523
|
+
async getWithConditionals(request) {
|
|
22524
|
+
const result = await this.get(request);
|
|
22525
|
+
if (!result?.found || result.stale) {
|
|
22526
|
+
return result;
|
|
22527
|
+
}
|
|
22528
|
+
const ifModifiedSince = request.headers.get("If-Modified-Since");
|
|
22529
|
+
const ifNoneMatch = request.headers.get("If-None-Match");
|
|
22530
|
+
if ((ifModifiedSince || ifNoneMatch) && result.response) {
|
|
22531
|
+
const lastModified = result.response.headers.get("Last-Modified");
|
|
22532
|
+
const etag = result.response.headers.get("ETag");
|
|
22533
|
+
if (this.checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag)) {
|
|
22534
|
+
result.notModified = true;
|
|
22535
|
+
result.conditional304 = new Response(null, {
|
|
22536
|
+
status: 304,
|
|
22537
|
+
headers: {
|
|
22538
|
+
"Last-Modified": lastModified || "",
|
|
22539
|
+
ETag: etag || "",
|
|
22540
|
+
"Cache-Control": result.response.headers.get("Cache-Control") || "",
|
|
22541
|
+
"Cloudflare-CDN-Cache-Control": result.response.headers.get("Cloudflare-CDN-Cache-Control") || "",
|
|
22542
|
+
"X-Cache-Status": "HIT-304"
|
|
22543
|
+
}
|
|
22544
|
+
});
|
|
22545
|
+
}
|
|
22546
|
+
}
|
|
22547
|
+
return result;
|
|
22548
|
+
}
|
|
22549
|
+
async put(request, response) {
|
|
22550
|
+
const trace = createTraceId();
|
|
22551
|
+
if (!this.canWriteToCache(request, response)) {
|
|
22552
|
+
logger.debug("[SDK Html-cache] put skipped, non-cacheable", { trace });
|
|
22553
|
+
return;
|
|
22554
|
+
}
|
|
22555
|
+
try {
|
|
22556
|
+
const cacheKey = this.buildCacheKey(request);
|
|
22557
|
+
const ttl = this.getTTLForRequest(request);
|
|
22558
|
+
const swr = this.getSWRForRequest(request);
|
|
22559
|
+
const body = await response.text();
|
|
22560
|
+
const cacheTimeISO = (/* @__PURE__ */ new Date()).toISOString();
|
|
22561
|
+
const headers = this.normalizeHeaders(response.headers);
|
|
22562
|
+
const entry = {
|
|
22563
|
+
status: response.status,
|
|
22564
|
+
statusText: response.statusText,
|
|
22565
|
+
headers,
|
|
22566
|
+
body,
|
|
22567
|
+
cacheTimeISO,
|
|
22568
|
+
ttl,
|
|
22569
|
+
swr,
|
|
22570
|
+
etag: this.quoteETag(headers["etag"] || md5(body)),
|
|
22571
|
+
lastModifiedUTC: headers["last-modified"] || new Date(cacheTimeISO).toUTCString()
|
|
22572
|
+
};
|
|
22573
|
+
const hardExpireSeconds = ttl + swr;
|
|
22574
|
+
await this.backend.write(cacheKey, entry, hardExpireSeconds);
|
|
22575
|
+
logger.debug("[SDK Html-cache] put done", { trace });
|
|
22576
|
+
} catch (e) {
|
|
22577
|
+
logger.warn("[SDK Html-cache] put failed", {
|
|
22578
|
+
trace,
|
|
22579
|
+
error: e instanceof Error ? e.message : String(e)
|
|
22580
|
+
});
|
|
22581
|
+
}
|
|
22582
|
+
}
|
|
22583
|
+
async delete(requestOrKey) {
|
|
22584
|
+
try {
|
|
22585
|
+
const key = typeof requestOrKey === "string" ? requestOrKey : this.buildCacheKey(requestOrKey);
|
|
22586
|
+
if (this.backend.delete) {
|
|
22587
|
+
await this.backend.delete(key);
|
|
22588
|
+
}
|
|
22589
|
+
} catch (e) {
|
|
22590
|
+
logger.warn("[SDK Html-cache] delete failed", {
|
|
22591
|
+
error: e instanceof Error ? e.message : String(e)
|
|
22592
|
+
});
|
|
22593
|
+
}
|
|
22594
|
+
}
|
|
22595
|
+
canReadFromCache(request) {
|
|
22596
|
+
const method = request.method.toUpperCase();
|
|
22597
|
+
return (method === "GET" || method === "HEAD") && this.isRequestCacheable(request);
|
|
22598
|
+
}
|
|
22599
|
+
canWriteToCache(request, response) {
|
|
22600
|
+
const method = request.method.toUpperCase();
|
|
22601
|
+
return method === "GET" && response.ok && this.isRequestCacheable(request) && this.isResponseCacheable(response);
|
|
22602
|
+
}
|
|
22603
|
+
createRevalidationRequest(request) {
|
|
22604
|
+
const headers = new Headers(request.headers);
|
|
22605
|
+
headers.set("X-Cache-Bypass", "revalidation");
|
|
22606
|
+
headers.delete("If-None-Match");
|
|
22607
|
+
headers.delete("If-Modified-Since");
|
|
22608
|
+
headers.delete("Cache-Control");
|
|
22609
|
+
headers.delete("Pragma");
|
|
22610
|
+
return new Request(request.url, {
|
|
22611
|
+
method: "GET",
|
|
22612
|
+
headers
|
|
22613
|
+
});
|
|
22614
|
+
}
|
|
22615
|
+
buildClientResponse(entry, isStale, age) {
|
|
22616
|
+
const headers = new Headers(entry.headers);
|
|
22617
|
+
headers.set("Cache-Control", "public, max-age=0, must-revalidate");
|
|
22618
|
+
headers.set(
|
|
22619
|
+
"Cloudflare-CDN-Cache-Control",
|
|
22620
|
+
`public, s-maxage=${entry.ttl}, stale-while-revalidate=${entry.swr}, stale-if-error=60`
|
|
22621
|
+
);
|
|
22622
|
+
if (entry.lastModifiedUTC) {
|
|
22623
|
+
headers.set("Last-Modified", entry.lastModifiedUTC);
|
|
22624
|
+
}
|
|
22625
|
+
if (entry.etag) {
|
|
22626
|
+
headers.set("ETag", entry.etag);
|
|
22627
|
+
}
|
|
22628
|
+
headers.set("X-Cache-Status", isStale ? "STALE" : "HIT");
|
|
22629
|
+
headers.set("X-Cache-Age", Math.floor(age).toString());
|
|
22630
|
+
this.sanitizeClientHeaders(headers);
|
|
22631
|
+
return new Response(entry.body, {
|
|
22632
|
+
status: entry.status,
|
|
22633
|
+
statusText: entry.statusText,
|
|
22634
|
+
headers
|
|
22635
|
+
});
|
|
22636
|
+
}
|
|
22637
|
+
buildCacheKey(request) {
|
|
22638
|
+
const url = new URL(request.url);
|
|
22639
|
+
const versionHash = this.generateVersionHash(request.headers);
|
|
22640
|
+
const normalizedQuery = this.normalizeSearchParams(url.searchParams);
|
|
22641
|
+
const cacheKeyPath = `${versionHash}${url.pathname}`;
|
|
22642
|
+
const keyUrl = new URL(`${CACHE_KEY_ORIGIN}${cacheKeyPath}`);
|
|
22643
|
+
if (normalizedQuery) {
|
|
22644
|
+
keyUrl.search = `?${normalizedQuery}`;
|
|
22645
|
+
}
|
|
22646
|
+
return keyUrl.toString();
|
|
22647
|
+
}
|
|
22648
|
+
checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag) {
|
|
22649
|
+
if (this.ifNoneMatchMatches(ifNoneMatch, etag)) {
|
|
22650
|
+
return true;
|
|
22651
|
+
}
|
|
22652
|
+
if (ifModifiedSince && lastModified) {
|
|
22653
|
+
try {
|
|
22654
|
+
const ifModDate = new Date(ifModifiedSince);
|
|
22655
|
+
const lastModDate = new Date(lastModified);
|
|
22656
|
+
if (isNaN(ifModDate.getTime()) || isNaN(lastModDate.getTime())) {
|
|
22657
|
+
return false;
|
|
22658
|
+
}
|
|
22659
|
+
return ifModDate >= lastModDate;
|
|
22660
|
+
} catch {
|
|
22661
|
+
return false;
|
|
22662
|
+
}
|
|
22663
|
+
}
|
|
22664
|
+
return false;
|
|
22665
|
+
}
|
|
22666
|
+
ifNoneMatchMatches(ifNoneMatch, etag) {
|
|
22667
|
+
if (!ifNoneMatch || !etag) return false;
|
|
22668
|
+
const header = ifNoneMatch.trim();
|
|
22669
|
+
if (header === "*") return true;
|
|
22670
|
+
const tokens = header.split(",").map((t) => t.trim());
|
|
22671
|
+
const normalizedEtag = etag.replace(/^W\//, "");
|
|
22672
|
+
for (const token of tokens) {
|
|
22673
|
+
if (token === etag) return true;
|
|
22674
|
+
const normalizedToken = token.replace(/^W\//, "");
|
|
22675
|
+
if (normalizedToken === normalizedEtag) return true;
|
|
22676
|
+
}
|
|
22677
|
+
return false;
|
|
22678
|
+
}
|
|
22679
|
+
quoteETag(value) {
|
|
22680
|
+
if (!value) return value;
|
|
22681
|
+
if (value.startsWith('"') || value.startsWith('W/"')) return value;
|
|
22682
|
+
if (value.startsWith("W/")) return `W/"${value.slice(2)}"`;
|
|
22683
|
+
return `"${value}"`;
|
|
22684
|
+
}
|
|
22685
|
+
sanitizeClientHeaders(headers) {
|
|
22686
|
+
const HOP_BY_HOP = [
|
|
22687
|
+
"connection",
|
|
22688
|
+
"proxy-connection",
|
|
22689
|
+
"keep-alive",
|
|
22690
|
+
"transfer-encoding",
|
|
22691
|
+
"upgrade",
|
|
22692
|
+
"proxy-authenticate",
|
|
22693
|
+
"proxy-authorization",
|
|
22694
|
+
"te",
|
|
22695
|
+
"trailers",
|
|
22696
|
+
"via",
|
|
22697
|
+
"alt-svc",
|
|
22698
|
+
"content-length"
|
|
22699
|
+
];
|
|
22700
|
+
for (const h of HOP_BY_HOP) headers.delete(h);
|
|
22701
|
+
headers.delete("content-encoding");
|
|
22702
|
+
headers.delete("x-original-ttl");
|
|
22703
|
+
headers.delete("x-original-swr");
|
|
22704
|
+
headers.delete("x-cache-time");
|
|
22705
|
+
}
|
|
22706
|
+
generateVersionHash(headers) {
|
|
22707
|
+
const swellData = this.extractSwellData(headers);
|
|
22708
|
+
const acceptLang = headers.get("accept-language") || "";
|
|
22709
|
+
const accept = headers.get("accept") || "";
|
|
22710
|
+
const versionFactors = {
|
|
22711
|
+
store: headers.get("swell-storefront-id") || "",
|
|
22712
|
+
app: (headers.get("swell-app-id") || "") + "@" + (swellData["swell-app-version"] || ""),
|
|
22713
|
+
auth: headers.get("swell-access-token") || "",
|
|
22714
|
+
theme: headers.get("swell-theme-version-hash") || "",
|
|
22715
|
+
modified: headers.get("swell-cache-modified") || "",
|
|
22716
|
+
currency: swellData["swell-currency"] || "USD",
|
|
22717
|
+
locale: headers.get("x-locale") || acceptLang.split(",")[0].trim().toLowerCase() || "default",
|
|
22718
|
+
context: headers.get("swell-storefront-context"),
|
|
22719
|
+
accept,
|
|
22720
|
+
epoch: this.epoch
|
|
22721
|
+
};
|
|
22722
|
+
return md5(JSON.stringify(versionFactors));
|
|
22723
|
+
}
|
|
22724
|
+
extractSwellData(headers) {
|
|
22725
|
+
const cookie = headers.get("cookie");
|
|
22726
|
+
if (!cookie) return {};
|
|
22727
|
+
const swellDataMatch = cookie.match(/swell-data=([^;]+)/);
|
|
22728
|
+
if (!swellDataMatch) return {};
|
|
22729
|
+
try {
|
|
22730
|
+
return JSON.parse(decodeURIComponent(swellDataMatch[1])) || {};
|
|
22731
|
+
} catch {
|
|
22732
|
+
return {};
|
|
22733
|
+
}
|
|
22734
|
+
}
|
|
22735
|
+
isRequestCacheable(request) {
|
|
22736
|
+
const url = new URL(request.url);
|
|
22737
|
+
if (request.headers.get("swell-deployment-mode") === "editor") return false;
|
|
22738
|
+
if (this.cacheRules.pathRules) {
|
|
22739
|
+
for (const rule of this.cacheRules.pathRules) {
|
|
22740
|
+
if (this.pathMatches(rule.path, url.pathname) && rule.skip) {
|
|
22741
|
+
return false;
|
|
22742
|
+
}
|
|
22743
|
+
}
|
|
22744
|
+
}
|
|
22745
|
+
if (request.headers.get("cache-control")?.includes("no-cache"))
|
|
22746
|
+
return false;
|
|
22747
|
+
return true;
|
|
22748
|
+
}
|
|
22749
|
+
isResponseCacheable(response) {
|
|
22750
|
+
if (!response.headers.get("content-type")?.includes("text/html"))
|
|
22751
|
+
return false;
|
|
22752
|
+
if (response.headers.get("set-cookie")) return false;
|
|
22753
|
+
const cacheControl = response.headers.get("cache-control");
|
|
22754
|
+
if (cacheControl?.includes("no-store") || cacheControl?.includes("private"))
|
|
22755
|
+
return false;
|
|
22756
|
+
return true;
|
|
22757
|
+
}
|
|
22758
|
+
getDeploymentMode(headers) {
|
|
22759
|
+
const mode = headers.get("swell-deployment-mode");
|
|
22760
|
+
return mode === "preview" ? "preview" : "live";
|
|
22761
|
+
}
|
|
22762
|
+
getTTLForRequest(request) {
|
|
22763
|
+
const url = new URL(request.url);
|
|
22764
|
+
const mode = this.getDeploymentMode(request.headers);
|
|
22765
|
+
if (this.cacheRules.pathRules) {
|
|
22766
|
+
for (const rule of this.cacheRules.pathRules) {
|
|
22767
|
+
if (this.pathMatches(rule.path, url.pathname) && rule.ttl !== void 0) {
|
|
22768
|
+
return rule.ttl;
|
|
22769
|
+
}
|
|
22770
|
+
}
|
|
22771
|
+
}
|
|
22772
|
+
const defaults = this.cacheRules.defaults?.[mode];
|
|
22773
|
+
return defaults?.ttl ?? DEFAULT_CACHE_RULES.defaults[mode].ttl;
|
|
22774
|
+
}
|
|
22775
|
+
getSWRForRequest(request) {
|
|
22776
|
+
const url = new URL(request.url);
|
|
22777
|
+
const mode = this.getDeploymentMode(request.headers);
|
|
22778
|
+
if (this.cacheRules.pathRules) {
|
|
22779
|
+
for (const rule of this.cacheRules.pathRules) {
|
|
22780
|
+
if (this.pathMatches(rule.path, url.pathname) && rule.swr !== void 0) {
|
|
22781
|
+
return rule.swr;
|
|
22782
|
+
}
|
|
22783
|
+
}
|
|
22784
|
+
}
|
|
22785
|
+
const defaults = this.cacheRules.defaults?.[mode];
|
|
22786
|
+
return defaults?.swr ?? DEFAULT_CACHE_RULES.defaults[mode].swr;
|
|
22787
|
+
}
|
|
22788
|
+
getEntryAge(entry) {
|
|
22789
|
+
const t = Date.parse(entry.cacheTimeISO);
|
|
22790
|
+
if (Number.isNaN(t)) return Infinity;
|
|
22791
|
+
const age = (Date.now() - t) / 1e3;
|
|
22792
|
+
return age < 0 ? 0 : age;
|
|
22793
|
+
}
|
|
22794
|
+
/**
|
|
22795
|
+
* Converts wildcard pattern to regex and tests against path.
|
|
22796
|
+
* - * matches any characters except /
|
|
22797
|
+
* - ** matches any characters including /
|
|
22798
|
+
*/
|
|
22799
|
+
pathMatches(pattern, path) {
|
|
22800
|
+
const regex = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "___DOUBLE_STAR___").replace(/\*/g, "[^/]*").replace(/___DOUBLE_STAR___/g, ".*");
|
|
22801
|
+
return new RegExp(`^${regex}$`).test(path);
|
|
22802
|
+
}
|
|
22803
|
+
normalizeHeaders(headers) {
|
|
22804
|
+
const normalized = {};
|
|
22805
|
+
headers.forEach((value, key) => {
|
|
22806
|
+
normalized[key.toLowerCase()] = value;
|
|
22807
|
+
});
|
|
22808
|
+
return normalized;
|
|
22809
|
+
}
|
|
22810
|
+
normalizeSearchParams(searchParams) {
|
|
22811
|
+
const ignoredParams = [
|
|
22812
|
+
"utm_source",
|
|
22813
|
+
"utm_medium",
|
|
22814
|
+
"utm_campaign",
|
|
22815
|
+
"utm_content",
|
|
22816
|
+
"utm_term",
|
|
22817
|
+
"fbclid",
|
|
22818
|
+
"gclid",
|
|
22819
|
+
"gbraid",
|
|
22820
|
+
"wbraid",
|
|
22821
|
+
"ref",
|
|
22822
|
+
"source",
|
|
22823
|
+
"mc_cid",
|
|
22824
|
+
"mc_eid"
|
|
22825
|
+
];
|
|
22826
|
+
const relevantParams = [];
|
|
22827
|
+
searchParams.forEach((value, key) => {
|
|
22828
|
+
if (!ignoredParams.includes(key)) {
|
|
22829
|
+
relevantParams.push(
|
|
22830
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
|
22831
|
+
);
|
|
22832
|
+
}
|
|
22833
|
+
});
|
|
22834
|
+
return relevantParams.sort().join("&");
|
|
22835
|
+
}
|
|
22836
|
+
};
|
|
22837
|
+
|
|
22838
|
+
// src/cache/html-cache/html-cache-kv.ts
|
|
22839
|
+
var KVCacheBackend = class {
|
|
22840
|
+
kv;
|
|
22841
|
+
prefix;
|
|
22842
|
+
hashKeys;
|
|
22843
|
+
maxValueBytes;
|
|
22844
|
+
constructor(kv, opts = {}) {
|
|
22845
|
+
this.kv = kv;
|
|
22846
|
+
this.prefix = opts.prefix;
|
|
22847
|
+
this.hashKeys = opts.hashKeys !== false;
|
|
22848
|
+
this.maxValueBytes = opts.maxValueBytes ?? Math.floor(24.5 * 1024 * 1024);
|
|
22849
|
+
}
|
|
22850
|
+
async read(key) {
|
|
22851
|
+
const kvKey = this.makeKey(key);
|
|
22852
|
+
const value = await this.kv.get(kvKey, "json");
|
|
22853
|
+
if (!value) return null;
|
|
22854
|
+
const entry = {
|
|
22855
|
+
status: value.status,
|
|
22856
|
+
statusText: value.statusText,
|
|
22857
|
+
headers: value.headers,
|
|
22858
|
+
body: value.body,
|
|
22859
|
+
cacheTimeISO: value.cacheTimeISO,
|
|
22860
|
+
ttl: value.ttl,
|
|
22861
|
+
swr: value.swr,
|
|
22862
|
+
etag: value.etag,
|
|
22863
|
+
lastModifiedUTC: value.lastModifiedUTC
|
|
22864
|
+
};
|
|
22865
|
+
return entry;
|
|
22866
|
+
}
|
|
22867
|
+
async write(key, entry, hardExpireSeconds) {
|
|
22868
|
+
const kvKey = this.makeKey(key);
|
|
22869
|
+
const payload = {
|
|
22870
|
+
v: 1,
|
|
22871
|
+
status: entry.status,
|
|
22872
|
+
statusText: entry.statusText,
|
|
22873
|
+
headers: entry.headers,
|
|
22874
|
+
body: entry.body,
|
|
22875
|
+
cacheTimeISO: entry.cacheTimeISO,
|
|
22876
|
+
ttl: entry.ttl,
|
|
22877
|
+
swr: entry.swr,
|
|
22878
|
+
etag: entry.etag,
|
|
22879
|
+
lastModifiedUTC: entry.lastModifiedUTC
|
|
22880
|
+
};
|
|
22881
|
+
const json = JSON.stringify(payload);
|
|
22882
|
+
this.assertSize(json);
|
|
22883
|
+
const metadata = {
|
|
22884
|
+
v: 1,
|
|
22885
|
+
cacheTimeISO: entry.cacheTimeISO,
|
|
22886
|
+
ttl: entry.ttl,
|
|
22887
|
+
swr: entry.swr,
|
|
22888
|
+
etag: entry.etag,
|
|
22889
|
+
lastModifiedUTC: entry.lastModifiedUTC
|
|
22890
|
+
};
|
|
22891
|
+
await this.kv.put(kvKey, json, {
|
|
22892
|
+
expirationTtl: hardExpireSeconds + 60,
|
|
22893
|
+
// natural hard expiry after SWR + grace period
|
|
22894
|
+
metadata
|
|
22895
|
+
});
|
|
22896
|
+
}
|
|
22897
|
+
async delete(key) {
|
|
22898
|
+
const kvKey = this.makeKey(key);
|
|
22899
|
+
await this.kv.delete(kvKey);
|
|
22900
|
+
}
|
|
22901
|
+
// ---- private helpers ----
|
|
22902
|
+
makeKey(raw) {
|
|
22903
|
+
const core = this.hashKeys ? md5(raw) : raw;
|
|
22904
|
+
return this.prefix ? `${this.prefix}:${core}` : core;
|
|
22905
|
+
}
|
|
22906
|
+
assertSize(json) {
|
|
22907
|
+
const bytes = typeof TextEncoder !== "undefined" ? new TextEncoder().encode(json).length : this.approxUtf8Bytes(json);
|
|
22908
|
+
if (bytes > this.maxValueBytes) {
|
|
22909
|
+
throw new Error(
|
|
22910
|
+
`KV value too large: ${bytes} bytes exceeds limit ${this.maxValueBytes} bytes`
|
|
22911
|
+
);
|
|
22912
|
+
}
|
|
22913
|
+
}
|
|
22914
|
+
approxUtf8Bytes(str) {
|
|
22915
|
+
let count = 0;
|
|
22916
|
+
for (let i = 0; i < str.length; i++) {
|
|
22917
|
+
const code = str.charCodeAt(i);
|
|
22918
|
+
if (code <= 127) count += 1;
|
|
22919
|
+
else if (code <= 2047) count += 2;
|
|
22920
|
+
else count += 3;
|
|
22921
|
+
}
|
|
22922
|
+
return count;
|
|
22923
|
+
}
|
|
22924
|
+
};
|
|
22925
|
+
|
|
22926
|
+
// src/cache/html-cache/html-cache-worker.ts
|
|
22927
|
+
var CACHE_NAME_PREFIX = "swell-html-v0";
|
|
22928
|
+
var WorkerCacheBackend = class {
|
|
22929
|
+
cacheName;
|
|
22930
|
+
constructor(epoch) {
|
|
22931
|
+
this.cacheName = CACHE_NAME_PREFIX + epoch;
|
|
22932
|
+
}
|
|
22933
|
+
async read(key) {
|
|
22934
|
+
const cache = await caches.open(this.cacheName);
|
|
22935
|
+
const request = new Request(key);
|
|
22936
|
+
const response = await cache.match(request);
|
|
22937
|
+
if (!response) return null;
|
|
22938
|
+
const headers = {};
|
|
22939
|
+
response.headers.forEach((value, name) => {
|
|
22940
|
+
headers[name.toLowerCase()] = value;
|
|
22941
|
+
});
|
|
22942
|
+
return {
|
|
22943
|
+
status: response.status,
|
|
22944
|
+
statusText: response.statusText,
|
|
22945
|
+
headers,
|
|
22946
|
+
body: await response.text(),
|
|
22947
|
+
cacheTimeISO: response.headers.get("x-cache-time") || (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
22948
|
+
ttl: parseInt(response.headers.get("x-original-ttl") || "0", 10),
|
|
22949
|
+
swr: parseInt(response.headers.get("x-original-swr") || "0", 10),
|
|
22950
|
+
etag: response.headers.get("etag") || void 0,
|
|
22951
|
+
lastModifiedUTC: response.headers.get("last-modified") || void 0
|
|
22952
|
+
};
|
|
22953
|
+
}
|
|
22954
|
+
async write(key, entry, _hardExpireSeconds) {
|
|
22955
|
+
const cache = await caches.open(this.cacheName);
|
|
22956
|
+
const request = new Request(key);
|
|
22957
|
+
const headers = new Headers(entry.headers);
|
|
22958
|
+
if (entry.lastModifiedUTC && !headers.get("Last-Modified")) {
|
|
22959
|
+
headers.set("Last-Modified", entry.lastModifiedUTC);
|
|
22960
|
+
}
|
|
22961
|
+
if (entry.etag && !headers.get("ETag")) {
|
|
22962
|
+
headers.set("ETag", entry.etag);
|
|
22963
|
+
}
|
|
22964
|
+
headers.set("X-Cache-Time", entry.cacheTimeISO);
|
|
22965
|
+
headers.set("X-Original-TTL", String(entry.ttl));
|
|
22966
|
+
headers.set("X-Original-SWR", String(entry.swr));
|
|
22967
|
+
headers.delete("content-encoding");
|
|
22968
|
+
headers.delete("content-length");
|
|
22969
|
+
const existing = headers.get("Cache-Control");
|
|
22970
|
+
if (!existing || existing.trim().toLowerCase() === "public") {
|
|
22971
|
+
headers.set("Cache-Control", `public, max-age=${entry.ttl + entry.swr}`);
|
|
22972
|
+
}
|
|
22973
|
+
const response = new Response(entry.body, {
|
|
22974
|
+
status: entry.status,
|
|
22975
|
+
statusText: entry.statusText,
|
|
22976
|
+
headers
|
|
22977
|
+
});
|
|
22978
|
+
await cache.delete(request);
|
|
22979
|
+
await cache.put(request, response);
|
|
22980
|
+
}
|
|
22981
|
+
async delete(key) {
|
|
22982
|
+
const cache = await caches.open(this.cacheName);
|
|
22983
|
+
const request = new Request(key);
|
|
22984
|
+
await cache.delete(request);
|
|
22985
|
+
}
|
|
22986
|
+
};
|
|
22987
|
+
|
|
22988
|
+
// src/cache/html-cache/html-cache-factory.ts
|
|
22989
|
+
var _instance = null;
|
|
22990
|
+
function getHtmlCache(env, cacheRules) {
|
|
22991
|
+
const epoch = env?.HTML_CACHE_EPOCH;
|
|
22992
|
+
if (typeof epoch !== "string" || !epoch) return null;
|
|
22993
|
+
if (_instance) return _instance;
|
|
22994
|
+
const kv = env?.NAMESPACE;
|
|
22995
|
+
const rules = cacheRules || env?.HTML_CACHE_RULES;
|
|
22996
|
+
if (env?.HTML_CACHE_BACKEND !== "worker" && kv) {
|
|
22997
|
+
_instance = new HtmlCache(epoch, new KVCacheBackend(kv), rules);
|
|
22998
|
+
return _instance;
|
|
22999
|
+
}
|
|
23000
|
+
_instance = new HtmlCache(epoch, new WorkerCacheBackend(epoch), rules);
|
|
23001
|
+
return _instance;
|
|
23002
|
+
}
|
|
22849
23003
|
// Annotate the CommonJS export names for ESM import in node:
|
|
22850
23004
|
0 && (module.exports = {
|
|
22851
23005
|
AccountAddressesResource,
|
|
@@ -22860,10 +23014,12 @@ function getResourceQuery(slug, query) {
|
|
|
22860
23014
|
CartResource,
|
|
22861
23015
|
CategoriesResource,
|
|
22862
23016
|
CategoryResource,
|
|
23017
|
+
DEFAULT_CACHE_RULES,
|
|
22863
23018
|
DEFAULT_QUERY_PAGE_LIMIT,
|
|
22864
23019
|
DeferredShopifyResource,
|
|
22865
23020
|
FILE_DATA_INCLUDE_QUERY,
|
|
22866
23021
|
GEO_DATA,
|
|
23022
|
+
HtmlCache,
|
|
22867
23023
|
LANG_TO_COUNTRY_CODES,
|
|
22868
23024
|
LiquidSwell,
|
|
22869
23025
|
MAX_QUERY_PAGE_LIMIT,
|
|
@@ -22911,7 +23067,6 @@ function getResourceQuery(slug, query) {
|
|
|
22911
23067
|
ThemeForm,
|
|
22912
23068
|
ThemeFormErrors,
|
|
22913
23069
|
VariantResource,
|
|
22914
|
-
WorkerHtmlCache,
|
|
22915
23070
|
adaptShopifyFontData,
|
|
22916
23071
|
adaptShopifyFormData,
|
|
22917
23072
|
adaptShopifyMenuData,
|
|
@@ -22939,6 +23094,7 @@ function getResourceQuery(slug, query) {
|
|
|
22939
23094
|
getEasyblocksComponentDefinitions,
|
|
22940
23095
|
getEasyblocksPagePropsWithConfigs,
|
|
22941
23096
|
getEasyblocksPageTemplate,
|
|
23097
|
+
getHtmlCache,
|
|
22942
23098
|
getKVFlavor,
|
|
22943
23099
|
getLayoutSectionGroups,
|
|
22944
23100
|
getMenuItemStorefrontUrl,
|