scratch-storage 6.1.11 → 6.2.1

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.
@@ -4,12 +4,18 @@ import { ScratchGetRequest, ScratchSendRequest, Tool } from './Tool';
4
4
  import { AssetType } from './AssetType';
5
5
  import { DataFormat } from './DataFormat';
6
6
  /**
7
- * @typedef {function} UrlFunction - A function which computes a URL from asset information.
8
- * @param {Asset} - The asset for which the URL should be computed.
9
- * @returns {(string|object)} - A string representing the URL for the asset request OR an object with configuration for
10
- * the underlying fetch call (necessary for configuring e.g. authentication)
7
+ * The result of a UrlFunction, which can be a string URL or a full request configuration
8
+ * object, or a promise for either of those.
9
+ *
10
+ * If set to null or undefined, the WebHelper will skip that store and move on to the
11
+ * next one. This allows stores to be registered that only provide a subset of their
12
+ * declared asset types at a given time.
11
13
  */
12
- export type UrlFunction = (asset: Asset) => string | ScratchGetRequest | ScratchSendRequest;
14
+ type RequestFnResult = null | undefined | string | ScratchGetRequest | ScratchSendRequest;
15
+ /**
16
+ * A function which computes a URL from asset information.
17
+ */
18
+ export type UrlFunction = (asset: Asset) => RequestFnResult | Promise<RequestFnResult>;
13
19
  interface StoreRecord {
14
20
  types: string[];
15
21
  get: UrlFunction;
@@ -4886,7 +4886,12 @@ function WebHelper_toPrimitive(t, r) { if ("object" != typeof t || !t) return t;
4886
4886
 
4887
4887
 
4888
4888
 
4889
- const ensureRequestConfig = reqConfig => {
4889
+ /**
4890
+ * Ensure that the provided request configuration is in object form, converting from
4891
+ * string if necessary.
4892
+ */
4893
+ const ensureRequestConfig = async reqConfig => {
4894
+ reqConfig = await reqConfig;
4890
4895
  if (typeof reqConfig === 'string') {
4891
4896
  return {
4892
4897
  url: reqConfig
@@ -4955,8 +4960,8 @@ class WebHelper extends Helper {
4955
4960
  * @param {DataFormat} dataFormat - The file format / file extension of the asset to fetch: PNG, JPG, etc.
4956
4961
  * @returns {Promise.<Asset>} A promise for the contents of the asset.
4957
4962
  */
4958
- load(assetType, assetId, dataFormat) {
4959
- /** @type {Array.<{url:string, result:*}>} List of URLs attempted & errors encountered. */
4963
+ async load(assetType, assetId, dataFormat) {
4964
+ /** @type {unknown[]} List of errors encountered while attempting to load the asset. */
4960
4965
  const errors = [];
4961
4966
  const stores = this.stores.slice().filter(store => store.types.indexOf(assetType.name) >= 0);
4962
4967
  // New empty asset but it doesn't have data yet
@@ -4965,33 +4970,29 @@ class WebHelper extends Helper {
4965
4970
  if (assetType.name === 'Project') {
4966
4971
  tool = this.projectTool;
4967
4972
  }
4968
- let storeIndex = 0;
4969
- const tryNextSource = err => {
4970
- if (err) {
4971
- errors.push(err);
4972
- }
4973
- const store = stores[storeIndex++];
4974
- /** @type {UrlFunction} */
4973
+ for (const store of stores) {
4975
4974
  const reqConfigFunction = store && store.get;
4976
4975
  if (reqConfigFunction) {
4977
- const reqConfig = ensureRequestConfig(reqConfigFunction(asset));
4978
- if (reqConfig === false) {
4979
- return tryNextSource();
4980
- }
4981
- return tool.get(reqConfig).then(body => {
4976
+ try {
4977
+ const reqConfig = await ensureRequestConfig(reqConfigFunction(asset));
4978
+ if (!reqConfig) {
4979
+ continue;
4980
+ }
4981
+ const body = await tool.get(reqConfig);
4982
4982
  if (body) {
4983
4983
  asset.setData(body, dataFormat);
4984
4984
  return asset;
4985
4985
  }
4986
- return tryNextSource();
4987
- }).catch(tryNextSource);
4988
- } else if (errors.length > 0) {
4989
- return Promise.reject(errors);
4986
+ } catch (err) {
4987
+ errors.push(err);
4988
+ }
4990
4989
  }
4991
- // no stores matching asset
4992
- return Promise.resolve(null);
4993
- };
4994
- return tryNextSource();
4990
+ }
4991
+ if (errors.length > 0) {
4992
+ return Promise.reject(errors);
4993
+ }
4994
+ // no stores matching asset
4995
+ return Promise.resolve(null);
4995
4996
  }
4996
4997
  /**
4997
4998
  * Create or update an asset with provided data. The create function is called if no asset id is provided
@@ -5001,33 +5002,38 @@ class WebHelper extends Helper {
5001
5002
  * @param {?string} assetId - The ID of the asset to fetch: a project ID, MD5, etc.
5002
5003
  * @returns {Promise.<object>} A promise for the response from the create or update request
5003
5004
  */
5004
- store(assetType, dataFormat, data, assetId) {
5005
+ async store(assetType, dataFormat, data, assetId) {
5005
5006
  const asset = new Asset(assetType, assetId, dataFormat);
5006
5007
  // If we have an asset id, we should update, otherwise create to get an id
5007
5008
  const create = assetId === '' || assetId === null || typeof assetId === 'undefined';
5008
- // Use the first store with the appropriate asset type and url function
5009
- const store = this.stores.filter(s =>
5009
+ const candidateStores = this.stores.filter(s =>
5010
5010
  // Only use stores for the incoming asset type
5011
5011
  s.types.indexOf(assetType.name) !== -1 && (
5012
5012
  // Only use stores that have a create function if this is a create request
5013
5013
  // or an update function if this is an update request
5014
- create && s.create || s.update))[0];
5014
+ create && s.create || s.update));
5015
5015
  const method = create ? 'post' : 'put';
5016
- if (!store) return Promise.reject(new Error('No appropriate stores'));
5016
+ if (candidateStores.length === 0) {
5017
+ return Promise.reject(new Error('No appropriate stores'));
5018
+ }
5017
5019
  let tool = this.assetTool;
5018
5020
  if (assetType.name === 'Project') {
5019
5021
  tool = this.projectTool;
5020
5022
  }
5021
- const reqConfig = ensureRequestConfig(
5022
- // The non-nullability of this gets checked above while looking up the store.
5023
- // Making TS understand that is going to require code refactoring which we currently don't
5024
- // feel safe to do.
5025
- create ? store.create(asset) : store.update(asset));
5026
- const reqBodyConfig = Object.assign({
5027
- body: data,
5028
- method
5029
- }, reqConfig);
5030
- return tool.send(reqBodyConfig).then(body => {
5023
+ for (const store of candidateStores) {
5024
+ const reqConfig = await ensureRequestConfig(
5025
+ // The non-nullability of this gets checked above while looking up the store.
5026
+ // Making TS understand that is going to require code refactoring which we currently don't
5027
+ // feel safe to do.
5028
+ create ? store.create(asset) : store.update(asset));
5029
+ if (!reqConfig) {
5030
+ continue;
5031
+ }
5032
+ const reqBodyConfig = Object.assign({
5033
+ body: data,
5034
+ method
5035
+ }, reqConfig);
5036
+ let body = await tool.send(reqBodyConfig);
5031
5037
  // xhr makes it difficult to both send FormData and
5032
5038
  // automatically parse a JSON response. So try to parse
5033
5039
  // everything as JSON.
@@ -5044,7 +5050,8 @@ class WebHelper extends Helper {
5044
5050
  return Object.assign({
5045
5051
  id: body['content-name'] || assetId
5046
5052
  }, body);
5047
- });
5053
+ }
5054
+ return Promise.reject(new Error('No store could handle the request'));
5048
5055
  }
5049
5056
  }
5050
5057
  ;// ./src/ScratchStorage.ts