diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 373aa59650..8cf462c259 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,5 +28,5 @@ jobs: - name: Run tests run: yarn test - - name: Check for missing redirects - run: yarn tsx scripts/check-redirects.ts --ci + - name: Check for deleted markdown files + run: yarn tsx scripts/check-markdown.ts --ci \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index edc7718e65..d0a9d2e40b 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -86,9 +86,10 @@ check_command() { time_command() { local start_time=$(date +%s) "$@" + local exit_code=$? local end_time=$(date +%s) local duration=$((end_time - start_time)) - log_info "⏱️ Completed in ${duration}s" + return $exit_code } # Get list of staged files for selective processing @@ -106,7 +107,6 @@ main() { log_step "Starting pre-commit hook validation..." # Environment and dependency checks - log_info "🔍 Checking environment and dependencies..." check_command "node" check_command "yarn" check_command "git" @@ -125,20 +125,17 @@ main() { exit 0 fi - log_info "📁 Found $(echo "$staged_files" | wc -l | tr -d ' ') staged files" - # 1. Fast redirect validation (project-specific) - log_step "Validating redirects..." - time_command yarn check-redirects || exit_with_error "Redirect validation failed. Fix redirect issues before committing." - log_success "Redirect validation passed" + # log_step "Validating redirects..." + # Will need to replace the 'check-redirects' line below when we replace it + # time_command yarn check-redirects || exit_with_error "Redirect validation failed. Fix redirect issues before committing." + # log_success "Redirect validation passed" # 2. Submodule updates (only if submodules are staged or .gitmodules changed) if echo "$staged_files" | grep -E "(\.gitmodules|submodules/)" >/dev/null 2>&1; then log_step "Updating git submodules..." time_command git submodule update --init --recursive || exit_with_error "Git submodule update failed. Check submodule configuration." log_success "Git submodules updated" - else - log_info "⏭️ Skipping submodule update (no submodule changes detected)" fi # 3. Selective code formatting (only format staged files) @@ -152,30 +149,44 @@ main() { # Format JavaScript/TypeScript files if any if [ -n "$js_files" ]; then - log_info "🎨 Formatting JS/TS files..." echo "$js_files" | xargs yarn prettier --write --config "./.prettierrc.js" || exit_with_error "JavaScript/TypeScript formatting failed" fi # Format Markdown files if any if [ -n "$md_files" ]; then - log_info "📝 Formatting Markdown files..." echo "$md_files" | xargs yarn prettier --write --config "./.prettierrc.js" || exit_with_error "Markdown formatting failed" fi # Re-stage formatted files echo "$staged_files" | grep -E "\.(js|jsx|ts|tsx|json|md|mdx|scss)$" | xargs git add || true + + # Validate formatting of staged files only + local formatted_files + formatted_files=$(echo "$staged_files" | grep -E "\.(js|jsx|ts|tsx|json|md|mdx|scss)$" || true) + if [ -n "$formatted_files" ]; then + if ! echo "$formatted_files" | xargs yarn prettier --check --config "./.prettierrc.js"; then + exit_with_error "Formatting validation failed. Some staged files are not properly formatted. This should not happen after auto-formatting." + fi + fi + log_success "Code formatting completed and files re-staged" - else - log_info "⏭️ Skipping code formatting (no formattable files staged)" fi # 4. Markdown linting (only if markdown files are staged) if has_staged_files "\.(md|mdx)$"; then log_step "Validating Markdown syntax..." - time_command yarn lint:markdown || exit_with_error "Markdown validation failed. Fix markdown syntax errors before committing." - log_success "Markdown validation passed" - else - log_info "⏭️ Skipping Markdown validation (no markdown files staged)" + + # Get staged markdown files excluding sdk directory + local lint_md_files + lint_md_files=$(echo "$staged_files" | grep -E "\.(md|mdx)$" | grep -v "docs/sdk/" || true) + + if [ -n "$lint_md_files" ]; then + # Run markdownlint only on staged files + if ! echo "$lint_md_files" | xargs yarn markdownlint; then + exit_with_error "Markdown validation failed. Fix markdown syntax errors before committing." + fi + log_success "Markdown validation passed" + fi fi # 5. TypeScript type checking (only if TS files are staged) @@ -183,13 +194,10 @@ main() { log_step "Running TypeScript type checking..." time_command yarn typecheck || exit_with_error "TypeScript type checking failed. Fix type errors before committing." log_success "TypeScript type checking passed" - else - log_info "⏭️ Skipping TypeScript check (no TypeScript files staged)" fi # Final success message with timing log_success "🎉 All pre-commit checks passed successfully!" - log_info "✨ Commit is ready to proceed..." } # Trap to handle interruptions gracefully diff --git a/README.md b/README.md index ff10554875..42d887c7ff 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,3 @@ This part will update the glossary. ### Formatting 1. Run `yarn format` from the root directory. - -### Redirects - -1. From the root directory, run `yarn check-redirects`. diff --git a/docs/launch-arbitrum-chain/05-customize-your-chain/customize-precompile.mdx b/docs/launch-arbitrum-chain/05-customize-your-chain/customize-precompile.mdx index bcc02defd6..8da00a7f98 100644 --- a/docs/launch-arbitrum-chain/05-customize-your-chain/customize-precompile.mdx +++ b/docs/launch-arbitrum-chain/05-customize-your-chain/customize-precompile.mdx @@ -103,7 +103,7 @@ func (con *ArbHi) SayHi(c ctx, evm mech) (string, error) { } ``` -Next, navigate to (arbitrum_signer.go)[https://github.com/OffchainLabs/go-ethereum/blob/v1.12.2/core/types/arbitrum_signer.go] and add the new precompile address. +Next, navigate to [arbitrum_signer.go](https://github.com/OffchainLabs/go-ethereum/blob/v1.12.2/core/types/arbitrum_signer.go) and add the new precompile address. ```go var ArbosAddress = common.HexToAddress("0xa4b05") diff --git a/package.json b/package.json index 480232ce9c..6a31b9593e 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,8 @@ "typecheck": "tsc", "test": "vitest run", "test:watch": "vitest", - "check-redirects": "tsx scripts/check-redirects.ts", "check-releases": "ts-node scripts/check-releases.ts", + "check-markdown": "tsx scripts/check-markdown.ts", "notion:update": "tsx scripts/notion-update.ts", "notion:verify-quicklooks": "tsx scripts/notion-verify-quicklooks.ts", "lint:markdown": "markdownlint \"docs/**/*.{md,mdx}\" --ignore \"docs/sdk/**\"", diff --git a/scripts/check-markdown.ts b/scripts/check-markdown.ts new file mode 100644 index 0000000000..b3dce3e4ea --- /dev/null +++ b/scripts/check-markdown.ts @@ -0,0 +1,84 @@ +import { execSync } from 'child_process'; +import { exit } from 'process'; + +// Function to check for staged deletions of .md or .mdx files +function checkStagedMarkdownDeletions() { + try { + // Run git diff --cached --name-status to get staged changes + const output = execSync('git diff --cached --name-status').toString().trim(); + // Split the output into lines + const lines = output.split('\n'); + // Filter for deletions (D) of .md or .mdx files + const deletedMarkdownFiles = lines + .filter((line) => { + const parts = line.split('\t'); + const status = parts[0]; + const file = parts[1]; + return status === 'D' && (file.endsWith('.md') || file.endsWith('.mdx')); + }) + .map((line) => line.split('\t')[1]); // Extract the file names + if (deletedMarkdownFiles.length > 0) { + console.error('# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # '); + console.error('Error: The following Markdown files are staged for deletion:'); + deletedMarkdownFiles.forEach((file) => console.error(`- ${file}`)); + console.error('Please unstage these deletions or remove them if unintended.'); + console.error('# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # '); + } else { + console.log('No staged deletions of Markdown files found.'); + // No exit here, to allow continuing to the next check + } + } catch (error) { + console.error('Failed to execute git command. Ensure this is run in a git repository.'); + console.error(error.message); + exit(1); + } +} + +// Function to check for staged renames or moves of .md or .mdx files +function checkStagedMarkdownRenames() { + try { + // Run git diff --cached --name-status to get staged changes + const output = execSync('git diff --cached --name-status').toString().trim(); + + // Split the output into lines + const lines = output.split('\n'); + + // Array to hold details of renamed/moved files + const renamedMarkdownFiles: string[] = []; + + lines.forEach((line) => { + if (!line.trim()) return; + + const parts = line.split('\t'); + const status = parts[0]; + + if (status.startsWith('R')) { + // For renames: parts[0] is Rxxx, parts[1] is old file, parts[2] is new file + const oldFile = parts[1]; + const newFile = parts[2]; + if (oldFile.endsWith('.md') || oldFile.endsWith('.mdx')) { + renamedMarkdownFiles.push(`${oldFile} -> ${newFile}`); + } + } + }); + + if (renamedMarkdownFiles.length > 0) { + console.error('# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # '); + console.error('Error: The following Markdown files are staged for rename or move:'); + renamedMarkdownFiles.forEach((detail) => console.error(`- ${detail}`)); + console.error('Please unstage these changes or review them if unintended.'); + console.error('# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # '); + } else { + console.log('No staged renames or moves of Markdown files found.'); + // No exit here, to allow the script to complete + } + } catch (error) { + console.error('Failed to execute git command. Ensure this is run in a git repository.'); + console.error(error.message); + exit(1); + } +} + +// Run both checks +checkStagedMarkdownDeletions(); +checkStagedMarkdownRenames(); diff --git a/scripts/check-redirects.test.ts b/scripts/check-redirects.test.ts deleted file mode 100644 index a71e26e04e..0000000000 --- a/scripts/check-redirects.test.ts +++ /dev/null @@ -1,635 +0,0 @@ -import { execSync } from 'child_process'; -import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'fs'; -import { dirname, resolve } from 'path'; -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; -import { RedirectChecker, type RedirectCheckResult } from './check-redirects.js'; - -describe('RedirectChecker', () => { - const TEST_DIR = resolve(__dirname, 'test-redirect-checker'); - const VERCEL_JSON_PATH = resolve(TEST_DIR, 'vercel.json'); - const DOCS_DIR = resolve(TEST_DIR, 'docs'); - const PAGES_DIR = resolve(TEST_DIR, 'pages'); - - beforeEach(() => { - // Create test directories - mkdirSync(TEST_DIR, { recursive: true }); - mkdirSync(DOCS_DIR, { recursive: true }); - mkdirSync(PAGES_DIR, { recursive: true }); - mkdirSync(resolve(PAGES_DIR, 'docs'), { recursive: true }); - - // Initialize git repo - execSync('git init', { cwd: TEST_DIR }); - execSync('git config user.email "test@example.com"', { cwd: TEST_DIR }); - execSync('git config user.name "Test User"', { cwd: TEST_DIR }); - execSync('git commit --allow-empty -m "Initial commit"', { cwd: TEST_DIR }); - }); - - afterEach(() => { - rmSync(TEST_DIR, { recursive: true, force: true }); - }); - - describe('URL Path Handling', () => { - it('should handle index files correctly', () => { - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - - // Create and stage files - writeFileSync(resolve(PAGES_DIR, 'index.md'), 'content'); - writeFileSync(resolve(PAGES_DIR, 'docs/index.mdx'), 'content'); - execSync('git add .', { cwd: TEST_DIR }); - - // Test index file paths - const rootResult = (checker as any).getUrlFromPath('pages/index.md'); - const nestedResult = (checker as any).getUrlFromPath('pages/docs/index.mdx'); - - expect(rootResult).toBe('/'); - expect(nestedResult).toBe('/(docs/?)'); - }); - - it('should handle numbered prefixes in file paths', () => { - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - - const result = (checker as any).getUrlFromPath('pages/01-intro/02-getting-started.md'); - expect(result).toBe('/(intro/getting-started/?)'); - }); - - it('should normalize URLs consistently', () => { - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - - const testCases = [ - { input: '/path/to/doc/', expected: '/path/to/doc' }, - { input: '(path/to/doc)', expected: '/path/to/doc' }, - { input: '//path//to//doc//', expected: '/path/to/doc' }, - { input: '/(path/to/doc/?)', expected: '/path/to/doc' }, - ]; - - testCases.forEach(({ input, expected }) => { - const result = (checker as any).normalizeUrl(input); - expect(result).toBe(expected); - }); - }); - }); - - describe('Mode-specific Behavior', () => { - it('should create vercel.json in commit-hook mode if missing', async () => { - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - - try { - await checker.check(); - } catch (error) { - expect(error.message).toBe( - 'vercel.json was created. Please review and stage the file before continuing.', - ); - } - - expect(existsSync(VERCEL_JSON_PATH)).toBe(true); - const content = JSON.parse(readFileSync(VERCEL_JSON_PATH, 'utf8')); - expect(content).toEqual({ redirects: [] }); - }); - - it('should throw error in CI mode if vercel.json is missing', async () => { - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'ci', - }); - - const result = await checker.check(); - expect(result.error).toBe(`vercel.json not found at ${VERCEL_JSON_PATH}`); - }); - - it('should detect moved files differently in CI mode', async () => { - // Setup initial commit - writeFileSync(resolve(PAGES_DIR, 'old.md'), 'content'); - execSync('git add .', { cwd: TEST_DIR }); - execSync('git commit -m "initial"', { cwd: TEST_DIR }); - - // Move file - renameSync(resolve(PAGES_DIR, 'old.md'), resolve(PAGES_DIR, 'new.md')); - execSync('git add .', { cwd: TEST_DIR }); - execSync('git commit -m "move file"', { cwd: TEST_DIR }); - - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'ci', - }); - - // Create properly formatted vercel.json using prettier - const prettier = require('prettier'); - const options = await prettier.resolveConfig(process.cwd()) || {}; - const formattedContent = prettier.format(JSON.stringify({ redirects: [] }), { - ...options, - parser: 'json', - filepath: VERCEL_JSON_PATH, - }); - writeFileSync(VERCEL_JSON_PATH, formattedContent); - - const result = await checker.check(); - - expect(result.hasMissingRedirects).toBe(true); - expect(result.missingRedirects).toHaveLength(1); - expect(result.missingRedirects[0]).toEqual({ - from: '/(old/?)', - to: '/(new/?)', - }); - }); - }); - - describe('Redirect Management', () => { - it('should detect missing redirects for moved files', async () => { - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - - // Setup vercel.json - writeFileSync(VERCEL_JSON_PATH, JSON.stringify({ redirects: [] }, null, 2)); - - // Create and move a file - writeFileSync(resolve(PAGES_DIR, 'old.md'), 'content'); - execSync('git add .', { cwd: TEST_DIR }); - execSync('git commit -m "add file"', { cwd: TEST_DIR }); - - renameSync(resolve(PAGES_DIR, 'old.md'), resolve(PAGES_DIR, 'new.md')); - execSync('git add .', { cwd: TEST_DIR }); - - try { - await checker.check(); - } catch (error) { - expect(error.message).toBe( - 'New redirects added to vercel.json. Please review and stage the changes before continuing.', - ); - } - - const config = JSON.parse(readFileSync(VERCEL_JSON_PATH, 'utf8')); - expect(config.redirects).toHaveLength(1); - expect(config.redirects[0]).toEqual({ - source: '/(old/?)', - destination: '/(new/?)', - permanent: false, - }); - }); - - it('should not add duplicate redirects', async () => { - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - - // Setup vercel.json with existing redirect - writeFileSync( - VERCEL_JSON_PATH, - JSON.stringify({ - redirects: [ - { - source: '/(old/?)', - destination: '/(new/?)', - permanent: false, - }, - ], - }), - ); - - // Create and move a file - writeFileSync(resolve(PAGES_DIR, 'old.md'), 'content'); - execSync('git add .', { cwd: TEST_DIR }); - execSync('git commit -m "add file"', { cwd: TEST_DIR }); - - renameSync(resolve(PAGES_DIR, 'old.md'), resolve(PAGES_DIR, 'new.md')); - execSync('git add .', { cwd: TEST_DIR }); - - const result = await checker.check(); - expect(result.hasMissingRedirects).toBe(false); - - const config = JSON.parse(readFileSync(VERCEL_JSON_PATH, 'utf8')); - expect(config.redirects).toHaveLength(1); - }); - - it('should handle multiple file moves in one commit', async () => { - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - - // Setup vercel.json - writeFileSync(VERCEL_JSON_PATH, JSON.stringify({ redirects: [] }, null, 2)); - - // Create and move multiple files - writeFileSync(resolve(PAGES_DIR, 'old1.md'), 'content'); - writeFileSync(resolve(PAGES_DIR, 'old2.md'), 'content'); - execSync('git add .', { cwd: TEST_DIR }); - execSync('git commit -m "add files"', { cwd: TEST_DIR }); - - renameSync(resolve(PAGES_DIR, 'old1.md'), resolve(PAGES_DIR, 'new1.md')); - renameSync(resolve(PAGES_DIR, 'old2.md'), resolve(PAGES_DIR, 'new2.md')); - execSync('git add .', { cwd: TEST_DIR }); - - try { - await checker.check(); - } catch (error) { - expect(error.message).toBe( - 'New redirects added to vercel.json. Please review and stage the changes before continuing.', - ); - } - - const config = JSON.parse(readFileSync(VERCEL_JSON_PATH, 'utf8')); - expect(config.redirects).toHaveLength(2); - expect(config.redirects).toEqual([ - { - source: '/(old1/?)', - destination: '/(new1/?)', - permanent: false, - }, - { - source: '/(old2/?)', - destination: '/(new2/?)', - permanent: false, - }, - ]); - }); - - it('should not create redirects for newly added files', async () => { - // Setup vercel.json and commit it - writeFileSync(VERCEL_JSON_PATH, JSON.stringify({ redirects: [] }, null, 2)); - execSync('git add vercel.json', { cwd: TEST_DIR }); - execSync('git commit -m "Add empty vercel.json for new file test"', { cwd: TEST_DIR }); - - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - - // Create and stage a new file - const newFilePath = resolve(PAGES_DIR, 'brand-new-file.md'); - writeFileSync(newFilePath, 'content'); - // Ensure the path used in git add is relative to TEST_DIR - execSync('git add pages/brand-new-file.md', { cwd: TEST_DIR }); - - let result: RedirectCheckResult | undefined; - try { - result = await checker.check(); - } catch (e: any) { - // Fail if it's the specific error we want to avoid - if ( - e.message === - 'New redirects added to vercel.json. Please review and stage the changes before continuing.' - ) { - throw new Error('Test failed: Redirects were unexpectedly added for a new file.'); - } - // Re-throw other unexpected errors - throw e; - } - - // If checker.check() did not throw the specific "New redirects added..." error, - // result should be defined. - expect(result).toBeDefined(); - // No other errors (like vercel.json not found/malformed, though unlikely here) should occur. - expect(result!.error).toBeUndefined(); - // No missing redirects should be flagged for a new file. - expect(result!.hasMissingRedirects).toBe(false); - - // Verify that vercel.json was not modified - const finalConfig = JSON.parse(readFileSync(VERCEL_JSON_PATH, 'utf8')); - expect(finalConfig.redirects).toHaveLength(0); // Assuming it started empty - }); - - it('should not create a redirect if source and destination are the same after normalization', async () => { - // Setup vercel.json and commit it - writeFileSync(VERCEL_JSON_PATH, JSON.stringify({ redirects: [] }, null, 2)); - execSync('git add vercel.json', { cwd: TEST_DIR }); - execSync('git commit -m "Add empty vercel.json for self-redirect test"', { cwd: TEST_DIR }); - - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - - // Simulate a move that results in the same normalized URL - // e.g. pages/old-path/index.md -> pages/old-path.md - // Both getUrlFromPath might resolve to something like /(old-path/?) - const oldFileDir = resolve(PAGES_DIR, 'self-redirect-test'); - mkdirSync(oldFileDir, { recursive: true }); - const oldFilePath = resolve(oldFileDir, 'index.md'); - const newFilePath = resolve(PAGES_DIR, 'self-redirect-test.md'); - - writeFileSync(oldFilePath, 'content'); - execSync('git add pages/self-redirect-test/index.md', { cwd: TEST_DIR }); - execSync('git commit -m "Add file for self-redirect test"', { cwd: TEST_DIR }); - - renameSync(oldFilePath, newFilePath); - // Add both old (now deleted) and new paths to staging for git to detect as a rename - execSync('git add pages/self-redirect-test/index.md pages/self-redirect-test.md', { - cwd: TEST_DIR, - }); - - let result: RedirectCheckResult | undefined; - try { - result = await checker.check(); - } catch (e: any) { - if ( - e.message === - 'New redirects added to vercel.json. Please review and stage the changes before continuing.' - ) { - throw new Error( - 'Test failed: Redirects were unexpectedly added when source and destination were the same.', - ); - } - throw e; - } - - expect(result).toBeDefined(); - expect(result!.error).toBeUndefined(); - expect(result!.hasMissingRedirects).toBe(false); - - const finalConfig = JSON.parse(readFileSync(VERCEL_JSON_PATH, 'utf8')); - expect(finalConfig.redirects).toHaveLength(0); - }); - - it('should handle sorting and adding redirects in commit-hook mode', async () => { - const unsortedRedirects = [ - { - source: '/(zebra/?)', - destination: '/(zoo/?)', - permanent: false, - }, - { - source: '/(apple/?)', - destination: '/(fruit-basket/?)', - permanent: false, - }, - ]; - // Create an initially unsorted vercel.json - writeFileSync(VERCEL_JSON_PATH, JSON.stringify({ redirects: unsortedRedirects }, null, 2)); - execSync('git add vercel.json', { cwd: TEST_DIR }); // Stage it initially - execSync('git commit -m "add unsorted vercel.json"', { cwd: TEST_DIR }); - - let checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - - // --- Step 1: Test re-sorting of an existing unsorted file --- - try { - await checker.check(); // This call should trigger loadVercelConfig - } catch (error: any) { - expect(error.message).toBe( - 'vercel.json was re-sorted and/or re-formatted. Please review and stage the changes before continuing.', - ); - } - // Verify the file is now sorted on disk - let currentConfig = JSON.parse(readFileSync(VERCEL_JSON_PATH, 'utf8')); - expect(currentConfig.redirects).toEqual([ - { - source: '/(apple/?)', - destination: '/(fruit-basket/?)', - permanent: false, - }, - { - source: '/(zebra/?)', - destination: '/(zoo/?)', - permanent: false, - }, - ]); - - // --- Step 2: Simulate staging the re-sorted file and adding a new redirect --- - execSync('git add vercel.json', { cwd: TEST_DIR }); // Stage the sorted vercel.json - // No commit needed here, just need it staged for the next check() - - // Create and move a file to add a new redirect - writeFileSync(resolve(PAGES_DIR, 'old-banana.md'), 'content'); - execSync('git add pages/old-banana.md', { cwd: TEST_DIR }); - execSync('git commit -m "add banana file"', { cwd: TEST_DIR }); - - renameSync(resolve(PAGES_DIR, 'old-banana.md'), resolve(PAGES_DIR, 'new-yellow-fruit.md')); - execSync('git add pages/old-banana.md pages/new-yellow-fruit.md', { cwd: TEST_DIR }); - - // Re-initialize checker or ensure its internal state is fresh if necessary, - // though for this test, a new instance works fine. - checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - - try { - await checker.check(); - } catch (error: any) { - expect(error.message).toBe( - 'New redirects added to vercel.json. Please review and stage the changes before continuing.', - ); - } - - // Verify the file on disk has the new redirect and is still sorted - currentConfig = JSON.parse(readFileSync(VERCEL_JSON_PATH, 'utf8')); - expect(currentConfig.redirects).toEqual([ - { - source: '/(apple/?)', - destination: '/(fruit-basket/?)', - permanent: false, - }, - { - source: '/(old-banana/?)', - destination: '/(new-yellow-fruit/?)', - permanent: false, - }, - { - source: '/(zebra/?)', - destination: '/(zoo/?)', - permanent: false, - }, - ]); - }); - - it('should error in CI mode if vercel.json is unsorted', async () => { - const unsortedRedirects = [ - { source: '/(b/?)', destination: '/(c/?)', permanent: false }, - { source: '/(a/?)', destination: '/(d/?)', permanent: false }, - ]; - writeFileSync(VERCEL_JSON_PATH, JSON.stringify({ redirects: unsortedRedirects }, null, 2)); - // In CI, we assume vercel.json is part of the committed state. - - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'ci', - }); - - const result = await checker.check(); - expect(result.error).toBe( - 'vercel.json is not correctly sorted/formatted. Please run the pre-commit hook locally to fix and commit the changes.', - ); - // Ensure the file was not modified in CI mode - const fileContent = JSON.parse(readFileSync(VERCEL_JSON_PATH, 'utf8')); - expect(fileContent.redirects).toEqual(unsortedRedirects); - }); - - it('should error if vercel.json is malformed', async () => { - writeFileSync(VERCEL_JSON_PATH, 'this is not json'); - execSync('git add vercel.json', { cwd: TEST_DIR }); // Stage the malformed file - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - const result = await checker.check(); - expect(result.error).toContain('Error parsing'); - expect(result.error).toContain('Please fix the JSON format and try again.'); - }); - - it('should handle cross-platform line endings and trailing whitespace', async () => { - const redirects = [ - { - source: '/(zebra/?)', - destination: '/(zoo/?)', - permanent: false, - }, - { - source: '/(apple/?)', - destination: '/(fruit/?)', - permanent: false, - }, - ]; - - // Create vercel.json files with different line ending styles but in wrong order (to trigger formatting) - const testCases = [ - { - name: 'CRLF line endings with trailing spaces', - content: JSON.stringify({ redirects }, null, 2).replace(/\n/g, '\r\n') + ' \r\n', - }, - { - name: 'LF line endings with trailing spaces', - content: JSON.stringify({ redirects }, null, 2) + ' \n', - }, - { - name: 'CR line endings with trailing spaces', - content: JSON.stringify({ redirects }, null, 2).replace(/\n/g, '\r') + ' \r', - }, - { - name: 'mixed line endings with various trailing whitespace', - content: JSON.stringify({ redirects }, null, 2).replace(/\n/g, '\r\n') + '\t \n ', - }, - ]; - - for (const testCase of testCases) { - // Write file with problematic formatting (unsorted to trigger reformatting) - writeFileSync(VERCEL_JSON_PATH, testCase.content); - execSync('git add vercel.json', { cwd: TEST_DIR }); - execSync(`git commit -m "Add vercel.json with ${testCase.name}"`, { cwd: TEST_DIR }); - - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - - const result = await checker.check(); - expect(result.hasMissingRedirects).toBe(false); - expect(result.error).toBeUndefined(); - - // Verify the file content was normalized properly after auto-formatting - const normalizedContent = readFileSync(VERCEL_JSON_PATH, 'utf8'); - // Prettier formats the JSON, so we just need to verify it's properly formatted and sorted - const parsedContent = JSON.parse(normalizedContent); - expect(parsedContent.redirects).toEqual([redirects[1], redirects[0]]); // Should be sorted - expect(normalizedContent.endsWith('\n')).toBe(true); - expect(normalizedContent.includes('\r')).toBe(false); - expect(/\s+$/.test(normalizedContent.replace(/\n$/, ''))).toBe(false); // No trailing spaces except final newline - } - }); - }); - - // Original tests - it('should pass when no files are moved', async () => { - // Setup: Create empty vercel.json and commit it - writeFileSync(VERCEL_JSON_PATH, JSON.stringify({ redirects: [] }, null, 2)); - execSync('git add vercel.json', { cwd: TEST_DIR }); - execSync('git commit -m "Add empty vercel.json"', { cwd: TEST_DIR }); - - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - const result = await checker.check(); - expect(result.hasMissingRedirects).toBe(false); - }); - - it('should pass when moved file has matching redirect', async () => { - // Setup: Add a redirect that matches what we'll test - const vercelJson = { - redirects: [ - { - source: '/(old-page/?)', - destination: '/(new-page/?)', - permanent: false, - }, - ], - }; - writeFileSync(VERCEL_JSON_PATH, JSON.stringify(vercelJson, null, 2)); - execSync('git add vercel.json', { cwd: TEST_DIR }); - execSync('git commit -m "Add test vercel.json"', { cwd: TEST_DIR }); - - // Create and move a test file - const oldPath = resolve(TEST_DIR, 'pages/old-page.mdx'); - const newPath = resolve(TEST_DIR, 'pages/new-page.mdx'); - mkdirSync(resolve(TEST_DIR, 'pages'), { recursive: true }); - writeFileSync(oldPath, 'test content'); - execSync('git add pages/old-page.mdx', { cwd: TEST_DIR }); - execSync('git commit -m "Add test file"', { cwd: TEST_DIR }); - - // Move the file and stage it - mkdirSync(dirname(newPath), { recursive: true }); - renameSync(oldPath, newPath); - execSync('git add pages/old-page.mdx pages/new-page.mdx', { cwd: TEST_DIR }); - - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - const result = await checker.check(); - expect(result.hasMissingRedirects).toBe(false); - }); - - it('should fail when vercel.json changes are not staged', async () => { - // Setup: Create empty vercel.json and commit it - writeFileSync(VERCEL_JSON_PATH, JSON.stringify({ redirects: [] }, null, 2)); - execSync('git add vercel.json', { cwd: TEST_DIR }); - execSync('git commit -m "Add empty vercel.json"', { cwd: TEST_DIR }); - - // Create unstaged changes - writeFileSync( - VERCEL_JSON_PATH, - JSON.stringify( - { - redirects: [ - { - source: '/test', - destination: '/test2', - permanent: false, - }, - ], - }, - null, - 2, - ), - ); - - // Verify that vercel.json is modified but not staged - const status = execSync('git status --porcelain', { cwd: TEST_DIR, encoding: 'utf8' }); - expect(status).toContain(' M vercel.json'); - - const checker = new RedirectChecker({ - vercelJsonPath: VERCEL_JSON_PATH, - mode: 'commit-hook', - }); - const result = await checker.check(); - expect(result.error).toBe( - 'Unstaged changes to vercel.json. Please review and stage the changes before continuing.', - ); - }); -}); diff --git a/scripts/check-redirects.ts b/scripts/check-redirects.ts deleted file mode 100644 index 91132b500a..0000000000 --- a/scripts/check-redirects.ts +++ /dev/null @@ -1,687 +0,0 @@ -import { execSync } from 'child_process'; -import { readFileSync, existsSync, writeFileSync } from 'fs'; -import { resolve, dirname, basename, parse } from 'path'; - -interface Redirect { - source: string; - destination: string; - permanent: boolean; -} - -interface VercelConfig { - redirects: Redirect[]; -} - -interface MovedFile { - oldPath: string; - newPath: string; -} - -export interface RedirectCheckResult { - hasMissingRedirects: boolean; - missingRedirects: Array<{ - from: string; - to: string; - }>; - error?: string; -} - -interface RedirectCheckerOptions { - vercelJsonPath?: string; - mode: 'commit-hook' | 'ci'; - gitCommand?: string; -} - -interface RedirectIndices { - bySource: Map; - byDestination: Map; -} - -/** - * RedirectChecker manages URL redirects in vercel.json when markdown files are moved or renamed. - * It ensures that existing links to documentation pages remain accessible after restructuring. - * - * Key features: - * 1. Creates vercel.json if it doesn't exist and requires review - * 2. Detects unstaged changes to vercel.json and prevents further processing - * 3. Uses git to detect moved/renamed .md(x) files in staged changes - * 4. Automatically adds non-permanent redirects for moved files - * 5. Flattens redirect chains while preserving all entry points (SEO-friendly) - * 6. Detects and prevents circular redirects - * 7. Requires manual review and staging of any changes to vercel.json - * - * The workflow: - * 1. Checks/creates vercel.json and validates its state - * 2. Detects file moves using git diff on staged changes - * 3. For each moved markdown file: - * - Converts file paths to URL paths (stripping extensions and prefixes) - * - Checks for existing matching redirects - * - Checks for circular redirect patterns - * - Flattens any existing chains (e.g., A→B becomes A→C when adding B→C) - * - Adds new redirects if needed (always non-permanent) - * - Result: All old URLs redirect directly to final destination (no chains) - * 4. If redirects are added: - * - Updates vercel.json - * - Requires manual review and staging before continuing - * - * Redirect chain handling example: - * - Initial state: A → B (existing redirect) - * - File move detected: B → C - * - Result: A → C, B → C (flattened, both entry points preserved) - * - * This ensures a controlled process for maintaining URL backwards compatibility - * while requiring human oversight of redirect changes and optimizing for SEO. - */ -export class RedirectChecker { - private vercelJsonPath: string; - private mode: 'commit-hook' | 'ci'; - private gitCommand?: string; - - constructor(options: RedirectCheckerOptions) { - this.vercelJsonPath = options.vercelJsonPath || resolve(process.cwd(), 'vercel.json'); - this.mode = options.mode; - this.gitCommand = options.gitCommand; - } - - /** - * Find the git repository root by looking for .git directory - * Cross-platform compatible (works on Windows, Unix, macOS) - */ - private findGitRoot(): string { - let currentDir = dirname(this.vercelJsonPath); - const { root } = parse(currentDir); - - while (currentDir !== root) { - if (existsSync(resolve(currentDir, '.git'))) { - return currentDir; - } - currentDir = dirname(currentDir); - } - throw new Error('Not in a git repository'); - } - - /** - * Build index maps for O(1) redirect lookups - * This significantly improves performance from O(n²) to O(n) for redirect operations - */ - private buildRedirectIndices(config: VercelConfig): RedirectIndices { - const bySource = new Map(); - const byDestination = new Map(); - - for (const redirect of config.redirects) { - const normalizedSource = this.normalizeUrl(redirect.source); - const normalizedDest = this.normalizeUrl(redirect.destination); - - bySource.set(normalizedSource, redirect); - - if (!byDestination.has(normalizedDest)) { - byDestination.set(normalizedDest, []); - } - byDestination.get(normalizedDest)!.push(redirect); - } - - return { bySource, byDestination }; - } - - /** - * Sorts the redirects array alphabetically by source, then by destination. - */ - private sortRedirects(redirects: Redirect[]): void { - redirects.sort((a, b) => { - const sourceA = a.source.toLowerCase(); - const sourceB = b.source.toLowerCase(); - if (sourceA < sourceB) return -1; - if (sourceA > sourceB) return 1; - - const destA = a.destination.toLowerCase(); - const destB = b.destination.toLowerCase(); - if (destA < destB) return -1; - if (destA > destB) return 1; - - return 0; - }); - } - - /** - * Normalize a URL by removing parentheses, trailing slashes, and ensuring single leading slash - */ - private normalizeUrl(url: string): string { - return ( - '/' + - url - .replace(/[()]/g, '') // Remove parentheses - .replace(/^\/+/, '') // Remove leading slashes before adding a single one - .replace(/\/+/g, '/') // Replace multiple slashes with single slash - .replace(/\/\?$/, '') // Remove optional trailing slash - .replace(/\/$/, '') - ); // Remove trailing slash - } - - /** - * Get moved files from git diff based on mode - */ - private getMovedFiles(): MovedFile[] { - const defaultCommands = { - 'commit-hook': 'git diff --cached --name-status -M100% --find-renames', - 'ci': 'git diff --name-status --diff-filter=R HEAD~1 HEAD', - }; - - const command = this.gitCommand || defaultCommands[this.mode]; - - try { - const output = execSync(command, { - encoding: 'utf8', - cwd: this.findGitRoot(), - }); - return !output.trim() - ? [] - : output - .trim() - .split('\n') - .map((line) => { - const match = line.match(/^R\d+\s+(.+?)\s+(.+?)$/); - if (match && (match[1].endsWith('.md') || match[1].endsWith('.mdx'))) { - return { - oldPath: match[1].trim(), - newPath: match[2].trim(), - }; - } - return null; - }) - .filter((file): file is MovedFile => file !== null); - } catch (error) { - return []; - } - } - - /** - * Format JSON content using prettier with project configuration - */ - private async formatJsonContent(content: string): Promise { - try { - const prettier = require('prettier'); - const options = (await prettier.resolveConfig(process.cwd())) || {}; - - return prettier.format(content, { - ...options, - parser: 'json', - filepath: this.vercelJsonPath, - }); - } catch (error) { - // Fallback to basic JSON formatting if prettier fails - console.warn('⚠️ Prettier formatting failed, using basic JSON formatting:', error); - return JSON.stringify(JSON.parse(content), null, 2) + '\n'; - } - } - - /** - * Normalize file content by handling cross-platform line endings and trailing whitespace - */ - private normalizeFileContent(content: string): string { - return content - .replace(/\r\n/g, '\n') // Convert CRLF to LF - .replace(/\r/g, '\n') // Convert CR to LF - .trim(); // Remove leading/trailing whitespace - } - - /** - * Load and parse the vercel.json configuration file - */ - private async loadVercelConfig(): Promise { - if (!existsSync(this.vercelJsonPath)) { - if (this.mode === 'commit-hook') { - const newConfig: VercelConfig = { redirects: [] }; - this.sortRedirects(newConfig.redirects); - const rawContent = JSON.stringify(newConfig); - const formattedContent = await this.formatJsonContent(rawContent); - writeFileSync(this.vercelJsonPath, formattedContent, 'utf8'); - throw new Error( - 'vercel.json was created. Please review and stage the file before continuing.', - ); - } else { - // ci mode - throw new Error(`vercel.json not found at ${this.vercelJsonPath}`); - } - } - - const originalFileContent = readFileSync(this.vercelJsonPath, 'utf8'); - let config: VercelConfig; - try { - config = JSON.parse(originalFileContent) as VercelConfig; - } catch (e) { - throw new Error( - `Error parsing ${this.vercelJsonPath}: ${ - (e as Error).message - }. Please fix the JSON format and try again.`, - ); - } - - if (!config.redirects || !Array.isArray(config.redirects)) { - config.redirects = []; // Ensure redirects array exists and is an array for safety - } - - // Sort the in-memory representation - this.sortRedirects(config.redirects); - - const rawContent = JSON.stringify(config); - const newFileContent = await this.formatJsonContent(rawContent); - - // Normalize both contents for comparison to handle cross-platform differences - const normalizedOriginal = this.normalizeFileContent(originalFileContent); - const normalizedNew = this.normalizeFileContent(newFileContent); - - // If in commit-hook mode and the content changed due to sorting or formatting - if (this.mode === 'commit-hook' && normalizedOriginal !== normalizedNew) { - writeFileSync(this.vercelJsonPath, newFileContent, 'utf8'); - // Auto-stage the reformatted vercel.json to prevent commit loop - try { - const gitRoot = this.findGitRoot(); - const relativePath = this.vercelJsonPath.replace(gitRoot + '/', ''); - execSync(`git add ${relativePath}`, { - cwd: gitRoot, - encoding: 'utf8', - }); - console.log('📝 vercel.json was automatically formatted and staged.'); - } catch (error) { - throw new Error( - 'vercel.json was re-sorted and/or re-formatted. Please review and stage the changes before continuing.', - ); - } - } - - // In CI mode, if the file was not sorted/formatted correctly, it's an error. - if (this.mode === 'ci' && normalizedOriginal !== normalizedNew) { - throw new Error( - `vercel.json is not correctly sorted/formatted. Please run the pre-commit hook locally to fix and commit the changes.`, - ); - } - - return config; // Return the (potentially modified in memory, and possibly written to disk) config - } - - /** - * Convert a file path to its corresponding URL path - */ - private getUrlFromPath(filePath: string): string { - // Remove the file extension and convert to URL path - let urlPath = filePath - .replace(/^(pages|arbitrum-docs)\//, '') // Remove leading directory - .replace(/\d{2,3}-/g, '') // Remove leading numbers like '01-', '02-', etc. - .replace(/\.mdx?$/, '') // Remove .md or .mdx extension - .replace(/\/index$/, ''); // Convert /index to / for cleaner URLs - - // Handle empty path (root index) - if (!urlPath || urlPath === 'index') { - return '/'; - } - - // Format URL to match existing patterns - return `/(${urlPath}/?)`; // Add parentheses and optional trailing slash - } - - /** - * Check if a redirect exists in the config - */ - private hasRedirect(config: VercelConfig, oldUrl: string, newUrl: string): boolean { - return config.redirects.some((redirect) => { - const normalizedSource = this.normalizeUrl(redirect.source); - const normalizedOldUrl = this.normalizeUrl(oldUrl); - const normalizedNewUrl = this.normalizeUrl(redirect.destination); - const normalizedDestination = this.normalizeUrl(newUrl); - - return normalizedSource === normalizedOldUrl && normalizedNewUrl === normalizedDestination; - }); - } - - /** - * Find all redirects that point TO a given URL (normalized comparison) - * Uses index-based lookup for O(1) performance - */ - private findRedirectsPointingTo(indices: RedirectIndices, url: string): Redirect[] { - const normalized = this.normalizeUrl(url); - return indices.byDestination.get(normalized) || []; - } - - /** - * Follow a redirect chain to find the ultimate destination - * Returns the final destination URL, detects circular references, and enforces max depth - * Uses index-based lookup for O(1) performance - */ - private resolveRedirectChain( - indices: RedirectIndices, - startUrl: string, - maxDepth: number = 10, - ): { destination: string; isCircular: boolean; depth: number } { - const visited = new Set(); - let current = this.normalizeUrl(startUrl); - let depth = 0; - - while (depth < maxDepth) { - if (visited.has(current)) { - // Found a circular reference - return { destination: current, isCircular: true, depth }; - } - - visited.add(current); - - // Find the next redirect in the chain using O(1) lookup - const nextRedirect = indices.bySource.get(current); - - if (!nextRedirect) { - // End of chain - current is the final destination - return { destination: current, isCircular: false, depth }; - } - - current = this.normalizeUrl(nextRedirect.destination); - depth++; - } - - // Max depth exceeded - likely indicates a problem - console.warn(`⚠️ Redirect chain exceeded max depth (${maxDepth}): ${startUrl}`); - return { destination: current, isCircular: true, depth }; - } - - /** - * Check if adding a redirect would create a circular reference - * Uses index-based lookup for O(1) performance - */ - private wouldCreateCircularRedirect( - indices: RedirectIndices, - source: string, - destination: string, - ): boolean { - const visited = new Set(); - let current = this.normalizeUrl(destination); - const normalizedSource = this.normalizeUrl(source); - - while (current) { - if (visited.has(current)) { - // Found a cycle in the existing redirects - return true; - } - if (current === normalizedSource) { - // Would create a loop back to source - return true; - } - - visited.add(current); - - // Find next redirect in chain using O(1) lookup - const next = indices.bySource.get(current); - - if (!next) break; - current = this.normalizeUrl(next.destination); - } - - return false; - } - - /** - * Flatten redirect chains while preserving all entry points - * This is the key method that implements the correct SEO-friendly approach: - * - Updates all redirects pointing to oldUrl to point to newUrl (flattens chains) - * - Does NOT remove the intermediate redirect (preserves all entry points) - * - Prevents creation of redirect chains (A→B→C becomes A→C, B→C) - * Uses index-based lookup for O(1) performance - */ - private flattenRedirectChains( - config: VercelConfig, - indices: RedirectIndices, - oldUrl: string, - newUrl: string, - ): void { - const normalizedOldUrl = this.normalizeUrl(oldUrl); - const normalizedNewUrl = this.normalizeUrl(newUrl); - - // Find all redirects that currently point to oldUrl using O(1) lookup - const redirectsPointingToOld = this.findRedirectsPointingTo(indices, oldUrl); - - // Update each redirect to point directly to newUrl (flatten the chain) - for (const redirect of redirectsPointingToOld) { - console.log( - ` 🔄 Flattening chain: ${redirect.source} → ${redirect.destination} becomes ${redirect.source} → ${newUrl}`, - ); - redirect.destination = newUrl; - } - - // Remove any existing redirect from oldUrl (will be replaced with the new one) - // This prevents having both A→B and A→C for the same source - const existingRedirect = indices.bySource.get(normalizedOldUrl); - - if (existingRedirect) { - const existingRedirectIndex = config.redirects.indexOf(existingRedirect); - if (existingRedirectIndex !== -1) { - console.log( - ` 🗑️ Removing old redirect: ${config.redirects[existingRedirectIndex].source} → ${config.redirects[existingRedirectIndex].destination}`, - ); - config.redirects.splice(existingRedirectIndex, 1); - } - } - } - - /** - * Detect and report existing redirect chains in the configuration - * Returns an array of chains found (for logging/debugging purposes) - * Uses index-based lookup for O(1) performance - */ - private detectExistingChains(indices: RedirectIndices): string[][] { - const chains: string[][] = []; - const processed = new Set(); - - // Convert Map keys to array for iteration - const sources = Array.from(indices.bySource.keys()); - - for (const source of sources) { - if (processed.has(source)) continue; - - const chain = [source]; - let current = this.normalizeUrl(indices.bySource.get(source)!.destination); - - // Follow the chain - while (true) { - chain.push(current); - - const nextRedirect = indices.bySource.get(current); - - if (!nextRedirect) break; - - const next = this.normalizeUrl(nextRedirect.destination); - if (chain.includes(next)) { - // Circular reference detected - chain.push(next); - chains.push(chain); - break; - } - - current = next; - } - - // Mark all URLs in this chain as processed - for (const url of chain) { - processed.add(url); - } - - // Only report if it's a chain (length > 2) or circular (last === first) - if (chain.length > 2) { - chains.push(chain); - } - } - - return chains; - } - - /** - * Add a new redirect to the config with chain flattening - * This method implements the SEO-friendly approach: - * 1. Checks for circular redirects - * 2. Flattens any existing chains (updates A→B to A→C when adding B→C) - * 3. Adds the new redirect (B→C) - * 4. Result: A→C, B→C (no chains, all entry points preserved) - * Uses index-based lookups for O(1) performance - */ - private async addRedirect(oldUrl: string, newUrl: string, config: VercelConfig): Promise { - const normalizedOldUrl = this.normalizeUrl(oldUrl); - const normalizedNewUrl = this.normalizeUrl(newUrl); - - // Skip if source and destination are the same - if (normalizedOldUrl === normalizedNewUrl) { - console.log(` ⏭️ Skipping self-redirect: ${oldUrl} → ${newUrl}`); - return; - } - - // Build indices for efficient lookups - const indices = this.buildRedirectIndices(config); - - // Check for circular redirects - if (this.wouldCreateCircularRedirect(indices, oldUrl, newUrl)) { - console.warn( - ` ⚠️ Warning: Skipping redirect ${oldUrl} → ${newUrl} - would create circular chain`, - ); - return; - } - - // Flatten any existing chains pointing to oldUrl - console.log(` ➕ Adding redirect: ${oldUrl} → ${newUrl}`); - this.flattenRedirectChains(config, indices, oldUrl, newUrl); - - // Add the new redirect - config.redirects.push({ - source: oldUrl, - destination: newUrl, - permanent: false, - }); - - this.sortRedirects(config.redirects); - const rawContent = JSON.stringify(config); - const formattedContent = await this.formatJsonContent(rawContent); - writeFileSync(this.vercelJsonPath, formattedContent, 'utf8'); - } - - /** - * Check for missing redirects and optionally update vercel.json - */ - public async check(): Promise { - try { - // In commit-hook mode, check for unstaged changes - if (this.mode === 'commit-hook') { - const gitRoot = this.findGitRoot(); - const initialStatus = execSync('git status --porcelain', { - cwd: gitRoot, - encoding: 'utf8', - }); - const vercelJsonStatus = initialStatus - .split('\n') - .find((line) => line.includes(basename(this.vercelJsonPath))); - - if ( - vercelJsonStatus && - (vercelJsonStatus.startsWith(' M') || vercelJsonStatus.startsWith('??')) - ) { - throw new Error( - 'Unstaged changes to vercel.json. Please review and stage the changes before continuing.', - ); - } - } - - const config = await this.loadVercelConfig(); - const movedFiles = this.getMovedFiles(); - - if (movedFiles.length === 0) { - return { - hasMissingRedirects: false, - missingRedirects: [], - }; - } - - const missingRedirects: Array<{ from: string; to: string }> = []; - let redirectsAdded = false; - - for (const { oldPath, newPath } of movedFiles) { - const oldUrl = this.getUrlFromPath(oldPath); - const newUrl = this.getUrlFromPath(newPath); - - if (newUrl.includes('archive')) { - // Skip archived files - continue; - } - - if (!this.hasRedirect(config, oldUrl, newUrl)) { - missingRedirects.push({ from: oldUrl, to: newUrl }); - - // Only add redirects in commit-hook mode - if (this.mode === 'commit-hook') { - const countBeforeAdd = config.redirects.length; - await this.addRedirect(oldUrl, newUrl, config); // addRedirect might not add if old/new are same - if (config.redirects.length > countBeforeAdd) { - redirectsAdded = true; - } - } - } - } - - if (this.mode === 'commit-hook' && redirectsAdded) { - throw new Error( - 'New redirects added to vercel.json. Please review and stage the changes before continuing.', - ); - } - - // Determine final hasMissingRedirects status - let finalHasMissingRedirects = false; - if (this.mode === 'ci') { - finalHasMissingRedirects = missingRedirects.length > 0; - // In CI, also filter out self-redirects from the reported missingRedirects array - // as these aren't actionable and shouldn't fail CI if they are the only thing found. - const actionableMissingRedirects = missingRedirects.filter((r) => { - return this.normalizeUrl(r.from) !== this.normalizeUrl(r.to); - }); - finalHasMissingRedirects = actionableMissingRedirects.length > 0; - // Return the actionable list for CI to report - return { - hasMissingRedirects: finalHasMissingRedirects, - missingRedirects: actionableMissingRedirects, - }; - } else { - // commit-hook - finalHasMissingRedirects = redirectsAdded; - } - - return { - hasMissingRedirects: finalHasMissingRedirects, - missingRedirects, // In commit-hook, this list might contain self-redirects, but hasMissingRedirects guides action - }; - } catch (error) { - return { - hasMissingRedirects: false, - missingRedirects: [], - error: error instanceof Error ? error.message : 'Unknown error', - }; - } - } -} - -// CLI entry point -if (require.main === module) { - const mode = process.argv.includes('--ci') ? 'ci' : 'commit-hook'; - const checker = new RedirectChecker({ mode }); - - checker.check().then((result) => { - if (result.error) { - console.error('Error:', result.error); - process.exit(1); - } - - if (result.hasMissingRedirects) { - console.error('❌ Missing redirects found:'); - for (const redirect of result.missingRedirects) { - console.error(` From: ${redirect.from}`); - console.error(` To: ${redirect.to}`); - } - process.exit(1); - } - - if (mode === 'ci') { - console.log('✅ All necessary redirects are in place'); - } - process.exit(0); - }); -} diff --git a/src/components/InteractiveDiagrams/index.js b/src/components/InteractiveDiagrams/index.ts similarity index 100% rename from src/components/InteractiveDiagrams/index.js rename to src/components/InteractiveDiagrams/index.ts diff --git a/vercel.json b/vercel.json index aeb72adc88..1ff04add47 100644 --- a/vercel.json +++ b/vercel.json @@ -3,245 +3,246 @@ "outputDirectory": "build", "installCommand": "bash scripts/init-submodules.sh && yarn install", "framework": "docusaurus-2", + "trailingSlash": false, "redirects": [ { "source": "/", "destination": "/get-started/overview", "permanent": false }, { - "source": "/(anytrust/?)", + "source": "/anytrust", "destination": "/how-arbitrum-works/deep-dives/anytrust-protocol", "permanent": false }, { - "source": "/(anytrust/inside-anytrust/?)", + "source": "/anytrust/inside-anytrust", "destination": "/how-arbitrum-works/deep-dives/anytrust-protocol", "permanent": false }, { - "source": "/(arb-specific-things/?)", + "source": "/arb-specific-things", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview", "permanent": false }, { - "source": "/(arbgas/?)", + "source": "/arbgas", "destination": "/how-arbitrum-works/deep-dives/gas-and-fees", "permanent": false }, { - "source": "/(arbitrum-ethereum-differences/?)", + "source": "/arbitrum-ethereum-differences", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview", "permanent": false }, { - "source": "/(arbos/?)", + "source": "/arbos", "destination": "/how-arbitrum-works/deep-dives/geth", "permanent": false }, { - "source": "/(arbos/common-precompiles/?)", + "source": "/arbos/common-precompiles", "destination": "/build-decentralized-apps/precompiles/reference", "permanent": false }, { - "source": "/(arbos/gas/?)", + "source": "/arbos/gas", "destination": "/how-arbitrum-works/deep-dives/gas-and-fees", "permanent": false }, { - "source": "/(arbos/gateways/?)", + "source": "/arbos/gateways", "destination": "/how-arbitrum-works/inside-arbitrum-nitro", "permanent": false }, { - "source": "/(arbos/geth/?)", + "source": "/arbos/geth", "destination": "/how-arbitrum-works/deep-dives/geth", "permanent": false }, { - "source": "/(arbos/introduction/?)", + "source": "/arbos/introduction", "destination": "/how-arbitrum-works/deep-dives/geth", "permanent": false }, { - "source": "/(arbos/l1-gas-pricing/?)", + "source": "/arbos/l1-gas-pricing", "destination": "/how-arbitrum-works/deep-dives/gas-and-fees", "permanent": false }, { - "source": "/(arbos/l1-l2-messaging/?)", + "source": "/arbos/l1-l2-messaging", "destination": "/how-arbitrum-works/deep-dives/l1-to-l2-messaging", "permanent": false }, { - "source": "/(arbos/l1-pricing/?)", + "source": "/arbos/l1-pricing", "destination": "/how-arbitrum-works/deep-dives/gas-and-fees", "permanent": false }, { - "source": "/(arbos/l1-to-l2-messaging/?)", + "source": "/arbos/l1-to-l2-messaging", "destination": "/how-arbitrum-works/deep-dives/l1-to-l2-messaging", "permanent": false }, { - "source": "/(arbos/l2-l1-messaging/?)", + "source": "/arbos/l2-l1-messaging", "destination": "/how-arbitrum-works/deep-dives/l2-to-l1-messaging", "permanent": false }, { - "source": "/(arbos/precompiles/?)", + "source": "/arbos/precompiles", "destination": "/build-decentralized-apps/precompiles/reference", "permanent": false }, { - "source": "/(arbos_formats/?)", + "source": "/arbos_formats", "destination": "/how-arbitrum-works/deep-dives/geth", "permanent": false }, { - "source": "/(arbsys/?)", + "source": "/arbsys", "destination": "/build-decentralized-apps/precompiles/reference#arbsys", "permanent": false }, { - "source": "/(assertion-tree/?)", + "source": "/assertion-tree", "destination": "/how-arbitrum-works/deep-dives/assertions", "permanent": false }, { - "source": "/(asset-bridging/?)", + "source": "/asset-bridging", "destination": "/build-decentralized-apps/token-bridging/token-bridge-erc20", "permanent": false }, { - "source": "/(avm_design/?)", + "source": "/avm_design", "destination": "/how-arbitrum-works/inside-arbitrum-nitro", "permanent": false }, { - "source": "/(avm_specification/?)", + "source": "/avm_specification", "destination": "/how-arbitrum-works/inside-arbitrum-nitro", "permanent": false }, { - "source": "/(bold/bold-gentle-introduction/?)", + "source": "/bold/bold-gentle-introduction", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(bold/concepts/bold-technical-deep-dive/?)", + "source": "/bold/concepts/bold-technical-deep-dive", "destination": "/how-arbitrum-works/bold/bold-technical-deep-dive", "permanent": false }, { - "source": "/(bold/concepts/public-preview-expectations/?)", + "source": "/bold/concepts/public-preview-expectations", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(bridge-tokens/concepts/usdc-concept/?)", + "source": "/bridge-tokens/concepts/usdc-concept", "destination": "/arbitrum-bridge/usdc-arbitrum-one", "permanent": false }, { - "source": "/(bridging_assets/?)", + "source": "/bridging_assets", "destination": "/build-decentralized-apps/token-bridging/token-bridge-erc20", "permanent": false }, { - "source": "/(build-decentralized-apps/oracles/how-to-use-oracles/?)", + "source": "/build-decentralized-apps/oracles/how-to-use-oracles", "destination": "/build-decentralized-apps/oracles/overview-oracles", "permanent": false }, { - "source": "/(build-decentralized-apps/oracles/overview/?)", + "source": "/build-decentralized-apps/oracles/overview", "destination": "/build-decentralized-apps/oracles/overview-oracles", "permanent": false }, { - "source": "/(build-decentralized-apps/oracles/reference/?)", + "source": "/build-decentralized-apps/oracles/reference", "destination": "/build-decentralized-apps/oracles/overview-oracles", "permanent": false }, { - "source": "/(build-decentralized-apps/quickstart-solidity-hardhat/?)", + "source": "/build-decentralized-apps/quickstart-solidity-hardhat", "destination": "/build-decentralized-apps/quickstart-solidity-remix", "permanent": false }, { - "source": "/(build-decentralized-apps/reference/useful-addresses/?)", + "source": "/build-decentralized-apps/reference/useful-addresses", "destination": "/build-decentralized-apps/reference/contract-addresses", "permanent": false }, { - "source": "/(build-decentralized-apps/troubleshooting/?)", + "source": "/build-decentralized-apps/troubleshooting", "destination": "/for-devs/troubleshooting-building/", "permanent": false }, { - "source": "/(censorship_resistance/?)", + "source": "/censorship_resistance", "destination": "/how-arbitrum-works/deep-dives/sequencer", "permanent": false }, { - "source": "/(contract_deployment/?)", + "source": "/contract_deployment", "destination": "/for-devs/quickstart-solidity-remix", "permanent": false }, { - "source": "/(das/daserver-instructions/?)", + "source": "/das/daserver-instructions", "destination": "/run-arbitrum-node/data-availability-committees/get-started", "permanent": false }, { - "source": "/(developer_quickstart/?)", + "source": "/developer_quickstart", "destination": "/get-started/overview", "permanent": false }, { - "source": "/(devs-how-tos/bridge-tokens/gentle-introduction-bridge/?)", + "source": "/devs-how-tos/bridge-tokens/gentle-introduction-bridge", "destination": "/build-decentralized-apps/token-bridging/overview", "permanent": false }, { - "source": "/(devs-how-tos/bridge-tokens/how-to-bridge-tokens-custom-gateway/?)", + "source": "/devs-how-tos/bridge-tokens/how-to-bridge-tokens-custom-gateway", "destination": "/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-custom-gateway", "permanent": false }, { - "source": "/(devs-how-tos/bridge-tokens/how-to-bridge-tokens-custom-generic/?)", + "source": "/devs-how-tos/bridge-tokens/how-to-bridge-tokens-custom-generic", "destination": "/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-generic-custom", "permanent": false }, { - "source": "/(devs-how-tos/bridge-tokens/how-to-bridge-tokens-generic-custom/?)", + "source": "/devs-how-tos/bridge-tokens/how-to-bridge-tokens-generic-custom", "destination": "/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-generic-custom", "permanent": false }, { - "source": "/(devs-how-tos/bridge-tokens/how-to-bridge-tokens-overview/?)", + "source": "/devs-how-tos/bridge-tokens/how-to-bridge-tokens-overview", "destination": "/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started", "permanent": false }, { - "source": "/(devs-how-tos/bridge-tokens/how-to-bridge-tokens-standard/?)", + "source": "/devs-how-tos/bridge-tokens/how-to-bridge-tokens-standard", "destination": "/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-standard", "permanent": false }, { - "source": "/(devs-how-tos/how-to-estimate-gas/?)", + "source": "/devs-how-tos/how-to-estimate-gas", "destination": "/build-decentralized-apps/how-to-estimate-gas", "permanent": false }, { - "source": "/(devs-how-tos/how-to-use-oracles/?)", + "source": "/devs-how-tos/how-to-use-oracles", "destination": "/build-decentralized-apps/oracles/overview-oracles", "permanent": false }, { - "source": "/(differences_overview/?)", + "source": "/differences_overview", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview", "permanent": false }, { - "source": "/(dispute_resolution/?)", + "source": "/dispute_resolution", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, @@ -286,941 +287,907 @@ "permanent": false }, { - "source": "/(faqs/beta-status/?)", + "source": "/faqs/beta-status", "destination": "/build-decentralized-apps/reference/mainnet-risks", "permanent": false }, - { "source": "/(faqs/faqs-index/?)", "destination": "/learn-more/faq", "permanent": false }, - { "source": "/(faqs/gas-faqs/?)", "destination": "/learn-more/faq", "permanent": false }, - { "source": "/(faqs/how-fees/?)", "destination": "/faqs/gas-faqs", "permanent": false }, - { "source": "/(faqs/misc-faqs/?)", "destination": "/learn-more/faq", "permanent": false }, - { "source": "/(faqs/nodes-faqs/?)", "destination": "/node-running/faq", "permanent": false }, - { "source": "/(faqs/protocol-faqs/?)", "destination": "/learn-more/faq", "permanent": false }, + { "source": "/faqs/faqs-index", "destination": "/learn-more/faq", "permanent": false }, + { "source": "/faqs/gas-faqs", "destination": "/learn-more/faq", "permanent": false }, + { "source": "/faqs/how-fees", "destination": "/faqs/gas-faqs", "permanent": false }, + { "source": "/faqs/misc-faqs", "destination": "/learn-more/faq", "permanent": false }, + { "source": "/faqs/nodes-faqs", "destination": "/node-running/faq", "permanent": false }, + { "source": "/faqs/protocol-faqs", "destination": "/learn-more/faq", "permanent": false }, { - "source": "/(faqs/seq-or-val/?)", + "source": "/faqs/seq-or-val", "destination": "/faqs/protocol-faqs#q-seq-vs-val", "permanent": false }, - { "source": "/(faqs/the-merge/?)", "destination": "/get-started/overview", "permanent": false }, + { "source": "/faqs/the-merge", "destination": "/get-started/overview", "permanent": false }, { - "source": "/(faqs/tooling-faqs/?)", + "source": "/faqs/tooling-faqs", "destination": "/for-devs/troubleshooting-building", "permanent": false }, { - "source": "/(faqs/what-if-dispute/?)", + "source": "/faqs/what-if-dispute", "destination": "/faqs/protocol-faqs#q-dispute-reorg", "permanent": false }, - { "source": "/(faqs/x-chain-faqs/?)", "destination": "/learn-more/faq", "permanent": false }, + { "source": "/faqs/x-chain-faqs", "destination": "/learn-more/faq", "permanent": false }, { - "source": "/(finality/?)", + "source": "/finality", "destination": "/how-arbitrum-works/deep-dives/transaction-lifecycle", "permanent": false }, { - "source": "/(for-devs/chain-params/?)", + "source": "/for-devs/chain-params", "destination": "/build-decentralized-apps/reference/chain-params", "permanent": false }, { - "source": "/(for-devs/concepts/differences-between-arbitrum-ethereum/block-numbers-and-time/?)", + "source": "/for-devs/concepts/differences-between-arbitrum-ethereum/block-numbers-and-time", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time", "permanent": false }, { - "source": "/(for-devs/concepts/differences-between-arbitrum-ethereum/overview/?)", + "source": "/for-devs/concepts/differences-between-arbitrum-ethereum/overview", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview", "permanent": false }, { - "source": "/(for-devs/concepts/differences-between-arbitrum-ethereum/rpc-methods/?)", + "source": "/for-devs/concepts/differences-between-arbitrum-ethereum/rpc-methods", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/rpc-methods", "permanent": false }, { - "source": "/(for-devs/concepts/differences-between-arbitrum-ethereum/solidity-support/?)", + "source": "/for-devs/concepts/differences-between-arbitrum-ethereum/solidity-support", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/solidity-support", "permanent": false }, { - "source": "/(for-devs/concepts/fees/?)", + "source": "/for-devs/concepts/fees", "destination": "/build-decentralized-apps/how-to-estimate-gas", "permanent": false }, { - "source": "/(for-devs/concepts/nodeinterface/?)", + "source": "/for-devs/concepts/nodeinterface", "destination": "/build-decentralized-apps/nodeinterface/overview", "permanent": false }, { - "source": "/(for-devs/concepts/oracles/?)", + "source": "/for-devs/concepts/oracles", "destination": "/build-decentralized-apps/oracles/overview-oracles", "permanent": false }, { - "source": "/(for-devs/concepts/precompiles/?)", + "source": "/for-devs/concepts/precompiles", "destination": "/build-decentralized-apps/precompiles/overview", "permanent": false }, { - "source": "/(for-devs/concepts/public-chains/?)", + "source": "/for-devs/concepts/public-chains", "destination": "/build-decentralized-apps/public-chains", "permanent": false }, { - "source": "/(for-devs/concepts/token-bridge/token-bridge-erc20/?)", + "source": "/for-devs/concepts/token-bridge/token-bridge-erc20", "destination": "/build-decentralized-apps/token-bridging/token-bridge-erc20", "permanent": false }, { - "source": "/(for-devs/concepts/token-bridge/token-bridge-ether/?)", + "source": "/for-devs/concepts/token-bridge/token-bridge-ether", "destination": "/build-decentralized-apps/token-bridging/token-bridge-ether", "permanent": false }, { - "source": "/(for-devs/concepts/token-bridge/token-bridge-overview/?)", + "source": "/for-devs/concepts/token-bridge/token-bridge-overview", "destination": "/build-decentralized-apps/token-bridging/overview", "permanent": false }, { - "source": "/(for-devs/cross-chain-messsaging/?)", + "source": "/for-devs/cross-chain-messsaging", "destination": "/build-decentralized-apps/cross-chain-messaging", "permanent": false }, { - "source": "/(for-devs/dev-tools-and-resources/debugging-tools/?)", + "source": "/for-devs/dev-tools-and-resources/debugging-tools", "destination": "/build-decentralized-apps/reference/debugging-tools", "permanent": false }, { - "source": "/(for-devs/dev-tools-and-resources/development-frameworks/?)", + "source": "/for-devs/dev-tools-and-resources/development-frameworks", "destination": "/build-decentralized-apps/reference/development-frameworks", "permanent": false }, { - "source": "/(for-devs/dev-tools-and-resources/monitoring-tools-block-explorers/?)", + "source": "/for-devs/dev-tools-and-resources/monitoring-tools-block-explorers", "destination": "/build-decentralized-apps/reference/monitoring-tools-block-explorers", "permanent": false }, { - "source": "/(for-devs/dev-tools-and-resources/nodeinterface/?)", + "source": "/for-devs/dev-tools-and-resources/nodeinterface", "destination": "/build-decentralized-apps/nodeinterface/reference", "permanent": false }, { - "source": "/(for-devs/dev-tools-and-resources/oracles/?)", + "source": "/for-devs/dev-tools-and-resources/oracles", "destination": "/build-decentralized-apps/oracles/overview-oracles", "permanent": false }, { - "source": "/(for-devs/dev-tools-and-resources/overview/?)", + "source": "/for-devs/dev-tools-and-resources/overview", "destination": "/build-decentralized-apps/reference/node-providers", "permanent": false }, { - "source": "/(for-devs/dev-tools-and-resources/overview/?)", - "destination": "/build-decentralized-apps/reference/node-providers", - "permanent": false - }, - { - "source": "/(for-devs/dev-tools-and-resources/precompiles/?)", + "source": "/for-devs/dev-tools-and-resources/precompiles", "destination": "/build-decentralized-apps/precompiles/reference", "permanent": false }, { - "source": "/(for-devs/dev-tools-and-resources/web3-libraries-tools/?)", + "source": "/for-devs/dev-tools-and-resources/web3-libraries-tools", "destination": "/build-decentralized-apps/reference/web3-libraries-tools", "permanent": false }, { - "source": "/(for-devs/gentle-introduction-dapps/?)", + "source": "/for-devs/gentle-introduction-dapps", "destination": "/get-started/arbitrum-introduction", "permanent": false }, { - "source": "/(for-devs/quickstart-solidity-hardhat/?)", + "source": "/for-devs/quickstart-solidity-hardhat", "destination": "/build-decentralized-apps/quickstart-solidity-remix", "permanent": false }, { - "source": "/(for-devs/quickstart-solidity-remix/?)", + "source": "/for-devs/quickstart-solidity-remix", "destination": "/build-decentralized-apps/quickstart-solidity-remix", "permanent": false }, { - "source": "/(for-devs/useful-addresses/?)", + "source": "/for-devs/useful-addresses", "destination": "/build-decentralized-apps/reference/contract-addresses", "permanent": false }, { - "source": "/(for-users/troubleshooting-users/?)", + "source": "/for-users/troubleshooting-users", "destination": "/arbitrum-bridge/troubleshooting", "permanent": false }, { - "source": "/(fraud-proofs/challenge-manager/?)", + "source": "/fraud-proofs/challenge-manager", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(fraud-proofs/osp-assumptions/?)", + "source": "/fraud-proofs/osp-assumptions", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(fraud-proofs/wasm-wavm/?)", + "source": "/fraud-proofs/wasm-wavm", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(fraud-proofs/wavm-custom-opcodes/?)", + "source": "/fraud-proofs/wavm-custom-opcodes", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(fraud-proofs/wavm-floats/?)", + "source": "/fraud-proofs/wavm-floats", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(fraud-proofs/wavm-modules/?)", + "source": "/fraud-proofs/wavm-modules", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(frontend_integration/?)", + "source": "/frontend_integration", "destination": "/for-devs/quickstart-solidity-remix", "permanent": false }, { - "source": "/(get-started/get-started/?)", + "source": "/get-started/get-started", "destination": "/get-started/overview", "permanent": false }, { - "source": "/(getting-started-devs/?)", + "source": "/getting-started-devs", "destination": "/for-devs/quickstart-solidity-remix", "permanent": false }, { - "source": "/(getting-started-users/?)", + "source": "/getting-started-users", "destination": "/arbitrum-bridge/quickstart", "permanent": false }, - { "source": "/(glossary/?)", "destination": "/intro/glossary", "permanent": false }, + { "source": "/glossary", "destination": "/intro/glossary", "permanent": false }, { - "source": "/(how-arbitrum-works/?)", + "source": "/how-arbitrum-works", "destination": "/how-arbitrum-works/inside-arbitrum-nitro", "permanent": false }, { - "source": "/(how-arbitrum-works/a-gentle-introduction/?)", + "source": "/how-arbitrum-works/a-gentle-introduction", "destination": "/how-arbitrum-works/inside-arbitrum-nitro", "permanent": false }, { - "source": "/(how-arbitrum-works/anytrust-protocol/?)", + "source": "/how-arbitrum-works/anytrust-protocol", "destination": "/how-arbitrum-works/deep-dives/anytrust-protocol", "permanent": false }, { - "source": "/(how-arbitrum-works/arbos/geth/?)", + "source": "/how-arbitrum-works/arbos/geth", "destination": "/how-arbitrum-works/deep-dives/geth", "permanent": false }, { - "source": "/(how-arbitrum-works/arbos/introduction/?)", + "source": "/how-arbitrum-works/arbos/introduction", "destination": "/how-arbitrum-works/deep-dives/arbos", "permanent": false }, { - "source": "/(how-arbitrum-works/arbos/l1-l2-messaging/?)", + "source": "/how-arbitrum-works/arbos/l1-l2-messaging", "destination": "/how-arbitrum-works/deep-dives/l1-to-l2-messaging", "permanent": false }, { - "source": "/(how-arbitrum-works/arbos/l2-l1-messaging/?)", + "source": "/how-arbitrum-works/arbos/l2-l1-messaging", "destination": "/how-arbitrum-works/deep-dives/l2-to-l1-messaging", "permanent": false }, { - "source": "/(how-arbitrum-works/assertion-tree/?)", + "source": "/how-arbitrum-works/assertion-tree", "destination": "/how-arbitrum-works/deep-dives/assertions", "permanent": false }, { - "source": "/(how-arbitrum-works/bold/public-preview-expectations/?)", + "source": "/how-arbitrum-works/bold/public-preview-expectations", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(how-arbitrum-works/data-availability/?)", + "source": "/how-arbitrum-works/data-availability", "destination": "/run-arbitrum-node/data-availability", "permanent": false }, { - "source": "/(how-arbitrum-works/fees/?)", + "source": "/how-arbitrum-works/fees", "destination": "/how-arbitrum-works/deep-dives/gas-and-fees", "permanent": false }, { - "source": "/(how-arbitrum-works/fraud-proofs/challenge-manager/?)", - "destination": "/how-arbitrum-works/bold/gentle-introduction", - "permanent": false - }, - { - "source": "/(how-arbitrum-works/fraud-proofs/challenge-manager/?)", + "source": "/how-arbitrum-works/fraud-proofs/challenge-manager", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(how-arbitrum-works/fraud-proofs/osp-assumptions/?)", + "source": "/how-arbitrum-works/fraud-proofs/osp-assumptions", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(how-arbitrum-works/fraud-proofs/osp-assumptions/?)", + "source": "/how-arbitrum-works/fraud-proofs/wasm-to-wavm", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(how-arbitrum-works/fraud-proofs/wasm-to-wavm/?)", + "source": "/how-arbitrum-works/fraud-proofs/wavm-custom-opcodes", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(how-arbitrum-works/fraud-proofs/wavm-custom-opcodes/?)", - "destination": "/how-arbitrum-works/bold/gentle-introduction", - "permanent": false - }, - { - "source": "/(how-arbitrum-works/fraud-proofs/wavm-custom-opcodes/?)", - "destination": "/how-arbitrum-works/bold/gentle-introduction", - "permanent": false - }, - { - "source": "/(how-arbitrum-works/gas-fees/?)", + "source": "/how-arbitrum-works/gas-fees", "destination": "/how-arbitrum-works/deep-dives/gas-and-fees", "permanent": false }, { - "source": "/(how-arbitrum-works/geth-at-the-core/?)", + "source": "/how-arbitrum-works/geth-at-the-core", "destination": "/how-arbitrum-works/deep-dives/stf-gentle-intro", "permanent": false }, { - "source": "/(how-arbitrum-works/inside-anytrust/?)", + "source": "/how-arbitrum-works/inside-anytrust", "destination": "/how-arbitrum-works/deep-dives/anytrust-protocol", "permanent": false }, { - "source": "/(how-arbitrum-works/interactive-fraud-proofs/?)", + "source": "/how-arbitrum-works/interactive-fraud-proofs", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(how-arbitrum-works/l1-gas-pricing/?)", + "source": "/how-arbitrum-works/l1-gas-pricing", "destination": "/how-arbitrum-works/deep-dives/gas-and-fees", "permanent": false }, { - "source": "/(how-arbitrum-works/l1-pricing/?)", + "source": "/how-arbitrum-works/l1-pricing", "destination": "/how-arbitrum-works/deep-dives/gas-and-fees", "permanent": false }, { - "source": "/(how-arbitrum-works/l1-to-l2-messaging/?)", + "source": "/how-arbitrum-works/l1-to-l2-messaging", "destination": "/how-arbitrum-works/deep-dives/l1-to-l2-messaging", "permanent": false }, { - "source": "/(how-arbitrum-works/l1-to-l2-messaging/?)", - "destination": "/how-arbitrum-works/deep-dives/l1-to-l2-messaging", - "permanent": false - }, - { - "source": "/(how-arbitrum-works/l2-to-l1-messaging/?)", + "source": "/how-arbitrum-works/l2-to-l1-messaging", "destination": "/how-arbitrum-works/deep-dives/l2-to-l1-messaging", "permanent": false }, { - "source": "/(how-arbitrum-works/l2-to-l1-messaging/?)", - "destination": "/how-arbitrum-works/deep-dives/l2-to-l1-messaging", - "permanent": false - }, - { - "source": "/(how-arbitrum-works/nitro-vs-classic/?)", + "source": "/how-arbitrum-works/nitro-vs-classic", "destination": "/how-arbitrum-works/inside-arbitrum-nitro", "permanent": false }, { - "source": "/(how-arbitrum-works/optimistic-rollup/?)", + "source": "/how-arbitrum-works/optimistic-rollup", "destination": "/how-arbitrum-works/inside-arbitrum-nitro", "permanent": false }, { - "source": "/(how-arbitrum-works/separating-execution-from-proving/?)", + "source": "/how-arbitrum-works/separating-execution-from-proving", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(how-arbitrum-works/sequencer/?)", + "source": "/how-arbitrum-works/sequencer", "destination": "/how-arbitrum-works/deep-dives/sequencer", "permanent": false }, { - "source": "/(how-arbitrum-works/state-transition-function/arbos/?)", + "source": "/how-arbitrum-works/state-transition-function/arbos", "destination": "/how-arbitrum-works/deep-dives/arbos", "permanent": false }, { - "source": "/(how-arbitrum-works/state-transition-function/ethereum-vs-arbitrum/?)", + "source": "/how-arbitrum-works/state-transition-function/ethereum-vs-arbitrum", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview", "permanent": false }, { - "source": "/(how-arbitrum-works/state-transition-function/modified-geth-on-arbitrum/?)", + "source": "/how-arbitrum-works/state-transition-function/modified-geth-on-arbitrum", "destination": "/how-arbitrum-works/deep-dives/geth", "permanent": false }, { - "source": "/(how-arbitrum-works/state-transition-function/stf-gentle-intro/?)", + "source": "/how-arbitrum-works/state-transition-function/stf-gentle-intro", "destination": "/how-arbitrum-works/deep-dives/stf-gentle-intro", "permanent": false }, { - "source": "/(how-arbitrum-works/state-transition-function/stf-inputs/?)", + "source": "/how-arbitrum-works/state-transition-function/stf-inputs", "destination": "/how-arbitrum-works/deep-dives/stf-inputs", "permanent": false }, { - "source": "/(how-arbitrum-works/state-transition-function/stylus-execution-path/?)", + "source": "/how-arbitrum-works/state-transition-function/stylus-execution-path", "destination": "/how-arbitrum-works/deep-dives/arbos#stylus-specific-differences", "permanent": false }, { - "source": "/(how-arbitrum-works/timeboost/?)", + "source": "/how-arbitrum-works/timeboost", "destination": "/how-arbitrum-works/timeboost/gentle-introduction", "permanent": false }, { - "source": "/(how-arbitrum-works/transaction-lifecycle/?)", + "source": "/how-arbitrum-works/transaction-lifecycle", "destination": "/how-arbitrum-works/deep-dives/transaction-lifecycle", "permanent": false }, { - "source": "/(how-arbitrum-works/tx-lifecycle/?)", + "source": "/how-arbitrum-works/tx-lifecycle", "destination": "/how-arbitrum-works/deep-dives/transaction-lifecycle", "permanent": false }, { - "source": "/(how-arbitrum-works/validation-and-proving/proving-and-challenges/?)", + "source": "/how-arbitrum-works/validation-and-proving/proving-and-challenges", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(how-arbitrum-works/validation-and-proving/rollup-protocol/?)", + "source": "/how-arbitrum-works/validation-and-proving/rollup-protocol", "destination": "/how-arbitrum-works/inside-arbitrum-nitro", "permanent": false }, { - "source": "/(how-arbitrum-works/validation-and-proving/validation-and-proving/?)", + "source": "/how-arbitrum-works/validation-and-proving/validation-and-proving", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(how-arbitrum-works/why-nitro/?)", + "source": "/how-arbitrum-works/why-nitro", "destination": "/how-arbitrum-works/inside-arbitrum-nitro", "permanent": false }, { - "source": "/(inside-anytrust/?)", + "source": "/inside-anytrust", "destination": "/how-arbitrum-works/deep-dives/anytrust-protocol", "permanent": false }, { - "source": "/(inside-arbitrum-nitro/?)", + "source": "/inside-arbitrum-nitro", "destination": "/how-arbitrum-works/inside-arbitrum-nitro", "permanent": false }, { - "source": "/(inside_arbitrum/?)", + "source": "/inside_arbitrum", "destination": "/how-arbitrum-works/inside-arbitrum-nitro", "permanent": false }, { - "source": "/(installation/?)", + "source": "/installation", "destination": "/run-arbitrum-node/run-full-node", "permanent": false }, + { "source": "/intro", "destination": "/get-started/arbitrum-introduction", "permanent": false }, { - "source": "/(intro/?)", - "destination": "/get-started/arbitrum-introduction", - "permanent": false - }, - { - "source": "/(l1_l2_messages/?)", + "source": "/l1_l2_messages", "destination": "/how-arbitrum-works/deep-dives/l1-to-l2-messaging", "permanent": false }, { - "source": "/(launch-arbitrum-chain/arbitrum-chain-quickstart/?)", + "source": "/launch-arbitrum-chain/arbitrum-chain-quickstart", "destination": "/launch-arbitrum-chain/a-gentle-introduction", "permanent": false }, { - "source": "/(launch-arbitrum-chain/arbitrum-node-runners/enale-post-4blobs/?)", + "source": "/launch-arbitrum-chain/arbitrum-node-runners/enale-post-4blobs", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/enable-post-4844-blobs", "permanent": false }, { - "source": "/(launch-arbitrum-chain/bold-adoption-for-arbitrum-chains/?)", + "source": "/launch-arbitrum-chain/bold-adoption-for-arbitrum-chains", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/bold-adoption-for-arbitrum-chains", "permanent": false }, { - "source": "/(launch-arbitrum-chain/concepts/custom-gas-token-sdk/?)", + "source": "/launch-arbitrum-chain/concepts/custom-gas-token-sdk", "destination": "/build-decentralized-apps/custom-gas-token-sdk", "permanent": false }, { - "source": "/(launch-arbitrum-chain/configure-your-chain/advanced-configurations/bold/?)", + "source": "/launch-arbitrum-chain/configure-your-chain/advanced-configurations/bold", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/bold-adoption-for-arbitrum-chains", "permanent": false }, { - "source": "/(launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-rollup-chain/?)", + "source": "/launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-rollup-chain", "destination": "/launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-an-arbitrum-chain", "permanent": false }, { - "source": "/(launch-arbitrum-chain/deploy-an-arbitrum-chain/monitoring-tools-and-considerations/?)", + "source": "/launch-arbitrum-chain/deploy-an-arbitrum-chain/monitoring-tools-and-considerations", "destination": "/launch-arbitrum-chain/maintain-your-chain/monitoring-tools-and-considerations", "permanent": false }, { - "source": "/(launch-arbitrum-chain/how-tos/arbitrum-chain-finality/?)", + "source": "/launch-arbitrum-chain/how-tos/arbitrum-chain-finality", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/arbitrum-chain-finality", "permanent": false }, { - "source": "/(launch-arbitrum-chain/maintain-your-chain/upgrade-to-bold/?)", + "source": "/launch-arbitrum-chain/maintain-your-chain/upgrade-to-bold", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/bold-adoption-for-arbitrum-chains", "permanent": false }, { - "source": "/(launch-arbitrum-chain/reference/arbitrum-chain-configuration-parameters/?)", + "source": "/launch-arbitrum-chain/reference/arbitrum-chain-configuration-parameters", "destination": "/launch-arbitrum-chain/a-gentle-introduction", "permanent": false }, { - "source": "/(launch-arbitrum-chain/timeboost-for-arbitrum-chains/?)", + "source": "/launch-arbitrum-chain/timeboost-for-arbitrum-chains", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/timeboost-for-arbitrum-chains", "permanent": false }, { - "source": "/(launch-arbitrum-chain/what-is-arbitrum-orbit/?)", + "source": "/launch-arbitrum-chain/what-is-arbitrum-orbit", "destination": "/launch-arbitrum-chain/a-gentle-introduction", "permanent": false }, { - "source": "/(launch-orbit-chain/?)", + "source": "/launch-orbit-chain", "destination": "/launch-arbitrum-chain/a-gentle-introduction", "permanent": false }, { - "source": "/(launch-orbit-chain/a-gentle-introduction/?)", + "source": "/launch-orbit-chain/a-gentle-introduction", "destination": "/launch-arbitrum-chain/a-gentle-introduction", "permanent": false }, { - "source": "/(launch-orbit-chain/aep-fee-router-introduction/?)", + "source": "/launch-orbit-chain/aep-fee-router-introduction", "destination": "/launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/aep-fee-router-introduction", "permanent": false }, { - "source": "/(launch-orbit-chain/aep-license/?)", + "source": "/launch-orbit-chain/aep-license", "destination": "/launch-arbitrum-chain/aep-license", "permanent": false }, { - "source": "/(launch-orbit-chain/aeplicense/?)", + "source": "/launch-orbit-chain/aeplicense", "destination": "/launch-arbitrum-chain/aep-license", "permanent": false }, { - "source": "/(launch-orbit-chain/bold-adoption-for-orbit-chains/?)", + "source": "/launch-orbit-chain/bold-adoption-for-orbit-chains", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/bold-adoption-for-arbitrum-chains", "permanent": false }, { - "source": "/(launch-orbit-chain/concepts/chain-ownership/?)", + "source": "/launch-orbit-chain/concepts/chain-ownership", "destination": "/launch-arbitrum-chain/maintain-your-chain/ownership-structure-access-control", "permanent": false }, { - "source": "/(launch-orbit-chain/concepts/custom-gas-token-sdk/?)", + "source": "/launch-orbit-chain/concepts/custom-gas-token-sdk", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-rollup", "permanent": false }, { - "source": "/(launch-orbit-chain/concepts/public-preview-expectations/?)", + "source": "/launch-orbit-chain/concepts/public-preview-expectations", "destination": "/launch-arbitrum-chain/concepts/public-preview-expectations", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/advanced-configurations/aep-fee-router/aep-fee-router-introduction/?)", + "source": "/launch-orbit-chain/configure-your-chain/advanced-configurations/aep-fee-router/aep-fee-router-introduction", "destination": "/launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/aep-fee-router-introduction", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/advanced-configurations/aep-fee-router/calculate-aep-fees/?)", + "source": "/launch-orbit-chain/configure-your-chain/advanced-configurations/aep-fee-router/calculate-aep-fees", "destination": "/launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/calculate-aep-fees", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/advanced-configurations/aep-fee-router/set-up-aep-fee-router/?)", + "source": "/launch-orbit-chain/configure-your-chain/advanced-configurations/aep-fee-router/set-up-aep-fee-router", "destination": "/launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/set-up-aep-fee-router", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/advanced-configurations/bold/?)", + "source": "/launch-orbit-chain/configure-your-chain/advanced-configurations/bold", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/bold-adoption-for-arbitrum-chains", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/advanced-configurations/fast-withdrawals/?)", + "source": "/launch-orbit-chain/configure-your-chain/advanced-configurations/fast-withdrawals", "destination": "/launch-arbitrum-chain/configure-your-chain/advanced-configurations/fast-withdrawals", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/advanced-configurations/layer-leap/?)", + "source": "/launch-orbit-chain/configure-your-chain/advanced-configurations/layer-leap", "destination": "/launch-arbitrum-chain/configure-your-chain/advanced-configurations/layer-leap", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/arbos-configuration/?)", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/arbos-configuration", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/arbos-upgrade", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/arbos-upgrade/?)", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/arbos-upgrade", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/arbos-upgrade", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/batch-posting-assertion-control)", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/batch-posting-assertion-control", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/batch-posting-assertion-control", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/calculate-aep-fees/?)", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/calculate-aep-fees", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/calculate-aep-fees", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/customizable-challenge-period/?)", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/customizable-challenge-period", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/customizable-challenge-period", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/customizing-anytrust/?)", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/customizing-anytrust", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-anytrust", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/fee-management/?)", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/fee-management", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/fee-management", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/gas-optimization-tools/?)", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/gas-optimization-tools", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/gas-optimization-tools", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/per-batch-gas-cost/?)", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/per-batch-gas-cost", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/fee-management", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/set-up-aep-fee-router/?)", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/set-up-aep-fee-router", "destination": "/launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/set-up-aep-fee-router", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/stake-and-validator-configurations/?)", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/stake-and-validator-configurations", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/stake-and-validator-configurations", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-anytrust/?)", - "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-anytrust", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/use-a-custom-gas-token", + "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-rollup", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-rollup/?)", - "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-rollup", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-anytrust", + "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-anytrust", "permanent": false }, { - "source": "/(launch-orbit-chain/configure-your-chain/common-configurations/use-a-custom-gas-token/?)", + "source": "/launch-orbit-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-rollup", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-rollup", "permanent": false }, { - "source": "/(launch-orbit-chain/customize-your-chain/customize-arbos/?)", + "source": "/launch-orbit-chain/customize-your-chain/customize-arbos", "destination": "/launch-arbitrum-chain/customize-your-chain/customize-arbos", "permanent": false }, { - "source": "/(launch-orbit-chain/customize-your-chain/customize-precompile/?)", + "source": "/launch-orbit-chain/customize-your-chain/customize-precompile", "destination": "/launch-arbitrum-chain/customize-your-chain/customize-precompile", "permanent": false }, { - "source": "/(launch-orbit-chain/customize-your-chain/customize-stf/?)", + "source": "/launch-orbit-chain/customize-your-chain/customize-stf", "destination": "/launch-arbitrum-chain/customize-your-chain/customize-stf", "permanent": false }, { - "source": "/(launch-orbit-chain/deploy-an-orbit-chain/canonical-factory-contracts/?)", + "source": "/launch-orbit-chain/deploy-an-orbit-chain/canonical-factory-contracts", "destination": "/launch-arbitrum-chain/deploy-an-arbitrum-chain/canonical-factory-contracts", "permanent": false }, { - "source": "/(launch-orbit-chain/deploy-an-orbit-chain/configuring-orbit-chain/?)", + "source": "/launch-orbit-chain/deploy-an-orbit-chain/configuring-orbit-chain", "destination": "/launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-an-arbitrum-chain", "permanent": false }, { - "source": "/(launch-orbit-chain/deploy-an-orbit-chain/deploying-anytrust-chain/?)", + "source": "/launch-orbit-chain/deploy-an-orbit-chain/deploying-anytrust-chain", "destination": "/launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-an-arbitrum-chain", "permanent": false }, { - "source": "/(launch-orbit-chain/deploy-an-orbit-chain/deploying-custom-gas-token-chain/?)", + "source": "/launch-orbit-chain/deploy-an-orbit-chain/deploying-custom-gas-token-chain", "destination": "/launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-an-arbitrum-chain", "permanent": false }, { - "source": "/(launch-orbit-chain/deploy-an-orbit-chain/deploying-rollup-chain/?)", + "source": "/launch-orbit-chain/deploy-an-orbit-chain/deploying-rollup-chain", "destination": "/launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-an-arbitrum-chain", "permanent": false }, { - "source": "/(launch-orbit-chain/deploy-an-orbit-chain/deploying-token-bridge/?)", + "source": "/launch-orbit-chain/deploy-an-orbit-chain/deploying-token-bridge", "destination": "/launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-token-bridge", "permanent": false }, { - "source": "/(launch-orbit-chain/deploy-an-orbit-chain/monitoring-tools-and-considerations/?)", + "source": "/launch-orbit-chain/deploy-an-orbit-chain/monitoring-tools-and-considerations", "destination": "/launch-arbitrum-chain/maintain-your-chain/monitoring-tools-and-considerations", "permanent": false }, { - "source": "/(launch-orbit-chain/ecosystem-support/add-orbit-chain-to-bridge-ui/?)", + "source": "/launch-orbit-chain/ecosystem-support/add-orbit-chain-to-bridge-ui", "destination": "/launch-arbitrum-chain/ecosystem-support/add-arbitrum-chain-to-bridge-ui", "permanent": false }, { - "source": "/(launch-orbit-chain/ecosystem-support/get-listed-orbit-platforms/?)", + "source": "/launch-orbit-chain/ecosystem-support/get-listed-orbit-platforms", "destination": "/for-devs/dev-tools-and-resources/chain-info", "permanent": false }, { - "source": "/(launch-orbit-chain/ecosystem-support/orbit-portal/?)", + "source": "/launch-orbit-chain/ecosystem-support/orbit-portal", "destination": "/for-devs/dev-tools-and-resources/chain-info", "permanent": false }, { - "source": "/(launch-orbit-chain/faq-troubleshooting/troubleshooting-building-orbit/?)", + "source": "/launch-orbit-chain/faq-troubleshooting/troubleshooting-building-orbit", "destination": "/launch-arbitrum-chain/faq-troubleshooting/troubleshooting-building-arbitrum-chain", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/add-orbit-chain-to-bridge-ui/?)", + "source": "/launch-orbit-chain/how-tos/add-orbit-chain-to-bridge-ui", "destination": "/launch-arbitrum-chain/ecosystem-support/add-arbitrum-chain-to-bridge-ui", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/arbos-upgrade/?)", + "source": "/launch-orbit-chain/how-tos/arbos-upgrade", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/arbos-upgrade", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/calculate-aep-fees/?)", + "source": "/launch-orbit-chain/how-tos/calculate-aep-fees", "destination": "/launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/calculate-aep-fees", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/customize-arbos/?)", + "source": "/launch-orbit-chain/how-tos/customize-arbos", "destination": "/launch-arbitrum-chain/customize-your-chain/customize-arbos", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/customize-deployment-configuration/?)", + "source": "/launch-orbit-chain/how-tos/customize-deployment-configuration", "destination": "/launch-arbitrum-chain/how-tos/customize-deployment-configuration", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/customize-precompile/?)", - "destination": "launch-arbitrum-chain/customize-your-chain/customize-precompile", + "source": "/launch-orbit-chain/how-tos/customize-precompile", + "destination": "/launch-arbitrum-chain/customize-your-chain/customize-precompile", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/customize-stf/?)", + "source": "/launch-orbit-chain/how-tos/customize-stf", "destination": "/launch-arbitrum-chain/customize-your-chain/customize-stf", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/fast-withdrawals/?)", + "source": "/launch-orbit-chain/how-tos/fast-withdrawals", "destination": "/launch-arbitrum-chain/configure-your-chain/advanced-configurations/fast-withdrawals", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/how-to-configure-your-chain/?)", + "source": "/launch-orbit-chain/how-tos/how-to-configure-your-chain", "destination": "/launch-arbitrum-chain/a-gentle-introduction", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/manage-fee-collectors/?)", + "source": "/launch-orbit-chain/how-tos/manage-fee-collectors", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/fee-management", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/orbit-chain-finality/?)", + "source": "/launch-orbit-chain/how-tos/orbit-chain-finality", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/arbitrum-chain-finality", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/orbit-managing-gas-speed-limit/?)", + "source": "/launch-orbit-chain/how-tos/orbit-managing-gas-speed-limit", "destination": "/launch-arbitrum-chain/maintain-your-chain/guidance/state-size-limit", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/orbit-managing-state-growth/?)", + "source": "/launch-orbit-chain/how-tos/orbit-managing-state-growth", "destination": "/launch-arbitrum-chain/maintain-your-chain/guidance/state-growth", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/orbit-sdk-configuring-orbit-chain/?)", + "source": "/launch-orbit-chain/how-tos/orbit-sdk-configuring-orbit-chain", "destination": "/launch-arbitrum-chain/a-gentle-introduction", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/orbit-sdk-deploying-anytrust-chain/?)", + "source": "/launch-orbit-chain/how-tos/orbit-sdk-deploying-anytrust-chain", "destination": "/launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-an-arbitrum-chain", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/orbit-sdk-deploying-custom-gas-token-chain/?)", + "source": "/launch-orbit-chain/how-tos/orbit-sdk-deploying-custom-gas-token-chain", "destination": "/launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-an-arbiturm-chain", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/orbit-sdk-deploying-rollup-chain/?)", + "source": "/launch-orbit-chain/how-tos/orbit-sdk-deploying-rollup-chain", "destination": "/launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-an-arbitrum-chain", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/orbit-sdk-deploying-token-bridge/?)", + "source": "/launch-orbit-chain/how-tos/orbit-sdk-deploying-token-bridge", "destination": "/launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-token-bridge", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/orbit-sdk-preparing-node-config/?)", + "source": "/launch-orbit-chain/how-tos/orbit-sdk-preparing-node-config", "destination": "/launch-arbitrum-chain/how-tos/arbitrum-chain-sdk-preparing-node-config", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/set-up-aep-fee-router/?)", + "source": "/launch-orbit-chain/how-tos/set-up-aep-fee-router", "destination": "/launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/set-up-aep-fee-router", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/usdc-standard-bridge/?)", + "source": "/launch-orbit-chain/how-tos/usdc-standard-bridge", "destination": "/launch-arbitrum-chain/third-party-integrations/bridged-usdc-standard", "permanent": false }, { - "source": "/(launch-orbit-chain/how-tos/use-a-custom-gas-token/?)", + "source": "/launch-orbit-chain/how-tos/use-a-custom-gas-token", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-rollup", "permanent": false }, { - "source": "/(launch-orbit-chain/infra-options-orbit-chains/?)", + "source": "/launch-orbit-chain/infra-options-orbit-chains", "destination": "/launch-arbitrum-chain/third-party-integrations/third-party-providers", "permanent": false }, { - "source": "/(launch-orbit-chain/maintain-your-chain/bridging/?)", + "source": "/launch-orbit-chain/maintain-your-chain/bridging", "destination": "/launch-arbitrum-chain/ecosystem-support/add-arbitrum-chain-to-bridge-ui", "permanent": false }, { - "source": "/(launch-orbit-chain/maintain-your-chain/guidance/post-launch-contract-deployments/?)", + "source": "/launch-orbit-chain/maintain-your-chain/guidance/post-launch-contract-deployments", "destination": "/launch-arbitrum-chain/maintain-your-chain/guidance/post-launch-contract-deployments", "permanent": false }, { - "source": "/(launch-orbit-chain/maintain-your-chain/guidance/state-growth/?)", + "source": "/launch-orbit-chain/maintain-your-chain/guidance/state-growth", "destination": "/launch-arbitrum-chain/maintain-your-chain/guidance/state-growth", "permanent": false }, { - "source": "/(launch-orbit-chain/maintain-your-chain/guidance/state-size-limit/?)", + "source": "/launch-orbit-chain/maintain-your-chain/guidance/state-size-limit", "destination": "/launch-arbitrum-chain/maintain-your-chain/guidance/state-size-limit", "permanent": false }, { - "source": "/(launch-orbit-chain/maintain-your-chain/monitoring/?)", + "source": "/launch-orbit-chain/maintain-your-chain/monitoring", "destination": "/launch-arbitrum-chain/maintain-your-chain/monitoring-tools-and-considerations", "permanent": false }, { - "source": "/(launch-orbit-chain/maintain-your-chain/ownership-structure-access-control/?)", + "source": "/launch-orbit-chain/maintain-your-chain/ownership-structure-access-control", "destination": "/launch-arbitrum-chain/maintain-your-chain/ownership-structure-access-control", "permanent": false }, { - "source": "/(launch-orbit-chain/maintain-your-chain/upgrade-to-bold/?)", + "source": "/launch-orbit-chain/maintain-your-chain/upgrade-to-bold", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/bold-adoption-for-arbitrum-chains", "permanent": false }, { - "source": "/(launch-orbit-chain/orbit-gentle-introduction/?)", + "source": "/launch-orbit-chain/orbit-gentle-introduction", "destination": "/launch-arbitrum-chain/a-gentle-introduction", "permanent": false }, { - "source": "/(launch-orbit-chain/orbit-license/?)", + "source": "/launch-orbit-chain/orbit-license", "destination": "/launch-arbitrum-chain/aep-license", "permanent": false }, { - "source": "/(launch-orbit-chain/orbit-node-runners/orbit-node-providers/?)", + "source": "/launch-orbit-chain/orbit-node-runners/orbit-node-providers", "destination": "/for-devs/dev-tools-and-resources/chain-info", "permanent": false }, { - "source": "/(launch-orbit-chain/orbit-quickstart/?)", + "source": "/launch-orbit-chain/orbit-quickstart", "destination": "/launch-arbitrum-chain/a-gentle-introduction", "permanent": false }, { - "source": "/(launch-orbit-chain/orbit-sdk-introduction/?)", + "source": "/launch-orbit-chain/orbit-sdk-introduction", "destination": "/launch-arbitrum-chain/arbitrum-chain-sdk-introduction", "permanent": false }, { - "source": "/(launch-orbit-chain/orbit-supported-parent-chains/?)", + "source": "/launch-orbit-chain/orbit-supported-parent-chains", "destination": "/launch-arbitrum-chain/a-gentle-introduction", "permanent": false }, { - "source": "/(launch-orbit-chain/reference/additional-configuration-parameters/?)", + "source": "/launch-orbit-chain/reference/additional-configuration-parameters", "destination": "/launch-arbitrum-chain/reference/additional-configuration-parameters", "permanent": false }, @@ -1230,259 +1197,259 @@ "permanent": false }, { - "source": "/(launch-orbit-chain/reference/how-tos/orbit-managing-state-growth/?)", + "source": "/launch-orbit-chain/reference/how-tos/orbit-managing-state-growth", "destination": "/launch-arbitrum-chain/maintain-your-chain/guidance/state-growth", "permanent": false }, { - "source": "/(launch-orbit-chain/reference/monitoring-tools-and-considerations/?)", + "source": "/launch-orbit-chain/reference/monitoring-tools-and-considerations", "destination": "/launch-arbitrum-chain/maintain-your-chain/monitoring-tools-and-considerations", "permanent": false }, { - "source": "/(launch-orbit-chain/reference/orbit-batch-poster-configuration/?)", + "source": "/launch-orbit-chain/reference/orbit-batch-poster-configuration", "destination": "/launch-arbitrum-chain/configure-your-chain/common-configurations/batch-posting-assertion-control", "permanent": false }, { - "source": "/(launch-orbit-chain/reference/orbit-configuration-parameters/?)", + "source": "/launch-orbit-chain/reference/orbit-configuration-parameters", "destination": "/launch-arbitrum-chain/reference/additional-configuration-parameters", "permanent": false }, { - "source": "/(launch-orbit-chain/start-your-journey/?)", + "source": "/launch-orbit-chain/start-your-journey", "destination": "/launch-arbitrum-chain/a-gentle-introduction", "permanent": false }, { - "source": "/(launch-orbit-chain/third-party-integrations/bridged-usdc-standard/?)", + "source": "/launch-orbit-chain/third-party-integrations/bridged-usdc-standard", "destination": "/launch-arbitrum-chain/third-party-integrations/bridged-usdc-standard", "permanent": false }, { - "source": "/(launch-orbit-chain/third-party-integrations/integrations/?)", + "source": "/launch-orbit-chain/third-party-integrations/integrations", "destination": "/launch-arbitrum-chain/third-party-integrations/third-party-providers", "permanent": false }, { - "source": "/(launch-orbit-chain/third-party-integrations/third-party-providers/?)", + "source": "/launch-orbit-chain/third-party-integrations/third-party-providers", "destination": "/launch-arbitrum-chain/third-party-integrations/third-party-providers", "permanent": false }, { - "source": "/(launch-orbit-chain/troubleshooting-building-orbit/?)", + "source": "/launch-orbit-chain/troubleshooting-building-orbit", "destination": "/launch-arbitrum-chain/faq-troubleshooting/troubleshooting-building-arbitrum-chain", "permanent": false }, - { "source": "/(learn-more/?)", "destination": "/learn-more/faq", "permanent": false }, + { "source": "/learn-more", "destination": "/learn-more/faq", "permanent": false }, { - "source": "/(learn-more/contribute/?)", + "source": "/learn-more/contribute", "destination": "/for-devs/contribute", "permanent": false }, - { "source": "/(learnmore/faq/?)", "destination": "/learn-more/faq", "permanent": false }, + { "source": "/learnmore/faq", "destination": "/learn-more/faq", "permanent": false }, { - "source": "/(mainnet-beta/?)", + "source": "/mainnet", "destination": "/build-decentralized-apps/reference/mainnet-risks", "permanent": false }, { - "source": "/(mainnet-risks/?)", + "source": "/mainnet-beta", "destination": "/build-decentralized-apps/reference/mainnet-risks", "permanent": false }, { - "source": "/(mainnet/?)", + "source": "/mainnet-risks", "destination": "/build-decentralized-apps/reference/mainnet-risks", "permanent": false }, { - "source": "/(migration/dapp-migration/?)", + "source": "/migration/dapp-migration", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview", "permanent": false }, { - "source": "/(migration/state-migration/?)", + "source": "/migration/state-migration", "destination": "/run-arbitrum-node/nitro/migrate-state-and-history-from-classic", "permanent": false }, { - "source": "/(node-running/build-nitro-locally/?)", + "source": "/node-running/build-nitro-locally", "destination": "/run-arbitrum-node/nitro/build-nitro-locally", "permanent": false }, { - "source": "/(node-running/command-line-options/?)", + "source": "/node-running/command-line-options", "destination": "/run-arbitrum-node/run-full-node", "permanent": false }, { - "source": "/(node-running/gentle-introduction-run-node/?)", + "source": "/node-running/gentle-introduction-run-node", "destination": "/run-arbitrum-node/overview", "permanent": false }, { - "source": "/(node-running/how-tos/build-nitro-locally/?)", + "source": "/node-running/how-tos/build-nitro-locally", "destination": "/run-arbitrum-node/nitro/build-nitro-locally", "permanent": false }, { - "source": "/(node-running/how-tos/data-availability-committee/configure-the-dac-in-your-chain/?)", + "source": "/node-running/how-tos/data-availability-committee/configure-the-dac-in-your-chain", "destination": "/run-arbitrum-node/data-availability-committees/configure-dac", "permanent": false }, { - "source": "/(node-running/how-tos/data-availability-committee/deploy-a-das/?)", + "source": "/node-running/how-tos/data-availability-committee/deploy-a-das", "destination": "/run-arbitrum-node/data-availability-committees/deploy-das", "permanent": false }, { - "source": "/(node-running/how-tos/data-availability-committee/deploy-a-mirror-das/?)", + "source": "/node-running/how-tos/data-availability-committee/deploy-a-mirror-das", "destination": "/run-arbitrum-node/data-availability-committees/deploy-mirror-das", "permanent": false }, { - "source": "/(node-running/how-tos/data-availability-committee/introduction/?)", + "source": "/node-running/how-tos/data-availability-committee/introduction", "destination": "/run-arbitrum-node/data-availability-committees/get-started", "permanent": false }, { - "source": "/(node-running/how-tos/local-dev-node/?)", + "source": "/node-running/how-tos/local-dev-node", "destination": "/run-arbitrum-node/run-local-dev-node", "permanent": false }, { - "source": "/(node-running/how-tos/migrate-state-and-history-from-classic/?)", + "source": "/node-running/how-tos/migrate-state-and-history-from-classic", "destination": "/run-arbitrum-node/nitro/migrate-state-and-history-from-classic", "permanent": false }, { - "source": "/(node-running/how-tos/read-sequencer-feed/?)", + "source": "/node-running/how-tos/read-sequencer-feed", "destination": "/run-arbitrum-node/sequencer/read-sequencer-feed", "permanent": false }, { - "source": "/(node-running/how-tos/running-a-classic-node/?)", + "source": "/node-running/how-tos/running-a-classic-node", "destination": "/run-arbitrum-node/more-types/run-classic-node", "permanent": false }, { - "source": "/(node-running/how-tos/running-a-daserver/?)", + "source": "/node-running/how-tos/running-a-daserver", "destination": "/run-arbitrum-node/data-availability-committees/get-started", "permanent": false }, { - "source": "/(node-running/how-tos/running-a-feed-relay/?)", + "source": "/node-running/how-tos/running-a-feed-relay", "destination": "/run-arbitrum-node/run-feed-relay", "permanent": false }, { - "source": "/(node-running/how-tos/running-a-full-node/?)", + "source": "/node-running/how-tos/running-a-full-node", "destination": "/run-arbitrum-node/run-full-node", "permanent": false }, { - "source": "/(node-running/how-tos/running-a-node/?)", + "source": "/node-running/how-tos/running-a-node", "destination": "/run-arbitrum-node/run-full-node", "permanent": false }, { - "source": "/(node-running/how-tos/running-a-sequencer-coordinator-manager/?)", + "source": "/node-running/how-tos/running-a-sequencer-coordinator-manager", "destination": "/run-arbitrum-node/sequencer/run-sequencer-coordination-manager", "permanent": false }, { - "source": "/(node-running/how-tos/running-a-validator/?)", + "source": "/node-running/how-tos/running-a-validator", "destination": "/run-arbitrum-node/more-types/run-validator-node", "permanent": false }, { - "source": "/(node-running/how-tos/running-an-archive-node/?)", + "source": "/node-running/how-tos/running-an-archive-node", "destination": "/run-arbitrum-node/more-types/run-archive-node", "permanent": false }, { - "source": "/(node-running/how-tos/running-an-orbit-node/?)", + "source": "/node-running/how-tos/running-an-orbit-node", "destination": "/run-arbitrum-node/run-full-node", "permanent": false }, { - "source": "/(node-running/local-dev-node/?)", + "source": "/node-running/local-dev-node", "destination": "/run-arbitrum-node/run-local-dev-node", "permanent": false }, { - "source": "/(node-running/node-providers/?)", + "source": "/node-running/node-providers", "destination": "/build-decentralized-apps/reference/node-providers", "permanent": false }, { - "source": "/(node-running/quickstart-running-a-node/?)", + "source": "/node-running/quickstart-running-a-node", "destination": "/run-arbitrum-node/overview", "permanent": false }, { - "source": "/(node-running/read-sequencer-feed/?)", + "source": "/node-running/read-sequencer-feed", "destination": "/run-arbitrum-node/sequencer/read-sequencer-feed", "permanent": false }, { - "source": "/(node-running/reference/arbos-software-releases/arbos11/?)", + "source": "/node-running/reference/arbos-software-releases/arbos11", "destination": "/run-arbitrum-node/arbos-releases/arbos11", "permanent": false }, { - "source": "/(node-running/reference/arbos-software-releases/arbos20/?)", + "source": "/node-running/reference/arbos-software-releases/arbos20", "destination": "/run-arbitrum-node/arbos-releases/arbos20", "permanent": false }, { - "source": "/(node-running/reference/arbos-software-releases/overview/?)", + "source": "/node-running/reference/arbos-software-releases/overview", "destination": "/run-arbitrum-node/arbos-releases/overview", "permanent": false }, { - "source": "/(node-running/reference/ethereum-beacon-rpc-providers/?)", + "source": "/node-running/reference/ethereum-beacon-rpc-providers", "destination": "/run-arbitrum-node/l1-ethereum-beacon-chain-rpc-providers", "permanent": false }, { - "source": "/(node-running/reference/software-releases/?)", + "source": "/node-running/reference/software-releases", "destination": "/run-arbitrum-node/arbos-releases/overview", "permanent": false }, { - "source": "/(node-running/running-a-classic-node/?)", + "source": "/node-running/running-a-classic-node", "destination": "/run-arbitrum-node/more-types/run-classic-node", "permanent": false }, { - "source": "/(node-running/running-a-feed-relay/?)", + "source": "/node-running/running-a-feed-relay", "destination": "/run-arbitrum-node/run-feed-relay", "permanent": false }, { - "source": "/(node-running/running-a-node/?)", + "source": "/node-running/running-a-node", "destination": "/run-arbitrum-node/run-full-node", "permanent": false }, { - "source": "/(node-running/running-a-validator/?)", + "source": "/node-running/running-a-validator", "destination": "/run-arbitrum-node/more-types/run-validator-node", "permanent": false }, { - "source": "/(node-running/running-an-archive-node/?)", + "source": "/node-running/running-an-archive-node", "destination": "/run-arbitrum-node/more-types/run-archive-node", "permanent": false }, { - "source": "/(node-running/troubleshooting-running-nodes/?)", + "source": "/node-running/troubleshooting-running-nodes", "destination": "/run-arbitrum-node/troubleshooting", "permanent": false }, { - "source": "/(node_providers/?)", + "source": "/node_providers", "destination": "/build-decentralized-apps/reference/node-providers", "permanent": false }, @@ -1492,7 +1459,7 @@ "permanent": false }, { - "source": "/(notices/arbos-fusaka/?)", + "source": "/notices/arbos-fusaka", "destination": "/notices/fusaka-upgrade-notice", "permanent": false }, @@ -1507,613 +1474,587 @@ "permanent": false }, { - "source": "/(proving/challenge-manager/?)", + "source": "/proving/challenge-manager", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(proving/osp-assumptions/?)", + "source": "/proving/osp-assumptions", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(proving/wasm-to-wavm/?)", + "source": "/proving/wasm-to-wavm", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(proving/wavm-custom-opcodes/?)", + "source": "/proving/wavm-custom-opcodes", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(proving/wavm-floats/?)", + "source": "/proving/wavm-floats", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(proving/wavm-modules/?)", + "source": "/proving/wavm-modules", "destination": "/how-arbitrum-works/bold/gentle-introduction", "permanent": false }, { - "source": "/(public-chains/?)", + "source": "/public-chains", "destination": "/for-devs/concepts/public-chains", "permanent": false }, { - "source": "/(public_chains/?)", + "source": "/public_chains", "destination": "/for-devs/concepts/public-chains", "permanent": false }, { - "source": "/(public_nitro_devnet/?)", + "source": "/public_nitro_devnet", "destination": "/for-devs/concepts/public-chains", "permanent": false }, { - "source": "/(public_nitro_testnet/?)", + "source": "/public_nitro_testnet", "destination": "/for-devs/concepts/public-chains", "permanent": false }, { - "source": "/(public_testnet/?)", + "source": "/public_testnet", "destination": "/for-devs/concepts/public-chains", "permanent": false }, { - "source": "/(quickstart/?)", + "source": "/quickstart", "destination": "/build-decentralized-apps/quickstart-solidity-remix", "permanent": false }, - { "source": "/(rollup_basics/?)", "destination": "/intro", "permanent": false }, + { "source": "/rollup_basics", "destination": "/intro", "permanent": false }, { - "source": "/(rollup_protocol/?)", + "source": "/rollup_protocol", "destination": "/how-arbitrum-works/inside-arbitrum-nitro", "permanent": false }, { - "source": "/(run-arbitrum-node/arbos-releases/arbos30/?)", + "source": "/run-arbitrum-node/arbos-releases/arbos30", "destination": "/run-arbitrum-node/arbos-releases/arbos32", "permanent": false }, { - "source": "/(run-arbitrum-node/arbos-releases/arbos31/?)", + "source": "/run-arbitrum-node/arbos-releases/arbos31", "destination": "/run-arbitrum-node/arbos-releases/arbos32", "permanent": false }, { - "source": "/(run-arbitrum-node/arbos-releases/arbos50/?)", + "source": "/run-arbitrum-node/arbos-releases/arbos50", "destination": "/run-arbitrum-node/arbos-releases/arbos51/", "permanent": false }, { - "source": "/(run-arbitrum-node/how-to-use-timeboost/?)", + "source": "/run-arbitrum-node/how-to-use-timeboost", "destination": "/how-arbitrum-works/timeboost/how-to-use-timeboost", "permanent": false }, { - "source": "/(run-arbitrum-node/more-types/split-validator-node/?)", + "source": "/run-arbitrum-node/more-types/split-validator-node", "destination": "/run-arbitrum-node/more-types/run-split-validator-node", "permanent": false }, { - "source": "/(run-arbitrum-node/node-types/?)", + "source": "/run-arbitrum-node/node-types", "destination": "/run-arbitrum-node/overview", "permanent": false }, { - "source": "/(run-arbitrum-node/quickstart/?)", + "source": "/run-arbitrum-node/quickstart", "destination": "/run-arbitrum-node/overview", "permanent": false }, { - "source": "/(run-arbitrum-node/run-local-dev-node/?)", + "source": "/run-arbitrum-node/run-local-dev-node", "destination": "/run-arbitrum-node/run-local-full-chain-simulation", "permanent": false }, { - "source": "/(run-arbitrum-node/sequencer/run-feed-relay/?)", + "source": "/run-arbitrum-node/sequencer/run-feed-relay", "destination": "/run-arbitrum-node/run-feed-relay", "permanent": false }, { - "source": "/(running_goerli_nitro_node/?)", + "source": "/running_goerli_nitro_node", "destination": "/run-arbitrum-node/run-full-node", "permanent": false }, { - "source": "/(running_nitro_node/?)", + "source": "/running_nitro_node", "destination": "/run-arbitrum-node/run-full-node", "permanent": false }, { - "source": "/(running_node/?)", + "source": "/running_node", "destination": "/run-arbitrum-node/run-full-node", "permanent": false }, { - "source": "/(running_rinkeby_nitro_node/?)", + "source": "/running_rinkeby_nitro_node", "destination": "/run-arbitrum-node/run-full-node", "permanent": false }, { - "source": "/(sdk-docs/assetBridger/?)", + "source": "/sdk-docs/assetBridger", "destination": "/sdk/assetbridger/assetbridger", "permanent": false }, { - "source": "/(sdk-docs/assetBridger/erc20Bridger/?)", + "source": "/sdk-docs/assetBridger/erc20Bridger", "destination": "/sdk/assetbridger/erc20bridger", "permanent": false }, { - "source": "/(sdk-docs/assetBridger/ethBridger/?)", + "source": "/sdk-docs/assetBridger/ethBridger", "destination": "/sdk/assetBridger/ethBridger", "permanent": false }, { - "source": "/(sdk-docs/dataEntities/address/?)", + "source": "/sdk-docs/dataEntities/address", "destination": "/sdk/dataEntities/address", "permanent": false }, { - "source": "/(sdk-docs/dataEntities/constants/?)", + "source": "/sdk-docs/dataEntities/constants", "destination": "/sdk/dataEntities/constants", "permanent": false }, { - "source": "/(sdk-docs/dataEntities/event/?)", + "source": "/sdk-docs/dataEntities/event", "destination": "/sdk/dataEntities/event", "permanent": false }, { - "source": "/(sdk-docs/dataEntities/message/?)", + "source": "/sdk-docs/dataEntities/message", "destination": "/sdk/dataEntities/message", "permanent": false }, { - "source": "/(sdk-docs/dataEntities/networks?)", + "source": "/sdk-docs/dataEntities/networks", "destination": "/sdk/dataEntities/networks", "permanent": false }, { - "source": "/(sdk-docs/dataEntities/retryableData/?)", + "source": "/sdk-docs/dataEntities/retryableData", "destination": "/sdk/dataEntities/retryableData", "permanent": false }, { - "source": "/(sdk-docs/dataEntities/rpc/?)", + "source": "/sdk-docs/dataEntities/rpc", "destination": "/sdk/dataEntities/rpc", "permanent": false }, { - "source": "/(sdk-docs/dataEntities/signerOrProvider/?)", + "source": "/sdk-docs/dataEntities/signerOrProvider", "destination": "/sdk/dataEntities/signerOrProvider", "permanent": false }, { - "source": "/(sdk-docs/dataEntities/transactionRequest/?)", + "source": "/sdk-docs/dataEntities/transactionRequest", "destination": "/sdk/dataEntities/transactionRequest", "permanent": false }, { - "source": "/(sdk-docs/dataEntities_constants/?)", + "source": "/sdk-docs/dataEntities_constants", "destination": "/sdk/dataEntities/constants", "permanent": false }, { - "source": "/(sdk-docs/message/L1ToL2Message/?)", + "source": "/sdk-docs/message/L1ToL2Message", "destination": "/sdk/message/ParentToChildMessage", "permanent": false }, { - "source": "/(sdk-docs/message/L1ToL2MessageCreator/?)", + "source": "/sdk-docs/message/L1ToL2MessageCreator", "destination": "/sdk/message/ParentToChildMessageCreator", "permanent": false }, { - "source": "/(sdk-docs/message/L1Transaction/?)", + "source": "/sdk-docs/message/L1Transaction", "destination": "/sdk/message/ParentTransaction", "permanent": false }, { - "source": "/(sdk-docs/message/L2ToL1Message/?)", + "source": "/sdk-docs/message/L2ToL1Message", "destination": "/sdk/message/ChildToParentMessage", "permanent": false }, { - "source": "/(sdk-docs/message/L2ToL1MessageClassic/?)", + "source": "/sdk-docs/message/L2ToL1MessageClassic", "destination": "/sdk/message/ChildToParentMessageClassic", "permanent": false }, { - "source": "/(sdk-docs/message/L2ToL1MessageNitro/?)", + "source": "/sdk-docs/message/L2ToL1MessageNitro", "destination": "/sdk/message/ChildToParentMessageNitro", "permanent": false }, { - "source": "/(sdk-docs/message/L2Transaction/?)", + "source": "/sdk-docs/message/L2Transaction", "destination": "/sdk/message/ChildTransaction", "permanent": false }, { - "source": "/(sdk-docs/utils/arbProvider/?)", + "source": "/sdk-docs/utils/arbProvider", "destination": "/sdk/utils/arbProvider", "permanent": false }, { - "source": "/(sdk-docs/utils/byte_serialize_params/?)", + "source": "/sdk-docs/utils/byte_serialize_params", "destination": "/sdk/utils/byte_serialize_params", "permanent": false }, { - "source": "/(sdk-docs/utils/eventFetcher/?)", + "source": "/sdk-docs/utils/eventFetcher", "destination": "/sdk/utils/eventFetcher", "permanent": false }, - { "source": "/(sdk-docs/utils/lib/?)", "destination": "/sdk/utils/lib", "permanent": false }, - { - "source": "/(sdk-docs/utils/types/?)", - "destination": "/sdk/utils/types", - "permanent": false - }, + { "source": "/sdk-docs/utils/lib", "destination": "/sdk/utils/lib", "permanent": false }, + { "source": "/sdk-docs/utils/types", "destination": "/sdk/utils/types", "permanent": false }, { - "source": "/(sdk/assetBridger_erc20Bridger/?)", + "source": "/sdk/assetBridger_erc20Bridger", "destination": "/sdk/assetBridger/erc20Bridger", "permanent": false }, { - "source": "/(sdk/assetBridger_ethBridger/?)", + "source": "/sdk/assetBridger_ethBridger", "destination": "/sdk/assetBridger/ethBridger", "permanent": false }, { - "source": "/(sdk/dataEntities_address/?)", + "source": "/sdk/dataEntities_address", "destination": "/sdk/dataEntities/address", "permanent": false }, { - "source": "/(sdk/dataEntities_constants/?)", + "source": "/sdk/dataEntities_constants", "destination": "/sdk/dataEntities/constants", "permanent": false }, { - "source": "/(sdk/dataEntities_errors/?)", + "source": "/sdk/dataEntities_errors", "destination": "/sdk/dataEntities/errors", "permanent": false }, { - "source": "/(sdk/dataEntities_event/?)", + "source": "/sdk/dataEntities_event", "destination": "/sdk/dataEntities/event", "permanent": false }, { - "source": "/(sdk/dataEntities_networks/?)", + "source": "/sdk/dataEntities_networks", "destination": "/sdk/dataEntities/networks", "permanent": false }, { - "source": "/(sdk/dataEntities_retryableData/?)", + "source": "/sdk/dataEntities_retryableData", "destination": "/sdk/dataEntities/retryableData", "permanent": false }, { - "source": "/(sdk/dataEntities_rpc/?)", + "source": "/sdk/dataEntities_rpc", "destination": "/sdk/dataEntities/rpc", "permanent": false }, { - "source": "/(sdk/dataEntities_signerOrProvider/?)", + "source": "/sdk/dataEntities_signerOrProvider", "destination": "/sdk/dataEntities/signerOrProvider", "permanent": false }, { - "source": "/(sdk/dataEntities_transactionRequest/?)", + "source": "/sdk/dataEntities_transactionRequest", "destination": "/sdk/dataEntities/transactionRequest", "permanent": false }, - { "source": "/(sdk/inbox_inbox/?)", "destination": "/sdk/inbox/inbox", "permanent": false }, - { "source": "/(sdk/introduction?)", "destination": "/sdk/", "permanent": false }, + { "source": "/sdk/inbox_inbox", "destination": "/sdk/inbox/inbox", "permanent": false }, + { "source": "/sdk/introduction", "destination": "/sdk/", "permanent": false }, { - "source": "/(sdk/message_L1ToL2Message/?)", + "source": "/sdk/message_L1ToL2Message", "destination": "/sdk/message/ParentToChildMessage", "permanent": false }, { - "source": "/(sdk/message_L1ToL2MessageCreator/?)", + "source": "/sdk/message_L1ToL2MessageCreator", "destination": "/sdk/message/ParentToChildMessageCreator", "permanent": false }, { - "source": "/(sdk/message_L1ToL2MessageGasEstimator/?)", + "source": "/sdk/message_L1ToL2MessageGasEstimator", "destination": "/sdk/message/ParentToChildGasEstimator", "permanent": false }, { - "source": "/(sdk/message_L1Transaction/?)", + "source": "/sdk/message_L1Transaction", "destination": "/sdk/message/ParentTransaction", "permanent": false }, { - "source": "/(sdk/message_L2ToL1Message/?)", + "source": "/sdk/message_L2ToL1Message", "destination": "/sdk/message/ChildToParentMessage", "permanent": false }, { - "source": "/(sdk/message_L2ToL1MessageClassic/?)", + "source": "/sdk/message_L2ToL1MessageClassic", "destination": "/sdk/message/ChildToParentMessageClassic", "permanent": false }, { - "source": "/(sdk/message_L2ToL1MessageNitro/?)", + "source": "/sdk/message_L2ToL1MessageNitro", "destination": "/sdk/message/ChildToParentMessageNitro", "permanent": false }, { - "source": "/(sdk/message_L2Transaction/?)", + "source": "/sdk/message_L2Transaction", "destination": "/sdk/message/ChildTransaction", "permanent": false }, + { "source": "/sdk/utils/multicall", "destination": "/sdk/utils/multicall", "permanent": false }, { - "source": "/(sdk/message_L2Transaction/?)", - "destination": "/sdk/message/ChildTransaction", - "permanent": false - }, - { - "source": "/(sdk/utils/multicall/?)", - "destination": "/sdk/utils/multicall", - "permanent": false - }, - { - "source": "/(sdk/utils_arbProvider/?)", + "source": "/sdk/utils_arbProvider", "destination": "/sdk/utils/arbProvider", "permanent": false }, { - "source": "/(sdk/utils_eventFetcher/?)", + "source": "/sdk/utils_eventFetcher", "destination": "/sdk/utils/eventFetcher", "permanent": false }, - { "source": "/(sdk/utils_lib/?)", "destination": "/sdk/utils/lib", "permanent": false }, + { "source": "/sdk/utils_lib", "destination": "/sdk/utils/lib", "permanent": false }, + { "source": "/sdk/utils_multicall", "destination": "/sdk/utils/multicall", "permanent": false }, { - "source": "/(sdk/utils_multicall?)", - "destination": "/sdk/utils/multicall", - "permanent": false - }, - { - "source": "/(security_considerations/?)", + "source": "/security_considerations", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview", "permanent": false }, { - "source": "/(sequencer/?)", + "source": "/sequencer", "destination": "/how-arbitrum-works/deep-dives/sequencer", "permanent": false }, { - "source": "/(solidity-support/?)", + "source": "/solidity-support", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/solidity-support", "permanent": false }, { - "source": "/(solidity_support/?)", + "source": "/solidity_support", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/solidity-support", "permanent": false }, { - "source": "/(special_features/?)", + "source": "/special_features", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview", "permanent": false }, + { "source": "/stylus", "destination": "/stylus/gentle-introduction", "permanent": false }, { - "source": "/(stylus-by-example/abi_decode/?)", + "source": "/stylus-by-example/abi_decode", "destination": "https://github.com/OffchainLabs/stylus-by-example/tree/master/example_code/basic_examples/abi_decode", "permanent": false }, { - "source": "/(stylus-by-example/abi_encode/?)", + "source": "/stylus-by-example/abi_encode", "destination": "https://github.com/OffchainLabs/stylus-by-example/tree/master/example_code/basic_examples/abi_encode", "permanent": false }, { - "source": "/(stylus-by-example/bytes_in_bytes_out/?)", + "source": "/stylus-by-example/bytes_in_bytes_out", "destination": "https://github.com/OffchainLabs/stylus-by-example/tree/master/example_code/applications", "permanent": false }, { - "source": "/(stylus-by-example/errors/?)", + "source": "/stylus-by-example/errors", "destination": "https://github.com/OffchainLabs/stylus-by-example/tree/master/example_code/basic_examples/errors", "permanent": false }, { - "source": "/(stylus-by-example/events/?)", + "source": "/stylus-by-example/events", "destination": "https://github.com/OffchainLabs/stylus-by-example/tree/master/example_code/basic_examples/events", "permanent": false }, { - "source": "/(stylus-by-example/function/?)", + "source": "/stylus-by-example/function", "destination": "https://github.com/OffchainLabs/stylus-by-example/tree/master/example_code/basic_examples/function", "permanent": false }, { - "source": "/(stylus-by-example/function_selector/?)", + "source": "/stylus-by-example/function_selector", "destination": "https://github.com/OffchainLabs/stylus-by-example/tree/master/example_code/applications", "permanent": false }, { - "source": "/(stylus-by-example/hashing/?)", + "source": "/stylus-by-example/hashing", "destination": "https://github.com/OffchainLabs/stylus-by-example/tree/master/example_code/applications", "permanent": false }, { - "source": "/(stylus-by-example/hello_world/?)", + "source": "/stylus-by-example/hello_world", "destination": "https://github.com/OffchainLabs/stylus-by-example/tree/master/example_code/basic_examples/hello_world", "permanent": false }, { - "source": "/(stylus-by-example/inheritance/?)", + "source": "/stylus-by-example/inheritance", "destination": "https://github.com/OffchainLabs/stylus-by-example/tree/master/example_code/applications", "permanent": false }, { - "source": "/(stylus-by-example/primitive_data_types/?)", + "source": "/stylus-by-example/primitive_data_types", "destination": "https://github.com/OffchainLabs/stylus-by-example/tree/master/example_code/basic_examples/primitive_data_types", "permanent": false }, { - "source": "/(stylus-by-example/sending_ether/?)", + "source": "/stylus-by-example/sending_ether", "destination": "https://github.com/OffchainLabs/stylus-by-example/tree/master/example_code/basic_examples/sending_ether", "permanent": false }, { - "source": "/(stylus-by-example/variables/?)", + "source": "/stylus-by-example/variables", "destination": "https://github.com/OffchainLabs/stylus-by-example/tree/master/example_code/basic_examples/variables", "permanent": false }, - { "source": "/(stylus/?)", "destination": "/stylus/gentle-introduction", "permanent": false }, { - "source": "/(stylus/concepts/stylus-cache-manager/?)", + "source": "/stylus/concepts/stylus-cache-manager", "destination": "/stylus/how-tos/caching-contracts", "permanent": false }, { - "source": "/(stylus/concepts/stylus-gas/?)", + "source": "/stylus/concepts/stylus-gas", "destination": "/stylus/concepts/gas-metering", "permanent": false }, { - "source": "/(stylus/how-tos/cache-contracts/?)", + "source": "/stylus/how-tos/cache-contracts", "destination": "/stylus/how-tos/caching-contracts", "permanent": false }, { - "source": "/(stylus/how-tos/debug-stylus-transactions/?)", + "source": "/stylus/how-tos/debug-stylus-transactions", "destination": "/stylus/how-tos/debugging-tx", "permanent": false }, { - "source": "/(stylus/how-tos/debugging-stylus-tx/?)", + "source": "/stylus/how-tos/debugging-stylus-tx", "destination": "/stylus/how-tos/debugging-tx", "permanent": false }, { - "source": "/(stylus/how-tos/local-stylus-dev-node/?)", - "destination": "/run-arbitrum-node/run-local-full-chain-simulation", - "permanent": false - }, - { - "source": "/(stylus/how-tos/local-stylus-dev-node/?)", + "source": "/stylus/how-tos/local-stylus-dev-node", "destination": "/run-arbitrum-node/run-nitro-dev-node", "permanent": false }, { - "source": "/(stylus/how-tos/using-stylus-cli/?)", + "source": "/stylus/how-tos/using-stylus-cli", "destination": "/stylus/cli-tools-overview", "permanent": false }, { - "source": "/(stylus/how-tos/verify-contracts/?)", + "source": "/stylus/how-tos/verify-contracts", "destination": "/stylus/how-tos/verifying-contracts", "permanent": false }, { - "source": "/(stylus/reference/cargo-stylus/?)", + "source": "/stylus/reference/cargo-stylus", "destination": "/stylus/gentle-introduction", "permanent": false }, { - "source": "/(stylus/reference/testnet-information/?)", + "source": "/stylus/reference/testnet-information", "destination": "/stylus/overview/", "permanent": false }, { - "source": "/(stylus/rust-sdk-guide/?)", + "source": "/stylus/rust-sdk-guide", "destination": "/stylus/reference/rust-sdk-guide", "permanent": false }, { - "source": "/(stylus/stylus-gentle-introduction/?)", + "source": "/stylus/stylus-gentle-introduction", "destination": "/stylus/gentle-introduction", "permanent": false }, { - "source": "/(stylus/stylus-quickstart/?)", + "source": "/stylus/stylus-quickstart", "destination": "/stylus/quickstart", "permanent": false }, { - "source": "/(stylus/tools/stylus-cli?)", - "destination": "stylus/using-cli", + "source": "/stylus/tools/stylus-cli", + "destination": "/stylus/using-cli", "permanent": false }, { - "source": "/(stylus/tools/using-stylus-cli/?)", + "source": "/stylus/tools/using-stylus-cli", "destination": "/stylus/using-cli", "permanent": false }, { - "source": "/(time/?)", + "source": "/time", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time", "permanent": false }, { - "source": "/(time_in_arbitrum/?)", + "source": "/time_in_arbitrum", "destination": "/build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time", "permanent": false }, { - "source": "/(tutorials/?)", + "source": "/tutorials", "destination": "/for-devs/quickstart-solidity-remix", "permanent": false }, { - "source": "/(tx-lifecycle/?)", + "source": "/tx-lifecycle", "destination": "/how-arbitrum-works/deep-dives/transaction-lifecycle", "permanent": false }, { - "source": "/(tx_lifecycle/?)", + "source": "/tx_lifecycle", "destination": "/how-arbitrum-works/deep-dives/transaction-lifecycle", "permanent": false }, { - "source": "/(txlifecycle/?)", + "source": "/txlifecycle", "destination": "/how-arbitrum-works/deep-dives/transaction-lifecycle", "permanent": false }, { - "source": "/(useful-addresses/?)", + "source": "/useful-addresses", "destination": "/build-decentralized-apps/reference/useful-addresses", "permanent": false }, { - "source": "/(useful_addresses/?)", + "source": "/useful_addresses", "destination": "/build-decentralized-apps/reference/contract-addresses", "permanent": false }, { - "source": "/(welcome/?)", + "source": "/welcome", "destination": "/get-started/arbitrum-introduction", "permanent": false }, { - "source": "/(welcome/arbitrum-gentle-introduction/?)", + "source": "/welcome/arbitrum-gentle-introduction", "destination": "/get-started/arbitrum-introduction", "permanent": false }, + { "source": "/welcome/get-started", "destination": "/get-started/overview", "permanent": true }, { - "source": "/(welcome/get-started/?)", - "destination": "/get-started/overview", - "permanent": true - }, - { - "source": "/(why-nitro/?)", + "source": "/why-nitro", "destination": "/how-arbitrum-works/inside-arbitrum-nitro", "permanent": false }, { - "source": "/(withdrawals/?)", + "source": "/withdrawals", "destination": "/how-arbitrum-works/deep-dives/transaction-lifecycle", "permanent": false }