From 7aac01cca1706784a3035c5b72b9387bce255d0a Mon Sep 17 00:00:00 2001 From: Nadim Kobeissi Date: Tue, 16 May 2023 15:35:13 +0200 Subject: [PATCH 1/7] 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. --- .../examples/plain-html/package.json | 71 ++++++------ .../examples/plain-html/tsconfig.json | 40 ++++++- .../{.webpack => plain-html}/webpack.base.js | 0 .../examples/plain-html/webpack.common.js | 2 +- .../assets/logo-circle.svg | 13 +++ .../package.json | 104 +++++++++--------- .../src/App.tsx | 2 +- .../tsconfig.json | 40 ++++++- .../webpack.base.js | 98 +++++++++++++++++ .../webpack.common.js | 2 +- 10 files changed, 275 insertions(+), 97 deletions(-) rename sdk/typescript/examples/{.webpack => plain-html}/webpack.base.js (100%) create mode 100644 sdk/typescript/examples/react-webpack-with-theme-example/assets/logo-circle.svg create mode 100644 sdk/typescript/examples/react-webpack-with-theme-example/webpack.base.js diff --git a/sdk/typescript/examples/plain-html/package.json b/sdk/typescript/examples/plain-html/package.json index a937165155..309abfc4ff 100644 --- a/sdk/typescript/examples/plain-html/package.json +++ b/sdk/typescript/examples/plain-html/package.json @@ -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": { diff --git a/sdk/typescript/examples/plain-html/tsconfig.json b/sdk/typescript/examples/plain-html/tsconfig.json index b8059e8698..b8c323c67c 100644 --- a/sdk/typescript/examples/plain-html/tsconfig.json +++ b/sdk/typescript/examples/plain-html/tsconfig.json @@ -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/*" ] } diff --git a/sdk/typescript/examples/.webpack/webpack.base.js b/sdk/typescript/examples/plain-html/webpack.base.js similarity index 100% rename from sdk/typescript/examples/.webpack/webpack.base.js rename to sdk/typescript/examples/plain-html/webpack.base.js diff --git a/sdk/typescript/examples/plain-html/webpack.common.js b/sdk/typescript/examples/plain-html/webpack.common.js index 3d5f37c912..9d86d2bc80 100644 --- a/sdk/typescript/examples/plain-html/webpack.common.js +++ b/sdk/typescript/examples/plain-html/webpack.common.js @@ -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: { diff --git a/sdk/typescript/examples/react-webpack-with-theme-example/assets/logo-circle.svg b/sdk/typescript/examples/react-webpack-with-theme-example/assets/logo-circle.svg new file mode 100644 index 0000000000..fdfff58182 --- /dev/null +++ b/sdk/typescript/examples/react-webpack-with-theme-example/assets/logo-circle.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/sdk/typescript/examples/react-webpack-with-theme-example/package.json b/sdk/typescript/examples/react-webpack-with-theme-example/package.json index 5af2e7b841..13f227e62e 100644 --- a/sdk/typescript/examples/react-webpack-with-theme-example/package.json +++ b/sdk/typescript/examples/react-webpack-with-theme-example/package.json @@ -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", diff --git a/sdk/typescript/examples/react-webpack-with-theme-example/src/App.tsx b/sdk/typescript/examples/react-webpack-with-theme-example/src/App.tsx index c2252585de..f435d2ddbc 100644 --- a/sdk/typescript/examples/react-webpack-with-theme-example/src/App.tsx +++ b/sdk/typescript/examples/react-webpack-with-theme-example/src/App.tsx @@ -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(); diff --git a/sdk/typescript/examples/react-webpack-with-theme-example/tsconfig.json b/sdk/typescript/examples/react-webpack-with-theme-example/tsconfig.json index b8059e8698..b8c323c67c 100644 --- a/sdk/typescript/examples/react-webpack-with-theme-example/tsconfig.json +++ b/sdk/typescript/examples/react-webpack-with-theme-example/tsconfig.json @@ -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/*" ] } diff --git a/sdk/typescript/examples/react-webpack-with-theme-example/webpack.base.js b/sdk/typescript/examples/react-webpack-with-theme-example/webpack.base.js new file mode 100644 index 0000000000..7725e12bc0 --- /dev/null +++ b/sdk/typescript/examples/react-webpack-with-theme-example/webpack.base.js @@ -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/favicon/favicon.png'), // the asset directory is relative to THIS file + }), + + new Dotenv(), + ], + output: { + path: path.resolve(baseDir, 'dist'), + publicPath: '/', + }, +}); + + +module.exports = { + webpackCommon, +}; diff --git a/sdk/typescript/examples/react-webpack-with-theme-example/webpack.common.js b/sdk/typescript/examples/react-webpack-with-theme-example/webpack.common.js index 459c5495e4..3d2b0f60c4 100644 --- a/sdk/typescript/examples/react-webpack-with-theme-example/webpack.common.js +++ b/sdk/typescript/examples/react-webpack-with-theme-example/webpack.common.js @@ -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: { From 6a69449e43ddfd5f597dce82abd1ca5970e237b1 Mon Sep 17 00:00:00 2001 From: Nadim Kobeissi Date: Wed, 17 May 2023 15:30:32 +0200 Subject: [PATCH 2/7] Update build instructions --- sdk/typescript/examples/plain-html/README.md | 2 +- .../examples/react-webpack-with-theme-example/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/typescript/examples/plain-html/README.md b/sdk/typescript/examples/plain-html/README.md index 4cc2c16e8e..91892da205 100644 --- a/sdk/typescript/examples/plain-html/README.md +++ b/sdk/typescript/examples/plain-html/README.md @@ -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 ``` diff --git a/sdk/typescript/examples/react-webpack-with-theme-example/README.md b/sdk/typescript/examples/react-webpack-with-theme-example/README.md index 9a773a9e0f..17ee5a9016 100644 --- a/sdk/typescript/examples/react-webpack-with-theme-example/README.md +++ b/sdk/typescript/examples/react-webpack-with-theme-example/README.md @@ -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 ``` From c4ea887319c521dbb34d779f4f23cd030a032cae Mon Sep 17 00:00:00 2001 From: Nadim Kobeissi Date: Wed, 17 May 2023 15:30:58 +0200 Subject: [PATCH 3/7] Fix favicon path --- .../examples/plain-html/assets/logo-circle.svg | 13 +++++++++++++ sdk/typescript/examples/plain-html/webpack.base.js | 2 +- .../webpack.base.js | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 sdk/typescript/examples/plain-html/assets/logo-circle.svg diff --git a/sdk/typescript/examples/plain-html/assets/logo-circle.svg b/sdk/typescript/examples/plain-html/assets/logo-circle.svg new file mode 100644 index 0000000000..fdfff58182 --- /dev/null +++ b/sdk/typescript/examples/plain-html/assets/logo-circle.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/sdk/typescript/examples/plain-html/webpack.base.js b/sdk/typescript/examples/plain-html/webpack.base.js index 7725e12bc0..c9656fb20b 100644 --- a/sdk/typescript/examples/plain-html/webpack.base.js +++ b/sdk/typescript/examples/plain-html/webpack.base.js @@ -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(), diff --git a/sdk/typescript/examples/react-webpack-with-theme-example/webpack.base.js b/sdk/typescript/examples/react-webpack-with-theme-example/webpack.base.js index 7725e12bc0..c9656fb20b 100644 --- a/sdk/typescript/examples/react-webpack-with-theme-example/webpack.base.js +++ b/sdk/typescript/examples/react-webpack-with-theme-example/webpack.base.js @@ -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(), From 31233b3b681dfaf8bce1a3c959c59d404234618c Mon Sep 17 00:00:00 2001 From: Nadim Kobeissi Date: Fri, 19 May 2023 14:28:30 +0200 Subject: [PATCH 4/7] New TypeScript SDK example: Google Chrome Example of a simple manifest v3 extension to load a Nym client within a popup view. --- .../examples/chrome-extension/README.md | 22 ++++++ .../examples/chrome-extension/icon.png | Bin 0 -> 11287 bytes .../examples/chrome-extension/manifest.json | 15 ++++ .../examples/chrome-extension/popup.css | 8 +++ .../examples/chrome-extension/popup.html | 21 ++++++ .../examples/chrome-extension/sdk/.gitkeep | 0 .../chrome-extension/src/dom-utils.js | 66 ++++++++++++++++++ .../examples/chrome-extension/src/main.js | 59 ++++++++++++++++ 8 files changed, 191 insertions(+) create mode 100644 sdk/typescript/examples/chrome-extension/README.md create mode 100644 sdk/typescript/examples/chrome-extension/icon.png create mode 100644 sdk/typescript/examples/chrome-extension/manifest.json create mode 100644 sdk/typescript/examples/chrome-extension/popup.css create mode 100644 sdk/typescript/examples/chrome-extension/popup.html create mode 100644 sdk/typescript/examples/chrome-extension/sdk/.gitkeep create mode 100644 sdk/typescript/examples/chrome-extension/src/dom-utils.js create mode 100644 sdk/typescript/examples/chrome-extension/src/main.js diff --git a/sdk/typescript/examples/chrome-extension/README.md b/sdk/typescript/examples/chrome-extension/README.md new file mode 100644 index 0000000000..8d7359e8c3 --- /dev/null +++ b/sdk/typescript/examples/chrome-extension/README.md @@ -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) diff --git a/sdk/typescript/examples/chrome-extension/icon.png b/sdk/typescript/examples/chrome-extension/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..23d00a82896808f6cb126073f5cd06935d61542d GIT binary patch literal 11287 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 + + + + + + + +

+

+

+

+

Send messages from your browser, through the mixnet, and to the recipient using the "send" button.

+

Sent messages show in blue, received messages show in green.

+
+

+
+

+ + + \ No newline at end of file diff --git a/sdk/typescript/examples/chrome-extension/sdk/.gitkeep b/sdk/typescript/examples/chrome-extension/sdk/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sdk/typescript/examples/chrome-extension/src/dom-utils.js b/sdk/typescript/examples/chrome-extension/src/dom-utils.js new file mode 100644 index 0000000000..322606c241 --- /dev/null +++ b/sdk/typescript/examples/chrome-extension/src/dom-utils.js @@ -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 } \ No newline at end of file diff --git a/sdk/typescript/examples/chrome-extension/src/main.js b/sdk/typescript/examples/chrome-extension/src/main.js new file mode 100644 index 0000000000..30d84fb4c3 --- /dev/null +++ b/sdk/typescript/examples/chrome-extension/src/main.js @@ -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(); +}); \ No newline at end of file From 9f408d4c79a821580380b1d860371cf31097bb60 Mon Sep 17 00:00:00 2001 From: Nadim Kobeissi Date: Mon, 22 May 2023 13:30:02 +0200 Subject: [PATCH 5/7] 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 --- sdk/typescript/packages/sdk/rollup-esm.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/typescript/packages/sdk/rollup-esm.config.mjs b/sdk/typescript/packages/sdk/rollup-esm.config.mjs index 546188a9c3..84722e6128 100644 --- a/sdk/typescript/packages/sdk/rollup-esm.config.mjs +++ b/sdk/typescript/packages/sdk/rollup-esm.config.mjs @@ -13,7 +13,7 @@ export default { format: 'es', }, plugins: [ - webWorkerLoader({ targetPlatform: 'browser', inline: true }), + webWorkerLoader({ targetPlatform: 'browser', inline: false }), resolve({ extensions }), typescript({ exclude: 'mixnet/wasm/worker.ts', compilerOptions: { outDir: 'dist/esm' } }), ], From 5ec20e55991b7776d09806e8c1f8464b867a7698 Mon Sep 17 00:00:00 2001 From: Nadim Kobeissi Date: Mon, 22 May 2023 13:30:55 +0200 Subject: [PATCH 6/7] Firefox Extension example for Nym TypeScript SDK --- .../examples/firefox-extension/README.md | 33 ++++++ .../firefox-extension/background.html | 10 ++ .../examples/firefox-extension/icon.png | Bin 0 -> 11287 bytes .../examples/firefox-extension/manifest.json | 23 ++++ .../examples/firefox-extension/popup.css | 8 ++ .../examples/firefox-extension/popup.html | 21 ++++ .../examples/firefox-extension/sdk/.gitkeep | 0 .../firefox-extension/src/background.js | 105 ++++++++++++++++++ .../firefox-extension/src/dom-utils.js | 80 +++++++++++++ .../examples/firefox-extension/src/popup.js | 45 ++++++++ 10 files changed, 325 insertions(+) create mode 100644 sdk/typescript/examples/firefox-extension/README.md create mode 100644 sdk/typescript/examples/firefox-extension/background.html create mode 100644 sdk/typescript/examples/firefox-extension/icon.png create mode 100644 sdk/typescript/examples/firefox-extension/manifest.json create mode 100644 sdk/typescript/examples/firefox-extension/popup.css create mode 100644 sdk/typescript/examples/firefox-extension/popup.html create mode 100644 sdk/typescript/examples/firefox-extension/sdk/.gitkeep create mode 100644 sdk/typescript/examples/firefox-extension/src/background.js create mode 100644 sdk/typescript/examples/firefox-extension/src/dom-utils.js create mode 100644 sdk/typescript/examples/firefox-extension/src/popup.js diff --git a/sdk/typescript/examples/firefox-extension/README.md b/sdk/typescript/examples/firefox-extension/README.md new file mode 100644 index 0000000000..cb779f1332 --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/README.md @@ -0,0 +1,33 @@ +# Nym Chrome Extension Example + +This is an example of how Nym can be used within the context of a Chrome 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) diff --git a/sdk/typescript/examples/firefox-extension/background.html b/sdk/typescript/examples/firefox-extension/background.html new file mode 100644 index 0000000000..f87c4b9f01 --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/background.html @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/sdk/typescript/examples/firefox-extension/icon.png b/sdk/typescript/examples/firefox-extension/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..23d00a82896808f6cb126073f5cd06935d61542d GIT binary patch literal 11287 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 + + + + + + + +

+

+

+

+

Send messages from your browser, through the mixnet, and to the recipient using the "send" button.

+

Sent messages show in blue, received messages show in green.

+
+

+
+

+ + + \ No newline at end of file diff --git a/sdk/typescript/examples/firefox-extension/sdk/.gitkeep b/sdk/typescript/examples/firefox-extension/sdk/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sdk/typescript/examples/firefox-extension/src/background.js b/sdk/typescript/examples/firefox-extension/src/background.js new file mode 100644 index 0000000000..dda0289578 --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/src/background.js @@ -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 + } + }); +}); \ No newline at end of file diff --git a/sdk/typescript/examples/firefox-extension/src/dom-utils.js b/sdk/typescript/examples/firefox-extension/src/dom-utils.js new file mode 100644 index 0000000000..1bbfd628d3 --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/src/dom-utils.js @@ -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 +} \ No newline at end of file diff --git a/sdk/typescript/examples/firefox-extension/src/popup.js b/sdk/typescript/examples/firefox-extension/src/popup.js new file mode 100644 index 0000000000..9fe4600d4b --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/src/popup.js @@ -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: '', + }) +}); \ No newline at end of file From 3515e4e1c3942b80d8a72b67ce322340e6e43a7c Mon Sep 17 00:00:00 2001 From: Nadim Kobeissi Date: Mon, 22 May 2023 13:35:08 +0200 Subject: [PATCH 7/7] Fix typo --- sdk/typescript/examples/firefox-extension/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/typescript/examples/firefox-extension/README.md b/sdk/typescript/examples/firefox-extension/README.md index cb779f1332..8739d1da1f 100644 --- a/sdk/typescript/examples/firefox-extension/README.md +++ b/sdk/typescript/examples/firefox-extension/README.md @@ -1,6 +1,6 @@ -# Nym Chrome Extension Example +# Nym Firefox Extension Example -This is an example of how Nym can be used within the context of a Chrome extension. +This is an example of how Nym can be used within the context of a Mozilla Firefox extension. ## Running the example