scratch-storage 2.2.1 → 2.3.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.
Files changed (39) hide show
  1. package/.circleci/config.yml +1 -1
  2. package/dist/node/scratch-storage.js +45 -36
  3. package/dist/node/scratch-storage.js.map +1 -1
  4. package/dist/web/scratch-storage.js +45 -36
  5. package/dist/web/scratch-storage.js.map +1 -1
  6. package/dist/web/scratch-storage.min.js +45 -36
  7. package/dist/web/scratch-storage.min.js.map +1 -1
  8. package/jest.config.js +5 -0
  9. package/package.json +11 -9
  10. package/src/BuiltinHelper.js +3 -3
  11. package/src/scratchFetch.js +13 -0
  12. package/test/.eslintrc.js +5 -1
  13. package/test/{mocks/mock-fetch.js → __mocks__/cross-fetch.js} +25 -16
  14. package/test/fixtures/.gitattributes +3 -0
  15. package/test/fixtures/assets/117504922.json +196 -0
  16. package/test/fixtures/assets/66895930177178ea01d9e610917f8acf.png +0 -0
  17. package/test/fixtures/assets/6e8bd9ae68fdb02b7e1e3df656a75635.svg +37 -0
  18. package/test/fixtures/assets/7e24c99c1b853e52f8e7f9004416fa34.png +0 -0
  19. package/test/fixtures/assets/83c36d806dc92327b9e7049a565c6bff.wav +0 -0
  20. package/test/fixtures/assets/f88bf1935daea28f8ca098462a31dbb0.svg +35 -0
  21. package/test/fixtures/assets/fe5e3566965f9de793beeffce377d054.jpg +0 -0
  22. package/test/fixtures/known-assets.js +60 -0
  23. package/test/integration/download-known-assets.test.js +141 -0
  24. package/test/transformers/.eslintrc.js +8 -0
  25. package/test/transformers/arraybuffer-loader.js +11 -0
  26. package/test/unit/{add-helper.js → add-helper.test.js} +16 -20
  27. package/test/unit/fetch-tool.test.js +56 -0
  28. package/test/unit/load-default-assets.test.js +67 -0
  29. package/test/unit/metadata.test.js +129 -0
  30. package/webpack.config.js +4 -0
  31. package/.nyc_output/f1105466-7461-4f73-8b18-ee824eb652b1.json +0 -1
  32. package/.nyc_output/fd8f05df-43a0-4695-b45d-f4877b37f387.json +0 -1
  33. package/.nyc_output/processinfo/f1105466-7461-4f73-8b18-ee824eb652b1.json +0 -1
  34. package/.nyc_output/processinfo/fd8f05df-43a0-4695-b45d-f4877b37f387.json +0 -1
  35. package/.nyc_output/processinfo/index.json +0 -1
  36. package/test/integration/download-known-assets.js +0 -111
  37. package/test/unit/fetch-tool.js +0 -57
  38. package/test/unit/load-default-assets.js +0 -48
  39. package/test/unit/metadata.js +0 -147
package/jest.config.js ADDED
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ transform: {
3
+ '\\.(png|svg|wav)$': '<rootDir>/test/transformers/arraybuffer-loader.js'
4
+ }
5
+ };
package/package.json CHANGED
@@ -1,25 +1,25 @@
1
1
  {
2
2
  "name": "scratch-storage",
3
- "version": "2.2.1",
3
+ "version": "2.3.1",
4
4
  "description": "Load and store project and asset files for Scratch 3.0",
5
5
  "license": "BSD-3-Clause",
6
6
  "homepage": "https://github.com/LLK/scratch-storage#readme",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "https://github.com/LLK/scratch-storage.git",
10
- "sha": "4294057468737f2e179afe2cc963326d20942aed"
10
+ "sha": "135b4dc424b873795602d6e2a112453128c2745b"
11
11
  },
12
12
  "main": "./dist/node/scratch-storage.js",
13
- "browser": "./src/index.js",
13
+ "browser": "./dist/web/scratch-storage.js",
14
14
  "scripts": {
15
15
  "build": "webpack --progress --colors --bail",
16
16
  "coverage": "tap ./test/{unit,integration}/*.js --coverage --coverage-report=lcov",
17
17
  "commitmsg": "commitlint -e $GIT_PARAMS",
18
- "lint": "eslint .",
19
- "tap-integration": "tap ./test/integration/*.js",
20
- "tap-unit": "tap ./test/unit/*.js",
21
- "tap": "npm run tap-unit && npm run tap-integration",
22
- "test": "npm run lint && npm run tap",
18
+ "test": "npm run test:lint && jest \"test[\\\\/](unit|integration)\"",
19
+ "test:clearCache": "jest --clearCache",
20
+ "test:lint": "eslint .",
21
+ "test:integration": "jest \"test[\\\\/]integration\"",
22
+ "test:unit": "jest \"test[\\\\/]unit\"",
23
23
  "version": "json -f package.json -I -e \"this.repository.sha = '$(git log -n1 --pretty=format:%H)'\"",
24
24
  "watch": "webpack --progress --colors --watch",
25
25
  "semantic-release": "semantic-release"
@@ -45,17 +45,19 @@
45
45
  "@commitlint/cli": "8.2.0",
46
46
  "@commitlint/config-conventional": "8.2.0",
47
47
  "@commitlint/travis-cli": "8.2.0",
48
+ "@types/jest": "29.5.1",
48
49
  "babel-eslint": "10.1.0",
49
50
  "babel-loader": "8.0.6",
50
51
  "cz-conventional-changelog": "3.3.0",
51
52
  "eslint": "7.27.0",
52
53
  "eslint-config-scratch": "6.0.0",
54
+ "eslint-plugin-jest": "27.2.1",
53
55
  "eslint-plugin-react": "7.24.0",
54
56
  "file-loader": "4.1.0",
55
57
  "husky": "1.3.1",
58
+ "jest": "29.5.0",
56
59
  "json": "^9.0.4",
57
60
  "semantic-release": "^15.10.5",
58
- "tap": "16.3.4",
59
61
  "uglifyjs-webpack-plugin": "2.2.0",
60
62
  "webpack": "4.46.0",
61
63
  "webpack-cli": "3.1.2"
@@ -24,7 +24,7 @@ const DefaultAssets = [
24
24
  format: DataFormat.PNG,
25
25
  id: null,
26
26
  data: Buffer.from(
27
- require('!arraybuffer-loader!./builtins/defaultBitmap.png') // eslint-disable-line global-require
27
+ require('./builtins/defaultBitmap.png') // eslint-disable-line global-require
28
28
  )
29
29
  },
30
30
  {
@@ -32,7 +32,7 @@ const DefaultAssets = [
32
32
  format: DataFormat.WAV,
33
33
  id: null,
34
34
  data: Buffer.from(
35
- require('!arraybuffer-loader!./builtins/defaultSound.wav') // eslint-disable-line global-require
35
+ require('./builtins/defaultSound.wav') // eslint-disable-line global-require
36
36
  )
37
37
  },
38
38
  {
@@ -40,7 +40,7 @@ const DefaultAssets = [
40
40
  format: DataFormat.SVG,
41
41
  id: null,
42
42
  data: Buffer.from(
43
- require('!arraybuffer-loader!./builtins/defaultVector.svg') // eslint-disable-line global-require
43
+ require('./builtins/defaultVector.svg') // eslint-disable-line global-require
44
44
  )
45
45
  }
46
46
  ];
@@ -23,6 +23,19 @@ const metadata = new crossFetch.Headers();
23
23
  * @returns {boolean} true if `metadata` has contents, or false if it is empty.
24
24
  */
25
25
  const hasMetadata = () => {
26
+ /* global self */
27
+ const searchParams = (
28
+ typeof self !== 'undefined' &&
29
+ self &&
30
+ self.location &&
31
+ self.location.search &&
32
+ self.location.search.split(/[?&]/)
33
+ ) || [];
34
+ if (!searchParams.includes('scratchMetadata=1')) {
35
+ // for now, disable this feature unless scratchMetadata=1
36
+ // TODO: remove this check once we're sure the feature works correctly in production
37
+ return false;
38
+ }
26
39
  for (const _ of metadata) {
27
40
  return true;
28
41
  }
package/test/.eslintrc.js CHANGED
@@ -1,3 +1,7 @@
1
1
  module.exports = {
2
- extends: ['scratch/node', 'scratch/es6']
2
+ extends: ['scratch/es6', 'plugin:jest/recommended'],
3
+ env: {
4
+ jest: true
5
+ },
6
+ plugins: ['jest']
3
7
  };
@@ -1,5 +1,6 @@
1
1
  const TextEncoder = require('util').TextEncoder;
2
- const crossFetch = require('cross-fetch');
2
+ const crossFetch = jest.requireActual('cross-fetch');
3
+ const knownAssets = require('../fixtures/known-assets.js');
3
4
 
4
5
  const Headers = crossFetch.Headers;
5
6
  const successText = 'successful response';
@@ -38,23 +39,31 @@ const mockFetch = (resource, options) => {
38
39
  options.mockFetchTestData.headers = new Headers(options.headers);
39
40
  options.mockFetchTestData.headersCount = Array.from(options.mockFetchTestData.headers).length;
40
41
  }
41
- switch (resource) {
42
- case '200':
42
+
43
+ const assetInfo = knownAssets[resource];
44
+ if (assetInfo) {
43
45
  results.ok = true;
44
46
  results.status = 200;
45
- results.text = () => Promise.resolve(successText);
46
- results.arrayBuffer = () => Promise.resolve(new TextEncoder().encode(successText));
47
- break;
48
- case '404':
49
- results.ok = false;
50
- results.status = 404;
51
- break;
52
- case '500':
53
- results.ok = false;
54
- results.status = 500;
55
- break;
56
- default:
57
- throw new Error('unimplemented');
47
+ results.arrayBuffer = () => Promise.resolve(assetInfo.content);
48
+ } else {
49
+ switch (resource) {
50
+ case '200':
51
+ results.ok = true;
52
+ results.status = 200;
53
+ results.text = () => Promise.resolve(successText);
54
+ results.arrayBuffer = () => Promise.resolve(new TextEncoder().encode(successText));
55
+ break;
56
+ case '404':
57
+ results.ok = false;
58
+ results.status = 404;
59
+ break;
60
+ case '500':
61
+ results.ok = false;
62
+ results.status = 500;
63
+ break;
64
+ default:
65
+ throw new Error(`mockFetch doesn't know how to download: ${resource}`);
66
+ }
58
67
  }
59
68
  return Promise.resolve(results);
60
69
  };
@@ -0,0 +1,3 @@
1
+ # Assets in this directory must be preserved exactly
2
+ # to ensure the MD5 hash matches the expected value.
3
+ * binary
@@ -0,0 +1,196 @@
1
+ {
2
+ "objName": "Stage",
3
+ "costumes": [{
4
+ "costumeName": "backdrop1",
5
+ "baseLayerID": -1,
6
+ "baseLayerMD5": "ba5d7bbc200d7925664a0878421c4ed2.png",
7
+ "bitmapResolution": 2,
8
+ "rotationCenterX": 480,
9
+ "rotationCenterY": 360
10
+ }],
11
+ "currentCostumeIndex": 0,
12
+ "penLayerMD5": "5c81a336fab8be57adc039a8a2b33ca9.png",
13
+ "penLayerID": -1,
14
+ "tempoBPM": 60,
15
+ "videoAlpha": 0.5,
16
+ "children": [{
17
+ "objName": "seymour cropped",
18
+ "scripts": [[89, 102, [["whenGreenFlag"], ["setSizeTo:", 85]]]],
19
+ "sounds": [{
20
+ "soundName": "pop",
21
+ "soundID": -1,
22
+ "md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
23
+ "sampleCount": 258,
24
+ "rate": 11025,
25
+ "format": ""
26
+ }],
27
+ "costumes": [{
28
+ "costumeName": "SeymourPapert001",
29
+ "baseLayerID": -1,
30
+ "baseLayerMD5": "2c8559602a79c7ec929636c3a5ea0291.png",
31
+ "bitmapResolution": 2,
32
+ "rotationCenterX": 204,
33
+ "rotationCenterY": 262
34
+ }],
35
+ "currentCostumeIndex": 0,
36
+ "scratchX": 115,
37
+ "scratchY": -28,
38
+ "scale": 0.85,
39
+ "direction": 90,
40
+ "rotationStyle": "normal",
41
+ "isDraggable": false,
42
+ "indexInLibrary": 2,
43
+ "visible": true,
44
+ "spriteInfo": {
45
+ }
46
+ },
47
+ {
48
+ "objName": "Sprite1",
49
+ "scripts": [[48,
50
+ 24,
51
+ [["whenGreenFlag"],
52
+ ["setSizeTo:", 110],
53
+ ["wait:elapsed:from:", 1],
54
+ ["say:", "Hi. I want to introduce you to Seymour Papert."],
55
+ ["doPlaySoundAndWait", "papert1"],
56
+ ["say:", "Seymour developed Logo, the first programming language for kids"],
57
+ ["doPlaySoundAndWait", "papert2"],
58
+ ["say:", "He believed that kids learn best"],
59
+ ["doPlaySoundAndWait", "papert3"],
60
+ ["say:", "when they are given the chance to create things and express themselves"],
61
+ ["doPlaySoundAndWait", "papert4"],
62
+ ["say:", "When we developed Scratch, we were very inspired by Seymour's ideas"],
63
+ ["doPlaySoundAndWait", "papert5"],
64
+ ["say:", "So you might call Seymour the grandfather of Scratch"],
65
+ ["doPlaySoundAndWait", "papert6"],
66
+ ["say:", "Sadly, Seymour passed away last week, on July 31, 2016"],
67
+ ["doPlaySoundAndWait", "papert7"],
68
+ ["say:", "But every time you create and share a Scratch project"],
69
+ ["doPlaySoundAndWait", "papert8"],
70
+ ["say:", "you're helping to keep Seymour's spirit alive"],
71
+ ["doPlaySoundAndWait", "papert9"],
72
+ ["say:", "So Scratch on!"],
73
+ ["doPlaySoundAndWait", "papert10"],
74
+ ["say:", "And Papert on!"],
75
+ ["doPlaySoundAndWait", "papert11"],
76
+ ["wait:elapsed:from:", 5],
77
+ ["say:", ""]]]],
78
+ "sounds": [{
79
+ "soundName": "papert1",
80
+ "soundID": -1,
81
+ "md5": "876c45f175060737364334daed9366a1.wav",
82
+ "sampleCount": 84993,
83
+ "rate": 22050,
84
+ "format": ""
85
+ },
86
+ {
87
+ "soundName": "papert2",
88
+ "soundID": -1,
89
+ "md5": "e439ca64ef29692ed611bb769fdbe543.wav",
90
+ "sampleCount": 103425,
91
+ "rate": 22050,
92
+ "format": ""
93
+ },
94
+ {
95
+ "soundName": "papert3",
96
+ "soundID": -1,
97
+ "md5": "0bd55bc2b3ff5741bafe981decdaca5b.wav",
98
+ "sampleCount": 54273,
99
+ "rate": 22050,
100
+ "format": ""
101
+ },
102
+ {
103
+ "soundName": "papert4",
104
+ "soundID": -1,
105
+ "md5": "123abc35f1934ed471436c59f7b99e2f.wav",
106
+ "sampleCount": 93185,
107
+ "rate": 22050,
108
+ "format": ""
109
+ },
110
+ {
111
+ "soundName": "papert5",
112
+ "soundID": -1,
113
+ "md5": "cd9f672266016a0e55152b2a3398c8d9.wav",
114
+ "sampleCount": 113665,
115
+ "rate": 22050,
116
+ "format": ""
117
+ },
118
+ {
119
+ "soundName": "papert6",
120
+ "soundID": -1,
121
+ "md5": "d38287334764b7d4cd2c5db59548234d.wav",
122
+ "sampleCount": 84993,
123
+ "rate": 22050,
124
+ "format": ""
125
+ },
126
+ {
127
+ "soundName": "papert7",
128
+ "soundID": -1,
129
+ "md5": "fab6bf9923265a999bcfea1e62384a2c.wav",
130
+ "sampleCount": 144385,
131
+ "rate": 22050,
132
+ "format": ""
133
+ },
134
+ {
135
+ "soundName": "papert8",
136
+ "soundID": -1,
137
+ "md5": "6826373fa0554c582f920cc65620395e.wav",
138
+ "sampleCount": 82945,
139
+ "rate": 22050,
140
+ "format": ""
141
+ },
142
+ {
143
+ "soundName": "papert9",
144
+ "soundID": -1,
145
+ "md5": "c6b907cbfe0081748ae83cb3bb6aa6d8.wav",
146
+ "sampleCount": 74753,
147
+ "rate": 22050,
148
+ "format": ""
149
+ },
150
+ {
151
+ "soundName": "papert10",
152
+ "soundID": -1,
153
+ "md5": "f455697888f2e7b36072a2189fc78bbc.wav",
154
+ "sampleCount": 39937,
155
+ "rate": 22050,
156
+ "format": ""
157
+ },
158
+ {
159
+ "soundName": "papert11",
160
+ "soundID": -1,
161
+ "md5": "fb58460f1cd8629eae3b3c58148984ad.wav",
162
+ "sampleCount": 54273,
163
+ "rate": 22050,
164
+ "format": ""
165
+ }],
166
+ "costumes": [{
167
+ "costumeName": "-mres-cartoon",
168
+ "baseLayerID": -1,
169
+ "baseLayerMD5": "dd88c3596176f24479a8e8ec9b6c8278.png",
170
+ "bitmapResolution": 2,
171
+ "rotationCenterX": 122,
172
+ "rotationCenterY": 204
173
+ }],
174
+ "currentCostumeIndex": 0,
175
+ "scratchX": -137.95,
176
+ "scratchY": -71,
177
+ "scale": 1.1,
178
+ "direction": 90,
179
+ "rotationStyle": "normal",
180
+ "isDraggable": false,
181
+ "indexInLibrary": 1,
182
+ "visible": true,
183
+ "spriteInfo": {
184
+ }
185
+ }],
186
+ "info": {
187
+ "spriteCount": 2,
188
+ "hasCloudData": false,
189
+ "projectID": "117504922",
190
+ "flashVersion": "MAC 22,0,0,209",
191
+ "swfVersion": "v448",
192
+ "userAgent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/51.0.2704.103 Safari\/537.36",
193
+ "scriptCount": 2,
194
+ "videoOn": false
195
+ }
196
+ }
@@ -0,0 +1,37 @@
1
+ <svg version="1.1" id="cat" x="0px" y="0px" width="95px" height="111px" viewBox="0 0 95 111" enable-background="new 0 0 95 111" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2
+ <g>
3
+ <g id="Layer_8">
4
+ <path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M23.754,82.506c-2.871-1.402-10.511-2.518-14.771-6.695&#xD;&#xA;&#x9;&#x9;c-9.431-9.246-3.824-21.732,0.704-21.797c3.611-0.061,3.363,5.482,4.622,11.145c0.875,3.939,1.673,6.988,6.407,9.674&#xD;&#xA;&#x9;&#x9;c4.735,2.688,6.918,2.916,6.918,2.916s3.258,0.791,1.596,2.562C27.568,82.076,23.754,82.506,23.754,82.506z"/>
5
+ <path fill="#FFFFFF" d="M14.291,66.369c-0.115-0.459-0.222-0.928-0.33-1.413c-1.172-5.271-0.941-10.432-4.303-10.377&#xD;&#xA;&#x9;&#x9;c-3.433,0.05-7.53,7.771-4.19,15.285c0.193-0.143,0.399-0.336,0.569-0.588c0.451-0.664,0.821-2.969,2.654-3.688&#xD;&#xA;&#x9;&#x9;C9.989,65.082,12.778,65.869,14.291,66.369z"/>
6
+ </g>
7
+ <path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M52.543,61.473c0,0,1.455,2.342,1.519,4.969&#xD;&#xA;&#x9;c0.065,2.723-0.351,6.984,1.804,8.158c3.381,1.842,9.959-0.463,10.174,5.191c0.211,5.652-10.152,3.623-13.299,2.547&#xD;&#xA;&#x9;c-2.695-0.926-4.559-1.758-5.18-5.182c0,0-3.764-13.969-0.196-15.59C50.933,59.945,52.543,61.473,52.543,61.473z"/>
8
+ <path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M14.61,89.584c0,0,1.504,10.391-5.922,8.387&#xD;&#xA;&#x9;c-5.586-1.51-5.705-20.68-0.406-20.691c3.237-0.006,4.425,2.859,4.425,2.859s4.385,4.812,6.95,4.312&#xD;&#xA;&#x9;c2.565-0.502,7.978-6.703,7.978-6.703s2.875-2.539,6.146,0.023c3.271,2.561,1.521,6.178,1.521,6.178s-6.07,7.072-11.014,8.072&#xD;&#xA;&#x9;c-4.945,1.002-9.375-2.568-11.58-3.451"/>
9
+ <g id="Layer_6">
10
+ <path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M42.435,76.238c0,0,4.866,4.062,8.166,7.52&#xD;&#xA;&#x9;&#x9;c4.384,4.592-3.339,15.039-3.339,15.039s11.234,6.467,2.998,11c-2.657,1.467-15.661-13.225-15.661-13.225s7.979-7.475,4.972-10.172&#xD;&#xA;&#x9;&#x9;c-4.964-4.449-10.771-4.959-11.6-9.824c-0.502-2.957,0.613-4.502,2.658-7.053c1.354-1.689,2.084-3.182,2.084-3.182&#xD;&#xA;&#x9;&#x9;s1.135-7.238,4.289-8.254c3.153-1.014,8.842,0.057,10.758,0.395c1.914,0.338,6.645,1.877,4.617,9.922c0,0-1.451,3.771-2.789,5.762&#xD;&#xA;&#x9;&#x9;c-1.604,2.389-2.854,3.924-4.08,4.867"/>
11
+ <path fill="#FFFFFF" d="M50.182,72.631c0,0,2.55-2.748-2.551-5.494c-5.102-2.746-7.449-0.016-9.024,1.572&#xD;&#xA;&#x9;&#x9;c-2.731,2.742,3.334,7.453,3.334,7.453l3.383,2.564c0,0,1.916-1.582,2.701-2.957C48.807,74.396,50.182,72.631,50.182,72.631"/>
12
+ </g>
13
+ <path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M24.428,65.395c0.939,0.057,3.625,0.213,6.064,0.391&#xD;&#xA;&#x9;c3.014,0.217,8.451-0.451,5.801-5.098c-2.649-4.65-6.74-3.674-11.961-3.754c-2.812-0.043-5.785-1.412-7.322,1.895&#xD;&#xA;&#x9;c-1.534,3.312-5.051,13.338,2.313,15.396c7.366,2.057,4.618-8.137,4.95-10.457"/>
14
+ <g id="Layer_2">
15
+ <path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M51.709,10.156c-1.54-0.143-4.75-0.316-6.518-0.231&#xD;&#xA;&#x9;&#x9;c-4.728,0.225-9.224,1.928-9.224,1.928L22.949,3.357l2.235,18.906c0.646-0.782-10.555,12.804-3.479,24.224&#xD;&#xA;&#x9;&#x9;c7.08,11.426,22.233,16.518,40.988,12.792c18.755-3.728,23.229-14.531,21.986-20.246c-1.242-5.714-8.322-7.823-8.322-7.823&#xD;&#xA;&#x9;&#x9;s-0.09-4.48-3.328-9.97c-1.926-3.268-8.348-8.041-8.348-8.041L61.822,1.647L54.37,8.851L51.709,10.156z"/>
16
+ <path fill="#FFFFFF" d="M75.42,31.066l-2.483-2.064l-9.115,2.661c0,0,0,3.419-4.367,4.367c-4.37,0.951-11.211-2.277-11.211-2.277&#xD;&#xA;&#x9;&#x9;l-7.784,3.417c0,0-8.437,0.928-8.739,6.081C31.048,54.704,45.1,59.478,50.425,59.783c2.905,0.167,8.235-0.337,12.277-1.14&#xD;&#xA;&#x9;&#x9;c17.752-3.235,22.551-13.92,21.309-19.635c-1.242-5.714-7.977-7.196-7.977-7.196L75.42,31.066z"/>
17
+ <path fill="none" stroke="#231F20" stroke-width="1.2" d="M9.673,42.155c0,0,4.107,0.374,5.974,0.268&#xD;&#xA;&#x9;&#x9;c1.865-0.107,5.492-0.587,5.492-0.587"/>
18
+ <path fill="none" stroke="#231F20" stroke-width="1.2" d="M80.656,36.671c0,0,4.549-0.743,6.859-1.549&#xD;&#xA;&#x9;&#x9;c2.715-0.942,4.543-2.545,4.543-2.545"/>
19
+ <path fill="none" stroke="#231F20" stroke-width="1.2" d="M21.337,37.909c0,0-2.384-1.777-6.117-3.43&#xD;&#xA;&#x9;&#x9;c-4.134-1.831-6.405-2.303-6.405-2.303"/>
20
+ <path fill="none" stroke="#231F20" stroke-width="1.2" d="M81.117,42.622c0,0,2.726,1.104,5.533,1.385&#xD;&#xA;&#x9;&#x9;c2.77,0.276,4.647,0.11,4.647,0.11"/>
21
+ <path fill="none" stroke="#000000" stroke-linecap="round" stroke-miterlimit="10" d="M51.349,10.212&#xD;&#xA;&#x9;&#x9;c2.84,0.7,3.888,1.469,3.888,1.469"/>
22
+ <line fill="none" stroke="#000000" x1="32.898" y1="9.684" x2="38.956" y2="14.042"/>
23
+ </g>
24
+ <g id="Layer_5">
25
+ <path fill="#FFFFFF" stroke="#231F20" d="M70.84,21.366c2.924,4.479,3.033,9.591,0.242,11.415&#xD;&#xA;&#x9;&#x9;c-2.793,1.825-7.426-0.332-10.354-4.813c-2.932-4.48-3.037-9.589-0.244-11.415C63.275,14.73,67.913,16.884,70.84,21.366z"/>
26
+ <path fill="#231F20" d="M70.089,28.522c0,1.08-0.802,1.956-1.8,1.956c-0.993,0-1.803-0.877-1.803-1.956&#xD;&#xA;&#x9;&#x9;c0-1.08,0.81-1.958,1.803-1.958C69.287,26.564,70.089,27.442,70.089,28.522"/>
27
+ </g>
28
+ <g id="Layer_7">
29
+ <path fill="#FFFFFF" stroke="#231F20" d="M46.867,24.619c2.926,4.48,2.619,9.862-0.681,12.015&#xD;&#xA;&#x9;&#x9;c-3.302,2.159-8.351,0.272-11.276-4.208c-2.928-4.48-2.624-9.86,0.678-12.017C38.891,18.253,43.938,20.137,46.867,24.619z"/>
30
+ <path fill="#231F20" d="M45.079,30.507c0,1.081-0.803,1.957-1.801,1.957c-0.992,0-1.803-0.878-1.803-1.957&#xD;&#xA;&#x9;&#x9;c0-1.08,0.811-1.957,1.803-1.957C44.274,28.55,45.079,29.427,45.079,30.507"/>
31
+ </g>
32
+ <path fill="#5E4A42" stroke="#000000" d="M58.766,33.926c1.854,0,4.555-0.284,4.697,0.569c0.143,0.855-1.709,4.203-2.988,4.345&#xD;&#xA;&#x9;c-1.283,0.142-6.125-2.353-6.195-3.919C54.206,33.355,57.055,33.926,58.766,33.926z"/>
33
+ <g id="Layer_4">
34
+ <path fill="none" stroke="#231F20" stroke-width="1.2" d="M45.774,41.235c0,0,10.347,3.054,14.217,3.897&#xD;&#xA;&#x9;&#x9;c3.868,0.842,10.851,1.684,10.851,1.684s-7.99,10.245-17.328,7.644C44.176,51.863,44.345,45.975,45.774,41.235z"/>
35
+ </g>
36
+ </g>
37
+ </svg>
@@ -0,0 +1,35 @@
1
+ <svg version="1.1" id="Scratch_Cat_A" x="0px" y="0px" width="94.227px" height="104.333px" viewBox="0 -2 94.227 104.333" enable-background="new 0 -2 94.227 104.333" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2
+ <g>
3
+ <path id="Back_Leg" fill="#FAA51D" stroke="#000000" d="M38.988,80.967c0,0-8.832,6.201-17.07,8.412l0.086,0.215&#xD;&#xA;&#x9;c1.247,1.824,5.869,7.498-0.334,9.496c-5.333,1.718-15.12-13.104-10.821-15.901c2.626-1.713,4.892-0.252,4.892-0.252&#xD;&#xA;&#x9;s3.475-1.069,6.001-2.345c4.304-2.162,5.784-3.453,5.784-3.453s4.185-4.307,6.856-4.137C37.051,73.172,42.44,77.715,38.988,80.967z&#xD;&#xA;&#x9; M19.646,90.09l2.271-0.711"/>
4
+ <g id="Tail">
5
+ <path id="Tail_1_" fill="#FAA51D" stroke="#000000" d="M23.295,73.217c-2.415-0.451-5.305-1.311-7.742-3.504&#xD;&#xA;&#x9;&#x9;c-5.451-4.906-7.194-13-11.048-10.914c-3.856,2.088-3.782,15.166,8.353,19.195c4.182,1.391,7.998,1.395,11.091,1.312&#xD;&#xA;&#x9;&#x9;c0.812-0.026,7.718-0.655,10.079-4.075c2.361-3.419,0.719-4.271-0.09-4.744C33.128,70.016,26.711,73.855,23.295,73.217z"/>
6
+ <path id="Tail_Tip" fill="#FFFFFF" d="M4.641,59.107c-1.437,0.778-2.256,3.397-1.994,6.374c0.141,1.594,1.104,5.652,4.968,9.072&#xD;&#xA;&#x9;&#x9;c0.275-0.545-0.975-2.936,0.742-4.379c1.837-1.543,4.86-1.225,5.853-1.061c-1.663-1.812-3.004-3.812-4.176-5.64&#xD;&#xA;&#x9;&#x9;C7.751,59.922,6.475,58.116,4.641,59.107z"/>
7
+ </g>
8
+ <path id="R_Arm" fill="#FAA51D" stroke="#000000" d="M52.94,69.096c0,0,1.234,0.166,4.744,3.677c3.509,3.509,6.025,2.16,8.911,0.724&#xD;&#xA;&#x9;c2.877-1.442,10.536-6.127,6.489-9.817c-4.05-3.688-6.207,1.146-9.716,2.405c-3.511,1.26-5.06-2.487-6.856-4.287&#xD;&#xA;&#x9;c-0.59-0.594-1.188-1.098-1.729-1.506c0,0-0.972-0.758-1.905,2.791C51.943,66.624,50.933,67.627,52.94,69.096z"/>
9
+ <g id="Front_Leg_x2F_Body_1_">
10
+ <path id="Front_Leg_x2F_Body" fill="#FAA51D" stroke="#000000" d="M47.524,76.224c1.188-0.912,2.396-2.401,3.951-4.714&#xD;&#xA;&#x9;&#x9;c1.296-1.926,2.699-5.577,2.699-5.577c0.875-2.521,1.935-6.576-1.901-7.296c-1.553-0.291-4.079-0.098-7.67-0.776&#xD;&#xA;&#x9;&#x9;c-3.593-0.681-6.798-2.522-9.517,2.233c-2.719,4.757-9.591,8.271-1.057,16.562c0,0,4.901,3.842,10.765,9.639&#xD;&#xA;&#x9;&#x9;c4.832,4.774,12.044,10.603,12.044,10.603s18.973,2.188,19.534-0.694c1.923-9.789-14.775-6.91-14.775-6.91&#xD;&#xA;&#x9;&#x9;s-4.604-3.933-6.727-5.795c-3.477-3.058-11.125-10.771-11.125-10.771"/>
11
+ <path id="Belly" fill="#FFFFFF" d="M49.74,64.551C44.8,61.895,42.526,64.536,41,66.073c-2.645,2.654,3.442,6.61,3.442,6.61&#xD;&#xA;&#x9;&#x9;l3.062,3.089c2.999-3.496,4.579-5.874,5.292-7.934C52.654,66.897,51.918,65.725,49.74,64.551z"/>
12
+ </g>
13
+ <path id="L_Arm" fill="#FAA51D" stroke="#000000" d="M30.697,67.43c0.749-0.571,2.89-2.202,4.854-3.657&#xD;&#xA;&#x9;c2.428-1.799,6.117-5.849,1.077-7.646c-5.04-1.801-7.508,1.604-11.52,4.945c-2.159,1.801-5.308,2.698-4.319,6.209&#xD;&#xA;&#x9;c0.993,3.512,4.862,13.407,11.789,10.17c6.93-3.238-1.799-9.181-3.06-11.156"/>
14
+ <g id="Head">
15
+ <g id="Head_1_">
16
+ <path id="Head_Fill" fill="#FAA51D" stroke="#000000" d="M86.452,37.457c-1.472-6.447-8.891-7.579-8.891-7.579&#xD;&#xA;&#x9;&#x9;&#x9;s0.176-5.351-2.628-10.347c-1.856-3.307-8.351-8.042-8.351-8.042l-2.854-11.55l-7.43,7.32l-2.854,1.105&#xD;&#xA;&#x9;&#x9;&#x9;c-1.539-0.143-4.718-0.312-6.485-0.227c-4.729,0.225-9.227,2.036-9.227,2.036L24.854,1.649l2.233,18.91&#xD;&#xA;&#x9;&#x9;&#x9;c0.646-0.786-10.553,12.802-3.477,24.22c3.319,5.36,10.159,10.124,15.599,11.947c4.062,1.36,10.833,1.737,14,1.737&#xD;&#xA;&#x9;&#x9;&#x9;c2.646,0,7.768-0.541,10.32-0.979c3.021-0.519,6.479-1.629,10.055-3.108C83.081,50.442,87.922,43.904,86.452,37.457z&#xD;&#xA;&#x9;&#x9;&#x9; M39.771,12.212l-2.035-2.039 M53.688,8.212c2,0.333,4.333,1.366,4.333,1.366"/>
17
+ <path id="Muzzle" fill="#FFFFFF" d="M77.146,30.129l-2.881-3.107l-9.113,2.661c0,0.01-0.005,1.619-1.42,2.933&#xD;&#xA;&#x9;&#x9;&#x9;c-0.646,0.6-1.583,1.138-2.948,1.435c-4.371,0.951-11.212-2.277-11.212-2.277l-7.784,3.417c0,0-4.744,0.522-7.244,2.956&#xD;&#xA;&#x9;&#x9;&#x9;c-0.833,0.812-1.417,1.835-1.492,3.123c-0.493,8.398,8.68,13.252,15.083,15.602c1.778,0.652,2.388,0.851,3.472,1.139&#xD;&#xA;&#x9;&#x9;&#x9;c0,0,7.424-0.195,10.181-0.588c3.032-0.432,7.578-1.758,11.154-3.236c9.498-3.933,14.601-10.024,13.129-16.471&#xD;&#xA;&#x9;&#x9;&#x9;C84.597,31.265,77.146,30.129,77.146,30.129z"/>
18
+ </g>
19
+ <g id="L_Eye">
20
+ <path id="Eyeball_1_" fill="#FFFFFF" stroke="#000000" d="M48.638,22.831c2.926,4.48,2.618,9.862-0.682,12.015&#xD;&#xA;&#x9;&#x9;&#x9;c-3.303,2.159-8.352,0.272-11.275-4.208c-2.928-4.48-2.624-9.86,0.678-12.017C40.661,16.465,45.709,18.349,48.638,22.831"/>
21
+ <path id="Pupil_1_" d="M46.85,28.719c0,1.081-0.803,1.957-1.801,1.957c-0.992,0-1.804-0.878-1.804-1.957&#xD;&#xA;&#x9;&#x9;&#x9;c0-1.08,0.812-1.957,1.804-1.957C46.045,26.762,46.85,27.639,46.85,28.719"/>
22
+ </g>
23
+ <g id="R_Eye">
24
+ <path id="Eyeball" fill="#FFFFFF" stroke="#000000" d="M72.361,19.75c2.925,4.479,3.283,9.419,0.492,11.243&#xD;&#xA;&#x9;&#x9;&#x9;c-2.793,1.825-7.181-0.552-10.104-5.034c-2.933-4.48-3.079-9.134-0.286-10.959C65.25,13.177,69.434,15.268,72.361,19.75"/>
25
+ <path id="Pupil" d="M71.86,26.734c0,1.08-0.802,1.956-1.8,1.956c-0.993,0-1.804-0.877-1.804-1.956c0-1.08,0.811-1.958,1.804-1.958&#xD;&#xA;&#x9;&#x9;&#x9;C71.058,24.776,71.86,25.654,71.86,26.734"/>
26
+ </g>
27
+ <path id="Nose" fill="#5E4A42" stroke="#000000" d="M60.536,32.138c1.854,0,4.556-0.284,4.696,0.569&#xD;&#xA;&#x9;&#x9;c0.145,0.855-1.709,4.203-2.987,4.345c-1.282,0.142-6.125-2.353-6.194-3.919C55.977,31.567,58.825,32.138,60.536,32.138"/>
28
+ <path id="Whisker_3_" d="M22.75,36.602c-0.023-0.017-2.382-1.76-6.002-3.362c-4.012-1.777-6.263-2.26-6.284-2.265l0.245-1.175&#xD;&#xA;&#x9;&#x9;c0.095,0.02,2.381,0.506,6.525,2.342c3.751,1.661,6.133,3.424,6.232,3.498L22.75,36.602L22.75,36.602z"/>
29
+ <path id="Whisker_2_" d="M16.628,41.254c-2.035,0-5.092-0.276-5.238-0.289l0.107-1.195c0.041,0.004,4.088,0.367,5.887,0.266&#xD;&#xA;&#x9;&#x9;c1.82-0.104,5.412-0.578,5.447-0.583l0.157,1.189c-0.148,0.02-3.67,0.484-5.537,0.591C17.209,41.248,16.929,41.254,16.628,41.254&#xD;&#xA;&#x9;&#x9;L16.628,41.254z"/>
30
+ <path id="Whisker_1_" d="M82.524,35.475L82.33,34.29c0.046-0.007,4.524-0.744,6.761-1.523c2.573-0.894,4.326-2.415,4.345-2.43&#xD;&#xA;&#x9;&#x9;l0.791,0.902c-0.078,0.068-1.948,1.691-4.742,2.661C87.151,34.714,82.711,35.444,82.524,35.475L82.524,35.475z"/>
31
+ <path id="Whisker" d="M91.551,42.979c-0.812,0-1.905-0.035-3.189-0.163c-2.858-0.286-5.584-1.379-5.699-1.426l0.45-1.112&#xD;&#xA;&#x9;&#x9;c0.027,0.011,2.686,1.075,5.368,1.344c2.672,0.267,4.518,0.111,4.535,0.11l0.104,1.195C93.077,42.931,92.508,42.979,91.551,42.979&#xD;&#xA;&#x9;&#x9;L91.551,42.979z"/>
32
+ <path id="Mouth" fill="none" stroke="#231F20" stroke-width="1.2" d="M47.545,39.447c0,0,10.347,3.054,14.217,3.896&#xD;&#xA;&#x9;&#x9;c3.867,0.842,10.851,1.684,10.851,1.684s-7.989,10.245-17.327,7.645C45.947,50.075,46.115,44.187,47.545,39.447z"/>
33
+ </g>
34
+ </g>
35
+ </svg>
@@ -0,0 +1,60 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const md5 = require('js-md5');
5
+
6
+ const projects = [
7
+ '117504922'
8
+ ];
9
+ const assets = [
10
+ '66895930177178ea01d9e610917f8acf.png',
11
+ '6e8bd9ae68fdb02b7e1e3df656a75635.svg',
12
+ '7e24c99c1b853e52f8e7f9004416fa34.png',
13
+ '83c36d806dc92327b9e7049a565c6bff.wav',
14
+ 'f88bf1935daea28f8ca098462a31dbb0.svg',
15
+ 'fe5e3566965f9de793beeffce377d054.jpg'
16
+ ];
17
+
18
+ const loadSomething = filename => {
19
+ const fullPath = path.join(__dirname, 'assets', filename);
20
+ const content = fs.readFileSync(fullPath);
21
+
22
+ return {
23
+ content,
24
+ hash: md5(content)
25
+ };
26
+ };
27
+
28
+ const loadProject = id => {
29
+ const filename = `${id}.json`;
30
+ const result = loadSomething(filename);
31
+
32
+ // throw if not a valid JSON string
33
+ JSON.parse(result.content.toString());
34
+
35
+ return result;
36
+ };
37
+
38
+ const loadAsset = filename => {
39
+ const result = loadSomething(filename);
40
+
41
+ const expectedHash = filename.split('.', 1)[0];
42
+ if (expectedHash !== result.hash) {
43
+ throw new Error(`Asset has wrong hash: ${filename}`);
44
+ }
45
+
46
+ return result;
47
+ };
48
+
49
+ const knownAssets = Object.assign({},
50
+ projects.reduce((bag, id) => {
51
+ bag[id] = loadProject(id);
52
+ return bag;
53
+ }, {}),
54
+ assets.reduce((bag, filename) => {
55
+ bag[filename] = loadAsset(filename);
56
+ return bag;
57
+ }, {})
58
+ );
59
+
60
+ module.exports = knownAssets;