scratch-storage 2.3.284 → 3.0.0

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.
Files changed (61) hide show
  1. package/.browserslistrc +7 -0
  2. package/CHANGELOG.md +11 -0
  3. package/dist/{web/f6240eab828e6d415177.worker.js → node/chunks/fetch-worker.eefe62e3c8ee125d3436.js} +105 -180
  4. package/dist/node/chunks/fetch-worker.eefe62e3c8ee125d3436.js.map +1 -0
  5. package/dist/node/scratch-storage.js +3898 -6304
  6. package/dist/node/scratch-storage.js.map +1 -1
  7. package/dist/types/Asset.d.ts +38 -0
  8. package/dist/types/AssetType.d.ts +49 -0
  9. package/dist/types/BuiltinHelper.d.ts +69 -0
  10. package/dist/types/DataFormat.d.ts +15 -0
  11. package/dist/types/FetchTool.d.ts +33 -0
  12. package/dist/types/FetchWorkerTool.d.ts +29 -0
  13. package/dist/types/Helper.d.ts +20 -0
  14. package/dist/types/ProxyTool.d.ts +53 -0
  15. package/dist/types/ScratchStorage.d.ts +202 -0
  16. package/dist/types/Tool.d.ts +13 -0
  17. package/dist/types/WebHelper.d.ts +59 -0
  18. package/dist/types/index.d.ts +11 -0
  19. package/dist/types/log.d.ts +2 -0
  20. package/dist/types/memoizedToString.d.ts +5 -0
  21. package/dist/web/chunks/fetch-worker.85da71862ab8ee15f1cd.js +2 -0
  22. package/dist/web/chunks/fetch-worker.85da71862ab8ee15f1cd.js.map +1 -0
  23. package/dist/web/chunks/fetch-worker.ba5eddd48c8ae259073b.js +676 -0
  24. package/dist/web/chunks/fetch-worker.ba5eddd48c8ae259073b.js.map +1 -0
  25. package/dist/web/scratch-storage.js +2002 -2758
  26. package/dist/web/scratch-storage.js.map +1 -1
  27. package/dist/web/scratch-storage.min.js +2 -6398
  28. package/dist/web/scratch-storage.min.js.LICENSE.txt +18 -0
  29. package/dist/web/scratch-storage.min.js.map +1 -1
  30. package/jest.config.js +32 -0
  31. package/package.json +18 -13
  32. package/src/.eslintrc.js +13 -1
  33. package/src/Asset.ts +100 -0
  34. package/src/{AssetType.js → AssetType.ts} +10 -5
  35. package/src/{BuiltinHelper.js → BuiltinHelper.ts} +44 -35
  36. package/src/{DataFormat.js → DataFormat.ts} +3 -3
  37. package/src/{FetchTool.js → FetchTool.ts} +8 -9
  38. package/src/{FetchWorkerTool.js → FetchWorkerTool.ts} +47 -22
  39. package/src/{Helper.js → Helper.ts} +10 -5
  40. package/src/{ProxyTool.js → ProxyTool.ts} +32 -29
  41. package/src/{ScratchStorage.js → ScratchStorage.ts} +57 -34
  42. package/src/Tool.ts +10 -0
  43. package/src/{WebHelper.js → WebHelper.ts} +42 -15
  44. package/src/index.ts +19 -0
  45. package/src/log.ts +4 -0
  46. package/src/{Asset.js → memoizedToString.ts} +16 -76
  47. package/src/scratchFetch.js +0 -2
  48. package/src/types.d.ts +3 -0
  49. package/test/integration/download-known-assets.test.js +1 -1
  50. package/test/unit/add-helper.test.js +1 -1
  51. package/test/unit/fetch-tool.test.js +1 -1
  52. package/test/unit/load-default-assets.test.js +1 -1
  53. package/test/unit/metadata.test.js +6 -6
  54. package/tsconfig.json +29 -0
  55. package/tsconfig.test.json +11 -0
  56. package/webpack.config.js +58 -65
  57. package/dist/node/ce8a01e629587e4f90e4.worker.js +0 -4274
  58. package/dist/node/ce8a01e629587e4f90e4.worker.js.map +0 -1
  59. package/dist/web/f6240eab828e6d415177.worker.js.map +0 -1
  60. package/src/index.js +0 -7
  61. package/src/log.js +0 -4
@@ -1,9 +1,21 @@
1
- const {Headers, applyMetadata} = require('./scratchFetch');
1
+ import {Headers, applyMetadata} from './scratchFetch';
2
+ import {ScratchGetRequest, Tool} from './Tool';
3
+
4
+ interface DeferredJob {
5
+ id: string,
6
+ resolve: (buffer: ArrayBuffer) => void;
7
+ reject: (error: unknown) => void;
8
+ }
2
9
 
3
10
  /**
4
11
  * Get and send assets with a worker that uses fetch.
5
12
  */
6
- class PrivateFetchWorkerTool {
13
+ class PrivateFetchWorkerTool implements Tool {
14
+ private _workerSupport: {fetch: boolean};
15
+ private _supportError: unknown;
16
+ private worker: Worker | null;
17
+ private jobs: Record<string, DeferredJob | undefined>;
18
+
7
19
  constructor () {
8
20
  /**
9
21
  * What does the worker support of the APIs we need?
@@ -33,10 +45,13 @@ class PrivateFetchWorkerTool {
33
45
 
34
46
  try {
35
47
  if (this.isGetSupported) {
36
- // eslint-disable-next-line global-require
37
- const FetchWorker = require('worker-loader?{"inline":true,"fallback":true}!./FetchWorkerTool.worker');
38
-
39
- const worker = new FetchWorker();
48
+ // Yes, this is a browser API and we've specified `browser: false` in the eslint env,
49
+ // but `isGetSupported` checks for the presence of Worker and uses it only if present.
50
+ // Also see https://webpack.js.org/guides/web-workers/
51
+ // eslint-disable-next-line no-undef
52
+ const worker = new Worker(
53
+ /* webpackChunkName: "fetch-worker" */ new URL('./FetchWorkerTool.worker', import.meta.url)
54
+ );
40
55
 
41
56
  worker.addEventListener('message', ({data}) => {
42
57
  if (data.support) {
@@ -44,11 +59,12 @@ class PrivateFetchWorkerTool {
44
59
  return;
45
60
  }
46
61
  for (const message of data) {
47
- if (this.jobs[message.id]) {
62
+ const job = this.jobs[message.id];
63
+ if (job) {
48
64
  if (message.error) {
49
- this.jobs[message.id].reject(message.error);
65
+ job.reject(message.error);
50
66
  } else {
51
- this.jobs[message.id].resolve(message.buffer);
67
+ job.resolve(message.buffer);
52
68
  }
53
69
  delete this.jobs[message.id];
54
70
  }
@@ -70,7 +86,7 @@ class PrivateFetchWorkerTool {
70
86
  * guess that it does if the window does until the worker can inform us.
71
87
  * @returns {boolean} Is get supported?
72
88
  */
73
- get isGetSupported () {
89
+ get isGetSupported (): boolean {
74
90
  return (
75
91
  typeof Worker !== 'undefined' &&
76
92
  this._workerSupport.fetch &&
@@ -84,8 +100,14 @@ class PrivateFetchWorkerTool {
84
100
  * @param {{method:string}} options - Additional options to configure fetch.
85
101
  * @returns {Promise.<Buffer|Uint8Array|null>} Resolve to Buffer of data from server.
86
102
  */
87
- get ({url, ...options}) {
88
- return new Promise((resolve, reject) => {
103
+ get ({url, ...options}: ScratchGetRequest): Promise<Uint8Array | null> {
104
+ const worker = this.worker;
105
+
106
+ if (!worker) {
107
+ return Promise.reject(new Error('The worker could not be initialized'));
108
+ }
109
+
110
+ return new Promise<ArrayBuffer>((resolve, reject) => {
89
111
  // TODO: Use a Scratch standard ID generator ...
90
112
  const id = Math.random().toString(16)
91
113
  .substring(2);
@@ -99,7 +121,8 @@ class PrivateFetchWorkerTool {
99
121
  if (augmentedOptions && augmentedOptions.headers instanceof Headers) {
100
122
  augmentedOptions.headers = Array.from(augmentedOptions.headers.entries());
101
123
  }
102
- this.worker.postMessage({
124
+
125
+ worker.postMessage({
103
126
  id,
104
127
  url,
105
128
  options: augmentedOptions
@@ -118,7 +141,7 @@ class PrivateFetchWorkerTool {
118
141
  * Is sending supported? always false for FetchWorkerTool.
119
142
  * @returns {boolean} Is sending supported?
120
143
  */
121
- get isSendSupported () {
144
+ get isSendSupported (): boolean {
122
145
  return false;
123
146
  }
124
147
 
@@ -126,10 +149,12 @@ class PrivateFetchWorkerTool {
126
149
  * Send data to a server.
127
150
  * @throws {Error} A not implemented error.
128
151
  */
129
- send () {
152
+ send (): never {
130
153
  throw new Error('Not implemented.');
131
154
  }
132
155
 
156
+ private static _instance?: PrivateFetchWorkerTool;
157
+
133
158
  /**
134
159
  * Return a static PrivateFetchWorkerTool instance on demand.
135
160
  * @returns {PrivateFetchWorkerTool} A static PrivateFetchWorkerTool
@@ -146,7 +171,9 @@ class PrivateFetchWorkerTool {
146
171
  /**
147
172
  * Get and send assets with a worker that uses fetch.
148
173
  */
149
- class PublicFetchWorkerTool {
174
+ export default class PublicFetchWorkerTool {
175
+ private inner: PrivateFetchWorkerTool;
176
+
150
177
  constructor () {
151
178
  /**
152
179
  * Shared instance of an internal worker. PublicFetchWorkerTool proxies
@@ -160,7 +187,7 @@ class PublicFetchWorkerTool {
160
187
  * Is get supported?
161
188
  * @returns {boolean} Is get supported?
162
189
  */
163
- get isGetSupported () {
190
+ get isGetSupported (): boolean {
164
191
  return this.inner.isGetSupported;
165
192
  }
166
193
 
@@ -169,7 +196,7 @@ class PublicFetchWorkerTool {
169
196
  * @param {{url:string}} reqConfig - Request configuration for data to get.
170
197
  * @returns {Promise.<Buffer|Uint8Array|null>} Resolve to Buffer of data from server.
171
198
  */
172
- get (reqConfig) {
199
+ get (reqConfig: ScratchGetRequest): Promise<Uint8Array | null> {
173
200
  return this.inner.get(reqConfig);
174
201
  }
175
202
 
@@ -177,7 +204,7 @@ class PublicFetchWorkerTool {
177
204
  * Is sending supported?
178
205
  * @returns {boolean} Is sending supported?
179
206
  */
180
- get isSendSupported () {
207
+ get isSendSupported (): boolean {
181
208
  return false;
182
209
  }
183
210
 
@@ -185,9 +212,7 @@ class PublicFetchWorkerTool {
185
212
  * Send data to a server with a worker that uses fetch.
186
213
  * @throws {Error} A not implemented error.
187
214
  */
188
- send () {
215
+ send (): never {
189
216
  throw new Error('Not implemented.');
190
217
  }
191
218
  }
192
-
193
- module.exports = PublicFetchWorkerTool;
@@ -1,9 +1,16 @@
1
+ import Asset, {AssetId} from './Asset';
2
+ import {AssetType} from './AssetType';
3
+ import {DataFormat} from './DataFormat';
4
+ import {ScratchStorage} from './ScratchStorage';
5
+
1
6
  /**
2
7
  * Base class for asset load/save helpers.
3
8
  * @abstract
4
9
  */
5
- class Helper {
6
- constructor (parent) {
10
+ export default class Helper {
11
+ public parent!: ScratchStorage;
12
+
13
+ constructor (parent: ScratchStorage) {
7
14
  this.parent = parent;
8
15
  }
9
16
 
@@ -14,9 +21,7 @@ class Helper {
14
21
  * @param {DataFormat} dataFormat - The file format / file extension of the asset to fetch: PNG, JPG, etc.
15
22
  * @return {Promise.<Asset>} A promise for the contents of the asset.
16
23
  */
17
- load (assetType, assetId, dataFormat) {
24
+ load (assetType: AssetType, assetId: AssetId, dataFormat: DataFormat): Promise<Asset | null> | null {
18
25
  return Promise.reject(new Error(`No asset of type ${assetType} for ID ${assetId} with format ${dataFormat}`));
19
26
  }
20
27
  }
21
-
22
- module.exports = Helper;
@@ -1,5 +1,6 @@
1
- const FetchWorkerTool = require('./FetchWorkerTool');
2
- const FetchTool = require('./FetchTool');
1
+ import FetchWorkerTool from './FetchWorkerTool';
2
+ import {FetchTool} from './FetchTool';
3
+ import {ScratchGetRequest, ScratchSendRequest, Tool} from './Tool';
3
4
 
4
5
  /**
5
6
  * @typedef {object} Request
@@ -9,12 +10,32 @@ const FetchTool = require('./FetchTool');
9
10
  * @property {boolean} withCredentials
10
11
  */
11
12
 
13
+ type ToolFilter = typeof ProxyTool.TOOL_FILTER[keyof typeof ProxyTool.TOOL_FILTER];
14
+
12
15
  /**
13
16
  * Get and send assets with other tools in sequence.
14
17
  */
15
- class ProxyTool {
16
- constructor (filter = ProxyTool.TOOL_FILTER.ALL) {
17
- let tools;
18
+ export default class ProxyTool implements Tool {
19
+ public tools: Tool[];
20
+
21
+ /**
22
+ * Constant values that filter the set of tools in a ProxyTool instance.
23
+ * @enum {string}
24
+ */
25
+ public static TOOL_FILTER = {
26
+ /**
27
+ * Use all tools.
28
+ */
29
+ ALL: 'all',
30
+
31
+ /**
32
+ * Use tools that are ready right now.
33
+ */
34
+ READY: 'ready'
35
+ } as const;
36
+
37
+ constructor (filter: ToolFilter = ProxyTool.TOOL_FILTER.ALL) {
38
+ let tools: Tool[];
18
39
  if (filter === ProxyTool.TOOL_FILTER.READY) {
19
40
  tools = [new FetchTool()];
20
41
  } else {
@@ -32,7 +53,7 @@ class ProxyTool {
32
53
  * Is get supported? false if all proxied tool return false.
33
54
  * @returns {boolean} Is get supported?
34
55
  */
35
- get isGetSupported () {
56
+ get isGetSupported (): boolean {
36
57
  return this.tools.some(tool => tool.isGetSupported);
37
58
  }
38
59
 
@@ -41,9 +62,9 @@ class ProxyTool {
41
62
  * @param {Request} reqConfig - Request configuration for data to get.
42
63
  * @returns {Promise.<Buffer>} Resolve to Buffer of data from server.
43
64
  */
44
- get (reqConfig) {
65
+ get (reqConfig: ScratchGetRequest): Promise<Uint8Array | null> {
45
66
  let toolIndex = 0;
46
- const nextTool = err => {
67
+ const nextTool = (err?: unknown): Promise<Uint8Array | null> => {
47
68
  const tool = this.tools[toolIndex++];
48
69
  if (!tool) {
49
70
  throw err;
@@ -60,7 +81,7 @@ class ProxyTool {
60
81
  * Is sending supported? false if all proxied tool return false.
61
82
  * @returns {boolean} Is sending supported?
62
83
  */
63
- get isSendSupported () {
84
+ get isSendSupported (): boolean {
64
85
  return this.tools.some(tool => tool.isSendSupported);
65
86
  }
66
87
 
@@ -69,9 +90,9 @@ class ProxyTool {
69
90
  * @param {Request} reqConfig - Request configuration for data to send.
70
91
  * @returns {Promise.<Buffer|string|object>} Server returned metadata.
71
92
  */
72
- send (reqConfig) {
93
+ send (reqConfig: ScratchSendRequest): Promise<string> {
73
94
  let toolIndex = 0;
74
- const nextTool = err => {
95
+ const nextTool = (err?: unknown): Promise<string> => {
75
96
  const tool = this.tools[toolIndex++];
76
97
  if (!tool) {
77
98
  throw err;
@@ -84,21 +105,3 @@ class ProxyTool {
84
105
  return nextTool();
85
106
  }
86
107
  }
87
-
88
- /**
89
- * Constant values that filter the set of tools in a ProxyTool instance.
90
- * @enum {string}
91
- */
92
- ProxyTool.TOOL_FILTER = {
93
- /**
94
- * Use all tools.
95
- */
96
- ALL: 'all',
97
-
98
- /**
99
- * Use tools that are ready right now.
100
- */
101
- READY: 'ready'
102
- };
103
-
104
- module.exports = ProxyTool;
@@ -1,20 +1,32 @@
1
- const log = require('./log');
1
+ import log from './log';
2
2
 
3
- const BuiltinHelper = require('./BuiltinHelper');
4
- const WebHelper = require('./WebHelper');
3
+ import BuiltinHelper from './BuiltinHelper';
4
+ import WebHelper, {UrlFunction} from './WebHelper';
5
5
 
6
- const _Asset = require('./Asset');
7
- const _AssetType = require('./AssetType');
8
- const _DataFormat = require('./DataFormat');
9
- const _scratchFetch = require('./scratchFetch');
6
+ import _Asset, {AssetData, AssetId} from './Asset';
7
+ import {AssetType as _AssetType, AssetType} from './AssetType';
8
+ import {DataFormat as _DataFormat, DataFormat} from './DataFormat';
9
+ import _scratchFetch from './scratchFetch';
10
+ import Helper from './Helper';
11
+
12
+ interface HelperWithPriority {
13
+ helper: Helper,
14
+ priority: number
15
+ }
16
+
17
+ export class ScratchStorage {
18
+ public defaultAssetId: Record<AssetType['name'], AssetId>;
19
+ public builtinHelper: BuiltinHelper;
20
+ public webHelper: WebHelper;
21
+
22
+ private _helpers: HelperWithPriority[];
10
23
 
11
- class ScratchStorage {
12
24
  constructor () {
13
25
  this.defaultAssetId = {};
14
26
 
15
27
  this.builtinHelper = new BuiltinHelper(this);
16
28
  this.webHelper = new WebHelper(this);
17
- this.builtinHelper.registerDefaultAssets(this);
29
+ this.builtinHelper.registerDefaultAssets();
18
30
 
19
31
  this._helpers = [
20
32
  {
@@ -85,7 +97,7 @@ class ScratchStorage {
85
97
  * @param {Helper} helper - the helper to be added.
86
98
  * @param {number} [priority] - the priority for this new helper (default: 0).
87
99
  */
88
- addHelper (helper, priority = 0) {
100
+ addHelper (helper: Helper, priority: number = 0) {
89
101
  this._helpers.push({helper, priority});
90
102
  this._helpers.sort((a, b) => b.priority - a.priority);
91
103
  }
@@ -95,7 +107,7 @@ class ScratchStorage {
95
107
  * @param {string} assetId - The id of the asset to fetch.
96
108
  * @returns {?Asset} The asset, if it exists.
97
109
  */
98
- get (assetId) {
110
+ get (assetId: string): _Asset | null {
99
111
  return this.builtinHelper.get(assetId);
100
112
  }
101
113
 
@@ -107,7 +119,7 @@ class ScratchStorage {
107
119
  * @param {string} id - The id for the cached asset.
108
120
  * @returns {string} The calculated id of the cached asset, or the supplied id if the asset is mutable.
109
121
  */
110
- cache (assetType, dataFormat, data, id) {
122
+ cache (assetType: AssetType, dataFormat: DataFormat, data: AssetData, id: AssetId): AssetId {
111
123
  log.warn('Deprecation: Storage.cache is deprecated. Use Storage.createAsset, and store assets externally.');
112
124
  return this.builtinHelper._store(assetType, dataFormat, data, id);
113
125
  }
@@ -121,7 +133,13 @@ class ScratchStorage {
121
133
  * @param {bool} [generateId] - flag to set id to an md5 hash of data if `id` isn't supplied
122
134
  * @returns {Asset} generated Asset with `id` attribute set if not supplied
123
135
  */
124
- createAsset (assetType, dataFormat, data, id, generateId) {
136
+ createAsset (
137
+ assetType: AssetType,
138
+ dataFormat: DataFormat,
139
+ data: AssetData,
140
+ id: AssetId,
141
+ generateId: boolean
142
+ ): _Asset {
125
143
  if (!dataFormat) throw new Error('Tried to create asset without a dataFormat');
126
144
  return new _Asset(assetType, id, dataFormat, data, generateId);
127
145
  }
@@ -133,7 +151,12 @@ class ScratchStorage {
133
151
  * @param {UrlFunction} createFunction - A function which computes a POST URL for asset data.
134
152
  * @param {UrlFunction} updateFunction - A function which computes a PUT URL for asset data.
135
153
  */
136
- addWebStore (types, getFunction, createFunction, updateFunction) {
154
+ addWebStore (
155
+ types: AssetType[],
156
+ getFunction: UrlFunction,
157
+ createFunction?: UrlFunction,
158
+ updateFunction?: UrlFunction
159
+ ): void {
137
160
  this.webHelper.addStore(types, getFunction, createFunction, updateFunction);
138
161
  }
139
162
 
@@ -143,7 +166,7 @@ class ScratchStorage {
143
166
  * @param {Array.<AssetType>} types - The types of asset provided by this source.
144
167
  * @param {UrlFunction} urlFunction - A function which computes a GET URL from an Asset.
145
168
  */
146
- addWebSource (types, urlFunction) {
169
+ addWebSource (types: AssetType[], urlFunction: UrlFunction): void {
147
170
  log.warn('Deprecation: Storage.addWebSource has been replaced by addWebStore.');
148
171
  this.addWebStore(types, urlFunction);
149
172
  }
@@ -153,7 +176,7 @@ class ScratchStorage {
153
176
  * @param {AssetType} type - Get the default ID for assets of this type.
154
177
  * @return {?string} The ID of the default asset of the given type, if any.
155
178
  */
156
- getDefaultAssetId (type) {
179
+ getDefaultAssetId (type: AssetType): AssetId | undefined {
157
180
  if (Object.prototype.hasOwnProperty.call(this.defaultAssetId, type.name)) {
158
181
  return this.defaultAssetId[type.name];
159
182
  }
@@ -167,7 +190,7 @@ class ScratchStorage {
167
190
  * @param {AssetType} type - The type of asset for which the default will be set.
168
191
  * @param {string} id - The default ID to use for this type of asset.
169
192
  */
170
- setDefaultAssetId (type, id) {
193
+ setDefaultAssetId (type: AssetType, id: AssetId): void {
171
194
  this.defaultAssetId[type.name] = id;
172
195
  }
173
196
 
@@ -182,15 +205,14 @@ class ScratchStorage {
182
205
  * If the promise is rejected, there was an error on at least one asset source. HTTP 404 does not count as an
183
206
  * error here, but (for example) HTTP 403 does.
184
207
  */
185
- load (assetType, assetId, dataFormat) {
186
- /** @type {Helper[]} */
208
+ load (assetType: AssetType, assetId: AssetId, dataFormat: DataFormat): Promise<_Asset | null> {
187
209
  const helpers = this._helpers.map(x => x.helper);
188
- const errors = [];
210
+ const errors: unknown[] = [];
189
211
  dataFormat = dataFormat || assetType.runtimeFormat;
190
212
 
191
213
  let helperIndex = 0;
192
- let helper;
193
- const tryNextHelper = err => {
214
+ let helper: Helper;
215
+ const tryNextHelper = (err?: unknown): Promise<_Asset | null> => {
194
216
  if (err) { // Track the error, but continue looking
195
217
  errors.push(err);
196
218
  }
@@ -227,18 +249,19 @@ class ScratchStorage {
227
249
  * @param {?string} [assetId] - The ID of the asset to fetch: a project ID, MD5, etc.
228
250
  * @return {Promise.<object>} A promise for asset metadata
229
251
  */
230
- store (assetType, dataFormat, data, assetId) {
252
+ store (assetType: AssetType, dataFormat: DataFormat | null | undefined, data: AssetData, assetId?: AssetId) {
231
253
  dataFormat = dataFormat || assetType.runtimeFormat;
232
- return new Promise(
233
- (resolve, reject) =>
234
- this.webHelper.store(assetType, dataFormat, data, assetId)
235
- .then(body => {
236
- this.builtinHelper._store(assetType, dataFormat, data, body.id);
237
- return resolve(body);
238
- })
239
- .catch(error => reject(error))
240
- );
254
+
255
+ return this.webHelper.store(assetType, dataFormat, data, assetId)
256
+ .then(body => {
257
+ // The previous logic here ignored that the body can be a string (if it's not a JSON),
258
+ // so just ignore that case.
259
+ // Also, having undefined was the previous behavior
260
+ // eslint-disable-next-line no-undefined
261
+ const id = typeof body === 'string' ? undefined : body.id;
262
+
263
+ this.builtinHelper._store(assetType, dataFormat, data, id);
264
+ return body;
265
+ });
241
266
  }
242
267
  }
243
-
244
- module.exports = ScratchStorage;
package/src/Tool.ts ADDED
@@ -0,0 +1,10 @@
1
+ export type ScratchGetRequest = {url: string} & RequestInit;
2
+ export type ScratchSendRequest = {url: string, withCredentials?: boolean} & RequestInit;
3
+
4
+ export interface Tool {
5
+ get isGetSupported (): boolean;
6
+ get (request: ScratchGetRequest): Promise<Uint8Array | null>;
7
+
8
+ get isSendSupported (): boolean;
9
+ send (request: ScratchSendRequest): Promise<string>;
10
+ }
@@ -1,8 +1,11 @@
1
- const log = require('./log');
1
+ import log from './log';
2
2
 
3
- const Asset = require('./Asset');
4
- const Helper = require('./Helper');
5
- const ProxyTool = require('./ProxyTool');
3
+ import Asset, {AssetData, AssetId} from './Asset';
4
+ import Helper from './Helper';
5
+ import ProxyTool from './ProxyTool';
6
+ import {ScratchGetRequest, ScratchSendRequest, Tool} from './Tool';
7
+ import {AssetType} from './AssetType';
8
+ import {DataFormat} from './DataFormat';
6
9
 
7
10
  const ensureRequestConfig = reqConfig => {
8
11
  if (typeof reqConfig === 'string') {
@@ -20,7 +23,20 @@ const ensureRequestConfig = reqConfig => {
20
23
  * the underlying fetch call (necessary for configuring e.g. authentication)
21
24
  */
22
25
 
23
- class WebHelper extends Helper {
26
+ export type UrlFunction = (asset: Asset) => string | ScratchGetRequest | ScratchSendRequest;
27
+
28
+ interface StoreRecord {
29
+ types: string[],
30
+ get: UrlFunction,
31
+ create?: UrlFunction,
32
+ update?: UrlFunction
33
+ }
34
+
35
+ export default class WebHelper extends Helper {
36
+ public stores: StoreRecord[];
37
+ public assetTool: Tool;
38
+ public projectTool: Tool;
39
+
24
40
  constructor (parent) {
25
41
  super(parent);
26
42
 
@@ -56,7 +72,7 @@ class WebHelper extends Helper {
56
72
  * @param {Array.<AssetType>} types - The types of asset provided by this source.
57
73
  * @param {UrlFunction} urlFunction - A function which computes a URL from an Asset.
58
74
  */
59
- addSource (types, urlFunction) {
75
+ addSource (types: AssetType[], urlFunction: UrlFunction): void {
60
76
  log.warn('Deprecation: WebHelper.addSource has been replaced with WebHelper.addStore.');
61
77
  this.addStore(types, urlFunction);
62
78
  }
@@ -68,7 +84,12 @@ class WebHelper extends Helper {
68
84
  * @param {UrlFunction} createFunction - A function which computes a POST URL for an Asset
69
85
  * @param {UrlFunction} updateFunction - A function which computes a PUT URL for an Asset
70
86
  */
71
- addStore (types, getFunction, createFunction, updateFunction) {
87
+ addStore (
88
+ types: AssetType[],
89
+ getFunction: UrlFunction,
90
+ createFunction?: UrlFunction,
91
+ updateFunction?: UrlFunction
92
+ ): void {
72
93
  this.stores.push({
73
94
  types: types.map(assetType => assetType.name),
74
95
  get: getFunction,
@@ -84,13 +105,13 @@ class WebHelper extends Helper {
84
105
  * @param {DataFormat} dataFormat - The file format / file extension of the asset to fetch: PNG, JPG, etc.
85
106
  * @return {Promise.<Asset>} A promise for the contents of the asset.
86
107
  */
87
- load (assetType, assetId, dataFormat) {
108
+ load (assetType: AssetType, assetId: AssetId, dataFormat: DataFormat): Promise<Asset | null> {
88
109
 
89
110
  /** @type {Array.<{url:string, result:*}>} List of URLs attempted & errors encountered. */
90
- const errors = [];
111
+ const errors: unknown[] = [];
91
112
  const stores = this.stores.slice()
92
113
  .filter(store => store.types.indexOf(assetType.name) >= 0);
93
-
114
+
94
115
  // New empty asset but it doesn't have data yet
95
116
  const asset = new Asset(assetType, assetId, dataFormat);
96
117
 
@@ -100,7 +121,7 @@ class WebHelper extends Helper {
100
121
  }
101
122
 
102
123
  let storeIndex = 0;
103
- const tryNextSource = err => {
124
+ const tryNextSource = (err?: unknown): Promise<Asset | null> => {
104
125
  if (err) {
105
126
  errors.push(err);
106
127
  }
@@ -143,7 +164,12 @@ class WebHelper extends Helper {
143
164
  * @param {?string} assetId - The ID of the asset to fetch: a project ID, MD5, etc.
144
165
  * @return {Promise.<object>} A promise for the response from the create or update request
145
166
  */
146
- store (assetType, dataFormat, data, assetId) {
167
+ store (
168
+ assetType: AssetType,
169
+ dataFormat: DataFormat | undefined,
170
+ data: AssetData,
171
+ assetId?: AssetId
172
+ ): Promise<string | {id: string}> {
147
173
  const asset = new Asset(assetType, assetId, dataFormat);
148
174
  // If we have an asset id, we should update, otherwise create to get an id
149
175
  const create = assetId === '' || assetId === null || typeof assetId === 'undefined';
@@ -168,7 +194,10 @@ class WebHelper extends Helper {
168
194
  }
169
195
 
170
196
  const reqConfig = ensureRequestConfig(
171
- create ? store.create(asset) : store.update(asset)
197
+ // The non-nullability of this gets checked above while looking up the store.
198
+ // Making TS understand that is going to require code refactoring which we currently don't
199
+ // feel safe to do.
200
+ create ? store.create!(asset) : store.update!(asset)
172
201
  );
173
202
  const reqBodyConfig = Object.assign({body: data, method}, reqConfig);
174
203
  return tool.send(reqBodyConfig)
@@ -191,5 +220,3 @@ class WebHelper extends Helper {
191
220
  });
192
221
  }
193
222
  }
194
-
195
- module.exports = WebHelper;
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ import {ScratchStorage} from './ScratchStorage';
2
+ import Asset, {AssetId} from './Asset';
3
+ import {AssetType} from './AssetType';
4
+ import {DataFormat} from './DataFormat';
5
+ import Helper from './Helper';
6
+
7
+ export {
8
+ /**
9
+ * Export for use with NPM & Node.js.
10
+ * @type {ScratchStorage}
11
+ */
12
+ ScratchStorage,
13
+
14
+ Asset,
15
+ AssetId,
16
+ AssetType,
17
+ DataFormat,
18
+ Helper
19
+ };
package/src/log.ts ADDED
@@ -0,0 +1,4 @@
1
+ import minilog from 'minilog';
2
+ minilog.enable();
3
+
4
+ export default minilog('storage');