mockaton 13.9.2 → 13.9.4

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 CHANGED
@@ -2,7 +2,6 @@
2
2
  ![NPM Version](https://img.shields.io/npm/v/mockaton)
3
3
  [![Test](https://github.com/ericfortis/mockaton/actions/workflows/test.yml/badge.svg)](https://github.com/ericfortis/mockaton/actions/workflows/test.yml)
4
4
  [![codecov](https://codecov.io/github/ericfortis/mockaton/graph/badge.svg?token=90NYLMMG1J)](https://codecov.io/github/ericfortis/mockaton)
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
5
 
7
6
  ## [Docs ↗](https://mockaton.com) | [Changelog ↗](https://mockaton.com/changelog) | [Skills](skills/mockaton/SKILL.md)
8
7
 
@@ -38,9 +37,10 @@ Dashboard: [localhost:2020/mockaton](http://localhost:2020/mockaton)
38
37
  npx mockaton --port 2020 my-mocks-dir/
39
38
  ```
40
39
 
41
- Mockaton will serve the files on the given directory. It's a file-system
42
- based router, so filenames can have dynamic parameters and comments.
43
- Also, each route can have different mock file variants.
40
+ Mockaton will serve the files on the given directory. It's a file-system based router, so filenames can have dynamic
41
+ parameters.
42
+ Also, filenames can have comments, which are anything within parentheses, this way each route can have different mock
43
+ file variants.
44
44
 
45
45
 
46
46
  | Route | Filename | Description |
package/index.d.ts CHANGED
@@ -46,6 +46,7 @@ export declare interface Config {
46
46
  onReady?: (address: string) => void
47
47
 
48
48
  hotReload?: boolean // For UI dev purposes only
49
+ bypassImportCache?: boolean
49
50
  }
50
51
 
51
52
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "mockaton",
3
3
  "description": "HTTP Mock Server",
4
4
  "type": "module",
5
- "version": "13.9.2",
5
+ "version": "13.9.4",
6
6
  "exports": {
7
7
  ".": {
8
8
  "import": "./index.js",
@@ -17,15 +17,10 @@ export class Commander {
17
17
  reset = () => this.#patch(API.reset)
18
18
 
19
19
  setGlobalDelay = delay => this.#patch(API.globalDelay, delay)
20
-
21
20
  setGlobalDelayJitter = jitterPct => this.#patch(API.globalDelayJitter, jitterPct)
22
-
23
21
  setCorsAllowed = value => this.#patch(API.cors, value)
24
-
25
22
  setWatchMocks = enabled => this.#patch(API.watchMocks, enabled)
26
-
27
23
  setProxyFallback = proxyAddr => this.#patch(API.fallback, proxyAddr)
28
-
29
24
  setCollectProxied = shouldCollect => this.#patch(API.collectProxied, shouldCollect)
30
25
 
31
26
  /** @returns {JsonPromise<State.cookies>} */
@@ -50,7 +45,6 @@ export class Commander {
50
45
 
51
46
 
52
47
  writeMock = (file, content) => this.#patch(API.writeMock, [file, content])
53
-
54
48
  deleteMock = file => this.#patch(API.deleteMock, file)
55
49
 
56
50
 
@@ -6,7 +6,6 @@ export const API = {
6
6
  dashboard: MOUNT,
7
7
 
8
8
  reset: MOUNT + '/reset',
9
-
10
9
  select: MOUNT + '/select',
11
10
  bulkSelect: MOUNT + '/bulk-select-by-comment',
12
11
 
@@ -27,7 +27,6 @@
27
27
 
28
28
  html,
29
29
  body {
30
- overflow: hidden;
31
30
  height: 100%;
32
31
  font-size: 12px;
33
32
  }
@@ -45,7 +44,7 @@ body {
45
44
  border: 0;
46
45
  margin: 0;
47
46
  letter-spacing: -0.374px;
48
- line-height: 1.2;
47
+ line-height: 14px;
49
48
  font-family: inherit;
50
49
  font-size: 100%;
51
50
  scrollbar-width: thin;
@@ -185,13 +184,13 @@ header {
185
184
  }
186
185
 
187
186
  .HelpLink {
188
- opacity: 0.8;
189
187
  width: 22px;
190
188
  height: 22px;
191
189
  flex-shrink: 0;
192
190
  align-self: end;
193
191
  margin-bottom: 3px;
194
192
  margin-left: auto;
193
+ opacity: 0.8;
195
194
  border-radius: 50%;
196
195
  fill: var(--colorBgHeader);
197
196
  background: var(--colorLabel);
@@ -342,6 +341,7 @@ main {
342
341
  }
343
342
 
344
343
  .leftSide {
344
+ overflow: hidden;
345
345
  width: 50%;
346
346
  border-top: 1px solid var(--colorBorder);
347
347
  border-right: 1px solid var(--colorBorder);
@@ -349,6 +349,7 @@ main {
349
349
 
350
350
  .rightSide {
351
351
  position: relative;
352
+ overflow: hidden;
352
353
  min-width: 100px;
353
354
  min-height: 0;
354
355
  flex: 1;
@@ -399,8 +400,7 @@ main {
399
400
  background: var(--colorBgHeader);
400
401
  }
401
402
 
402
- .GroupByMethod,
403
- .ViewSourceCheckbox {
403
+ .GroupByMethod {
404
404
  display: flex;
405
405
  align-items: center;
406
406
  gap: 6px;
@@ -426,7 +426,7 @@ main {
426
426
  height: 100%;
427
427
  padding: 16px;
428
428
  padding-bottom: 64px;
429
- padding-left: 12px;
429
+ padding-left: 15px;
430
430
  user-select: none;
431
431
  overflow-y: auto;
432
432
 
@@ -482,7 +482,8 @@ main {
482
482
  cursor: grabbing;
483
483
  }
484
484
 
485
- &::-webkit-details-marker {
485
+ &::-webkit-details-marker,
486
+ &::marker {
486
487
  display: none;
487
488
  }
488
489
 
package/src/server/Api.js CHANGED
@@ -4,8 +4,7 @@
4
4
  */
5
5
 
6
6
  import { join } from 'node:path'
7
- import { readdirSync } from 'node:fs'
8
- import { write, rm, isFile, resolveIn } from './utils/fs.js'
7
+ import { write, rm, isFile, resolveIn, listFilesRecursively } from './utils/fs.js'
9
8
 
10
9
  import openapi from '../../www/src/assets/openapi.json' with { type: 'json' }
11
10
  import pkgJSON from '../../package.json' with { type: 'json' }
@@ -22,12 +21,11 @@ import * as mockBrokersCollection from './mockBrokersCollection.js'
22
21
 
23
22
 
24
23
  export const CLIENT_DIR = join(import.meta.dirname, '../client')
25
- const DASHBOARD_ASSETS = readdirSync(CLIENT_DIR, { recursive: true })
26
-
27
24
 
28
25
  export const apiGetReqs = new Map([
29
26
  [API.dashboard, serveDashboard],
30
- ...DASHBOARD_ASSETS.map(f => [API.dashboard + '/' + f, serveStatic(f)]),
27
+ ...listFilesRecursively(CLIENT_DIR).map(f =>
28
+ [`${API.dashboard}/${f}`, serveStatic(f)]),
31
29
 
32
30
  [API.state, getState],
33
31
  [API.syncVersion, sseClientSyncVersion],
@@ -38,7 +36,6 @@ export const apiGetReqs = new Map([
38
36
  ])
39
37
 
40
38
 
41
-
42
39
  export const apiPatchReqs = new Map([
43
40
  [API.cors, setCorsAllowed],
44
41
  [API.reset, reset],
@@ -69,9 +66,7 @@ function serveDashboard(_, response) {
69
66
  }
70
67
 
71
68
  function serveStatic(f) {
72
- return (_, response) => {
73
- response.file(join(CLIENT_DIR, f))
74
- }
69
+ return (_, response) => { response.file(join(CLIENT_DIR, f)) }
75
70
  }
76
71
 
77
72
  function getState(_, response) {
@@ -25,9 +25,12 @@ export function Mockaton(options) {
25
25
  setup(options)
26
26
  cookie.init(config.cookies)
27
27
  mockBrokerCollection.init()
28
- register('./importResolver.js', import.meta.url)
29
28
 
30
- if (config.watcherEnabled)
29
+ register('./resolverResolveExtensionless.js', import.meta.url)
30
+ if (config.bypassImportCache)
31
+ register('./resolverBypassImportCache.js', import.meta.url)
32
+
33
+ if (config.watcherEnabled)
31
34
  watchMocksDir()
32
35
 
33
36
  if (config.hotReload)
@@ -1047,7 +1047,7 @@ describe('Write and Delete Mock', () => {
1047
1047
  })
1048
1048
 
1049
1049
 
1050
- describe('import resolver', () => {
1050
+ describe('import resolvers', () => {
1051
1051
  test('resolves extensionless ts', async () => {
1052
1052
  await api.writeMock('_scores.ts', 'export default [1,2,3]')
1053
1053
  await api.writeMock('user-scores.GET.200.ts',
@@ -54,7 +54,8 @@ const schema = {
54
54
 
55
55
  onReady: [await openInBrowser, is(Function)],
56
56
 
57
- hotReload: [false, is(Boolean)]
57
+ hotReload: [false, is(Boolean)],
58
+ bypassImportCache: [true, is(Boolean)]
58
59
  }
59
60
 
60
61
 
@@ -0,0 +1,19 @@
1
+ import { resolve as _resolve } from 'node:path'
2
+
3
+ const mockatonSrcRoot = `file://${_resolve(import.meta.dirname, '..')}`
4
+
5
+ // We register this hook at runtime so it doesn’t interfere with non-dynamic imports.
6
+ // Cache bust by appending timestamp query param
7
+ export async function resolve(specifier, context, nextResolve) {
8
+ const result = await nextResolve(specifier, context)
9
+ if (result.url?.startsWith('file://') && !result.url.startsWith(mockatonSrcRoot)) {
10
+ const url = new URL(result.url)
11
+ url.searchParams.set('t', performance.now())
12
+ return {
13
+ ...result,
14
+ url: url.href,
15
+ shortCircuit: true
16
+ }
17
+ }
18
+ return result
19
+ }
@@ -1,14 +1,11 @@
1
1
  import { existsSync } from 'node:fs'
2
- import { resolve as _resolve, join, dirname } from 'node:path'
2
+ import { join, dirname } from 'node:path'
3
3
  import { fileURLToPath, pathToFileURL } from 'node:url'
4
4
 
5
- const mockatonSrcRoot = `file://${_resolve(import.meta.dirname, '..')}`
6
5
 
7
- // We register this hook at runtime so it doesn’t interfere with non-dynamic imports.
8
6
  export async function resolve(specifier, context, nextResolve) {
9
- let result
10
7
  try {
11
- result = await nextResolve(specifier, context)
8
+ return await nextResolve(specifier, context)
12
9
  }
13
10
  catch (error) {
14
11
  // Attempt to resolve imports as .ts and .js
@@ -20,16 +17,4 @@ export async function resolve(specifier, context, nextResolve) {
20
17
  }
21
18
  throw error
22
19
  }
23
-
24
- // Cache bust by appending timestamp query param
25
- if (result.url?.startsWith('file://') && !result.url.startsWith(mockatonSrcRoot)) {
26
- const url = new URL(result.url)
27
- url.searchParams.set('t', performance.now())
28
- return {
29
- ...result,
30
- url: url.href,
31
- shortCircuit: true
32
- }
33
- }
34
- return result
35
20
  }
@@ -27,7 +27,7 @@ export class ServerResponse extends http.ServerResponse {
27
27
 
28
28
  async file(file) {
29
29
  this.setHeader('Content-Type', mimeFor(file))
30
- this.end(await fs.promises.readFile(file, 'utf8'))
30
+ this.end(await fs.promises.readFile(file))
31
31
  }
32
32
 
33
33
  noContent() {
@@ -34,8 +34,8 @@ export async function rm(path) {
34
34
  export async function resolveIn(baseDir, file) {
35
35
  try {
36
36
  const parent = await realpath(baseDir)
37
- const child = resolve(parent, file)
38
- return child.startsWith(parent + sep)
37
+ const child = resolve(join(parent, file))
38
+ return child.startsWith(join(parent, sep))
39
39
  ? child
40
40
  : null
41
41
  }
@@ -0,0 +1,31 @@
1
+ import { join } from 'node:path'
2
+ import { equal } from 'node:assert/strict'
3
+ import { tmpdir } from 'node:os'
4
+ import { after, describe, test } from 'node:test'
5
+ import { mkdtempSync, rmSync, realpathSync } from 'node:fs'
6
+
7
+ import { resolveIn } from './fs.js'
8
+
9
+ const isNull = v => equal(v, null)
10
+
11
+ describe('resolveIn', () => {
12
+ const baseDir = mkdtempSync(join(tmpdir(), '_resolveIn'))
13
+ const baseParentDir = join(baseDir, '..')
14
+ after(() => rmSync(baseDir, { recursive: true, force: true }))
15
+
16
+ test('null when baseDir does not exist', async () =>
17
+ isNull(await resolveIn(join(baseParentDir, 'missing'), 'file.json')))
18
+
19
+ test('null when relative path escapes baseDir', async () =>
20
+ isNull(await resolveIn(baseDir, '../outside.json')))
21
+
22
+
23
+ const realBaseDir = realpathSync(baseDir)
24
+ const onReal = f => join(realBaseDir, f)
25
+
26
+ test('resolves a relative file within baseDir', async () =>
27
+ equal(await resolveIn(baseDir, 'file.json'), onReal('file.json')))
28
+
29
+ test('resolves file starting with /', async () =>
30
+ equal(await resolveIn(baseDir, '/file.json'), onReal('file.json')))
31
+ })