mockaton 11.3.0 → 11.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.
- package/README.md +2 -3
- package/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/client/app.js +2 -3
- package/src/client/dom-utils.js +1 -0
- package/src/client/indexHtml.js +1 -0
- package/src/server/Mockaton.test.js +9 -8
- package/src/server/Watcher.js +1 -2
- package/src/server/WatcherDevClient.js +2 -3
- package/src/server/config.js +1 -3
- package/src/server/utils/http-cors.test.js +22 -22
- package/src/server/utils/jwt.js +1 -6
- package/src/server/utils/mime.js +32 -3
package/README.md
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
An HTTP mock server for simulating APIs with minimal setup — ideal
|
|
8
8
|
for testing difficult to reproduce backend states.
|
|
9
9
|
|
|
10
|
-
## https://mockaton.com
|
|
10
|
+
## [mockaton.com ↗](https://mockaton.com)
|
|
11
|
+
## [Changelog ↗](https://mockaton.com/changelog)
|
|
11
12
|
|
|
12
13
|
## Overview
|
|
13
14
|
With Mockaton, you don’t need to write code for wiring up your
|
|
@@ -64,7 +65,5 @@ Nonetheless, there’s a programmatic API, which is handy for
|
|
|
64
65
|
setting up tests (see **Commander API** section below).
|
|
65
66
|
|
|
66
67
|
|
|
67
|
-
## [Changelog ↗](https://mockaton.com/changelog)
|
|
68
|
-
|
|
69
68
|
## License
|
|
70
69
|
MIT
|
package/index.d.ts
CHANGED
package/package.json
CHANGED
package/src/client/app.js
CHANGED
|
@@ -22,7 +22,6 @@ const CSS = {
|
|
|
22
22
|
Logo: null,
|
|
23
23
|
MenuTrigger: null,
|
|
24
24
|
Method: null,
|
|
25
|
-
MockList: null,
|
|
26
25
|
MockSelector: null,
|
|
27
26
|
NotFoundToggler: null,
|
|
28
27
|
PayloadViewer: null,
|
|
@@ -110,7 +109,7 @@ function Header() {
|
|
|
110
109
|
r('header', null,
|
|
111
110
|
r('a', {
|
|
112
111
|
className: CSS.Logo,
|
|
113
|
-
href: 'https://mockaton.com'
|
|
112
|
+
href: 'https://mockaton.com'
|
|
114
113
|
},
|
|
115
114
|
r('object', {
|
|
116
115
|
data: 'logo.svg',
|
|
@@ -281,7 +280,7 @@ function SettingsMenu(id) {
|
|
|
281
280
|
target: '_blank',
|
|
282
281
|
rel: 'noopener noreferrer'
|
|
283
282
|
}, t`Website`),
|
|
284
|
-
|
|
283
|
+
|
|
285
284
|
r('a', {
|
|
286
285
|
href: 'https://github.com/ericfortis/mockaton',
|
|
287
286
|
target: '_blank',
|
package/src/client/dom-utils.js
CHANGED
package/src/client/indexHtml.js
CHANGED
|
@@ -39,7 +39,7 @@ const CUSTOM_EXT = 'custom_extension'
|
|
|
39
39
|
const CUSTOM_MIME = 'custom_mime'
|
|
40
40
|
const CUSTOM_HEADER_NAME = 'custom_header_name'
|
|
41
41
|
const CUSTOM_HEADER_VAL = 'custom_header_val'
|
|
42
|
-
const ALLOWED_ORIGIN = '
|
|
42
|
+
const ALLOWED_ORIGIN = 'https://example.test'
|
|
43
43
|
|
|
44
44
|
const server = await Mockaton({
|
|
45
45
|
mocksDir,
|
|
@@ -51,7 +51,8 @@ const server = await Mockaton({
|
|
|
51
51
|
logLevel: 'verbose',
|
|
52
52
|
corsOrigins: [ALLOWED_ORIGIN],
|
|
53
53
|
corsExposedHeaders: ['Content-Encoding'],
|
|
54
|
-
watcherEnabled: false,
|
|
54
|
+
watcherEnabled: false, // But we enable it at run-time
|
|
55
|
+
watcherDebounceMs: 0
|
|
55
56
|
})
|
|
56
57
|
after(() => server?.close())
|
|
57
58
|
|
|
@@ -404,9 +405,9 @@ describe('Proxy Fallback', () => {
|
|
|
404
405
|
})
|
|
405
406
|
|
|
406
407
|
test('sets fallback', async () => {
|
|
407
|
-
const r = await api.setProxyFallback('
|
|
408
|
+
const r = await api.setProxyFallback('https://example.test')
|
|
408
409
|
equal(r.status, 200)
|
|
409
|
-
equal((await fetchState()).proxyFallback, '
|
|
410
|
+
equal((await fetchState()).proxyFallback, 'https://example.test')
|
|
410
411
|
})
|
|
411
412
|
|
|
412
413
|
test('unsets fallback', async () => {
|
|
@@ -461,7 +462,7 @@ describe('Proxy Fallback', () => {
|
|
|
461
462
|
})
|
|
462
463
|
|
|
463
464
|
test('200 when setting', async () => {
|
|
464
|
-
await api.setProxyFallback('https://example.
|
|
465
|
+
await api.setProxyFallback('https://example.test')
|
|
465
466
|
const r0 = await api.setRouteIsProxied(fx.method, fx.urlMask, true)
|
|
466
467
|
equal(r0.status, 200)
|
|
467
468
|
equal((await r0.json()).proxied, true)
|
|
@@ -480,7 +481,7 @@ describe('Proxy Fallback', () => {
|
|
|
480
481
|
test('unsets auto500', async () => {
|
|
481
482
|
const fx = new Fixture('unset-500-on-proxy.GET.200.txt')
|
|
482
483
|
await fx.sync()
|
|
483
|
-
await api.setProxyFallback('https://example.
|
|
484
|
+
await api.setProxyFallback('https://example.test')
|
|
484
485
|
|
|
485
486
|
const r0 = await api.toggle500(fx.method, fx.urlMask)
|
|
486
487
|
const b0 = await r0.json()
|
|
@@ -500,7 +501,7 @@ describe('Proxy Fallback', () => {
|
|
|
500
501
|
test('updating selected mock resets proxied flag', async () => {
|
|
501
502
|
const fx = new Fixture('select-resets-proxied.GET.200.txt')
|
|
502
503
|
await fx.sync()
|
|
503
|
-
await api.setProxyFallback('
|
|
504
|
+
await api.setProxyFallback('https://example.test')
|
|
504
505
|
const r0 = await api.setRouteIsProxied(fx.method, fx.urlMask, true)
|
|
505
506
|
equal((await r0.json()).proxied, true)
|
|
506
507
|
|
|
@@ -789,7 +790,7 @@ describe('500', () => {
|
|
|
789
790
|
test('toggling ON 500 unsets `proxied` flag', async () => {
|
|
790
791
|
const fx = new Fixture('proxied-to-500.GET.200.txt')
|
|
791
792
|
await fx.sync()
|
|
792
|
-
await api.setProxyFallback('
|
|
793
|
+
await api.setProxyFallback('https://example.test')
|
|
793
794
|
await api.setRouteIsProxied(fx.method, fx.urlMask, true)
|
|
794
795
|
await api.toggle500(fx.method, fx.urlMask)
|
|
795
796
|
equal((await fx.fetchBroker()).proxied, false)
|
package/src/server/Watcher.js
CHANGED
|
@@ -21,7 +21,6 @@ import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
|
21
21
|
* and also renames, which are two events (delete + add).
|
|
22
22
|
*/
|
|
23
23
|
const uiSyncVersion = new class extends EventEmitter {
|
|
24
|
-
delay = Number(process.env.MOCKATON_WATCHER_DEBOUNCE_MS ?? 80)
|
|
25
24
|
version = 0
|
|
26
25
|
|
|
27
26
|
increment = /** @type {function} */ this.#debounce(() => {
|
|
@@ -40,7 +39,7 @@ const uiSyncVersion = new class extends EventEmitter {
|
|
|
40
39
|
let timer
|
|
41
40
|
return () => {
|
|
42
41
|
clearTimeout(timer)
|
|
43
|
-
timer = setTimeout(fn,
|
|
42
|
+
timer = setTimeout(fn, config.watcherDebounceMs)
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
45
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { join } from 'node:path'
|
|
2
2
|
import { EventEmitter } from 'node:events'
|
|
3
3
|
import { watch, readdirSync } from 'node:fs'
|
|
4
|
+
import { config } from './config.js'
|
|
4
5
|
import { LONG_POLL_SERVER_TIMEOUT } from './ApiConstants.js'
|
|
5
6
|
|
|
6
7
|
|
|
7
|
-
const DEV = process.env.NODE_ENV === 'development'
|
|
8
|
-
|
|
9
8
|
export const CLIENT_DIR = join(import.meta.dirname, '../client')
|
|
10
9
|
export const DASHBOARD_ASSETS = readdirSync(CLIENT_DIR)
|
|
11
10
|
|
|
@@ -28,7 +27,7 @@ export function watchDevSPA() {
|
|
|
28
27
|
|
|
29
28
|
/** Realtime notify Dev UI changes */
|
|
30
29
|
export function longPollDevClientHotReload(req, response) {
|
|
31
|
-
if (!
|
|
30
|
+
if (!config.hotReload) {
|
|
32
31
|
response.notFound()
|
|
33
32
|
return
|
|
34
33
|
}
|
package/src/server/config.js
CHANGED
|
@@ -21,6 +21,7 @@ const schema = {
|
|
|
21
21
|
staticDir: [resolve('mockaton-static-mocks'), optional(isDirectory)],
|
|
22
22
|
ignore: [/(\.DS_Store|~)$/, is(RegExp)],
|
|
23
23
|
watcherEnabled: [true, is(Boolean)],
|
|
24
|
+
watcherDebounceMs: [80, ms => Number.isInteger(ms) && ms >= 0],
|
|
24
25
|
|
|
25
26
|
host: ['127.0.0.1', is(String)],
|
|
26
27
|
port: [0, port => Number.isInteger(port) && port >= 0 && port < 2 ** 16], // 0 means auto-assigned
|
|
@@ -73,9 +74,6 @@ export const ConfigValidator = Object.freeze(validators)
|
|
|
73
74
|
|
|
74
75
|
/** @param {Partial<Config>} opts */
|
|
75
76
|
export function setup(opts) {
|
|
76
|
-
if (process.env.NODE_ENV !== 'development')
|
|
77
|
-
opts.hotReload = false
|
|
78
|
-
|
|
79
77
|
if (opts.mocksDir)
|
|
80
78
|
opts.mocksDir = resolve(opts.mocksDir)
|
|
81
79
|
|
|
@@ -9,9 +9,9 @@ function headerIs(response, header, value) {
|
|
|
9
9
|
equal(response.headers.get(header), value)
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
12
|
+
const FooDotTest = 'https://foo.test'
|
|
13
|
+
const AllowedDotTest = 'https://allowed.test'
|
|
14
|
+
const NotAllowedDotTest = 'https://not-allowed.test'
|
|
15
15
|
|
|
16
16
|
await describe('CORS', async () => {
|
|
17
17
|
let corsConfig = {}
|
|
@@ -87,7 +87,7 @@ await describe('CORS', async () => {
|
|
|
87
87
|
corsMethods: ['GET']
|
|
88
88
|
}
|
|
89
89
|
const p = await preflight({
|
|
90
|
-
[CH.Origin]:
|
|
90
|
+
[CH.Origin]: FooDotTest,
|
|
91
91
|
[CH.AcRequestMethod]: 'GET'
|
|
92
92
|
})
|
|
93
93
|
headerIs(p, CH.AcAllowOrigin, null)
|
|
@@ -99,11 +99,11 @@ await describe('CORS', async () => {
|
|
|
99
99
|
|
|
100
100
|
await test('not in allowed origins', async () => {
|
|
101
101
|
corsConfig = {
|
|
102
|
-
corsOrigins: [
|
|
102
|
+
corsOrigins: [AllowedDotTest],
|
|
103
103
|
corsMethods: ['GET']
|
|
104
104
|
}
|
|
105
105
|
const p = await preflight({
|
|
106
|
-
[CH.Origin]:
|
|
106
|
+
[CH.Origin]: NotAllowedDotTest,
|
|
107
107
|
[CH.AcRequestMethod]: 'GET'
|
|
108
108
|
})
|
|
109
109
|
headerIs(p, CH.AcAllowOrigin, null)
|
|
@@ -114,14 +114,14 @@ await describe('CORS', async () => {
|
|
|
114
114
|
|
|
115
115
|
await test('origin and method match', async () => {
|
|
116
116
|
corsConfig = {
|
|
117
|
-
corsOrigins: [
|
|
117
|
+
corsOrigins: [AllowedDotTest],
|
|
118
118
|
corsMethods: ['GET']
|
|
119
119
|
}
|
|
120
120
|
const p = await preflight({
|
|
121
|
-
[CH.Origin]:
|
|
121
|
+
[CH.Origin]: AllowedDotTest,
|
|
122
122
|
[CH.AcRequestMethod]: 'GET'
|
|
123
123
|
})
|
|
124
|
-
headerIs(p, CH.AcAllowOrigin,
|
|
124
|
+
headerIs(p, CH.AcAllowOrigin, AllowedDotTest)
|
|
125
125
|
headerIs(p, CH.AcAllowMethods, 'GET')
|
|
126
126
|
headerIs(p, CH.AcAllowCredentials, null)
|
|
127
127
|
headerIs(p, CH.AcAllowHeaders, null)
|
|
@@ -129,14 +129,14 @@ await describe('CORS', async () => {
|
|
|
129
129
|
|
|
130
130
|
await test('origin matches from multiple', async () => {
|
|
131
131
|
corsConfig = {
|
|
132
|
-
corsOrigins: [
|
|
132
|
+
corsOrigins: [AllowedDotTest, FooDotTest],
|
|
133
133
|
corsMethods: ['GET']
|
|
134
134
|
}
|
|
135
135
|
const p = await preflight({
|
|
136
|
-
[CH.Origin]:
|
|
136
|
+
[CH.Origin]: AllowedDotTest,
|
|
137
137
|
[CH.AcRequestMethod]: 'GET'
|
|
138
138
|
})
|
|
139
|
-
headerIs(p, CH.AcAllowOrigin,
|
|
139
|
+
headerIs(p, CH.AcAllowOrigin, AllowedDotTest)
|
|
140
140
|
headerIs(p, CH.AcAllowMethods, 'GET')
|
|
141
141
|
headerIs(p, CH.AcAllowCredentials, null)
|
|
142
142
|
headerIs(p, CH.AcAllowHeaders, null)
|
|
@@ -148,10 +148,10 @@ await describe('CORS', async () => {
|
|
|
148
148
|
corsMethods: ['GET']
|
|
149
149
|
}
|
|
150
150
|
const p = await preflight({
|
|
151
|
-
[CH.Origin]:
|
|
151
|
+
[CH.Origin]: FooDotTest,
|
|
152
152
|
[CH.AcRequestMethod]: 'GET'
|
|
153
153
|
})
|
|
154
|
-
headerIs(p, CH.AcAllowOrigin,
|
|
154
|
+
headerIs(p, CH.AcAllowOrigin, FooDotTest)
|
|
155
155
|
headerIs(p, CH.AcAllowMethods, 'GET')
|
|
156
156
|
headerIs(p, CH.AcAllowCredentials, null)
|
|
157
157
|
headerIs(p, CH.AcAllowHeaders, null)
|
|
@@ -164,10 +164,10 @@ await describe('CORS', async () => {
|
|
|
164
164
|
corsCredentials: true
|
|
165
165
|
}
|
|
166
166
|
const p = await preflight({
|
|
167
|
-
[CH.Origin]:
|
|
167
|
+
[CH.Origin]: FooDotTest,
|
|
168
168
|
[CH.AcRequestMethod]: 'GET'
|
|
169
169
|
})
|
|
170
|
-
headerIs(p, CH.AcAllowOrigin,
|
|
170
|
+
headerIs(p, CH.AcAllowOrigin, FooDotTest)
|
|
171
171
|
headerIs(p, CH.AcAllowMethods, 'GET')
|
|
172
172
|
headerIs(p, CH.AcAllowCredentials, 'true')
|
|
173
173
|
headerIs(p, CH.AcAllowHeaders, null)
|
|
@@ -181,10 +181,10 @@ await describe('CORS', async () => {
|
|
|
181
181
|
corsHeaders: ['content-type', 'my-header']
|
|
182
182
|
}
|
|
183
183
|
const p = await preflight({
|
|
184
|
-
[CH.Origin]:
|
|
184
|
+
[CH.Origin]: FooDotTest,
|
|
185
185
|
[CH.AcRequestMethod]: 'GET'
|
|
186
186
|
})
|
|
187
|
-
headerIs(p, CH.AcAllowOrigin,
|
|
187
|
+
headerIs(p, CH.AcAllowOrigin, FooDotTest)
|
|
188
188
|
headerIs(p, CH.AcAllowMethods, 'GET')
|
|
189
189
|
headerIs(p, CH.AcAllowCredentials, 'true')
|
|
190
190
|
headerIs(p, CH.AcAllowHeaders, 'content-type,my-header')
|
|
@@ -198,7 +198,7 @@ await describe('CORS', async () => {
|
|
|
198
198
|
corsMethods: ['GET']
|
|
199
199
|
}
|
|
200
200
|
const p = await request({
|
|
201
|
-
[CH.Origin]:
|
|
201
|
+
[CH.Origin]: NotAllowedDotTest
|
|
202
202
|
})
|
|
203
203
|
equal(p.status, 200)
|
|
204
204
|
headerIs(p, CH.AcAllowOrigin, null)
|
|
@@ -208,16 +208,16 @@ await describe('CORS', async () => {
|
|
|
208
208
|
|
|
209
209
|
await test('origin allowed', async () => {
|
|
210
210
|
corsConfig = {
|
|
211
|
-
corsOrigins: [
|
|
211
|
+
corsOrigins: [AllowedDotTest],
|
|
212
212
|
corsMethods: ['GET'],
|
|
213
213
|
corsCredentials: true,
|
|
214
214
|
corsExposedHeaders: ['x-h1', 'x-h2']
|
|
215
215
|
}
|
|
216
216
|
const p = await request({
|
|
217
|
-
[CH.Origin]:
|
|
217
|
+
[CH.Origin]: AllowedDotTest
|
|
218
218
|
})
|
|
219
219
|
equal(p.status, 200)
|
|
220
|
-
headerIs(p, CH.AcAllowOrigin,
|
|
220
|
+
headerIs(p, CH.AcAllowOrigin, AllowedDotTest)
|
|
221
221
|
headerIs(p, CH.AcAllowCredentials, 'true')
|
|
222
222
|
headerIs(p, CH.AcExposeHeaders, 'x-h1,x-h2')
|
|
223
223
|
})
|
package/src/server/utils/jwt.js
CHANGED
|
@@ -9,13 +9,8 @@ export function jwtCookie(cookieName, payload, path = '/') {
|
|
|
9
9
|
function jwt(payload) {
|
|
10
10
|
return [
|
|
11
11
|
'Header_Not_In_Use',
|
|
12
|
-
|
|
12
|
+
Buffer.from(JSON.stringify(payload), 'utf8').toString('base64url'),
|
|
13
13
|
'Signature_Not_In_Use'
|
|
14
14
|
].join('.')
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
function toBase64Url(obj) {
|
|
18
|
-
return btoa(JSON.stringify(obj))
|
|
19
|
-
.replace('+', '-')
|
|
20
|
-
.replace('/', '_')
|
|
21
|
-
}
|
package/src/server/utils/mime.js
CHANGED
|
@@ -11,9 +11,13 @@ import { UNKNOWN_MIME_EXT } from '../ApiConstants.js'
|
|
|
11
11
|
const extToMime = {
|
|
12
12
|
'3g2': 'video/3gpp2',
|
|
13
13
|
'3gp': 'video/3gpp',
|
|
14
|
+
'3mf': 'model/3mf',
|
|
14
15
|
'7z': 'application/x-7z-compressed',
|
|
15
16
|
aac: 'audio/aac',
|
|
16
17
|
abw: 'application/x-abiword',
|
|
18
|
+
aif: 'audio/aiff',
|
|
19
|
+
aifc: 'audio/aiff',
|
|
20
|
+
aiff: 'audio/aiff',
|
|
17
21
|
apng: 'image/apng',
|
|
18
22
|
arc: 'application/x-freearc',
|
|
19
23
|
avi: 'video/x-msvideo',
|
|
@@ -28,12 +32,22 @@ const extToMime = {
|
|
|
28
32
|
csh: 'application/x-csh',
|
|
29
33
|
css: 'text/css',
|
|
30
34
|
csv: 'text/csv',
|
|
35
|
+
dae: 'model/vnd.collada+xml',
|
|
31
36
|
doc: 'application/msword',
|
|
32
37
|
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
38
|
+
drc: 'model/vnd.draco',
|
|
39
|
+
eml: 'message/rfc822',
|
|
33
40
|
eot: 'application/vnd.ms-fontobject',
|
|
34
41
|
epub: 'application/epub+zip',
|
|
42
|
+
exe: 'application/vnd.microsoft.portable-executable',
|
|
43
|
+
fbx: 'application/octet-stream',
|
|
44
|
+
flac: 'audio/flac',
|
|
35
45
|
gif: 'image/gif',
|
|
46
|
+
glb: 'model/gltf-binary',
|
|
47
|
+
gltf: 'model/gltf+json',
|
|
36
48
|
gz: 'application/gzip',
|
|
49
|
+
heic: 'image/heic',
|
|
50
|
+
heif: 'image/heif',
|
|
37
51
|
htm: 'text/html',
|
|
38
52
|
html: 'text/html',
|
|
39
53
|
ico: 'image/vnd.microsoft.icon',
|
|
@@ -44,13 +58,21 @@ const extToMime = {
|
|
|
44
58
|
js: 'application/javascript',
|
|
45
59
|
json: 'application/json',
|
|
46
60
|
jsonld: 'application/ld+json',
|
|
61
|
+
lz: 'application/x-lzip',
|
|
62
|
+
m4a: 'audio/mp4',
|
|
63
|
+
map: 'application/json',
|
|
64
|
+
md: 'text/markdown',
|
|
47
65
|
mid: 'audio/midi',
|
|
48
66
|
midi: 'audio/midi',
|
|
49
67
|
mjs: 'text/javascript',
|
|
68
|
+
mkv: 'video/x-matroska',
|
|
69
|
+
mov: 'video/quicktime',
|
|
50
70
|
mp3: 'audio/mpeg',
|
|
51
71
|
mp4: 'video/mp4',
|
|
52
72
|
mpeg: 'video/mpeg',
|
|
53
73
|
mpkg: 'application/vnd.apple.installer+xml',
|
|
74
|
+
mtl: 'text/plain',
|
|
75
|
+
obj: 'text/plain',
|
|
54
76
|
odp: 'application/vnd.oasis.opendocument.presentation',
|
|
55
77
|
ods: 'application/vnd.oasis.opendocument.spreadsheet',
|
|
56
78
|
odt: 'application/vnd.oasis.opendocument.text',
|
|
@@ -61,19 +83,24 @@ const extToMime = {
|
|
|
61
83
|
otf: 'font/otf',
|
|
62
84
|
pdf: 'application/pdf',
|
|
63
85
|
php: 'application/x-httpd-php',
|
|
86
|
+
ply: 'application/octet-stream',
|
|
64
87
|
png: 'image/png',
|
|
65
88
|
ppt: 'application/vnd.ms-powerpoint',
|
|
66
89
|
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
67
90
|
rar: 'application/vnd.rar',
|
|
68
91
|
rtf: 'application/rtf',
|
|
69
92
|
sh: 'application/x-sh',
|
|
93
|
+
stl: 'model/stl',
|
|
70
94
|
svg: 'image/svg+xml',
|
|
71
95
|
tar: 'application/x-tar',
|
|
72
96
|
tif: 'image/tiff',
|
|
73
97
|
ts: 'video/mp2t',
|
|
74
98
|
ttf: 'font/ttf',
|
|
75
99
|
txt: 'text/plain',
|
|
100
|
+
usd: 'model/vnd.usd',
|
|
101
|
+
usdz: 'model/vnd.usdz+zip',
|
|
76
102
|
vsd: 'application/vnd.visio',
|
|
103
|
+
wasm: 'application/wasm',
|
|
77
104
|
wav: 'audio/wav',
|
|
78
105
|
weba: 'audio/webm',
|
|
79
106
|
webm: 'video/webm',
|
|
@@ -85,9 +112,11 @@ const extToMime = {
|
|
|
85
112
|
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
86
113
|
xml: 'application/xml',
|
|
87
114
|
xul: 'application/vnd.mozilla.xul+xml',
|
|
115
|
+
xz: 'application/x-xz',
|
|
88
116
|
yaml: 'application/yaml',
|
|
89
117
|
yml: 'application/yaml',
|
|
90
|
-
zip: 'application/zip'
|
|
118
|
+
zip: 'application/zip',
|
|
119
|
+
zst: 'application/zstd'
|
|
91
120
|
}
|
|
92
121
|
|
|
93
122
|
const mimeToExt = mapMimeToExt(extToMime)
|
|
@@ -105,8 +134,8 @@ export function mimeFor(filename) {
|
|
|
105
134
|
}
|
|
106
135
|
function extname(filename) {
|
|
107
136
|
const i = filename.lastIndexOf('.')
|
|
108
|
-
return i === -1
|
|
109
|
-
? ''
|
|
137
|
+
return i === -1
|
|
138
|
+
? ''
|
|
110
139
|
: filename.slice(i + 1)
|
|
111
140
|
}
|
|
112
141
|
|