diff --git a/.env b/.env new file mode 100644 index 000000000..e11872f8c --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +VITE_FEEDBACK_URL=https://164.92.190.45/feedback/form +# Leave empty to use the bundled src/public/compat-matrix.json snapshot. +VITE_COMPAT_MATRIX_URL= diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..5ed684d11 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +.vitepress/dist +.vitepress/cache +.vercel +src/snippets diff --git a/.eslintrc.js b/.eslintrc.js index 50d0d3aab..c1e88f677 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,15 +1,29 @@ +// eslint-disable-next-line no-undef module.exports = { - extends: ['plugin:vue'], - // env: { - // es2021: true, - // }, - rules: { - 'spaced-comment': [ - 'error', - 'always', - { - markers: ['/'], - }, - ], + root: true, + extends: ['plugin:vue/vue3-recommended', 'eslint:recommended', 'plugin:@typescript-eslint/recommended'], + parser: 'vue-eslint-parser', + parserOptions: { + parser: '@typescript-eslint/parser', + sourceType: 'module', + }, + rules: { + 'vue/html-indent': ['error', 2], + 'spaced-comment': [ + 'error', + 'always', + { + markers: ['/'], + }, + ], + }, + overrides: [ + { + files: ['.vitepress/theme/components/MermaidRender.vue'], + rules: { + // FIXME: find a way to disable this it for the particular line + 'vue/no-v-html': 'off', + }, }, + ], } diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..d93597c6a --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,14 @@ +* @outoftardis + +# doc sources +/src/ @outoftardis @WRRicht3r @nxsaken +*.md @outoftardis @WRRicht3r @nxsaken +src/guide/javascript.md @0x009922 @outoftardis @WRRicht3r @nxsaken + +# configurations +/.vitepress/ @0x009922 +*.js @0x009922 +*.ts @0x009922 +*.json @0x009922 +*.yaml @0x009922 +*.vue @0x009922 diff --git a/.github/workflows/gh-pages-deploy.yaml b/.github/workflows/gh-pages-deploy.yaml new file mode 100644 index 000000000..1d9e7fff1 --- /dev/null +++ b/.github/workflows/gh-pages-deploy.yaml @@ -0,0 +1,68 @@ +name: Deploy at GitHub Pages + +on: + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-node@v3 + with: + node-version: 18 + + # --- Install with caching + # https://github.com/pnpm/action-setup#use-cache-to-reduce-installation-time + + - name: Enable Corepack + run: corepack enable + + - name: Get pnpm store directory + id: pnpm-cache + run: | + echo "::set-output name=pnpm_cache_dir::$(pnpm store path)" + + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: | + ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} + /home/runner/.cache/Cypress + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install packages + run: pnpm install + + - name: Check formatting + run: pnpm format:check + + - name: Run linter + run: pnpm lint + + - name: Run tests + run: pnpm test + + - name: Build VitePress + run: | + pnpm build + pnpm cli validate-links .vitepress/dist --public-path $PUBLIC_PATH + env: + PUBLIC_PATH: /iroha-2-docs/ + # chalk has a color detection bug + FORCE_COLOR: 2 + + - name: Push static content into master:gh-pages + working-directory: .vitepress/dist + run: | + git config --global user.email "${GITHUB_ACTOR}@https://users.noreply.github.com/" + git config --global user.name "${GITHUB_ACTOR}" + git init + git add --all + git commit -m "Auto update pages on $(date +'%Y-%m-%d %H:%M:%S')" + git push -f -q https://git:${{ secrets.github_token }}@github.com/${{ github.repository }} master:gh-pages diff --git a/.github/workflows/pull-request-ci.yaml b/.github/workflows/pull-request-ci.yaml new file mode 100644 index 000000000..4615c4314 --- /dev/null +++ b/.github/workflows/pull-request-ci.yaml @@ -0,0 +1,60 @@ +name: Pull Request CI +on: + pull_request: + branches: [main] +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 22 + + # --- Install with caching + # https://github.com/pnpm/action-setup#use-cache-to-reduce-installation-time + + - name: Enable Corepack + run: | + npm i -g corepack@latest + corepack enable + + - name: Get pnpm store directory + id: pnpm-cache + run: | + echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT + + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: | + ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }} + /home/runner/.cache/Cypress + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install packages + run: pnpm install + + - name: Type check + run: pnpm typecheck + + - name: Lint + run: pnpm lint + + - name: Format + run: pnpm format:check + + - name: Run tests + run: pnpm test + + - name: Build + run: pnpm build + + - name: Validate links + run: pnpm cli validate-links .vitepress/dist + env: + # chalk has a color detection bug + FORCE_COLOR: 2 diff --git a/.gitignore b/.gitignore index f06235c46..daded15de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,16 @@ +.DS_Store + +# Installed dependencies and build output. node_modules dist + +# Local artifacts generated by documentation tools. +/src/flymd.md +/src/flymd.html +/src/*.temp +/src/snippets +.vitepress/cache + +# Local IDE settings. +.idea +.vercel diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..a65759dba --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@iroha2:registry=https://nexus.iroha.tech/repository/npm-group/ diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..00403fa6e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +.vitepress/dist +.vitepress/cache +.vercel +/src/snippets +/src/example_code diff --git a/.prettierrc.js b/.prettierrc.js index e084c13df..2053034c0 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,39 +1,32 @@ +// eslint-disable-next-line no-undef module.exports = { - // max 120 characters per line - printWidth: 120, - // use 24spaces for indentation - tabWidth: 4, - // use spaces instead of indentations - useTabs: false, - // semicolon at the end of the line - semi: false, - // use single quotes - singleQuote: true, - // object's key is quoted only when necessary - quoteProps: 'as-needed', - // use double quotes instead of single quotes in jsx - jsxSingleQuote: false, - // no comma at the end - trailingComma: 'all', - // spaces are required at the beginning and end of the braces - bracketSpacing: true, - // brackets are required for arrow function parameter, even when there is only one parameter - arrowParens: 'always', - // format the entire contents of the file - rangeStart: 0, - rangeEnd: Infinity, - // no need to write the beginning @prettier of the file - requirePragma: false, - // No need to automatically insert @prettier at the beginning of the file - insertPragma: false, - // use default break criteria - proseWrap: 'preserve', - // decide whether to break the html according to the display style - htmlWhitespaceSensitivity: 'css', - // vue files script and style tags indentation - vueIndentScriptAndStyle: false, - // lf for newline - endOfLine: 'lf', - // formats quoted code embedded - embeddedLanguageFormatting: 'auto', + printWidth: 120, + tabWidth: 2, + useTabs: false, + semi: false, + singleQuote: true, + quoteProps: 'as-needed', + jsxSingleQuote: false, + trailingComma: 'all', + bracketSpacing: true, + arrowParens: 'always', + rangeStart: 0, + rangeEnd: Infinity, + requirePragma: false, + insertPragma: false, + proseWrap: 'preserve', + htmlWhitespaceSensitivity: 'css', + vueIndentScriptAndStyle: false, + endOfLine: 'lf', + embeddedLanguageFormatting: 'auto', + + overrides: [ + { + files: ['./src/**/*.md'], + options: { + printWidth: 75, + proseWrap: 'always', + }, + }, + ], } diff --git a/.vitepress/config.mts b/.vitepress/config.mts new file mode 100644 index 000000000..218df1705 --- /dev/null +++ b/.vitepress/config.mts @@ -0,0 +1,550 @@ +/// + +import { DefaultTheme, defineConfig } from 'vitepress' +import footnote from 'markdown-it-footnote' +import { resolve } from 'path' +import ViteSvgLoader from 'vite-svg-loader' +import ViteUnoCSS from 'unocss/vite' +import { mermaid } from './md-mermaid' +import { katex } from '@mdit/plugin-katex' + +function nav(): DefaultTheme.NavItem[] { + return [ + { + text: 'Get Started', + link: '/get-started/', + activeMatch: '/get-started/', + }, + { + text: 'Guide', + link: '/guide/', + activeMatch: '/guide/', + }, + { + text: 'Architecture', + link: '/blockchain/iroha-explained', + activeMatch: '/blockchain/', + }, + { + text: 'Reference', + link: '/reference/', + activeMatch: '^/reference/', + }, + { + text: 'Help', + link: '/help/', + activeMatch: '/help/', + }, + ] +} + +function sidebarStart(): DefaultTheme.SidebarItem[] { + return [ + { + text: 'Get Started', + link: '/get-started/', + items: [ + { + text: 'Install Iroha 3', + link: '/get-started/install-iroha-2', + }, + { + text: 'Launch Iroha 3', + link: '/get-started/launch-iroha-2', + }, + { + text: 'Operate Iroha 3 via CLI', + link: '/get-started/operate-iroha-2-via-cli', + }, + { + text: 'Connect to SORA Nexus Dataspaces', + link: '/get-started/sora-nexus-dataspaces', + }, + { + text: 'Sponsor Private Dataspace Fees', + link: '/get-started/private-dataspace-fee-sponsor', + }, + { + text: 'Iroha 3 vs. Iroha 2', + link: '/get-started/iroha-2', + }, + ], + }, + ] +} + +function sidebarGuide(): DefaultTheme.SidebarItem[] { + return [ + { + text: 'Guide', + link: '/guide/', + collapsed: false, + items: [ + { + text: 'Overview', + link: '/guide/', + }, + ], + }, + { + text: 'SDK Tutorials', + link: '/guide/tutorials/', + collapsed: false, + items: [ + { + text: 'Rust', + link: '/guide/tutorials/rust', + }, + { + text: 'Python', + link: '/guide/tutorials/python', + }, + { + text: 'JavaScript / TypeScript', + link: '/guide/tutorials/javascript', + }, + { + text: 'Android / Kotlin / Java', + link: '/guide/tutorials/kotlin-java', + }, + { + text: 'Swift / iOS', + link: '/guide/tutorials/swift', + }, + { + text: 'Sample Apps', + link: '/guide/tutorials/sample-apps', + }, + { + text: 'Embed Kaigi', + link: '/guide/tutorials/kaigi', + }, + { + text: 'Musubi Packages', + link: '/guide/tutorials/musubi', + }, + { + text: 'Compatibility Matrix', + link: '/reference/compatibility-matrix', + }, + ], + }, + { + text: 'Best Practices', + link: '/guide/best-practices/', + collapsed: false, + items: [ + { + text: 'Overview', + link: '/guide/best-practices/', + }, + { + text: 'Application Development', + link: '/guide/best-practices/application-development.md', + }, + { + text: 'Data Modeling', + link: '/guide/best-practices/data-modeling.md', + }, + { + text: 'Network Deployment', + link: '/guide/best-practices/network-deployment.md', + }, + { + text: 'Operations', + link: '/guide/best-practices/operations.md', + }, + { + text: 'Security and Access', + link: '/guide/best-practices/security-and-access.md', + }, + { + text: 'Release Readiness', + link: '/guide/best-practices/release-readiness.md', + }, + ], + }, + { + text: 'Operator Quick Links', + collapsed: false, + items: [ + { + text: 'Configuration Overview', + link: '/guide/configure/overview.md', + }, + { + text: 'Genesis', + link: '/reference/genesis.md', + }, + { + text: 'Client Configuration', + link: '/guide/configure/client-configuration.md', + }, + { + text: 'Keys for Deployment', + link: '/guide/configure/keys-for-network-deployment.md', + }, + { + text: 'Peer Management', + link: '/guide/configure/peer-management.md', + }, + { + text: 'Metadata Storage Choices', + link: '/guide/configure/metadata-and-store-assets.md', + }, + { + text: 'Torii Endpoints', + link: '/reference/torii-endpoints.md', + }, + { + text: 'Torii API Console', + link: '/reference/torii-api-console.md', + }, + { + text: 'Performance and Metrics', + link: '/guide/advanced/metrics.md', + }, + { + text: 'Chaos Testing', + link: '/guide/advanced/chaos-testing.md', + }, + { + text: 'Binaries', + link: '/reference/binaries.md', + }, + ], + }, + { + text: 'Security', + link: '/guide/security/', + collapsed: false, + items: [ + { + text: 'Overview', + link: '/guide/security/', + }, + { + text: 'Security Principles', + link: '/guide/security/security-principles.md', + }, + { + text: 'Virtual Private Networks', + link: '/guide/security/vpn.md', + }, + { + text: 'Operational Security', + link: '/guide/security/operational-security.md', + }, + { + text: 'Fraud Monitoring', + link: '/guide/security/fraud-monitoring.md', + }, + { + text: 'Password Security', + link: '/guide/security/password-security.md', + }, + { + text: 'Public Key Cryptography', + link: '/guide/security/public-key-cryptography.md', + }, + { + text: 'Generating Cryptographic Keys', + link: '/guide/security/generating-cryptographic-keys.md', + }, + { + text: 'Storing Cryptographic Keys', + link: '/guide/security/storing-cryptographic-keys.md', + }, + ], + }, + ] +} + +function sidebarChain(): DefaultTheme.SidebarItem[] { + return [ + { + text: 'Architecture', + link: '/blockchain/iroha-explained', + items: [ + { + text: 'Iroha 3 Overview', + link: '/blockchain/iroha-explained', + }, + { + text: 'SORA Nexus Services', + link: '/blockchain/sora-nexus-services', + }, + { + text: 'World, WSV, and Kura', + link: '/blockchain/world', + }, + { + text: 'Data Model', + link: '/blockchain/data-model', + }, + ], + }, + { + text: 'Ledger Objects', + items: [ + { + text: 'Domains', + link: '/blockchain/domains', + }, + { + text: 'Accounts', + link: '/blockchain/accounts', + }, + { + text: 'Assets', + link: '/blockchain/assets', + }, + { + text: 'NFTs', + link: '/blockchain/nfts', + }, + { + text: 'Real-World Assets', + link: '/blockchain/rwas', + }, + { + text: 'Metadata', + link: '/blockchain/metadata', + }, + ], + }, + { + text: 'Transactions and Queries', + items: [ + { + text: 'Transactions', + link: '/blockchain/transactions', + }, + { + text: 'Anonymous Transactions', + link: '/blockchain/anonymous-transactions', + }, + { + text: 'Instructions', + link: '/blockchain/instructions', + }, + { + text: 'Queries', + link: '/blockchain/queries', + }, + { + text: 'Filters', + link: '/blockchain/filters', + }, + { + text: 'Expressions', + link: '/blockchain/expressions', + }, + ], + }, + { + text: 'Runtime', + items: [ + { + text: 'Permissions', + link: '/blockchain/permissions', + }, + { + text: 'Events', + link: '/blockchain/events', + }, + { + text: 'Triggers', + link: '/blockchain/triggers', + }, + { + text: 'Trigger Examples', + link: '/blockchain/trigger-examples', + }, + { + text: 'Smart Contracts', + link: '/blockchain/wasm', + }, + { + text: 'Consensus', + link: '/blockchain/consensus', + }, + { + text: 'FastPQ', + link: '/blockchain/fastpq', + }, + ], + }, + ] +} + +function sidebarReference(): DefaultTheme.SidebarItem[] { + return [ + { + text: 'Reference', + items: [ + { + text: 'Overview', + link: '/reference/', + }, + { + text: 'Binaries', + link: '/reference/binaries.md', + }, + { + text: 'Torii API', + link: '/reference/torii-endpoints.md', + }, + { + text: 'Torii API Console', + link: '/reference/torii-api-console.md', + }, + { + text: 'Norito', + link: '/reference/norito.md', + }, + { + text: 'Compatibility Matrix', + link: '/reference/compatibility-matrix.md', + }, + { + text: 'Genesis', + link: '/reference/genesis.md', + }, + ], + }, + ] +} + +function sidebarHelp(): DefaultTheme.SidebarItem[] { + return [ + { + text: 'Receive support', + link: '/help/', + }, + { + text: 'Troubleshooting', + items: [ + { + text: 'Overview', + link: '/help/overview', + }, + { + text: 'Installation', + link: '/help/installation-issues', + }, + { + text: 'Configuration', + link: '/help/configuration-issues', + }, + { + text: 'Deployment', + link: '/help/deployment-issues', + }, + { + text: 'Integration', + link: '/help/integration-issues', + }, + ], + }, + ] +} + +const BASE = process.env.PUBLIC_PATH ?? '/' + +export default defineConfig({ + base: BASE, + srcDir: 'src', + srcExclude: ['snippets/*.md'], + title: 'Hyperledger Iroha 3 Docs', + description: + 'Documentation for Hyperledger Iroha 3 covering quickstart flows, SDK entry points, Torii, genesis, and operator tooling.', + lang: 'en-US', + vite: { + plugins: [ViteUnoCSS('../uno.config.ts'), ViteSvgLoader()], + envDir: resolve(__dirname, '../'), + }, + vue: { + template: { + compilerOptions: { + isCustomElement: (tag: string) => tag === 'rapi-doc', + }, + }, + }, + lastUpdated: true, + + head: [ + // Based on: https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs + ['link', { rel: 'icon', href: BASE + 'favicon.ico', sizes: 'any' }], + ['link', { rel: 'icon', href: BASE + 'icon.svg', sizes: 'image/svg+xml' }], + ['link', { rel: 'apple-touch-icon', href: BASE + 'apple-touch-icon.png' }], + ['link', { rel: 'manifest', href: BASE + 'manifest.webmanifest' }], + // Google Analytics integration + ['script', { src: 'https://www.googletagmanager.com/gtag/js?id=G-D6ETK9TN47' }], + [ + 'script', + {}, + ` + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-D6ETK9TN47'); + `, + ], + // KaTeX stylesheet + ['link', { rel: 'stylesheet', href: 'https://esm.sh/katex@0.16.8/dist/katex.min.css' }], + ], + + markdown: { + async config(md) { + md.use(footnote) + .use(mermaid) + // Note: Since vitepress@1.0.0-rc.14, it supports MathJax natively with `markdown.math = true`: + // https://github.com/vuejs/vitepress/pull/2977 + // Although KaTeX is more efficient, we might consider removing it in the future. + .use(katex) + }, + }, + + themeConfig: { + logo: '/icon.svg', + siteTitle: 'Iroha 3', + + socialLinks: [ + { icon: 'github', link: 'https://github.com/hyperledger-iroha/iroha-2-docs' }, + { + icon: { + /** + * https://icones.js.org/collection/material-symbols?s=bug + */ + svg: ``, + }, + link: 'https://github.com/hyperledger-iroha/iroha-2-docs/issues/new', + }, + ], + + editLink: { + pattern: 'https://github.com/hyperledger-iroha/iroha-2-docs/edit/main/src/:path', + text: 'Edit this page on GitHub', + }, + + lastUpdated: { + text: 'Last Updated', + }, + + nav: nav(), + + sidebar: { + '/get-started/': sidebarStart(), + '/guide/': sidebarGuide(), + '/blockchain/': sidebarChain(), + '/reference/': sidebarReference(), + '/help/': sidebarHelp(), + }, + + search: { + provider: 'local', + }, + }, +}) diff --git a/.vitepress/config.ts b/.vitepress/config.ts deleted file mode 100644 index 189af52aa..000000000 --- a/.vitepress/config.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { defineConfigWithTheme } from 'vitepress' - -export default defineConfigWithTheme({ - srcDir: 'src', -}) diff --git a/.vitepress/md-mermaid.ts b/.vitepress/md-mermaid.ts new file mode 100644 index 000000000..222131391 --- /dev/null +++ b/.vitepress/md-mermaid.ts @@ -0,0 +1,26 @@ +import MarkdownIt from 'markdown-it' +import hasha from 'hasha' + +/** + * Mermaid-js markdown-it plugin + */ +export const mermaid = (md: MarkdownIt) => { + const fence = md.renderer.rules.fence.bind(md.renderer.rules) + + md.renderer.rules.fence = (tokens, index, options, env, slf) => { + const token = tokens[index] + + if (token.info.trim() === 'mermaid') { + const content = token.content.trim() + const id = `mermaid_${hasha(content)}` + return `` + } + + // Shiki will highlight `mmd` as `mermaid` + if (token.info.trim() === 'mmd') { + tokens[index].info = 'mermaid' + } + + return fence(tokens, index, options, env, slf) + } +} diff --git a/.vitepress/theme/components/CompatibilityMatrixTable.vue b/.vitepress/theme/components/CompatibilityMatrixTable.vue new file mode 100644 index 000000000..284fdc2a2 --- /dev/null +++ b/.vitepress/theme/components/CompatibilityMatrixTable.vue @@ -0,0 +1,285 @@ + + + + + diff --git a/.vitepress/theme/components/CompatibilityMatrixTableIcon.vue b/.vitepress/theme/components/CompatibilityMatrixTableIcon.vue new file mode 100644 index 000000000..4a895f98a --- /dev/null +++ b/.vitepress/theme/components/CompatibilityMatrixTableIcon.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/.vitepress/theme/components/LayoutCustom.vue b/.vitepress/theme/components/LayoutCustom.vue new file mode 100644 index 000000000..2be2e3235 --- /dev/null +++ b/.vitepress/theme/components/LayoutCustom.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/.vitepress/theme/components/MermaidRender.vue b/.vitepress/theme/components/MermaidRender.vue new file mode 100644 index 000000000..f6dbcc7df --- /dev/null +++ b/.vitepress/theme/components/MermaidRender.vue @@ -0,0 +1,71 @@ + + + diff --git a/.vitepress/theme/components/MermaidRenderWrap.vue b/.vitepress/theme/components/MermaidRenderWrap.vue new file mode 100644 index 000000000..e7b4b20d5 --- /dev/null +++ b/.vitepress/theme/components/MermaidRenderWrap.vue @@ -0,0 +1,16 @@ + + + diff --git a/.vitepress/theme/components/ShareFeedback.vue b/.vitepress/theme/components/ShareFeedback.vue new file mode 100644 index 000000000..691b1138c --- /dev/null +++ b/.vitepress/theme/components/ShareFeedback.vue @@ -0,0 +1,260 @@ + + +