@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.cjs
CHANGED
|
@@ -66,21 +66,30 @@ __export(index_exports, {
|
|
|
66
66
|
StorefrontResource: () => StorefrontResource,
|
|
67
67
|
Swell: () => Swell,
|
|
68
68
|
SwellAccount: () => SwellAccount,
|
|
69
|
+
SwellAddresses: () => SwellAddresses,
|
|
69
70
|
SwellBackendAPI: () => SwellBackendAPI,
|
|
70
71
|
SwellBlog: () => SwellBlog,
|
|
71
72
|
SwellBlogCategory: () => SwellBlogCategory,
|
|
72
73
|
SwellCart: () => SwellCart,
|
|
74
|
+
SwellCategories: () => SwellCategories,
|
|
73
75
|
SwellCategory: () => SwellCategory,
|
|
74
76
|
SwellError: () => SwellError,
|
|
75
77
|
SwellOrder: () => SwellOrder,
|
|
78
|
+
SwellOrders: () => SwellOrders,
|
|
76
79
|
SwellPage: () => SwellPage,
|
|
80
|
+
SwellPredictiveSearch: () => SwellPredictiveSearch,
|
|
77
81
|
SwellProduct: () => SwellProduct,
|
|
82
|
+
SwellProductRecommendations: () => SwellProductRecommendations,
|
|
83
|
+
SwellSearch: () => SwellSearch,
|
|
78
84
|
SwellStorefrontCollection: () => SwellStorefrontCollection,
|
|
79
85
|
SwellStorefrontPagination: () => SwellStorefrontPagination,
|
|
80
86
|
SwellStorefrontRecord: () => SwellStorefrontRecord,
|
|
81
87
|
SwellStorefrontResource: () => SwellStorefrontResource,
|
|
82
88
|
SwellStorefrontSingleton: () => SwellStorefrontSingleton,
|
|
89
|
+
SwellSubscription: () => SwellSubscription,
|
|
90
|
+
SwellSubscriptions: () => SwellSubscriptions,
|
|
83
91
|
SwellTheme: () => SwellTheme3,
|
|
92
|
+
SwellVariant: () => SwellVariant,
|
|
84
93
|
ThemeColor: () => ThemeColor,
|
|
85
94
|
ThemeFont: () => ThemeFont,
|
|
86
95
|
ThemeForm: () => ThemeForm,
|
|
@@ -112,6 +121,7 @@ __export(index_exports, {
|
|
|
112
121
|
getEasyblocksComponentDefinitions: () => getEasyblocksComponentDefinitions,
|
|
113
122
|
getEasyblocksPagePropsWithConfigs: () => getEasyblocksPagePropsWithConfigs,
|
|
114
123
|
getEasyblocksPageTemplate: () => getEasyblocksPageTemplate,
|
|
124
|
+
getKVFlavor: () => getKVFlavor,
|
|
115
125
|
getLayoutSectionGroups: () => getLayoutSectionGroups,
|
|
116
126
|
getMenuItemStorefrontUrl: () => getMenuItemStorefrontUrl,
|
|
117
127
|
getMenuItemUrlAndResource: () => getMenuItemUrlAndResource,
|
|
@@ -127,6 +137,7 @@ __export(index_exports, {
|
|
|
127
137
|
isObject: () => isObject2,
|
|
128
138
|
md5: () => md5,
|
|
129
139
|
removeCircularReferences: () => removeCircularReferences,
|
|
140
|
+
resetKVFlavorCache: () => resetKVFlavorCache,
|
|
130
141
|
resolveAsyncResources: () => resolveAsyncResources,
|
|
131
142
|
resolveLookupCollection: () => resolveLookupCollection,
|
|
132
143
|
resolveMenuItemUrlAndResource: () => resolveMenuItemUrlAndResource,
|
|
@@ -761,18 +772,12 @@ var StorefrontResource = class {
|
|
|
761
772
|
}
|
|
762
773
|
return instance[prop];
|
|
763
774
|
}
|
|
764
|
-
// add additional properties to the loaded result
|
|
765
|
-
_transformResult(result) {
|
|
766
|
-
return result;
|
|
767
|
-
}
|
|
768
775
|
async _get(..._args) {
|
|
769
776
|
if (this._getter) {
|
|
770
777
|
const getter = this._getter.bind(
|
|
771
778
|
this
|
|
772
779
|
);
|
|
773
780
|
return Promise.resolve().then(getter).then((result) => {
|
|
774
|
-
return this._transformResult(result);
|
|
775
|
-
}).then((result) => {
|
|
776
781
|
this._result = result ?? null;
|
|
777
782
|
if (result) {
|
|
778
783
|
Object.assign(this, result);
|
|
@@ -855,7 +860,6 @@ function cloneStorefrontResource(input) {
|
|
|
855
860
|
});
|
|
856
861
|
const clone = new ClonedClass(input._getter);
|
|
857
862
|
clone._params = input._params;
|
|
858
|
-
clone._transformResult = input._transformResult.bind(clone);
|
|
859
863
|
Object.defineProperty(clone, "_resourceName", {
|
|
860
864
|
value: resourceName
|
|
861
865
|
});
|
|
@@ -909,7 +913,11 @@ var SwellStorefrontCollection = class _SwellStorefrontCollection extends SwellSt
|
|
|
909
913
|
limit = DEFAULT_QUERY_PAGE_LIMIT;
|
|
910
914
|
name;
|
|
911
915
|
constructor(swell, collection, query = {}, getter) {
|
|
912
|
-
super(
|
|
916
|
+
super(
|
|
917
|
+
swell,
|
|
918
|
+
collection,
|
|
919
|
+
getter
|
|
920
|
+
);
|
|
913
921
|
this._query = this._initQuery(query);
|
|
914
922
|
if (!getter) {
|
|
915
923
|
this._setGetter(this._defaultGetter());
|
|
@@ -1085,8 +1093,6 @@ var SwellStorefrontRecord = class extends SwellStorefrontResource {
|
|
|
1085
1093
|
getter,
|
|
1086
1094
|
isResourceCacheble(this._collection)
|
|
1087
1095
|
).then((result) => {
|
|
1088
|
-
return this._transformResult(result);
|
|
1089
|
-
}).then((result) => {
|
|
1090
1096
|
this._result = result;
|
|
1091
1097
|
if (result) {
|
|
1092
1098
|
Object.assign(this, result);
|
|
@@ -6832,6 +6838,50 @@ function md5(inputString) {
|
|
|
6832
6838
|
return rh(a) + rh(b) + rh(c) + rh(d);
|
|
6833
6839
|
}
|
|
6834
6840
|
|
|
6841
|
+
// src/utils/kv-flavor.ts
|
|
6842
|
+
var cachedKVFlavor;
|
|
6843
|
+
function getKVFlavor(workerEnv) {
|
|
6844
|
+
if (cachedKVFlavor) {
|
|
6845
|
+
return cachedKVFlavor;
|
|
6846
|
+
}
|
|
6847
|
+
if (!workerEnv?.THEME) {
|
|
6848
|
+
cachedKVFlavor = "memory";
|
|
6849
|
+
return cachedKVFlavor;
|
|
6850
|
+
}
|
|
6851
|
+
const kvFlavor = workerEnv.KV_FLAVOR;
|
|
6852
|
+
if (kvFlavor) {
|
|
6853
|
+
const flavorLower = String(kvFlavor).toLowerCase();
|
|
6854
|
+
switch (flavorLower) {
|
|
6855
|
+
case "cloudflare":
|
|
6856
|
+
case "cf":
|
|
6857
|
+
cachedKVFlavor = "cf";
|
|
6858
|
+
break;
|
|
6859
|
+
case "miniflare":
|
|
6860
|
+
cachedKVFlavor = "miniflare";
|
|
6861
|
+
break;
|
|
6862
|
+
case "memory":
|
|
6863
|
+
cachedKVFlavor = "memory";
|
|
6864
|
+
break;
|
|
6865
|
+
default:
|
|
6866
|
+
logger.warn(`[KV] Unknown KV_FLAVOR: ${kvFlavor}, using miniflare`);
|
|
6867
|
+
cachedKVFlavor = "miniflare";
|
|
6868
|
+
}
|
|
6869
|
+
return cachedKVFlavor;
|
|
6870
|
+
}
|
|
6871
|
+
try {
|
|
6872
|
+
if (typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers") {
|
|
6873
|
+
cachedKVFlavor = "cf";
|
|
6874
|
+
return cachedKVFlavor;
|
|
6875
|
+
}
|
|
6876
|
+
} catch {
|
|
6877
|
+
}
|
|
6878
|
+
cachedKVFlavor = "miniflare";
|
|
6879
|
+
return cachedKVFlavor;
|
|
6880
|
+
}
|
|
6881
|
+
function resetKVFlavorCache() {
|
|
6882
|
+
cachedKVFlavor = void 0;
|
|
6883
|
+
}
|
|
6884
|
+
|
|
6835
6885
|
// src/utils/index.ts
|
|
6836
6886
|
function isSectionConfig(config, themeConfigs) {
|
|
6837
6887
|
if (!config.file_path.startsWith("theme/sections/")) {
|
|
@@ -7361,68 +7411,584 @@ function buildStores2() {
|
|
|
7361
7411
|
|
|
7362
7412
|
// src/cache/theme-cache.ts
|
|
7363
7413
|
var TTL = 90 * 24 * 60 * 60 * 1e3;
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7414
|
+
|
|
7415
|
+
// src/cache/theme-file-storage.ts
|
|
7416
|
+
var import_bluebird2 = __toESM(require("bluebird"), 1);
|
|
7417
|
+
|
|
7418
|
+
// src/cache/kv-variety.ts
|
|
7419
|
+
var import_bluebird = __toESM(require("bluebird"), 1);
|
|
7420
|
+
var { Promise: Promise2 } = import_bluebird.default;
|
|
7421
|
+
var CFKV = class {
|
|
7422
|
+
constructor(kv) {
|
|
7423
|
+
this.kv = kv;
|
|
7424
|
+
}
|
|
7425
|
+
async get(keys) {
|
|
7426
|
+
if (keys.length === 0) {
|
|
7427
|
+
return /* @__PURE__ */ new Map();
|
|
7428
|
+
}
|
|
7429
|
+
const result = await this.kv.get(keys, "text");
|
|
7430
|
+
if (!(result instanceof Map)) {
|
|
7431
|
+
const map = /* @__PURE__ */ new Map();
|
|
7432
|
+
for (const key of keys) {
|
|
7433
|
+
map.set(key, null);
|
|
7434
|
+
}
|
|
7435
|
+
return map;
|
|
7436
|
+
}
|
|
7437
|
+
return result;
|
|
7438
|
+
}
|
|
7439
|
+
async put(key, value, metadata) {
|
|
7440
|
+
await this.kv.put(key, value, { metadata });
|
|
7441
|
+
}
|
|
7442
|
+
};
|
|
7443
|
+
var MiniflareKV = class {
|
|
7444
|
+
constructor(kv) {
|
|
7445
|
+
this.kv = kv;
|
|
7446
|
+
}
|
|
7447
|
+
async get(keys) {
|
|
7448
|
+
if (keys.length === 0) {
|
|
7449
|
+
return /* @__PURE__ */ new Map();
|
|
7450
|
+
}
|
|
7451
|
+
const result = /* @__PURE__ */ new Map();
|
|
7452
|
+
await Promise2.map(
|
|
7453
|
+
keys,
|
|
7454
|
+
async (key) => {
|
|
7455
|
+
const value = await this.kv.get(key, "text");
|
|
7456
|
+
result.set(key, value);
|
|
7457
|
+
},
|
|
7458
|
+
{ concurrency: 50 }
|
|
7459
|
+
);
|
|
7460
|
+
return result;
|
|
7461
|
+
}
|
|
7462
|
+
async put(key, value, metadata) {
|
|
7463
|
+
await this.kv.put(key, value, { metadata });
|
|
7464
|
+
}
|
|
7465
|
+
};
|
|
7466
|
+
var MemoryKV = class {
|
|
7467
|
+
store = /* @__PURE__ */ new Map();
|
|
7468
|
+
async get(keys) {
|
|
7469
|
+
const result = /* @__PURE__ */ new Map();
|
|
7470
|
+
for (const key of keys) {
|
|
7471
|
+
const entry = this.store.get(key);
|
|
7472
|
+
result.set(key, entry?.value ?? null);
|
|
7473
|
+
}
|
|
7474
|
+
return result;
|
|
7475
|
+
}
|
|
7476
|
+
async put(key, value, metadata) {
|
|
7477
|
+
this.store.set(key, { value, metadata });
|
|
7478
|
+
}
|
|
7479
|
+
};
|
|
7480
|
+
function createClientKV(env, flavor = "cf") {
|
|
7481
|
+
if (env?.THEME) {
|
|
7482
|
+
if (flavor === "miniflare") {
|
|
7483
|
+
return new MiniflareKV(env.THEME);
|
|
7484
|
+
}
|
|
7485
|
+
return new CFKV(env.THEME);
|
|
7486
|
+
}
|
|
7487
|
+
return new MemoryKV();
|
|
7488
|
+
}
|
|
7489
|
+
|
|
7490
|
+
// src/cache/theme-file-storage.ts
|
|
7491
|
+
var { Promise: Promise3 } = import_bluebird2.default;
|
|
7492
|
+
var ThemeFileStorage = class {
|
|
7493
|
+
kv;
|
|
7494
|
+
maxConcurrency;
|
|
7495
|
+
maxBatchSize = 20 * 1024 * 1024;
|
|
7496
|
+
// 20MB safety margin
|
|
7497
|
+
constructor(env, flavor = "cf") {
|
|
7498
|
+
this.kv = createClientKV(env, flavor);
|
|
7499
|
+
this.maxConcurrency = flavor === "miniflare" ? 50 : 6;
|
|
7500
|
+
}
|
|
7501
|
+
/**
|
|
7502
|
+
* Build a KV storage key from a file hash
|
|
7503
|
+
*/
|
|
7504
|
+
buildKey(hash) {
|
|
7505
|
+
return `file_data:${hash}`;
|
|
7506
|
+
}
|
|
7507
|
+
/**
|
|
7508
|
+
* Extract hash from a KV storage key
|
|
7509
|
+
*/
|
|
7510
|
+
extractHashFromKey(key) {
|
|
7511
|
+
return key.replace("file_data:", "");
|
|
7512
|
+
}
|
|
7513
|
+
/**
|
|
7514
|
+
* Plan GET batches based on file sizes to avoid 413 errors
|
|
7515
|
+
* Uses round-robin distribution for even batch sizes
|
|
7516
|
+
*/
|
|
7517
|
+
planGetBatches(configs) {
|
|
7518
|
+
if (configs.length === 0) {
|
|
7519
|
+
return [];
|
|
7520
|
+
}
|
|
7521
|
+
const sorted = [...configs].sort((a, b) => {
|
|
7522
|
+
const sizeA = a.file?.length || 0;
|
|
7523
|
+
const sizeB = b.file?.length || 0;
|
|
7524
|
+
return sizeB - sizeA;
|
|
7525
|
+
});
|
|
7526
|
+
const totalSize = sorted.reduce((sum, config) => {
|
|
7527
|
+
return sum + (config.file?.length || 0);
|
|
7528
|
+
}, 0);
|
|
7529
|
+
const sizeBatches = Math.ceil(totalSize / this.maxBatchSize);
|
|
7530
|
+
const keyBatches = Math.ceil(sorted.length / 100);
|
|
7531
|
+
const targetBatches = Math.max(sizeBatches, keyBatches);
|
|
7532
|
+
const batches = Array.from(
|
|
7533
|
+
{ length: targetBatches },
|
|
7534
|
+
() => ({
|
|
7535
|
+
configs: [],
|
|
7536
|
+
keys: [],
|
|
7537
|
+
estimatedSize: 0
|
|
7538
|
+
})
|
|
7539
|
+
);
|
|
7540
|
+
sorted.forEach((config, index) => {
|
|
7541
|
+
const batchIndex = index % targetBatches;
|
|
7542
|
+
const batch = batches[batchIndex];
|
|
7543
|
+
batch.configs.push(config);
|
|
7544
|
+
batch.keys.push(this.buildKey(config.hash));
|
|
7545
|
+
batch.estimatedSize += config.file?.length || 0;
|
|
7546
|
+
});
|
|
7547
|
+
return batches.filter((batch) => batch.configs.length > 0);
|
|
7548
|
+
}
|
|
7549
|
+
/**
|
|
7550
|
+
* Load a single batch from KV storage
|
|
7551
|
+
*/
|
|
7552
|
+
async loadBatch(batch) {
|
|
7553
|
+
return this.kv.get(batch.keys);
|
|
7554
|
+
}
|
|
7555
|
+
/**
|
|
7556
|
+
* Merge batch results with original configs
|
|
7557
|
+
*/
|
|
7558
|
+
mergeResults(configs, batchResults) {
|
|
7559
|
+
const allData = /* @__PURE__ */ new Map();
|
|
7560
|
+
for (const batchResult of batchResults) {
|
|
7561
|
+
for (const [key, value] of batchResult.entries()) {
|
|
7562
|
+
allData.set(key, value);
|
|
7563
|
+
}
|
|
7564
|
+
}
|
|
7565
|
+
return configs.map((config) => {
|
|
7566
|
+
const key = this.buildKey(config.hash);
|
|
7567
|
+
const fileData = allData.get(key);
|
|
7568
|
+
if (fileData) {
|
|
7569
|
+
return {
|
|
7570
|
+
...config,
|
|
7571
|
+
file_data: fileData
|
|
7572
|
+
};
|
|
7573
|
+
}
|
|
7574
|
+
return config;
|
|
7575
|
+
});
|
|
7576
|
+
}
|
|
7577
|
+
async getFiles(configs) {
|
|
7578
|
+
if (configs.length === 0) {
|
|
7579
|
+
return [];
|
|
7580
|
+
}
|
|
7581
|
+
const trace = createTraceId();
|
|
7582
|
+
const batches = this.planGetBatches(configs);
|
|
7583
|
+
const totalSize = batches.reduce((sum, b) => sum + b.estimatedSize, 0);
|
|
7584
|
+
const maxBatchSize = Math.max(...batches.map((b) => b.estimatedSize));
|
|
7585
|
+
logger.debug("[ThemeFileStorage] Loading files start", {
|
|
7586
|
+
totalConfigs: configs.length,
|
|
7587
|
+
batchCount: batches.length,
|
|
7588
|
+
maxBatchSize,
|
|
7589
|
+
totalSize,
|
|
7590
|
+
trace
|
|
7591
|
+
});
|
|
7592
|
+
const results = await Promise3.map(
|
|
7593
|
+
batches,
|
|
7594
|
+
(batch) => this.loadBatch(batch),
|
|
7595
|
+
{ concurrency: Math.min(this.maxConcurrency, batches.length) }
|
|
7596
|
+
);
|
|
7597
|
+
const mergedConfigs = this.mergeResults(configs, results);
|
|
7598
|
+
const loadedCount = mergedConfigs.filter((c) => c.file_data).length;
|
|
7599
|
+
logger.debug("[ThemeFileStorage] Loading files end", {
|
|
7600
|
+
requested: configs.length,
|
|
7601
|
+
loaded: loadedCount,
|
|
7602
|
+
missing: configs.length - loadedCount,
|
|
7603
|
+
batches: batches.length,
|
|
7604
|
+
trace
|
|
7605
|
+
});
|
|
7606
|
+
return mergedConfigs;
|
|
7607
|
+
}
|
|
7608
|
+
/**
|
|
7609
|
+
* Validate file sizes and categorize by threshold
|
|
7610
|
+
*/
|
|
7611
|
+
validateFiles(configs) {
|
|
7612
|
+
const valid = [];
|
|
7613
|
+
const warnings = [];
|
|
7614
|
+
for (const config of configs) {
|
|
7615
|
+
if (!config.file_data) {
|
|
7616
|
+
continue;
|
|
7617
|
+
}
|
|
7618
|
+
const size = config.file?.length || 0;
|
|
7619
|
+
if (size >= 25 * 1024 * 1024) {
|
|
7620
|
+
warnings.push({
|
|
7621
|
+
hash: config.hash,
|
|
7622
|
+
filePath: config.file_path,
|
|
7623
|
+
size,
|
|
7624
|
+
reason: "exceeded_25mb",
|
|
7625
|
+
action: "rejected"
|
|
7626
|
+
});
|
|
7627
|
+
} else if (size >= 5 * 1024 * 1024) {
|
|
7628
|
+
warnings.push({
|
|
7629
|
+
hash: config.hash,
|
|
7630
|
+
filePath: config.file_path,
|
|
7631
|
+
size,
|
|
7632
|
+
reason: "rejected_5mb",
|
|
7633
|
+
action: "rejected"
|
|
7634
|
+
});
|
|
7635
|
+
} else {
|
|
7636
|
+
if (size >= 1024 * 1024) {
|
|
7637
|
+
warnings.push({
|
|
7638
|
+
hash: config.hash,
|
|
7639
|
+
filePath: config.file_path,
|
|
7640
|
+
size,
|
|
7641
|
+
reason: "warning_1mb",
|
|
7642
|
+
action: "stored"
|
|
7643
|
+
});
|
|
7644
|
+
}
|
|
7645
|
+
valid.push(config);
|
|
7646
|
+
}
|
|
7647
|
+
}
|
|
7648
|
+
return { valid, warnings };
|
|
7649
|
+
}
|
|
7650
|
+
/**
|
|
7651
|
+
* Check which files already exist in KV storage
|
|
7652
|
+
* Uses batch planning to avoid 413 errors when checking existence
|
|
7653
|
+
*/
|
|
7654
|
+
async checkExistence(configs) {
|
|
7655
|
+
if (configs.length === 0) {
|
|
7656
|
+
return /* @__PURE__ */ new Set();
|
|
7657
|
+
}
|
|
7658
|
+
const existing = /* @__PURE__ */ new Set();
|
|
7659
|
+
const batches = this.planGetBatches(configs);
|
|
7660
|
+
const results = await Promise3.map(
|
|
7661
|
+
batches,
|
|
7662
|
+
(batch) => this.kv.get(batch.keys),
|
|
7663
|
+
{ concurrency: this.maxConcurrency }
|
|
7664
|
+
);
|
|
7665
|
+
for (const batchResult of results) {
|
|
7666
|
+
for (const [key, value] of batchResult.entries()) {
|
|
7667
|
+
if (value !== null) {
|
|
7668
|
+
const hash = this.extractHashFromKey(key);
|
|
7669
|
+
existing.add(hash);
|
|
7670
|
+
}
|
|
7671
|
+
}
|
|
7672
|
+
}
|
|
7673
|
+
return existing;
|
|
7674
|
+
}
|
|
7675
|
+
async putFiles(configs) {
|
|
7676
|
+
const result = {
|
|
7677
|
+
written: 0,
|
|
7678
|
+
skipped: 0,
|
|
7679
|
+
skippedExisting: 0,
|
|
7680
|
+
warnings: []
|
|
7681
|
+
};
|
|
7682
|
+
if (configs.length === 0) {
|
|
7683
|
+
return result;
|
|
7684
|
+
}
|
|
7685
|
+
const trace = createTraceId();
|
|
7686
|
+
logger.debug("[ThemeFileStorage] Put files start", {
|
|
7687
|
+
totalConfigs: configs.length,
|
|
7688
|
+
trace
|
|
7689
|
+
});
|
|
7690
|
+
const { valid, warnings } = this.validateFiles(configs);
|
|
7691
|
+
result.warnings = warnings;
|
|
7692
|
+
if (warnings.length > 0) {
|
|
7693
|
+
const rejectedCount = warnings.filter(
|
|
7694
|
+
(w) => w.action === "rejected"
|
|
7695
|
+
).length;
|
|
7696
|
+
const warnedCount = warnings.filter((w) => w.action === "stored").length;
|
|
7697
|
+
logger.warn("[ThemeFileStorage] File size validation issues", {
|
|
7698
|
+
totalWarnings: warnings.length,
|
|
7699
|
+
rejected: rejectedCount,
|
|
7700
|
+
warned: warnedCount,
|
|
7701
|
+
trace
|
|
7702
|
+
});
|
|
7703
|
+
warnings.filter((w) => w.action === "rejected").forEach((w) => {
|
|
7704
|
+
logger.error("[ThemeFileStorage] File rejected due to size", {
|
|
7705
|
+
filePath: w.filePath,
|
|
7706
|
+
size: w.size,
|
|
7707
|
+
reason: w.reason,
|
|
7708
|
+
trace
|
|
7709
|
+
});
|
|
7710
|
+
});
|
|
7711
|
+
}
|
|
7712
|
+
const rejected = warnings.filter((w) => w.action === "rejected").length;
|
|
7713
|
+
result.skipped = rejected + (configs.length - configs.filter((c) => c.file_data).length);
|
|
7714
|
+
logger.debug("[ThemeFileStorage] Checking existence", {
|
|
7715
|
+
validFiles: valid.length,
|
|
7716
|
+
trace
|
|
7717
|
+
});
|
|
7718
|
+
const existing = await this.checkExistence(valid);
|
|
7719
|
+
result.skippedExisting = existing.size;
|
|
7720
|
+
const toWrite = valid.filter((config) => !existing.has(config.hash));
|
|
7721
|
+
if (toWrite.length > 0) {
|
|
7722
|
+
logger.debug("[ThemeFileStorage] Writing new files", {
|
|
7723
|
+
toWrite: toWrite.length,
|
|
7724
|
+
skippedExisting: existing.size,
|
|
7725
|
+
trace
|
|
7726
|
+
});
|
|
7727
|
+
await Promise3.map(
|
|
7728
|
+
toWrite,
|
|
7729
|
+
async (config) => {
|
|
7730
|
+
const key = this.buildKey(config.hash);
|
|
7731
|
+
const metadata = config.file?.content_type ? { content_type: config.file.content_type } : void 0;
|
|
7732
|
+
await this.kv.put(key, config.file_data, metadata);
|
|
7733
|
+
result.written++;
|
|
7734
|
+
},
|
|
7735
|
+
{ concurrency: this.maxConcurrency }
|
|
7736
|
+
);
|
|
7737
|
+
}
|
|
7738
|
+
logger.info("[ThemeFileStorage] Put files complete", {
|
|
7739
|
+
written: result.written,
|
|
7740
|
+
skipped: result.skipped,
|
|
7741
|
+
skippedExisting: result.skippedExisting,
|
|
7742
|
+
warnings: result.warnings.length,
|
|
7743
|
+
trace
|
|
7369
7744
|
});
|
|
7745
|
+
return result;
|
|
7746
|
+
}
|
|
7747
|
+
};
|
|
7748
|
+
|
|
7749
|
+
// src/cache/constants.ts
|
|
7750
|
+
var SECOND = 1e3;
|
|
7751
|
+
var MINUTE = 60 * SECOND;
|
|
7752
|
+
var HOUR = 60 * MINUTE;
|
|
7753
|
+
var DAY = 24 * HOUR;
|
|
7754
|
+
var YEAR = 365 * DAY;
|
|
7755
|
+
var MAX_TTL = YEAR;
|
|
7756
|
+
var SHORT_TTL = 5 * SECOND;
|
|
7757
|
+
|
|
7758
|
+
// src/cache/worker-cache-proxy.ts
|
|
7759
|
+
var CACHE_NAME = "swell-cache-v1";
|
|
7760
|
+
var CACHE_KEY_ORIGIN = "https://cache.swell.store";
|
|
7761
|
+
var WorkerCacheProxy = class {
|
|
7762
|
+
swell;
|
|
7763
|
+
constructor(swell) {
|
|
7764
|
+
this.swell = swell;
|
|
7765
|
+
}
|
|
7766
|
+
/**
|
|
7767
|
+
* Reads a JSON value from Worker Cache using a key built from path+query.
|
|
7768
|
+
* Returns null on miss or if running outside of a Worker environment.
|
|
7769
|
+
*/
|
|
7770
|
+
async get(path, query, opts) {
|
|
7771
|
+
if (typeof caches === "undefined") {
|
|
7772
|
+
return null;
|
|
7773
|
+
}
|
|
7774
|
+
const { keyUrl } = await this.buildKeyUrl(path, query, opts?.version);
|
|
7775
|
+
try {
|
|
7776
|
+
const cache = await caches.open(CACHE_NAME);
|
|
7777
|
+
const match = await cache.match(keyUrl);
|
|
7778
|
+
if (!match) return null;
|
|
7779
|
+
const data = await match.json();
|
|
7780
|
+
return data;
|
|
7781
|
+
} catch {
|
|
7782
|
+
return null;
|
|
7783
|
+
}
|
|
7784
|
+
}
|
|
7785
|
+
/**
|
|
7786
|
+
* Stores a JSON value in Worker Cache under key built from path+query.
|
|
7787
|
+
* No-ops outside of a Worker environment.
|
|
7788
|
+
*/
|
|
7789
|
+
async put(path, query, value, opts) {
|
|
7790
|
+
if (typeof caches === "undefined") {
|
|
7791
|
+
return;
|
|
7792
|
+
}
|
|
7793
|
+
const { keyUrl, hasVersion } = await this.buildKeyUrl(
|
|
7794
|
+
path,
|
|
7795
|
+
query,
|
|
7796
|
+
opts?.version
|
|
7797
|
+
);
|
|
7798
|
+
const ttlMs = hasVersion ? MAX_TTL : SHORT_TTL;
|
|
7799
|
+
try {
|
|
7800
|
+
const cache = await caches.open(CACHE_NAME);
|
|
7801
|
+
const response = new Response(JSON.stringify(value), {
|
|
7802
|
+
headers: {
|
|
7803
|
+
"Content-Type": "application/json",
|
|
7804
|
+
"Cache-Control": `public, max-age=${Math.floor(ttlMs / 1e3)}`
|
|
7805
|
+
}
|
|
7806
|
+
});
|
|
7807
|
+
await cache.put(keyUrl, response);
|
|
7808
|
+
} catch {
|
|
7809
|
+
}
|
|
7810
|
+
}
|
|
7811
|
+
/**
|
|
7812
|
+
* Builds a deterministic key URL for Worker Cache from the backend API URL
|
|
7813
|
+
* composed using path and query. Includes tenant and auth isolation and an
|
|
7814
|
+
* optional version segment.
|
|
7815
|
+
*/
|
|
7816
|
+
async buildKeyUrl(path, query, explicitVersion) {
|
|
7817
|
+
const apiHost = this.swell.backend?.apiHost;
|
|
7818
|
+
const endpointPath = String(path).startsWith("/") ? String(path).substring(1) : String(path);
|
|
7819
|
+
let queryString = "";
|
|
7820
|
+
if (query && this.swell.backend) {
|
|
7821
|
+
queryString = this.swell.backend.stringifyQuery(query);
|
|
7822
|
+
}
|
|
7823
|
+
const fullUrl = `${apiHost}/${endpointPath}${queryString ? `?${queryString}` : ""}`;
|
|
7824
|
+
const instanceId = this.swell.instanceId || "";
|
|
7825
|
+
const authKey = String(this.swell.swellHeaders?.["swell-auth-key"] || "");
|
|
7826
|
+
const tenantHash = await this.sha256Hex(`${instanceId}|${authKey}`);
|
|
7827
|
+
const version = explicitVersion !== void 0 ? explicitVersion : this.swell.swellHeaders?.["theme-version-hash"] || null;
|
|
7828
|
+
const hasVersion = Boolean(version);
|
|
7829
|
+
const versionHash = hasVersion ? await this.sha256Hex(String(version)) : null;
|
|
7830
|
+
const urlHash = await this.sha256Hex(fullUrl);
|
|
7831
|
+
const keyUrl = versionHash ? `${CACHE_KEY_ORIGIN}/v1/${tenantHash}/${versionHash}/${urlHash}` : `${CACHE_KEY_ORIGIN}/v1/${tenantHash}/${urlHash}`;
|
|
7832
|
+
return { keyUrl, hasVersion };
|
|
7833
|
+
}
|
|
7834
|
+
/**
|
|
7835
|
+
* SHA-256 digest with hex encoding. Requires Worker crypto; callers
|
|
7836
|
+
* should avoid invoking this outside of Worker code paths.
|
|
7837
|
+
*/
|
|
7838
|
+
async sha256Hex(input) {
|
|
7839
|
+
if (typeof crypto !== "undefined" && crypto.subtle && crypto.subtle.digest) {
|
|
7840
|
+
const encoder = new TextEncoder();
|
|
7841
|
+
const digest = await crypto.subtle.digest(
|
|
7842
|
+
"SHA-256",
|
|
7843
|
+
encoder.encode(input)
|
|
7844
|
+
);
|
|
7845
|
+
const bytes = new Uint8Array(digest);
|
|
7846
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
7847
|
+
}
|
|
7848
|
+
return md5(input);
|
|
7849
|
+
}
|
|
7850
|
+
};
|
|
7851
|
+
|
|
7852
|
+
// src/resources/addresses.ts
|
|
7853
|
+
var SwellAddresses = class extends SwellStorefrontCollection {
|
|
7854
|
+
constructor(swell, query) {
|
|
7855
|
+
const { page, limit: limit2 } = swell.queryParams;
|
|
7856
|
+
super(swell, "accounts:addresses", { page, limit: limit2, ...query }, function() {
|
|
7857
|
+
return this._swell.storefront.account.listAddresses(
|
|
7858
|
+
this._query
|
|
7859
|
+
);
|
|
7860
|
+
});
|
|
7861
|
+
}
|
|
7862
|
+
};
|
|
7863
|
+
|
|
7864
|
+
// src/resources/orders.ts
|
|
7865
|
+
var SwellOrders = class extends SwellStorefrontCollection {
|
|
7866
|
+
constructor(swell, query) {
|
|
7867
|
+
const { page, limit: limit2 } = swell.queryParams;
|
|
7868
|
+
super(swell, "accounts:orders", { page, limit: limit2, ...query }, function() {
|
|
7869
|
+
return this._swell.storefront.account.listOrders(this._query);
|
|
7870
|
+
});
|
|
7871
|
+
}
|
|
7872
|
+
};
|
|
7873
|
+
|
|
7874
|
+
// src/resources/subscriptions.ts
|
|
7875
|
+
var SwellSubscriptions = class extends SwellStorefrontCollection {
|
|
7876
|
+
constructor(swell, query) {
|
|
7877
|
+
const { page, limit: limit2 } = swell.queryParams;
|
|
7878
|
+
super(
|
|
7879
|
+
swell,
|
|
7880
|
+
"accounts:subscriptions",
|
|
7881
|
+
{ page, limit: limit2, ...query },
|
|
7882
|
+
function() {
|
|
7883
|
+
return this._swell.storefront.subscriptions.list(
|
|
7884
|
+
this._query
|
|
7885
|
+
);
|
|
7886
|
+
}
|
|
7887
|
+
);
|
|
7370
7888
|
}
|
|
7371
7889
|
};
|
|
7372
7890
|
|
|
7373
7891
|
// src/resources/account.ts
|
|
7374
7892
|
var SwellAccount = class extends SwellStorefrontSingleton {
|
|
7375
|
-
constructor(swell
|
|
7376
|
-
super(swell, "account",
|
|
7893
|
+
constructor(swell) {
|
|
7894
|
+
super(swell, "account", async function() {
|
|
7895
|
+
const account = await this._defaultGetter().call(this);
|
|
7896
|
+
if (!account) {
|
|
7897
|
+
return null;
|
|
7898
|
+
}
|
|
7899
|
+
account.addresses = new SwellAddresses(
|
|
7900
|
+
this._swell
|
|
7901
|
+
);
|
|
7902
|
+
account.orders = new SwellOrders(
|
|
7903
|
+
this._swell
|
|
7904
|
+
);
|
|
7905
|
+
account.subscriptions = new SwellSubscriptions(
|
|
7906
|
+
this._swell
|
|
7907
|
+
);
|
|
7908
|
+
return account;
|
|
7909
|
+
});
|
|
7377
7910
|
return this._getProxy();
|
|
7378
7911
|
}
|
|
7379
7912
|
};
|
|
7380
7913
|
|
|
7381
7914
|
// src/resources/blog_category.ts
|
|
7382
7915
|
var SwellBlogCategory = class extends SwellStorefrontRecord {
|
|
7383
|
-
constructor(swell, id, query
|
|
7384
|
-
super(swell, "content/blog-categories", id, query,
|
|
7916
|
+
constructor(swell, id, query) {
|
|
7917
|
+
super(swell, "content/blog-categories", id, query, async function() {
|
|
7918
|
+
const category = await this._defaultGetter().call(this);
|
|
7919
|
+
if (!category) {
|
|
7920
|
+
return null;
|
|
7921
|
+
}
|
|
7922
|
+
category.blogs = new SwellStorefrontCollection(
|
|
7923
|
+
this._swell,
|
|
7924
|
+
"content/blogs",
|
|
7925
|
+
{
|
|
7926
|
+
category_id: category.id,
|
|
7927
|
+
expand: "author"
|
|
7928
|
+
}
|
|
7929
|
+
);
|
|
7930
|
+
return category;
|
|
7931
|
+
});
|
|
7385
7932
|
return this._getProxy();
|
|
7386
7933
|
}
|
|
7387
7934
|
};
|
|
7388
7935
|
|
|
7389
7936
|
// src/resources/blog.ts
|
|
7390
7937
|
var SwellBlog = class extends SwellStorefrontRecord {
|
|
7391
|
-
constructor(swell,
|
|
7392
|
-
super(swell, "content/blogs",
|
|
7938
|
+
constructor(swell, blogId, categoryId, query) {
|
|
7939
|
+
super(swell, "content/blogs", blogId, query, async function() {
|
|
7940
|
+
this._query = { ...this._query, expand: "author" };
|
|
7941
|
+
const blog = await this._defaultGetter().call(this);
|
|
7942
|
+
if (!blog) {
|
|
7943
|
+
return null;
|
|
7944
|
+
}
|
|
7945
|
+
if (categoryId) {
|
|
7946
|
+
blog.category = new SwellStorefrontRecord(
|
|
7947
|
+
this._swell,
|
|
7948
|
+
"content/blog-categories",
|
|
7949
|
+
categoryId
|
|
7950
|
+
);
|
|
7951
|
+
}
|
|
7952
|
+
return blog;
|
|
7953
|
+
});
|
|
7393
7954
|
return this._getProxy();
|
|
7394
7955
|
}
|
|
7395
7956
|
};
|
|
7396
7957
|
|
|
7397
7958
|
// src/resources/cart.ts
|
|
7398
7959
|
var SwellCart = class extends SwellStorefrontSingleton {
|
|
7399
|
-
constructor(swell
|
|
7400
|
-
super(swell, "cart"
|
|
7401
|
-
return this._getProxy();
|
|
7402
|
-
}
|
|
7403
|
-
};
|
|
7404
|
-
|
|
7405
|
-
// src/resources/category.ts
|
|
7406
|
-
var SwellCategory = class extends SwellStorefrontRecord {
|
|
7407
|
-
constructor(swell, id, query = {}, getter) {
|
|
7408
|
-
super(swell, "categories", id, query, getter);
|
|
7409
|
-
return this._getProxy();
|
|
7410
|
-
}
|
|
7411
|
-
};
|
|
7412
|
-
|
|
7413
|
-
// src/resources/order.ts
|
|
7414
|
-
var SwellOrder = class extends SwellStorefrontRecord {
|
|
7415
|
-
constructor(swell, id, query = {}, getter) {
|
|
7416
|
-
super(swell, "orders", id, query, getter);
|
|
7960
|
+
constructor(swell) {
|
|
7961
|
+
super(swell, "cart");
|
|
7417
7962
|
return this._getProxy();
|
|
7418
7963
|
}
|
|
7419
7964
|
};
|
|
7420
7965
|
|
|
7421
|
-
// src/resources/
|
|
7422
|
-
var
|
|
7423
|
-
constructor(swell,
|
|
7424
|
-
super(
|
|
7425
|
-
|
|
7966
|
+
// src/resources/categories.ts
|
|
7967
|
+
var SwellCategories = class extends SwellStorefrontCollection {
|
|
7968
|
+
constructor(swell, query) {
|
|
7969
|
+
super(
|
|
7970
|
+
swell,
|
|
7971
|
+
"categories",
|
|
7972
|
+
{
|
|
7973
|
+
limit: 100,
|
|
7974
|
+
top_id: null,
|
|
7975
|
+
...query
|
|
7976
|
+
},
|
|
7977
|
+
async function() {
|
|
7978
|
+
const categories = await this._defaultGetter().call(this);
|
|
7979
|
+
if (!categories) {
|
|
7980
|
+
return null;
|
|
7981
|
+
}
|
|
7982
|
+
for (const category of categories.results) {
|
|
7983
|
+
category.products = new SwellStorefrontCollection(
|
|
7984
|
+
this._swell,
|
|
7985
|
+
"products",
|
|
7986
|
+
{ category: category.id }
|
|
7987
|
+
);
|
|
7988
|
+
}
|
|
7989
|
+
return categories;
|
|
7990
|
+
}
|
|
7991
|
+
);
|
|
7426
7992
|
}
|
|
7427
7993
|
};
|
|
7428
7994
|
|
|
@@ -7573,15 +8139,152 @@ function getSelectedSubscriptionPurchaseOptionPlan(selectedPurchaseOptionType, s
|
|
|
7573
8139
|
if (selectedPurchaseOptionType !== "subscription") {
|
|
7574
8140
|
return null;
|
|
7575
8141
|
}
|
|
7576
|
-
const { purchase_option: purchaseOption } = queryParams;
|
|
7577
|
-
let selectedPlan = null;
|
|
7578
|
-
if (purchaseOption?.plan_id) {
|
|
7579
|
-
selectedPlan = subscriptionPurchaseOption.plans.find(
|
|
7580
|
-
(plan) => plan.id === purchaseOption.plan_id
|
|
7581
|
-
);
|
|
8142
|
+
const { purchase_option: purchaseOption } = queryParams;
|
|
8143
|
+
let selectedPlan = null;
|
|
8144
|
+
if (purchaseOption?.plan_id) {
|
|
8145
|
+
selectedPlan = subscriptionPurchaseOption.plans.find(
|
|
8146
|
+
(plan) => plan.id === purchaseOption.plan_id
|
|
8147
|
+
);
|
|
8148
|
+
}
|
|
8149
|
+
return selectedPlan || subscriptionPurchaseOption.plans[0];
|
|
8150
|
+
}
|
|
8151
|
+
var SORT_OPTIONS = Object.freeze([
|
|
8152
|
+
{ value: "", name: "Featured" },
|
|
8153
|
+
{ value: "popularity", name: "Popularity", query: "popularity desc" },
|
|
8154
|
+
{ value: "price_asc", name: "Price, low to high", query: "price asc" },
|
|
8155
|
+
{ value: "price_desc", name: "Price, high to low", query: "price desc" },
|
|
8156
|
+
{ value: "date_asc", name: "Date, old to new", query: "date asc" },
|
|
8157
|
+
{ value: "date_desc", name: "Date, new to old", query: "date desc" },
|
|
8158
|
+
{ value: "name_asc", name: "Product name, A-Z", query: "name asc" },
|
|
8159
|
+
{ value: "name_desc", name: "Product name, Z-A", query: "name desc" }
|
|
8160
|
+
]);
|
|
8161
|
+
async function getProductFilters(swell, productQuery) {
|
|
8162
|
+
const sortBy = swell.queryParams.sort || "";
|
|
8163
|
+
const filterQuery = productQueryWithFilters(swell, productQuery);
|
|
8164
|
+
return {
|
|
8165
|
+
filter_options: await getProductFiltersByQuery(swell, filterQuery),
|
|
8166
|
+
sort: SORT_OPTIONS.find((option) => option.value === sortBy)?.value,
|
|
8167
|
+
sort_options: [...SORT_OPTIONS]
|
|
8168
|
+
};
|
|
8169
|
+
}
|
|
8170
|
+
async function getProductFiltersByQuery(swell, query = {}) {
|
|
8171
|
+
const filters2 = await swell.get("/products/:filters", {
|
|
8172
|
+
...query,
|
|
8173
|
+
sort: void 0
|
|
8174
|
+
}) || [];
|
|
8175
|
+
if (!Array.isArray(filters2)) {
|
|
8176
|
+
throw new Error("Product filters must be an array");
|
|
8177
|
+
}
|
|
8178
|
+
for (const filter of filters2) {
|
|
8179
|
+
filter.param_name = `filter_${filter.id}`;
|
|
8180
|
+
if (Array.isArray(filter.options)) {
|
|
8181
|
+
filter.active_options = [];
|
|
8182
|
+
filter.inactive_options = [];
|
|
8183
|
+
for (const option of filter.options) {
|
|
8184
|
+
const queryValue = swell.queryParams[filter.param_name];
|
|
8185
|
+
option.active = Array.isArray(queryValue) ? queryValue.includes(option.value) : queryValue === option.value;
|
|
8186
|
+
const list = option.active ? filter.active_options : filter.inactive_options;
|
|
8187
|
+
list.push(option);
|
|
8188
|
+
}
|
|
8189
|
+
}
|
|
8190
|
+
}
|
|
8191
|
+
return filters2;
|
|
8192
|
+
}
|
|
8193
|
+
function productQueryWithFilters(swell, query) {
|
|
8194
|
+
const filters2 = Object.keys(swell.queryParams).reduce(
|
|
8195
|
+
(acc, key) => {
|
|
8196
|
+
if (key.startsWith("filter_")) {
|
|
8197
|
+
const qkey = key.replace("filter_", "");
|
|
8198
|
+
const value = swell.queryParams[key];
|
|
8199
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && (value.gte !== void 0 || value.lte !== void 0)) {
|
|
8200
|
+
acc[qkey] = [value.gte || 0, value.lte || void 0];
|
|
8201
|
+
} else {
|
|
8202
|
+
acc[qkey] = value;
|
|
8203
|
+
}
|
|
8204
|
+
}
|
|
8205
|
+
return acc;
|
|
8206
|
+
},
|
|
8207
|
+
{}
|
|
8208
|
+
);
|
|
8209
|
+
const sortBy = swell.queryParams.sort || "";
|
|
8210
|
+
return {
|
|
8211
|
+
sort: SORT_OPTIONS.find((option) => option.value === sortBy)?.query || void 0,
|
|
8212
|
+
$filters: filters2,
|
|
8213
|
+
...query
|
|
8214
|
+
};
|
|
8215
|
+
}
|
|
8216
|
+
|
|
8217
|
+
// src/resources/category.ts
|
|
8218
|
+
var SwellCategory = class extends SwellStorefrontRecord {
|
|
8219
|
+
constructor(swell, id, query) {
|
|
8220
|
+
super(swell, "categories", id, query, async function() {
|
|
8221
|
+
let category = await this._defaultGetter().call(this);
|
|
8222
|
+
if (!category && this._id === "all") {
|
|
8223
|
+
category = {
|
|
8224
|
+
name: "Products",
|
|
8225
|
+
id: "all",
|
|
8226
|
+
slug: "all",
|
|
8227
|
+
filter_options: [],
|
|
8228
|
+
sort_options: []
|
|
8229
|
+
};
|
|
8230
|
+
}
|
|
8231
|
+
if (!category) {
|
|
8232
|
+
return null;
|
|
8233
|
+
}
|
|
8234
|
+
const productFilters = await getProductFilters(
|
|
8235
|
+
this._swell,
|
|
8236
|
+
category.id !== "all" ? { category: category.id, $variants: true } : { $variants: true }
|
|
8237
|
+
);
|
|
8238
|
+
Object.assign(category, productFilters);
|
|
8239
|
+
return category;
|
|
8240
|
+
});
|
|
8241
|
+
return this._getProxy();
|
|
8242
|
+
}
|
|
8243
|
+
};
|
|
8244
|
+
|
|
8245
|
+
// src/resources/order.ts
|
|
8246
|
+
var SwellOrder = class extends SwellStorefrontRecord {
|
|
8247
|
+
constructor(swell, id, query) {
|
|
8248
|
+
super(swell, "accounts:orders", id, query, function() {
|
|
8249
|
+
return this._swell.storefront.account.getOrder(this._id);
|
|
8250
|
+
});
|
|
8251
|
+
return this._getProxy();
|
|
8252
|
+
}
|
|
8253
|
+
};
|
|
8254
|
+
|
|
8255
|
+
// src/resources/page.ts
|
|
8256
|
+
var SwellPage = class extends SwellStorefrontRecord {
|
|
8257
|
+
constructor(swell, id, query) {
|
|
8258
|
+
super(swell, "content/pages", id, query);
|
|
8259
|
+
return this._getProxy();
|
|
8260
|
+
}
|
|
8261
|
+
};
|
|
8262
|
+
|
|
8263
|
+
// src/resources/predictive_search.ts
|
|
8264
|
+
var SwellPredictiveSearch = class extends StorefrontResource {
|
|
8265
|
+
constructor(swell, query) {
|
|
8266
|
+
super(async function() {
|
|
8267
|
+
const performed = String(query || "").length > 0;
|
|
8268
|
+
let products;
|
|
8269
|
+
if (performed) {
|
|
8270
|
+
products = new SwellStorefrontCollection(
|
|
8271
|
+
swell,
|
|
8272
|
+
"products",
|
|
8273
|
+
{
|
|
8274
|
+
search: query,
|
|
8275
|
+
limit: 10
|
|
8276
|
+
}
|
|
8277
|
+
);
|
|
8278
|
+
await products.resolve();
|
|
8279
|
+
}
|
|
8280
|
+
return {
|
|
8281
|
+
query,
|
|
8282
|
+
performed,
|
|
8283
|
+
products
|
|
8284
|
+
};
|
|
8285
|
+
});
|
|
7582
8286
|
}
|
|
7583
|
-
|
|
7584
|
-
}
|
|
8287
|
+
};
|
|
7585
8288
|
|
|
7586
8289
|
// src/resources/variant.ts
|
|
7587
8290
|
function transformSwellVariant(params, product, variant) {
|
|
@@ -7601,27 +8304,30 @@ function transformSwellVariant(params, product, variant) {
|
|
|
7601
8304
|
)
|
|
7602
8305
|
};
|
|
7603
8306
|
}
|
|
8307
|
+
var SwellVariant = class extends SwellStorefrontRecord {
|
|
8308
|
+
product;
|
|
8309
|
+
constructor(swell, product, id, query) {
|
|
8310
|
+
super(swell, "products:variants", id, query, async function() {
|
|
8311
|
+
const variant = await this._swell.get(
|
|
8312
|
+
"/products:variants/{id}",
|
|
8313
|
+
{ id: this._id }
|
|
8314
|
+
);
|
|
8315
|
+
return variant ?? null;
|
|
8316
|
+
});
|
|
8317
|
+
this.product = product;
|
|
8318
|
+
}
|
|
8319
|
+
};
|
|
7604
8320
|
|
|
7605
8321
|
// src/resources/product.ts
|
|
7606
|
-
var SORT_OPTIONS = [
|
|
7607
|
-
{ value: "", name: "Featured" },
|
|
7608
|
-
{ value: "popularity", name: "Popularity", query: "popularity desc" },
|
|
7609
|
-
{ value: "price_asc", name: "Price, low to high", query: "price asc" },
|
|
7610
|
-
{ value: "price_desc", name: "Price, high to low", query: "price desc" },
|
|
7611
|
-
{ value: "date_asc", name: "Date, old to new", query: "date asc" },
|
|
7612
|
-
{ value: "date_desc", name: "Date, new to old", query: "date desc" },
|
|
7613
|
-
{ value: "name_asc", name: "Product name, A-Z", query: "name asc" },
|
|
7614
|
-
{ value: "name_desc", name: "Product name, Z-A", query: "name desc" }
|
|
7615
|
-
];
|
|
7616
8322
|
function transformSwellProduct(params, product) {
|
|
7617
8323
|
if (!product) {
|
|
7618
|
-
return
|
|
8324
|
+
return null;
|
|
7619
8325
|
}
|
|
7620
8326
|
const newProduct = {
|
|
7621
8327
|
...product,
|
|
7622
8328
|
// add swell properties there
|
|
7623
8329
|
selected_option_values: getSelectedVariantOptionValues(product, params),
|
|
7624
|
-
purchase_options: getPurchaseOptions(product, params)
|
|
8330
|
+
purchase_options: getPurchaseOptions(product, params) ?? void 0
|
|
7625
8331
|
};
|
|
7626
8332
|
if (Array.isArray(newProduct.variants?.results)) {
|
|
7627
8333
|
newProduct.variants = {
|
|
@@ -7634,43 +8340,49 @@ function transformSwellProduct(params, product) {
|
|
|
7634
8340
|
return newProduct;
|
|
7635
8341
|
}
|
|
7636
8342
|
var SwellProduct = class extends SwellStorefrontRecord {
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
super(swell, "products", id, query,
|
|
7640
|
-
|
|
8343
|
+
constructor(swell, id, query) {
|
|
8344
|
+
const params = swell.queryParams;
|
|
8345
|
+
super(swell, "products", id, query, async function() {
|
|
8346
|
+
const result = await this._defaultGetter().call(this);
|
|
8347
|
+
return transformSwellProduct(params, result);
|
|
8348
|
+
});
|
|
7641
8349
|
return this._getProxy();
|
|
7642
8350
|
}
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
);
|
|
7649
|
-
|
|
8351
|
+
};
|
|
8352
|
+
|
|
8353
|
+
// src/resources/product_recommendations.ts
|
|
8354
|
+
var SwellProductRecommendations = class extends SwellProduct {
|
|
8355
|
+
constructor(swell, id, query) {
|
|
8356
|
+
super(swell, id, { ...query, $recommendations: true });
|
|
8357
|
+
}
|
|
8358
|
+
};
|
|
8359
|
+
|
|
8360
|
+
// src/resources/search.ts
|
|
8361
|
+
var SwellSearch = class extends StorefrontResource {
|
|
8362
|
+
constructor(swell, query) {
|
|
8363
|
+
super(async () => {
|
|
8364
|
+
const performed = String(query || "").length > 0;
|
|
8365
|
+
const productFilters = await getProductFilters(
|
|
8366
|
+
swell,
|
|
8367
|
+
performed ? { search: query } : void 0
|
|
8368
|
+
);
|
|
8369
|
+
return {
|
|
8370
|
+
query,
|
|
8371
|
+
performed,
|
|
8372
|
+
...productFilters
|
|
8373
|
+
};
|
|
8374
|
+
});
|
|
8375
|
+
}
|
|
8376
|
+
};
|
|
8377
|
+
|
|
8378
|
+
// src/resources/subscription.ts
|
|
8379
|
+
var SwellSubscription = class extends SwellStorefrontRecord {
|
|
8380
|
+
constructor(swell, id, query) {
|
|
8381
|
+
super(swell, "accounts:subscriptions", id, query, function() {
|
|
8382
|
+
return this._swell.storefront.subscriptions.get(this._id, this._query);
|
|
8383
|
+
});
|
|
7650
8384
|
}
|
|
7651
8385
|
};
|
|
7652
|
-
function productQueryWithFilters(swell, query = {}) {
|
|
7653
|
-
const sortBy = swell.queryParams.sort || "";
|
|
7654
|
-
const filters2 = Object.entries(swell.queryParams).reduce(
|
|
7655
|
-
(acc, [key, value]) => {
|
|
7656
|
-
if (key.startsWith("filter_")) {
|
|
7657
|
-
const qkey = key.replace("filter_", "");
|
|
7658
|
-
if (value?.gte !== void 0 || value?.lte !== void 0) {
|
|
7659
|
-
acc[qkey] = [value.gte || 0, value.lte || void 0];
|
|
7660
|
-
} else {
|
|
7661
|
-
acc[qkey] = value;
|
|
7662
|
-
}
|
|
7663
|
-
}
|
|
7664
|
-
return acc;
|
|
7665
|
-
},
|
|
7666
|
-
{}
|
|
7667
|
-
);
|
|
7668
|
-
return {
|
|
7669
|
-
sort: SORT_OPTIONS.find((option) => option.value === sortBy)?.query || void 0,
|
|
7670
|
-
$filters: filters2,
|
|
7671
|
-
...query
|
|
7672
|
-
};
|
|
7673
|
-
}
|
|
7674
8386
|
|
|
7675
8387
|
// src/api.ts
|
|
7676
8388
|
var DEFAULT_API_HOST = "https://api.schema.io";
|
|
@@ -7731,7 +8443,7 @@ var Swell = class _Swell {
|
|
|
7731
8443
|
this.workerEnv = workerEnv;
|
|
7732
8444
|
this.resourceLoadingIndicator = params.resourceLoadingIndicator;
|
|
7733
8445
|
logger.info(
|
|
7734
|
-
`[SDK] KV cache: ${this.workerEnv?.THEME ? "enabled" : "disabled"}`
|
|
8446
|
+
`[SDK] KV cache: ${this.workerEnv?.THEME ? "enabled" : "disabled"}, flavor: ${getKVFlavor(this.workerEnv)}`
|
|
7735
8447
|
);
|
|
7736
8448
|
if (serverHeaders) {
|
|
7737
8449
|
const { headers: headers2, swellHeaders: swellHeaders2 } = _Swell.formatHeaders(serverHeaders);
|
|
@@ -15241,11 +15953,9 @@ function getProducts(instance, object, mapper) {
|
|
|
15241
15953
|
return this._defaultGetter().call(this);
|
|
15242
15954
|
}
|
|
15243
15955
|
);
|
|
15244
|
-
return products._cloneWithCompatibilityResult(
|
|
15245
|
-
(
|
|
15246
|
-
|
|
15247
|
-
}
|
|
15248
|
-
);
|
|
15956
|
+
return products._cloneWithCompatibilityResult((products2) => {
|
|
15957
|
+
return { ...products2, results: products2.results.map(mapper) };
|
|
15958
|
+
});
|
|
15249
15959
|
});
|
|
15250
15960
|
}
|
|
15251
15961
|
function makeProductsCollectionResolve(instance, object, mapper) {
|
|
@@ -15262,19 +15972,24 @@ function makeProductsCollectionResolve(instance, object, mapper) {
|
|
|
15262
15972
|
|
|
15263
15973
|
// src/compatibility/shopify-objects/collections.ts
|
|
15264
15974
|
function ShopifyCollections(instance, categories) {
|
|
15265
|
-
return new SwellStorefrontCollection(
|
|
15266
|
-
|
|
15267
|
-
|
|
15268
|
-
|
|
15269
|
-
|
|
15270
|
-
|
|
15271
|
-
|
|
15272
|
-
|
|
15273
|
-
|
|
15274
|
-
|
|
15275
|
-
|
|
15276
|
-
|
|
15277
|
-
|
|
15975
|
+
return new SwellStorefrontCollection(
|
|
15976
|
+
instance.swell,
|
|
15977
|
+
categories._collection,
|
|
15978
|
+
categories._query,
|
|
15979
|
+
async () => {
|
|
15980
|
+
const results = (await categories.results)?.map((category) => {
|
|
15981
|
+
return ShopifyCollection(instance, category);
|
|
15982
|
+
});
|
|
15983
|
+
return {
|
|
15984
|
+
page: categories.page ?? 1,
|
|
15985
|
+
count: categories.count ?? 0,
|
|
15986
|
+
results: results ?? [],
|
|
15987
|
+
page_count: categories.page_count ?? 0,
|
|
15988
|
+
limit: categories.limit,
|
|
15989
|
+
pages: categories.pages ?? {}
|
|
15990
|
+
};
|
|
15991
|
+
}
|
|
15992
|
+
);
|
|
15278
15993
|
}
|
|
15279
15994
|
|
|
15280
15995
|
// src/compatibility/shopify-objects/address.ts
|
|
@@ -16461,13 +17176,12 @@ var ImagesDrop = class extends import_liquidjs7.Drop {
|
|
|
16461
17176
|
var SwellImage = class extends StorefrontResource {
|
|
16462
17177
|
constructor(swell, name) {
|
|
16463
17178
|
super(async () => {
|
|
16464
|
-
const
|
|
17179
|
+
const file = await swell.get("/:files/:last", {
|
|
16465
17180
|
private: { $ne: true },
|
|
16466
17181
|
content_type: { $regex: "^image/" },
|
|
16467
17182
|
filename: name
|
|
16468
17183
|
});
|
|
16469
|
-
|
|
16470
|
-
if (file === null) {
|
|
17184
|
+
if (!file) {
|
|
16471
17185
|
return null;
|
|
16472
17186
|
}
|
|
16473
17187
|
return { file };
|
|
@@ -19382,296 +20096,255 @@ function getLiquidFS(getThemeConfig, extName) {
|
|
|
19382
20096
|
}
|
|
19383
20097
|
|
|
19384
20098
|
// src/theme/theme-loader.ts
|
|
19385
|
-
var import_bluebird = __toESM(require("bluebird"), 1);
|
|
19386
|
-
var { Promise: Promise2 } = import_bluebird.default;
|
|
19387
20099
|
var MAX_INDIVIDUAL_CONFIGS_TO_FETCH = 50;
|
|
19388
|
-
var ThemeLoader = class
|
|
19389
|
-
static cache = null;
|
|
20100
|
+
var ThemeLoader = class {
|
|
19390
20101
|
swell;
|
|
19391
|
-
manifest;
|
|
19392
20102
|
configs;
|
|
19393
|
-
configPaths;
|
|
19394
20103
|
constructor(swell) {
|
|
19395
20104
|
this.swell = swell;
|
|
19396
|
-
this.manifest = null;
|
|
19397
20105
|
this.configs = /* @__PURE__ */ new Map();
|
|
19398
|
-
this.configPaths = [];
|
|
19399
20106
|
}
|
|
20107
|
+
/**
|
|
20108
|
+
* Initialize the theme loader with all configurations.
|
|
20109
|
+
* Either uses provided configs (editor mode) or loads from storage.
|
|
20110
|
+
*/
|
|
19400
20111
|
async init(themeConfigs) {
|
|
19401
20112
|
if (themeConfigs) {
|
|
19402
20113
|
this.setConfigs(themeConfigs);
|
|
19403
20114
|
return;
|
|
19404
20115
|
}
|
|
19405
20116
|
if (!this.getThemeId()) {
|
|
20117
|
+
logger.debug("[ThemeLoader] No theme ID, skipping init");
|
|
19406
20118
|
return;
|
|
19407
20119
|
}
|
|
19408
|
-
await this.
|
|
19409
|
-
|
|
19410
|
-
|
|
19411
|
-
|
|
19412
|
-
}
|
|
20120
|
+
await this.loadAllConfigs();
|
|
20121
|
+
logger.info("[ThemeLoader] Initialization complete", {
|
|
20122
|
+
configCount: this.configs.size,
|
|
20123
|
+
themeId: this.getThemeId()
|
|
20124
|
+
});
|
|
19413
20125
|
}
|
|
19414
20126
|
/**
|
|
19415
|
-
*
|
|
20127
|
+
* Get a single config by file path (synchronous).
|
|
20128
|
+
* Returns null if config not found.
|
|
19416
20129
|
*/
|
|
19417
|
-
|
|
19418
|
-
|
|
19419
|
-
console.log("ThemeLoader.loadTheme", swellHeaders["theme-version-hash"]);
|
|
19420
|
-
if (swellHeaders["theme-version-hash"]) {
|
|
19421
|
-
const configs = await this.loadThemeFromManifest();
|
|
19422
|
-
if (configs) {
|
|
19423
|
-
return configs;
|
|
19424
|
-
}
|
|
19425
|
-
}
|
|
19426
|
-
return this.loadThemeAllConfigs();
|
|
20130
|
+
getConfig(filePath) {
|
|
20131
|
+
return this.configs.get(filePath) ?? null;
|
|
19427
20132
|
}
|
|
19428
20133
|
/**
|
|
19429
|
-
*
|
|
20134
|
+
* Get all loaded configs.
|
|
20135
|
+
* Used by theme getter to expose configs to editor/tests.
|
|
19430
20136
|
*/
|
|
19431
|
-
|
|
19432
|
-
|
|
19433
|
-
_ThemeLoader.cache = new ThemeCache({
|
|
19434
|
-
kvStore: this.swell.workerEnv?.THEME
|
|
19435
|
-
});
|
|
19436
|
-
}
|
|
19437
|
-
return _ThemeLoader.cache;
|
|
20137
|
+
getConfigs() {
|
|
20138
|
+
return this.configs;
|
|
19438
20139
|
}
|
|
19439
20140
|
/**
|
|
19440
|
-
*
|
|
20141
|
+
* Get multiple configs by path pattern (synchronous).
|
|
20142
|
+
* Filters configs by prefix and optional suffix.
|
|
19441
20143
|
*/
|
|
19442
|
-
|
|
19443
|
-
|
|
19444
|
-
|
|
19445
|
-
|
|
19446
|
-
|
|
20144
|
+
getConfigsByPath(pathPrefix, pathSuffix) {
|
|
20145
|
+
const results = [];
|
|
20146
|
+
for (const [path, config] of this.configs) {
|
|
20147
|
+
if (path.startsWith(pathPrefix) && (!pathSuffix || path.endsWith(pathSuffix))) {
|
|
20148
|
+
results.push(config);
|
|
20149
|
+
}
|
|
19447
20150
|
}
|
|
19448
|
-
|
|
20151
|
+
return results;
|
|
19449
20152
|
}
|
|
19450
20153
|
/**
|
|
19451
|
-
*
|
|
20154
|
+
* Load theme configs from internal data, typically in the editor.
|
|
20155
|
+
* Used when configs are provided externally (e.g., from editor).
|
|
19452
20156
|
*/
|
|
19453
|
-
|
|
19454
|
-
|
|
19455
|
-
console.log(
|
|
19456
|
-
`ThemeLoader.preloadTheme${version?.hash ? ` - manifest: ${version.hash}` : ""}${configs?.length ? ` - configs: ${configs.length}` : ""}`
|
|
19457
|
-
);
|
|
19458
|
-
const promises = [];
|
|
19459
|
-
if (version) {
|
|
19460
|
-
promises.push(this.cacheManifest(version));
|
|
19461
|
-
}
|
|
19462
|
-
if (configs) {
|
|
19463
|
-
const themeId = this.getThemeId();
|
|
19464
|
-
promises.push(
|
|
19465
|
-
Promise2.map(
|
|
19466
|
-
configs,
|
|
19467
|
-
async (config) => {
|
|
19468
|
-
const promises2 = [
|
|
19469
|
-
this.cacheThemeConfig(config)
|
|
19470
|
-
];
|
|
19471
|
-
if (themeId && config.file?.url) {
|
|
19472
|
-
promises2.push(
|
|
19473
|
-
this.cacheThemeFileUrl(themeId, config.hash, config.file.url)
|
|
19474
|
-
);
|
|
19475
|
-
}
|
|
19476
|
-
await Promise2.all(promises2);
|
|
19477
|
-
},
|
|
19478
|
-
{ concurrency: 10 }
|
|
19479
|
-
)
|
|
19480
|
-
);
|
|
19481
|
-
}
|
|
19482
|
-
await Promise2.all(promises);
|
|
20157
|
+
setConfigs(themeConfigs) {
|
|
20158
|
+
this.configs = new Map(themeConfigs);
|
|
19483
20159
|
}
|
|
19484
20160
|
/**
|
|
19485
|
-
*
|
|
20161
|
+
* Updates KV with file_data for provided theme configs (warmup path).
|
|
20162
|
+
* Uses the new ThemeFileStorage abstraction for optimized operations.
|
|
19486
20163
|
*/
|
|
19487
|
-
async
|
|
19488
|
-
const
|
|
19489
|
-
if (
|
|
19490
|
-
|
|
19491
|
-
|
|
19492
|
-
|
|
19493
|
-
|
|
19494
|
-
|
|
20164
|
+
async updateThemeCache(payload) {
|
|
20165
|
+
const configs = payload?.configs || [];
|
|
20166
|
+
if (configs.length === 0) {
|
|
20167
|
+
logger.debug("[ThemeLoader] No configs to cache");
|
|
20168
|
+
return {
|
|
20169
|
+
written: 0,
|
|
20170
|
+
skipped: 0,
|
|
20171
|
+
skippedExisting: 0,
|
|
20172
|
+
warnings: []
|
|
20173
|
+
};
|
|
19495
20174
|
}
|
|
19496
|
-
const
|
|
19497
|
-
const
|
|
19498
|
-
|
|
19499
|
-
|
|
19500
|
-
|
|
19501
|
-
|
|
19502
|
-
|
|
19503
|
-
|
|
19504
|
-
|
|
19505
|
-
|
|
19506
|
-
|
|
19507
|
-
|
|
19508
|
-
|
|
19509
|
-
|
|
19510
|
-
|
|
19511
|
-
|
|
20175
|
+
const flavor = getKVFlavor(this.swell.workerEnv);
|
|
20176
|
+
const trace = createTraceId();
|
|
20177
|
+
logger.info("[ThemeLoader] Starting theme cache update", {
|
|
20178
|
+
totalConfigs: configs.length,
|
|
20179
|
+
flavor,
|
|
20180
|
+
trace
|
|
20181
|
+
});
|
|
20182
|
+
const storage = new ThemeFileStorage(this.swell.workerEnv, flavor);
|
|
20183
|
+
const result = await storage.putFiles(configs);
|
|
20184
|
+
if (result.warnings.length > 0) {
|
|
20185
|
+
logger.warn("[ThemeLoader] Theme cache updated with warnings", {
|
|
20186
|
+
total: configs.length,
|
|
20187
|
+
written: result.written,
|
|
20188
|
+
skipped: result.skipped,
|
|
20189
|
+
skippedExisting: result.skippedExisting,
|
|
20190
|
+
warnings: result.warnings.length,
|
|
20191
|
+
trace
|
|
20192
|
+
});
|
|
20193
|
+
} else {
|
|
20194
|
+
logger.info("[ThemeLoader] Theme cache updated successfully", {
|
|
20195
|
+
total: configs.length,
|
|
20196
|
+
written: result.written,
|
|
20197
|
+
skipped: result.skipped,
|
|
20198
|
+
skippedExisting: result.skippedExisting,
|
|
20199
|
+
trace
|
|
20200
|
+
});
|
|
19512
20201
|
}
|
|
19513
|
-
return
|
|
19514
|
-
}
|
|
19515
|
-
async fetchThemeConfigsByPath(pathPrefix, pathSuffix) {
|
|
19516
|
-
const paths = this.configPaths.filter(
|
|
19517
|
-
(path) => path.startsWith(pathPrefix) && (!pathSuffix || path.endsWith(pathSuffix))
|
|
19518
|
-
);
|
|
19519
|
-
const configs = await Promise2.map(
|
|
19520
|
-
paths,
|
|
19521
|
-
(path) => this.fetchThemeConfig(path),
|
|
19522
|
-
{ concurrency: 10 }
|
|
19523
|
-
);
|
|
19524
|
-
return configs.filter((config) => config !== null);
|
|
20202
|
+
return result;
|
|
19525
20203
|
}
|
|
19526
20204
|
/**
|
|
19527
|
-
*
|
|
20205
|
+
* Main loading logic - loads all configs at once.
|
|
20206
|
+
* 1. Fetches lightweight metadata (cached when possible)
|
|
20207
|
+
* 2. Batch hydrates file_data from KV
|
|
20208
|
+
* 3. Fetches missing file_data from API if needed
|
|
19528
20209
|
*/
|
|
19529
|
-
async
|
|
19530
|
-
const
|
|
19531
|
-
|
|
19532
|
-
|
|
19533
|
-
|
|
20210
|
+
async loadAllConfigs() {
|
|
20211
|
+
const configMetadata = await this.fetchConfigMetadata();
|
|
20212
|
+
if (configMetadata.length === 0) {
|
|
20213
|
+
logger.warn("[ThemeLoader] No configs found");
|
|
20214
|
+
return;
|
|
19534
20215
|
}
|
|
19535
|
-
|
|
19536
|
-
|
|
19537
|
-
|
|
19538
|
-
);
|
|
19539
|
-
|
|
20216
|
+
logger.debug("[ThemeLoader] Loading configs", {
|
|
20217
|
+
total: configMetadata.length
|
|
20218
|
+
});
|
|
20219
|
+
const flavor = getKVFlavor(this.swell.workerEnv);
|
|
20220
|
+
const storage = new ThemeFileStorage(this.swell.workerEnv, flavor);
|
|
20221
|
+
const kvHydrated = await storage.getFiles(configMetadata);
|
|
20222
|
+
const completeConfigs = await this.ensureConfigsHaveData(kvHydrated);
|
|
20223
|
+
for (const config of completeConfigs) {
|
|
20224
|
+
this.configs.set(config.file_path, config);
|
|
20225
|
+
}
|
|
20226
|
+
logger.info("[ThemeLoader] All configs loaded", {
|
|
20227
|
+
total: completeConfigs.length,
|
|
20228
|
+
withData: completeConfigs.filter((c) => c.file_data).length
|
|
20229
|
+
});
|
|
19540
20230
|
}
|
|
19541
20231
|
/**
|
|
19542
|
-
*
|
|
19543
|
-
*
|
|
19544
|
-
* This approach has the following optimizations:
|
|
19545
|
-
* - cached manifests and configs can be shared by other clients
|
|
19546
|
-
* - when fetching from source, only fetch the missing records
|
|
20232
|
+
* Fetch lightweight config metadata from API or cache.
|
|
20233
|
+
* Does NOT include file_data to minimize payload size.
|
|
19547
20234
|
*/
|
|
19548
|
-
async
|
|
19549
|
-
const
|
|
19550
|
-
|
|
19551
|
-
|
|
19552
|
-
|
|
19553
|
-
|
|
19554
|
-
|
|
19555
|
-
|
|
19556
|
-
|
|
19557
|
-
|
|
19558
|
-
|
|
19559
|
-
|
|
19560
|
-
|
|
19561
|
-
|
|
19562
|
-
|
|
19563
|
-
|
|
19564
|
-
if (!themeConfig) {
|
|
19565
|
-
configHashesUnresolved.push(configHash);
|
|
19566
|
-
return;
|
|
19567
|
-
}
|
|
19568
|
-
let config = themeConfig;
|
|
19569
|
-
if (fileUrl && themeConfig.file?.url) {
|
|
19570
|
-
config = {
|
|
19571
|
-
...themeConfig,
|
|
19572
|
-
file: { ...themeConfig.file, url: fileUrl }
|
|
19573
|
-
};
|
|
20235
|
+
async fetchConfigMetadata() {
|
|
20236
|
+
const query = {
|
|
20237
|
+
...this.themeVersionQueryFilter(),
|
|
20238
|
+
limit: 1e3,
|
|
20239
|
+
type: "theme",
|
|
20240
|
+
fields: "id, name, type, file, file_path, hash"
|
|
20241
|
+
// NO file_data
|
|
20242
|
+
};
|
|
20243
|
+
try {
|
|
20244
|
+
const cache = new WorkerCacheProxy(this.swell);
|
|
20245
|
+
const versionHash = this.swell.swellHeaders["theme-version-hash"];
|
|
20246
|
+
const cached = await cache.get(
|
|
20247
|
+
"/:themes:configs",
|
|
20248
|
+
query,
|
|
20249
|
+
{
|
|
20250
|
+
version: versionHash || null
|
|
19574
20251
|
}
|
|
19575
|
-
configsByHash.set(config.hash, config);
|
|
19576
|
-
this.configs.set(config.file_path, config);
|
|
19577
|
-
},
|
|
19578
|
-
{ concurrency: 10 }
|
|
19579
|
-
);
|
|
19580
|
-
if (configHashesUnresolved.length > 0) {
|
|
19581
|
-
const configs = await this.fetchThemeConfigsFromSource(
|
|
19582
|
-
// If no configs were resolved, then fetch them all. otherwise fetch
|
|
19583
|
-
// the specific subset of configs.
|
|
19584
|
-
configsByHash.size === 0 ? void 0 : configHashesUnresolved
|
|
19585
20252
|
);
|
|
19586
|
-
|
|
19587
|
-
|
|
19588
|
-
|
|
19589
|
-
this.configs.set(config.file_path, config);
|
|
20253
|
+
if (cached) {
|
|
20254
|
+
logger.debug("[ThemeLoader] Config metadata cache hit");
|
|
20255
|
+
return cached;
|
|
19590
20256
|
}
|
|
19591
|
-
|
|
19592
|
-
|
|
19593
|
-
async (config) => {
|
|
19594
|
-
const promises = [this.cacheThemeConfig(config)];
|
|
19595
|
-
if (themeId && config.file?.url) {
|
|
19596
|
-
promises.push(
|
|
19597
|
-
this.cacheThemeFileUrl(themeId, config.hash, config.file.url)
|
|
19598
|
-
);
|
|
19599
|
-
}
|
|
19600
|
-
await Promise2.all(promises);
|
|
19601
|
-
},
|
|
19602
|
-
{ concurrency: 10 }
|
|
19603
|
-
);
|
|
20257
|
+
} catch (err) {
|
|
20258
|
+
logger.warn("[ThemeLoader] Cache read failed, fetching from API", err);
|
|
19604
20259
|
}
|
|
19605
|
-
|
|
19606
|
-
|
|
19607
|
-
|
|
19608
|
-
|
|
19609
|
-
|
|
19610
|
-
|
|
19611
|
-
|
|
19612
|
-
|
|
20260
|
+
logger.debug("[ThemeLoader] Fetching config metadata from API");
|
|
20261
|
+
const response = await this.swell.get(
|
|
20262
|
+
"/:themes:configs",
|
|
20263
|
+
query
|
|
20264
|
+
);
|
|
20265
|
+
const configs = response?.results || [];
|
|
20266
|
+
try {
|
|
20267
|
+
const cache = new WorkerCacheProxy(this.swell);
|
|
20268
|
+
const versionHash = this.swell.swellHeaders["theme-version-hash"];
|
|
20269
|
+
await cache.put("/:themes:configs", query, configs, {
|
|
20270
|
+
version: versionHash || null
|
|
20271
|
+
});
|
|
20272
|
+
} catch (err) {
|
|
20273
|
+
logger.warn("[ThemeLoader] Cache write failed", err);
|
|
19613
20274
|
}
|
|
20275
|
+
return configs;
|
|
19614
20276
|
}
|
|
19615
20277
|
/**
|
|
19616
|
-
*
|
|
20278
|
+
* Helper to ensure all configs have file_data.
|
|
20279
|
+
* Fetches missing data from API and updates KV cache.
|
|
19617
20280
|
*/
|
|
19618
|
-
async
|
|
19619
|
-
|
|
19620
|
-
|
|
20281
|
+
async ensureConfigsHaveData(configs) {
|
|
20282
|
+
const missingData = configs.filter((c) => !c.file_data);
|
|
20283
|
+
if (missingData.length === 0) {
|
|
20284
|
+
logger.debug("[ThemeLoader] All configs have file_data from KV");
|
|
20285
|
+
return configs;
|
|
19621
20286
|
}
|
|
19622
|
-
|
|
19623
|
-
|
|
19624
|
-
|
|
19625
|
-
|
|
19626
|
-
|
|
19627
|
-
|
|
19628
|
-
}
|
|
19629
|
-
/**
|
|
19630
|
-
* Fetches the manifest (set of config hashes) for a theme version.
|
|
19631
|
-
*/
|
|
19632
|
-
async fetchManifest() {
|
|
19633
|
-
const { swellHeaders } = this.swell;
|
|
19634
|
-
const versionHash = swellHeaders["theme-version-hash"];
|
|
19635
|
-
console.log("ThemeLoader.fetchManifest", versionHash);
|
|
19636
|
-
let manifest = await this.getCache().get(
|
|
19637
|
-
`manifest:${versionHash}`
|
|
20287
|
+
const trace = createTraceId();
|
|
20288
|
+
logger.info(
|
|
20289
|
+
`[ThemeLoader] Loading ${missingData.length} missing file_data from API`,
|
|
20290
|
+
{
|
|
20291
|
+
trace
|
|
20292
|
+
}
|
|
19638
20293
|
);
|
|
19639
|
-
|
|
19640
|
-
|
|
19641
|
-
|
|
19642
|
-
|
|
19643
|
-
|
|
19644
|
-
|
|
19645
|
-
|
|
19646
|
-
|
|
19647
|
-
|
|
19648
|
-
|
|
19649
|
-
|
|
20294
|
+
const hashes = missingData.map((c) => c.hash);
|
|
20295
|
+
const apiResponse = await this.fetchThemeConfigsFromSource(hashes);
|
|
20296
|
+
const fetched = apiResponse.results || [];
|
|
20297
|
+
logger.info(`[ThemeLoader] Fetched ${fetched.length} configs from API`, {
|
|
20298
|
+
trace
|
|
20299
|
+
});
|
|
20300
|
+
if (fetched.length > 0) {
|
|
20301
|
+
const cacheResult = await this.updateThemeCache({
|
|
20302
|
+
api: 1,
|
|
20303
|
+
// Required by SwellThemePreload type
|
|
20304
|
+
configs: fetched
|
|
20305
|
+
});
|
|
20306
|
+
if (cacheResult.warnings.length > 0) {
|
|
20307
|
+
logger.warn("[ThemeLoader] Some files had size issues", {
|
|
20308
|
+
warnings: cacheResult.warnings.length
|
|
20309
|
+
});
|
|
19650
20310
|
}
|
|
19651
20311
|
}
|
|
19652
|
-
|
|
19653
|
-
|
|
19654
|
-
|
|
20312
|
+
const fetchedMap = new Map(fetched.map((c) => [c.hash, c]));
|
|
20313
|
+
const mergedConfigs = configs.map((config) => {
|
|
20314
|
+
if (!config.file_data) {
|
|
20315
|
+
const withData = fetchedMap.get(config.hash);
|
|
20316
|
+
if (withData?.file_data) {
|
|
20317
|
+
return { ...config, file_data: withData.file_data };
|
|
20318
|
+
}
|
|
20319
|
+
}
|
|
20320
|
+
return config;
|
|
20321
|
+
});
|
|
20322
|
+
const stillMissing = mergedConfigs.filter((c) => !c.file_data).length;
|
|
20323
|
+
if (stillMissing > 0) {
|
|
20324
|
+
logger.warn(
|
|
20325
|
+
`[ThemeLoader] ${stillMissing} configs still missing file_data after fetch`
|
|
20326
|
+
);
|
|
19655
20327
|
}
|
|
19656
|
-
return
|
|
20328
|
+
return mergedConfigs;
|
|
19657
20329
|
}
|
|
19658
20330
|
/**
|
|
19659
|
-
* Fetches
|
|
20331
|
+
* Fetches theme configs with file_data from Swell Backend API.
|
|
20332
|
+
* Used to retrieve missing file_data for configs.
|
|
19660
20333
|
*/
|
|
19661
20334
|
async fetchThemeConfigsFromSource(configHashes = void 0) {
|
|
19662
20335
|
configHashes = configHashes || [];
|
|
19663
20336
|
const { swellHeaders } = this.swell;
|
|
19664
20337
|
const version = String(swellHeaders["theme-config-version"]);
|
|
19665
20338
|
const fetchAll = configHashes.length === 0 || configHashes.length > MAX_INDIVIDUAL_CONFIGS_TO_FETCH;
|
|
19666
|
-
|
|
19667
|
-
`
|
|
20339
|
+
logger.debug(
|
|
20340
|
+
`[ThemeLoader] Fetching ${fetchAll ? "all" : configHashes.length} configs with file_data`,
|
|
20341
|
+
{ version }
|
|
19668
20342
|
);
|
|
19669
20343
|
const configs = await this.swell.get(
|
|
19670
20344
|
"/:themes:configs",
|
|
19671
20345
|
{
|
|
19672
20346
|
...this.themeVersionQueryFilter(),
|
|
19673
20347
|
...fetchAll ? void 0 : { hash: { $in: configHashes } },
|
|
19674
|
-
// TODO: paginate to support more than 1000 configs
|
|
19675
20348
|
limit: 1e3,
|
|
19676
20349
|
type: "theme",
|
|
19677
20350
|
fields: "name, file, file_path, hash",
|
|
@@ -19683,45 +20356,13 @@ var ThemeLoader = class _ThemeLoader {
|
|
|
19683
20356
|
return configs;
|
|
19684
20357
|
}
|
|
19685
20358
|
/**
|
|
19686
|
-
*
|
|
19687
|
-
* This is used when a hash entry cannot be found.
|
|
19688
|
-
* We may override the cached hash in order to ensure it is found on reload,
|
|
19689
|
-
* but we probably need to find why that happens in the first place (TODO).
|
|
20359
|
+
* Get the current theme ID from headers.
|
|
19690
20360
|
*/
|
|
19691
|
-
async fetchThemeConfigsFromSourceByPath(filePath, hash) {
|
|
19692
|
-
console.log(`Retrieving theme config - ${filePath}`);
|
|
19693
|
-
const config = await this.swell.get(
|
|
19694
|
-
"/:themes:configs/:last",
|
|
19695
|
-
{
|
|
19696
|
-
...this.themeVersionQueryFilter(),
|
|
19697
|
-
file_path: filePath,
|
|
19698
|
-
fields: "name, file, file_path, hash",
|
|
19699
|
-
include: {
|
|
19700
|
-
file_data: FILE_DATA_INCLUDE_QUERY
|
|
19701
|
-
}
|
|
19702
|
-
}
|
|
19703
|
-
);
|
|
19704
|
-
if (config) {
|
|
19705
|
-
this.configs.set(filePath, config);
|
|
19706
|
-
if (hash) {
|
|
19707
|
-
config.hash = hash;
|
|
19708
|
-
}
|
|
19709
|
-
const themeId = this.getThemeId();
|
|
19710
|
-
const promises = [this.cacheThemeConfig(config)];
|
|
19711
|
-
if (themeId && config.file?.url) {
|
|
19712
|
-
promises.push(
|
|
19713
|
-
this.cacheThemeFileUrl(themeId, config.hash, config.file.url)
|
|
19714
|
-
);
|
|
19715
|
-
}
|
|
19716
|
-
await Promise2.all(promises);
|
|
19717
|
-
}
|
|
19718
|
-
return config ?? null;
|
|
19719
|
-
}
|
|
19720
20361
|
getThemeId() {
|
|
19721
20362
|
return this.swell.swellHeaders["theme-id"];
|
|
19722
20363
|
}
|
|
19723
20364
|
/**
|
|
19724
|
-
*
|
|
20365
|
+
* Generate a Swell API query filter for this theme version.
|
|
19725
20366
|
*/
|
|
19726
20367
|
themeVersionQueryFilter() {
|
|
19727
20368
|
const { swellHeaders } = this.swell;
|
|
@@ -19901,7 +20542,6 @@ var SwellTheme3 = class {
|
|
|
19901
20542
|
resources;
|
|
19902
20543
|
liquidSwell;
|
|
19903
20544
|
themeLoader;
|
|
19904
|
-
themeConfigs = null;
|
|
19905
20545
|
page;
|
|
19906
20546
|
pageId;
|
|
19907
20547
|
shopifyCompatibility = null;
|
|
@@ -19935,6 +20575,23 @@ var SwellTheme3 = class {
|
|
|
19935
20575
|
});
|
|
19936
20576
|
this.themeLoader = new ThemeLoader(swell);
|
|
19937
20577
|
}
|
|
20578
|
+
/**
|
|
20579
|
+
* Getter for theme configs - returns the configs from the loader.
|
|
20580
|
+
* Used by editor and tests to access loaded configs.
|
|
20581
|
+
*/
|
|
20582
|
+
get themeConfigs() {
|
|
20583
|
+
const configs = this.themeLoader.getConfigs();
|
|
20584
|
+
return configs.size > 0 ? configs : null;
|
|
20585
|
+
}
|
|
20586
|
+
/**
|
|
20587
|
+
* Setter for theme configs - directly sets configs in the loader.
|
|
20588
|
+
* Used by editor and tests to inject configs without API/KV loading.
|
|
20589
|
+
*/
|
|
20590
|
+
set themeConfigs(configs) {
|
|
20591
|
+
if (configs) {
|
|
20592
|
+
this.themeLoader.setConfigs(configs);
|
|
20593
|
+
}
|
|
20594
|
+
}
|
|
19938
20595
|
getSwellAppThemeProps(swellConfig) {
|
|
19939
20596
|
return swellConfig?.storefront?.theme || {};
|
|
19940
20597
|
}
|
|
@@ -20008,10 +20665,8 @@ var SwellTheme3 = class {
|
|
|
20008
20665
|
}
|
|
20009
20666
|
async getSettingsAndConfigs() {
|
|
20010
20667
|
const geo = GEO_DATA;
|
|
20011
|
-
const
|
|
20012
|
-
|
|
20013
|
-
this.getThemeConfigsByPath("theme/config/", ".json")
|
|
20014
|
-
]);
|
|
20668
|
+
const storefrontSettings = await this.swell.getStorefrontSettings();
|
|
20669
|
+
const settingConfigs = this.getThemeConfigsByPath("theme/config/", ".json");
|
|
20015
20670
|
const configs = {
|
|
20016
20671
|
theme: {},
|
|
20017
20672
|
editor: {},
|
|
@@ -20106,7 +20761,7 @@ var SwellTheme3 = class {
|
|
|
20106
20761
|
$locale: void 0
|
|
20107
20762
|
};
|
|
20108
20763
|
if (pageId) {
|
|
20109
|
-
const templateConfig =
|
|
20764
|
+
const templateConfig = this._getTemplateConfigByType(
|
|
20110
20765
|
"templates",
|
|
20111
20766
|
pageId,
|
|
20112
20767
|
altTemplate
|
|
@@ -20401,7 +21056,7 @@ var SwellTheme3 = class {
|
|
|
20401
21056
|
this.shopifyCompatibility.adaptPageData(pageData);
|
|
20402
21057
|
}
|
|
20403
21058
|
async getLocaleConfig(localeCode = "en", suffix = ".json") {
|
|
20404
|
-
const allLocaleConfigs =
|
|
21059
|
+
const allLocaleConfigs = this.getThemeConfigsByPath(
|
|
20405
21060
|
"theme/locales/",
|
|
20406
21061
|
suffix
|
|
20407
21062
|
);
|
|
@@ -20505,22 +21160,24 @@ var SwellTheme3 = class {
|
|
|
20505
21160
|
}
|
|
20506
21161
|
return resolvedUrl;
|
|
20507
21162
|
}
|
|
20508
|
-
async getAllThemeConfigs() {
|
|
20509
|
-
if (this.themeConfigs === null) {
|
|
20510
|
-
const configs = await this.themeLoader.loadTheme();
|
|
20511
|
-
const configsByPath = /* @__PURE__ */ new Map();
|
|
20512
|
-
for (const config of configs) {
|
|
20513
|
-
configsByPath.set(config.file_path, config);
|
|
20514
|
-
}
|
|
20515
|
-
this.themeConfigs = configsByPath;
|
|
20516
|
-
}
|
|
20517
|
-
return this.themeConfigs;
|
|
20518
|
-
}
|
|
20519
|
-
/**
|
|
20520
|
-
* Preloads updated theme configs. Used to optimize initial theme load.
|
|
20521
|
-
*/
|
|
20522
21163
|
async preloadThemeConfigs(payload) {
|
|
20523
|
-
await this.themeLoader.
|
|
21164
|
+
const result = await this.themeLoader.updateThemeCache(payload);
|
|
21165
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
21166
|
+
const rejected = result.warnings.filter(
|
|
21167
|
+
(w) => w.reason === "rejected_5mb" || w.reason === "exceeded_25mb"
|
|
21168
|
+
).length;
|
|
21169
|
+
const warned = result.warnings.filter(
|
|
21170
|
+
(w) => w.reason === "warning_1mb"
|
|
21171
|
+
).length;
|
|
21172
|
+
logger.warn("[Theme] File size issues detected during cache update", {
|
|
21173
|
+
totalWarnings: result.warnings.length,
|
|
21174
|
+
rejected,
|
|
21175
|
+
warned,
|
|
21176
|
+
details: result.warnings.slice(0, 5)
|
|
21177
|
+
// Log first 5 warnings for debugging
|
|
21178
|
+
});
|
|
21179
|
+
}
|
|
21180
|
+
return result;
|
|
20524
21181
|
}
|
|
20525
21182
|
getPageConfigPath(pageId, altTemplate) {
|
|
20526
21183
|
if (this.shopifyCompatibility) {
|
|
@@ -20533,16 +21190,10 @@ var SwellTheme3 = class {
|
|
|
20533
21190
|
return `${withSuffix(`theme/templates/${pageId}`, altTemplate)}.json`;
|
|
20534
21191
|
}
|
|
20535
21192
|
async getThemeConfig(filePath) {
|
|
20536
|
-
|
|
20537
|
-
return this.themeConfigs.get(filePath) ?? null;
|
|
20538
|
-
}
|
|
20539
|
-
return this.themeLoader.fetchThemeConfig(filePath);
|
|
21193
|
+
return this.themeLoader.getConfig(filePath);
|
|
20540
21194
|
}
|
|
20541
|
-
|
|
20542
|
-
const configs =
|
|
20543
|
-
pathPrefix,
|
|
20544
|
-
pathSuffix
|
|
20545
|
-
);
|
|
21195
|
+
getThemeConfigsByPath(pathPrefix, pathSuffix) {
|
|
21196
|
+
const configs = this.themeLoader.getConfigsByPath(pathPrefix, pathSuffix);
|
|
20546
21197
|
const configsByPath = /* @__PURE__ */ new Map();
|
|
20547
21198
|
for (const config of configs) {
|
|
20548
21199
|
configsByPath.set(config.file_path, config);
|
|
@@ -20550,31 +21201,42 @@ var SwellTheme3 = class {
|
|
|
20550
21201
|
return configsByPath;
|
|
20551
21202
|
}
|
|
20552
21203
|
async getThemeTemplateConfig(filePath) {
|
|
20553
|
-
|
|
20554
|
-
return this.getThemeConfig(filePath);
|
|
20555
|
-
}
|
|
20556
|
-
const jsonTemplate = await this.getThemeConfig(`${filePath}.json`);
|
|
20557
|
-
if (jsonTemplate) {
|
|
20558
|
-
return jsonTemplate;
|
|
20559
|
-
}
|
|
20560
|
-
return this.getThemeConfig(`${filePath}.liquid`);
|
|
21204
|
+
return this._getTemplateConfig(filePath);
|
|
20561
21205
|
}
|
|
20562
|
-
|
|
21206
|
+
/**
|
|
21207
|
+
* Internal synchronous helper for getting template configs by type.
|
|
21208
|
+
* Used internally within theme.ts to avoid async overhead.
|
|
21209
|
+
*/
|
|
21210
|
+
_getTemplateConfigByType(type, name, suffix) {
|
|
20563
21211
|
const templatesByPriority = [withSuffix(`${type}/${name}`, suffix)];
|
|
20564
21212
|
if (this.shopifyCompatibility) {
|
|
20565
21213
|
const path = this.shopifyCompatibility.getThemeFilePath(type, name);
|
|
20566
21214
|
templatesByPriority.push(withSuffix(path, suffix));
|
|
20567
21215
|
}
|
|
20568
21216
|
for (const filePath of templatesByPriority) {
|
|
20569
|
-
const templateConfig =
|
|
20570
|
-
`theme/${filePath}`
|
|
20571
|
-
);
|
|
21217
|
+
const templateConfig = this._getTemplateConfig(`theme/${filePath}`);
|
|
20572
21218
|
if (templateConfig) {
|
|
20573
21219
|
return templateConfig;
|
|
20574
21220
|
}
|
|
20575
21221
|
}
|
|
20576
21222
|
return null;
|
|
20577
21223
|
}
|
|
21224
|
+
/**
|
|
21225
|
+
* Internal synchronous helper for getting template configs.
|
|
21226
|
+
*/
|
|
21227
|
+
_getTemplateConfig(filePath) {
|
|
21228
|
+
if (filePath.endsWith(".json") || filePath.endsWith(".liquid")) {
|
|
21229
|
+
return this.themeLoader.getConfig(filePath);
|
|
21230
|
+
}
|
|
21231
|
+
const jsonTemplate = this.themeLoader.getConfig(`${filePath}.json`);
|
|
21232
|
+
if (jsonTemplate) {
|
|
21233
|
+
return jsonTemplate;
|
|
21234
|
+
}
|
|
21235
|
+
return this.themeLoader.getConfig(`${filePath}.liquid`);
|
|
21236
|
+
}
|
|
21237
|
+
async getThemeTemplateConfigByType(type, name, suffix) {
|
|
21238
|
+
return this._getTemplateConfigByType(type, name, suffix);
|
|
21239
|
+
}
|
|
20578
21240
|
async getAssetConfig(assetName) {
|
|
20579
21241
|
return await this.getThemeConfig(`theme/assets/${assetName}`) ?? await this.getThemeConfig(`assets/${assetName}`) ?? null;
|
|
20580
21242
|
}
|
|
@@ -20596,17 +21258,8 @@ var SwellTheme3 = class {
|
|
|
20596
21258
|
return "";
|
|
20597
21259
|
}
|
|
20598
21260
|
template = unescapeLiquidSyntax(template);
|
|
20599
|
-
const trace = createTraceId();
|
|
20600
21261
|
try {
|
|
20601
|
-
logger.debug("[SDK] Render template start", {
|
|
20602
|
-
config: config.name,
|
|
20603
|
-
trace
|
|
20604
|
-
});
|
|
20605
21262
|
const result = await this.liquidSwell.parseAndRender(template, data);
|
|
20606
|
-
logger.debug("[SDK] Render template end", {
|
|
20607
|
-
config: config.name,
|
|
20608
|
-
trace
|
|
20609
|
-
});
|
|
20610
21263
|
return result;
|
|
20611
21264
|
} catch (err) {
|
|
20612
21265
|
logger.error(err);
|
|
@@ -20623,10 +21276,7 @@ var SwellTheme3 = class {
|
|
|
20623
21276
|
}
|
|
20624
21277
|
async getSectionSchema(sectionName) {
|
|
20625
21278
|
let result;
|
|
20626
|
-
const config =
|
|
20627
|
-
"sections",
|
|
20628
|
-
sectionName
|
|
20629
|
-
);
|
|
21279
|
+
const config = this._getTemplateConfigByType("sections", sectionName);
|
|
20630
21280
|
if (config?.file_path?.endsWith(".json")) {
|
|
20631
21281
|
try {
|
|
20632
21282
|
result = import_json56.default.parse(config.file_data) || void 0;
|
|
@@ -20704,10 +21354,7 @@ var SwellTheme3 = class {
|
|
|
20704
21354
|
return content;
|
|
20705
21355
|
}
|
|
20706
21356
|
async renderLayoutTemplate(name, data) {
|
|
20707
|
-
const templateConfig =
|
|
20708
|
-
"layouts",
|
|
20709
|
-
name
|
|
20710
|
-
);
|
|
21357
|
+
const templateConfig = this._getTemplateConfigByType("layouts", name);
|
|
20711
21358
|
if (!templateConfig) {
|
|
20712
21359
|
throw new Error(`Layout template not found: ${name}`);
|
|
20713
21360
|
}
|
|
@@ -20742,17 +21389,14 @@ ${content.slice(pos)}`;
|
|
|
20742
21389
|
async renderPageTemplate(name, data, altTemplateId) {
|
|
20743
21390
|
let templateConfig = null;
|
|
20744
21391
|
if (altTemplateId) {
|
|
20745
|
-
templateConfig =
|
|
21392
|
+
templateConfig = this._getTemplateConfigByType(
|
|
20746
21393
|
"templates",
|
|
20747
21394
|
name,
|
|
20748
21395
|
altTemplateId
|
|
20749
21396
|
);
|
|
20750
21397
|
}
|
|
20751
21398
|
if (!templateConfig) {
|
|
20752
|
-
templateConfig =
|
|
20753
|
-
"templates",
|
|
20754
|
-
name
|
|
20755
|
-
);
|
|
21399
|
+
templateConfig = this._getTemplateConfigByType("templates", name);
|
|
20756
21400
|
}
|
|
20757
21401
|
if (templateConfig) {
|
|
20758
21402
|
const templatePath = name.split("/").splice(1).join("/") || null;
|
|
@@ -20853,7 +21497,7 @@ ${content.slice(pos)}`;
|
|
|
20853
21497
|
}
|
|
20854
21498
|
const [sectionKey, originalPageId] = sectionId.split(/__/).reverse();
|
|
20855
21499
|
const pageId = (originalPageId || "").replaceAll("_", "/");
|
|
20856
|
-
const templateConfig =
|
|
21500
|
+
const templateConfig = this._getTemplateConfigByType(
|
|
20857
21501
|
pageId ? "templates" : "sections",
|
|
20858
21502
|
pageId ? pageId : sectionKey
|
|
20859
21503
|
);
|
|
@@ -21028,7 +21672,7 @@ ${this.shopifyCompatibility.getContentForHeader()}`;
|
|
|
21028
21672
|
return defaults;
|
|
21029
21673
|
}
|
|
21030
21674
|
async getAllSections() {
|
|
21031
|
-
const configs =
|
|
21675
|
+
const configs = this.getThemeConfigsByPath("theme/sections/");
|
|
21032
21676
|
return getAllSections(configs, this.getTemplateSchema.bind(this));
|
|
21033
21677
|
}
|
|
21034
21678
|
async getPageSections(sectionGroup, resolveSettings = true) {
|
|
@@ -21055,7 +21699,7 @@ ${this.shopifyCompatibility.getContentForHeader()}`;
|
|
|
21055
21699
|
const sectionName = sectionSchema?.label || sectionFileName;
|
|
21056
21700
|
let sourcePath = "";
|
|
21057
21701
|
if (group) {
|
|
21058
|
-
const sectionConfig =
|
|
21702
|
+
const sectionConfig = this._getTemplateConfigByType(
|
|
21059
21703
|
"sections",
|
|
21060
21704
|
`${sectionFileName}.json`
|
|
21061
21705
|
);
|
|
@@ -21072,7 +21716,7 @@ ${this.shopifyCompatibility.getContentForHeader()}`;
|
|
|
21072
21716
|
* Get a list of sections and section groups in a page layout.
|
|
21073
21717
|
*/
|
|
21074
21718
|
async getPageSectionGroups(pageId, altTemplate) {
|
|
21075
|
-
const pageConfig =
|
|
21719
|
+
const pageConfig = this._getTemplateConfigByType(
|
|
21076
21720
|
"templates",
|
|
21077
21721
|
pageId,
|
|
21078
21722
|
altTemplate
|
|
@@ -21139,7 +21783,7 @@ ${this.shopifyCompatibility.getContentForHeader()}`;
|
|
|
21139
21783
|
sectionConfigs.map(async (sectionConfig, index) => {
|
|
21140
21784
|
const { section, schema } = sectionConfig;
|
|
21141
21785
|
const settings = schema?.fields && this.globals ? resolveSectionSettings(this, sectionConfig, index) : { ...sectionConfig.settings };
|
|
21142
|
-
const templateConfig =
|
|
21786
|
+
const templateConfig = this._getTemplateConfigByType(
|
|
21143
21787
|
"sections",
|
|
21144
21788
|
`${section.type}.liquid`
|
|
21145
21789
|
);
|
|
@@ -21832,21 +22476,30 @@ function getResourceQuery(slug, query) {
|
|
|
21832
22476
|
StorefrontResource,
|
|
21833
22477
|
Swell,
|
|
21834
22478
|
SwellAccount,
|
|
22479
|
+
SwellAddresses,
|
|
21835
22480
|
SwellBackendAPI,
|
|
21836
22481
|
SwellBlog,
|
|
21837
22482
|
SwellBlogCategory,
|
|
21838
22483
|
SwellCart,
|
|
22484
|
+
SwellCategories,
|
|
21839
22485
|
SwellCategory,
|
|
21840
22486
|
SwellError,
|
|
21841
22487
|
SwellOrder,
|
|
22488
|
+
SwellOrders,
|
|
21842
22489
|
SwellPage,
|
|
22490
|
+
SwellPredictiveSearch,
|
|
21843
22491
|
SwellProduct,
|
|
22492
|
+
SwellProductRecommendations,
|
|
22493
|
+
SwellSearch,
|
|
21844
22494
|
SwellStorefrontCollection,
|
|
21845
22495
|
SwellStorefrontPagination,
|
|
21846
22496
|
SwellStorefrontRecord,
|
|
21847
22497
|
SwellStorefrontResource,
|
|
21848
22498
|
SwellStorefrontSingleton,
|
|
22499
|
+
SwellSubscription,
|
|
22500
|
+
SwellSubscriptions,
|
|
21849
22501
|
SwellTheme,
|
|
22502
|
+
SwellVariant,
|
|
21850
22503
|
ThemeColor,
|
|
21851
22504
|
ThemeFont,
|
|
21852
22505
|
ThemeForm,
|
|
@@ -21878,6 +22531,7 @@ function getResourceQuery(slug, query) {
|
|
|
21878
22531
|
getEasyblocksComponentDefinitions,
|
|
21879
22532
|
getEasyblocksPagePropsWithConfigs,
|
|
21880
22533
|
getEasyblocksPageTemplate,
|
|
22534
|
+
getKVFlavor,
|
|
21881
22535
|
getLayoutSectionGroups,
|
|
21882
22536
|
getMenuItemStorefrontUrl,
|
|
21883
22537
|
getMenuItemUrlAndResource,
|
|
@@ -21893,6 +22547,7 @@ function getResourceQuery(slug, query) {
|
|
|
21893
22547
|
isObject,
|
|
21894
22548
|
md5,
|
|
21895
22549
|
removeCircularReferences,
|
|
22550
|
+
resetKVFlavorCache,
|
|
21896
22551
|
resolveAsyncResources,
|
|
21897
22552
|
resolveLookupCollection,
|
|
21898
22553
|
resolveMenuItemUrlAndResource,
|