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.
- package/.browserslistrc +7 -0
- package/CHANGELOG.md +11 -0
- package/dist/{web/f6240eab828e6d415177.worker.js → node/chunks/fetch-worker.eefe62e3c8ee125d3436.js} +105 -180
- package/dist/node/chunks/fetch-worker.eefe62e3c8ee125d3436.js.map +1 -0
- package/dist/node/scratch-storage.js +3898 -6304
- package/dist/node/scratch-storage.js.map +1 -1
- package/dist/types/Asset.d.ts +38 -0
- package/dist/types/AssetType.d.ts +49 -0
- package/dist/types/BuiltinHelper.d.ts +69 -0
- package/dist/types/DataFormat.d.ts +15 -0
- package/dist/types/FetchTool.d.ts +33 -0
- package/dist/types/FetchWorkerTool.d.ts +29 -0
- package/dist/types/Helper.d.ts +20 -0
- package/dist/types/ProxyTool.d.ts +53 -0
- package/dist/types/ScratchStorage.d.ts +202 -0
- package/dist/types/Tool.d.ts +13 -0
- package/dist/types/WebHelper.d.ts +59 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/log.d.ts +2 -0
- package/dist/types/memoizedToString.d.ts +5 -0
- package/dist/web/chunks/fetch-worker.85da71862ab8ee15f1cd.js +2 -0
- package/dist/web/chunks/fetch-worker.85da71862ab8ee15f1cd.js.map +1 -0
- package/dist/web/chunks/fetch-worker.ba5eddd48c8ae259073b.js +676 -0
- package/dist/web/chunks/fetch-worker.ba5eddd48c8ae259073b.js.map +1 -0
- package/dist/web/scratch-storage.js +2002 -2758
- package/dist/web/scratch-storage.js.map +1 -1
- package/dist/web/scratch-storage.min.js +2 -6398
- package/dist/web/scratch-storage.min.js.LICENSE.txt +18 -0
- package/dist/web/scratch-storage.min.js.map +1 -1
- package/jest.config.js +32 -0
- package/package.json +18 -13
- package/src/.eslintrc.js +13 -1
- package/src/Asset.ts +100 -0
- package/src/{AssetType.js → AssetType.ts} +10 -5
- package/src/{BuiltinHelper.js → BuiltinHelper.ts} +44 -35
- package/src/{DataFormat.js → DataFormat.ts} +3 -3
- package/src/{FetchTool.js → FetchTool.ts} +8 -9
- package/src/{FetchWorkerTool.js → FetchWorkerTool.ts} +47 -22
- package/src/{Helper.js → Helper.ts} +10 -5
- package/src/{ProxyTool.js → ProxyTool.ts} +32 -29
- package/src/{ScratchStorage.js → ScratchStorage.ts} +57 -34
- package/src/Tool.ts +10 -0
- package/src/{WebHelper.js → WebHelper.ts} +42 -15
- package/src/index.ts +19 -0
- package/src/log.ts +4 -0
- package/src/{Asset.js → memoizedToString.ts} +16 -76
- package/src/scratchFetch.js +0 -2
- package/src/types.d.ts +3 -0
- package/test/integration/download-known-assets.test.js +1 -1
- package/test/unit/add-helper.test.js +1 -1
- package/test/unit/fetch-tool.test.js +1 -1
- package/test/unit/load-default-assets.test.js +1 -1
- package/test/unit/metadata.test.js +6 -6
- package/tsconfig.json +29 -0
- package/tsconfig.test.json +11 -0
- package/webpack.config.js +58 -65
- package/dist/node/ce8a01e629587e4f90e4.worker.js +0 -4274
- package/dist/node/ce8a01e629587e4f90e4.worker.js.map +0 -1
- package/dist/web/f6240eab828e6d415177.worker.js.map +0 -1
- package/src/index.js +0 -7
- package/src/log.js +0 -4
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
|
|
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
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
62
|
+
const job = this.jobs[message.id];
|
|
63
|
+
if (job) {
|
|
48
64
|
if (message.error) {
|
|
49
|
-
|
|
65
|
+
job.reject(message.error);
|
|
50
66
|
} else {
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2
|
-
|
|
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
|
-
|
|
17
|
-
|
|
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
|
-
|
|
1
|
+
import log from './log';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import BuiltinHelper from './BuiltinHelper';
|
|
4
|
+
import WebHelper, {UrlFunction} from './WebHelper';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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(
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
1
|
+
import log from './log';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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