@swell/apps-sdk 1.0.138 → 1.0.140

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 CHANGED
@@ -149,31 +149,44 @@ var import_cache_manager = require("cache-manager");
149
149
 
150
150
  // src/cache/cf-worker-kv-keyv-adapter.ts
151
151
  var CFWorkerKVKeyvAdapter = class {
152
+ store;
152
153
  namespace;
153
154
  // magically passed in from Keyv
154
- store;
155
+ opts;
155
156
  constructor(store) {
156
157
  this.store = store;
158
+ this.opts = null;
157
159
  this.namespace = "dummy";
158
160
  }
159
161
  async has(key) {
160
- return this.get(key) !== void 0;
162
+ const stream = await this.store.get(key, "stream");
163
+ if (stream !== null) {
164
+ await stream.cancel();
165
+ return true;
166
+ }
167
+ return false;
161
168
  }
162
169
  async get(key) {
163
- return this.store.get(key);
170
+ const value = await this.store.get(key);
171
+ return value !== null ? value : void 0;
164
172
  }
165
- async set(key, value) {
166
- return this.store.put(key, value);
173
+ set(key, value, ttl) {
174
+ if (typeof ttl === "number") {
175
+ ttl = Math.max(60, ttl / 1e3);
176
+ }
177
+ return this.store.put(key, value, { expirationTtl: ttl });
167
178
  }
168
179
  async delete(key) {
169
180
  await this.store.delete(key);
181
+ return true;
170
182
  }
171
183
  async clear() {
172
184
  let cursor = "";
173
185
  let complete = false;
174
- while (!complete) {
186
+ const prefix = `${this.namespace}:`;
187
+ do {
175
188
  const response = await this.store.list({
176
- prefix: `${this.namespace}:`,
189
+ prefix,
177
190
  cursor: cursor || void 0
178
191
  });
179
192
  cursor = response.cursor ?? "";
@@ -185,7 +198,10 @@ var CFWorkerKVKeyvAdapter = class {
185
198
  })
186
199
  );
187
200
  }
188
- }
201
+ } while (!complete);
202
+ }
203
+ on(_event, _listener) {
204
+ return this;
189
205
  }
190
206
  };
191
207
 
@@ -7089,23 +7105,25 @@ var Cache = class {
7089
7105
  ...options
7090
7106
  });
7091
7107
  }
7092
- async fetch(key, fetchFn) {
7093
- return this.client.wrap(key, fetchFn);
7108
+ async fetch(key, fetchFn, ttl) {
7109
+ return this.client.wrap(key, fetchFn, ttl);
7094
7110
  }
7095
- // Fetch cache using SWR (stale-while-revalidate)
7096
- // This will always return the cached value immediately if exists
7111
+ /**
7112
+ * Fetch cache using SWR (stale-while-revalidate)
7113
+ *
7114
+ * This will always return the cached value immediately if exists
7115
+ */
7097
7116
  async fetchSWR(key, fetchFn, ttl = DEFAULT_SWR_TTL) {
7098
7117
  const cacheValue = await this.client.get(key);
7099
- const promiseValue = Promise.resolve().then(() => fetchFn()).then(async (value) => {
7118
+ const promiseValue = Promise.resolve().then(fetchFn).then(resolveAsyncResources).then(async (value) => {
7100
7119
  const isNull = value === null || value === void 0;
7101
- const valueResolved = await resolveAsyncResources(value);
7102
- await this.client.set(key, isNull ? NULL_VALUE : valueResolved, ttl);
7120
+ await this.client.set(key, isNull ? NULL_VALUE : value, ttl);
7103
7121
  return value;
7104
7122
  });
7105
7123
  if (this.workerCtx?.waitUntil) {
7106
7124
  this.workerCtx.waitUntil(promiseValue);
7107
7125
  }
7108
- if (cacheValue !== null) {
7126
+ if (cacheValue !== void 0) {
7109
7127
  return cacheValue === NULL_VALUE ? null : cacheValue;
7110
7128
  }
7111
7129
  const result = await promiseValue;
@@ -7122,8 +7140,9 @@ var Cache = class {
7122
7140
  }
7123
7141
  /**
7124
7142
  * Flushes the entire cache.
7125
- * WARNING: If the cache store is shared among many cache clients,
7126
- * this will flush entries for other clients.
7143
+ *
7144
+ * __WARNING__: If the cache store is shared among many cache clients,
7145
+ * this will flush entries for other clients.
7127
7146
  */
7128
7147
  async flushAll() {
7129
7148
  await this.client.clear();
@@ -7166,7 +7185,7 @@ var ResourceCache = class extends Cache {
7166
7185
  function buildStores2() {
7167
7186
  return [
7168
7187
  new import_keyv2.Keyv({
7169
- // Disabling serialization allows for pure memo-ization of class instances
7188
+ // Disabling serialization allows for pure memoization of class instances
7170
7189
  // at the tradeoff of no support for compression.
7171
7190
  serialize: void 0,
7172
7191
  deserialize: void 0
@@ -18236,10 +18255,10 @@ var svgs = {
18236
18255
  src: "https://cdn.swell.store/schema/685ef581b79e3e0012cf3222/fba884c7fe8122c472e130355fb52db7/collection-6.png"
18237
18256
  },
18238
18257
  "lifestyle-1": {
18239
- src: "https://cdn.swell.store/schema/685ef58bb79e3e0012cf3256/1da0da57ea80d90038fcf6b100a16fe6/lifestyle-1.png"
18258
+ src: "https://cdn.swell.store/schema/6870eddc1657130012643fc6/1777bcff40736610093833ef858b5936/lifestyle-1.png"
18240
18259
  },
18241
18260
  "lifestyle-2": {
18242
- src: "https://cdn.swell.store/schema/685ef58cb79e3e0012cf3259/f963448f1a6f4d6588fcd283b5c00c7c/lifestyle-2.png"
18261
+ src: "https://cdn.swell.store/schema/6870eddc1657130012643fcb/4d29197e3fa0b6d8255c1b7770f8dc12/lifestyle-2.png"
18243
18262
  },
18244
18263
  "product-apparel-1": {
18245
18264
  src: "https://cdn.swell.store/schema/685ef58eb79e3e0012cf3276/37c3c973a01287a580ab9db2e05dd36d/product-apparel-1.png"
@@ -18837,12 +18856,11 @@ var ThemeLoader = class _ThemeLoader {
18837
18856
  this.configPaths = [];
18838
18857
  }
18839
18858
  async init(themeConfigs) {
18840
- const { swellHeaders } = this.swell;
18841
18859
  if (themeConfigs) {
18842
18860
  this.setConfigs(themeConfigs);
18843
18861
  return;
18844
18862
  }
18845
- if (!swellHeaders["theme-id"]) {
18863
+ if (!this.getThemeId()) {
18846
18864
  return;
18847
18865
  }
18848
18866
  await this.fetchManifest();
@@ -18881,25 +18899,45 @@ var ThemeLoader = class _ThemeLoader {
18881
18899
  */
18882
18900
  setConfigs(themeConfigs) {
18883
18901
  this.configs = new Map(themeConfigs);
18902
+ this.manifest = /* @__PURE__ */ new Map();
18903
+ for (const { file_path, hash } of this.configs.values()) {
18904
+ this.manifest.set(file_path, hash);
18905
+ }
18884
18906
  this.configPaths = Array.from(this.configs.keys());
18885
- this.manifest = this.configPaths.reduce((manifest, path) => {
18886
- manifest[path] = this.configs.get(path)?.hash;
18887
- return manifest;
18888
- }, {});
18889
18907
  }
18890
18908
  /**
18891
18909
  * Preloads a theme version and configs. This is used to optimize initial theme load.
18892
18910
  */
18893
- async preloadTheme(version, configs) {
18911
+ async preloadTheme(payload) {
18912
+ const { version, configs } = payload;
18894
18913
  console.log(
18895
18914
  `ThemeLoader.preloadTheme${version?.hash ? ` - manifest: ${version.hash}` : ""}${configs?.length ? ` - configs: ${configs.length}` : ""}`
18896
18915
  );
18897
- await Promise2.all([
18898
- version && this.cacheManifest(version),
18899
- configs && Promise2.map(configs, (config) => this.cacheThemeConfig(config), {
18900
- concurrency: 10
18901
- })
18902
- ]);
18916
+ const promises = [];
18917
+ if (version) {
18918
+ promises.push(this.cacheManifest(version));
18919
+ }
18920
+ if (configs) {
18921
+ const themeId = this.getThemeId();
18922
+ promises.push(
18923
+ Promise2.map(
18924
+ configs,
18925
+ async (config) => {
18926
+ const promises2 = [
18927
+ this.cacheThemeConfig(config)
18928
+ ];
18929
+ if (themeId && config.file?.url) {
18930
+ promises2.push(
18931
+ this.cacheThemeFileUrl(themeId, config.hash, config.file.url)
18932
+ );
18933
+ }
18934
+ await Promise2.all(promises2);
18935
+ },
18936
+ { concurrency: 10 }
18937
+ )
18938
+ );
18939
+ }
18940
+ await Promise2.all(promises);
18903
18941
  }
18904
18942
  /**
18905
18943
  * Fetches a theme config by file path.
@@ -18909,18 +18947,28 @@ var ThemeLoader = class _ThemeLoader {
18909
18947
  if (config !== void 0) {
18910
18948
  return config;
18911
18949
  }
18912
- const hash = this.manifest?.[filePath];
18913
- if (hash) {
18914
- const config2 = await this.getCache().get(
18915
- `config:${hash}`
18916
- );
18917
- if (config2) {
18918
- this.configs.set(filePath, config2);
18919
- return config2;
18950
+ const hash = this.manifest?.get(filePath);
18951
+ if (!hash) {
18952
+ return null;
18953
+ }
18954
+ const cache = this.getCache();
18955
+ const themeId = this.getThemeId();
18956
+ const [themeConfig, fileUrl] = await Promise2.all([
18957
+ cache.get(`config:${hash}`),
18958
+ themeId ? cache.get(`file:${themeId}:${hash}`) : void 0
18959
+ ]);
18960
+ if (themeConfig) {
18961
+ let config2 = themeConfig;
18962
+ if (fileUrl && themeConfig.file?.url) {
18963
+ config2 = {
18964
+ ...themeConfig,
18965
+ file: { ...themeConfig.file, url: fileUrl }
18966
+ };
18920
18967
  }
18921
- return this.fetchThemeConfigsFromSourceByPath(filePath, hash);
18968
+ this.configs.set(filePath, config2);
18969
+ return config2;
18922
18970
  }
18923
- return null;
18971
+ return this.fetchThemeConfigsFromSourceByPath(filePath, hash);
18924
18972
  }
18925
18973
  async fetchThemeConfigsByPath(pathPrefix, pathSuffix) {
18926
18974
  const paths = this.configPaths.filter(
@@ -18929,9 +18977,7 @@ var ThemeLoader = class _ThemeLoader {
18929
18977
  const configs = await Promise2.map(
18930
18978
  paths,
18931
18979
  (path) => this.fetchThemeConfig(path),
18932
- {
18933
- concurrency: 10
18934
- }
18980
+ { concurrency: 10 }
18935
18981
  );
18936
18982
  return configs.filter((config) => config !== null);
18937
18983
  }
@@ -18964,17 +19010,28 @@ var ThemeLoader = class _ThemeLoader {
18964
19010
  }
18965
19011
  const configHashesUnresolved = [];
18966
19012
  const configsByHash = /* @__PURE__ */ new Map();
19013
+ const themeId = this.getThemeId();
19014
+ const cache = this.getCache();
18967
19015
  await Promise2.map(
18968
- Object.values(manifest),
18969
- (configHash) => {
18970
- return this.getCache().get(`config:${configHash}`).then((config) => {
18971
- if (config) {
18972
- configsByHash.set(config.hash, config);
18973
- this.configs.set(config.file_path, config);
18974
- } else {
18975
- configHashesUnresolved.push(configHash);
18976
- }
18977
- });
19016
+ manifest.values(),
19017
+ async (configHash) => {
19018
+ const [themeConfig, fileUrl] = await Promise2.all([
19019
+ cache.get(`config:${configHash}`),
19020
+ themeId ? cache.get(`file:${themeId}:${configHash}`) : void 0
19021
+ ]);
19022
+ if (!themeConfig) {
19023
+ configHashesUnresolved.push(configHash);
19024
+ return;
19025
+ }
19026
+ let config = themeConfig;
19027
+ if (fileUrl && themeConfig.file?.url) {
19028
+ config = {
19029
+ ...themeConfig,
19030
+ file: { ...themeConfig.file, url: fileUrl }
19031
+ };
19032
+ }
19033
+ configsByHash.set(config.hash, config);
19034
+ this.configs.set(config.file_path, config);
18978
19035
  },
18979
19036
  { concurrency: 10 }
18980
19037
  );
@@ -18989,9 +19046,19 @@ var ThemeLoader = class _ThemeLoader {
18989
19046
  configsByHash.set(config.hash, config);
18990
19047
  this.configs.set(config.file_path, config);
18991
19048
  }
18992
- await Promise2.map(newConfigs, (config) => this.cacheThemeConfig(config), {
18993
- concurrency: 10
18994
- });
19049
+ await Promise2.map(
19050
+ newConfigs,
19051
+ async (config) => {
19052
+ const promises = [this.cacheThemeConfig(config)];
19053
+ if (themeId && config.file?.url) {
19054
+ promises.push(
19055
+ this.cacheThemeFileUrl(themeId, config.hash, config.file.url)
19056
+ );
19057
+ }
19058
+ await Promise2.all(promises);
19059
+ },
19060
+ { concurrency: 10 }
19061
+ );
18995
19062
  }
18996
19063
  return Array.from(configsByHash.values());
18997
19064
  }
@@ -19011,6 +19078,12 @@ var ThemeLoader = class _ThemeLoader {
19011
19078
  await this.getCache().set(`config:${config.hash}`, config);
19012
19079
  }
19013
19080
  }
19081
+ /**
19082
+ * Caches a CDN file url by config hash.
19083
+ */
19084
+ async cacheThemeFileUrl(themeId, configHash, fileUrl) {
19085
+ await this.getCache().set(`file:${themeId}:${configHash}`, fileUrl);
19086
+ }
19014
19087
  /**
19015
19088
  * Fetches the manifest (set of config hashes) for a theme version.
19016
19089
  */
@@ -19034,8 +19107,10 @@ var ThemeLoader = class _ThemeLoader {
19034
19107
  manifest = themeVersion.manifest;
19035
19108
  }
19036
19109
  }
19037
- this.manifest = manifest;
19038
- this.configPaths = Object.keys(manifest || {});
19110
+ if (manifest) {
19111
+ this.manifest = new Map(Object.entries(manifest));
19112
+ this.configPaths = [...this.manifest.keys()];
19113
+ }
19039
19114
  return this.manifest;
19040
19115
  }
19041
19116
  /**
@@ -19053,7 +19128,7 @@ var ThemeLoader = class _ThemeLoader {
19053
19128
  "/:themes:configs",
19054
19129
  {
19055
19130
  ...this.themeVersionQueryFilter(),
19056
- ...fetchAll ? {} : { hash: { $in: configHashes } },
19131
+ ...fetchAll ? void 0 : { hash: { $in: configHashes } },
19057
19132
  // TODO: paginate to support more than 1000 configs
19058
19133
  limit: 1e3,
19059
19134
  type: "theme",
@@ -19089,10 +19164,20 @@ var ThemeLoader = class _ThemeLoader {
19089
19164
  if (hash) {
19090
19165
  config.hash = hash;
19091
19166
  }
19092
- await this.cacheThemeConfig(config);
19167
+ const themeId = this.getThemeId();
19168
+ const promises = [this.cacheThemeConfig(config)];
19169
+ if (themeId && config.file?.url) {
19170
+ promises.push(
19171
+ this.cacheThemeFileUrl(themeId, config.hash, config.file.url)
19172
+ );
19173
+ }
19174
+ await Promise2.all(promises);
19093
19175
  }
19094
19176
  return config ?? null;
19095
19177
  }
19178
+ getThemeId() {
19179
+ return this.swell.swellHeaders["theme-id"];
19180
+ }
19096
19181
  /**
19097
19182
  * Generates a Swell API query filter for this theme version.
19098
19183
  */
@@ -19327,13 +19412,15 @@ var SwellTheme3 = class {
19327
19412
  const [cart, account] = await Promise.all([
19328
19413
  this.fetchSingletonResourceCached(
19329
19414
  "cart",
19415
+ // The cached cart may be null, but we need the StorefrontResource
19330
19416
  () => this.fetchCart(),
19331
- {}
19417
+ // Default value (always StorefrontResource)
19418
+ () => this.fetchCart()
19332
19419
  ),
19333
19420
  this.fetchSingletonResourceCached(
19334
19421
  "account",
19335
19422
  () => this.fetchAccount(),
19336
- null
19423
+ () => null
19337
19424
  )
19338
19425
  ]);
19339
19426
  if (!cart) {
@@ -19356,13 +19443,14 @@ var SwellTheme3 = class {
19356
19443
  async fetchSingletonResourceCached(key, handler, defaultValue) {
19357
19444
  const cacheKey = this.swell.storefront.session.getCookie();
19358
19445
  if (!cacheKey) {
19359
- return defaultValue;
19446
+ return defaultValue();
19360
19447
  }
19361
- return this.swell.getCachedResource(
19448
+ const result = await this.swell.getCachedResource(
19362
19449
  `${key}-${cacheKey}`,
19363
19450
  [],
19364
- () => handler()
19451
+ handler
19365
19452
  );
19453
+ return result ?? defaultValue();
19366
19454
  }
19367
19455
  async fetchCart() {
19368
19456
  const CartResource = this.resources?.singletons?.cart;
@@ -19703,8 +19791,8 @@ var SwellTheme3 = class {
19703
19791
  /**
19704
19792
  * Preloads updated theme configs. Used to optimize initial theme load.
19705
19793
  */
19706
- async preloadThemeConfigs(version, configs) {
19707
- await this.themeLoader.preloadTheme(version, configs);
19794
+ async preloadThemeConfigs(payload) {
19795
+ await this.themeLoader.preloadTheme(payload);
19708
19796
  }
19709
19797
  getPageConfigPath(pageId, altTemplate) {
19710
19798
  if (this.shopifyCompatibility) {