diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 55172b321..0c2cf9336 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -6,6 +6,8 @@ updates:
schedule:
interval: 'weekly'
day: 'saturday'
+ cooldown:
+ default-days: 7
labels: [ 'dependencies' ]
commit-message:
prefix: 'chore' ## prefix maximum string length of 15
@@ -16,6 +18,8 @@ updates:
schedule:
interval: 'weekly'
day: 'saturday'
+ cooldown:
+ default-days: 7
labels: [ 'dependencies' ]
commit-message:
prefix: 'chore' ## prefix maximum string length of 15
@@ -26,6 +30,8 @@ updates:
schedule:
interval: 'weekly'
day: 'saturday'
+ cooldown:
+ default-days: 7
labels: [ 'dependencies' ]
commit-message:
prefix: 'chore' ## prefix maximum string length of 15
@@ -36,6 +42,8 @@ updates:
schedule:
interval: 'weekly'
day: 'saturday'
+ cooldown:
+ default-days: 7
labels: [ 'dependencies' ]
commit-message:
prefix: 'chore' ## prefix maximum string length of 15
@@ -46,6 +54,8 @@ updates:
schedule:
interval: 'weekly'
day: 'saturday'
+ cooldown:
+ default-days: 7
labels: [ 'dependencies' ]
commit-message:
prefix: 'chore' ## prefix maximum string length of 15
@@ -56,6 +66,8 @@ updates:
schedule:
interval: 'weekly'
day: 'saturday'
+ cooldown:
+ default-days: 7
labels: [ 'dependencies' ]
commit-message:
prefix: 'chore' ## prefix maximum string length of 15
diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml
index 2b862b92e..b919ba5b8 100644
--- a/.github/workflows/build_docs.yml
+++ b/.github/workflows/build_docs.yml
@@ -21,10 +21,12 @@ jobs:
steps:
- name: Checkout
# see https://github.com/actions/checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Set up JDK
# see https://github.com/actions/setup-java
- uses: actions/setup-java@v5
+ uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
java-version: '21'
distribution: 'zulu'
@@ -33,7 +35,7 @@ jobs:
run: ./gen.sh
- name: Archive Schema documentation
# https://github.com/actions/upload-artifact
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: XML-Schema-documentation
path: docgen/xml/docs
@@ -46,10 +48,12 @@ jobs:
steps:
- name: Checkout
# see https://github.com/actions/checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Setup Python Environment
# see https://github.com/actions/setup-python
- uses: actions/setup-python@v6
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.PYTHON_VERSION_DEFAULT }}
architecture: 'x64'
@@ -57,7 +61,7 @@ jobs:
run: ./gen.sh
- name: Archive Schema documentation
# https://github.com/actions/upload-artifact
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: JSON-Schema-documentation
path: docgen/json/docs
@@ -70,12 +74,14 @@ jobs:
steps:
- name: Checkout
# see https://github.com/actions/checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Generate Schema documentation
run: ./gen.sh
- name: Archive Schema documentation
# https://github.com/actions/upload-artifact
- uses: actions/upload-artifact@v6
+ uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: PROTO-Schema-documentation
path: docgen/proto/docs
diff --git a/.github/workflows/bundle_2.0_schemas.yml b/.github/workflows/bundle_2.0_schemas.yml
index fe800cafc..5808d63d8 100644
--- a/.github/workflows/bundle_2.0_schemas.yml
+++ b/.github/workflows/bundle_2.0_schemas.yml
@@ -19,12 +19,14 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ # see https://github.com/actions/checkout
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
- token: ${{ secrets.GITHUB_TOKEN }}
+ persist-credentials: false
- name: Setup Node.js
- uses: actions/setup-node@v6
+ # see https://github.com/actions/setup-node
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '20'
@@ -40,6 +42,8 @@ jobs:
../../../../schema/2.0/cyclonedx-2.0.schema.json
- name: Check for changes and commit
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BUNDLED_FILE="schema/2.0/cyclonedx-2.0-bundled.schema.json"
@@ -54,5 +58,5 @@ jobs:
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git commit -m "chore: update bundled schema [skip ci]"
- git push
+ git push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
fi
diff --git a/.github/workflows/generate_algorithm_families.yml b/.github/workflows/generate_algorithm_families.yml
index d349edcf9..ae5ee2c53 100644
--- a/.github/workflows/generate_algorithm_families.yml
+++ b/.github/workflows/generate_algorithm_families.yml
@@ -17,13 +17,15 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ # see https://github.com/actions/checkout
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: false
- name: Set up Python
- uses: actions/setup-python@v5
+ # see https://github.com/actions/setup-python
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: '3.x'
diff --git a/.github/workflows/test_java.yml b/.github/workflows/test_java.yml
index 226e1d797..061300a84 100644
--- a/.github/workflows/test_java.yml
+++ b/.github/workflows/test_java.yml
@@ -23,10 +23,12 @@ jobs:
steps:
- name: Checkout
# see https://github.com/actions/checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Set up JDK
# see https://github.com/actions/setup-java
- uses: actions/setup-java@v5
+ uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
java-version: '8'
distribution: 'zulu'
diff --git a/.github/workflows/test_js.yml b/.github/workflows/test_js.yml
index f2029ba40..02e5dcdf2 100644
--- a/.github/workflows/test_js.yml
+++ b/.github/workflows/test_js.yml
@@ -26,10 +26,12 @@ jobs:
steps:
- name: Checkout
# see https://github.com/actions/checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Setup Node.js
# see https://github.com/actions/setup-node
- uses: actions/setup-node@v6
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '24.x'
package-manager-cache: false
diff --git a/.github/workflows/test_php.yml b/.github/workflows/test_php.yml
index 3d044081b..b761bc7b3 100644
--- a/.github/workflows/test_php.yml
+++ b/.github/workflows/test_php.yml
@@ -26,10 +26,12 @@ jobs:
steps:
- name: Checkout
# see https://github.com/actions/checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Setup PHP
# see https://github.com/shivammathur/setup-php
- uses: shivammathur/setup-php@v2
+ uses: shivammathur/setup-php@7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc # 2.37.1
with:
php-version: "8.4"
tools: composer:v2
diff --git a/.github/workflows/test_proto.yml b/.github/workflows/test_proto.yml
index 7758e146e..30b9acda1 100644
--- a/.github/workflows/test_proto.yml
+++ b/.github/workflows/test_proto.yml
@@ -26,6 +26,8 @@ jobs:
steps:
- name: Checkout
# see https://github.com/actions/checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
- name: Run test
run: ./test.sh
diff --git a/.github/workflows/update_spdx_licenses.yml b/.github/workflows/update_spdx_licenses.yml
index d751798b0..fe95faa76 100644
--- a/.github/workflows/update_spdx_licenses.yml
+++ b/.github/workflows/update_spdx_licenses.yml
@@ -23,12 +23,13 @@ jobs:
steps:
- name: Checkout
# see https://github.com/actions/checkout
- uses: actions/checkout@v5
+ uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
ref: ${{ github.ref_name }}
+ persist-credentials: false
- name: Set up JDK
# see https://github.com/actions/setup-java
- uses: actions/setup-java@v5
+ uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
java-version: '21'
distribution: 'zulu'
@@ -54,7 +55,7 @@ jobs:
- name: Artifact changes
if: ${{ steps.diff.outputs.changed == 'true' }}
# https://github.com/actions/upload-artifact
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
retention-days: 1
name: schema-spdx
@@ -74,9 +75,10 @@ jobs:
steps:
- name: Checkout
# see https://github.com/actions/checkout
- uses: actions/checkout@v5
+ uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
ref: ${{ github.ref_name }}
+ persist-credentials: false
- name: Switch branch
id: branch
run: |
@@ -93,11 +95,13 @@ jobs:
fi
- name: Fetch changes
# https://github.com/actions/download-artifact
- uses: actions/download-artifact@v5
+ uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: schema-spdx
path: schema
- name: Commit and push
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -eux
if git diff --quiet -- 'schema/spdx.*'
@@ -109,7 +113,7 @@ jobs:
git config user.email 'spdx-license-bumper@bot.local'
git add -A schema
git commit -s -m "feat: bump SPDX licenses $SB_VERSION"
- git push origin "$SB_BRANCH"
+ git push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "$SB_BRANCH"
- name: Pull request
if: ${{ steps.branch.outputs.existed == 'false' }}
run: >
diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml
new file mode 100644
index 000000000..03725cafc
--- /dev/null
+++ b/.github/workflows/zizmor.yml
@@ -0,0 +1,42 @@
+name: Zizmor
+
+on:
+ push:
+ branches: ['master', 'main']
+ pull_request:
+ branches: ['**']
+ workflow_dispatch:
+ schedule:
+ - cron: '0 0 * * 6'
+
+permissions: {}
+
+concurrency:
+ group: '${{ github.workflow }}-${{ github.ref }}'
+ cancel-in-progress: true
+
+jobs:
+ zizmor:
+ name: Zizmor
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ permissions:
+ contents: read
+ steps:
+ - name: Checkout
+ # see https://github.com/actions/checkout
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ - name: Run zizmor 🌈
+ # see https://github.com/zizmorcore/zizmor-action
+ uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6
+ with:
+ # advanced-security: false => emit findings as workflow-command annotations (::error file=…) rather than
+ # uploading a SARIF report to GitHub's Security tab.
+ # Uploading SARIF requires `security-events: write` and GitHub Advanced Security (GHAS),
+ # both of which are unnecessary here and would violate the least-privilege policy.
+ # The two modes are mutually exclusive: advanced-security must be false for
+ # annotations to take effect.
+ advanced-security: false
+ annotations: true
diff --git a/.gitignore b/.gitignore
index 2628a5390..875ca5863 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,11 @@
+# Filesystem
+**/.DS_Store
+
+# Tooling
.idea/
.vscode/
tools/target/
+.bob
+
+# UML diagrams
+**/.uml/
diff --git a/docgen/json/gen.sh b/docgen/json/gen.sh
index 4eb653264..8357f2ef5 100755
--- a/docgen/json/gen.sh
+++ b/docgen/json/gen.sh
@@ -2,6 +2,7 @@
set -eu
declare -a CDX_VERSIONS=(
+ '2.0'
'1.7'
'1.6'
'1.5'
@@ -25,6 +26,9 @@ SCHEMA_PATH="$(realpath "$THIS_PATH/../../schema")"
DOCS_PATH="$THIS_PATH/docs"
TEMPLATES_PATH="$THIS_PATH/templates"
+# Centralized header injection
+source "$THIS_PATH/../static/inject-header.sh"
+
# --
@@ -44,11 +48,15 @@ generate () {
local title="CycloneDX v${version} JSON Reference"
echo "Generating: $title"
- local SCHEMA_FILE="$SCHEMA_PATH/bom-${version}.schema.json"
- local STRICT_SCHEMA_FILE="$SCHEMA_PATH/bom-${version}-strict.schema.json"
- if [ -f "$STRICT_SCHEMA_FILE" ]
- then
+ local SCHEMA_FILE
+ if [[ "$version" == 1.* ]]; then
+ SCHEMA_FILE="$SCHEMA_PATH/bom-${version}.schema.json"
+ local STRICT_SCHEMA_FILE="$SCHEMA_PATH/bom-${version}-strict.schema.json"
+ if [ -f "$STRICT_SCHEMA_FILE" ]; then
SCHEMA_FILE="$STRICT_SCHEMA_FILE"
+ fi
+ else
+ SCHEMA_FILE="$SCHEMA_PATH/${version}/cyclonedx-${version}-bundled.schema.json"
fi
echo "SCHEMA_FILE: $SCHEMA_FILE"
@@ -58,7 +66,7 @@ generate () {
mkdir -p "$OUT_DIR"
generate-schema-doc \
- --config no_link_to_reused_ref \
+ --config link_to_reused_ref \
--config no_show_breadcrumbs \
--config no_collapse_long_descriptions \
--deprecated-from-description \
@@ -71,6 +79,8 @@ generate () {
sed -i -e "s/\${quotedTitle}/\"$title\"/g" "$OUT_FILE"
sed -i -e "s/\${title}/$title/g" "$OUT_FILE"
sed -i -e "s/\${version}/$version/g" "$OUT_FILE"
+
+ inject_header "$OUT_FILE" "$version" "json"
}
diff --git a/docgen/json/requirements.txt b/docgen/json/requirements.txt
index d5e43567e..dbb2b26d0 100644
--- a/docgen/json/requirements.txt
+++ b/docgen/json/requirements.txt
@@ -1 +1 @@
-json-schema-for-humans==1.3.4
+json-schema-for-humans==1.5.1
diff --git a/docgen/json/templates/cyclonedx/base.html b/docgen/json/templates/cyclonedx/base.html
index fd480909b..00f172874 100644
--- a/docgen/json/templates/cyclonedx/base.html
+++ b/docgen/json/templates/cyclonedx/base.html
@@ -18,54 +18,15 @@
-
-
+
+
-
-
-
+
-
+
-
-
-
-
-
-
-
+
@@ -75,9 +36,9 @@
${title}
{{ title }}
{%- endif -%}
{%- if config.expand_buttons -%}
-
-
Expand all
-
Collapse all
+
+ Expand all
+ Collapse all
{%- endif -%}
@@ -95,5 +56,7 @@
{{ title }}
-->
+
+
diff --git a/docgen/json/templates/cyclonedx/content.html b/docgen/json/templates/cyclonedx/content.html
index 53bc8f7f1..ecfc23627 100644
--- a/docgen/json/templates/cyclonedx/content.html
+++ b/docgen/json/templates/cyclonedx/content.html
@@ -17,17 +17,17 @@
{# Display type #}
{%- if not schema is combining -%}
-
Type: {{ type_name }}
+
Type: {{ type_name }}
{%- endif -%}
{%- if schema.format -%}
-
Format: {{ schema.format }}
+
Format: {{ schema.format }}
{%- endif -%}
{# Display default #}
{%- set default_value = schema.default_value -%}
{%- if default_value -%}
- {{ " " }}
Default: {{ default_value }}
+ {{ " " }}
Default: {{ default_value }}
{%- endif -%}
@@ -42,7 +42,7 @@
{{ content(schema.refers_to_merged, True) }}
{%- else -%}
{%- if schema.explicit_no_additional_properties -%}
- {{ " " }}
No Additional Properties
+ {{ " " }}
No Additional Properties
{%- endif -%}
{# Combining: allOf, anyOf, oneOf, not #}
@@ -64,7 +64,7 @@
Must be one of:
-
+
Name
Description
diff --git a/docgen/json/templates/cyclonedx/schema_doc.css b/docgen/json/templates/cyclonedx/schema_doc.css
index ecf2b5d29..1367c320a 100644
--- a/docgen/json/templates/cyclonedx/schema_doc.css
+++ b/docgen/json/templates/cyclonedx/schema_doc.css
@@ -5,19 +5,22 @@ body {
padding: 0;
}
.navbar {
- height: 90px;
+ min-height: 90px;
padding: 0;
}
-.navbar-inverse .navbar-nav>.open>a,
-.navbar-inverse .navbar-nav>.open>a:focus,
-.navbar-inverse .navbar-nav>.open>a:hover,
-.navbar-inverse {
+.navbar-toggler-icon {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") !important;
+}
+.navbar-dark .navbar-nav>.open>a,
+.navbar-dark .navbar-nav>.open>a:focus,
+.navbar-dark .navbar-nav>.open>a:hover,
+.navbar-dark {
background-image: linear-gradient(269.12deg, rgba(232, 52, 82, 1) 0%, rgba(136, 38, 125, 1) 51.26%, rgba(52, 57, 175, 1) 100%);
}
-.navbar-brand, .navbar-fixed-top {
+.navbar-brand, .fixed-top {
padding: 0 30px 0 30px;
}
-.navbar-inverse .navbar-nav>li>a {
+.navbar-dark .navbar-nav>li>a {
color: #ffffff;
}
.site-header__logo img {
@@ -26,8 +29,9 @@ body {
.version-selector {
font-size: 1.2rem
}
-.table .thead-dark th {
+.table .table-dark th {
background-color: #323550;
+ color: #ffffff;
}
.container {
margin-right: auto;
@@ -73,15 +77,24 @@ ul .dropdown-menu li {
}
.card {
border-radius: 0;
+ --bs-card-border-color: rgba(0, 0, 0, 0.125);
+}
+.accordion + .accordion .card {
+ margin-top: -1px;
}
.card-header {
padding: 0;
}
-.card-header .fa {
+.card-header .btn .bi {
+ display: inline-block;
transition: .3s transform ease-in-out;
+ -webkit-text-stroke: 2px;
+}
+.card-header .btn[aria-expanded="true"] .bi {
transform: rotate(90deg);
}
-.card-header .collapsed .fa {
+.card-header .btn[aria-expanded="false"] .bi,
+.card-header .btn.collapsed .bi {
transform: rotate(0deg);
}
.btn.btn-link {
@@ -136,7 +149,7 @@ ul .dropdown-menu li {
content: '- Read Less';
}
.badge {
- color: #222222;
+ color: #222222 !important;
padding: .1em .4em .2em;
margin-right: .2em;
font-weight: normal;
@@ -144,44 +157,47 @@ ul .dropdown-menu li {
border-radius: 0;
}
.badge.required-property {
- background-color: rgba(255,137,29,0.3);
+ background-color: rgba(255,137,29,0.3) !important;
border: 1px solid #FF7F0B;
}
.badge.value-type {
- background-color: rgba(174,206,229,0.3);
+ background-color: rgba(174,206,229,0.3) !important;
border: 1px solid #5C9CCB;
}
.badge.default-value {
- background-color: rgba(175,228,191,0.3);
+ background-color: rgba(175,228,191,0.3) !important;
border: 1px solid #73D08F;
}
.badge.example {
- background-color: rgba(235,202,255,0.3);
+ background-color: rgba(235,202,255,0.3) !important;
border: 1px solid #DA9FFF;
}
.badge.deprecated-property {
- background-color: rgba(255,95,95,0.3);
+ background-color: rgba(255,95,95,0.3) !important;
border: 1px solid #FF3333;
}
.badge.no-additional {
- background-color: rgba(255,82,174,0.3);
+ background-color: rgba(255,82,174,0.3) !important;
border: 1px solid #FF33A0;
}
.badge.pattern-property {
- background-color: rgba(235,229,168,0.3);
+ background-color: rgba(235,229,168,0.3) !important;
border: 1px solid #FFEA1C;
}
.accordion div.card:only-child {
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
}
.examples {
- padding: 1rem !important;
+ padding: 0 !important;
+ margin-top: 0.5rem;
+ margin-bottom: 0.5rem;
}
.examples pre {
margin-bottom: 0;
+ padding: 1rem 1.5rem;
}
.highlight.jumbotron {
- padding: 1rem !important;
+ padding: 1rem 1.5rem !important;
}
.generated-by-footer {
margin-top: 1em;
@@ -261,4 +277,19 @@ ul .dropdown-menu li {
.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
.highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */
-.highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */
\ No newline at end of file
+.highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */
+
+/* ═══════════════════════════════════════════════════════════
+ Inline expansion for reused definitions (ref-links)
+ ═══════════════════════════════════════════════════════════ */
+
+/* Hide the "Same definition as..." link text; content is
+ cloned inline automatically when the parent row expands. */
+.ref-link[data-ref-expanded="true"] {
+ display: none;
+}
+
+/* Container for the cloned definition content */
+.ref-expand-content {
+ margin-top: 0.25rem;
+}
\ No newline at end of file
diff --git a/docgen/json/templates/cyclonedx/schema_doc.js b/docgen/json/templates/cyclonedx/schema_doc.js
index e03ce9464..fa9faf5be 100644
--- a/docgen/json/templates/cyclonedx/schema_doc.js
+++ b/docgen/json/templates/cyclonedx/schema_doc.js
@@ -1,15 +1,26 @@
-$(document).on('click', 'a[href^="#"]', function(event) {
- event.preventDefault();
- history.pushState({}, '', this.href);
+document.addEventListener('click', function(event) {
+ var anchor = event.target.closest('a[href^="#"]');
+ if (anchor) {
+ // Skip ref-links; they are replaced by inline expansions
+ if (anchor.classList.contains('ref-link')) {
+ event.preventDefault();
+ return;
+ }
+ // Don't interfere with Bootstrap tabs or collapse toggles
+ if (anchor.getAttribute('data-bs-toggle')) return;
+ event.preventDefault();
+ history.pushState({}, '', anchor.href);
+ }
});
function flashElement(elementId) {
- // $( "#" + elementId ).fadeOut(100).fadeIn(200).fadeOut(100).fadeIn(500);
- myElement = document.getElementById(elementId);
- myElement.classList.add("jsfh-animated-property");
- setTimeout(function() {
- myElement.classList.remove("jsfh-animated-property");
- }, 1000);
+ var myElement = document.getElementById(elementId);
+ if (myElement) {
+ myElement.classList.add("jsfh-animated-property");
+ setTimeout(function() {
+ myElement.classList.remove("jsfh-animated-property");
+ }, 1000);
+ }
}
function setAnchor(anchorLinkDestination) {
@@ -19,7 +30,7 @@ function setAnchor(anchorLinkDestination) {
function anchorOnLoad() {
// Added to onload on body, checks if there is an anchor link and if so, expand
- let linkTarget = decodeURIComponent(window.location.hash.split("?")[0].split("&")[0]);
+ var linkTarget = decodeURIComponent(window.location.hash.split("?")[0].split("&")[0]);
if (linkTarget[0] === "#") {
linkTarget = linkTarget.substr(1);
}
@@ -30,31 +41,35 @@ function anchorOnLoad() {
}
function anchorLink(linkTarget) {
- const target = $( "#" + linkTarget );
- // Find the targeted element to expand and all its parents that can be expanded
- target.parents().addBack().filter(".collapse:not(.show), .tab-pane, [role='tab']").each(
- function(index) {
- if($( this ).hasClass("collapse")) {
- $( this ).collapse("show");
- } else if ($( this ).hasClass("tab-pane")) {
- // We have the pane and not the tab itself, find the tab
- const tabToShow = $( "a[href='#" + $( this ).attr("id") + "']" );
- if (tabToShow) {
- tabToShow.tab("show");
- }
- } else if ($( this ).attr("role") === "tab") {
- // The tab is not a parent of underlying elements, the tab pane is
- // However, it can still be linked directly
- $( this ).tab("show");
+ var target = document.getElementById(linkTarget);
+ if (!target) return;
+
+ // Find the targeted element and all its parents that can be expanded
+ var element = target;
+ while (element) {
+ // Expand collapsed sections
+ if (element.classList.contains("collapse") && !element.classList.contains("show")) {
+ var bsCollapse = new bootstrap.Collapse(element, { toggle: true });
+ }
+ // Activate tab panes
+ if (element.classList.contains("tab-pane")) {
+ var tabTrigger = document.querySelector('a[href="#' + element.id + '"]');
+ if (tabTrigger) {
+ var bsTab = new bootstrap.Tab(tabTrigger);
+ bsTab.show();
}
}
- );
+ // Handle direct tab links
+ if (element.getAttribute("role") === "tab") {
+ var bsTab = new bootstrap.Tab(element);
+ bsTab.show();
+ }
+ element = element.parentElement;
+ }
// Wait a little so the user has time to see the page scroll
- // Or maybe it is to be sure everything is expanded before scrolling and I was not able to bind to the bootstrap
- // events in a way that works all the time, we may never know
setTimeout(function() {
- let targetElement = document.getElementById(linkTarget);
+ var targetElement = document.getElementById(linkTarget);
if (targetElement) {
targetElement.scrollIntoView({ block: "center", behavior:"smooth" });
// Flash the element so that the user notices where the link points to
@@ -63,4 +78,342 @@ function anchorLink(linkTarget) {
}, 500);
}
}, 1000);
-}
\ No newline at end of file
+}
+
+
+// ═══════════════════════════════════════════════════════════
+// Fix duplicate IDs produced by link_to_reused_ref
+// ═══════════════════════════════════════════════════════════
+//
+// The schema doc generator reuses the same IDs when inlining
+// a $ref definition at multiple schema paths. Duplicate IDs
+// break Bootstrap tabs/collapses because getElementById
+// always returns the first match. This pass finds duplicates
+// and rewrites subsequent occurrences so every ID is unique.
+// ═══════════════════════════════════════════════════════════
+
+(function() {
+ function fixDuplicateIds() {
+ var seen = {}; // id -> true for first occurrence
+ var dupCount = 0;
+
+ // Pass 1: rename duplicate IDs. First occurrence keeps
+ // its id; subsequent occurrences get a unique suffix.
+ var allWithId = document.querySelectorAll('[id]');
+ allWithId.forEach(function(el) {
+ var id = el.id;
+ if (!id) return;
+ if (seen[id]) {
+ dupCount++;
+ el.setAttribute('data-orig-id', id);
+ el.id = id + '__d' + dupCount;
+ } else {
+ seen[id] = true;
+ }
+ });
+
+ if (dupCount === 0) return;
+
+ // Build lookup: origId -> [el, el, ...] for fast scoping
+ var renamed = {};
+ document.querySelectorAll('[data-orig-id]').forEach(function(el) {
+ var origId = el.getAttribute('data-orig-id');
+ if (!renamed[origId]) renamed[origId] = [];
+ renamed[origId].push(el);
+ });
+
+ // Build full candidate list: origId -> [el, ...] including
+ // both the original (first-occurrence) element and all renamed
+ // duplicates so that scoping works for every occurrence.
+ var allTargets = {};
+ Object.keys(renamed).forEach(function(origId) {
+ var orig = document.getElementById(origId);
+ allTargets[origId] = orig ? [orig].concat(renamed[origId]) : renamed[origId];
+ });
+
+ // Find the target element (original or renamed) that shares
+ // the closest common ancestor with the referrer.
+ function findLocalTarget(referrer, origId) {
+ var candidates = allTargets[origId];
+ if (!candidates) return origId;
+ var scope = referrer.parentElement;
+ while (scope) {
+ for (var i = 0; i < candidates.length; i++) {
+ if (scope.contains(candidates[i])) return candidates[i].id;
+ }
+ scope = scope.parentElement;
+ }
+ return origId;
+ }
+
+ // Pass 2: fix references that point to renamed IDs.
+ function fixHashAttr(el, attr) {
+ var val = el.getAttribute(attr);
+ if (!val || val.charAt(0) !== '#') return;
+ var refId = val.substring(1);
+ if (!renamed[refId]) return;
+ var localId = findLocalTarget(el, refId);
+ if (localId !== refId) el.setAttribute(attr, '#' + localId);
+ }
+
+ function fixPlainAttr(el, attr) {
+ var val = el.getAttribute(attr);
+ if (!val || !renamed[val]) return;
+ var localId = findLocalTarget(el, val);
+ if (localId !== val) el.setAttribute(attr, localId);
+ }
+
+ document.querySelectorAll('a[href^="#"]').forEach(function(el) {
+ fixHashAttr(el, 'href');
+ });
+ document.querySelectorAll('[data-bs-target^="#"]').forEach(function(el) {
+ fixHashAttr(el, 'data-bs-target');
+ });
+ document.querySelectorAll('[data-bs-parent^="#"]').forEach(function(el) {
+ fixHashAttr(el, 'data-bs-parent');
+ });
+ document.querySelectorAll('[aria-controls]').forEach(function(el) {
+ fixPlainAttr(el, 'aria-controls');
+ });
+ document.querySelectorAll('[aria-labelledby]').forEach(function(el) {
+ fixPlainAttr(el, 'aria-labelledby');
+ });
+ document.querySelectorAll('[onclick]').forEach(function(el) {
+ var onclick = el.getAttribute('onclick');
+ if (!onclick) return;
+ var changed = false;
+ var updated = onclick.replace(
+ /anchorLink\('([^']+)'\)/g,
+ function(match, id) {
+ if (!renamed[id]) return match;
+ var localId = findLocalTarget(el, id);
+ if (localId !== id) { changed = true; return "anchorLink('" + localId + "')"; }
+ return match;
+ }
+ ).replace(
+ /setAnchor\('#([^']+)'\)/g,
+ function(match, id) {
+ if (!renamed[id]) return match;
+ var localId = findLocalTarget(el, id);
+ if (localId !== id) { changed = true; return "setAnchor('#" + localId + "')"; }
+ return match;
+ }
+ );
+ if (changed) el.setAttribute('onclick', updated);
+ });
+ }
+
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', fixDuplicateIds);
+ } else {
+ fixDuplicateIds();
+ }
+})();
+
+
+// ═══════════════════════════════════════════════════════════
+// Automatic inline expansion for reused definitions
+// ═══════════════════════════════════════════════════════════
+//
+// When link_to_reused_ref is enabled, repeated definitions
+// render as "Same definition as X" links pointing to the
+// original. This enhancement hides those links and clones
+// the original definition inline automatically when the
+// parent property row is expanded. No user click required.
+// The full HTML stays in the DOM for SEO crawlability.
+// ═══════════════════════════════════════════════════════════
+
+(function() {
+ var expandCounter = 0;
+
+ /**
+ * Rewrite IDs inside a cloned subtree so they don't collide
+ * with the originals. Also updates internal href="#...",
+ * data-bs-target, data-bs-parent, and aria attributes.
+ */
+ function deduplicateIds(container, suffix) {
+ var elements = container.querySelectorAll('[id]');
+ var idMap = {};
+ elements.forEach(function(el) {
+ var oldId = el.id;
+ var newId = oldId + suffix;
+ idMap[oldId] = newId;
+ el.id = newId;
+ });
+
+ container.querySelectorAll('[href]').forEach(function(el) {
+ var href = el.getAttribute('href');
+ if (href && href.charAt(0) === '#') {
+ var refId = href.substring(1);
+ if (idMap[refId]) {
+ el.setAttribute('href', '#' + idMap[refId]);
+ }
+ }
+ });
+ container.querySelectorAll('[data-bs-target]').forEach(function(el) {
+ var val = el.getAttribute('data-bs-target');
+ if (val && val.charAt(0) === '#') {
+ var refId = val.substring(1);
+ if (idMap[refId]) {
+ el.setAttribute('data-bs-target', '#' + idMap[refId]);
+ }
+ }
+ });
+ container.querySelectorAll('[data-bs-parent]').forEach(function(el) {
+ var val = el.getAttribute('data-bs-parent');
+ if (val && val.charAt(0) === '#') {
+ var refId = val.substring(1);
+ if (idMap[refId]) {
+ el.setAttribute('data-bs-parent', '#' + idMap[refId]);
+ }
+ }
+ });
+ container.querySelectorAll('[aria-controls]').forEach(function(el) {
+ var val = el.getAttribute('aria-controls');
+ if (val && idMap[val]) {
+ el.setAttribute('aria-controls', idMap[val]);
+ }
+ });
+ container.querySelectorAll('[aria-labelledby]').forEach(function(el) {
+ var val = el.getAttribute('aria-labelledby');
+ if (val && idMap[val]) {
+ el.setAttribute('aria-labelledby', idMap[val]);
+ }
+ });
+
+ container.querySelectorAll('[onclick]').forEach(function(el) {
+ var onclick = el.getAttribute('onclick');
+ if (onclick) {
+ var updated = onclick.replace(
+ /anchorLink\('([^']+)'\)/g,
+ function(match, id) {
+ return idMap[id] ? "anchorLink('" + idMap[id] + "')" : match;
+ }
+ ).replace(
+ /setAnchor\('#([^']+)'\)/g,
+ function(match, id) {
+ return idMap[id] ? "setAnchor('#" + idMap[id] + "')" : match;
+ }
+ );
+ el.setAttribute('onclick', updated);
+ }
+ });
+ }
+
+ /**
+ * Check whether a node is "leading metadata" that already
+ * appears in the ref-link's container: the type badge
+ * (span.badge.value-type), a , a description span,
+ * or whitespace text nodes between them.
+ */
+ function isLeadingMeta(node) {
+ if (node.nodeType === 3) {
+ // Text node: skip if whitespace-only
+ return node.textContent.trim() === '';
+ }
+ if (node.nodeType !== 1) return false;
+ var el = node;
+ // Type badge, e.g.
+ if (el.tagName === 'SPAN' && el.classList.contains('value-type')) return true;
+ // element right after the type badge
+ if (el.tagName === 'BR') return true;
+ // Description span
+ if (el.tagName === 'SPAN' && el.classList.contains('description')) return true;
+ return false;
+ }
+
+ /**
+ * Clone a source definition into the container that holds
+ * the ref-link. The ref-link itself is hidden via CSS.
+ * Leading type badge, , and description are skipped
+ * because the container already shows them.
+ */
+ function expandRefLink(link) {
+ // Skip if already expanded
+ if (link.getAttribute('data-ref-expanded') === 'true') return;
+ link.setAttribute('data-ref-expanded', 'true');
+
+ var targetId = link.getAttribute('href').substring(1);
+ var source = document.getElementById(targetId);
+ if (!source) return;
+
+ expandCounter++;
+ var suffix = '__exp' + expandCounter;
+
+ var content = document.createElement('div');
+ content.className = 'ref-expand-content';
+
+ // Clone child nodes, skipping leading metadata that
+ // duplicates what the container already displays.
+ var nodes = source.childNodes;
+ var pastLeading = false;
+ for (var i = 0; i < nodes.length; i++) {
+ if (!pastLeading && isLeadingMeta(nodes[i])) continue;
+ pastLeading = true;
+ content.appendChild(nodes[i].cloneNode(true));
+ }
+
+ deduplicateIds(content, suffix);
+
+ // Insert the cloned content after the ref-link
+ link.parentNode.insertBefore(content, link.nextSibling);
+ }
+
+ /**
+ * Check whether a ref-link is directly visible within the
+ * panel that was just shown. Returns false if the link sits
+ * inside a nested collapse that is still hidden.
+ */
+ function isVisibleInPanel(link, panel) {
+ var el = link.parentElement;
+ while (el && el !== panel) {
+ if (el.classList.contains('collapse') && !el.classList.contains('show')) {
+ return false;
+ }
+ el = el.parentElement;
+ }
+ return true;
+ }
+
+ /**
+ * When a collapse panel is shown, expand only the ref-links
+ * that are directly visible (not buried in nested collapses).
+ */
+ function onCollapseShown(e) {
+ var panel = e.target;
+ var refLinks = panel.querySelectorAll('.ref-link');
+ refLinks.forEach(function(link) {
+ if (isVisibleInPanel(link, panel)) {
+ expandRefLink(link);
+ }
+ });
+ }
+
+ /**
+ * Initialize: hide ref-link text, listen for collapse events.
+ */
+ function initRefLinks() {
+ var refLinks = document.querySelectorAll('.ref-link');
+ refLinks.forEach(function(link) {
+ // Remove the original onclick
+ link.removeAttribute('onclick');
+
+ // Expand ref-links that are already visible on load
+ // (not inside any collapsed panel)
+ var parentCollapse = link.closest('.collapse');
+ if (!parentCollapse || parentCollapse.classList.contains('show')) {
+ expandRefLink(link);
+ }
+ });
+
+ // Listen for Bootstrap collapse show events
+ document.addEventListener('shown.bs.collapse', onCollapseShown);
+ }
+
+ // Run on DOM ready
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', initRefLinks);
+ } else {
+ initRefLinks();
+ }
+})();
diff --git a/docgen/json/templates/cyclonedx/schema_doc.min.js b/docgen/json/templates/cyclonedx/schema_doc.min.js
index 0c58a70ef..a3934ac7c 100644
--- a/docgen/json/templates/cyclonedx/schema_doc.min.js
+++ b/docgen/json/templates/cyclonedx/schema_doc.min.js
@@ -1 +1 @@
-function flashElement(t){(myElement=document.getElementById(t)).classList.add("jsfh-animated-property"),setTimeout(function(){myElement.classList.remove("jsfh-animated-property")},1e3)}function setAnchor(t){history.pushState({},"",t)}function anchorOnLoad(){let t=decodeURIComponent(window.location.hash.split("?")[0].split("&")[0]);"#"===t[0]&&(t=t.substr(1)),t.length>0&&anchorLink(t)}function anchorLink(t){let e=$("#"+t);e.parents().addBack().filter(".collapse:not(.show), .tab-pane, [role='tab']").each(function(t){if($(this).hasClass("collapse"))$(this).collapse("show");else if($(this).hasClass("tab-pane")){let e=$("a[href='#"+$(this).attr("id")+"']");e&&e.tab("show")}else"tab"===$(this).attr("role")&&$(this).tab("show")}),setTimeout(function(){let e=document.getElementById(t);e&&(e.scrollIntoView({block:"center",behavior:"smooth"}),setTimeout(function(){flashElement(t)},500))},1e3)}$(document).on("click",'a[href^="#"]',function(t){t.preventDefault(),history.pushState({},"",this.href)});
\ No newline at end of file
+function flashElement(t){var e=document.getElementById(t);e&&(e.classList.add("jsfh-animated-property"),setTimeout(function(){e.classList.remove("jsfh-animated-property")},1e3))}function setAnchor(t){history.pushState({},"",t)}function anchorOnLoad(){var t=decodeURIComponent(window.location.hash.split("?")[0].split("&")[0]);"#"===t[0]&&(t=t.substr(1)),t.length>0&&anchorLink(t)}function anchorLink(t){var e=document.getElementById(t);if(e){for(var r=e;r;){if(r.classList.contains("collapse")&&!r.classList.contains("show"))new bootstrap.Collapse(r,{toggle:!0});if(r.classList.contains("tab-pane")){var n=document.querySelector('a[href="#'+r.id+'"]');if(n)new bootstrap.Tab(n).show()}if("tab"===r.getAttribute("role"))new bootstrap.Tab(r).show();r=r.parentElement}setTimeout(function(){var e=document.getElementById(t);e&&(e.scrollIntoView({block:"center",behavior:"smooth"}),setTimeout(function(){flashElement(t)},500))},1e3)}}document.addEventListener("click",function(t){var e=t.target.closest('a[href^="#"]');if(e){if(e.classList.contains("ref-link"))return void t.preventDefault();if(e.getAttribute("data-bs-toggle"))return;t.preventDefault(),history.pushState({},"",e.href)}}),function(){function t(){var t={},e=0;if(document.querySelectorAll("[id]").forEach(function(r){var n=r.id;n&&(t[n]?(e++,r.setAttribute("data-orig-id",n),r.id=n+"__d"+e):t[n]=!0)}),0!==e){var r={};document.querySelectorAll("[data-orig-id]").forEach(function(t){var e=t.getAttribute("data-orig-id");r[e]||(r[e]=[]),r[e].push(t)});var n={};Object.keys(r).forEach(function(t){var e=document.getElementById(t);n[t]=e?[e].concat(r[t]):r[t]}),document.querySelectorAll('a[href^="#"]').forEach(function(t){o(t,"href")}),document.querySelectorAll('[data-bs-target^="#"]').forEach(function(t){o(t,"data-bs-target")}),document.querySelectorAll('[data-bs-parent^="#"]').forEach(function(t){o(t,"data-bs-parent")}),document.querySelectorAll("[aria-controls]").forEach(function(t){i(t,"aria-controls")}),document.querySelectorAll("[aria-labelledby]").forEach(function(t){i(t,"aria-labelledby")}),document.querySelectorAll("[onclick]").forEach(function(t){var e=t.getAttribute("onclick");if(e){var n=!1,o=e.replace(/anchorLink\('([^']+)'\)/g,function(e,o){if(!r[o])return e;var i=a(t,o);return i!==o?(n=!0,"anchorLink('"+i+"')"):e}).replace(/setAnchor\('#([^']+)'\)/g,function(e,o){if(!r[o])return e;var i=a(t,o);return i!==o?(n=!0,"setAnchor('#"+i+"')"):e});n&&t.setAttribute("onclick",o)}})}function a(t,e){var r=n[e];if(!r)return e;for(var a=t.parentElement;a;){for(var o=0;oNo Additional Items
+ {{ " " }}No Additional Items
{%- endif -%}
{%- if schema.array_items_def -%}
Each item of this array must be:
@@ -24,7 +24,7 @@ Tuple Validation
Item at {{ loop.index }} must be:
diff --git a/docgen/json/templates/cyclonedx/section_conditional_subschema.html b/docgen/json/templates/cyclonedx/section_conditional_subschema.html
index e543669d9..01ba04d58 100644
--- a/docgen/json/templates/cyclonedx/section_conditional_subschema.html
+++ b/docgen/json/templates/cyclonedx/section_conditional_subschema.html
@@ -7,7 +7,7 @@
{% set tab_id = schema.kw_if.html_id %}
If
@@ -16,7 +16,7 @@
{% set tab_id = schema.kw_then.html_id %}
Then
@@ -26,7 +26,7 @@
{%- set tab_id = schema.kw_else.html_id -%}
Else
diff --git a/docgen/json/templates/cyclonedx/section_description.html b/docgen/json/templates/cyclonedx/section_description.html
index e0cc3fab5..e5ecc5f34 100644
--- a/docgen/json/templates/cyclonedx/section_description.html
+++ b/docgen/json/templates/cyclonedx/section_description.html
@@ -8,7 +8,7 @@
{{ description }}
diff --git a/docgen/json/templates/cyclonedx/section_examples.html b/docgen/json/templates/cyclonedx/section_examples.html
index 06f485e93..b21809102 100644
--- a/docgen/json/templates/cyclonedx/section_examples.html
+++ b/docgen/json/templates/cyclonedx/section_examples.html
@@ -6,7 +6,7 @@
{%- set example_id = schema.html_id ~ "_ex" ~ loop.index -%}
{%- set example_is_long = example is not description_short -%}
{%- if example_is_long -%}
-
+
{%- endif -%}
{%- if not examples_as_yaml -%}
diff --git a/docgen/json/templates/cyclonedx/section_properties.html b/docgen/json/templates/cyclonedx/section_properties.html
index 222fd8b61..e07c9a445 100644
--- a/docgen/json/templates/cyclonedx/section_properties.html
+++ b/docgen/json/templates/cyclonedx/section_properties.html
@@ -3,10 +3,10 @@
+
\ No newline at end of file
diff --git a/docgen/json/templates/cyclonedx/tabbed_section.html b/docgen/json/templates/cyclonedx/tabbed_section.html
index 81ea390bf..ce3b2dfaa 100644
--- a/docgen/json/templates/cyclonedx/tabbed_section.html
+++ b/docgen/json/templates/cyclonedx/tabbed_section.html
@@ -9,7 +9,7 @@
{%- for node in current_node.array_items -%}
{{ node.definition_name or tab_label ~ " " ~ loop.index }}
{%- if node is deprecated -%}
diff --git a/docgen/proto/gen.sh b/docgen/proto/gen.sh
index 871220fca..93d8ec6f4 100755
--- a/docgen/proto/gen.sh
+++ b/docgen/proto/gen.sh
@@ -24,6 +24,9 @@ SCHEMA_PATH="$(realpath "$THIS_PATH/../../schema")"
DOCS_PATH="$THIS_PATH/docs"
TEMPLATES_PATH="$THIS_PATH/templates"
+# Centralized header injection
+source "$THIS_PATH/../static/inject-header.sh"
+
PROTOC_GEN_DOC_VERSION='1.5.1'
@@ -63,6 +66,11 @@ generate () {
sed -i -e "s/\${quotedTitle}/\"$title\"/g" "$OUT_DIR/$OUT_FILE"
sed -i -e "s/\${title}/$title/g" "$OUT_DIR/$OUT_FILE"
sed -i -e "s/\${version}/$version/g" "$OUT_DIR/$OUT_FILE"
+
+ sed -i -E -e "s#()buf:.+
##g" "$OUT_DIR/$OUT_FILE"
+ sed -i -E -e "s#^buf:[^<\n]+##g" "$OUT_DIR/$OUT_FILE"
+
+ inject_header "$OUT_DIR/$OUT_FILE" "$version" "proto"
}
diff --git a/docgen/proto/templates/html.tmpl b/docgen/proto/templates/html.tmpl
index 0e11b9066..246852908 100644
--- a/docgen/proto/templates/html.tmpl
+++ b/docgen/proto/templates/html.tmpl
@@ -22,33 +22,13 @@ https://github.com/pseudomuto/protoc-gen-doc/blob/master/resources/html.tmpl
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
+
${title}
-
Table of Contents
-
-
-
{{range .Files}}
{{$file_name := .Name}}
{{$file_package := .Package}}
-
-
Package: {{.Package}} Top
-
- {{p .Description}}
{{range .Messages}}
-
{{.LongName}}
- {{p .Description}}
-
- {{if .HasFields}}
-
-
- Field Type Label Description
-
-
- {{range .Fields}}
-
- {{.Name}}
- {{.LongType}}
- {{.Label}}
- {{if (index .Options "deprecated"|default false)}}Deprecated. {{end}}{{.Description}} {{if .DefaultValue}}Default: {{.DefaultValue}}{{end}}
-
- {{end}}
-
-
-
- {{$message := .}}
- {{- range .FieldOptions}}
- {{$option := .}}
- {{if eq . "validator.field" "validate.rules" }}
-
Validated Fields
-
-
-
- Field
- Validations
-
-
-
- {{range $message.FieldsWithOption .}}
-
- {{.Name}}
-
-
- {{range (.Option $option).Rules}}
- {{.Name}}: {{.Value}}
- {{end}}
-
-
-
- {{end}}
-
-
- {{else}}
-
Fields with {{.}} option
-
-
-
- Name
- Option
-
-
-
- {{range $message.FieldsWithOption .}}
-
- {{.Name}}
- {{ printf "%+v" (.Option $option)}}
-
- {{end}}
-
-
- {{end}}
- {{end -}}
- {{end}}
-
- {{if .HasExtensions}}
-
-
-
- Extension Type Base Number Description
-
-
- {{range .Extensions}}
-
- {{.Name}}
- {{.LongType}}
- {{.ContainingLongType}}
- {{.Number}}
- {{.Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}}
-
- {{end}}
-
-
- {{end}}
+
+
+
+
+
+ {{p .Description}}
+
+ {{if .HasFields}}
+
+
+ Field Type Label Description
+
+
+ {{range .Fields}}
+
+ {{.Name}}
+ {{.LongType}}
+ {{.Label}}
+ {{if (index .Options "deprecated"|default false)}}Deprecated. {{end}}{{.Description}} {{if .DefaultValue}}Default: {{.DefaultValue}}{{end}}
+
+ {{end}}
+
+
+
+ {{$message := .}}
+ {{- range .FieldOptions}}
+ {{$option := .}}
+ {{if eq . "validator.field" "validate.rules" }}
+
Validated Fields
+
+
+
+ Field
+ Validations
+
+
+
+ {{range $message.FieldsWithOption .}}
+
+ {{.Name}}
+
+
+ {{range (.Option $option).Rules}}
+ {{.Name}}: {{.Value}}
+ {{end}}
+
+
+
+ {{end}}
+
+
+ {{else}}
+
Fields with {{.}} option
+
+
+
+ Name
+ Option
+
+
+
+ {{range $message.FieldsWithOption .}}
+
+ {{.Name}}
+ {{ printf "%+v" (.Option $option)}}
+
+ {{end}}
+
+
+ {{end}}
+ {{end -}}
+ {{end}}
+
+ {{if .HasExtensions}}
+
+
+
+ Extension Type Base Number Description
+
+
+ {{range .Extensions}}
+
+ {{.Name}}
+ {{.LongType}}
+ {{.ContainingLongType}}
+ {{.Number}}
+ {{.Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}}
+
+ {{end}}
+
+
+ {{end}}
+
+
+
+
{{end}}
{{range .Enums}}
-
{{.LongName}}
- {{p .Description}}
-
-
- Name Number Description
-
-
- {{range .Values}}
-
- {{.Name}}
- {{.Number}}
- {{.Description}}
-
- {{end}}
-
-
+
+
+
+
+
+ {{p .Description}}
+
+
+ Name Number Description
+
+
+ {{range .Values}}
+
+ {{.Name}}
+ {{.Number}}
+ {{.Description}}
+
+ {{end}}
+
+
+
+
+
+
{{end}}
{{if .HasExtensions}}
-
File-level Extensions
-
-
- Extension Type Base Number Description
-
-
- {{range .Extensions}}
-
- {{.Name}}
- {{.LongType}}
- {{.ContainingLongType}}
- {{.Number}}
- {{.Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}}
-
- {{end}}
-
-
+
+
+
+
+
+
+
+ Extension Type Base Number Description
+
+
+ {{range .Extensions}}
+
+ {{.Name}}
+ {{.LongType}}
+ {{.ContainingLongType}}
+ {{.Number}}
+ {{.Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}}
+
+ {{end}}
+
+
+
+
+
+
{{end}}
{{range .Services}}
-
{{.Name}}
- {{p .Description}}
-
-
- Method Name Request Type Response Type Description
-
-
- {{range .Methods}}
-
- {{.Name}}
- {{.RequestLongType}} {{if .RequestStreaming}} stream{{end}}
- {{.ResponseLongType}} {{if .ResponseStreaming}} stream{{end}}
- {{.Description}}
-
- {{end}}
-
-
-
- {{$service := .}}
- {{- range .MethodOptions}}
- {{$option := .}}
- {{if eq . "google.api.http"}}
-
Methods with HTTP bindings
-
-
-
- Method Name
- Method
- Pattern
- Body
-
-
-
- {{range $service.MethodsWithOption .}}
- {{$name := .Name}}
- {{range (.Option $option).Rules}}
-
- {{$name}}
- {{.Method}}
- {{.Pattern}}
- {{.Body}}
-
- {{end}}
- {{end}}
-
-
- {{else}}
-
Methods with {{.}} option
-
-
-
- Method Name
- Option
-
-
-
- {{range $service.MethodsWithOption .}}
-
- {{.Name}}
- {{ printf "%+v" (.Option $option)}}
-
- {{end}}
-
-
- {{end}}
- {{end -}}
+
+
+
+
+
+ {{p .Description}}
+
+
+ Method Name Request Type Response Type Description
+
+
+ {{range .Methods}}
+
+ {{.Name}}
+ {{.RequestLongType}} {{if .RequestStreaming}} stream{{end}}
+ {{.ResponseLongType}} {{if .ResponseStreaming}} stream{{end}}
+ {{.Description}}
+
+ {{end}}
+
+
+
+ {{$service := .}}
+ {{- range .MethodOptions}}
+ {{$option := .}}
+ {{if eq . "google.api.http"}}
+
Methods with HTTP bindings
+
+
+
+ Method Name
+ Method
+ Pattern
+ Body
+
+
+
+ {{range $service.MethodsWithOption .}}
+ {{$name := .Name}}
+ {{range (.Option $option).Rules}}
+
+ {{$name}}
+ {{.Method}}
+ {{.Pattern}}
+ {{.Body}}
+
+ {{end}}
+ {{end}}
+
+
+ {{else}}
+
Methods with {{.}} option
+
+
+
+ Method Name
+ Option
+
+
+
+ {{range $service.MethodsWithOption .}}
+
+ {{.Name}}
+ {{ printf "%+v" (.Option $option)}}
+
+ {{end}}
+
+
+ {{end}}
+ {{end -}}
+
+
+
+
{{end}}
{{end}}
-
Scalar Value Types
-
-
- .proto Type Notes C++ Java Python Go C# PHP Ruby
-
-
- {{range .Scalars}}
-
- {{.ProtoType}}
- {{.Notes}}
- {{.CppType}}
- {{.JavaType}}
- {{.PythonType}}
- {{.GoType}}
- {{.CSharp}}
- {{.PhpType}}
- {{.RubyType}}
-
- {{end}}
-
-
+
+
+
+
+
+
+
+ .proto Type Notes C++ Java Python Go C# PHP Ruby
+
+
+ {{range .Scalars}}
+
+ {{.ProtoType}}
+ {{.Notes}}
+ {{.CppType}}
+ {{.JavaType}}
+ {{.PythonType}}
+ {{.GoType}}
+ {{.CSharp}}
+ {{.PhpType}}
+ {{.RubyType}}
+
+ {{end}}
+
+
+
+
+
+
+
+