@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.mjs
CHANGED
|
@@ -7291,12 +7291,7 @@ var ThemeCache = class extends Cache {
|
|
|
7291
7291
|
}
|
|
7292
7292
|
};
|
|
7293
7293
|
|
|
7294
|
-
// src/cache/theme-file-storage.ts
|
|
7295
|
-
import bluebird2 from "bluebird";
|
|
7296
|
-
|
|
7297
7294
|
// src/cache/kv-variety.ts
|
|
7298
|
-
import bluebird from "bluebird";
|
|
7299
|
-
var { Promise: Promise2 } = bluebird;
|
|
7300
7295
|
var CFKV = class {
|
|
7301
7296
|
constructor(kv) {
|
|
7302
7297
|
this.kv = kv;
|
|
@@ -7328,13 +7323,11 @@ var MiniflareKV = class {
|
|
|
7328
7323
|
return /* @__PURE__ */ new Map();
|
|
7329
7324
|
}
|
|
7330
7325
|
const result = /* @__PURE__ */ new Map();
|
|
7331
|
-
await
|
|
7332
|
-
keys
|
|
7333
|
-
async (key) => {
|
|
7326
|
+
await Promise.all(
|
|
7327
|
+
keys.map(async (key) => {
|
|
7334
7328
|
const value = await this.kv.get(key, "text");
|
|
7335
7329
|
result.set(key, value);
|
|
7336
|
-
}
|
|
7337
|
-
{ concurrency: 50 }
|
|
7330
|
+
})
|
|
7338
7331
|
);
|
|
7339
7332
|
return result;
|
|
7340
7333
|
}
|
|
@@ -7366,16 +7359,13 @@ function createClientKV(env, flavor = "cf") {
|
|
|
7366
7359
|
return new MemoryKV();
|
|
7367
7360
|
}
|
|
7368
7361
|
|
|
7369
|
-
// src/cache/theme-file-
|
|
7370
|
-
var
|
|
7371
|
-
var ThemeFileStorage = class {
|
|
7362
|
+
// src/cache/theme-file-cache.ts
|
|
7363
|
+
var ThemeFileCache = class {
|
|
7372
7364
|
kv;
|
|
7373
|
-
maxConcurrency;
|
|
7374
7365
|
maxBatchSize = 20 * 1024 * 1024;
|
|
7375
7366
|
// 20MB safety margin
|
|
7376
7367
|
constructor(env, flavor = "cf") {
|
|
7377
7368
|
this.kv = createClientKV(env, flavor);
|
|
7378
|
-
this.maxConcurrency = flavor === "miniflare" ? 50 : 6;
|
|
7379
7369
|
}
|
|
7380
7370
|
/**
|
|
7381
7371
|
* Build a KV storage key from a file hash
|
|
@@ -7468,10 +7458,8 @@ var ThemeFileStorage = class {
|
|
|
7468
7458
|
totalSize,
|
|
7469
7459
|
trace
|
|
7470
7460
|
});
|
|
7471
|
-
const results = await
|
|
7472
|
-
batches
|
|
7473
|
-
(batch) => this.loadBatch(batch),
|
|
7474
|
-
{ concurrency: Math.min(this.maxConcurrency, batches.length) }
|
|
7461
|
+
const results = await Promise.all(
|
|
7462
|
+
batches.map((batch) => this.loadBatch(batch))
|
|
7475
7463
|
);
|
|
7476
7464
|
const mergedConfigs = this.mergeResults(configs, results);
|
|
7477
7465
|
const loadedCount = mergedConfigs.filter((c) => c.file_data).length;
|
|
@@ -7536,10 +7524,8 @@ var ThemeFileStorage = class {
|
|
|
7536
7524
|
}
|
|
7537
7525
|
const existing = /* @__PURE__ */ new Set();
|
|
7538
7526
|
const batches = this.planGetBatches(configs);
|
|
7539
|
-
const results = await
|
|
7540
|
-
batches
|
|
7541
|
-
(batch) => this.kv.get(batch.keys),
|
|
7542
|
-
{ concurrency: this.maxConcurrency }
|
|
7527
|
+
const results = await Promise.all(
|
|
7528
|
+
batches.map((batch) => this.kv.get(batch.keys))
|
|
7543
7529
|
);
|
|
7544
7530
|
for (const batchResult of results) {
|
|
7545
7531
|
for (const [key, value] of batchResult.entries()) {
|
|
@@ -7603,15 +7589,13 @@ var ThemeFileStorage = class {
|
|
|
7603
7589
|
skippedExisting: existing.size,
|
|
7604
7590
|
trace
|
|
7605
7591
|
});
|
|
7606
|
-
await
|
|
7607
|
-
toWrite
|
|
7608
|
-
async (config) => {
|
|
7592
|
+
await Promise.all(
|
|
7593
|
+
toWrite.map(async (config) => {
|
|
7609
7594
|
const key = this.buildKey(config.hash);
|
|
7610
7595
|
const metadata = config.file?.content_type ? { content_type: config.file.content_type } : void 0;
|
|
7611
|
-
await this.kv.put(key, config.file_data, metadata);
|
|
7596
|
+
await this.kv.put(key, config.file_data, { metadata });
|
|
7612
7597
|
result.written++;
|
|
7613
|
-
}
|
|
7614
|
-
{ concurrency: this.maxConcurrency }
|
|
7598
|
+
})
|
|
7615
7599
|
);
|
|
7616
7600
|
}
|
|
7617
7601
|
logger.info("[ThemeFileStorage] Put files complete", {
|
|
@@ -7625,378 +7609,6 @@ var ThemeFileStorage = class {
|
|
|
7625
7609
|
}
|
|
7626
7610
|
};
|
|
7627
7611
|
|
|
7628
|
-
// src/cache/constants.ts
|
|
7629
|
-
var SECOND = 1e3;
|
|
7630
|
-
var MINUTE = 60 * SECOND;
|
|
7631
|
-
var HOUR = 60 * MINUTE;
|
|
7632
|
-
var DAY = 24 * HOUR;
|
|
7633
|
-
var YEAR = 365 * DAY;
|
|
7634
|
-
var SHORT_TTL = 5 * SECOND;
|
|
7635
|
-
|
|
7636
|
-
// src/cache/worker-html-cache.ts
|
|
7637
|
-
var CACHE_NAME = "swell-html-v0";
|
|
7638
|
-
var CACHE_KEY_ORIGIN = "https://cache.swell.store";
|
|
7639
|
-
var TTL_CONFIG = {
|
|
7640
|
-
LIVE: {
|
|
7641
|
-
DEFAULT: 20,
|
|
7642
|
-
HOME: 20,
|
|
7643
|
-
PRODUCT: 20,
|
|
7644
|
-
COLLECTION: 20,
|
|
7645
|
-
PAGE: 20,
|
|
7646
|
-
BLOG: 20,
|
|
7647
|
-
SWR: 180
|
|
7648
|
-
},
|
|
7649
|
-
PREVIEW: {
|
|
7650
|
-
DEFAULT: 20,
|
|
7651
|
-
HOME: 20,
|
|
7652
|
-
PRODUCT: 20,
|
|
7653
|
-
COLLECTION: 20,
|
|
7654
|
-
PAGE: 20,
|
|
7655
|
-
BLOG: 20,
|
|
7656
|
-
SWR: 180
|
|
7657
|
-
}
|
|
7658
|
-
};
|
|
7659
|
-
var WorkerHtmlCache = class {
|
|
7660
|
-
epoch;
|
|
7661
|
-
constructor(epoch) {
|
|
7662
|
-
this.epoch = epoch;
|
|
7663
|
-
}
|
|
7664
|
-
async get(request) {
|
|
7665
|
-
const trace = createTraceId();
|
|
7666
|
-
if (request.method !== "GET") {
|
|
7667
|
-
logger.debug("[SDK Html-cache] non-cacheable", { trace });
|
|
7668
|
-
return { found: false, cacheable: false };
|
|
7669
|
-
}
|
|
7670
|
-
if (!this.isCacheable(request)) {
|
|
7671
|
-
logger.debug("[SDK Html-cache] non-cacheable", { trace });
|
|
7672
|
-
return { found: false, cacheable: false };
|
|
7673
|
-
}
|
|
7674
|
-
try {
|
|
7675
|
-
const cache = await caches.open(CACHE_NAME + this.epoch);
|
|
7676
|
-
const cacheKey = this.buildCacheKey(request);
|
|
7677
|
-
const cached = await cache.match(cacheKey);
|
|
7678
|
-
if (!cached) {
|
|
7679
|
-
logger.debug("[SDK Html-cache] cacheable, MISS", { trace });
|
|
7680
|
-
return { found: false, cacheable: true };
|
|
7681
|
-
}
|
|
7682
|
-
const age = this.getResponseAge(cached);
|
|
7683
|
-
const ttl = parseInt(cached.headers.get("X-Original-TTL") || "") || this.getTTLForRequest(request);
|
|
7684
|
-
const swr = parseInt(cached.headers.get("X-Original-SWR") || "") || this.getSWRForRequest(request);
|
|
7685
|
-
const isStale = age >= ttl;
|
|
7686
|
-
const isExpired = age >= ttl + swr;
|
|
7687
|
-
if (!isExpired) {
|
|
7688
|
-
logger.debug("[SDK Html-cache] cacheable, HIT", {
|
|
7689
|
-
stale: isStale,
|
|
7690
|
-
age,
|
|
7691
|
-
trace
|
|
7692
|
-
});
|
|
7693
|
-
const clientResponse = this.buildClientResponse(
|
|
7694
|
-
cached,
|
|
7695
|
-
ttl,
|
|
7696
|
-
swr,
|
|
7697
|
-
isStale,
|
|
7698
|
-
age
|
|
7699
|
-
);
|
|
7700
|
-
return {
|
|
7701
|
-
found: true,
|
|
7702
|
-
stale: isStale,
|
|
7703
|
-
response: clientResponse,
|
|
7704
|
-
cacheable: true,
|
|
7705
|
-
age: Math.floor(age)
|
|
7706
|
-
};
|
|
7707
|
-
}
|
|
7708
|
-
logger.debug("[SDK Html-cache] cacheable, hit, expired", { trace });
|
|
7709
|
-
return { found: false, cacheable: true };
|
|
7710
|
-
} catch (_) {
|
|
7711
|
-
logger.warn("[SDK Html-cache] no get support", { trace });
|
|
7712
|
-
return null;
|
|
7713
|
-
}
|
|
7714
|
-
}
|
|
7715
|
-
// 304 support
|
|
7716
|
-
async getWithConditionals(request) {
|
|
7717
|
-
const result = await this.get(request);
|
|
7718
|
-
if (!result?.found || result.stale) {
|
|
7719
|
-
return result;
|
|
7720
|
-
}
|
|
7721
|
-
const ifModifiedSince = request.headers.get("If-Modified-Since");
|
|
7722
|
-
const ifNoneMatch = request.headers.get("If-None-Match");
|
|
7723
|
-
if ((ifModifiedSince || ifNoneMatch) && result.response) {
|
|
7724
|
-
const lastModified = result.response.headers.get("Last-Modified");
|
|
7725
|
-
const etag = result.response.headers.get("ETag");
|
|
7726
|
-
if (this.checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag)) {
|
|
7727
|
-
result.notModified = true;
|
|
7728
|
-
result.conditional304 = new Response(null, {
|
|
7729
|
-
status: 304,
|
|
7730
|
-
headers: {
|
|
7731
|
-
"Last-Modified": lastModified || "",
|
|
7732
|
-
ETag: etag || "",
|
|
7733
|
-
"Cache-Control": result.response.headers.get("Cache-Control") || "",
|
|
7734
|
-
"Cloudflare-CDN-Cache-Control": result.response.headers.get("Cloudflare-CDN-Cache-Control") || "",
|
|
7735
|
-
"X-Cache-Status": "HIT-304"
|
|
7736
|
-
}
|
|
7737
|
-
});
|
|
7738
|
-
}
|
|
7739
|
-
}
|
|
7740
|
-
return result;
|
|
7741
|
-
}
|
|
7742
|
-
checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag) {
|
|
7743
|
-
if (ifNoneMatch && etag) {
|
|
7744
|
-
return ifNoneMatch === etag;
|
|
7745
|
-
}
|
|
7746
|
-
if (ifModifiedSince && lastModified) {
|
|
7747
|
-
const ifModDate = new Date(ifModifiedSince);
|
|
7748
|
-
const lastModDate = new Date(lastModified);
|
|
7749
|
-
return !isNaN(ifModDate.getTime()) && !isNaN(lastModDate.getTime()) && ifModDate >= lastModDate;
|
|
7750
|
-
}
|
|
7751
|
-
return false;
|
|
7752
|
-
}
|
|
7753
|
-
createRevalidationRequest(request) {
|
|
7754
|
-
const headers = new Headers(request.headers);
|
|
7755
|
-
headers.set("X-Cache-Bypass", "revalidation");
|
|
7756
|
-
headers.delete("If-None-Match");
|
|
7757
|
-
headers.delete("If-Modified-Since");
|
|
7758
|
-
headers.delete("Cache-Control");
|
|
7759
|
-
headers.delete("Pragma");
|
|
7760
|
-
return new Request(request.url, {
|
|
7761
|
-
method: "GET",
|
|
7762
|
-
headers
|
|
7763
|
-
});
|
|
7764
|
-
}
|
|
7765
|
-
async put(request, response) {
|
|
7766
|
-
const trace = createTraceId();
|
|
7767
|
-
if (request.method !== "GET" || !response.ok) {
|
|
7768
|
-
logger.debug("[SDK Html-cache] put skipped", { trace });
|
|
7769
|
-
return;
|
|
7770
|
-
}
|
|
7771
|
-
if (!this.isCacheable(request) || !this.isResponseCacheable(response)) {
|
|
7772
|
-
logger.debug("[SDK Html-cache] put skipped, non-cacheable", {
|
|
7773
|
-
trace
|
|
7774
|
-
});
|
|
7775
|
-
return;
|
|
7776
|
-
}
|
|
7777
|
-
try {
|
|
7778
|
-
const cache = await caches.open(CACHE_NAME + this.epoch);
|
|
7779
|
-
const cacheKey = this.buildCacheKey(request);
|
|
7780
|
-
await cache.delete(cacheKey);
|
|
7781
|
-
const ttl = this.getTTLForRequest(request);
|
|
7782
|
-
const swr = this.getSWRForRequest(request);
|
|
7783
|
-
const headers = new Headers(response.headers);
|
|
7784
|
-
const existingCacheControl = response.headers.get("Cache-Control");
|
|
7785
|
-
if (!existingCacheControl || existingCacheControl === "public") {
|
|
7786
|
-
const internalMaxAge = ttl + swr;
|
|
7787
|
-
headers.set("Cache-Control", `public, max-age=${internalMaxAge}`);
|
|
7788
|
-
}
|
|
7789
|
-
const cacheTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
7790
|
-
headers.set("X-Cache-Time", cacheTime);
|
|
7791
|
-
headers.set("X-Original-TTL", ttl.toString());
|
|
7792
|
-
headers.set("X-Original-SWR", swr.toString());
|
|
7793
|
-
if (!headers.get("Last-Modified")) {
|
|
7794
|
-
headers.set("Last-Modified", new Date(cacheTime).toUTCString());
|
|
7795
|
-
}
|
|
7796
|
-
const cacheableResponse = new Response(response.body, {
|
|
7797
|
-
status: response.status,
|
|
7798
|
-
statusText: response.statusText,
|
|
7799
|
-
headers
|
|
7800
|
-
});
|
|
7801
|
-
await cache.put(cacheKey, cacheableResponse);
|
|
7802
|
-
logger.debug("[SDK Html-cache] put done", { trace });
|
|
7803
|
-
} catch (_) {
|
|
7804
|
-
logger.warn("[SDK Html-cache] no put support", { trace });
|
|
7805
|
-
}
|
|
7806
|
-
}
|
|
7807
|
-
buildClientResponse(cachedResponse, ttl, swr, isStale, age) {
|
|
7808
|
-
const headers = new Headers(cachedResponse.headers);
|
|
7809
|
-
headers.set(
|
|
7810
|
-
"Cache-Control",
|
|
7811
|
-
`public, max-age=${ttl}, stale-while-revalidate=${swr}`
|
|
7812
|
-
);
|
|
7813
|
-
headers.set(
|
|
7814
|
-
"Cloudflare-CDN-Cache-Control",
|
|
7815
|
-
`public, s-maxage=${ttl}, stale-while-revalidate=${swr}, stale-if-error=60`
|
|
7816
|
-
);
|
|
7817
|
-
const cacheTime = headers.get("X-Cache-Time");
|
|
7818
|
-
if (cacheTime) {
|
|
7819
|
-
const lastModified = new Date(cacheTime).toUTCString();
|
|
7820
|
-
headers.set("Last-Modified", lastModified);
|
|
7821
|
-
}
|
|
7822
|
-
headers.set("X-Cache-Status", isStale ? "STALE" : "HIT");
|
|
7823
|
-
headers.set("X-Cache-Age", Math.floor(age).toString());
|
|
7824
|
-
headers.delete("X-Original-TTL");
|
|
7825
|
-
headers.delete("X-Original-SWR");
|
|
7826
|
-
headers.delete("X-Cache-Time");
|
|
7827
|
-
return new Response(cachedResponse.body, {
|
|
7828
|
-
status: cachedResponse.status,
|
|
7829
|
-
statusText: cachedResponse.statusText,
|
|
7830
|
-
headers
|
|
7831
|
-
});
|
|
7832
|
-
}
|
|
7833
|
-
buildCacheKey(request) {
|
|
7834
|
-
const url = new URL(request.url);
|
|
7835
|
-
const versionHash = this.generateVersionHash(request.headers);
|
|
7836
|
-
const normalizedQuery = this.normalizeSearchParams(url.searchParams);
|
|
7837
|
-
const cacheKeyPath = `${versionHash}${url.pathname}`;
|
|
7838
|
-
const keyUrl = new URL(`${CACHE_KEY_ORIGIN}${cacheKeyPath}`);
|
|
7839
|
-
if (normalizedQuery) {
|
|
7840
|
-
keyUrl.search = `?${normalizedQuery}`;
|
|
7841
|
-
}
|
|
7842
|
-
const sanitizedHeaders = this.sanitizeHeaders(request.headers);
|
|
7843
|
-
return new Request(keyUrl.toString(), {
|
|
7844
|
-
method: "GET",
|
|
7845
|
-
headers: sanitizedHeaders
|
|
7846
|
-
});
|
|
7847
|
-
}
|
|
7848
|
-
sanitizeHeaders(originalHeaders) {
|
|
7849
|
-
const CACHE_RELEVANT_HEADERS = [
|
|
7850
|
-
// Content negotiation (affects response format)
|
|
7851
|
-
"accept",
|
|
7852
|
-
"accept-language"
|
|
7853
|
-
];
|
|
7854
|
-
const sanitized = new Headers();
|
|
7855
|
-
CACHE_RELEVANT_HEADERS.forEach((header) => {
|
|
7856
|
-
const value = originalHeaders.get(header);
|
|
7857
|
-
if (value) {
|
|
7858
|
-
sanitized.set(header, value);
|
|
7859
|
-
}
|
|
7860
|
-
});
|
|
7861
|
-
return sanitized;
|
|
7862
|
-
}
|
|
7863
|
-
generateVersionHash(headers) {
|
|
7864
|
-
const swellData = this.extractSwellData(headers);
|
|
7865
|
-
const versionFactors = {
|
|
7866
|
-
store: headers.get("swell-storefront-id") || "",
|
|
7867
|
-
auth: headers.get("swell-access-token") || "",
|
|
7868
|
-
theme: headers.get("swell-theme-version-hash") || "",
|
|
7869
|
-
modified: headers.get("swell-cache-modified") || "",
|
|
7870
|
-
currency: swellData["swell-currency"] || "USD",
|
|
7871
|
-
locale: headers.get("x-locale") || headers.get("accept-language")?.split(",")[0] || "default",
|
|
7872
|
-
context: headers.get("swell-storefront-context"),
|
|
7873
|
-
epoch: this.epoch
|
|
7874
|
-
};
|
|
7875
|
-
return md5(JSON.stringify(versionFactors));
|
|
7876
|
-
}
|
|
7877
|
-
extractSwellData(headers) {
|
|
7878
|
-
const cookie = headers.get("cookie");
|
|
7879
|
-
if (!cookie) return {};
|
|
7880
|
-
const swellDataMatch = cookie.match(/swell-data=([^;]+)/);
|
|
7881
|
-
if (!swellDataMatch) return {};
|
|
7882
|
-
try {
|
|
7883
|
-
const parsed = JSON.parse(decodeURIComponent(swellDataMatch[1]));
|
|
7884
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
7885
|
-
return parsed;
|
|
7886
|
-
}
|
|
7887
|
-
return {};
|
|
7888
|
-
} catch {
|
|
7889
|
-
return {};
|
|
7890
|
-
}
|
|
7891
|
-
}
|
|
7892
|
-
isCacheable(request) {
|
|
7893
|
-
const url = new URL(request.url);
|
|
7894
|
-
const headers = request.headers;
|
|
7895
|
-
if (headers.get("swell-deployment-mode") === "editor") {
|
|
7896
|
-
return false;
|
|
7897
|
-
}
|
|
7898
|
-
const skipPaths = ["/checkout"];
|
|
7899
|
-
if (skipPaths.some((path) => url.pathname.startsWith(path))) {
|
|
7900
|
-
return false;
|
|
7901
|
-
}
|
|
7902
|
-
if (headers.get("cache-control")?.includes("no-cache")) {
|
|
7903
|
-
return false;
|
|
7904
|
-
}
|
|
7905
|
-
return true;
|
|
7906
|
-
}
|
|
7907
|
-
isResponseCacheable(response) {
|
|
7908
|
-
const contentType = response.headers.get("content-type");
|
|
7909
|
-
if (!contentType?.includes("text/html")) {
|
|
7910
|
-
return false;
|
|
7911
|
-
}
|
|
7912
|
-
if (response.headers.get("set-cookie")) {
|
|
7913
|
-
return false;
|
|
7914
|
-
}
|
|
7915
|
-
const cacheControl = response.headers.get("cache-control");
|
|
7916
|
-
if (cacheControl?.includes("no-store") || cacheControl?.includes("private")) {
|
|
7917
|
-
return false;
|
|
7918
|
-
}
|
|
7919
|
-
return true;
|
|
7920
|
-
}
|
|
7921
|
-
getDeploymentMode(headers) {
|
|
7922
|
-
const mode = headers.get("swell-deployment-mode");
|
|
7923
|
-
if (mode === "preview" || mode === "editor") {
|
|
7924
|
-
return mode;
|
|
7925
|
-
}
|
|
7926
|
-
return "live";
|
|
7927
|
-
}
|
|
7928
|
-
getTTLForRequest(request) {
|
|
7929
|
-
const url = new URL(request.url);
|
|
7930
|
-
const path = url.pathname;
|
|
7931
|
-
const mode = this.getDeploymentMode(request.headers);
|
|
7932
|
-
if (mode === "editor") {
|
|
7933
|
-
return 0;
|
|
7934
|
-
}
|
|
7935
|
-
const config = mode === "preview" ? TTL_CONFIG.PREVIEW : TTL_CONFIG.LIVE;
|
|
7936
|
-
if (path === "/") {
|
|
7937
|
-
return config.HOME;
|
|
7938
|
-
}
|
|
7939
|
-
if (path.startsWith("/products/")) {
|
|
7940
|
-
return config.PRODUCT;
|
|
7941
|
-
}
|
|
7942
|
-
if (path.startsWith("/categories/")) {
|
|
7943
|
-
return config.COLLECTION;
|
|
7944
|
-
}
|
|
7945
|
-
if (path.startsWith("/pages/")) {
|
|
7946
|
-
return config.PAGE;
|
|
7947
|
-
}
|
|
7948
|
-
if (path.startsWith("/blogs/")) {
|
|
7949
|
-
return config.BLOG;
|
|
7950
|
-
}
|
|
7951
|
-
return config.DEFAULT;
|
|
7952
|
-
}
|
|
7953
|
-
getSWRForRequest(request) {
|
|
7954
|
-
const mode = this.getDeploymentMode(request.headers);
|
|
7955
|
-
if (mode === "editor") {
|
|
7956
|
-
return 0;
|
|
7957
|
-
}
|
|
7958
|
-
return mode === "preview" ? TTL_CONFIG.PREVIEW.SWR : TTL_CONFIG.LIVE.SWR;
|
|
7959
|
-
}
|
|
7960
|
-
getResponseAge(response) {
|
|
7961
|
-
const cacheTime = response.headers.get("X-Cache-Time");
|
|
7962
|
-
if (!cacheTime) {
|
|
7963
|
-
return Infinity;
|
|
7964
|
-
}
|
|
7965
|
-
const cacheDate = new Date(cacheTime);
|
|
7966
|
-
if (isNaN(cacheDate.getTime())) {
|
|
7967
|
-
return Infinity;
|
|
7968
|
-
}
|
|
7969
|
-
const age = (Date.now() - cacheDate.getTime()) / 1e3;
|
|
7970
|
-
return Math.max(0, age);
|
|
7971
|
-
}
|
|
7972
|
-
normalizeSearchParams(searchParams) {
|
|
7973
|
-
const ignoredParams = [
|
|
7974
|
-
"utm_source",
|
|
7975
|
-
"utm_medium",
|
|
7976
|
-
"utm_campaign",
|
|
7977
|
-
"utm_content",
|
|
7978
|
-
"utm_term",
|
|
7979
|
-
"fbclid",
|
|
7980
|
-
"gclid",
|
|
7981
|
-
"gbraid",
|
|
7982
|
-
"wbraid",
|
|
7983
|
-
"ref",
|
|
7984
|
-
"source",
|
|
7985
|
-
"mc_cid",
|
|
7986
|
-
"mc_eid"
|
|
7987
|
-
];
|
|
7988
|
-
const relevantParams = [];
|
|
7989
|
-
searchParams.forEach((value, key) => {
|
|
7990
|
-
if (!ignoredParams.includes(key)) {
|
|
7991
|
-
relevantParams.push(
|
|
7992
|
-
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
|
7993
|
-
);
|
|
7994
|
-
}
|
|
7995
|
-
});
|
|
7996
|
-
return relevantParams.sort().join("&");
|
|
7997
|
-
}
|
|
7998
|
-
};
|
|
7999
|
-
|
|
8000
7612
|
// src/resources/addresses.ts
|
|
8001
7613
|
var SwellAddresses = class extends SwellStorefrontCollection {
|
|
8002
7614
|
constructor(swell, query) {
|
|
@@ -16262,7 +15874,7 @@ function ShopifyAddress(instance, address, account) {
|
|
|
16262
15874
|
function joinAddressLines(...props) {
|
|
16263
15875
|
return props.filter(Boolean).join("\n");
|
|
16264
15876
|
}
|
|
16265
|
-
function ShopifyCountry(
|
|
15877
|
+
function ShopifyCountry(_instance2, countryCode) {
|
|
16266
15878
|
const currencyCode = getCurrencyByCountry(countryCode) || "USD";
|
|
16267
15879
|
return new ShopifyResource(
|
|
16268
15880
|
{
|
|
@@ -16595,7 +16207,7 @@ async function resolveLastOrder(instance, account) {
|
|
|
16595
16207
|
}
|
|
16596
16208
|
|
|
16597
16209
|
// src/compatibility/shopify-objects/font.ts
|
|
16598
|
-
function ShopifyFont(
|
|
16210
|
+
function ShopifyFont(_instance2, font) {
|
|
16599
16211
|
if (font instanceof ShopifyResource) {
|
|
16600
16212
|
return font.clone();
|
|
16601
16213
|
}
|
|
@@ -16606,7 +16218,7 @@ function ShopifyFont(_instance, font) {
|
|
|
16606
16218
|
family: font.family,
|
|
16607
16219
|
style: font.style,
|
|
16608
16220
|
"system?": font.system,
|
|
16609
|
-
variants: font.variants.map((variant) => ShopifyFont(
|
|
16221
|
+
variants: font.variants.map((variant) => ShopifyFont(_instance2, variant)),
|
|
16610
16222
|
weight: font.weight
|
|
16611
16223
|
});
|
|
16612
16224
|
}
|
|
@@ -16619,7 +16231,7 @@ var SHOPIFY_FORMS = {
|
|
|
16619
16231
|
})
|
|
16620
16232
|
}
|
|
16621
16233
|
};
|
|
16622
|
-
function ShopifyForm(
|
|
16234
|
+
function ShopifyForm(_instance2, form) {
|
|
16623
16235
|
if (form instanceof ShopifyResource) {
|
|
16624
16236
|
return form.clone();
|
|
16625
16237
|
}
|
|
@@ -16916,7 +16528,7 @@ function ShopifyRecommendations(instance, product) {
|
|
|
16916
16528
|
}
|
|
16917
16529
|
|
|
16918
16530
|
// src/compatibility/shopify-objects/page.ts
|
|
16919
|
-
function ShopifyPage(
|
|
16531
|
+
function ShopifyPage(_instance2, page) {
|
|
16920
16532
|
if (page instanceof ShopifyResource) {
|
|
16921
16533
|
return page.clone();
|
|
16922
16534
|
}
|
|
@@ -18128,7 +17740,7 @@ ${injects.join("\n")}</script>`;
|
|
|
18128
17740
|
};
|
|
18129
17741
|
|
|
18130
17742
|
// src/compatibility/shopify-objects/template.ts
|
|
18131
|
-
function ShopifyTemplate(
|
|
17743
|
+
function ShopifyTemplate(_instance2, template) {
|
|
18132
17744
|
return new ShopifyResource(
|
|
18133
17745
|
{
|
|
18134
17746
|
directory: template.path,
|
|
@@ -20448,7 +20060,7 @@ var ThemeLoader = class _ThemeLoader {
|
|
|
20448
20060
|
flavor,
|
|
20449
20061
|
trace
|
|
20450
20062
|
});
|
|
20451
|
-
const storage = new
|
|
20063
|
+
const storage = new ThemeFileCache(this.swell.workerEnv, flavor);
|
|
20452
20064
|
const result = await storage.putFiles(configs);
|
|
20453
20065
|
if (result.warnings.length > 0) {
|
|
20454
20066
|
logger.warn("[ThemeLoader] Theme cache updated with warnings", {
|
|
@@ -20486,7 +20098,7 @@ var ThemeLoader = class _ThemeLoader {
|
|
|
20486
20098
|
total: configMetadata.length
|
|
20487
20099
|
});
|
|
20488
20100
|
const flavor = getKVFlavor(this.swell.workerEnv);
|
|
20489
|
-
const storage = new
|
|
20101
|
+
const storage = new ThemeFileCache(this.swell.workerEnv, flavor);
|
|
20490
20102
|
const kvHydrated = await storage.getFiles(configMetadata);
|
|
20491
20103
|
const completeConfigs = await this.ensureConfigsHaveData(kvHydrated);
|
|
20492
20104
|
for (const config of completeConfigs) {
|
|
@@ -21068,12 +20680,7 @@ var SwellTheme3 = class {
|
|
|
21068
20680
|
// Default value (always StorefrontResource)
|
|
21069
20681
|
() => this.fetchCart()
|
|
21070
20682
|
),
|
|
21071
|
-
this.
|
|
21072
|
-
"account",
|
|
21073
|
-
() => this.fetchAccount(),
|
|
21074
|
-
() => null,
|
|
21075
|
-
false
|
|
21076
|
-
)
|
|
20683
|
+
this.fetchAccount()
|
|
21077
20684
|
]);
|
|
21078
20685
|
if (!cart) {
|
|
21079
20686
|
throw new Error("Failed to fetch cart");
|
|
@@ -22708,6 +22315,551 @@ function getResourceQuery(slug, query) {
|
|
|
22708
22315
|
...query
|
|
22709
22316
|
};
|
|
22710
22317
|
}
|
|
22318
|
+
|
|
22319
|
+
// src/cache/html-cache/html-cache.ts
|
|
22320
|
+
var CACHE_KEY_ORIGIN = "https://cache.swell.store";
|
|
22321
|
+
var DEFAULT_CACHE_RULES = {
|
|
22322
|
+
defaults: {
|
|
22323
|
+
live: { ttl: 20, swr: 60 * 60 * 24 * 7 },
|
|
22324
|
+
// 20s TTL, 1 week SWR
|
|
22325
|
+
preview: { ttl: 10, swr: 60 * 60 * 24 * 7 }
|
|
22326
|
+
// 10s TTL, 1 week SWR
|
|
22327
|
+
},
|
|
22328
|
+
pathRules: [
|
|
22329
|
+
{ path: "/checkout/*", skip: true }
|
|
22330
|
+
]
|
|
22331
|
+
};
|
|
22332
|
+
var HtmlCache = class {
|
|
22333
|
+
epoch;
|
|
22334
|
+
backend;
|
|
22335
|
+
cacheRules;
|
|
22336
|
+
constructor(epoch, backend, cacheRules = DEFAULT_CACHE_RULES) {
|
|
22337
|
+
this.epoch = epoch;
|
|
22338
|
+
this.backend = backend;
|
|
22339
|
+
this.cacheRules = cacheRules;
|
|
22340
|
+
}
|
|
22341
|
+
async get(request) {
|
|
22342
|
+
const trace = createTraceId();
|
|
22343
|
+
if (!this.canReadFromCache(request)) {
|
|
22344
|
+
logger.debug("[SDK Html-cache] non-cacheable request", { trace });
|
|
22345
|
+
return { found: false, cacheable: false };
|
|
22346
|
+
}
|
|
22347
|
+
try {
|
|
22348
|
+
const cacheKey = this.buildCacheKey(request);
|
|
22349
|
+
const entry = await this.backend.read(cacheKey);
|
|
22350
|
+
if (!entry) {
|
|
22351
|
+
logger.debug("[SDK Html-cache] cacheable, MISS", { trace });
|
|
22352
|
+
return { found: false, cacheable: true };
|
|
22353
|
+
}
|
|
22354
|
+
const age = this.getEntryAge(entry);
|
|
22355
|
+
const { ttl, swr } = entry;
|
|
22356
|
+
const isStale = age >= ttl;
|
|
22357
|
+
const isExpired = age >= ttl + swr;
|
|
22358
|
+
if (!isExpired) {
|
|
22359
|
+
logger.debug("[SDK Html-cache] cacheable, HIT", {
|
|
22360
|
+
stale: isStale,
|
|
22361
|
+
age,
|
|
22362
|
+
trace
|
|
22363
|
+
});
|
|
22364
|
+
const clientResponse = this.buildClientResponse(entry, isStale, age);
|
|
22365
|
+
return {
|
|
22366
|
+
found: true,
|
|
22367
|
+
stale: isStale,
|
|
22368
|
+
response: clientResponse,
|
|
22369
|
+
cacheable: true,
|
|
22370
|
+
age: Math.floor(age)
|
|
22371
|
+
};
|
|
22372
|
+
}
|
|
22373
|
+
logger.debug("[SDK Html-cache] cacheable, hit, expired", { trace });
|
|
22374
|
+
return { found: false, cacheable: true };
|
|
22375
|
+
} catch (e) {
|
|
22376
|
+
logger.warn("[SDK Html-cache] get failed", {
|
|
22377
|
+
trace,
|
|
22378
|
+
error: e instanceof Error ? e.message : String(e)
|
|
22379
|
+
});
|
|
22380
|
+
return null;
|
|
22381
|
+
}
|
|
22382
|
+
}
|
|
22383
|
+
async getWithConditionals(request) {
|
|
22384
|
+
const result = await this.get(request);
|
|
22385
|
+
if (!result?.found || result.stale) {
|
|
22386
|
+
return result;
|
|
22387
|
+
}
|
|
22388
|
+
const ifModifiedSince = request.headers.get("If-Modified-Since");
|
|
22389
|
+
const ifNoneMatch = request.headers.get("If-None-Match");
|
|
22390
|
+
if ((ifModifiedSince || ifNoneMatch) && result.response) {
|
|
22391
|
+
const lastModified = result.response.headers.get("Last-Modified");
|
|
22392
|
+
const etag = result.response.headers.get("ETag");
|
|
22393
|
+
if (this.checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag)) {
|
|
22394
|
+
result.notModified = true;
|
|
22395
|
+
result.conditional304 = new Response(null, {
|
|
22396
|
+
status: 304,
|
|
22397
|
+
headers: {
|
|
22398
|
+
"Last-Modified": lastModified || "",
|
|
22399
|
+
ETag: etag || "",
|
|
22400
|
+
"Cache-Control": result.response.headers.get("Cache-Control") || "",
|
|
22401
|
+
"Cloudflare-CDN-Cache-Control": result.response.headers.get("Cloudflare-CDN-Cache-Control") || "",
|
|
22402
|
+
"X-Cache-Status": "HIT-304"
|
|
22403
|
+
}
|
|
22404
|
+
});
|
|
22405
|
+
}
|
|
22406
|
+
}
|
|
22407
|
+
return result;
|
|
22408
|
+
}
|
|
22409
|
+
async put(request, response) {
|
|
22410
|
+
const trace = createTraceId();
|
|
22411
|
+
if (!this.canWriteToCache(request, response)) {
|
|
22412
|
+
logger.debug("[SDK Html-cache] put skipped, non-cacheable", { trace });
|
|
22413
|
+
return;
|
|
22414
|
+
}
|
|
22415
|
+
try {
|
|
22416
|
+
const cacheKey = this.buildCacheKey(request);
|
|
22417
|
+
const ttl = this.getTTLForRequest(request);
|
|
22418
|
+
const swr = this.getSWRForRequest(request);
|
|
22419
|
+
const body = await response.text();
|
|
22420
|
+
const cacheTimeISO = (/* @__PURE__ */ new Date()).toISOString();
|
|
22421
|
+
const headers = this.normalizeHeaders(response.headers);
|
|
22422
|
+
const entry = {
|
|
22423
|
+
status: response.status,
|
|
22424
|
+
statusText: response.statusText,
|
|
22425
|
+
headers,
|
|
22426
|
+
body,
|
|
22427
|
+
cacheTimeISO,
|
|
22428
|
+
ttl,
|
|
22429
|
+
swr,
|
|
22430
|
+
etag: this.quoteETag(headers["etag"] || md5(body)),
|
|
22431
|
+
lastModifiedUTC: headers["last-modified"] || new Date(cacheTimeISO).toUTCString()
|
|
22432
|
+
};
|
|
22433
|
+
const hardExpireSeconds = ttl + swr;
|
|
22434
|
+
await this.backend.write(cacheKey, entry, hardExpireSeconds);
|
|
22435
|
+
logger.debug("[SDK Html-cache] put done", { trace });
|
|
22436
|
+
} catch (e) {
|
|
22437
|
+
logger.warn("[SDK Html-cache] put failed", {
|
|
22438
|
+
trace,
|
|
22439
|
+
error: e instanceof Error ? e.message : String(e)
|
|
22440
|
+
});
|
|
22441
|
+
}
|
|
22442
|
+
}
|
|
22443
|
+
async delete(requestOrKey) {
|
|
22444
|
+
try {
|
|
22445
|
+
const key = typeof requestOrKey === "string" ? requestOrKey : this.buildCacheKey(requestOrKey);
|
|
22446
|
+
if (this.backend.delete) {
|
|
22447
|
+
await this.backend.delete(key);
|
|
22448
|
+
}
|
|
22449
|
+
} catch (e) {
|
|
22450
|
+
logger.warn("[SDK Html-cache] delete failed", {
|
|
22451
|
+
error: e instanceof Error ? e.message : String(e)
|
|
22452
|
+
});
|
|
22453
|
+
}
|
|
22454
|
+
}
|
|
22455
|
+
canReadFromCache(request) {
|
|
22456
|
+
const method = request.method.toUpperCase();
|
|
22457
|
+
return (method === "GET" || method === "HEAD") && this.isRequestCacheable(request);
|
|
22458
|
+
}
|
|
22459
|
+
canWriteToCache(request, response) {
|
|
22460
|
+
const method = request.method.toUpperCase();
|
|
22461
|
+
return method === "GET" && response.ok && this.isRequestCacheable(request) && this.isResponseCacheable(response);
|
|
22462
|
+
}
|
|
22463
|
+
createRevalidationRequest(request) {
|
|
22464
|
+
const headers = new Headers(request.headers);
|
|
22465
|
+
headers.set("X-Cache-Bypass", "revalidation");
|
|
22466
|
+
headers.delete("If-None-Match");
|
|
22467
|
+
headers.delete("If-Modified-Since");
|
|
22468
|
+
headers.delete("Cache-Control");
|
|
22469
|
+
headers.delete("Pragma");
|
|
22470
|
+
return new Request(request.url, {
|
|
22471
|
+
method: "GET",
|
|
22472
|
+
headers
|
|
22473
|
+
});
|
|
22474
|
+
}
|
|
22475
|
+
buildClientResponse(entry, isStale, age) {
|
|
22476
|
+
const headers = new Headers(entry.headers);
|
|
22477
|
+
headers.set("Cache-Control", "public, max-age=0, must-revalidate");
|
|
22478
|
+
headers.set(
|
|
22479
|
+
"Cloudflare-CDN-Cache-Control",
|
|
22480
|
+
`public, s-maxage=${entry.ttl}, stale-while-revalidate=${entry.swr}, stale-if-error=60`
|
|
22481
|
+
);
|
|
22482
|
+
if (entry.lastModifiedUTC) {
|
|
22483
|
+
headers.set("Last-Modified", entry.lastModifiedUTC);
|
|
22484
|
+
}
|
|
22485
|
+
if (entry.etag) {
|
|
22486
|
+
headers.set("ETag", entry.etag);
|
|
22487
|
+
}
|
|
22488
|
+
headers.set("X-Cache-Status", isStale ? "STALE" : "HIT");
|
|
22489
|
+
headers.set("X-Cache-Age", Math.floor(age).toString());
|
|
22490
|
+
this.sanitizeClientHeaders(headers);
|
|
22491
|
+
return new Response(entry.body, {
|
|
22492
|
+
status: entry.status,
|
|
22493
|
+
statusText: entry.statusText,
|
|
22494
|
+
headers
|
|
22495
|
+
});
|
|
22496
|
+
}
|
|
22497
|
+
buildCacheKey(request) {
|
|
22498
|
+
const url = new URL(request.url);
|
|
22499
|
+
const versionHash = this.generateVersionHash(request.headers);
|
|
22500
|
+
const normalizedQuery = this.normalizeSearchParams(url.searchParams);
|
|
22501
|
+
const cacheKeyPath = `${versionHash}${url.pathname}`;
|
|
22502
|
+
const keyUrl = new URL(`${CACHE_KEY_ORIGIN}${cacheKeyPath}`);
|
|
22503
|
+
if (normalizedQuery) {
|
|
22504
|
+
keyUrl.search = `?${normalizedQuery}`;
|
|
22505
|
+
}
|
|
22506
|
+
return keyUrl.toString();
|
|
22507
|
+
}
|
|
22508
|
+
checkNotModified(ifModifiedSince, ifNoneMatch, lastModified, etag) {
|
|
22509
|
+
if (this.ifNoneMatchMatches(ifNoneMatch, etag)) {
|
|
22510
|
+
return true;
|
|
22511
|
+
}
|
|
22512
|
+
if (ifModifiedSince && lastModified) {
|
|
22513
|
+
try {
|
|
22514
|
+
const ifModDate = new Date(ifModifiedSince);
|
|
22515
|
+
const lastModDate = new Date(lastModified);
|
|
22516
|
+
if (isNaN(ifModDate.getTime()) || isNaN(lastModDate.getTime())) {
|
|
22517
|
+
return false;
|
|
22518
|
+
}
|
|
22519
|
+
return ifModDate >= lastModDate;
|
|
22520
|
+
} catch {
|
|
22521
|
+
return false;
|
|
22522
|
+
}
|
|
22523
|
+
}
|
|
22524
|
+
return false;
|
|
22525
|
+
}
|
|
22526
|
+
ifNoneMatchMatches(ifNoneMatch, etag) {
|
|
22527
|
+
if (!ifNoneMatch || !etag) return false;
|
|
22528
|
+
const header = ifNoneMatch.trim();
|
|
22529
|
+
if (header === "*") return true;
|
|
22530
|
+
const tokens = header.split(",").map((t) => t.trim());
|
|
22531
|
+
const normalizedEtag = etag.replace(/^W\//, "");
|
|
22532
|
+
for (const token of tokens) {
|
|
22533
|
+
if (token === etag) return true;
|
|
22534
|
+
const normalizedToken = token.replace(/^W\//, "");
|
|
22535
|
+
if (normalizedToken === normalizedEtag) return true;
|
|
22536
|
+
}
|
|
22537
|
+
return false;
|
|
22538
|
+
}
|
|
22539
|
+
quoteETag(value) {
|
|
22540
|
+
if (!value) return value;
|
|
22541
|
+
if (value.startsWith('"') || value.startsWith('W/"')) return value;
|
|
22542
|
+
if (value.startsWith("W/")) return `W/"${value.slice(2)}"`;
|
|
22543
|
+
return `"${value}"`;
|
|
22544
|
+
}
|
|
22545
|
+
sanitizeClientHeaders(headers) {
|
|
22546
|
+
const HOP_BY_HOP = [
|
|
22547
|
+
"connection",
|
|
22548
|
+
"proxy-connection",
|
|
22549
|
+
"keep-alive",
|
|
22550
|
+
"transfer-encoding",
|
|
22551
|
+
"upgrade",
|
|
22552
|
+
"proxy-authenticate",
|
|
22553
|
+
"proxy-authorization",
|
|
22554
|
+
"te",
|
|
22555
|
+
"trailers",
|
|
22556
|
+
"via",
|
|
22557
|
+
"alt-svc",
|
|
22558
|
+
"content-length"
|
|
22559
|
+
];
|
|
22560
|
+
for (const h of HOP_BY_HOP) headers.delete(h);
|
|
22561
|
+
headers.delete("content-encoding");
|
|
22562
|
+
headers.delete("x-original-ttl");
|
|
22563
|
+
headers.delete("x-original-swr");
|
|
22564
|
+
headers.delete("x-cache-time");
|
|
22565
|
+
}
|
|
22566
|
+
generateVersionHash(headers) {
|
|
22567
|
+
const swellData = this.extractSwellData(headers);
|
|
22568
|
+
const acceptLang = headers.get("accept-language") || "";
|
|
22569
|
+
const accept = headers.get("accept") || "";
|
|
22570
|
+
const versionFactors = {
|
|
22571
|
+
store: headers.get("swell-storefront-id") || "",
|
|
22572
|
+
app: (headers.get("swell-app-id") || "") + "@" + (swellData["swell-app-version"] || ""),
|
|
22573
|
+
auth: headers.get("swell-access-token") || "",
|
|
22574
|
+
theme: headers.get("swell-theme-version-hash") || "",
|
|
22575
|
+
modified: headers.get("swell-cache-modified") || "",
|
|
22576
|
+
currency: swellData["swell-currency"] || "USD",
|
|
22577
|
+
locale: headers.get("x-locale") || acceptLang.split(",")[0].trim().toLowerCase() || "default",
|
|
22578
|
+
context: headers.get("swell-storefront-context"),
|
|
22579
|
+
accept,
|
|
22580
|
+
epoch: this.epoch
|
|
22581
|
+
};
|
|
22582
|
+
return md5(JSON.stringify(versionFactors));
|
|
22583
|
+
}
|
|
22584
|
+
extractSwellData(headers) {
|
|
22585
|
+
const cookie = headers.get("cookie");
|
|
22586
|
+
if (!cookie) return {};
|
|
22587
|
+
const swellDataMatch = cookie.match(/swell-data=([^;]+)/);
|
|
22588
|
+
if (!swellDataMatch) return {};
|
|
22589
|
+
try {
|
|
22590
|
+
return JSON.parse(decodeURIComponent(swellDataMatch[1])) || {};
|
|
22591
|
+
} catch {
|
|
22592
|
+
return {};
|
|
22593
|
+
}
|
|
22594
|
+
}
|
|
22595
|
+
isRequestCacheable(request) {
|
|
22596
|
+
const url = new URL(request.url);
|
|
22597
|
+
if (request.headers.get("swell-deployment-mode") === "editor") return false;
|
|
22598
|
+
if (this.cacheRules.pathRules) {
|
|
22599
|
+
for (const rule of this.cacheRules.pathRules) {
|
|
22600
|
+
if (this.pathMatches(rule.path, url.pathname) && rule.skip) {
|
|
22601
|
+
return false;
|
|
22602
|
+
}
|
|
22603
|
+
}
|
|
22604
|
+
}
|
|
22605
|
+
if (request.headers.get("cache-control")?.includes("no-cache"))
|
|
22606
|
+
return false;
|
|
22607
|
+
return true;
|
|
22608
|
+
}
|
|
22609
|
+
isResponseCacheable(response) {
|
|
22610
|
+
if (!response.headers.get("content-type")?.includes("text/html"))
|
|
22611
|
+
return false;
|
|
22612
|
+
if (response.headers.get("set-cookie")) return false;
|
|
22613
|
+
const cacheControl = response.headers.get("cache-control");
|
|
22614
|
+
if (cacheControl?.includes("no-store") || cacheControl?.includes("private"))
|
|
22615
|
+
return false;
|
|
22616
|
+
return true;
|
|
22617
|
+
}
|
|
22618
|
+
getDeploymentMode(headers) {
|
|
22619
|
+
const mode = headers.get("swell-deployment-mode");
|
|
22620
|
+
return mode === "preview" ? "preview" : "live";
|
|
22621
|
+
}
|
|
22622
|
+
getTTLForRequest(request) {
|
|
22623
|
+
const url = new URL(request.url);
|
|
22624
|
+
const mode = this.getDeploymentMode(request.headers);
|
|
22625
|
+
if (this.cacheRules.pathRules) {
|
|
22626
|
+
for (const rule of this.cacheRules.pathRules) {
|
|
22627
|
+
if (this.pathMatches(rule.path, url.pathname) && rule.ttl !== void 0) {
|
|
22628
|
+
return rule.ttl;
|
|
22629
|
+
}
|
|
22630
|
+
}
|
|
22631
|
+
}
|
|
22632
|
+
const defaults = this.cacheRules.defaults?.[mode];
|
|
22633
|
+
return defaults?.ttl ?? DEFAULT_CACHE_RULES.defaults[mode].ttl;
|
|
22634
|
+
}
|
|
22635
|
+
getSWRForRequest(request) {
|
|
22636
|
+
const url = new URL(request.url);
|
|
22637
|
+
const mode = this.getDeploymentMode(request.headers);
|
|
22638
|
+
if (this.cacheRules.pathRules) {
|
|
22639
|
+
for (const rule of this.cacheRules.pathRules) {
|
|
22640
|
+
if (this.pathMatches(rule.path, url.pathname) && rule.swr !== void 0) {
|
|
22641
|
+
return rule.swr;
|
|
22642
|
+
}
|
|
22643
|
+
}
|
|
22644
|
+
}
|
|
22645
|
+
const defaults = this.cacheRules.defaults?.[mode];
|
|
22646
|
+
return defaults?.swr ?? DEFAULT_CACHE_RULES.defaults[mode].swr;
|
|
22647
|
+
}
|
|
22648
|
+
getEntryAge(entry) {
|
|
22649
|
+
const t = Date.parse(entry.cacheTimeISO);
|
|
22650
|
+
if (Number.isNaN(t)) return Infinity;
|
|
22651
|
+
const age = (Date.now() - t) / 1e3;
|
|
22652
|
+
return age < 0 ? 0 : age;
|
|
22653
|
+
}
|
|
22654
|
+
/**
|
|
22655
|
+
* Converts wildcard pattern to regex and tests against path.
|
|
22656
|
+
* - * matches any characters except /
|
|
22657
|
+
* - ** matches any characters including /
|
|
22658
|
+
*/
|
|
22659
|
+
pathMatches(pattern, path) {
|
|
22660
|
+
const regex = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "___DOUBLE_STAR___").replace(/\*/g, "[^/]*").replace(/___DOUBLE_STAR___/g, ".*");
|
|
22661
|
+
return new RegExp(`^${regex}$`).test(path);
|
|
22662
|
+
}
|
|
22663
|
+
normalizeHeaders(headers) {
|
|
22664
|
+
const normalized = {};
|
|
22665
|
+
headers.forEach((value, key) => {
|
|
22666
|
+
normalized[key.toLowerCase()] = value;
|
|
22667
|
+
});
|
|
22668
|
+
return normalized;
|
|
22669
|
+
}
|
|
22670
|
+
normalizeSearchParams(searchParams) {
|
|
22671
|
+
const ignoredParams = [
|
|
22672
|
+
"utm_source",
|
|
22673
|
+
"utm_medium",
|
|
22674
|
+
"utm_campaign",
|
|
22675
|
+
"utm_content",
|
|
22676
|
+
"utm_term",
|
|
22677
|
+
"fbclid",
|
|
22678
|
+
"gclid",
|
|
22679
|
+
"gbraid",
|
|
22680
|
+
"wbraid",
|
|
22681
|
+
"ref",
|
|
22682
|
+
"source",
|
|
22683
|
+
"mc_cid",
|
|
22684
|
+
"mc_eid"
|
|
22685
|
+
];
|
|
22686
|
+
const relevantParams = [];
|
|
22687
|
+
searchParams.forEach((value, key) => {
|
|
22688
|
+
if (!ignoredParams.includes(key)) {
|
|
22689
|
+
relevantParams.push(
|
|
22690
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
|
22691
|
+
);
|
|
22692
|
+
}
|
|
22693
|
+
});
|
|
22694
|
+
return relevantParams.sort().join("&");
|
|
22695
|
+
}
|
|
22696
|
+
};
|
|
22697
|
+
|
|
22698
|
+
// src/cache/html-cache/html-cache-kv.ts
|
|
22699
|
+
var KVCacheBackend = class {
|
|
22700
|
+
kv;
|
|
22701
|
+
prefix;
|
|
22702
|
+
hashKeys;
|
|
22703
|
+
maxValueBytes;
|
|
22704
|
+
constructor(kv, opts = {}) {
|
|
22705
|
+
this.kv = kv;
|
|
22706
|
+
this.prefix = opts.prefix;
|
|
22707
|
+
this.hashKeys = opts.hashKeys !== false;
|
|
22708
|
+
this.maxValueBytes = opts.maxValueBytes ?? Math.floor(24.5 * 1024 * 1024);
|
|
22709
|
+
}
|
|
22710
|
+
async read(key) {
|
|
22711
|
+
const kvKey = this.makeKey(key);
|
|
22712
|
+
const value = await this.kv.get(kvKey, "json");
|
|
22713
|
+
if (!value) return null;
|
|
22714
|
+
const entry = {
|
|
22715
|
+
status: value.status,
|
|
22716
|
+
statusText: value.statusText,
|
|
22717
|
+
headers: value.headers,
|
|
22718
|
+
body: value.body,
|
|
22719
|
+
cacheTimeISO: value.cacheTimeISO,
|
|
22720
|
+
ttl: value.ttl,
|
|
22721
|
+
swr: value.swr,
|
|
22722
|
+
etag: value.etag,
|
|
22723
|
+
lastModifiedUTC: value.lastModifiedUTC
|
|
22724
|
+
};
|
|
22725
|
+
return entry;
|
|
22726
|
+
}
|
|
22727
|
+
async write(key, entry, hardExpireSeconds) {
|
|
22728
|
+
const kvKey = this.makeKey(key);
|
|
22729
|
+
const payload = {
|
|
22730
|
+
v: 1,
|
|
22731
|
+
status: entry.status,
|
|
22732
|
+
statusText: entry.statusText,
|
|
22733
|
+
headers: entry.headers,
|
|
22734
|
+
body: entry.body,
|
|
22735
|
+
cacheTimeISO: entry.cacheTimeISO,
|
|
22736
|
+
ttl: entry.ttl,
|
|
22737
|
+
swr: entry.swr,
|
|
22738
|
+
etag: entry.etag,
|
|
22739
|
+
lastModifiedUTC: entry.lastModifiedUTC
|
|
22740
|
+
};
|
|
22741
|
+
const json = JSON.stringify(payload);
|
|
22742
|
+
this.assertSize(json);
|
|
22743
|
+
const metadata = {
|
|
22744
|
+
v: 1,
|
|
22745
|
+
cacheTimeISO: entry.cacheTimeISO,
|
|
22746
|
+
ttl: entry.ttl,
|
|
22747
|
+
swr: entry.swr,
|
|
22748
|
+
etag: entry.etag,
|
|
22749
|
+
lastModifiedUTC: entry.lastModifiedUTC
|
|
22750
|
+
};
|
|
22751
|
+
await this.kv.put(kvKey, json, {
|
|
22752
|
+
expirationTtl: hardExpireSeconds + 60,
|
|
22753
|
+
// natural hard expiry after SWR + grace period
|
|
22754
|
+
metadata
|
|
22755
|
+
});
|
|
22756
|
+
}
|
|
22757
|
+
async delete(key) {
|
|
22758
|
+
const kvKey = this.makeKey(key);
|
|
22759
|
+
await this.kv.delete(kvKey);
|
|
22760
|
+
}
|
|
22761
|
+
// ---- private helpers ----
|
|
22762
|
+
makeKey(raw) {
|
|
22763
|
+
const core = this.hashKeys ? md5(raw) : raw;
|
|
22764
|
+
return this.prefix ? `${this.prefix}:${core}` : core;
|
|
22765
|
+
}
|
|
22766
|
+
assertSize(json) {
|
|
22767
|
+
const bytes = typeof TextEncoder !== "undefined" ? new TextEncoder().encode(json).length : this.approxUtf8Bytes(json);
|
|
22768
|
+
if (bytes > this.maxValueBytes) {
|
|
22769
|
+
throw new Error(
|
|
22770
|
+
`KV value too large: ${bytes} bytes exceeds limit ${this.maxValueBytes} bytes`
|
|
22771
|
+
);
|
|
22772
|
+
}
|
|
22773
|
+
}
|
|
22774
|
+
approxUtf8Bytes(str) {
|
|
22775
|
+
let count = 0;
|
|
22776
|
+
for (let i = 0; i < str.length; i++) {
|
|
22777
|
+
const code = str.charCodeAt(i);
|
|
22778
|
+
if (code <= 127) count += 1;
|
|
22779
|
+
else if (code <= 2047) count += 2;
|
|
22780
|
+
else count += 3;
|
|
22781
|
+
}
|
|
22782
|
+
return count;
|
|
22783
|
+
}
|
|
22784
|
+
};
|
|
22785
|
+
|
|
22786
|
+
// src/cache/html-cache/html-cache-worker.ts
|
|
22787
|
+
var CACHE_NAME_PREFIX = "swell-html-v0";
|
|
22788
|
+
var WorkerCacheBackend = class {
|
|
22789
|
+
cacheName;
|
|
22790
|
+
constructor(epoch) {
|
|
22791
|
+
this.cacheName = CACHE_NAME_PREFIX + epoch;
|
|
22792
|
+
}
|
|
22793
|
+
async read(key) {
|
|
22794
|
+
const cache = await caches.open(this.cacheName);
|
|
22795
|
+
const request = new Request(key);
|
|
22796
|
+
const response = await cache.match(request);
|
|
22797
|
+
if (!response) return null;
|
|
22798
|
+
const headers = {};
|
|
22799
|
+
response.headers.forEach((value, name) => {
|
|
22800
|
+
headers[name.toLowerCase()] = value;
|
|
22801
|
+
});
|
|
22802
|
+
return {
|
|
22803
|
+
status: response.status,
|
|
22804
|
+
statusText: response.statusText,
|
|
22805
|
+
headers,
|
|
22806
|
+
body: await response.text(),
|
|
22807
|
+
cacheTimeISO: response.headers.get("x-cache-time") || (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
22808
|
+
ttl: parseInt(response.headers.get("x-original-ttl") || "0", 10),
|
|
22809
|
+
swr: parseInt(response.headers.get("x-original-swr") || "0", 10),
|
|
22810
|
+
etag: response.headers.get("etag") || void 0,
|
|
22811
|
+
lastModifiedUTC: response.headers.get("last-modified") || void 0
|
|
22812
|
+
};
|
|
22813
|
+
}
|
|
22814
|
+
async write(key, entry, _hardExpireSeconds) {
|
|
22815
|
+
const cache = await caches.open(this.cacheName);
|
|
22816
|
+
const request = new Request(key);
|
|
22817
|
+
const headers = new Headers(entry.headers);
|
|
22818
|
+
if (entry.lastModifiedUTC && !headers.get("Last-Modified")) {
|
|
22819
|
+
headers.set("Last-Modified", entry.lastModifiedUTC);
|
|
22820
|
+
}
|
|
22821
|
+
if (entry.etag && !headers.get("ETag")) {
|
|
22822
|
+
headers.set("ETag", entry.etag);
|
|
22823
|
+
}
|
|
22824
|
+
headers.set("X-Cache-Time", entry.cacheTimeISO);
|
|
22825
|
+
headers.set("X-Original-TTL", String(entry.ttl));
|
|
22826
|
+
headers.set("X-Original-SWR", String(entry.swr));
|
|
22827
|
+
headers.delete("content-encoding");
|
|
22828
|
+
headers.delete("content-length");
|
|
22829
|
+
const existing = headers.get("Cache-Control");
|
|
22830
|
+
if (!existing || existing.trim().toLowerCase() === "public") {
|
|
22831
|
+
headers.set("Cache-Control", `public, max-age=${entry.ttl + entry.swr}`);
|
|
22832
|
+
}
|
|
22833
|
+
const response = new Response(entry.body, {
|
|
22834
|
+
status: entry.status,
|
|
22835
|
+
statusText: entry.statusText,
|
|
22836
|
+
headers
|
|
22837
|
+
});
|
|
22838
|
+
await cache.delete(request);
|
|
22839
|
+
await cache.put(request, response);
|
|
22840
|
+
}
|
|
22841
|
+
async delete(key) {
|
|
22842
|
+
const cache = await caches.open(this.cacheName);
|
|
22843
|
+
const request = new Request(key);
|
|
22844
|
+
await cache.delete(request);
|
|
22845
|
+
}
|
|
22846
|
+
};
|
|
22847
|
+
|
|
22848
|
+
// src/cache/html-cache/html-cache-factory.ts
|
|
22849
|
+
var _instance = null;
|
|
22850
|
+
function getHtmlCache(env, cacheRules) {
|
|
22851
|
+
const epoch = env?.HTML_CACHE_EPOCH;
|
|
22852
|
+
if (typeof epoch !== "string" || !epoch) return null;
|
|
22853
|
+
if (_instance) return _instance;
|
|
22854
|
+
const kv = env?.NAMESPACE;
|
|
22855
|
+
const rules = cacheRules || env?.HTML_CACHE_RULES;
|
|
22856
|
+
if (env?.HTML_CACHE_BACKEND !== "worker" && kv) {
|
|
22857
|
+
_instance = new HtmlCache(epoch, new KVCacheBackend(kv), rules);
|
|
22858
|
+
return _instance;
|
|
22859
|
+
}
|
|
22860
|
+
_instance = new HtmlCache(epoch, new WorkerCacheBackend(epoch), rules);
|
|
22861
|
+
return _instance;
|
|
22862
|
+
}
|
|
22711
22863
|
export {
|
|
22712
22864
|
AccountAddressesResource,
|
|
22713
22865
|
AccountOrderResource,
|
|
@@ -22721,10 +22873,12 @@ export {
|
|
|
22721
22873
|
CartResource,
|
|
22722
22874
|
CategoriesResource,
|
|
22723
22875
|
CategoryResource,
|
|
22876
|
+
DEFAULT_CACHE_RULES,
|
|
22724
22877
|
DEFAULT_QUERY_PAGE_LIMIT,
|
|
22725
22878
|
DeferredShopifyResource,
|
|
22726
22879
|
FILE_DATA_INCLUDE_QUERY,
|
|
22727
22880
|
GEO_DATA,
|
|
22881
|
+
HtmlCache,
|
|
22728
22882
|
LANG_TO_COUNTRY_CODES,
|
|
22729
22883
|
LiquidSwell30 as LiquidSwell,
|
|
22730
22884
|
MAX_QUERY_PAGE_LIMIT,
|
|
@@ -22772,7 +22926,6 @@ export {
|
|
|
22772
22926
|
ThemeForm,
|
|
22773
22927
|
ThemeFormErrors,
|
|
22774
22928
|
VariantResource,
|
|
22775
|
-
WorkerHtmlCache,
|
|
22776
22929
|
adaptShopifyFontData,
|
|
22777
22930
|
adaptShopifyFormData,
|
|
22778
22931
|
adaptShopifyMenuData,
|
|
@@ -22800,6 +22953,7 @@ export {
|
|
|
22800
22953
|
getEasyblocksComponentDefinitions,
|
|
22801
22954
|
getEasyblocksPagePropsWithConfigs,
|
|
22802
22955
|
getEasyblocksPageTemplate,
|
|
22956
|
+
getHtmlCache,
|
|
22803
22957
|
getKVFlavor,
|
|
22804
22958
|
getLayoutSectionGroups,
|
|
22805
22959
|
getMenuItemStorefrontUrl,
|