mirror of
https://github.com/luxfi/safe-wallet.git
synced 2026-10-30 15:19:21 +00:00
* feat(web): support chat with Pylon (#7459) * feat(web): integrate external support-chat package - Add @safe-global/safe-react-support-chat dependency - Configure CSP headers for support chat iframe - Integrate support chat widget in Header component - Add support chat constants and configuration - Create useSupportChat hook for managing chat state * feat(web): add HelpMenu FAB with support chat integration - Add HelpMenu component with FAB that transforms to X when chat open - Integrate SupportChatDrawer from support-chat-embed package - Update useSupportChat hook with local type definitions - Add HelpMenu to PageLayout * fix: Set requester name to Safe{Wallet} instead of wallet label Ensures Pylon shows the source app rather than the wallet provider name (e.g. "Rabby") as the support requester identity. * refactor: remove support chat changes from Header Keep Header unchanged from upstream - support chat is only accessed via the HelpMenu FAB. * fix: remove unused shortenAddress function * fix(web): update support chat config and remove What's new menu item - Use link: instead of portal: for support-chat-embed to fix dependency hoisting - Update production chat URL to safe-support.vercel.app - Update allowed parents with correct production and localhost origins - Remove unused What's new menu item from HelpMenu * chore: add .npmrc for GitHub Packages registry auth Configures @safe-global scope to resolve from GitHub Packages registry, needed for the private @safe-global/support-chat-embed package. Note: Currently using link: for local dev. To switch to published package (1.0.0), the embed package needs to update its @mui/material peer dep to "^5.0.0 || ^6.0.0" to avoid hoisting conflicts with MUI v6. * fix(web): use published support-chat-embed and fix monorepo module resolution Switch from local link to published @safe-global/support-chat-embed@^1.0.2 from GitHub Packages registry. Add @safe-global/theme to transpilePackages and prioritize web app node_modules for module resolution so workspace packages can resolve @mui/material. --------- * refactor(web): inline support-chat-embed and use feature architecture Remove @safe-global/support-chat-embed npm dependency (private GitHub Package Registry) and inline the source directly as a feature module. This eliminates the GITHUB_TOKEN requirement for yarn install. - Add FEATURES.SUPPORT_CHAT chain feature flag - Create src/features/support-chat/ with standard feature architecture - Remove GitHub Package Registry config from .yarnrc.yml and .npmrc - Remove react/react-dom webpack aliases from next.config.mjs - Remove env-var based SUPPORT_CHAT_ENABLED constant * fix(web): harden support-chat origin validation, rate limit, and data minimization - Remove unconditional localhost origin bypass in postMessage listener (only allow localhost when chatOrigin itself is localhost) - Tighten rate limit from 100 to 10 messages/second - Use only safeAddress for email alias, don't leak signer EOA address * chore: remove empty .npmrc No longer needed after removing GitHub Package Registry dependency. * fix: button css --------- Co-authored-by: Rudy | Safe <[email protected]> Co-authored-by: Hanzo Dev <[email protected]>
185 lines
5.4 KiB
JavaScript
185 lines
5.4 KiB
JavaScript
import path from 'path'
|
|
import withBundleAnalyzer from '@next/bundle-analyzer'
|
|
import withPWAInit from '@ducanh2912/next-pwa'
|
|
import remarkGfm from 'remark-gfm'
|
|
import remarkHeadingId from 'remark-heading-id'
|
|
import createMDX from '@next/mdx'
|
|
import remarkFrontmatter from 'remark-frontmatter'
|
|
import remarkMdxFrontmatter from 'remark-mdx-frontmatter'
|
|
import { readFile } from 'fs/promises'
|
|
import { fileURLToPath } from 'url'
|
|
import { execSync } from 'child_process'
|
|
import { SriManifestWebpackPlugin } from './plugins/sri-manifest-webpack-plugin.mjs'
|
|
|
|
let withRspack = null
|
|
if (process.env.USE_RSPACK === '1') {
|
|
process.env.NEXT_RSPACK = 'true'
|
|
// Disable rspack config validation to avoid warnings, use 'loose' to log errors.
|
|
process.env.RSPACK_CONFIG_VALIDATE = 'loose-silent'
|
|
delete process.env.TURBOPACK
|
|
try {
|
|
withRspack = (await import('next-rspack')).default
|
|
} catch {}
|
|
}
|
|
|
|
const SERVICE_WORKERS_PATH = './src/service-workers'
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
const pkgPath = path.join(__dirname, 'package.json')
|
|
const data = await readFile(pkgPath, 'utf-8')
|
|
const pkg = JSON.parse(data)
|
|
|
|
let commitHash = process.env.NEXT_PUBLIC_COMMIT_HASH
|
|
if (!commitHash) {
|
|
try {
|
|
commitHash = execSync('git rev-parse --short HEAD').toString().trim()
|
|
} catch {
|
|
commitHash = ''
|
|
}
|
|
}
|
|
|
|
const withPWA = withPWAInit({
|
|
dest: 'public',
|
|
workboxOptions: {
|
|
mode: 'production',
|
|
},
|
|
reloadOnOnline: false,
|
|
publicExcludes: [],
|
|
buildExcludes: [/./],
|
|
customWorkerSrc: SERVICE_WORKERS_PATH,
|
|
// Prefer InjectManifest for Web Push
|
|
swSrc: `${SERVICE_WORKERS_PATH}/index.ts`,
|
|
|
|
runtimeCaching: [
|
|
{
|
|
urlPattern: /\.(js|css|png|jpg|jpeg|gif|webp|svg|ico|ttf|woff|woff2|eot)$/,
|
|
handler: 'CacheFirst',
|
|
options: {
|
|
cacheName: 'static-assets',
|
|
expiration: {
|
|
maxEntries: 1000,
|
|
maxAgeSeconds: 60 * 60 * 24 * 365, // 1 year
|
|
},
|
|
},
|
|
},
|
|
],
|
|
|
|
cacheId: pkg.version,
|
|
})
|
|
|
|
const isProd = process.env.NODE_ENV === 'production'
|
|
const enableExperimentalOptimizations = process.env.ENABLE_EXPERIMENTAL_OPTIMIZATIONS === '1'
|
|
|
|
let appVersion = pkg.version
|
|
|
|
// Pin volatile values for visual regression builds to avoid Chromatic diffs
|
|
if (process.env.VISUAL_REGRESSION_BUILD === 'true') {
|
|
commitHash = 'vistest'
|
|
appVersion = 'istest' // UI prepends 'v' → displays 'vistest'
|
|
}
|
|
|
|
/** @type {import('next').NextConfig} */
|
|
const nextConfig = {
|
|
output: 'export', // static site export
|
|
|
|
transpilePackages: ['@safe-global/store', '@safe-global/theme'],
|
|
images: {
|
|
unoptimized: true,
|
|
},
|
|
|
|
env: {
|
|
NEXT_PUBLIC_COMMIT_HASH: commitHash,
|
|
NEXT_PUBLIC_APP_VERSION: process.env.VISUAL_REGRESSION_BUILD === 'true' ? 'vistest' : pkg.version,
|
|
NEXT_PUBLIC_APP_HOMEPAGE: pkg.homepage,
|
|
VISUAL_REGRESSION_BUILD: process.env.VISUAL_REGRESSION_BUILD || '',
|
|
},
|
|
|
|
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
|
|
reactStrictMode: false,
|
|
productionBrowserSourceMaps: true,
|
|
eslint: {
|
|
dirs: ['src', 'cypress'],
|
|
},
|
|
...(isProd || enableExperimentalOptimizations
|
|
? {
|
|
experimental: {
|
|
optimizePackageImports: ['@mui/material', '@mui/icons-material', 'lodash', 'date-fns', '@gnosis.pm/zodiac'],
|
|
},
|
|
}
|
|
: {}),
|
|
webpack(config, { dev }) {
|
|
config.module.rules.push({
|
|
test: /\.svg$/i,
|
|
issuer: { and: [/\.(js|ts|md)x?$/] },
|
|
use: [
|
|
{
|
|
loader: '@svgr/webpack',
|
|
options: {
|
|
prettier: false,
|
|
svgo: false,
|
|
svgoConfig: {
|
|
plugins: [
|
|
{
|
|
name: 'preset-default',
|
|
params: {
|
|
overrides: { removeViewBox: false },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
titleProp: true,
|
|
},
|
|
},
|
|
],
|
|
})
|
|
|
|
config.resolve.alias = {
|
|
...config.resolve.alias,
|
|
'bn.js': path.resolve('../../node_modules/bn.js/lib/bn.js'),
|
|
'mainnet.json': path.resolve('../..node_modules/@ethereumjs/common/dist.browser/genesisStates/mainnet.json'),
|
|
'@mui/material$': path.resolve('./src/components/common/Mui'),
|
|
}
|
|
|
|
if (dev) {
|
|
config.optimization.splitChunks = {
|
|
...config.optimization.splitChunks,
|
|
cacheGroups: {
|
|
...config.optimization.splitChunks.cacheGroups,
|
|
customModule: {
|
|
test: /[\\/]..[\\/]..[\\/]node_modules[\\/](@safe-global|ethers)[\\/]/,
|
|
name: 'protocol-kit-ethers',
|
|
chunks: 'all',
|
|
},
|
|
},
|
|
}
|
|
config.optimization.minimize = false
|
|
}
|
|
|
|
// Add SRI manifest plugin (production only, skip for Cypress tests)
|
|
if (!dev && process.env.NODE_ENV !== 'cypress') {
|
|
config.plugins.push(new SriManifestWebpackPlugin())
|
|
}
|
|
|
|
return config
|
|
},
|
|
}
|
|
|
|
const isRspack = process.env.USE_RSPACK === '1'
|
|
const enablePWA = process.env.ENABLE_PWA === '1'
|
|
|
|
const withMDX = isRspack
|
|
? createMDX({ extension: /\.(md|mdx)?$/, jsx: true, options: {} })
|
|
: createMDX({
|
|
extension: /\.(md|mdx)?$/,
|
|
jsx: true,
|
|
options: {
|
|
remarkPlugins: [remarkFrontmatter, [remarkMdxFrontmatter, { name: 'metadata' }], remarkHeadingId, remarkGfm],
|
|
rehypePlugins: [],
|
|
},
|
|
})
|
|
|
|
const shouldEnablePWA = isProd || enablePWA
|
|
let config = shouldEnablePWA ? withPWA(withMDX(nextConfig)) : withMDX(nextConfig)
|
|
if (withRspack) config = withRspack(config)
|
|
export default withBundleAnalyzer({ enabled: process.env.ANALYZE === 'true' })(config)
|