Skip to content

Commit

Permalink
Passthrough support
Browse files Browse the repository at this point in the history
  • Loading branch information
vkruglikov committed Jan 7, 2025
1 parent 74ff3f8 commit c8a7263
Show file tree
Hide file tree
Showing 16 changed files with 143 additions and 74 deletions.
6 changes: 6 additions & 0 deletions .changeset/tasty-windows-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@msw-devtools/extension': patch
'@msw-devtools/core': patch
---

Passthrough support
28 changes: 24 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,31 @@ Switch seamlessly between multiple JSON configurations within the same host envi

<img width="300px" src="./media/extension/multi-configs.png" />

### Edit json config in the DevTools
<img src="https://img.shields.io/badge/WIP-roadmap_feature-ff6a33" />
### 🚀 Pass-Through Mode
Enable pass-through mode to bypass MSW and send requests directly to the server
even if configurations are uploaded.

### Pause/Resume mode
<img src="https://img.shields.io/badge/WIP-roadmap_feature-ff6a33" />
<img width="300px" src="./media/extension/passthrough-mode.png" />


## Roadmap Features 🚧

### Edit json config in the DevTools

Currently, you can only upload JSON files, but you can't edit them in the DevTools.

### Upload multiple JSON config at once

Currently, you can only upload one JSON file at a time, because
it simplifies the process of validation.

### Multi window support

Currently, you can use multi tabs with different configurations per host, but only one browser window is supported.

### Early request interception

Currently, requests sent before the extension is initialized are not intercepted by JSON handlers.

## Motivation 💡

Expand Down
Binary file modified media/extension/multi-configs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added media/extension/passthrough-mode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 12 additions & 6 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,18 @@ export type BackgroundResponseMessage =
hasHandle: boolean
hasConfig: boolean
activeConfig?: LocalStorageConfigKey
configNames: Record<
string,
{
key: LocalStorageConfigKey
}
>
configNames: (
| {
name: string
passthrough?: false
key: LocalStorageConfigKey
}
| {
name: string
passthrough: true
key: `host=${string}&name=__mswde_passthrough_config&jsonConfig=1`
}
)[]
}
}
))
Expand Down
1 change: 1 addition & 0 deletions packages/extension/src/background/activeMswResolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Tab = chrome.tabs.Tab
type IndexInfo = {
tabId: number
host?: string
windowId?: number
}

export const activeMswResolvers = {
Expand Down
4 changes: 2 additions & 2 deletions packages/extension/src/background/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const messages: HandlersMap = {
[MessageType.HandleInitialized]: async ({ type, host }, sender) => {
activeMswResolvers.add({
tabId: sender.tab!.id!,
windowId: sender.tab!.windowId!,
host
})

Expand Down Expand Up @@ -95,11 +96,10 @@ const messages: HandlersMap = {

let hasConfig = false
let currentConfig: Awaited<ReturnType<typeof getJsonConfig>> = null
let configNames: Awaited<ReturnType<typeof getJsonConfigNames>> = {}
let configNames: Awaited<ReturnType<typeof getJsonConfigNames>> = []

try {
currentConfig = await getJsonConfig(host)

configNames = await getJsonConfigNames(host)
hasConfig = !!currentConfig
} catch (e) {
Expand Down
22 changes: 13 additions & 9 deletions packages/extension/src/background/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,24 @@ export const getJsonConfigNames = async (
(memo, [key, config]) => {
if (!config) return memo

memo[config.name] = {
memo.push({
name: config.name,
key: key as LocalStorageConfigKey
}
})

return memo
},
{} as Extract<
BackgroundResponseMessage,
{
type: MessageType.Status
payload: any
}
>['payload']['configNames']
[] as Awaited<ReturnType<typeof getJsonConfigNames>>
)

if (names.length) {
names.push({
name: 'passthrough',
passthrough: true,
key: `host=${host}&name=__mswde_passthrough_config&jsonConfig=1`
})
}

return names
}

Expand Down
8 changes: 6 additions & 2 deletions packages/extension/src/popup/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,13 @@ export const App = () => {
{(() => {
switch (configStatus) {
case 'success':
return <>JSON config has been detected</>
return <>Active JSON config has been detected</>
default:
return <>JSON config hasn't been detected</>
return !status?.configNames.length ? (
<>JSON config hasn't been detected</>
) : (
<>Active JSON config hasn't been detected</>
)
}
})()}
</Status>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.5em;

&:last-child {
&:not(&:first-child) {
padding-left: 5px;
padding-right: 5px;
flex-shrink: 0;
Expand All @@ -41,4 +42,9 @@
&:hover {
background-color: #404040;
}

&:disabled&:hover {
cursor: not-allowed;
background: inherit;
}
}
Original file line number Diff line number Diff line change
@@ -1,55 +1,66 @@
import React, { FC } from 'react'
import { clsx } from 'clsx'

import {
BackgroundResponseMessage,
LocalStorageConfigKey,
MessageType
} from '@msw-devtools/core'
import { BackgroundResponseMessage, MessageType } from '@msw-devtools/core'

import TrashIcon from './trash.svg'
import PausedIcon from './pause.svg'
import PlayIcon from './play.svg'

import styles from './ConfigListButtons.module.css'

type ConfigNames = Extract<
BackgroundResponseMessage,
{ type: MessageType.Status; payload: any }
>['payload']['configNames']

export const ConfigListButtons: FC<{
list?: Extract<
BackgroundResponseMessage,
{ type: MessageType.Status; payload: any }
>['payload']['configNames']
activeKey?: LocalStorageConfigKey
onRemove: (key: LocalStorageConfigKey) => void
onSetActive: (key: LocalStorageConfigKey) => void
list?: ConfigNames
activeKey?: ConfigNames[0]['key']
onRemove: (key: ConfigNames[0]['key']) => void
onSetActive: (key: ConfigNames[0]['key']) => void
}> = ({ list, onSetActive, activeKey, onRemove }) => {
if (!list) return

const listEntries = Object.entries(list)
if (!listEntries.length) return
if (!list?.length) return

return (
<div className={styles.wrapper}>
{listEntries.map(([name, { key }], index) => (
<div
className={clsx(styles.item, {
[styles.active]: key === activeKey
})}
key={key}
>
<button
onClick={() => onSetActive(key)}
title="Click to chose"
className={styles.button}
>
{name}
</button>
<button
onClick={() => onRemove(key)}
title="Click to remove"
className={styles.button}
{list.map(({ name, key, passthrough }) => {
const isActive = key === activeKey || (passthrough && !activeKey)

return (
<div
className={clsx(styles.item, {
[styles.active]: isActive
})}
key={key}
>
<TrashIcon />
</button>
</div>
))}
<button
onClick={() => onSetActive(key)}
title="Click to chose"
className={styles.button}
>
{name}
</button>
<button
onClick={() => {
onRemove(key)
}}
disabled={passthrough && !isActive}
className={styles.button}
>
{passthrough ? (
isActive ? (
<PlayIcon fill="#fff" width={14} height={14} />
) : (
<PausedIcon fill="#fff" width={14} height={14} />
)
) : (
<TrashIcon fill="#fff" width={14} height={14} />
)}
</button>
</div>
)
})}
</div>
)
}
1 change: 1 addition & 0 deletions packages/extension/src/popup/ConfigListButtons/pause.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions packages/extension/src/popup/ConfigListButtons/play.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 1 addition & 4 deletions packages/extension/src/popup/ConfigListButtons/trash.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 15 additions & 7 deletions packages/extension/src/popup/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export const getStatus = async (host: string) => {
BackgroundResponseMessage,
{ type: MessageType.Status; payload: any }
>['payload']
>((resolve, reject) => {
>((resolve) => {
sendMessage<
Extract<BackgroundReceiveMessage, { type: MessageType.Status }>
>(
Expand All @@ -112,7 +112,7 @@ export const getStatus = async (host: string) => {
host,
hasHandle: false,
hasConfig: false,
configNames: {}
configNames: []
})
}
},
Expand All @@ -123,17 +123,25 @@ export const getStatus = async (host: string) => {
hasHandle: true,
hasConfig: true,
activeConfig: 'host=localhost&name=production&jsonConfig=1',
configNames: {
production: {
configNames: [
{
name: 'production',
key: 'host=localhost&name=production&jsonConfig=1'
},
development: {
{
name: 'development',
key: 'host=localhost&name=development&jsonConfig=1'
},
staging: {
{
name: 'staging',
key: 'host=localhost&name=staging&jsonConfig=1'
},
{
name: 'passthrough',
passthrough: true,
key: `host=${host}&name=__mswde_passthrough_config&jsonConfig=1`
}
},
],
host: window.location.host
}
}
Expand Down
6 changes: 5 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,9 @@
"skipLibCheck": true
},
"include": ["packages/*/src/**/*", "packages/*/*.d.ts", "declaration.d.ts"],
"exclude": ["node_modules", "dist"]
"exclude": [
"node_modules",
"packages/*/dist/**/*",
"packages/*/node_modules/**/*"
]
}

0 comments on commit c8a7263

Please sign in to comment.