@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.mjs CHANGED
@@ -16,31 +16,44 @@ import {
16
16
 
17
17
  // src/cache/cf-worker-kv-keyv-adapter.ts
18
18
  var CFWorkerKVKeyvAdapter = class {
19
+ store;
19
20
  namespace;
20
21
  // magically passed in from Keyv
21
- store;
22
+ opts;
22
23
  constructor(store) {
23
24
  this.store = store;
25
+ this.opts = null;
24
26
  this.namespace = "dummy";
25
27
  }
26
28
  async has(key) {
27
- return this.get(key) !== void 0;
29
+ const stream = await this.store.get(key, "stream");
30
+ if (stream !== null) {
31
+ await stream.cancel();
32
+ return true;
33
+ }
34
+ return false;
28
35
  }
29
36
  async get(key) {
30
- return this.store.get(key);
37
+ const value = await this.store.get(key);
38
+ return value !== null ? value : void 0;
31
39
  }
32
- async set(key, value) {
33
- return this.store.put(key, value);
40
+ set(key, value, ttl) {
41
+ if (typeof ttl === "number") {
42
+ ttl = Math.max(60, ttl / 1e3);
43
+ }
44
+ return this.store.put(key, value, { expirationTtl: ttl });
34
45
  }
35
46
  async delete(key) {
36
47
  await this.store.delete(key);
48
+ return true;
37
49
  }
38
50
  async clear() {
39
51
  let cursor = "";
40
52
  let complete = false;
41
- while (!complete) {
53
+ const prefix = `${this.namespace}:`;
54
+ do {
42
55
  const response = await this.store.list({
43
- prefix: `${this.namespace}:`,
56
+ prefix,
44
57
  cursor: cursor || void 0
45
58
  });
46
59
  cursor = response.cursor ?? "";
@@ -52,7 +65,10 @@ var CFWorkerKVKeyvAdapter = class {
52
65
  })
53
66
  );
54
67
  }
55
- }
68
+ } while (!complete);
69
+ }
70
+ on(_event, _listener) {
71
+ return this;
56
72
  }
57
73
  };
58
74
 
@@ -6956,23 +6972,25 @@ var Cache = class {
6956
6972
  ...options
6957
6973
  });
6958
6974
  }
6959
- async fetch(key, fetchFn) {
6960
- return this.client.wrap(key, fetchFn);
6975
+ async fetch(key, fetchFn, ttl) {
6976
+ return this.client.wrap(key, fetchFn, ttl);
6961
6977
  }
6962
- // Fetch cache using SWR (stale-while-revalidate)
6963
- // This will always return the cached value immediately if exists
6978
+ /**
6979
+ * Fetch cache using SWR (stale-while-revalidate)
6980
+ *
6981
+ * This will always return the cached value immediately if exists
6982
+ */
6964
6983
  async fetchSWR(key, fetchFn, ttl = DEFAULT_SWR_TTL) {
6965
6984
  const cacheValue = await this.client.get(key);
6966
- const promiseValue = Promise.resolve().then(() => fetchFn()).then(async (value) => {
6985
+ const promiseValue = Promise.resolve().then(fetchFn).then(resolveAsyncResources).then(async (value) => {
6967
6986
  const isNull = value === null || value === void 0;
6968
- const valueResolved = await resolveAsyncResources(value);
6969
- await this.client.set(key, isNull ? NULL_VALUE : valueResolved, ttl);
6987
+ await this.client.set(key, isNull ? NULL_VALUE : value, ttl);
6970
6988
  return value;
6971
6989
  });
6972
6990
  if (this.workerCtx?.waitUntil) {
6973
6991
  this.workerCtx.waitUntil(promiseValue);
6974
6992
  }
6975
- if (cacheValue !== null) {
6993
+ if (cacheValue !== void 0) {
6976
6994
  return cacheValue === NULL_VALUE ? null : cacheValue;
6977
6995
  }
6978
6996
  const result = await promiseValue;
@@ -6989,8 +7007,9 @@ var Cache = class {
6989
7007
  }
6990
7008
  /**
6991
7009
  * Flushes the entire cache.
6992
- * WARNING: If the cache store is shared among many cache clients,
6993
- * this will flush entries for other clients.
7010
+ *
7011
+ * __WARNING__: If the cache store is shared among many cache clients,
7012
+ * this will flush entries for other clients.
6994
7013
  */
6995
7014
  async flushAll() {
6996
7015
  await this.client.clear();
@@ -7033,7 +7052,7 @@ var ResourceCache = class extends Cache {
7033
7052
  function buildStores2() {
7034
7053
  return [
7035
7054
  new Keyv2({
7036
- // Disabling serialization allows for pure memo-ization of class instances
7055
+ // Disabling serialization allows for pure memoization of class instances
7037
7056
  // at the tradeoff of no support for compression.
7038
7057
  serialize: void 0,
7039
7058
  deserialize: void 0
@@ -18111,10 +18130,10 @@ var svgs = {
18111
18130
  src: "https://cdn.swell.store/schema/685ef581b79e3e0012cf3222/fba884c7fe8122c472e130355fb52db7/collection-6.png"
18112
18131
  },
18113
18132
  "lifestyle-1": {
18114
- src: "https://cdn.swell.store/schema/685ef58bb79e3e0012cf3256/1da0da57ea80d90038fcf6b100a16fe6/lifestyle-1.png"
18133
+ src: "https://cdn.swell.store/schema/6870eddc1657130012643fc6/1777bcff40736610093833ef858b5936/lifestyle-1.png"
18115
18134
  },
18116
18135
  "lifestyle-2": {
18117
- src: "https://cdn.swell.store/schema/685ef58cb79e3e0012cf3259/f963448f1a6f4d6588fcd283b5c00c7c/lifestyle-2.png"
18136
+ src: "https://cdn.swell.store/schema/6870eddc1657130012643fcb/4d29197e3fa0b6d8255c1b7770f8dc12/lifestyle-2.png"
18118
18137
  },
18119
18138
  "product-apparel-1": {
18120
18139
  src: "https://cdn.swell.store/schema/685ef58eb79e3e0012cf3276/37c3c973a01287a580ab9db2e05dd36d/product-apparel-1.png"
@@ -18712,12 +18731,11 @@ var ThemeLoader = class _ThemeLoader {
18712
18731
  this.configPaths = [];
18713
18732
  }
18714
18733
  async init(themeConfigs) {
18715
- const { swellHeaders } = this.swell;
18716
18734
  if (themeConfigs) {
18717
18735
  this.setConfigs(themeConfigs);
18718
18736
  return;
18719
18737
  }
18720
- if (!swellHeaders["theme-id"]) {
18738
+ if (!this.getThemeId()) {
18721
18739
  return;
18722
18740
  }
18723
18741
  await this.fetchManifest();
@@ -18756,25 +18774,45 @@ var ThemeLoader = class _ThemeLoader {
18756
18774
  */
18757
18775
  setConfigs(themeConfigs) {
18758
18776
  this.configs = new Map(themeConfigs);
18777
+ this.manifest = /* @__PURE__ */ new Map();
18778
+ for (const { file_path, hash } of this.configs.values()) {
18779
+ this.manifest.set(file_path, hash);
18780
+ }
18759
18781
  this.configPaths = Array.from(this.configs.keys());
18760
- this.manifest = this.configPaths.reduce((manifest, path) => {
18761
- manifest[path] = this.configs.get(path)?.hash;
18762
- return manifest;
18763
- }, {});
18764
18782
  }
18765
18783
  /**
18766
18784
  * Preloads a theme version and configs. This is used to optimize initial theme load.
18767
18785
  */
18768
- async preloadTheme(version, configs) {
18786
+ async preloadTheme(payload) {
18787
+ const { version, configs } = payload;
18769
18788
  console.log(
18770
18789
  `ThemeLoader.preloadTheme${version?.hash ? ` - manifest: ${version.hash}` : ""}${configs?.length ? ` - configs: ${configs.length}` : ""}`
18771
18790
  );
18772
- await Promise2.all([
18773
- version && this.cacheManifest(version),
18774
- configs && Promise2.map(configs, (config) => this.cacheThemeConfig(config), {
18775
- concurrency: 10
18776
- })
18777
- ]);
18791
+ const promises = [];
18792
+ if (version) {
18793
+ promises.push(this.cacheManifest(version));
18794
+ }
18795
+ if (configs) {
18796
+ const themeId = this.getThemeId();
18797
+ promises.push(
18798
+ Promise2.map(
18799
+ configs,
18800
+ async (config) => {
18801
+ const promises2 = [
18802
+ this.cacheThemeConfig(config)
18803
+ ];
18804
+ if (themeId && config.file?.url) {
18805
+ promises2.push(
18806
+ this.cacheThemeFileUrl(themeId, config.hash, config.file.url)
18807
+ );
18808
+ }
18809
+ await Promise2.all(promises2);
18810
+ },
18811
+ { concurrency: 10 }
18812
+ )
18813
+ );
18814
+ }
18815
+ await Promise2.all(promises);
18778
18816
  }
18779
18817
  /**
18780
18818
  * Fetches a theme config by file path.
@@ -18784,18 +18822,28 @@ var ThemeLoader = class _ThemeLoader {
18784
18822
  if (config !== void 0) {
18785
18823
  return config;
18786
18824
  }
18787
- const hash = this.manifest?.[filePath];
18788
- if (hash) {
18789
- const config2 = await this.getCache().get(
18790
- `config:${hash}`
18791
- );
18792
- if (config2) {
18793
- this.configs.set(filePath, config2);
18794
- return config2;
18825
+ const hash = this.manifest?.get(filePath);
18826
+ if (!hash) {
18827
+ return null;
18828
+ }
18829
+ const cache = this.getCache();
18830
+ const themeId = this.getThemeId();
18831
+ const [themeConfig, fileUrl] = await Promise2.all([
18832
+ cache.get(`config:${hash}`),
18833
+ themeId ? cache.get(`file:${themeId}:${hash}`) : void 0
18834
+ ]);
18835
+ if (themeConfig) {
18836
+ let config2 = themeConfig;
18837
+ if (fileUrl && themeConfig.file?.url) {
18838
+ config2 = {
18839
+ ...themeConfig,
18840
+ file: { ...themeConfig.file, url: fileUrl }
18841
+ };
18795
18842
  }
18796
- return this.fetchThemeConfigsFromSourceByPath(filePath, hash);
18843
+ this.configs.set(filePath, config2);
18844
+ return config2;
18797
18845
  }
18798
- return null;
18846
+ return this.fetchThemeConfigsFromSourceByPath(filePath, hash);
18799
18847
  }
18800
18848
  async fetchThemeConfigsByPath(pathPrefix, pathSuffix) {
18801
18849
  const paths = this.configPaths.filter(
@@ -18804,9 +18852,7 @@ var ThemeLoader = class _ThemeLoader {
18804
18852
  const configs = await Promise2.map(
18805
18853
  paths,
18806
18854
  (path) => this.fetchThemeConfig(path),
18807
- {
18808
- concurrency: 10
18809
- }
18855
+ { concurrency: 10 }
18810
18856
  );
18811
18857
  return configs.filter((config) => config !== null);
18812
18858
  }
@@ -18839,17 +18885,28 @@ var ThemeLoader = class _ThemeLoader {
18839
18885
  }
18840
18886
  const configHashesUnresolved = [];
18841
18887
  const configsByHash = /* @__PURE__ */ new Map();
18888
+ const themeId = this.getThemeId();
18889
+ const cache = this.getCache();
18842
18890
  await Promise2.map(
18843
- Object.values(manifest),
18844
- (configHash) => {
18845
- return this.getCache().get(`config:${configHash}`).then((config) => {
18846
- if (config) {
18847
- configsByHash.set(config.hash, config);
18848
- this.configs.set(config.file_path, config);
18849
- } else {
18850
- configHashesUnresolved.push(configHash);
18851
- }
18852
- });
18891
+ manifest.values(),
18892
+ async (configHash) => {
18893
+ const [themeConfig, fileUrl] = await Promise2.all([
18894
+ cache.get(`config:${configHash}`),
18895
+ themeId ? cache.get(`file:${themeId}:${configHash}`) : void 0
18896
+ ]);
18897
+ if (!themeConfig) {
18898
+ configHashesUnresolved.push(configHash);
18899
+ return;
18900
+ }
18901
+ let config = themeConfig;
18902
+ if (fileUrl && themeConfig.file?.url) {
18903
+ config = {
18904
+ ...themeConfig,
18905
+ file: { ...themeConfig.file, url: fileUrl }
18906
+ };
18907
+ }
18908
+ configsByHash.set(config.hash, config);
18909
+ this.configs.set(config.file_path, config);
18853
18910
  },
18854
18911
  { concurrency: 10 }
18855
18912
  );
@@ -18864,9 +18921,19 @@ var ThemeLoader = class _ThemeLoader {
18864
18921
  configsByHash.set(config.hash, config);
18865
18922
  this.configs.set(config.file_path, config);
18866
18923
  }
18867
- await Promise2.map(newConfigs, (config) => this.cacheThemeConfig(config), {
18868
- concurrency: 10
18869
- });
18924
+ await Promise2.map(
18925
+ newConfigs,
18926
+ async (config) => {
18927
+ const promises = [this.cacheThemeConfig(config)];
18928
+ if (themeId && config.file?.url) {
18929
+ promises.push(
18930
+ this.cacheThemeFileUrl(themeId, config.hash, config.file.url)
18931
+ );
18932
+ }
18933
+ await Promise2.all(promises);
18934
+ },
18935
+ { concurrency: 10 }
18936
+ );
18870
18937
  }
18871
18938
  return Array.from(configsByHash.values());
18872
18939
  }
@@ -18886,6 +18953,12 @@ var ThemeLoader = class _ThemeLoader {
18886
18953
  await this.getCache().set(`config:${config.hash}`, config);
18887
18954
  }
18888
18955
  }
18956
+ /**
18957
+ * Caches a CDN file url by config hash.
18958
+ */
18959
+ async cacheThemeFileUrl(themeId, configHash, fileUrl) {
18960
+ await this.getCache().set(`file:${themeId}:${configHash}`, fileUrl);
18961
+ }
18889
18962
  /**
18890
18963
  * Fetches the manifest (set of config hashes) for a theme version.
18891
18964
  */
@@ -18909,8 +18982,10 @@ var ThemeLoader = class _ThemeLoader {
18909
18982
  manifest = themeVersion.manifest;
18910
18983
  }
18911
18984
  }
18912
- this.manifest = manifest;
18913
- this.configPaths = Object.keys(manifest || {});
18985
+ if (manifest) {
18986
+ this.manifest = new Map(Object.entries(manifest));
18987
+ this.configPaths = [...this.manifest.keys()];
18988
+ }
18914
18989
  return this.manifest;
18915
18990
  }
18916
18991
  /**
@@ -18928,7 +19003,7 @@ var ThemeLoader = class _ThemeLoader {
18928
19003
  "/:themes:configs",
18929
19004
  {
18930
19005
  ...this.themeVersionQueryFilter(),
18931
- ...fetchAll ? {} : { hash: { $in: configHashes } },
19006
+ ...fetchAll ? void 0 : { hash: { $in: configHashes } },
18932
19007
  // TODO: paginate to support more than 1000 configs
18933
19008
  limit: 1e3,
18934
19009
  type: "theme",
@@ -18964,10 +19039,20 @@ var ThemeLoader = class _ThemeLoader {
18964
19039
  if (hash) {
18965
19040
  config.hash = hash;
18966
19041
  }
18967
- await this.cacheThemeConfig(config);
19042
+ const themeId = this.getThemeId();
19043
+ const promises = [this.cacheThemeConfig(config)];
19044
+ if (themeId && config.file?.url) {
19045
+ promises.push(
19046
+ this.cacheThemeFileUrl(themeId, config.hash, config.file.url)
19047
+ );
19048
+ }
19049
+ await Promise2.all(promises);
18968
19050
  }
18969
19051
  return config ?? null;
18970
19052
  }
19053
+ getThemeId() {
19054
+ return this.swell.swellHeaders["theme-id"];
19055
+ }
18971
19056
  /**
18972
19057
  * Generates a Swell API query filter for this theme version.
18973
19058
  */
@@ -19202,13 +19287,15 @@ var SwellTheme3 = class {
19202
19287
  const [cart, account] = await Promise.all([
19203
19288
  this.fetchSingletonResourceCached(
19204
19289
  "cart",
19290
+ // The cached cart may be null, but we need the StorefrontResource
19205
19291
  () => this.fetchCart(),
19206
- {}
19292
+ // Default value (always StorefrontResource)
19293
+ () => this.fetchCart()
19207
19294
  ),
19208
19295
  this.fetchSingletonResourceCached(
19209
19296
  "account",
19210
19297
  () => this.fetchAccount(),
19211
- null
19298
+ () => null
19212
19299
  )
19213
19300
  ]);
19214
19301
  if (!cart) {
@@ -19231,13 +19318,14 @@ var SwellTheme3 = class {
19231
19318
  async fetchSingletonResourceCached(key, handler, defaultValue) {
19232
19319
  const cacheKey = this.swell.storefront.session.getCookie();
19233
19320
  if (!cacheKey) {
19234
- return defaultValue;
19321
+ return defaultValue();
19235
19322
  }
19236
- return this.swell.getCachedResource(
19323
+ const result = await this.swell.getCachedResource(
19237
19324
  `${key}-${cacheKey}`,
19238
19325
  [],
19239
- () => handler()
19326
+ handler
19240
19327
  );
19328
+ return result ?? defaultValue();
19241
19329
  }
19242
19330
  async fetchCart() {
19243
19331
  const CartResource = this.resources?.singletons?.cart;
@@ -19578,8 +19666,8 @@ var SwellTheme3 = class {
19578
19666
  /**
19579
19667
  * Preloads updated theme configs. Used to optimize initial theme load.
19580
19668
  */
19581
- async preloadThemeConfigs(version, configs) {
19582
- await this.themeLoader.preloadTheme(version, configs);
19669
+ async preloadThemeConfigs(payload) {
19670
+ await this.themeLoader.preloadTheme(payload);
19583
19671
  }
19584
19672
  getPageConfigPath(pageId, altTemplate) {
19585
19673
  if (this.shopifyCompatibility) {