Compare commits

...

8 Commits

Author SHA1 Message Date
Mark Sinclair dfee78e6c0 Merge pull request #3425 from nymtech/fix-sdk-examples
Fix TS SDK examples and make them independent
2023-07-05 14:53:44 +01:00
Nadim Kobeissi 3515e4e1c3 Fix typo 2023-05-22 13:35:08 +02:00
Nadim Kobeissi 5ec20e5599 Firefox Extension example for Nym TypeScript SDK 2023-05-22 13:30:55 +02:00
Nadim Kobeissi 9f408d4c79 Do not inline WASM for ESM TypeScript SDK
This change is necessary for us to be able to use the TypeScript Nym SDK
in the context of Firefox extensions. See:

https://bugzilla.mozilla.org/show_bug.cgi?id=1294996
2023-05-22 13:30:02 +02:00
Nadim Kobeissi 31233b3b68 New TypeScript SDK example: Google Chrome
Example of a simple manifest v3 extension to load a Nym client within  a
popup view.
2023-05-19 14:28:30 +02:00
Nadim Kobeissi c4ea887319 Fix favicon path 2023-05-17 15:30:58 +02:00
Nadim Kobeissi 6a69449e43 Update build instructions 2023-05-17 15:30:32 +02:00
Nadim Kobeissi 7aac01cca1 Fix TS SDK examples and make them independent
The Nym TypeScript SDK comes with two main examples:

- `plain-html`
- `react-webpack-with-theme-example`

As of time of testing on the latest `develop` branch over at
@nymtech/nym, `react-webpack-with-theme-example` was broken because the
example was expected to load some of its packages from its own
`package.json` and the rest from the repository's root `package.json`,
which caused the problems outlined here:

https://stackoverflow.com/q/72413194

While addressing this issue, I noticed that the examples were not truly
independent from the rest of the repo. While I know that this is a
monorepo with mulitple workspaces and understand how that's supposed to
work, I think that specifically in the case of example folders, we need
to ensure the ability for these folders to work fully independently from
the rest of the repository, because example folders are overwhelmingly
likely to be copied out of the repo to be expected by curious third
parties to work while fully self-contained. Furthermore, this makes
resolving the above-mentioned original issue easier.

Therefore, in this commit, I did the following for both `plain-html` and
`react-webpack-with-theme-example`:

- Ensure that their `package.json`s are fully self-sufficient and that
  no packages would need to be loaded from the repository's root
  `package.json`.
- Ensure that their `tsconfig.json` are equally self-sufficient and do
  not reference the repository's root `tsconfig.json`.
- Ensure that the webpack configuration is present in each example,
  instead of being snuck into a hidden `.webpack` folder in the
  `sdk/typescript/examples` folder (this struck me as an ugly hack
  anyhow).
- Ensure that `react-webpack-with-theme-example` has direct access to a
  small SVG graphic of the Nym logo within its own folder.

I think these changes will both render the examples less likely to break
as the rest of the monorepo evolves. Freezing dependencies in time is
not appropriate for the monorepo, but it is totally appropriate for
examples since we don't really care about migrating them to the latest
best practices/security fixes for their dependencies as much.

Also, these changes will make the example folders more friendly for
third-party engineers, since they can just copy out the example folder
from the repo and have everything they need to understand how things
work within the example folder itself, instead of needing to hunt down
other files in the sprawling monorepo.
2023-05-16 15:35:13 +02:00
32 changed files with 808 additions and 101 deletions
@@ -0,0 +1,22 @@
# Nym Chrome Extension Example
This is an example of how Nym can be used within the context of a Chrome extension.
## Running the example
1. Copy a build of the Nym TypeScript SDK (ESM version) into `./sdk`.
2. Navigate to `chrome://extensions` in Google Chrome.
3. Enable "Developer mode" (top right of the page).
4. Click on "Load unpacked" (top left of the page).
5. Load this extension folder.
## How does it work?
The Nym Mixnet Client runs a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) that wraps
a WASM library that builds and encrypts Sphinx packets in the browser to send over the Nym mixnet:
![Sphinx packet](../docs/worker.svg)
The WASM code encrypts each layer of the Sphinx packet in the browser, before sending the Sphinx packet over a websocket to the ingress gateway:
![Sphinx packet](../docs/sphinx.svg)
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@@ -0,0 +1,15 @@
{
"name": "Nym Chrome Extension Example",
"description": "An example demonstrating how to integrate the Nym TypeScript SDK in the context of a Google Chrome browser extension.",
"version": "1.0",
"manifest_version": 3,
"permissions": [],
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
},
"action": {
"default_title": "Nym Chrome Extension Example",
"default_icon": "icon.png",
"default_popup": "popup.html"
}
}
@@ -0,0 +1,8 @@
body {
width: 800px;
min-height: 400px;
}
#editdialog input {
width: 100%;
}
@@ -0,0 +1,21 @@
<html>
<head>
<link rel="stylesheet" href="popup.css" />
<script type="module" src="src/main.js"></script>
</head>
<body>
<p><label>Sender:</label><input disabled="true" size="85" id="sender" value=""></p>
<p><label>Recipient:</label><input size="85" id="recipient" value=""></p>
<p><label>Message:</label><input id="message" value="Hello mixnet!"></p>
<p><button id="send-button">Send</button></p>
<p>Send messages from your browser, through the mixnet, and to the recipient using the "send" button.</p>
<p><span style="color: blue;">Sent</span> messages show in blue, <span style="color: green;">received</span> messages show in green.</p>
<hr>
<p></p>
<div id="output"></div>
<p></p>
</body>
</html>
@@ -0,0 +1,66 @@
// dom-utils.js
// Contains utility functionality to help manipulate the DOM elements necessary to demonstrate the Nym example.
/**
* Create a Sphinx packet and send it to the mixnet through the gateway node.
*
* Message and recipient are taken from the values in the user interface.
*
* @param {Client} nymClient the nym client to use for message sending
*/
async function sendMessageTo(nym) {
const message = document.getElementById('message').value;
const recipient = document.getElementById('recipient').value;
await nym.client.send({
payload: {
message,
mimeType: 'text/plain'
},
recipient
});
displaySend(message);
}
/**
* Display messages that have been sent up the websocket. Colours them blue.
*
* @param {string} message
*/
function displaySend(message) {
const timestamp = new Date().toISOString().substr(11, 12);
const sendDiv = document.createElement('div');
const paragraph = document.createElement('p');
paragraph.setAttribute('style', 'color: blue');
const paragraphContent = document.createTextNode(`${timestamp} sent >>> ${message}`);
paragraph.appendChild(paragraphContent);
sendDiv.appendChild(paragraph);
document.getElementById('output')?.appendChild(sendDiv);
}
/**
* Display received text messages in the browser. Colour them green.
*
* @param {string} message
*/
function displayReceived(message) {
const content = message;
const timestamp = new Date().toLocaleTimeString();
const receivedDiv = document.createElement('div');
const paragraph = document.createElement('p');
paragraph.setAttribute('style', 'color: green');
const paragraphContent = document.createTextNode(`${timestamp} received >>> ${content}`);
paragraph.appendChild(paragraphContent);
receivedDiv.appendChild(paragraph);
document.getElementById('output')?.appendChild(receivedDiv);
}
/**
* Display the nymClient's sender address in the user interface
*
* @param {Client} nymClient
*/
function displaySenderAddress(address) {
document.getElementById('sender').value = address;
}
export { sendMessageTo, displaySend, displayReceived , displaySenderAddress }
@@ -0,0 +1,59 @@
// main.js
// Simple example of how to load Nym's TypeScript SDK and bind it to a DOM.
// Look at dom-utils.js for the DOM utility functionality referenced here.
// Import the Nym mixnet ESM module.
import {
createNymMixnetClient
} from "../sdk/index.js";
// Import the DOM utility functionality.
import {
displaySenderAddress,
displayReceived,
sendMessageTo
} from "./dom-utils.js"
async function main() {
// Initialize the Nym mixnet client.
let nymClient = await createNymMixnetClient();
if (!nymClient) {
console.error('Oh no! Could not create client');
return;
}
const nymApiUrl = 'https://validator.nymtech.net/api';
const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
// subscribe to connect event, so that we can show the client's address
nymClient.events.subscribeToConnected((e) => {
if (e.args.address) {
displaySenderAddress(e.args.address);
}
});
// subscribe to message received events and show any string messages received
nymClient.events.subscribeToTextMessageReceivedEvent((e) => {
displayReceived(e.args.payload);
});
const sendButton = document.querySelector('#send-button')
if (sendButton) {
sendButton.onclick = function() {
if (nymClient) {
sendMessageTo(nymClient);
}
};
}
nymClient.events.subscribeToRawMessageReceivedEvent((e) => console.log('Received: ', e.args.payload));
await nymClient.client.start({
clientId: 'My awesome client',
nymApiUrl,
preferredGatewayIdentityKey,
});
}
window.addEventListener('DOMContentLoaded', () => {
main();
});
@@ -0,0 +1,33 @@
# Nym Firefox Extension Example
This is an example of how Nym can be used within the context of a Mozilla Firefox extension.
## Running the example
Copy a build of the Nym TypeScript SDK (ESM version) into `./sdk`.
Then, Open `sdk/index.js` and change the following line:
```js
var WorkerFactory = createURLWorkerFactory('web-worker-0.js');
```
to:
```js
var WorkerFactory = createURLWorkerFactory('sdk/web-worker-0.js');
```
The above annoying workaround is currently necessary for Firefox extensions.
Load the extension normally via manifest.json.
## How does it work?
The Nym Mixnet Client runs a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) that wraps
a WASM library that builds and encrypts Sphinx packets in the browser to send over the Nym mixnet:
![Sphinx packet](../docs/worker.svg)
The WASM code encrypts each layer of the Sphinx packet in the browser, before sending the Sphinx packet over a websocket to the ingress gateway:
![Sphinx packet](../docs/sphinx.svg)
@@ -0,0 +1,10 @@
<html>
<head>
<script type="module" src="src/background.js"></script>
</head>
<body>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@@ -0,0 +1,23 @@
{
"manifest_version": 3,
"name": "Nym Firefox Extension Example",
"version": "1.0",
"description": "An example demonstrating how to integrate the Nym TypeScript SDK in the context of a Mozilla Firefox browser extension.",
"icons": {
"48": "icon.png"
},
"permissions": [],
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval' blob:; object-src 'self' 'wasm-unsafe-eval' blob:; worker-src 'self' 'wasm-unsafe-eval' blob:;"
},
"background": {
"page": "background.html"
},
"action": {
"default_icon": {
"32" : "icon.png"
},
"default_title": "Quicknote",
"default_popup": "popup.html"
}
}
@@ -0,0 +1,8 @@
body {
width: 800px;
min-height: 400px;
}
#editdialog input {
width: 100%;
}
@@ -0,0 +1,21 @@
<html>
<head>
<link rel="stylesheet" href="popup.css" />
<script type="module" src="src/popup.js"></script>
</head>
<body>
<p><label>Sender:</label><input disabled="true" size="85" id="sender" value=""></p>
<p><label>Recipient:</label><input size="85" id="recipient" value=""></p>
<p><label>Message:</label><input id="message" value="Hello mixnet!"></p>
<p><button id="send-button">Send</button></p>
<p>Send messages from your browser, through the mixnet, and to the recipient using the "send" button.</p>
<p><span style="color: blue;">Sent</span> messages show in blue, <span style="color: green;">received</span> messages show in green.</p>
<hr>
<p></p>
<div id="output"></div>
<p></p>
</body>
</html>
@@ -0,0 +1,105 @@
// main.js
// Simple example of how to load Nym's TypeScript SDK and bind it to a DOM.
// Look at dom-utils.js for the DOM utility functionality referenced here.
// Import the Nym mixnet ESM module.
import {
createNymMixnetClient
} from "../sdk/index.js";
const backgroundState = {
isReady: false,
address: '',
recipient: '',
messageLog: []
}
async function initBackground() {
// Initialize the Nym mixnet client.
let nymClient = await createNymMixnetClient().catch((err) => {
console.log(err)
});
if (!nymClient) {
console.error('Oh no! Could not create client');
return;
}
const nymApiUrl = 'https://validator.nymtech.net/api';
const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
// subscribe to connect event, so that we can show the client's address
nymClient.events.subscribeToConnected((e) => {
if (e.args.address) {
backgroundState.address = e.args.address
browser.runtime.sendMessage({
type: 'displaySenderAddress',
message: backgroundState.address
})
}
});
// subscribe to message received events and show any string messages received
nymClient.events.subscribeToTextMessageReceivedEvent((e) => {
backgroundState.messageLog.push({
type: 'received',
message: e.args.payload
})
browser.runtime.sendMessage({
type: 'displayReceived',
message: e.args.payload
})
});
nymClient.events.subscribeToRawMessageReceivedEvent((e) => console.log('Received: ', e.args.payload));
await nymClient.client.start({
clientId: 'My awesome client',
nymApiUrl,
preferredGatewayIdentityKey,
});
browser.runtime.onMessage.addListener(async (data) => {
switch (data.type) {
case 'nymClientSendMessage':
if (nymClient) {
await nymClient.client.send({
payload: {
message: data.message,
mimeType: 'text/plain'
},
recipient: data.recipient
});
backgroundState.messageLog.push({
type: 'sent',
message: data.message
})
break;
}
}
})
backgroundState.isReady = true
}
window.addEventListener('DOMContentLoaded', () => {
browser.runtime.onMessage.addListener((data) => {
switch (data.type) {
case 'popupReady':
if (backgroundState.isReady) {
browser.runtime.sendMessage({
type: 'displaySenderAddress',
message: backgroundState.address
})
browser.runtime.sendMessage({
type: 'displayMessageLog',
message: backgroundState.messageLog
})
browser.runtime.sendMessage({
type: 'updateRecipient',
message: backgroundState.recipient
})
} else {
initBackground();
}
break;
case 'updateRecipient':
backgroundState.recipient = data.message
}
});
});
@@ -0,0 +1,80 @@
// dom-utils.js
// Contains utility functionality to help manipulate the DOM elements necessary to demonstrate the Nym example.
/**
* Create a Sphinx packet and send it to the mixnet through the gateway node.
*
* Message and recipient are taken from the values in the user interface.
*
* @param {Client} nymClient the nym client to use for message sending
*/
async function sendMessageTo() {
const message = document.getElementById('message').value;
const recipient = document.getElementById('recipient').value;
browser.runtime.sendMessage({
type: 'nymClientSendMessage',
message,
recipient
})
displaySend(message);
}
/**
* Display messages that have been sent up the websocket. Colours them blue.
*
* @param {string} message
*/
function displaySend(message) {
const timestamp = new Date().toISOString().substr(11, 12);
const sendDiv = document.createElement('div');
const paragraph = document.createElement('p');
paragraph.setAttribute('style', 'color: blue');
const paragraphContent = document.createTextNode(`${timestamp} sent >>> ${message}`);
paragraph.appendChild(paragraphContent);
sendDiv.appendChild(paragraph);
document.getElementById('output')?.appendChild(sendDiv);
}
/**
* Display received text messages in the browser. Colour them green.
*
* @param {string} message
*/
function displayReceived(message) {
const content = message;
const timestamp = new Date().toLocaleTimeString();
const receivedDiv = document.createElement('div');
const paragraph = document.createElement('p');
paragraph.setAttribute('style', 'color: green');
const paragraphContent = document.createTextNode(`${timestamp} received >>> ${content}`);
paragraph.appendChild(paragraphContent);
receivedDiv.appendChild(paragraph);
document.getElementById('output')?.appendChild(receivedDiv);
}
/**
* Display the nymClient's sender address in the user interface
*
* @param {Client} nymClient
*/
function displaySenderAddress(address) {
document.getElementById('sender').value = address;
}
function displayMessageLog(messageLog) {
for (let i = 0; i < messageLog.length; i++) {
if (messageLog[i].type === 'sent') {
displaySend(messageLog[i].message)
} else if (messageLog[i].type === 'received') {
displayReceived(messageLog[i].message)
}
}
}
export {
sendMessageTo,
displaySend,
displayReceived,
displaySenderAddress,
displayMessageLog
}
@@ -0,0 +1,45 @@
// Import the DOM utility functionality.
import {
displaySenderAddress,
displayReceived,
sendMessageTo,
displayMessageLog
} from "./dom-utils.js"
window.addEventListener('DOMContentLoaded', () => {
const sendButton = document.querySelector('#send-button')
if (sendButton) {
sendButton.onclick = function() {
sendMessageTo()
};
}
const recipient = document.getElementById('recipient')
recipient.onchange = () => {
browser.runtime.sendMessage({
type: 'updateRecipient',
message: recipient.value
})
}
browser.runtime.onMessage.addListener((data) => {
switch (data.type) {
case 'displaySenderAddress':
displaySenderAddress(data.message);
break;
case 'displayReceived':
displayReceived(data.message);
break;
case 'sendMessageTo':
sendMessageTo(data.message);
break;
case 'displayMessageLog':
displayMessageLog(data.message)
break;
case 'updateRecipient':
recipient.value = data.message
}
});
browser.runtime.sendMessage({
type: 'popupReady',
message: '',
})
});
+1 -1
View File
@@ -9,7 +9,7 @@ You can use this example as a seed for a new project.
Try out the chat app by running:
```
npm install
npm install --legacy-peer-deps
npm start
```
@@ -0,0 +1,13 @@
<svg width="300" height="300" viewBox="0 0 296 296" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M148 296C229.738 296 296 229.738 296 148C296 66.2619 229.738 0 148 0C66.2619 0 0 66.2619 0 148C0 229.738 66.2619 296 148 296Z" fill="url(#paint0_linear_113_1244)"/>
<path d="M148 285.875C224.147 285.875 285.875 224.146 285.875 148C285.875 71.8536 224.147 10.1248 148 10.1248C71.8538 10.1248 10.125 71.8536 10.125 148C10.125 224.146 71.8538 285.875 148 285.875Z" fill="#121725"/>
<path d="M88.8829 120.143H88.7169V120.281V168.637L68.3289 120.226L68.3012 120.143H68.1905H56.6272H43.653H43.5146V120.281V175.719V175.857H43.653H56.6272H56.7655V175.719V127.28L77.2365 175.774L77.2642 175.857H77.3748H88.8829H101.829H101.968V175.719V120.281V120.143H101.829H88.8829Z" fill="white"/>
<path d="M252.347 120.143H227.616H227.477L227.45 120.253L214.78 168.858L202.082 120.253L202.054 120.143H201.944H177.157H176.991V120.281V175.719V175.857H177.157H190.104H190.242V175.719V127.667L202.774 175.747L202.801 175.857H202.94H226.564H226.675L226.703 175.747L239.234 127.667V175.719V175.857H239.373H252.347H252.485V175.719V120.281V120.143H252.347Z" fill="white"/>
<path d="M155.663 120.143H155.58L155.552 120.198L139.812 147.557L123.988 120.198L123.96 120.143H123.877H108.911H108.635L108.773 120.364L133.145 162.579V175.719V175.857H133.283H146.257H146.396V175.719V162.579L170.767 120.364L170.905 120.143H170.629H155.663Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_113_1244" x1="0" y1="148" x2="296" y2="148" gradientUnits="userSpaceOnUse">
<stop offset="0.09375" stop-color="#FB6E4E"/>
<stop offset="1" stop-color="#FC1D60"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+39 -32
View File
@@ -4,49 +4,56 @@
"version": "1.0.0",
"license": "Apache-2.0",
"dependencies": {
"@nymproject/sdk": "1"
"@nymproject/sdk": "1",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"fork-ts-checker-webpack-plugin": "^8.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-refresh": "^0.14.0",
"react-refresh-typescript": "^2.0.9",
"webpack-favicons": "^1.3.8"
},
"devDependencies": {
"@babel/core": "^7.15.0",
"@babel/plugin-transform-async-to-generator": "^7.14.5",
"@babel/preset-env": "^7.15.0",
"@babel/preset-typescript": "^7.15.0",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"babel-loader": "^8.3.0",
"babel-plugin-root-import": "^5.1.0",
"@babel/core": "^7.21.8",
"@babel/plugin-transform-async-to-generator": "^7.20.7",
"@babel/preset-env": "^7.21.5",
"@babel/preset-typescript": "^7.21.5",
"@types/jest": "^29.5.1",
"@types/node": "^20.1.1",
"@typescript-eslint/eslint-plugin": "^5.59.5",
"@typescript-eslint/parser": "^5.59.5",
"ajv": "^8.12.0",
"babel-loader": "^9.1.2",
"babel-plugin-root-import": "^6.6.0",
"clean-webpack-plugin": "^4.0.0",
"css-loader": "^6.7.3",
"css-minimizer-webpack-plugin": "^3.0.2",
"dotenv-webpack": "^7.0.3",
"eslint": "^8.10.0",
"css-minimizer-webpack-plugin": "^5.0.0",
"dotenv-webpack": "^8.0.1",
"eslint": "^8.40.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^16.1.0",
"eslint-config-prettier": "^8.5.0",
"eslint-config-airbnb-typescript": "^17.0.0",
"eslint-config-prettier": "^8.8.0",
"eslint-import-resolver-root-import": "^1.0.4",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jest": "^26.1.1",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-prettier": "^4.2.1",
"file-loader": "^6.2.0",
"fork-ts-checker-webpack-plugin": "^7.2.1",
"html-webpack-plugin": "^5.3.2",
"jest": "^27.1.0",
"mini-css-extract-plugin": "^2.2.2",
"html-webpack-plugin": "^5.5.1",
"jest": "^29.5.0",
"mini-css-extract-plugin": "^2.7.5",
"npm-run-all": "^4.1.5",
"prettier": "^2.8.7",
"style-loader": "^3.3.1",
"prettier": "^2.8.8",
"style-loader": "^3.3.2",
"thread-loader": "^3.0.4",
"ts-jest": "^27.0.5",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.2",
"tsconfig-paths-webpack-plugin": "^3.5.2",
"typescript": "^4.6.2",
"tsconfig-paths-webpack-plugin": "^4.0.1",
"typescript": "^5.0.4",
"url-loader": "^4.1.1",
"webpack": "^5.75.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.5.0",
"webpack": "^5.82.0",
"webpack-cli": "^5.1.1",
"webpack-dev-server": "^4.15.0",
"webpack-merge": "^5.8.0"
},
"scripts": {
@@ -1,7 +1,26 @@
{
"extends": "../../tsconfig.json",
"compileOnSave": false,
"compilerOptions": {
"jsx": "react-jsx",
"target": "es2021",
"lib": ["es2021", "dom", "dom.iterable", "esnext"],
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"module": "ES2020",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": false,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@assets/*": ["../assets/*"]
},
"jsx": "react-jsx",
"outDir": "./dist"
},
"include": [
@@ -9,8 +28,21 @@
"src/**/*.tsx"
],
"exclude": [
"jest.config.js",
"webpack.config.js",
"webpack.prod.js",
"webpack.common.js",
"node_modules",
"build",
"dist"
"build",
"**/node_modules",
"dist",
"**/dist",
"scripts",
"jest",
"__tests__",
"**/__tests__",
"__jest__",
"**/__jest__",
"config/*"
]
}
@@ -81,7 +81,7 @@ const webpackCommon = (baseDir, htmlPath) => ({
}),
new WebpackFavicons({
src: path.resolve(__dirname, '../../assets/favicon/favicon.png'), // the asset directory is relative to THIS file
src: path.resolve(__dirname, 'assets/logo-circle.svg'), // the asset directory is relative to THIS file
}),
new Dotenv(),
@@ -1,6 +1,6 @@
const path = require('path');
const { mergeWithRules } = require('webpack-merge');
const { webpackCommon } = require('../.webpack/webpack.base');
const { webpackCommon } = require('./webpack.base');
module.exports = mergeWithRules({
module: {
@@ -15,7 +15,7 @@ You can use this example as a seed for a new project, and it uses:
Try out the chat app by running:
```
npm install
npm install --legacy-peer-deps
npm start
```
@@ -0,0 +1,13 @@
<svg width="300" height="300" viewBox="0 0 296 296" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M148 296C229.738 296 296 229.738 296 148C296 66.2619 229.738 0 148 0C66.2619 0 0 66.2619 0 148C0 229.738 66.2619 296 148 296Z" fill="url(#paint0_linear_113_1244)"/>
<path d="M148 285.875C224.147 285.875 285.875 224.146 285.875 148C285.875 71.8536 224.147 10.1248 148 10.1248C71.8538 10.1248 10.125 71.8536 10.125 148C10.125 224.146 71.8538 285.875 148 285.875Z" fill="#121725"/>
<path d="M88.8829 120.143H88.7169V120.281V168.637L68.3289 120.226L68.3012 120.143H68.1905H56.6272H43.653H43.5146V120.281V175.719V175.857H43.653H56.6272H56.7655V175.719V127.28L77.2365 175.774L77.2642 175.857H77.3748H88.8829H101.829H101.968V175.719V120.281V120.143H101.829H88.8829Z" fill="white"/>
<path d="M252.347 120.143H227.616H227.477L227.45 120.253L214.78 168.858L202.082 120.253L202.054 120.143H201.944H177.157H176.991V120.281V175.719V175.857H177.157H190.104H190.242V175.719V127.667L202.774 175.747L202.801 175.857H202.94H226.564H226.675L226.703 175.747L239.234 127.667V175.719V175.857H239.373H252.347H252.485V175.719V120.281V120.143H252.347Z" fill="white"/>
<path d="M155.663 120.143H155.58L155.552 120.198L139.812 147.557L123.988 120.198L123.96 120.143H123.877H108.911H108.635L108.773 120.364L133.145 162.579V175.719V175.857H133.283H146.257H146.396V175.719V162.579L170.767 120.364L170.905 120.143H170.629H155.663Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_113_1244" x1="0" y1="148" x2="296" y2="148" gradientUnits="userSpaceOnUse">
<stop offset="0.09375" stop-color="#FB6E4E"/>
<stop offset="1" stop-color="#FC1D60"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -4,80 +4,76 @@
"version": "1.0.0",
"license": "Apache-2.0",
"dependencies": {
"@mui/icons-material": "^5.5.0",
"@mui/lab": "^5.0.0-alpha.72",
"@mui/material": "^5.0.1",
"@mui/styles": "^5.0.1",
"react-mui-dropzone": "^4.0.6",
"@emotion/react": "^11.11.0",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.11.16",
"@mui/lab": "^5.0.0-alpha.129",
"@mui/material": "^5.12.3",
"@mui/styles": "^5.12.3",
"@nymproject/sdk": "1",
"fork-ts-checker-webpack-plugin": "^8.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
"react-mui-dropzone": "^4.0.6",
"react-refresh": "^0.14.0",
"use-clipboard-copy": "^0.2.0"
},
"devDependencies": {
"@babel/core": "^7.15.0",
"@babel/plugin-transform-async-to-generator": "^7.14.5",
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
"@svgr/webpack": "^6.1.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"babel-loader": "^8.3.0",
"babel-plugin-root-import": "^5.1.0",
"@babel/core": "^7.21.8",
"@babel/plugin-transform-async-to-generator": "^7.20.7",
"@babel/preset-env": "^7.21.5",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.5",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@svgr/webpack": "^8.0.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@types/jest": "^29.5.1",
"@types/node": "^20.1.1",
"@types/react": "^18.2.6",
"@types/react-dom": "^18.2.4",
"@typescript-eslint/eslint-plugin": "^5.59.5",
"@typescript-eslint/parser": "^5.59.5",
"babel-loader": "^9.1.2",
"babel-plugin-root-import": "^6.6.0",
"clean-webpack-plugin": "^4.0.0",
"css-loader": "^6.7.3",
"css-minimizer-webpack-plugin": "^3.0.2",
"dotenv-webpack": "^7.0.3",
"eslint": "^8.10.0",
"css-minimizer-webpack-plugin": "^5.0.0",
"dotenv-webpack": "^8.0.1",
"eslint": "^8.40.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^16.1.0",
"eslint-config-prettier": "^8.5.0",
"eslint-config-airbnb-typescript": "^17.0.0",
"eslint-config-prettier": "^8.8.0",
"eslint-import-resolver-root-import": "^1.0.4",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jest": "^26.1.1",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.2",
"eslint-plugin-react-hooks": "^4.3.0",
"favicons": "^7.0.2",
"favicons-webpack-plugin": "^5.0.2",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"favicons": "^7.1.2",
"favicons-webpack-plugin": "^6.0.0",
"file-loader": "^6.2.0",
"fork-ts-checker-webpack-plugin": "^7.2.1",
"html-webpack-plugin": "^5.3.2",
"jest": "^27.1.0",
"mini-css-extract-plugin": "^2.2.2",
"html-webpack-plugin": "^5.5.1",
"jest": "^29.5.0",
"mini-css-extract-plugin": "^2.7.5",
"npm-run-all": "^4.1.5",
"prettier": "^2.8.7",
"react-refresh-typescript": "^2.0.3",
"style-loader": "^3.3.1",
"prettier": "^2.8.8",
"react-refresh-typescript": "^2.0.9",
"style-loader": "^3.3.2",
"thread-loader": "^3.0.4",
"ts-jest": "^27.0.5",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.2",
"tsconfig-paths-webpack-plugin": "^3.5.2",
"typescript": "^4.6.2",
"tsconfig-paths-webpack-plugin": "^4.0.1",
"typescript": "^5.0.4",
"url-loader": "^4.1.1",
"webpack": "^5.75.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.5.0",
"webpack": "^5.82.0",
"webpack-cli": "^5.1.1",
"webpack-dev-server": "^4.15.0",
"webpack-favicons": "^1.3.8",
"webpack-merge": "^5.8.0"
},
"overrides": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10"
},
"scripts": {
"start": "webpack serve --progress --port 3000",
"build": "webpack build --progress --config webpack.prod.js",
@@ -31,7 +31,7 @@ import { ThemeToggle } from './ThemeToggle';
import { AppContextProvider, useAppContext } from './context';
import { MixnetContextProvider, parseBinaryMessageHeaders, useMixnetContext } from './context/mixnet';
// eslint-disable-next-line import/no-relative-packages
import Logo from '../../../../../assets/logo/logo-circle.svg';
import Logo from '../assets/logo-circle.svg';
export const AppTheme: FCWithChildren = ({ children }) => {
const { mode } = useAppContext();
@@ -1,7 +1,26 @@
{
"extends": "../../tsconfig.json",
"compileOnSave": false,
"compilerOptions": {
"jsx": "react-jsx",
"target": "es2021",
"lib": ["es2021", "dom", "dom.iterable", "esnext"],
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"module": "ES2020",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": false,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@assets/*": ["../assets/*"]
},
"jsx": "react-jsx",
"outDir": "./dist"
},
"include": [
@@ -9,8 +28,21 @@
"src/**/*.tsx"
],
"exclude": [
"jest.config.js",
"webpack.config.js",
"webpack.prod.js",
"webpack.common.js",
"node_modules",
"build",
"dist"
"build",
"**/node_modules",
"dist",
"**/dist",
"scripts",
"jest",
"__tests__",
"**/__tests__",
"__jest__",
"**/__jest__",
"config/*"
]
}
@@ -0,0 +1,98 @@
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
// const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const WebpackFavicons = require('webpack-favicons');
const Dotenv = require('dotenv-webpack');
const path = require('path');
const os = require('os');
/**
* Creates the default Webpack config
* @param baseDir The base directory path, e.g. pass `__dirname` of the webpack config file using this method
*/
const webpackCommon = (baseDir, htmlPath) => ({
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'thread-loader',
options: { workers: Math.max(2, os.cpus().length - 1) },
},
{ loader: 'ts-loader', options: { happyPackMode: true } },
],
exclude: /node_modules/,
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack'],
},
{
test: /\.(png|jpe?g|gif|md|webp)$/i,
// More information here https://webpack.js.org/guides/asset-modules/
type: 'asset',
},
{
// See https://webpack.js.org/guides/asset-management/#loading-fonts
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
{
test: /\.ya?ml$/,
type: 'json',
use: 'yaml-loader',
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
plugins: [new TsconfigPathsPlugin()],
alias: {
'react/jsx-runtime': require.resolve('react/jsx-runtime'),
},
},
plugins: [
// new CleanWebpackPlugin(),
...(Array.isArray(htmlPath)
? htmlPath.map((item) => new HtmlWebpackPlugin(item))
: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(baseDir, htmlPath || 'src/index.html'),
}),
]),
new ForkTsCheckerWebpackPlugin({
typescript: {
mode: 'write-references',
diagnosticOptions: {
semantic: true,
syntactic: true,
},
},
}),
new WebpackFavicons({
src: path.resolve(__dirname, 'assets/logo-circle.svg'), // the asset directory is relative to THIS file
}),
new Dotenv(),
],
output: {
path: path.resolve(baseDir, 'dist'),
publicPath: '/',
},
});
module.exports = {
webpackCommon,
};
@@ -1,6 +1,6 @@
const path = require('path');
const { mergeWithRules } = require('webpack-merge');
const { webpackCommon } = require('../.webpack/webpack.base');
const { webpackCommon } = require('./webpack.base');
module.exports = mergeWithRules({
module: {
@@ -11,7 +11,7 @@ export default {
format: 'es',
},
plugins: [
webWorkerLoader({ targetPlatform: 'browser', inline: true }),
webWorkerLoader({ targetPlatform: 'browser', inline: false }),
resolve({ extensions }),
typescript({
exclude: ['mixnet/wasm/worker.ts', 'mixnet/node-tester/worker.ts'],