@swell/apps-sdk 1.0.149 → 1.0.151
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 +1101 -446
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +1079 -446
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +1090 -446
- package/dist/index.mjs.map +4 -4
- package/dist/src/cache/constants.d.ts +7 -0
- package/dist/src/cache/index.d.ts +4 -0
- package/dist/src/cache/kv-variety.d.ts +10 -0
- package/dist/src/cache/theme-file-storage.d.ts +88 -0
- package/dist/src/cache/worker-cache-proxy.d.ts +31 -0
- package/dist/src/compatibility/drops/all_products.d.ts +1 -1
- package/dist/src/compatibility/drops/articles.d.ts +1 -1
- package/dist/src/compatibility/drops/blogs.d.ts +1 -1
- package/dist/src/compatibility/drops/collections.d.ts +2 -3
- package/dist/src/compatibility/drops/images.d.ts +1 -1
- package/dist/src/compatibility/drops/pages.d.ts +2 -3
- package/dist/src/compatibility/shopify-objects/collections.d.ts +2 -2
- package/dist/src/content.d.ts +3 -3
- package/dist/src/liquid/filters/shopify/default_pagination.d.ts +1 -1
- package/dist/src/resources/account.d.ts +4 -4
- package/dist/src/resources/addresses.d.ts +7 -0
- package/dist/src/resources/blog.d.ts +5 -4
- package/dist/src/resources/blog_category.d.ts +5 -4
- package/dist/src/resources/cart.d.ts +4 -4
- package/dist/src/resources/categories.d.ts +7 -0
- package/dist/src/resources/category.d.ts +5 -4
- package/dist/src/resources/index.d.ts +18 -9
- package/dist/src/resources/order.d.ts +5 -4
- package/dist/src/resources/orders.d.ts +7 -0
- package/dist/src/resources/page.d.ts +5 -4
- package/dist/src/resources/predictive_search.d.ts +6 -0
- package/dist/src/resources/product.d.ts +5 -19
- package/dist/src/resources/product_helpers.d.ts +12 -2
- package/dist/src/resources/product_recommendations.d.ts +6 -0
- package/dist/src/resources/search.d.ts +6 -0
- package/dist/src/resources/subscription.d.ts +7 -0
- package/dist/src/resources/subscriptions.d.ts +7 -0
- package/dist/src/resources/swell_types.d.ts +66 -9
- package/dist/src/resources/variant.d.ts +8 -8
- package/dist/src/resources.d.ts +11 -12
- package/dist/src/theme/theme-loader.d.ts +36 -44
- package/dist/src/theme.d.ts +22 -7
- package/dist/src/utils/index.d.ts +1 -0
- package/dist/src/utils/kv-flavor.d.ts +7 -0
- package/dist/types/cloudflare.d.ts +14 -3
- package/dist/types/shopify.d.ts +2 -2
- package/dist/types/swell.d.ts +3 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -626,18 +626,12 @@ var StorefrontResource = class {
|
|
|
626
626
|
}
|
|
627
627
|
return instance[prop];
|
|
628
628
|
}
|
|
629
|
-
// add additional properties to the loaded result
|
|
630
|
-
_transformResult(result) {
|
|
631
|
-
return result;
|
|
632
|
-
}
|
|
633
629
|
async _get(..._args) {
|
|
634
630
|
if (this._getter) {
|
|
635
631
|
const getter = this._getter.bind(
|
|
636
632
|
this
|
|
637
633
|
);
|
|
638
634
|
return Promise.resolve().then(getter).then((result) => {
|
|
639
|
-
return this._transformResult(result);
|
|
640
|
-
}).then((result) => {
|
|
641
635
|
this._result = result ?? null;
|
|
642
636
|
if (result) {
|
|
643
637
|
Object.assign(this, result);
|
|
@@ -720,7 +714,6 @@ function cloneStorefrontResource(input) {
|
|
|
720
714
|
});
|
|
721
715
|
const clone = new ClonedClass(input._getter);
|
|
722
716
|
clone._params = input._params;
|
|
723
|
-
clone._transformResult = input._transformResult.bind(clone);
|
|
724
717
|
Object.defineProperty(clone, "_resourceName", {
|
|
725
718
|
value: resourceName
|
|
726
719
|
});
|
|
@@ -774,7 +767,11 @@ var SwellStorefrontCollection = class _SwellStorefrontCollection extends SwellSt
|
|
|
774
767
|
limit = DEFAULT_QUERY_PAGE_LIMIT;
|
|
775
768
|
name;
|
|
776
769
|
constructor(swell, collection, query = {}, getter) {
|
|
777
|
-
super(
|
|
770
|
+
super(
|
|
771
|
+
swell,
|
|
772
|
+
collection,
|
|
773
|
+
getter
|
|
774
|
+
);
|
|
778
775
|
this._query = this._initQuery(query);
|
|
779
776
|
if (!getter) {
|
|
780
777
|
this._setGetter(this._defaultGetter());
|
|
@@ -950,8 +947,6 @@ var SwellStorefrontRecord = class extends SwellStorefrontResource {
|
|
|
950
947
|
getter,
|
|
951
948
|
isResourceCacheble(this._collection)
|
|
952
949
|
).then((result) => {
|
|
953
|
-
return this._transformResult(result);
|
|
954
|
-
}).then((result) => {
|
|
955
950
|
this._result = result;
|
|
956
951
|
if (result) {
|
|
957
952
|
Object.assign(this, result);
|
|
@@ -6697,6 +6692,50 @@ function md5(inputString) {
|
|
|
6697
6692
|
return rh(a) + rh(b) + rh(c) + rh(d);
|
|
6698
6693
|
}
|
|
6699
6694
|
|
|
6695
|
+
// src/utils/kv-flavor.ts
|
|
6696
|
+
var cachedKVFlavor;
|
|
6697
|
+
function getKVFlavor(workerEnv) {
|
|
6698
|
+
if (cachedKVFlavor) {
|
|
6699
|
+
return cachedKVFlavor;
|
|
6700
|
+
}
|
|
6701
|
+
if (!workerEnv?.THEME) {
|
|
6702
|
+
cachedKVFlavor = "memory";
|
|
6703
|
+
return cachedKVFlavor;
|
|
6704
|
+
}
|
|
6705
|
+
const kvFlavor = workerEnv.KV_FLAVOR;
|
|
6706
|
+
if (kvFlavor) {
|
|
6707
|
+
const flavorLower = String(kvFlavor).toLowerCase();
|
|
6708
|
+
switch (flavorLower) {
|
|
6709
|
+
case "cloudflare":
|
|
6710
|
+
case "cf":
|
|
6711
|
+
cachedKVFlavor = "cf";
|
|
6712
|
+
break;
|
|
6713
|
+
case "miniflare":
|
|
6714
|
+
cachedKVFlavor = "miniflare";
|
|
6715
|
+
break;
|
|
6716
|
+
case "memory":
|
|
6717
|
+
cachedKVFlavor = "memory";
|
|
6718
|
+
break;
|
|
6719
|
+
default:
|
|
6720
|
+
logger.warn(`[KV] Unknown KV_FLAVOR: ${kvFlavor}, using miniflare`);
|
|
6721
|
+
cachedKVFlavor = "miniflare";
|
|
6722
|
+
}
|
|
6723
|
+
return cachedKVFlavor;
|
|
6724
|
+
}
|
|
6725
|
+
try {
|
|
6726
|
+
if (typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers") {
|
|
6727
|
+
cachedKVFlavor = "cf";
|
|
6728
|
+
return cachedKVFlavor;
|
|
6729
|
+
}
|
|
6730
|
+
} catch {
|
|
6731
|
+
}
|
|
6732
|
+
cachedKVFlavor = "miniflare";
|
|
6733
|
+
return cachedKVFlavor;
|
|
6734
|
+
}
|
|
6735
|
+
function resetKVFlavorCache() {
|
|
6736
|
+
cachedKVFlavor = void 0;
|
|
6737
|
+
}
|
|
6738
|
+
|
|
6700
6739
|
// src/utils/index.ts
|
|
6701
6740
|
function isSectionConfig(config, themeConfigs) {
|
|
6702
6741
|
if (!config.file_path.startsWith("theme/sections/")) {
|
|
@@ -7226,68 +7265,584 @@ function buildStores2() {
|
|
|
7226
7265
|
|
|
7227
7266
|
// src/cache/theme-cache.ts
|
|
7228
7267
|
var TTL = 90 * 24 * 60 * 60 * 1e3;
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7268
|
+
|
|
7269
|
+
// src/cache/theme-file-storage.ts
|
|
7270
|
+
import bluebird2 from "bluebird";
|
|
7271
|
+
|
|
7272
|
+
// src/cache/kv-variety.ts
|
|
7273
|
+
import bluebird from "bluebird";
|
|
7274
|
+
var { Promise: Promise2 } = bluebird;
|
|
7275
|
+
var CFKV = class {
|
|
7276
|
+
constructor(kv) {
|
|
7277
|
+
this.kv = kv;
|
|
7278
|
+
}
|
|
7279
|
+
async get(keys) {
|
|
7280
|
+
if (keys.length === 0) {
|
|
7281
|
+
return /* @__PURE__ */ new Map();
|
|
7282
|
+
}
|
|
7283
|
+
const result = await this.kv.get(keys, "text");
|
|
7284
|
+
if (!(result instanceof Map)) {
|
|
7285
|
+
const map = /* @__PURE__ */ new Map();
|
|
7286
|
+
for (const key of keys) {
|
|
7287
|
+
map.set(key, null);
|
|
7288
|
+
}
|
|
7289
|
+
return map;
|
|
7290
|
+
}
|
|
7291
|
+
return result;
|
|
7292
|
+
}
|
|
7293
|
+
async put(key, value, metadata) {
|
|
7294
|
+
await this.kv.put(key, value, { metadata });
|
|
7295
|
+
}
|
|
7296
|
+
};
|
|
7297
|
+
var MiniflareKV = class {
|
|
7298
|
+
constructor(kv) {
|
|
7299
|
+
this.kv = kv;
|
|
7300
|
+
}
|
|
7301
|
+
async get(keys) {
|
|
7302
|
+
if (keys.length === 0) {
|
|
7303
|
+
return /* @__PURE__ */ new Map();
|
|
7304
|
+
}
|
|
7305
|
+
const result = /* @__PURE__ */ new Map();
|
|
7306
|
+
await Promise2.map(
|
|
7307
|
+
keys,
|
|
7308
|
+
async (key) => {
|
|
7309
|
+
const value = await this.kv.get(key, "text");
|
|
7310
|
+
result.set(key, value);
|
|
7311
|
+
},
|
|
7312
|
+
{ concurrency: 50 }
|
|
7313
|
+
);
|
|
7314
|
+
return result;
|
|
7315
|
+
}
|
|
7316
|
+
async put(key, value, metadata) {
|
|
7317
|
+
await this.kv.put(key, value, { metadata });
|
|
7318
|
+
}
|
|
7319
|
+
};
|
|
7320
|
+
var MemoryKV = class {
|
|
7321
|
+
store = /* @__PURE__ */ new Map();
|
|
7322
|
+
async get(keys) {
|
|
7323
|
+
const result = /* @__PURE__ */ new Map();
|
|
7324
|
+
for (const key of keys) {
|
|
7325
|
+
const entry = this.store.get(key);
|
|
7326
|
+
result.set(key, entry?.value ?? null);
|
|
7327
|
+
}
|
|
7328
|
+
return result;
|
|
7329
|
+
}
|
|
7330
|
+
async put(key, value, metadata) {
|
|
7331
|
+
this.store.set(key, { value, metadata });
|
|
7332
|
+
}
|
|
7333
|
+
};
|
|
7334
|
+
function createClientKV(env, flavor = "cf") {
|
|
7335
|
+
if (env?.THEME) {
|
|
7336
|
+
if (flavor === "miniflare") {
|
|
7337
|
+
return new MiniflareKV(env.THEME);
|
|
7338
|
+
}
|
|
7339
|
+
return new CFKV(env.THEME);
|
|
7340
|
+
}
|
|
7341
|
+
return new MemoryKV();
|
|
7342
|
+
}
|
|
7343
|
+
|
|
7344
|
+
// src/cache/theme-file-storage.ts
|
|
7345
|
+
var { Promise: Promise3 } = bluebird2;
|
|
7346
|
+
var ThemeFileStorage = class {
|
|
7347
|
+
kv;
|
|
7348
|
+
maxConcurrency;
|
|
7349
|
+
maxBatchSize = 20 * 1024 * 1024;
|
|
7350
|
+
// 20MB safety margin
|
|
7351
|
+
constructor(env, flavor = "cf") {
|
|
7352
|
+
this.kv = createClientKV(env, flavor);
|
|
7353
|
+
this.maxConcurrency = flavor === "miniflare" ? 50 : 6;
|
|
7354
|
+
}
|
|
7355
|
+
/**
|
|
7356
|
+
* Build a KV storage key from a file hash
|
|
7357
|
+
*/
|
|
7358
|
+
buildKey(hash) {
|
|
7359
|
+
return `file_data:${hash}`;
|
|
7360
|
+
}
|
|
7361
|
+
/**
|
|
7362
|
+
* Extract hash from a KV storage key
|
|
7363
|
+
*/
|
|
7364
|
+
extractHashFromKey(key) {
|
|
7365
|
+
return key.replace("file_data:", "");
|
|
7366
|
+
}
|
|
7367
|
+
/**
|
|
7368
|
+
* Plan GET batches based on file sizes to avoid 413 errors
|
|
7369
|
+
* Uses round-robin distribution for even batch sizes
|
|
7370
|
+
*/
|
|
7371
|
+
planGetBatches(configs) {
|
|
7372
|
+
if (configs.length === 0) {
|
|
7373
|
+
return [];
|
|
7374
|
+
}
|
|
7375
|
+
const sorted = [...configs].sort((a, b) => {
|
|
7376
|
+
const sizeA = a.file?.length || 0;
|
|
7377
|
+
const sizeB = b.file?.length || 0;
|
|
7378
|
+
return sizeB - sizeA;
|
|
7379
|
+
});
|
|
7380
|
+
const totalSize = sorted.reduce((sum, config) => {
|
|
7381
|
+
return sum + (config.file?.length || 0);
|
|
7382
|
+
}, 0);
|
|
7383
|
+
const sizeBatches = Math.ceil(totalSize / this.maxBatchSize);
|
|
7384
|
+
const keyBatches = Math.ceil(sorted.length / 100);
|
|
7385
|
+
const targetBatches = Math.max(sizeBatches, keyBatches);
|
|
7386
|
+
const batches = Array.from(
|
|
7387
|
+
{ length: targetBatches },
|
|
7388
|
+
() => ({
|
|
7389
|
+
configs: [],
|
|
7390
|
+
keys: [],
|
|
7391
|
+
estimatedSize: 0
|
|
7392
|
+
})
|
|
7393
|
+
);
|
|
7394
|
+
sorted.forEach((config, index) => {
|
|
7395
|
+
const batchIndex = index % targetBatches;
|
|
7396
|
+
const batch = batches[batchIndex];
|
|
7397
|
+
batch.configs.push(config);
|
|
7398
|
+
batch.keys.push(this.buildKey(config.hash));
|
|
7399
|
+
batch.estimatedSize += config.file?.length || 0;
|
|
7400
|
+
});
|
|
7401
|
+
return batches.filter((batch) => batch.configs.length > 0);
|
|
7402
|
+
}
|
|
7403
|
+
/**
|
|
7404
|
+
* Load a single batch from KV storage
|
|
7405
|
+
*/
|
|
7406
|
+
async loadBatch(batch) {
|
|
7407
|
+
return this.kv.get(batch.keys);
|
|
7408
|
+
}
|
|
7409
|
+
/**
|
|
7410
|
+
* Merge batch results with original configs
|
|
7411
|
+
*/
|
|
7412
|
+
mergeResults(configs, batchResults) {
|
|
7413
|
+
const allData = /* @__PURE__ */ new Map();
|
|
7414
|
+
for (const batchResult of batchResults) {
|
|
7415
|
+
for (const [key, value] of batchResult.entries()) {
|
|
7416
|
+
allData.set(key, value);
|
|
7417
|
+
}
|
|
7418
|
+
}
|
|
7419
|
+
return configs.map((config) => {
|
|
7420
|
+
const key = this.buildKey(config.hash);
|
|
7421
|
+
const fileData = allData.get(key);
|
|
7422
|
+
if (fileData) {
|
|
7423
|
+
return {
|
|
7424
|
+
...config,
|
|
7425
|
+
file_data: fileData
|
|
7426
|
+
};
|
|
7427
|
+
}
|
|
7428
|
+
return config;
|
|
7429
|
+
});
|
|
7430
|
+
}
|
|
7431
|
+
async getFiles(configs) {
|
|
7432
|
+
if (configs.length === 0) {
|
|
7433
|
+
return [];
|
|
7434
|
+
}
|
|
7435
|
+
const trace = createTraceId();
|
|
7436
|
+
const batches = this.planGetBatches(configs);
|
|
7437
|
+
const totalSize = batches.reduce((sum, b) => sum + b.estimatedSize, 0);
|
|
7438
|
+
const maxBatchSize = Math.max(...batches.map((b) => b.estimatedSize));
|
|
7439
|
+
logger.debug("[ThemeFileStorage] Loading files start", {
|
|
7440
|
+
totalConfigs: configs.length,
|
|
7441
|
+
batchCount: batches.length,
|
|
7442
|
+
maxBatchSize,
|
|
7443
|
+
totalSize,
|
|
7444
|
+
trace
|
|
7445
|
+
});
|
|
7446
|
+
const results = await Promise3.map(
|
|
7447
|
+
batches,
|
|
7448
|
+
(batch) => this.loadBatch(batch),
|
|
7449
|
+
{ concurrency: Math.min(this.maxConcurrency, batches.length) }
|
|
7450
|
+
);
|
|
7451
|
+
const mergedConfigs = this.mergeResults(configs, results);
|
|
7452
|
+
const loadedCount = mergedConfigs.filter((c) => c.file_data).length;
|
|
7453
|
+
logger.debug("[ThemeFileStorage] Loading files end", {
|
|
7454
|
+
requested: configs.length,
|
|
7455
|
+
loaded: loadedCount,
|
|
7456
|
+
missing: configs.length - loadedCount,
|
|
7457
|
+
batches: batches.length,
|
|
7458
|
+
trace
|
|
7459
|
+
});
|
|
7460
|
+
return mergedConfigs;
|
|
7461
|
+
}
|
|
7462
|
+
/**
|
|
7463
|
+
* Validate file sizes and categorize by threshold
|
|
7464
|
+
*/
|
|
7465
|
+
validateFiles(configs) {
|
|
7466
|
+
const valid = [];
|
|
7467
|
+
const warnings = [];
|
|
7468
|
+
for (const config of configs) {
|
|
7469
|
+
if (!config.file_data) {
|
|
7470
|
+
continue;
|
|
7471
|
+
}
|
|
7472
|
+
const size = config.file?.length || 0;
|
|
7473
|
+
if (size >= 25 * 1024 * 1024) {
|
|
7474
|
+
warnings.push({
|
|
7475
|
+
hash: config.hash,
|
|
7476
|
+
filePath: config.file_path,
|
|
7477
|
+
size,
|
|
7478
|
+
reason: "exceeded_25mb",
|
|
7479
|
+
action: "rejected"
|
|
7480
|
+
});
|
|
7481
|
+
} else if (size >= 5 * 1024 * 1024) {
|
|
7482
|
+
warnings.push({
|
|
7483
|
+
hash: config.hash,
|
|
7484
|
+
filePath: config.file_path,
|
|
7485
|
+
size,
|
|
7486
|
+
reason: "rejected_5mb",
|
|
7487
|
+
action: "rejected"
|
|
7488
|
+
});
|
|
7489
|
+
} else {
|
|
7490
|
+
if (size >= 1024 * 1024) {
|
|
7491
|
+
warnings.push({
|
|
7492
|
+
hash: config.hash,
|
|
7493
|
+
filePath: config.file_path,
|
|
7494
|
+
size,
|
|
7495
|
+
reason: "warning_1mb",
|
|
7496
|
+
action: "stored"
|
|
7497
|
+
});
|
|
7498
|
+
}
|
|
7499
|
+
valid.push(config);
|
|
7500
|
+
}
|
|
7501
|
+
}
|
|
7502
|
+
return { valid, warnings };
|
|
7503
|
+
}
|
|
7504
|
+
/**
|
|
7505
|
+
* Check which files already exist in KV storage
|
|
7506
|
+
* Uses batch planning to avoid 413 errors when checking existence
|
|
7507
|
+
*/
|
|
7508
|
+
async checkExistence(configs) {
|
|
7509
|
+
if (configs.length === 0) {
|
|
7510
|
+
return /* @__PURE__ */ new Set();
|
|
7511
|
+
}
|
|
7512
|
+
const existing = /* @__PURE__ */ new Set();
|
|
7513
|
+
const batches = this.planGetBatches(configs);
|
|
7514
|
+
const results = await Promise3.map(
|
|
7515
|
+
batches,
|
|
7516
|
+
(batch) => this.kv.get(batch.keys),
|
|
7517
|
+
{ concurrency: this.maxConcurrency }
|
|
7518
|
+
);
|
|
7519
|
+
for (const batchResult of results) {
|
|
7520
|
+
for (const [key, value] of batchResult.entries()) {
|
|
7521
|
+
if (value !== null) {
|
|
7522
|
+
const hash = this.extractHashFromKey(key);
|
|
7523
|
+
existing.add(hash);
|
|
7524
|
+
}
|
|
7525
|
+
}
|
|
7526
|
+
}
|
|
7527
|
+
return existing;
|
|
7528
|
+
}
|
|
7529
|
+
async putFiles(configs) {
|
|
7530
|
+
const result = {
|
|
7531
|
+
written: 0,
|
|
7532
|
+
skipped: 0,
|
|
7533
|
+
skippedExisting: 0,
|
|
7534
|
+
warnings: []
|
|
7535
|
+
};
|
|
7536
|
+
if (configs.length === 0) {
|
|
7537
|
+
return result;
|
|
7538
|
+
}
|
|
7539
|
+
const trace = createTraceId();
|
|
7540
|
+
logger.debug("[ThemeFileStorage] Put files start", {
|
|
7541
|
+
totalConfigs: configs.length,
|
|
7542
|
+
trace
|
|
7543
|
+
});
|
|
7544
|
+
const { valid, warnings } = this.validateFiles(configs);
|
|
7545
|
+
result.warnings = warnings;
|
|
7546
|
+
if (warnings.length > 0) {
|
|
7547
|
+
const rejectedCount = warnings.filter(
|
|
7548
|
+
(w) => w.action === "rejected"
|
|
7549
|
+
).length;
|
|
7550
|
+
const warnedCount = warnings.filter((w) => w.action === "stored").length;
|
|
7551
|
+
logger.warn("[ThemeFileStorage] File size validation issues", {
|
|
7552
|
+
totalWarnings: warnings.length,
|
|
7553
|
+
rejected: rejectedCount,
|
|
7554
|
+
warned: warnedCount,
|
|
7555
|
+
trace
|
|
7556
|
+
});
|
|
7557
|
+
warnings.filter((w) => w.action === "rejected").forEach((w) => {
|
|
7558
|
+
logger.error("[ThemeFileStorage] File rejected due to size", {
|
|
7559
|
+
filePath: w.filePath,
|
|
7560
|
+
size: w.size,
|
|
7561
|
+
reason: w.reason,
|
|
7562
|
+
trace
|
|
7563
|
+
});
|
|
7564
|
+
});
|
|
7565
|
+
}
|
|
7566
|
+
const rejected = warnings.filter((w) => w.action === "rejected").length;
|
|
7567
|
+
result.skipped = rejected + (configs.length - configs.filter((c) => c.file_data).length);
|
|
7568
|
+
logger.debug("[ThemeFileStorage] Checking existence", {
|
|
7569
|
+
validFiles: valid.length,
|
|
7570
|
+
trace
|
|
7571
|
+
});
|
|
7572
|
+
const existing = await this.checkExistence(valid);
|
|
7573
|
+
result.skippedExisting = existing.size;
|
|
7574
|
+
const toWrite = valid.filter((config) => !existing.has(config.hash));
|
|
7575
|
+
if (toWrite.length > 0) {
|
|
7576
|
+
logger.debug("[ThemeFileStorage] Writing new files", {
|
|
7577
|
+
toWrite: toWrite.length,
|
|
7578
|
+
skippedExisting: existing.size,
|
|
7579
|
+
trace
|
|
7580
|
+
});
|
|
7581
|
+
await Promise3.map(
|
|
7582
|
+
toWrite,
|
|
7583
|
+
async (config) => {
|
|
7584
|
+
const key = this.buildKey(config.hash);
|
|
7585
|
+
const metadata = config.file?.content_type ? { content_type: config.file.content_type } : void 0;
|
|
7586
|
+
await this.kv.put(key, config.file_data, metadata);
|
|
7587
|
+
result.written++;
|
|
7588
|
+
},
|
|
7589
|
+
{ concurrency: this.maxConcurrency }
|
|
7590
|
+
);
|
|
7591
|
+
}
|
|
7592
|
+
logger.info("[ThemeFileStorage] Put files complete", {
|
|
7593
|
+
written: result.written,
|
|
7594
|
+
skipped: result.skipped,
|
|
7595
|
+
skippedExisting: result.skippedExisting,
|
|
7596
|
+
warnings: result.warnings.length,
|
|
7597
|
+
trace
|
|
7234
7598
|
});
|
|
7599
|
+
return result;
|
|
7600
|
+
}
|
|
7601
|
+
};
|
|
7602
|
+
|
|
7603
|
+
// src/cache/constants.ts
|
|
7604
|
+
var SECOND = 1e3;
|
|
7605
|
+
var MINUTE = 60 * SECOND;
|
|
7606
|
+
var HOUR = 60 * MINUTE;
|
|
7607
|
+
var DAY = 24 * HOUR;
|
|
7608
|
+
var YEAR = 365 * DAY;
|
|
7609
|
+
var MAX_TTL = YEAR;
|
|
7610
|
+
var SHORT_TTL = 5 * SECOND;
|
|
7611
|
+
|
|
7612
|
+
// src/cache/worker-cache-proxy.ts
|
|
7613
|
+
var CACHE_NAME = "swell-cache-v1";
|
|
7614
|
+
var CACHE_KEY_ORIGIN = "https://cache.swell.store";
|
|
7615
|
+
var WorkerCacheProxy = class {
|
|
7616
|
+
swell;
|
|
7617
|
+
constructor(swell) {
|
|
7618
|
+
this.swell = swell;
|
|
7619
|
+
}
|
|
7620
|
+
/**
|
|
7621
|
+
* Reads a JSON value from Worker Cache using a key built from path+query.
|
|
7622
|
+
* Returns null on miss or if running outside of a Worker environment.
|
|
7623
|
+
*/
|
|
7624
|
+
async get(path, query, opts) {
|
|
7625
|
+
if (typeof caches === "undefined") {
|
|
7626
|
+
return null;
|
|
7627
|
+
}
|
|
7628
|
+
const { keyUrl } = await this.buildKeyUrl(path, query, opts?.version);
|
|
7629
|
+
try {
|
|
7630
|
+
const cache = await caches.open(CACHE_NAME);
|
|
7631
|
+
const match = await cache.match(keyUrl);
|
|
7632
|
+
if (!match) return null;
|
|
7633
|
+
const data = await match.json();
|
|
7634
|
+
return data;
|
|
7635
|
+
} catch {
|
|
7636
|
+
return null;
|
|
7637
|
+
}
|
|
7638
|
+
}
|
|
7639
|
+
/**
|
|
7640
|
+
* Stores a JSON value in Worker Cache under key built from path+query.
|
|
7641
|
+
* No-ops outside of a Worker environment.
|
|
7642
|
+
*/
|
|
7643
|
+
async put(path, query, value, opts) {
|
|
7644
|
+
if (typeof caches === "undefined") {
|
|
7645
|
+
return;
|
|
7646
|
+
}
|
|
7647
|
+
const { keyUrl, hasVersion } = await this.buildKeyUrl(
|
|
7648
|
+
path,
|
|
7649
|
+
query,
|
|
7650
|
+
opts?.version
|
|
7651
|
+
);
|
|
7652
|
+
const ttlMs = hasVersion ? MAX_TTL : SHORT_TTL;
|
|
7653
|
+
try {
|
|
7654
|
+
const cache = await caches.open(CACHE_NAME);
|
|
7655
|
+
const response = new Response(JSON.stringify(value), {
|
|
7656
|
+
headers: {
|
|
7657
|
+
"Content-Type": "application/json",
|
|
7658
|
+
"Cache-Control": `public, max-age=${Math.floor(ttlMs / 1e3)}`
|
|
7659
|
+
}
|
|
7660
|
+
});
|
|
7661
|
+
await cache.put(keyUrl, response);
|
|
7662
|
+
} catch {
|
|
7663
|
+
}
|
|
7664
|
+
}
|
|
7665
|
+
/**
|
|
7666
|
+
* Builds a deterministic key URL for Worker Cache from the backend API URL
|
|
7667
|
+
* composed using path and query. Includes tenant and auth isolation and an
|
|
7668
|
+
* optional version segment.
|
|
7669
|
+
*/
|
|
7670
|
+
async buildKeyUrl(path, query, explicitVersion) {
|
|
7671
|
+
const apiHost = this.swell.backend?.apiHost;
|
|
7672
|
+
const endpointPath = String(path).startsWith("/") ? String(path).substring(1) : String(path);
|
|
7673
|
+
let queryString = "";
|
|
7674
|
+
if (query && this.swell.backend) {
|
|
7675
|
+
queryString = this.swell.backend.stringifyQuery(query);
|
|
7676
|
+
}
|
|
7677
|
+
const fullUrl = `${apiHost}/${endpointPath}${queryString ? `?${queryString}` : ""}`;
|
|
7678
|
+
const instanceId = this.swell.instanceId || "";
|
|
7679
|
+
const authKey = String(this.swell.swellHeaders?.["swell-auth-key"] || "");
|
|
7680
|
+
const tenantHash = await this.sha256Hex(`${instanceId}|${authKey}`);
|
|
7681
|
+
const version = explicitVersion !== void 0 ? explicitVersion : this.swell.swellHeaders?.["theme-version-hash"] || null;
|
|
7682
|
+
const hasVersion = Boolean(version);
|
|
7683
|
+
const versionHash = hasVersion ? await this.sha256Hex(String(version)) : null;
|
|
7684
|
+
const urlHash = await this.sha256Hex(fullUrl);
|
|
7685
|
+
const keyUrl = versionHash ? `${CACHE_KEY_ORIGIN}/v1/${tenantHash}/${versionHash}/${urlHash}` : `${CACHE_KEY_ORIGIN}/v1/${tenantHash}/${urlHash}`;
|
|
7686
|
+
return { keyUrl, hasVersion };
|
|
7687
|
+
}
|
|
7688
|
+
/**
|
|
7689
|
+
* SHA-256 digest with hex encoding. Requires Worker crypto; callers
|
|
7690
|
+
* should avoid invoking this outside of Worker code paths.
|
|
7691
|
+
*/
|
|
7692
|
+
async sha256Hex(input) {
|
|
7693
|
+
if (typeof crypto !== "undefined" && crypto.subtle && crypto.subtle.digest) {
|
|
7694
|
+
const encoder = new TextEncoder();
|
|
7695
|
+
const digest = await crypto.subtle.digest(
|
|
7696
|
+
"SHA-256",
|
|
7697
|
+
encoder.encode(input)
|
|
7698
|
+
);
|
|
7699
|
+
const bytes = new Uint8Array(digest);
|
|
7700
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
7701
|
+
}
|
|
7702
|
+
return md5(input);
|
|
7703
|
+
}
|
|
7704
|
+
};
|
|
7705
|
+
|
|
7706
|
+
// src/resources/addresses.ts
|
|
7707
|
+
var SwellAddresses = class extends SwellStorefrontCollection {
|
|
7708
|
+
constructor(swell, query) {
|
|
7709
|
+
const { page, limit: limit2 } = swell.queryParams;
|
|
7710
|
+
super(swell, "accounts:addresses", { page, limit: limit2, ...query }, function() {
|
|
7711
|
+
return this._swell.storefront.account.listAddresses(
|
|
7712
|
+
this._query
|
|
7713
|
+
);
|
|
7714
|
+
});
|
|
7715
|
+
}
|
|
7716
|
+
};
|
|
7717
|
+
|
|
7718
|
+
// src/resources/orders.ts
|
|
7719
|
+
var SwellOrders = class extends SwellStorefrontCollection {
|
|
7720
|
+
constructor(swell, query) {
|
|
7721
|
+
const { page, limit: limit2 } = swell.queryParams;
|
|
7722
|
+
super(swell, "accounts:orders", { page, limit: limit2, ...query }, function() {
|
|
7723
|
+
return this._swell.storefront.account.listOrders(this._query);
|
|
7724
|
+
});
|
|
7725
|
+
}
|
|
7726
|
+
};
|
|
7727
|
+
|
|
7728
|
+
// src/resources/subscriptions.ts
|
|
7729
|
+
var SwellSubscriptions = class extends SwellStorefrontCollection {
|
|
7730
|
+
constructor(swell, query) {
|
|
7731
|
+
const { page, limit: limit2 } = swell.queryParams;
|
|
7732
|
+
super(
|
|
7733
|
+
swell,
|
|
7734
|
+
"accounts:subscriptions",
|
|
7735
|
+
{ page, limit: limit2, ...query },
|
|
7736
|
+
function() {
|
|
7737
|
+
return this._swell.storefront.subscriptions.list(
|
|
7738
|
+
this._query
|
|
7739
|
+
);
|
|
7740
|
+
}
|
|
7741
|
+
);
|
|
7235
7742
|
}
|
|
7236
7743
|
};
|
|
7237
7744
|
|
|
7238
7745
|
// src/resources/account.ts
|
|
7239
7746
|
var SwellAccount = class extends SwellStorefrontSingleton {
|
|
7240
|
-
constructor(swell
|
|
7241
|
-
super(swell, "account",
|
|
7747
|
+
constructor(swell) {
|
|
7748
|
+
super(swell, "account", async function() {
|
|
7749
|
+
const account = await this._defaultGetter().call(this);
|
|
7750
|
+
if (!account) {
|
|
7751
|
+
return null;
|
|
7752
|
+
}
|
|
7753
|
+
account.addresses = new SwellAddresses(
|
|
7754
|
+
this._swell
|
|
7755
|
+
);
|
|
7756
|
+
account.orders = new SwellOrders(
|
|
7757
|
+
this._swell
|
|
7758
|
+
);
|
|
7759
|
+
account.subscriptions = new SwellSubscriptions(
|
|
7760
|
+
this._swell
|
|
7761
|
+
);
|
|
7762
|
+
return account;
|
|
7763
|
+
});
|
|
7242
7764
|
return this._getProxy();
|
|
7243
7765
|
}
|
|
7244
7766
|
};
|
|
7245
7767
|
|
|
7246
7768
|
// src/resources/blog_category.ts
|
|
7247
7769
|
var SwellBlogCategory = class extends SwellStorefrontRecord {
|
|
7248
|
-
constructor(swell, id, query
|
|
7249
|
-
super(swell, "content/blog-categories", id, query,
|
|
7770
|
+
constructor(swell, id, query) {
|
|
7771
|
+
super(swell, "content/blog-categories", id, query, async function() {
|
|
7772
|
+
const category = await this._defaultGetter().call(this);
|
|
7773
|
+
if (!category) {
|
|
7774
|
+
return null;
|
|
7775
|
+
}
|
|
7776
|
+
category.blogs = new SwellStorefrontCollection(
|
|
7777
|
+
this._swell,
|
|
7778
|
+
"content/blogs",
|
|
7779
|
+
{
|
|
7780
|
+
category_id: category.id,
|
|
7781
|
+
expand: "author"
|
|
7782
|
+
}
|
|
7783
|
+
);
|
|
7784
|
+
return category;
|
|
7785
|
+
});
|
|
7250
7786
|
return this._getProxy();
|
|
7251
7787
|
}
|
|
7252
7788
|
};
|
|
7253
7789
|
|
|
7254
7790
|
// src/resources/blog.ts
|
|
7255
7791
|
var SwellBlog = class extends SwellStorefrontRecord {
|
|
7256
|
-
constructor(swell,
|
|
7257
|
-
super(swell, "content/blogs",
|
|
7792
|
+
constructor(swell, blogId, categoryId, query) {
|
|
7793
|
+
super(swell, "content/blogs", blogId, query, async function() {
|
|
7794
|
+
this._query = { ...this._query, expand: "author" };
|
|
7795
|
+
const blog = await this._defaultGetter().call(this);
|
|
7796
|
+
if (!blog) {
|
|
7797
|
+
return null;
|
|
7798
|
+
}
|
|
7799
|
+
if (categoryId) {
|
|
7800
|
+
blog.category = new SwellStorefrontRecord(
|
|
7801
|
+
this._swell,
|
|
7802
|
+
"content/blog-categories",
|
|
7803
|
+
categoryId
|
|
7804
|
+
);
|
|
7805
|
+
}
|
|
7806
|
+
return blog;
|
|
7807
|
+
});
|
|
7258
7808
|
return this._getProxy();
|
|
7259
7809
|
}
|
|
7260
7810
|
};
|
|
7261
7811
|
|
|
7262
7812
|
// src/resources/cart.ts
|
|
7263
7813
|
var SwellCart = class extends SwellStorefrontSingleton {
|
|
7264
|
-
constructor(swell
|
|
7265
|
-
super(swell, "cart"
|
|
7266
|
-
return this._getProxy();
|
|
7267
|
-
}
|
|
7268
|
-
};
|
|
7269
|
-
|
|
7270
|
-
// src/resources/category.ts
|
|
7271
|
-
var SwellCategory = class extends SwellStorefrontRecord {
|
|
7272
|
-
constructor(swell, id, query = {}, getter) {
|
|
7273
|
-
super(swell, "categories", id, query, getter);
|
|
7274
|
-
return this._getProxy();
|
|
7275
|
-
}
|
|
7276
|
-
};
|
|
7277
|
-
|
|
7278
|
-
// src/resources/order.ts
|
|
7279
|
-
var SwellOrder = class extends SwellStorefrontRecord {
|
|
7280
|
-
constructor(swell, id, query = {}, getter) {
|
|
7281
|
-
super(swell, "orders", id, query, getter);
|
|
7814
|
+
constructor(swell) {
|
|
7815
|
+
super(swell, "cart");
|
|
7282
7816
|
return this._getProxy();
|
|
7283
7817
|
}
|
|
7284
7818
|
};
|
|
7285
7819
|
|
|
7286
|
-
// src/resources/
|
|
7287
|
-
var
|
|
7288
|
-
constructor(swell,
|
|
7289
|
-
super(
|
|
7290
|
-
|
|
7820
|
+
// src/resources/categories.ts
|
|
7821
|
+
var SwellCategories = class extends SwellStorefrontCollection {
|
|
7822
|
+
constructor(swell, query) {
|
|
7823
|
+
super(
|
|
7824
|
+
swell,
|
|
7825
|
+
"categories",
|
|
7826
|
+
{
|
|
7827
|
+
limit: 100,
|
|
7828
|
+
top_id: null,
|
|
7829
|
+
...query
|
|
7830
|
+
},
|
|
7831
|
+
async function() {
|
|
7832
|
+
const categories = await this._defaultGetter().call(this);
|
|
7833
|
+
if (!categories) {
|
|
7834
|
+
return null;
|
|
7835
|
+
}
|
|
7836
|
+
for (const category of categories.results) {
|
|
7837
|
+
category.products = new SwellStorefrontCollection(
|
|
7838
|
+
this._swell,
|
|
7839
|
+
"products",
|
|
7840
|
+
{ category: category.id }
|
|
7841
|
+
);
|
|
7842
|
+
}
|
|
7843
|
+
return categories;
|
|
7844
|
+
}
|
|
7845
|
+
);
|
|
7291
7846
|
}
|
|
7292
7847
|
};
|
|
7293
7848
|
|
|
@@ -7438,15 +7993,152 @@ function getSelectedSubscriptionPurchaseOptionPlan(selectedPurchaseOptionType, s
|
|
|
7438
7993
|
if (selectedPurchaseOptionType !== "subscription") {
|
|
7439
7994
|
return null;
|
|
7440
7995
|
}
|
|
7441
|
-
const { purchase_option: purchaseOption } = queryParams;
|
|
7442
|
-
let selectedPlan = null;
|
|
7443
|
-
if (purchaseOption?.plan_id) {
|
|
7444
|
-
selectedPlan = subscriptionPurchaseOption.plans.find(
|
|
7445
|
-
(plan) => plan.id === purchaseOption.plan_id
|
|
7446
|
-
);
|
|
7996
|
+
const { purchase_option: purchaseOption } = queryParams;
|
|
7997
|
+
let selectedPlan = null;
|
|
7998
|
+
if (purchaseOption?.plan_id) {
|
|
7999
|
+
selectedPlan = subscriptionPurchaseOption.plans.find(
|
|
8000
|
+
(plan) => plan.id === purchaseOption.plan_id
|
|
8001
|
+
);
|
|
8002
|
+
}
|
|
8003
|
+
return selectedPlan || subscriptionPurchaseOption.plans[0];
|
|
8004
|
+
}
|
|
8005
|
+
var SORT_OPTIONS = Object.freeze([
|
|
8006
|
+
{ value: "", name: "Featured" },
|
|
8007
|
+
{ value: "popularity", name: "Popularity", query: "popularity desc" },
|
|
8008
|
+
{ value: "price_asc", name: "Price, low to high", query: "price asc" },
|
|
8009
|
+
{ value: "price_desc", name: "Price, high to low", query: "price desc" },
|
|
8010
|
+
{ value: "date_asc", name: "Date, old to new", query: "date asc" },
|
|
8011
|
+
{ value: "date_desc", name: "Date, new to old", query: "date desc" },
|
|
8012
|
+
{ value: "name_asc", name: "Product name, A-Z", query: "name asc" },
|
|
8013
|
+
{ value: "name_desc", name: "Product name, Z-A", query: "name desc" }
|
|
8014
|
+
]);
|
|
8015
|
+
async function getProductFilters(swell, productQuery) {
|
|
8016
|
+
const sortBy = swell.queryParams.sort || "";
|
|
8017
|
+
const filterQuery = productQueryWithFilters(swell, productQuery);
|
|
8018
|
+
return {
|
|
8019
|
+
filter_options: await getProductFiltersByQuery(swell, filterQuery),
|
|
8020
|
+
sort: SORT_OPTIONS.find((option) => option.value === sortBy)?.value,
|
|
8021
|
+
sort_options: [...SORT_OPTIONS]
|
|
8022
|
+
};
|
|
8023
|
+
}
|
|
8024
|
+
async function getProductFiltersByQuery(swell, query = {}) {
|
|
8025
|
+
const filters2 = await swell.get("/products/:filters", {
|
|
8026
|
+
...query,
|
|
8027
|
+
sort: void 0
|
|
8028
|
+
}) || [];
|
|
8029
|
+
if (!Array.isArray(filters2)) {
|
|
8030
|
+
throw new Error("Product filters must be an array");
|
|
8031
|
+
}
|
|
8032
|
+
for (const filter of filters2) {
|
|
8033
|
+
filter.param_name = `filter_${filter.id}`;
|
|
8034
|
+
if (Array.isArray(filter.options)) {
|
|
8035
|
+
filter.active_options = [];
|
|
8036
|
+
filter.inactive_options = [];
|
|
8037
|
+
for (const option of filter.options) {
|
|
8038
|
+
const queryValue = swell.queryParams[filter.param_name];
|
|
8039
|
+
option.active = Array.isArray(queryValue) ? queryValue.includes(option.value) : queryValue === option.value;
|
|
8040
|
+
const list = option.active ? filter.active_options : filter.inactive_options;
|
|
8041
|
+
list.push(option);
|
|
8042
|
+
}
|
|
8043
|
+
}
|
|
8044
|
+
}
|
|
8045
|
+
return filters2;
|
|
8046
|
+
}
|
|
8047
|
+
function productQueryWithFilters(swell, query) {
|
|
8048
|
+
const filters2 = Object.keys(swell.queryParams).reduce(
|
|
8049
|
+
(acc, key) => {
|
|
8050
|
+
if (key.startsWith("filter_")) {
|
|
8051
|
+
const qkey = key.replace("filter_", "");
|
|
8052
|
+
const value = swell.queryParams[key];
|
|
8053
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && (value.gte !== void 0 || value.lte !== void 0)) {
|
|
8054
|
+
acc[qkey] = [value.gte || 0, value.lte || void 0];
|
|
8055
|
+
} else {
|
|
8056
|
+
acc[qkey] = value;
|
|
8057
|
+
}
|
|
8058
|
+
}
|
|
8059
|
+
return acc;
|
|
8060
|
+
},
|
|
8061
|
+
{}
|
|
8062
|
+
);
|
|
8063
|
+
const sortBy = swell.queryParams.sort || "";
|
|
8064
|
+
return {
|
|
8065
|
+
sort: SORT_OPTIONS.find((option) => option.value === sortBy)?.query || void 0,
|
|
8066
|
+
$filters: filters2,
|
|
8067
|
+
...query
|
|
8068
|
+
};
|
|
8069
|
+
}
|
|
8070
|
+
|
|
8071
|
+
// src/resources/category.ts
|
|
8072
|
+
var SwellCategory = class extends SwellStorefrontRecord {
|
|
8073
|
+
constructor(swell, id, query) {
|
|
8074
|
+
super(swell, "categories", id, query, async function() {
|
|
8075
|
+
let category = await this._defaultGetter().call(this);
|
|
8076
|
+
if (!category && this._id === "all") {
|
|
8077
|
+
category = {
|
|
8078
|
+
name: "Products",
|
|
8079
|
+
id: "all",
|
|
8080
|
+
slug: "all",
|
|
8081
|
+
filter_options: [],
|
|
8082
|
+
sort_options: []
|
|
8083
|
+
};
|
|
8084
|
+
}
|
|
8085
|
+
if (!category) {
|
|
8086
|
+
return null;
|
|
8087
|
+
}
|
|
8088
|
+
const productFilters = await getProductFilters(
|
|
8089
|
+
this._swell,
|
|
8090
|
+
category.id !== "all" ? { category: category.id, $variants: true } : { $variants: true }
|
|
8091
|
+
);
|
|
8092
|
+
Object.assign(category, productFilters);
|
|
8093
|
+
return category;
|
|
8094
|
+
});
|
|
8095
|
+
return this._getProxy();
|
|
8096
|
+
}
|
|
8097
|
+
};
|
|
8098
|
+
|
|
8099
|
+
// src/resources/order.ts
|
|
8100
|
+
var SwellOrder = class extends SwellStorefrontRecord {
|
|
8101
|
+
constructor(swell, id, query) {
|
|
8102
|
+
super(swell, "accounts:orders", id, query, function() {
|
|
8103
|
+
return this._swell.storefront.account.getOrder(this._id);
|
|
8104
|
+
});
|
|
8105
|
+
return this._getProxy();
|
|
8106
|
+
}
|
|
8107
|
+
};
|
|
8108
|
+
|
|
8109
|
+
// src/resources/page.ts
|
|
8110
|
+
var SwellPage = class extends SwellStorefrontRecord {
|
|
8111
|
+
constructor(swell, id, query) {
|
|
8112
|
+
super(swell, "content/pages", id, query);
|
|
8113
|
+
return this._getProxy();
|
|
8114
|
+
}
|
|
8115
|
+
};
|
|
8116
|
+
|
|
8117
|
+
// src/resources/predictive_search.ts
|
|
8118
|
+
var SwellPredictiveSearch = class extends StorefrontResource {
|
|
8119
|
+
constructor(swell, query) {
|
|
8120
|
+
super(async function() {
|
|
8121
|
+
const performed = String(query || "").length > 0;
|
|
8122
|
+
let products;
|
|
8123
|
+
if (performed) {
|
|
8124
|
+
products = new SwellStorefrontCollection(
|
|
8125
|
+
swell,
|
|
8126
|
+
"products",
|
|
8127
|
+
{
|
|
8128
|
+
search: query,
|
|
8129
|
+
limit: 10
|
|
8130
|
+
}
|
|
8131
|
+
);
|
|
8132
|
+
await products.resolve();
|
|
8133
|
+
}
|
|
8134
|
+
return {
|
|
8135
|
+
query,
|
|
8136
|
+
performed,
|
|
8137
|
+
products
|
|
8138
|
+
};
|
|
8139
|
+
});
|
|
7447
8140
|
}
|
|
7448
|
-
|
|
7449
|
-
}
|
|
8141
|
+
};
|
|
7450
8142
|
|
|
7451
8143
|
// src/resources/variant.ts
|
|
7452
8144
|
function transformSwellVariant(params, product, variant) {
|
|
@@ -7466,27 +8158,30 @@ function transformSwellVariant(params, product, variant) {
|
|
|
7466
8158
|
)
|
|
7467
8159
|
};
|
|
7468
8160
|
}
|
|
8161
|
+
var SwellVariant = class extends SwellStorefrontRecord {
|
|
8162
|
+
product;
|
|
8163
|
+
constructor(swell, product, id, query) {
|
|
8164
|
+
super(swell, "products:variants", id, query, async function() {
|
|
8165
|
+
const variant = await this._swell.get(
|
|
8166
|
+
"/products:variants/{id}",
|
|
8167
|
+
{ id: this._id }
|
|
8168
|
+
);
|
|
8169
|
+
return variant ?? null;
|
|
8170
|
+
});
|
|
8171
|
+
this.product = product;
|
|
8172
|
+
}
|
|
8173
|
+
};
|
|
7469
8174
|
|
|
7470
8175
|
// src/resources/product.ts
|
|
7471
|
-
var SORT_OPTIONS = [
|
|
7472
|
-
{ value: "", name: "Featured" },
|
|
7473
|
-
{ value: "popularity", name: "Popularity", query: "popularity desc" },
|
|
7474
|
-
{ value: "price_asc", name: "Price, low to high", query: "price asc" },
|
|
7475
|
-
{ value: "price_desc", name: "Price, high to low", query: "price desc" },
|
|
7476
|
-
{ value: "date_asc", name: "Date, old to new", query: "date asc" },
|
|
7477
|
-
{ value: "date_desc", name: "Date, new to old", query: "date desc" },
|
|
7478
|
-
{ value: "name_asc", name: "Product name, A-Z", query: "name asc" },
|
|
7479
|
-
{ value: "name_desc", name: "Product name, Z-A", query: "name desc" }
|
|
7480
|
-
];
|
|
7481
8176
|
function transformSwellProduct(params, product) {
|
|
7482
8177
|
if (!product) {
|
|
7483
|
-
return
|
|
8178
|
+
return null;
|
|
7484
8179
|
}
|
|
7485
8180
|
const newProduct = {
|
|
7486
8181
|
...product,
|
|
7487
8182
|
// add swell properties there
|
|
7488
8183
|
selected_option_values: getSelectedVariantOptionValues(product, params),
|
|
7489
|
-
purchase_options: getPurchaseOptions(product, params)
|
|
8184
|
+
purchase_options: getPurchaseOptions(product, params) ?? void 0
|
|
7490
8185
|
};
|
|
7491
8186
|
if (Array.isArray(newProduct.variants?.results)) {
|
|
7492
8187
|
newProduct.variants = {
|
|
@@ -7499,43 +8194,49 @@ function transformSwellProduct(params, product) {
|
|
|
7499
8194
|
return newProduct;
|
|
7500
8195
|
}
|
|
7501
8196
|
var SwellProduct = class extends SwellStorefrontRecord {
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
super(swell, "products", id, query,
|
|
7505
|
-
|
|
8197
|
+
constructor(swell, id, query) {
|
|
8198
|
+
const params = swell.queryParams;
|
|
8199
|
+
super(swell, "products", id, query, async function() {
|
|
8200
|
+
const result = await this._defaultGetter().call(this);
|
|
8201
|
+
return transformSwellProduct(params, result);
|
|
8202
|
+
});
|
|
7506
8203
|
return this._getProxy();
|
|
7507
8204
|
}
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
);
|
|
7514
|
-
|
|
8205
|
+
};
|
|
8206
|
+
|
|
8207
|
+
// src/resources/product_recommendations.ts
|
|
8208
|
+
var SwellProductRecommendations = class extends SwellProduct {
|
|
8209
|
+
constructor(swell, id, query) {
|
|
8210
|
+
super(swell, id, { ...query, $recommendations: true });
|
|
8211
|
+
}
|
|
8212
|
+
};
|
|
8213
|
+
|
|
8214
|
+
// src/resources/search.ts
|
|
8215
|
+
var SwellSearch = class extends StorefrontResource {
|
|
8216
|
+
constructor(swell, query) {
|
|
8217
|
+
super(async () => {
|
|
8218
|
+
const performed = String(query || "").length > 0;
|
|
8219
|
+
const productFilters = await getProductFilters(
|
|
8220
|
+
swell,
|
|
8221
|
+
performed ? { search: query } : void 0
|
|
8222
|
+
);
|
|
8223
|
+
return {
|
|
8224
|
+
query,
|
|
8225
|
+
performed,
|
|
8226
|
+
...productFilters
|
|
8227
|
+
};
|
|
8228
|
+
});
|
|
8229
|
+
}
|
|
8230
|
+
};
|
|
8231
|
+
|
|
8232
|
+
// src/resources/subscription.ts
|
|
8233
|
+
var SwellSubscription = class extends SwellStorefrontRecord {
|
|
8234
|
+
constructor(swell, id, query) {
|
|
8235
|
+
super(swell, "accounts:subscriptions", id, query, function() {
|
|
8236
|
+
return this._swell.storefront.subscriptions.get(this._id, this._query);
|
|
8237
|
+
});
|
|
7515
8238
|
}
|
|
7516
8239
|
};
|
|
7517
|
-
function productQueryWithFilters(swell, query = {}) {
|
|
7518
|
-
const sortBy = swell.queryParams.sort || "";
|
|
7519
|
-
const filters2 = Object.entries(swell.queryParams).reduce(
|
|
7520
|
-
(acc, [key, value]) => {
|
|
7521
|
-
if (key.startsWith("filter_")) {
|
|
7522
|
-
const qkey = key.replace("filter_", "");
|
|
7523
|
-
if (value?.gte !== void 0 || value?.lte !== void 0) {
|
|
7524
|
-
acc[qkey] = [value.gte || 0, value.lte || void 0];
|
|
7525
|
-
} else {
|
|
7526
|
-
acc[qkey] = value;
|
|
7527
|
-
}
|
|
7528
|
-
}
|
|
7529
|
-
return acc;
|
|
7530
|
-
},
|
|
7531
|
-
{}
|
|
7532
|
-
);
|
|
7533
|
-
return {
|
|
7534
|
-
sort: SORT_OPTIONS.find((option) => option.value === sortBy)?.query || void 0,
|
|
7535
|
-
$filters: filters2,
|
|
7536
|
-
...query
|
|
7537
|
-
};
|
|
7538
|
-
}
|
|
7539
8240
|
|
|
7540
8241
|
// src/api.ts
|
|
7541
8242
|
var DEFAULT_API_HOST = "https://api.schema.io";
|
|
@@ -7596,7 +8297,7 @@ var Swell = class _Swell {
|
|
|
7596
8297
|
this.workerEnv = workerEnv;
|
|
7597
8298
|
this.resourceLoadingIndicator = params.resourceLoadingIndicator;
|
|
7598
8299
|
logger.info(
|
|
7599
|
-
`[SDK] KV cache: ${this.workerEnv?.THEME ? "enabled" : "disabled"}`
|
|
8300
|
+
`[SDK] KV cache: ${this.workerEnv?.THEME ? "enabled" : "disabled"}, flavor: ${getKVFlavor(this.workerEnv)}`
|
|
7600
8301
|
);
|
|
7601
8302
|
if (serverHeaders) {
|
|
7602
8303
|
const { headers: headers2, swellHeaders: swellHeaders2 } = _Swell.formatHeaders(serverHeaders);
|
|
@@ -15106,11 +15807,9 @@ function getProducts(instance, object, mapper) {
|
|
|
15106
15807
|
return this._defaultGetter().call(this);
|
|
15107
15808
|
}
|
|
15108
15809
|
);
|
|
15109
|
-
return products._cloneWithCompatibilityResult(
|
|
15110
|
-
(
|
|
15111
|
-
|
|
15112
|
-
}
|
|
15113
|
-
);
|
|
15810
|
+
return products._cloneWithCompatibilityResult((products2) => {
|
|
15811
|
+
return { ...products2, results: products2.results.map(mapper) };
|
|
15812
|
+
});
|
|
15114
15813
|
});
|
|
15115
15814
|
}
|
|
15116
15815
|
function makeProductsCollectionResolve(instance, object, mapper) {
|
|
@@ -15127,19 +15826,24 @@ function makeProductsCollectionResolve(instance, object, mapper) {
|
|
|
15127
15826
|
|
|
15128
15827
|
// src/compatibility/shopify-objects/collections.ts
|
|
15129
15828
|
function ShopifyCollections(instance, categories) {
|
|
15130
|
-
return new SwellStorefrontCollection(
|
|
15131
|
-
|
|
15132
|
-
|
|
15133
|
-
|
|
15134
|
-
|
|
15135
|
-
|
|
15136
|
-
|
|
15137
|
-
|
|
15138
|
-
|
|
15139
|
-
|
|
15140
|
-
|
|
15141
|
-
|
|
15142
|
-
|
|
15829
|
+
return new SwellStorefrontCollection(
|
|
15830
|
+
instance.swell,
|
|
15831
|
+
categories._collection,
|
|
15832
|
+
categories._query,
|
|
15833
|
+
async () => {
|
|
15834
|
+
const results = (await categories.results)?.map((category) => {
|
|
15835
|
+
return ShopifyCollection(instance, category);
|
|
15836
|
+
});
|
|
15837
|
+
return {
|
|
15838
|
+
page: categories.page ?? 1,
|
|
15839
|
+
count: categories.count ?? 0,
|
|
15840
|
+
results: results ?? [],
|
|
15841
|
+
page_count: categories.page_count ?? 0,
|
|
15842
|
+
limit: categories.limit,
|
|
15843
|
+
pages: categories.pages ?? {}
|
|
15844
|
+
};
|
|
15845
|
+
}
|
|
15846
|
+
);
|
|
15143
15847
|
}
|
|
15144
15848
|
|
|
15145
15849
|
// src/compatibility/shopify-objects/address.ts
|
|
@@ -16326,13 +17030,12 @@ var ImagesDrop = class extends Drop7 {
|
|
|
16326
17030
|
var SwellImage = class extends StorefrontResource {
|
|
16327
17031
|
constructor(swell, name) {
|
|
16328
17032
|
super(async () => {
|
|
16329
|
-
const
|
|
17033
|
+
const file = await swell.get("/:files/:last", {
|
|
16330
17034
|
private: { $ne: true },
|
|
16331
17035
|
content_type: { $regex: "^image/" },
|
|
16332
17036
|
filename: name
|
|
16333
17037
|
});
|
|
16334
|
-
|
|
16335
|
-
if (file === null) {
|
|
17038
|
+
if (!file) {
|
|
16336
17039
|
return null;
|
|
16337
17040
|
}
|
|
16338
17041
|
return { file };
|
|
@@ -19255,296 +19958,255 @@ function getLiquidFS(getThemeConfig, extName) {
|
|
|
19255
19958
|
}
|
|
19256
19959
|
|
|
19257
19960
|
// src/theme/theme-loader.ts
|
|
19258
|
-
import bluebird from "bluebird";
|
|
19259
|
-
var { Promise: Promise2 } = bluebird;
|
|
19260
19961
|
var MAX_INDIVIDUAL_CONFIGS_TO_FETCH = 50;
|
|
19261
|
-
var ThemeLoader = class
|
|
19262
|
-
static cache = null;
|
|
19962
|
+
var ThemeLoader = class {
|
|
19263
19963
|
swell;
|
|
19264
|
-
manifest;
|
|
19265
19964
|
configs;
|
|
19266
|
-
configPaths;
|
|
19267
19965
|
constructor(swell) {
|
|
19268
19966
|
this.swell = swell;
|
|
19269
|
-
this.manifest = null;
|
|
19270
19967
|
this.configs = /* @__PURE__ */ new Map();
|
|
19271
|
-
this.configPaths = [];
|
|
19272
19968
|
}
|
|
19969
|
+
/**
|
|
19970
|
+
* Initialize the theme loader with all configurations.
|
|
19971
|
+
* Either uses provided configs (editor mode) or loads from storage.
|
|
19972
|
+
*/
|
|
19273
19973
|
async init(themeConfigs) {
|
|
19274
19974
|
if (themeConfigs) {
|
|
19275
19975
|
this.setConfigs(themeConfigs);
|
|
19276
19976
|
return;
|
|
19277
19977
|
}
|
|
19278
19978
|
if (!this.getThemeId()) {
|
|
19979
|
+
logger.debug("[ThemeLoader] No theme ID, skipping init");
|
|
19279
19980
|
return;
|
|
19280
19981
|
}
|
|
19281
|
-
await this.
|
|
19282
|
-
|
|
19283
|
-
|
|
19284
|
-
|
|
19285
|
-
}
|
|
19982
|
+
await this.loadAllConfigs();
|
|
19983
|
+
logger.info("[ThemeLoader] Initialization complete", {
|
|
19984
|
+
configCount: this.configs.size,
|
|
19985
|
+
themeId: this.getThemeId()
|
|
19986
|
+
});
|
|
19286
19987
|
}
|
|
19287
19988
|
/**
|
|
19288
|
-
*
|
|
19989
|
+
* Get a single config by file path (synchronous).
|
|
19990
|
+
* Returns null if config not found.
|
|
19289
19991
|
*/
|
|
19290
|
-
|
|
19291
|
-
|
|
19292
|
-
console.log("ThemeLoader.loadTheme", swellHeaders["theme-version-hash"]);
|
|
19293
|
-
if (swellHeaders["theme-version-hash"]) {
|
|
19294
|
-
const configs = await this.loadThemeFromManifest();
|
|
19295
|
-
if (configs) {
|
|
19296
|
-
return configs;
|
|
19297
|
-
}
|
|
19298
|
-
}
|
|
19299
|
-
return this.loadThemeAllConfigs();
|
|
19992
|
+
getConfig(filePath) {
|
|
19993
|
+
return this.configs.get(filePath) ?? null;
|
|
19300
19994
|
}
|
|
19301
19995
|
/**
|
|
19302
|
-
*
|
|
19996
|
+
* Get all loaded configs.
|
|
19997
|
+
* Used by theme getter to expose configs to editor/tests.
|
|
19303
19998
|
*/
|
|
19304
|
-
|
|
19305
|
-
|
|
19306
|
-
_ThemeLoader.cache = new ThemeCache({
|
|
19307
|
-
kvStore: this.swell.workerEnv?.THEME
|
|
19308
|
-
});
|
|
19309
|
-
}
|
|
19310
|
-
return _ThemeLoader.cache;
|
|
19999
|
+
getConfigs() {
|
|
20000
|
+
return this.configs;
|
|
19311
20001
|
}
|
|
19312
20002
|
/**
|
|
19313
|
-
*
|
|
20003
|
+
* Get multiple configs by path pattern (synchronous).
|
|
20004
|
+
* Filters configs by prefix and optional suffix.
|
|
19314
20005
|
*/
|
|
19315
|
-
|
|
19316
|
-
|
|
19317
|
-
|
|
19318
|
-
|
|
19319
|
-
|
|
20006
|
+
getConfigsByPath(pathPrefix, pathSuffix) {
|
|
20007
|
+
const results = [];
|
|
20008
|
+
for (const [path, config] of this.configs) {
|
|
20009
|
+
if (path.startsWith(pathPrefix) && (!pathSuffix || path.endsWith(pathSuffix))) {
|
|
20010
|
+
results.push(config);
|
|
20011
|
+
}
|
|
19320
20012
|
}
|
|
19321
|
-
|
|
20013
|
+
return results;
|
|
19322
20014
|
}
|
|
19323
20015
|
/**
|
|
19324
|
-
*
|
|
20016
|
+
* Load theme configs from internal data, typically in the editor.
|
|
20017
|
+
* Used when configs are provided externally (e.g., from editor).
|
|
19325
20018
|
*/
|
|
19326
|
-
|
|
19327
|
-
|
|
19328
|
-
console.log(
|
|
19329
|
-
`ThemeLoader.preloadTheme${version?.hash ? ` - manifest: ${version.hash}` : ""}${configs?.length ? ` - configs: ${configs.length}` : ""}`
|
|
19330
|
-
);
|
|
19331
|
-
const promises = [];
|
|
19332
|
-
if (version) {
|
|
19333
|
-
promises.push(this.cacheManifest(version));
|
|
19334
|
-
}
|
|
19335
|
-
if (configs) {
|
|
19336
|
-
const themeId = this.getThemeId();
|
|
19337
|
-
promises.push(
|
|
19338
|
-
Promise2.map(
|
|
19339
|
-
configs,
|
|
19340
|
-
async (config) => {
|
|
19341
|
-
const promises2 = [
|
|
19342
|
-
this.cacheThemeConfig(config)
|
|
19343
|
-
];
|
|
19344
|
-
if (themeId && config.file?.url) {
|
|
19345
|
-
promises2.push(
|
|
19346
|
-
this.cacheThemeFileUrl(themeId, config.hash, config.file.url)
|
|
19347
|
-
);
|
|
19348
|
-
}
|
|
19349
|
-
await Promise2.all(promises2);
|
|
19350
|
-
},
|
|
19351
|
-
{ concurrency: 10 }
|
|
19352
|
-
)
|
|
19353
|
-
);
|
|
19354
|
-
}
|
|
19355
|
-
await Promise2.all(promises);
|
|
20019
|
+
setConfigs(themeConfigs) {
|
|
20020
|
+
this.configs = new Map(themeConfigs);
|
|
19356
20021
|
}
|
|
19357
20022
|
/**
|
|
19358
|
-
*
|
|
20023
|
+
* Updates KV with file_data for provided theme configs (warmup path).
|
|
20024
|
+
* Uses the new ThemeFileStorage abstraction for optimized operations.
|
|
19359
20025
|
*/
|
|
19360
|
-
async
|
|
19361
|
-
const
|
|
19362
|
-
if (
|
|
19363
|
-
|
|
19364
|
-
|
|
19365
|
-
|
|
19366
|
-
|
|
19367
|
-
|
|
20026
|
+
async updateThemeCache(payload) {
|
|
20027
|
+
const configs = payload?.configs || [];
|
|
20028
|
+
if (configs.length === 0) {
|
|
20029
|
+
logger.debug("[ThemeLoader] No configs to cache");
|
|
20030
|
+
return {
|
|
20031
|
+
written: 0,
|
|
20032
|
+
skipped: 0,
|
|
20033
|
+
skippedExisting: 0,
|
|
20034
|
+
warnings: []
|
|
20035
|
+
};
|
|
19368
20036
|
}
|
|
19369
|
-
const
|
|
19370
|
-
const
|
|
19371
|
-
|
|
19372
|
-
|
|
19373
|
-
|
|
19374
|
-
|
|
19375
|
-
|
|
19376
|
-
|
|
19377
|
-
|
|
19378
|
-
|
|
19379
|
-
|
|
19380
|
-
|
|
19381
|
-
|
|
19382
|
-
|
|
19383
|
-
|
|
19384
|
-
|
|
20037
|
+
const flavor = getKVFlavor(this.swell.workerEnv);
|
|
20038
|
+
const trace = createTraceId();
|
|
20039
|
+
logger.info("[ThemeLoader] Starting theme cache update", {
|
|
20040
|
+
totalConfigs: configs.length,
|
|
20041
|
+
flavor,
|
|
20042
|
+
trace
|
|
20043
|
+
});
|
|
20044
|
+
const storage = new ThemeFileStorage(this.swell.workerEnv, flavor);
|
|
20045
|
+
const result = await storage.putFiles(configs);
|
|
20046
|
+
if (result.warnings.length > 0) {
|
|
20047
|
+
logger.warn("[ThemeLoader] Theme cache updated with warnings", {
|
|
20048
|
+
total: configs.length,
|
|
20049
|
+
written: result.written,
|
|
20050
|
+
skipped: result.skipped,
|
|
20051
|
+
skippedExisting: result.skippedExisting,
|
|
20052
|
+
warnings: result.warnings.length,
|
|
20053
|
+
trace
|
|
20054
|
+
});
|
|
20055
|
+
} else {
|
|
20056
|
+
logger.info("[ThemeLoader] Theme cache updated successfully", {
|
|
20057
|
+
total: configs.length,
|
|
20058
|
+
written: result.written,
|
|
20059
|
+
skipped: result.skipped,
|
|
20060
|
+
skippedExisting: result.skippedExisting,
|
|
20061
|
+
trace
|
|
20062
|
+
});
|
|
19385
20063
|
}
|
|
19386
|
-
return
|
|
19387
|
-
}
|
|
19388
|
-
async fetchThemeConfigsByPath(pathPrefix, pathSuffix) {
|
|
19389
|
-
const paths = this.configPaths.filter(
|
|
19390
|
-
(path) => path.startsWith(pathPrefix) && (!pathSuffix || path.endsWith(pathSuffix))
|
|
19391
|
-
);
|
|
19392
|
-
const configs = await Promise2.map(
|
|
19393
|
-
paths,
|
|
19394
|
-
(path) => this.fetchThemeConfig(path),
|
|
19395
|
-
{ concurrency: 10 }
|
|
19396
|
-
);
|
|
19397
|
-
return configs.filter((config) => config !== null);
|
|
20064
|
+
return result;
|
|
19398
20065
|
}
|
|
19399
20066
|
/**
|
|
19400
|
-
*
|
|
20067
|
+
* Main loading logic - loads all configs at once.
|
|
20068
|
+
* 1. Fetches lightweight metadata (cached when possible)
|
|
20069
|
+
* 2. Batch hydrates file_data from KV
|
|
20070
|
+
* 3. Fetches missing file_data from API if needed
|
|
19401
20071
|
*/
|
|
19402
|
-
async
|
|
19403
|
-
const
|
|
19404
|
-
|
|
19405
|
-
|
|
19406
|
-
|
|
20072
|
+
async loadAllConfigs() {
|
|
20073
|
+
const configMetadata = await this.fetchConfigMetadata();
|
|
20074
|
+
if (configMetadata.length === 0) {
|
|
20075
|
+
logger.warn("[ThemeLoader] No configs found");
|
|
20076
|
+
return;
|
|
19407
20077
|
}
|
|
19408
|
-
|
|
19409
|
-
|
|
19410
|
-
|
|
19411
|
-
);
|
|
19412
|
-
|
|
20078
|
+
logger.debug("[ThemeLoader] Loading configs", {
|
|
20079
|
+
total: configMetadata.length
|
|
20080
|
+
});
|
|
20081
|
+
const flavor = getKVFlavor(this.swell.workerEnv);
|
|
20082
|
+
const storage = new ThemeFileStorage(this.swell.workerEnv, flavor);
|
|
20083
|
+
const kvHydrated = await storage.getFiles(configMetadata);
|
|
20084
|
+
const completeConfigs = await this.ensureConfigsHaveData(kvHydrated);
|
|
20085
|
+
for (const config of completeConfigs) {
|
|
20086
|
+
this.configs.set(config.file_path, config);
|
|
20087
|
+
}
|
|
20088
|
+
logger.info("[ThemeLoader] All configs loaded", {
|
|
20089
|
+
total: completeConfigs.length,
|
|
20090
|
+
withData: completeConfigs.filter((c) => c.file_data).length
|
|
20091
|
+
});
|
|
19413
20092
|
}
|
|
19414
20093
|
/**
|
|
19415
|
-
*
|
|
19416
|
-
*
|
|
19417
|
-
* This approach has the following optimizations:
|
|
19418
|
-
* - cached manifests and configs can be shared by other clients
|
|
19419
|
-
* - when fetching from source, only fetch the missing records
|
|
20094
|
+
* Fetch lightweight config metadata from API or cache.
|
|
20095
|
+
* Does NOT include file_data to minimize payload size.
|
|
19420
20096
|
*/
|
|
19421
|
-
async
|
|
19422
|
-
const
|
|
19423
|
-
|
|
19424
|
-
|
|
19425
|
-
|
|
19426
|
-
|
|
19427
|
-
|
|
19428
|
-
|
|
19429
|
-
|
|
19430
|
-
|
|
19431
|
-
|
|
19432
|
-
|
|
19433
|
-
|
|
19434
|
-
|
|
19435
|
-
|
|
19436
|
-
|
|
19437
|
-
if (!themeConfig) {
|
|
19438
|
-
configHashesUnresolved.push(configHash);
|
|
19439
|
-
return;
|
|
19440
|
-
}
|
|
19441
|
-
let config = themeConfig;
|
|
19442
|
-
if (fileUrl && themeConfig.file?.url) {
|
|
19443
|
-
config = {
|
|
19444
|
-
...themeConfig,
|
|
19445
|
-
file: { ...themeConfig.file, url: fileUrl }
|
|
19446
|
-
};
|
|
20097
|
+
async fetchConfigMetadata() {
|
|
20098
|
+
const query = {
|
|
20099
|
+
...this.themeVersionQueryFilter(),
|
|
20100
|
+
limit: 1e3,
|
|
20101
|
+
type: "theme",
|
|
20102
|
+
fields: "id, name, type, file, file_path, hash"
|
|
20103
|
+
// NO file_data
|
|
20104
|
+
};
|
|
20105
|
+
try {
|
|
20106
|
+
const cache = new WorkerCacheProxy(this.swell);
|
|
20107
|
+
const versionHash = this.swell.swellHeaders["theme-version-hash"];
|
|
20108
|
+
const cached = await cache.get(
|
|
20109
|
+
"/:themes:configs",
|
|
20110
|
+
query,
|
|
20111
|
+
{
|
|
20112
|
+
version: versionHash || null
|
|
19447
20113
|
}
|
|
19448
|
-
configsByHash.set(config.hash, config);
|
|
19449
|
-
this.configs.set(config.file_path, config);
|
|
19450
|
-
},
|
|
19451
|
-
{ concurrency: 10 }
|
|
19452
|
-
);
|
|
19453
|
-
if (configHashesUnresolved.length > 0) {
|
|
19454
|
-
const configs = await this.fetchThemeConfigsFromSource(
|
|
19455
|
-
// If no configs were resolved, then fetch them all. otherwise fetch
|
|
19456
|
-
// the specific subset of configs.
|
|
19457
|
-
configsByHash.size === 0 ? void 0 : configHashesUnresolved
|
|
19458
20114
|
);
|
|
19459
|
-
|
|
19460
|
-
|
|
19461
|
-
|
|
19462
|
-
this.configs.set(config.file_path, config);
|
|
20115
|
+
if (cached) {
|
|
20116
|
+
logger.debug("[ThemeLoader] Config metadata cache hit");
|
|
20117
|
+
return cached;
|
|
19463
20118
|
}
|
|
19464
|
-
|
|
19465
|
-
|
|
19466
|
-
async (config) => {
|
|
19467
|
-
const promises = [this.cacheThemeConfig(config)];
|
|
19468
|
-
if (themeId && config.file?.url) {
|
|
19469
|
-
promises.push(
|
|
19470
|
-
this.cacheThemeFileUrl(themeId, config.hash, config.file.url)
|
|
19471
|
-
);
|
|
19472
|
-
}
|
|
19473
|
-
await Promise2.all(promises);
|
|
19474
|
-
},
|
|
19475
|
-
{ concurrency: 10 }
|
|
19476
|
-
);
|
|
20119
|
+
} catch (err) {
|
|
20120
|
+
logger.warn("[ThemeLoader] Cache read failed, fetching from API", err);
|
|
19477
20121
|
}
|
|
19478
|
-
|
|
19479
|
-
|
|
19480
|
-
|
|
19481
|
-
|
|
19482
|
-
|
|
19483
|
-
|
|
19484
|
-
|
|
19485
|
-
|
|
20122
|
+
logger.debug("[ThemeLoader] Fetching config metadata from API");
|
|
20123
|
+
const response = await this.swell.get(
|
|
20124
|
+
"/:themes:configs",
|
|
20125
|
+
query
|
|
20126
|
+
);
|
|
20127
|
+
const configs = response?.results || [];
|
|
20128
|
+
try {
|
|
20129
|
+
const cache = new WorkerCacheProxy(this.swell);
|
|
20130
|
+
const versionHash = this.swell.swellHeaders["theme-version-hash"];
|
|
20131
|
+
await cache.put("/:themes:configs", query, configs, {
|
|
20132
|
+
version: versionHash || null
|
|
20133
|
+
});
|
|
20134
|
+
} catch (err) {
|
|
20135
|
+
logger.warn("[ThemeLoader] Cache write failed", err);
|
|
19486
20136
|
}
|
|
20137
|
+
return configs;
|
|
19487
20138
|
}
|
|
19488
20139
|
/**
|
|
19489
|
-
*
|
|
20140
|
+
* Helper to ensure all configs have file_data.
|
|
20141
|
+
* Fetches missing data from API and updates KV cache.
|
|
19490
20142
|
*/
|
|
19491
|
-
async
|
|
19492
|
-
|
|
19493
|
-
|
|
20143
|
+
async ensureConfigsHaveData(configs) {
|
|
20144
|
+
const missingData = configs.filter((c) => !c.file_data);
|
|
20145
|
+
if (missingData.length === 0) {
|
|
20146
|
+
logger.debug("[ThemeLoader] All configs have file_data from KV");
|
|
20147
|
+
return configs;
|
|
19494
20148
|
}
|
|
19495
|
-
|
|
19496
|
-
|
|
19497
|
-
|
|
19498
|
-
|
|
19499
|
-
|
|
19500
|
-
|
|
19501
|
-
}
|
|
19502
|
-
/**
|
|
19503
|
-
* Fetches the manifest (set of config hashes) for a theme version.
|
|
19504
|
-
*/
|
|
19505
|
-
async fetchManifest() {
|
|
19506
|
-
const { swellHeaders } = this.swell;
|
|
19507
|
-
const versionHash = swellHeaders["theme-version-hash"];
|
|
19508
|
-
console.log("ThemeLoader.fetchManifest", versionHash);
|
|
19509
|
-
let manifest = await this.getCache().get(
|
|
19510
|
-
`manifest:${versionHash}`
|
|
20149
|
+
const trace = createTraceId();
|
|
20150
|
+
logger.info(
|
|
20151
|
+
`[ThemeLoader] Loading ${missingData.length} missing file_data from API`,
|
|
20152
|
+
{
|
|
20153
|
+
trace
|
|
20154
|
+
}
|
|
19511
20155
|
);
|
|
19512
|
-
|
|
19513
|
-
|
|
19514
|
-
|
|
19515
|
-
|
|
19516
|
-
|
|
19517
|
-
|
|
19518
|
-
|
|
19519
|
-
|
|
19520
|
-
|
|
19521
|
-
|
|
19522
|
-
|
|
20156
|
+
const hashes = missingData.map((c) => c.hash);
|
|
20157
|
+
const apiResponse = await this.fetchThemeConfigsFromSource(hashes);
|
|
20158
|
+
const fetched = apiResponse.results || [];
|
|
20159
|
+
logger.info(`[ThemeLoader] Fetched ${fetched.length} configs from API`, {
|
|
20160
|
+
trace
|
|
20161
|
+
});
|
|
20162
|
+
if (fetched.length > 0) {
|
|
20163
|
+
const cacheResult = await this.updateThemeCache({
|
|
20164
|
+
api: 1,
|
|
20165
|
+
// Required by SwellThemePreload type
|
|
20166
|
+
configs: fetched
|
|
20167
|
+
});
|
|
20168
|
+
if (cacheResult.warnings.length > 0) {
|
|
20169
|
+
logger.warn("[ThemeLoader] Some files had size issues", {
|
|
20170
|
+
warnings: cacheResult.warnings.length
|
|
20171
|
+
});
|
|
19523
20172
|
}
|
|
19524
20173
|
}
|
|
19525
|
-
|
|
19526
|
-
|
|
19527
|
-
|
|
20174
|
+
const fetchedMap = new Map(fetched.map((c) => [c.hash, c]));
|
|
20175
|
+
const mergedConfigs = configs.map((config) => {
|
|
20176
|
+
if (!config.file_data) {
|
|
20177
|
+
const withData = fetchedMap.get(config.hash);
|
|
20178
|
+
if (withData?.file_data) {
|
|
20179
|
+
return { ...config, file_data: withData.file_data };
|
|
20180
|
+
}
|
|
20181
|
+
}
|
|
20182
|
+
return config;
|
|
20183
|
+
});
|
|
20184
|
+
const stillMissing = mergedConfigs.filter((c) => !c.file_data).length;
|
|
20185
|
+
if (stillMissing > 0) {
|
|
20186
|
+
logger.warn(
|
|
20187
|
+
`[ThemeLoader] ${stillMissing} configs still missing file_data after fetch`
|
|
20188
|
+
);
|
|
19528
20189
|
}
|
|
19529
|
-
return
|
|
20190
|
+
return mergedConfigs;
|
|
19530
20191
|
}
|
|
19531
20192
|
/**
|
|
19532
|
-
* Fetches
|
|
20193
|
+
* Fetches theme configs with file_data from Swell Backend API.
|
|
20194
|
+
* Used to retrieve missing file_data for configs.
|
|
19533
20195
|
*/
|
|
19534
20196
|
async fetchThemeConfigsFromSource(configHashes = void 0) {
|
|
19535
20197
|
configHashes = configHashes || [];
|
|
19536
20198
|
const { swellHeaders } = this.swell;
|
|
19537
20199
|
const version = String(swellHeaders["theme-config-version"]);
|
|
19538
20200
|
const fetchAll = configHashes.length === 0 || configHashes.length > MAX_INDIVIDUAL_CONFIGS_TO_FETCH;
|
|
19539
|
-
|
|
19540
|
-
`
|
|
20201
|
+
logger.debug(
|
|
20202
|
+
`[ThemeLoader] Fetching ${fetchAll ? "all" : configHashes.length} configs with file_data`,
|
|
20203
|
+
{ version }
|
|
19541
20204
|
);
|
|
19542
20205
|
const configs = await this.swell.get(
|
|
19543
20206
|
"/:themes:configs",
|
|
19544
20207
|
{
|
|
19545
20208
|
...this.themeVersionQueryFilter(),
|
|
19546
20209
|
...fetchAll ? void 0 : { hash: { $in: configHashes } },
|
|
19547
|
-
// TODO: paginate to support more than 1000 configs
|
|
19548
20210
|
limit: 1e3,
|
|
19549
20211
|
type: "theme",
|
|
19550
20212
|
fields: "name, file, file_path, hash",
|
|
@@ -19556,45 +20218,13 @@ var ThemeLoader = class _ThemeLoader {
|
|
|
19556
20218
|
return configs;
|
|
19557
20219
|
}
|
|
19558
20220
|
/**
|
|
19559
|
-
*
|
|
19560
|
-
* This is used when a hash entry cannot be found.
|
|
19561
|
-
* We may override the cached hash in order to ensure it is found on reload,
|
|
19562
|
-
* but we probably need to find why that happens in the first place (TODO).
|
|
20221
|
+
* Get the current theme ID from headers.
|
|
19563
20222
|
*/
|
|
19564
|
-
async fetchThemeConfigsFromSourceByPath(filePath, hash) {
|
|
19565
|
-
console.log(`Retrieving theme config - ${filePath}`);
|
|
19566
|
-
const config = await this.swell.get(
|
|
19567
|
-
"/:themes:configs/:last",
|
|
19568
|
-
{
|
|
19569
|
-
...this.themeVersionQueryFilter(),
|
|
19570
|
-
file_path: filePath,
|
|
19571
|
-
fields: "name, file, file_path, hash",
|
|
19572
|
-
include: {
|
|
19573
|
-
file_data: FILE_DATA_INCLUDE_QUERY
|
|
19574
|
-
}
|
|
19575
|
-
}
|
|
19576
|
-
);
|
|
19577
|
-
if (config) {
|
|
19578
|
-
this.configs.set(filePath, config);
|
|
19579
|
-
if (hash) {
|
|
19580
|
-
config.hash = hash;
|
|
19581
|
-
}
|
|
19582
|
-
const themeId = this.getThemeId();
|
|
19583
|
-
const promises = [this.cacheThemeConfig(config)];
|
|
19584
|
-
if (themeId && config.file?.url) {
|
|
19585
|
-
promises.push(
|
|
19586
|
-
this.cacheThemeFileUrl(themeId, config.hash, config.file.url)
|
|
19587
|
-
);
|
|
19588
|
-
}
|
|
19589
|
-
await Promise2.all(promises);
|
|
19590
|
-
}
|
|
19591
|
-
return config ?? null;
|
|
19592
|
-
}
|
|
19593
20223
|
getThemeId() {
|
|
19594
20224
|
return this.swell.swellHeaders["theme-id"];
|
|
19595
20225
|
}
|
|
19596
20226
|
/**
|
|
19597
|
-
*
|
|
20227
|
+
* Generate a Swell API query filter for this theme version.
|
|
19598
20228
|
*/
|
|
19599
20229
|
themeVersionQueryFilter() {
|
|
19600
20230
|
const { swellHeaders } = this.swell;
|
|
@@ -19774,7 +20404,6 @@ var SwellTheme3 = class {
|
|
|
19774
20404
|
resources;
|
|
19775
20405
|
liquidSwell;
|
|
19776
20406
|
themeLoader;
|
|
19777
|
-
themeConfigs = null;
|
|
19778
20407
|
page;
|
|
19779
20408
|
pageId;
|
|
19780
20409
|
shopifyCompatibility = null;
|
|
@@ -19808,6 +20437,23 @@ var SwellTheme3 = class {
|
|
|
19808
20437
|
});
|
|
19809
20438
|
this.themeLoader = new ThemeLoader(swell);
|
|
19810
20439
|
}
|
|
20440
|
+
/**
|
|
20441
|
+
* Getter for theme configs - returns the configs from the loader.
|
|
20442
|
+
* Used by editor and tests to access loaded configs.
|
|
20443
|
+
*/
|
|
20444
|
+
get themeConfigs() {
|
|
20445
|
+
const configs = this.themeLoader.getConfigs();
|
|
20446
|
+
return configs.size > 0 ? configs : null;
|
|
20447
|
+
}
|
|
20448
|
+
/**
|
|
20449
|
+
* Setter for theme configs - directly sets configs in the loader.
|
|
20450
|
+
* Used by editor and tests to inject configs without API/KV loading.
|
|
20451
|
+
*/
|
|
20452
|
+
set themeConfigs(configs) {
|
|
20453
|
+
if (configs) {
|
|
20454
|
+
this.themeLoader.setConfigs(configs);
|
|
20455
|
+
}
|
|
20456
|
+
}
|
|
19811
20457
|
getSwellAppThemeProps(swellConfig) {
|
|
19812
20458
|
return swellConfig?.storefront?.theme || {};
|
|
19813
20459
|
}
|
|
@@ -19881,10 +20527,8 @@ var SwellTheme3 = class {
|
|
|
19881
20527
|
}
|
|
19882
20528
|
async getSettingsAndConfigs() {
|
|
19883
20529
|
const geo = GEO_DATA;
|
|
19884
|
-
const
|
|
19885
|
-
|
|
19886
|
-
this.getThemeConfigsByPath("theme/config/", ".json")
|
|
19887
|
-
]);
|
|
20530
|
+
const storefrontSettings = await this.swell.getStorefrontSettings();
|
|
20531
|
+
const settingConfigs = this.getThemeConfigsByPath("theme/config/", ".json");
|
|
19888
20532
|
const configs = {
|
|
19889
20533
|
theme: {},
|
|
19890
20534
|
editor: {},
|
|
@@ -19979,7 +20623,7 @@ var SwellTheme3 = class {
|
|
|
19979
20623
|
$locale: void 0
|
|
19980
20624
|
};
|
|
19981
20625
|
if (pageId) {
|
|
19982
|
-
const templateConfig =
|
|
20626
|
+
const templateConfig = this._getTemplateConfigByType(
|
|
19983
20627
|
"templates",
|
|
19984
20628
|
pageId,
|
|
19985
20629
|
altTemplate
|
|
@@ -20274,7 +20918,7 @@ var SwellTheme3 = class {
|
|
|
20274
20918
|
this.shopifyCompatibility.adaptPageData(pageData);
|
|
20275
20919
|
}
|
|
20276
20920
|
async getLocaleConfig(localeCode = "en", suffix = ".json") {
|
|
20277
|
-
const allLocaleConfigs =
|
|
20921
|
+
const allLocaleConfigs = this.getThemeConfigsByPath(
|
|
20278
20922
|
"theme/locales/",
|
|
20279
20923
|
suffix
|
|
20280
20924
|
);
|
|
@@ -20378,22 +21022,24 @@ var SwellTheme3 = class {
|
|
|
20378
21022
|
}
|
|
20379
21023
|
return resolvedUrl;
|
|
20380
21024
|
}
|
|
20381
|
-
async getAllThemeConfigs() {
|
|
20382
|
-
if (this.themeConfigs === null) {
|
|
20383
|
-
const configs = await this.themeLoader.loadTheme();
|
|
20384
|
-
const configsByPath = /* @__PURE__ */ new Map();
|
|
20385
|
-
for (const config of configs) {
|
|
20386
|
-
configsByPath.set(config.file_path, config);
|
|
20387
|
-
}
|
|
20388
|
-
this.themeConfigs = configsByPath;
|
|
20389
|
-
}
|
|
20390
|
-
return this.themeConfigs;
|
|
20391
|
-
}
|
|
20392
|
-
/**
|
|
20393
|
-
* Preloads updated theme configs. Used to optimize initial theme load.
|
|
20394
|
-
*/
|
|
20395
21025
|
async preloadThemeConfigs(payload) {
|
|
20396
|
-
await this.themeLoader.
|
|
21026
|
+
const result = await this.themeLoader.updateThemeCache(payload);
|
|
21027
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
21028
|
+
const rejected = result.warnings.filter(
|
|
21029
|
+
(w) => w.reason === "rejected_5mb" || w.reason === "exceeded_25mb"
|
|
21030
|
+
).length;
|
|
21031
|
+
const warned = result.warnings.filter(
|
|
21032
|
+
(w) => w.reason === "warning_1mb"
|
|
21033
|
+
).length;
|
|
21034
|
+
logger.warn("[Theme] File size issues detected during cache update", {
|
|
21035
|
+
totalWarnings: result.warnings.length,
|
|
21036
|
+
rejected,
|
|
21037
|
+
warned,
|
|
21038
|
+
details: result.warnings.slice(0, 5)
|
|
21039
|
+
// Log first 5 warnings for debugging
|
|
21040
|
+
});
|
|
21041
|
+
}
|
|
21042
|
+
return result;
|
|
20397
21043
|
}
|
|
20398
21044
|
getPageConfigPath(pageId, altTemplate) {
|
|
20399
21045
|
if (this.shopifyCompatibility) {
|
|
@@ -20406,16 +21052,10 @@ var SwellTheme3 = class {
|
|
|
20406
21052
|
return `${withSuffix(`theme/templates/${pageId}`, altTemplate)}.json`;
|
|
20407
21053
|
}
|
|
20408
21054
|
async getThemeConfig(filePath) {
|
|
20409
|
-
|
|
20410
|
-
return this.themeConfigs.get(filePath) ?? null;
|
|
20411
|
-
}
|
|
20412
|
-
return this.themeLoader.fetchThemeConfig(filePath);
|
|
21055
|
+
return this.themeLoader.getConfig(filePath);
|
|
20413
21056
|
}
|
|
20414
|
-
|
|
20415
|
-
const configs =
|
|
20416
|
-
pathPrefix,
|
|
20417
|
-
pathSuffix
|
|
20418
|
-
);
|
|
21057
|
+
getThemeConfigsByPath(pathPrefix, pathSuffix) {
|
|
21058
|
+
const configs = this.themeLoader.getConfigsByPath(pathPrefix, pathSuffix);
|
|
20419
21059
|
const configsByPath = /* @__PURE__ */ new Map();
|
|
20420
21060
|
for (const config of configs) {
|
|
20421
21061
|
configsByPath.set(config.file_path, config);
|
|
@@ -20423,31 +21063,42 @@ var SwellTheme3 = class {
|
|
|
20423
21063
|
return configsByPath;
|
|
20424
21064
|
}
|
|
20425
21065
|
async getThemeTemplateConfig(filePath) {
|
|
20426
|
-
|
|
20427
|
-
return this.getThemeConfig(filePath);
|
|
20428
|
-
}
|
|
20429
|
-
const jsonTemplate = await this.getThemeConfig(`${filePath}.json`);
|
|
20430
|
-
if (jsonTemplate) {
|
|
20431
|
-
return jsonTemplate;
|
|
20432
|
-
}
|
|
20433
|
-
return this.getThemeConfig(`${filePath}.liquid`);
|
|
21066
|
+
return this._getTemplateConfig(filePath);
|
|
20434
21067
|
}
|
|
20435
|
-
|
|
21068
|
+
/**
|
|
21069
|
+
* Internal synchronous helper for getting template configs by type.
|
|
21070
|
+
* Used internally within theme.ts to avoid async overhead.
|
|
21071
|
+
*/
|
|
21072
|
+
_getTemplateConfigByType(type, name, suffix) {
|
|
20436
21073
|
const templatesByPriority = [withSuffix(`${type}/${name}`, suffix)];
|
|
20437
21074
|
if (this.shopifyCompatibility) {
|
|
20438
21075
|
const path = this.shopifyCompatibility.getThemeFilePath(type, name);
|
|
20439
21076
|
templatesByPriority.push(withSuffix(path, suffix));
|
|
20440
21077
|
}
|
|
20441
21078
|
for (const filePath of templatesByPriority) {
|
|
20442
|
-
const templateConfig =
|
|
20443
|
-
`theme/${filePath}`
|
|
20444
|
-
);
|
|
21079
|
+
const templateConfig = this._getTemplateConfig(`theme/${filePath}`);
|
|
20445
21080
|
if (templateConfig) {
|
|
20446
21081
|
return templateConfig;
|
|
20447
21082
|
}
|
|
20448
21083
|
}
|
|
20449
21084
|
return null;
|
|
20450
21085
|
}
|
|
21086
|
+
/**
|
|
21087
|
+
* Internal synchronous helper for getting template configs.
|
|
21088
|
+
*/
|
|
21089
|
+
_getTemplateConfig(filePath) {
|
|
21090
|
+
if (filePath.endsWith(".json") || filePath.endsWith(".liquid")) {
|
|
21091
|
+
return this.themeLoader.getConfig(filePath);
|
|
21092
|
+
}
|
|
21093
|
+
const jsonTemplate = this.themeLoader.getConfig(`${filePath}.json`);
|
|
21094
|
+
if (jsonTemplate) {
|
|
21095
|
+
return jsonTemplate;
|
|
21096
|
+
}
|
|
21097
|
+
return this.themeLoader.getConfig(`${filePath}.liquid`);
|
|
21098
|
+
}
|
|
21099
|
+
async getThemeTemplateConfigByType(type, name, suffix) {
|
|
21100
|
+
return this._getTemplateConfigByType(type, name, suffix);
|
|
21101
|
+
}
|
|
20451
21102
|
async getAssetConfig(assetName) {
|
|
20452
21103
|
return await this.getThemeConfig(`theme/assets/${assetName}`) ?? await this.getThemeConfig(`assets/${assetName}`) ?? null;
|
|
20453
21104
|
}
|
|
@@ -20469,17 +21120,8 @@ var SwellTheme3 = class {
|
|
|
20469
21120
|
return "";
|
|
20470
21121
|
}
|
|
20471
21122
|
template = unescapeLiquidSyntax(template);
|
|
20472
|
-
const trace = createTraceId();
|
|
20473
21123
|
try {
|
|
20474
|
-
logger.debug("[SDK] Render template start", {
|
|
20475
|
-
config: config.name,
|
|
20476
|
-
trace
|
|
20477
|
-
});
|
|
20478
21124
|
const result = await this.liquidSwell.parseAndRender(template, data);
|
|
20479
|
-
logger.debug("[SDK] Render template end", {
|
|
20480
|
-
config: config.name,
|
|
20481
|
-
trace
|
|
20482
|
-
});
|
|
20483
21125
|
return result;
|
|
20484
21126
|
} catch (err) {
|
|
20485
21127
|
logger.error(err);
|
|
@@ -20496,10 +21138,7 @@ var SwellTheme3 = class {
|
|
|
20496
21138
|
}
|
|
20497
21139
|
async getSectionSchema(sectionName) {
|
|
20498
21140
|
let result;
|
|
20499
|
-
const config =
|
|
20500
|
-
"sections",
|
|
20501
|
-
sectionName
|
|
20502
|
-
);
|
|
21141
|
+
const config = this._getTemplateConfigByType("sections", sectionName);
|
|
20503
21142
|
if (config?.file_path?.endsWith(".json")) {
|
|
20504
21143
|
try {
|
|
20505
21144
|
result = JSON56.parse(config.file_data) || void 0;
|
|
@@ -20577,10 +21216,7 @@ var SwellTheme3 = class {
|
|
|
20577
21216
|
return content;
|
|
20578
21217
|
}
|
|
20579
21218
|
async renderLayoutTemplate(name, data) {
|
|
20580
|
-
const templateConfig =
|
|
20581
|
-
"layouts",
|
|
20582
|
-
name
|
|
20583
|
-
);
|
|
21219
|
+
const templateConfig = this._getTemplateConfigByType("layouts", name);
|
|
20584
21220
|
if (!templateConfig) {
|
|
20585
21221
|
throw new Error(`Layout template not found: ${name}`);
|
|
20586
21222
|
}
|
|
@@ -20615,17 +21251,14 @@ ${content.slice(pos)}`;
|
|
|
20615
21251
|
async renderPageTemplate(name, data, altTemplateId) {
|
|
20616
21252
|
let templateConfig = null;
|
|
20617
21253
|
if (altTemplateId) {
|
|
20618
|
-
templateConfig =
|
|
21254
|
+
templateConfig = this._getTemplateConfigByType(
|
|
20619
21255
|
"templates",
|
|
20620
21256
|
name,
|
|
20621
21257
|
altTemplateId
|
|
20622
21258
|
);
|
|
20623
21259
|
}
|
|
20624
21260
|
if (!templateConfig) {
|
|
20625
|
-
templateConfig =
|
|
20626
|
-
"templates",
|
|
20627
|
-
name
|
|
20628
|
-
);
|
|
21261
|
+
templateConfig = this._getTemplateConfigByType("templates", name);
|
|
20629
21262
|
}
|
|
20630
21263
|
if (templateConfig) {
|
|
20631
21264
|
const templatePath = name.split("/").splice(1).join("/") || null;
|
|
@@ -20726,7 +21359,7 @@ ${content.slice(pos)}`;
|
|
|
20726
21359
|
}
|
|
20727
21360
|
const [sectionKey, originalPageId] = sectionId.split(/__/).reverse();
|
|
20728
21361
|
const pageId = (originalPageId || "").replaceAll("_", "/");
|
|
20729
|
-
const templateConfig =
|
|
21362
|
+
const templateConfig = this._getTemplateConfigByType(
|
|
20730
21363
|
pageId ? "templates" : "sections",
|
|
20731
21364
|
pageId ? pageId : sectionKey
|
|
20732
21365
|
);
|
|
@@ -20901,7 +21534,7 @@ ${this.shopifyCompatibility.getContentForHeader()}`;
|
|
|
20901
21534
|
return defaults;
|
|
20902
21535
|
}
|
|
20903
21536
|
async getAllSections() {
|
|
20904
|
-
const configs =
|
|
21537
|
+
const configs = this.getThemeConfigsByPath("theme/sections/");
|
|
20905
21538
|
return getAllSections(configs, this.getTemplateSchema.bind(this));
|
|
20906
21539
|
}
|
|
20907
21540
|
async getPageSections(sectionGroup, resolveSettings = true) {
|
|
@@ -20928,7 +21561,7 @@ ${this.shopifyCompatibility.getContentForHeader()}`;
|
|
|
20928
21561
|
const sectionName = sectionSchema?.label || sectionFileName;
|
|
20929
21562
|
let sourcePath = "";
|
|
20930
21563
|
if (group) {
|
|
20931
|
-
const sectionConfig =
|
|
21564
|
+
const sectionConfig = this._getTemplateConfigByType(
|
|
20932
21565
|
"sections",
|
|
20933
21566
|
`${sectionFileName}.json`
|
|
20934
21567
|
);
|
|
@@ -20945,7 +21578,7 @@ ${this.shopifyCompatibility.getContentForHeader()}`;
|
|
|
20945
21578
|
* Get a list of sections and section groups in a page layout.
|
|
20946
21579
|
*/
|
|
20947
21580
|
async getPageSectionGroups(pageId, altTemplate) {
|
|
20948
|
-
const pageConfig =
|
|
21581
|
+
const pageConfig = this._getTemplateConfigByType(
|
|
20949
21582
|
"templates",
|
|
20950
21583
|
pageId,
|
|
20951
21584
|
altTemplate
|
|
@@ -21012,7 +21645,7 @@ ${this.shopifyCompatibility.getContentForHeader()}`;
|
|
|
21012
21645
|
sectionConfigs.map(async (sectionConfig, index) => {
|
|
21013
21646
|
const { section, schema } = sectionConfig;
|
|
21014
21647
|
const settings = schema?.fields && this.globals ? resolveSectionSettings(this, sectionConfig, index) : { ...sectionConfig.settings };
|
|
21015
|
-
const templateConfig =
|
|
21648
|
+
const templateConfig = this._getTemplateConfigByType(
|
|
21016
21649
|
"sections",
|
|
21017
21650
|
`${section.type}.liquid`
|
|
21018
21651
|
);
|
|
@@ -21704,21 +22337,30 @@ export {
|
|
|
21704
22337
|
StorefrontResource,
|
|
21705
22338
|
Swell,
|
|
21706
22339
|
SwellAccount,
|
|
22340
|
+
SwellAddresses,
|
|
21707
22341
|
SwellBackendAPI,
|
|
21708
22342
|
SwellBlog,
|
|
21709
22343
|
SwellBlogCategory,
|
|
21710
22344
|
SwellCart,
|
|
22345
|
+
SwellCategories,
|
|
21711
22346
|
SwellCategory,
|
|
21712
22347
|
SwellError,
|
|
21713
22348
|
SwellOrder,
|
|
22349
|
+
SwellOrders,
|
|
21714
22350
|
SwellPage,
|
|
22351
|
+
SwellPredictiveSearch,
|
|
21715
22352
|
SwellProduct,
|
|
22353
|
+
SwellProductRecommendations,
|
|
22354
|
+
SwellSearch,
|
|
21716
22355
|
SwellStorefrontCollection,
|
|
21717
22356
|
SwellStorefrontPagination,
|
|
21718
22357
|
SwellStorefrontRecord,
|
|
21719
22358
|
SwellStorefrontResource,
|
|
21720
22359
|
SwellStorefrontSingleton,
|
|
22360
|
+
SwellSubscription,
|
|
22361
|
+
SwellSubscriptions,
|
|
21721
22362
|
SwellTheme3 as SwellTheme,
|
|
22363
|
+
SwellVariant,
|
|
21722
22364
|
ThemeColor,
|
|
21723
22365
|
ThemeFont,
|
|
21724
22366
|
ThemeForm,
|
|
@@ -21750,6 +22392,7 @@ export {
|
|
|
21750
22392
|
getEasyblocksComponentDefinitions,
|
|
21751
22393
|
getEasyblocksPagePropsWithConfigs,
|
|
21752
22394
|
getEasyblocksPageTemplate,
|
|
22395
|
+
getKVFlavor,
|
|
21753
22396
|
getLayoutSectionGroups,
|
|
21754
22397
|
getMenuItemStorefrontUrl,
|
|
21755
22398
|
getMenuItemUrlAndResource,
|
|
@@ -21765,6 +22408,7 @@ export {
|
|
|
21765
22408
|
isObject2 as isObject,
|
|
21766
22409
|
md5,
|
|
21767
22410
|
removeCircularReferences,
|
|
22411
|
+
resetKVFlavorCache,
|
|
21768
22412
|
resolveAsyncResources,
|
|
21769
22413
|
resolveLookupCollection,
|
|
21770
22414
|
resolveMenuItemUrlAndResource,
|