From 7751c9eaf18822e3a2d62e9579bbf57ef89f9dca Mon Sep 17 00:00:00 2001 From: Michael Taylor Date: Thu, 30 Jul 2026 05:27:48 +0000 Subject: [PATCH 1/3] =?UTF-8?q?ci(docs.dig.net):=20anchor=20=C2=A72.4a=20P?= =?UTF-8?q?rettier=20format=20gate=20(#1042)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Anchor commit — implementation in progress. Bumps 0.7.2 -> 0.7.3 (patch). Co-Authored-By: Claude --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 622b9e0..0395a6a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "docs-dig-net", - "version": "0.7.2", + "version": "0.7.3", "private": true, "scripts": { "docusaurus": "docusaurus", From ace6879b542b63649448570d9b22a0d14532ebf1 Mon Sep 17 00:00:00 2001 From: Michael Taylor Date: Thu, 30 Jul 2026 05:30:55 +0000 Subject: [PATCH 2/3] =?UTF-8?q?ci(docs.dig.net):=20add=20Prettier=20format?= =?UTF-8?q?=20gate=20(=C2=A72.4a)=20(#1042)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire prettier + eslint-config-prettier so format:check runs in the PR gate before lint/build. Scoped to the JS/TS code surface only (src/, scripts/, tests/, root configs) via .prettierignore — docs content, i18n translations, and generated artifacts are explicitly excluded so this never touches the 1155+ md/mdx files across 14 locales. lint tightened to --max-warnings 0 to match the other gates. Co-Authored-By: Claude --- .github/workflows/deploy.yml | 6 ++++-- .prettierignore | 38 ++++++++++++++++++++++++++++++++++++ .prettierrc | 7 +++++++ eslint.config.mjs | 4 ++++ package-lock.json | 38 ++++++++++++++++++++++++++++++++++-- package.json | 6 +++++- 6 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ea570d1..524ea84 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -41,8 +41,10 @@ jobs: cache: npm cache-dependency-path: package-lock.json - run: npm ci - # Static gates (§2.4a): types must check and lint must be clean before we - # spend time on the full multi-locale build + browser suites. + # Static gates (§2.4a): format, types, and lint must all be clean before + # we spend time on the full multi-locale build + browser suites. + - name: format check (prettier) + run: npm run format:check - run: npm run typecheck # tsc --noEmit across the TS surface - run: npm run lint # eslint . — zero errors (§2.4a/§6.4) # onBrokenLinks/onBrokenAnchors: "throw" (docusaurus.config.ts) means this diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..21304e2 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,38 @@ +# Prettier formats the CODE surface only (src/**, scripts/**, tests/**, root +# configs) — it must NEVER touch docs content or i18n translations. Reformatting +# 1155+ md/mdx files across 14 locales would be an enormous, unsafe diff that +# violates the ecosystem's docs-integrity (§4.3) and i18n (§6.6) rules. + +# CI workflows are YAML, not part of the JS/TS code surface this gate covers +.github/ + +# build/tooling output +node_modules/ +dist/ +build/ +.docusaurus/ +coverage/ +playwright-report/ +test-results/ + +# docs content — every locale, every format +docs/ +i18n/ +blog/ +**/*.md +**/*.mdx + +# generated/static artifacts (machine specs, sitemaps, llms.txt, openrpc, etc.) +static/ +**/knowledge-graph.json +**/machine-specs.json +**/openrpc*.json +**/dht-methods.json +**/error-codes.json +**/llms.txt +**/sitemap*.xml +**/robots.txt + +# lockfiles + changelog (generated, never hand-formatted) +package-lock.json +CHANGELOG.md diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..baee92c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "printWidth": 100, + "singleQuote": false, + "trailingComma": "all", + "semi": true, + "tabWidth": 2 +} diff --git a/eslint.config.mjs b/eslint.config.mjs index 9dea595..2ae29ab 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -10,6 +10,7 @@ import globals from "globals"; import reactHooks from "eslint-plugin-react-hooks"; import jsxA11y from "eslint-plugin-jsx-a11y"; import tseslint from "typescript-eslint"; +import prettierConfig from "eslint-config-prettier"; export default tseslint.config( { @@ -65,4 +66,7 @@ export default tseslint.config( "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], }, }, + // Must stay LAST: disables ESLint's stylistic rules so it never fights + // Prettier's formatting opinions (§2.4a — format and lint are separate gates). + prettierConfig, ); diff --git a/package-lock.json b/package-lock.json index 58338ad..6a32227 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "docs-dig-net", - "version": "0.4.6", + "version": "0.7.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "docs-dig-net", - "version": "0.4.6", + "version": "0.7.3", "dependencies": { "@docusaurus/core": "3.5.2", "@docusaurus/plugin-client-redirects": "^3.5.2", @@ -27,11 +27,13 @@ "@playwright/test": "^1.61.1", "autoprefixer": "^10.4.20", "eslint": "^9.39.5", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react-hooks": "^5.2.0", "globals": "^15.15.0", "jsdom": "^29.1.1", "postcss": "^8.4.47", + "prettier": "^3.9.6", "tailwindcss": "^3.4.14", "typescript": "~5.5.2", "typescript-eslint": "^8.64.0" @@ -7471,6 +7473,22 @@ } } }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", @@ -14696,6 +14714,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", diff --git a/package.json b/package.json index 0395a6a..3f99308 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,9 @@ "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", "typecheck": "tsc", - "lint": "eslint .", + "lint": "eslint . --max-warnings 0", + "format": "prettier --write .", + "format:check": "prettier --check .", "test:unit": "node --test tests/unit/", "test:e2e": "playwright test", "test:a11y": "playwright test tests/e2e/a11y.spec.ts tests/e2e/mobile-nav.spec.ts tests/e2e/aria-snapshot.spec.ts tests/e2e/keyboard-nav.spec.ts", @@ -45,11 +47,13 @@ "@playwright/test": "^1.61.1", "autoprefixer": "^10.4.20", "eslint": "^9.39.5", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react-hooks": "^5.2.0", "globals": "^15.15.0", "jsdom": "^29.1.1", "postcss": "^8.4.47", + "prettier": "^3.9.6", "tailwindcss": "^3.4.14", "typescript": "~5.5.2", "typescript-eslint": "^8.64.0" From 07eff030e4c801df2be88af1cb378b79f3fdd6c0 Mon Sep 17 00:00:00 2001 From: Michael Taylor Date: Thu, 30 Jul 2026 05:31:10 +0000 Subject: [PATCH 3/3] style(docs.dig.net): prettier --write code surface (#1042) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Isolated formatting-only sweep over src/, scripts/, tests/, and root config files per the new .prettierrc — no behavior change. Co-Authored-By: Claude --- babel.config.js | 2 +- commitlint.config.mjs | 26 +- docusaurus.config.ts | 3 +- postcss.config.js | 2 +- scripts/dig-spec.mjs | 1292 ++++++++++++++------- scripts/gen-hreflang-sitemaps.mjs | 22 +- scripts/gen-knowledge-graph.mjs | 118 +- scripts/gen-machine-specs.mjs | 114 +- sidebars.ts | 243 ++-- src/css/custom.css | 53 +- src/pages/index.module.css | 123 +- src/pages/index.tsx | 169 ++- src/theme/DocItem/Footer/index.tsx | 43 +- src/theme/NavbarItem/HtmlNavbarItem.tsx | 12 +- src/theme/NotFound/index.tsx | 6 +- tailwind.config.js | 4 +- tests/e2e/aria-snapshot.spec.ts | 4 +- tests/e2e/keyboard-nav.spec.ts | 8 +- tests/e2e/mobile-nav.spec.ts | 12 +- tests/e2e/seo-meta.spec.ts | 13 +- tests/unit/gen-hreflang-sitemaps.test.mjs | 54 +- tests/unit/gen-knowledge-graph.test.mjs | 56 +- tests/unit/install-path-lint.test.mjs | 13 +- tests/unit/locale-resolver.test.mjs | 60 +- tests/unit/urn-format-lint.test.mjs | 3 +- tests/unit/virtual-screen-reader.test.mjs | 20 +- 26 files changed, 1601 insertions(+), 874 deletions(-) diff --git a/babel.config.js b/babel.config.js index e00595d..bfd75db 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,3 +1,3 @@ module.exports = { - presets: [require.resolve('@docusaurus/core/lib/babel/preset')], + presets: [require.resolve("@docusaurus/core/lib/babel/preset")], }; diff --git a/commitlint.config.mjs b/commitlint.config.mjs index 6ad466d..2328968 100644 --- a/commitlint.config.mjs +++ b/commitlint.config.mjs @@ -5,15 +5,27 @@ // (fix -> patch, feat -> minor, ! / BREAKING CHANGE -> major). // Enforced in CI by .github/workflows/commitlint.yml (wagoid/commitlint-github-action). export default { - extends: ['@commitlint/config-conventional'], + extends: ["@commitlint/config-conventional"], rules: { - 'type-enum': [ + "type-enum": [ 2, - 'always', - ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore', 'revert'], + "always", + [ + "feat", + "fix", + "docs", + "style", + "refactor", + "perf", + "test", + "build", + "ci", + "chore", + "revert", + ], ], - 'subject-case': [0], // allow any subject casing (proper nouns, scheme literals like chia://) - 'body-max-line-length': [0], // long bodies (URLs, logs) are fine - 'footer-max-line-length': [0], + "subject-case": [0], // allow any subject casing (proper nouns, scheme literals like chia://) + "body-max-line-length": [0], // long bodies (URLs, logs) are fine + "footer-max-line-length": [0], }, }; diff --git a/docusaurus.config.ts b/docusaurus.config.ts index ee84e12..9814013 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -4,8 +4,7 @@ import type * as Preset from "@docusaurus/preset-classic"; const config: Config = { title: "DIG Network", - tagline: - "A Proof-of-Stake Layer 2 on Chia — developer docs for the network and its primitives.", + tagline: "A Proof-of-Stake Layer 2 on Chia — developer docs for the network and its primitives.", favicon: "img/favicon.png", url: "https://docs.dig.net", diff --git a/postcss.config.js b/postcss.config.js index a589075..12a703d 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -2,5 +2,5 @@ module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, - } + }, }; diff --git a/scripts/dig-spec.mjs b/scripts/dig-spec.mjs index b31b8de..61dd3b0 100644 --- a/scripts/dig-spec.mjs +++ b/scripts/dig-spec.mjs @@ -25,209 +25,317 @@ * ecosystem agent-friendly plan. */ -export const SITE = 'https://docs.dig.net'; -export const RPC_ENDPOINT = 'https://rpc.dig.net'; +export const SITE = "https://docs.dig.net"; +export const RPC_ENDPOINT = "https://rpc.dig.net"; /* ------------------------------------------------------------------ * * Reusable JSON Schemas for the dig RPC (OpenRPC `components.schemas`) * ------------------------------------------------------------------ */ const hex64 = { - type: 'string', - pattern: '^[0-9a-f]{64}$', - description: '32 bytes as 64 lower-case hex characters.', + type: "string", + pattern: "^[0-9a-f]{64}$", + description: "32 bytes as 64 lower-case hex characters.", }; const rootRef = { oneOf: [ - { type: 'string', pattern: '^[0-9a-f]{64}$' }, - { type: 'string', const: 'latest' }, + { type: "string", pattern: "^[0-9a-f]{64}$" }, + { type: "string", const: "latest" }, ], description: 'A generation root: 64 hex of one capsule, or the literal "latest" for the newest confirmed generation.', }; export const schemas = { - StoreId: { ...hex64, title: 'StoreId' }, - RetrievalKey: { ...hex64, title: 'RetrievalKey', description: 'retrieval_key = sha256(urn).' }, - Root: { title: 'Root', ...rootRef }, + StoreId: { ...hex64, title: "StoreId" }, + RetrievalKey: { ...hex64, title: "RetrievalKey", description: "retrieval_key = sha256(urn)." }, + Root: { title: "Root", ...rootRef }, ChunkObject: { - title: 'ChunkObject', - type: 'object', + title: "ChunkObject", + type: "object", description: - 'The streaming chunk object returned by every byte-bearing method (dig.getContent, dig.getCapsule, dig.getManifest). Reassemble until `complete`, verify `inclusion_proof` over the whole ciphertext against the CALLER-supplied chain-anchored `root`, split by `chunk_lens`, then AES-256-GCM-SIV-open each chunk. There is NO `decoy` field on the wire: a blind miss is the capsule\'s own indistinguishable, non-verifying response, discovered client-side by inclusion-proof failure and/or decryption-tag failure.', + "The streaming chunk object returned by every byte-bearing method (dig.getContent, dig.getCapsule, dig.getManifest). Reassemble until `complete`, verify `inclusion_proof` over the whole ciphertext against the CALLER-supplied chain-anchored `root`, split by `chunk_lens`, then AES-256-GCM-SIV-open each chunk. There is NO `decoy` field on the wire: a blind miss is the capsule's own indistinguishable, non-verifying response, discovered client-side by inclusion-proof failure and/or decryption-tag failure.", required: [ - 'ciphertext', - 'total_length', - 'offset', - 'length', - 'complete', - 'next_offset', - 'inclusion_proof', - 'root', + "ciphertext", + "total_length", + "offset", + "length", + "complete", + "next_offset", + "inclusion_proof", + "root", ], properties: { - ciphertext: { type: 'string', contentEncoding: 'base64', description: "This window's bytes, standard base64." }, - total_length: { type: 'integer', minimum: 0, description: "The full resource ciphertext length before windowing." }, - offset: { type: 'integer', minimum: 0, description: "Byte offset where this window begins in the full object." }, - length: { type: 'integer', minimum: 0, description: "This window's byte length (= decoded ciphertext length)." }, - complete: { type: 'boolean', description: 'true when offset + length >= total_length.' }, - next_offset: { type: ['integer', 'null'], description: 'The offset to request next, or null when complete.' }, + ciphertext: { + type: "string", + contentEncoding: "base64", + description: "This window's bytes, standard base64.", + }, + total_length: { + type: "integer", + minimum: 0, + description: "The full resource ciphertext length before windowing.", + }, + offset: { + type: "integer", + minimum: 0, + description: "Byte offset where this window begins in the full object.", + }, + length: { + type: "integer", + minimum: 0, + description: "This window's byte length (= decoded ciphertext length).", + }, + complete: { type: "boolean", description: "true when offset + length >= total_length." }, + next_offset: { + type: ["integer", "null"], + description: "The offset to request next, or null when complete.", + }, inclusion_proof: { - type: ['string', 'null'], - contentEncoding: 'base64', - description: 'Base64 merkle inclusion proof for the WHOLE resource, relayed verbatim from the capsule. Sent on every window for getContent/getManifest; empty string / null for getCapsule.', + type: ["string", "null"], + contentEncoding: "base64", + description: + "Base64 merkle inclusion proof for the WHOLE resource, relayed verbatim from the capsule. Sent on every window for getContent/getManifest; empty string / null for getCapsule.", }, chunk_lens: { - type: 'array', - items: { type: 'integer', minimum: 0 }, - description: 'Per-chunk CIPHERTEXT byte lengths of the full resource, in order. REQUIRED to split + decrypt a multi-chunk resource. Emitted on the FIRST window only (offset == 0); empty/absent ⇒ a single chunk = the whole ciphertext.', + type: "array", + items: { type: "integer", minimum: 0 }, + description: + "Per-chunk CIPHERTEXT byte lengths of the full resource, in order. REQUIRED to split + decrypt a multi-chunk resource. Emitted on the FIRST window only (offset == 0); empty/absent ⇒ a single chunk = the whole ciphertext.", + }, + root: { + ...hex64, + description: "The resolved generation root (hex). Pin subsequent chunks to it.", + }, + program_hash: { + ...hex64, + description: "The served .dig's sha256 (hex) — the on-chain program identity.", }, - root: { ...hex64, description: 'The resolved generation root (hex). Pin subsequent chunks to it.' }, - program_hash: { ...hex64, description: "The served .dig's sha256 (hex) — the on-chain program identity." }, }, }, MetadataManifest: { - title: 'MetadataManifest', - type: 'object', - description: "The store's plaintext, ungated metadata manifest, embedded in the compiled module (dig-store §8.4).", + title: "MetadataManifest", + type: "object", + description: + "The store's plaintext, ungated metadata manifest, embedded in the compiled module (dig-store §8.4).", properties: { - schema_version: { type: 'integer' }, - name: { type: 'string' }, - version: { type: 'string' }, - description: { type: 'string' }, + schema_version: { type: "integer" }, + name: { type: "string" }, + version: { type: "string" }, + description: { type: "string" }, authors: { - type: 'array', + type: "array", items: { - type: 'object', - properties: { name: { type: 'string' }, handle: { type: 'string' }, contact: { type: 'string' } }, + type: "object", + properties: { + name: { type: "string" }, + handle: { type: "string" }, + contact: { type: "string" }, + }, }, }, - license: { type: 'string' }, - homepage: { type: 'string' }, - repository: { type: 'string' }, - keywords: { type: 'array', items: { type: 'string' } }, - categories: { type: 'array', items: { type: 'string' } }, - icon: { type: 'string' }, - content_type: { type: 'string' }, - links: { type: 'object' }, - custom: { type: 'object' }, + license: { type: "string" }, + homepage: { type: "string" }, + repository: { type: "string" }, + keywords: { type: "array", items: { type: "string" } }, + categories: { type: "array", items: { type: "string" } }, + icon: { type: "string" }, + content_type: { type: "string" }, + links: { type: "object" }, + custom: { type: "object" }, }, }, CapsuleEntry: { - title: 'CapsuleEntry', - type: 'object', - description: 'One confirmed capsule (anchored generation) in a store lineage.', - required: ['seq', 'root'], + title: "CapsuleEntry", + type: "object", + description: "One confirmed capsule (anchored generation) in a store lineage.", + required: ["seq", "root"], properties: { - seq: { type: 'integer', minimum: 0, description: 'Monotonic generation number.' }, - root: { ...hex64, description: 'The generation root passed to the byte methods.' }, + seq: { type: "integer", minimum: 0, description: "Monotonic generation number." }, + root: { ...hex64, description: "The generation root passed to the byte methods." }, program_hash: { ...hex64 }, - coin_id: { ...hex64, description: 'The anchoring spend coin id.' }, - anchored_at: { type: 'integer', description: 'Unix timestamp of the anchoring spend.' }, + coin_id: { ...hex64, description: "The anchoring spend coin id." }, + anchored_at: { type: "integer", description: "Unix timestamp of the anchoring spend." }, }, }, PeerId: { - type: 'string', - pattern: '^[0-9a-f]{64}$', - title: 'PeerId', - description: 'A DIG Node peer identity: SHA-256 of the peer\'s TLS SubjectPublicKeyInfo DER, as 64 lower-case hex characters (32 bytes).', + type: "string", + pattern: "^[0-9a-f]{64}$", + title: "PeerId", + description: + "A DIG Node peer identity: SHA-256 of the peer's TLS SubjectPublicKeyInfo DER, as 64 lower-case hex characters (32 bytes).", }, CandidateAddress: { - title: 'CandidateAddress', - type: 'object', - description: 'One address at which a peer may be reachable, tagged by how it was learned. Peers dial candidates most-direct-first.', - required: ['host', 'port', 'kind'], + title: "CandidateAddress", + type: "object", + description: + "One address at which a peer may be reachable, tagged by how it was learned. Peers dial candidates most-direct-first.", + required: ["host", "port", "kind"], properties: { - host: { type: 'string', description: 'IP literal or hostname.' }, - port: { type: 'integer', minimum: 0, maximum: 65535, description: 'The peer\'s P2P port (default 9444).' }, + host: { type: "string", description: "IP literal or hostname." }, + port: { + type: "integer", + minimum: 0, + maximum: 65535, + description: "The peer's P2P port (default 9444).", + }, kind: { - type: 'string', - enum: ['direct', 'reflexive', 'mapped', 'relay'], - description: 'How the address was learned: advertised/observed direct, STUN reflexive, UPnP/NAT-PMP/PCP mapped, or relay-reachable.', + type: "string", + enum: ["direct", "reflexive", "mapped", "relay"], + description: + "How the address was learned: advertised/observed direct, STUN reflexive, UPnP/NAT-PMP/PCP mapped, or relay-reachable.", }, }, }, PeerRecord: { - title: 'PeerRecord', - type: 'object', - description: 'A known peer with its identity and candidate addresses (the peer-exchange record).', - required: ['peer_id', 'addresses'], + title: "PeerRecord", + type: "object", + description: + "A known peer with its identity and candidate addresses (the peer-exchange record).", + required: ["peer_id", "addresses"], properties: { - peer_id: { $ref: '#/components/schemas/PeerId' }, - addresses: { type: 'array', items: { $ref: '#/components/schemas/CandidateAddress' }, description: 'The peer\'s candidate addresses.' }, - network_id: { type: 'string', description: 'The network the peer is on (e.g. DIG_MAINNET).' }, - last_seen: { type: 'integer', description: 'Unix seconds this peer was last seen.' }, - via: { type: 'string', enum: ['direct', 'relay'], description: 'How this node currently reaches the peer.' }, + peer_id: { $ref: "#/components/schemas/PeerId" }, + addresses: { + type: "array", + items: { $ref: "#/components/schemas/CandidateAddress" }, + description: "The peer's candidate addresses.", + }, + network_id: { type: "string", description: "The network the peer is on (e.g. DIG_MAINNET)." }, + last_seen: { type: "integer", description: "Unix seconds this peer was last seen." }, + via: { + type: "string", + enum: ["direct", "relay"], + description: "How this node currently reaches the peer.", + }, }, }, AvailabilityItem: { - title: 'AvailabilityItem', - type: 'object', + title: "AvailabilityItem", + type: "object", description: - 'One item to check in a dig.getAvailability batch. Granularity is inferred from the fields present: store_id only = has_store; + root = has_root (the capsule store_id:root); + retrieval_key = has_resource within the capsule.', - required: ['store_id'], + "One item to check in a dig.getAvailability batch. Granularity is inferred from the fields present: store_id only = has_store; + root = has_root (the capsule store_id:root); + retrieval_key = has_resource within the capsule.", + required: ["store_id"], properties: { - store_id: { $ref: '#/components/schemas/StoreId' }, - root: { ...hex64, description: 'Optional generation root — narrows the check to that capsule.' }, - retrieval_key: { $ref: '#/components/schemas/RetrievalKey' }, + store_id: { $ref: "#/components/schemas/StoreId" }, + root: { + ...hex64, + description: "Optional generation root — narrows the check to that capsule.", + }, + retrieval_key: { $ref: "#/components/schemas/RetrievalKey" }, }, }, AvailabilityAnswer: { - title: 'AvailabilityAnswer', - type: 'object', + title: "AvailabilityAnswer", + type: "object", description: - 'One answer in a dig.getAvailability result, positionally aligned with the queried items. Reports whether the peer holds the item plus (where cheap) planning metadata so the caller can partition ranges without a probe fetch.', - required: ['available'], + "One answer in a dig.getAvailability result, positionally aligned with the queried items. Reports whether the peer holds the item plus (where cheap) planning metadata so the caller can partition ranges without a probe fetch.", + required: ["available"], properties: { - available: { type: 'boolean', description: 'Whether the peer holds the queried item.' }, - roots: { type: 'array', items: { ...hex64 }, description: 'STORE granularity: the generation roots the peer currently holds for the store, newest-first.' }, - total_length: { type: 'integer', minimum: 0, description: 'ROOT/RESOURCE granularity: the resource/capsule ciphertext length.' }, - chunk_count: { type: 'integer', minimum: 0, description: 'ROOT/RESOURCE granularity: the number of chunks (plan ranges against this).' }, - complete: { type: 'boolean', description: 'Whether the peer holds the FULL resource/capsule (true) or only part of it (false); a partial holder still serves the ranges it has.' }, + available: { type: "boolean", description: "Whether the peer holds the queried item." }, + roots: { + type: "array", + items: { ...hex64 }, + description: + "STORE granularity: the generation roots the peer currently holds for the store, newest-first.", + }, + total_length: { + type: "integer", + minimum: 0, + description: "ROOT/RESOURCE granularity: the resource/capsule ciphertext length.", + }, + chunk_count: { + type: "integer", + minimum: 0, + description: "ROOT/RESOURCE granularity: the number of chunks (plan ranges against this).", + }, + complete: { + type: "boolean", + description: + "Whether the peer holds the FULL resource/capsule (true) or only part of it (false); a partial holder still serves the ranges it has.", + }, }, }, RangeFrame: { - title: 'RangeFrame', - type: 'object', + title: "RangeFrame", + type: "object", description: - 'One frame of a streamed dig.fetchRange response. Data is delivered as an ordered stream of these frames over a logical stream of the multiplexed peer transport — the caller reads incrementally with backpressure and reassembles by `offset`. Frame metadata divides in two by whether it scales with the resource: the FIXED-SIZE identity set (root, total_length, chunk_count, plus chunk_index when the window is chunk-aligned) rides EVERY frame, so a wrong-generation holder is detected as the first bytes arrive; the RESOURCE-SCALING layout set (chunk_lens, inclusion_proof) rides the first frame or a paged prologue ONCE per range stream, each page located by chunk_lens_offset, and is omitted entirely when the caller sets skip_layout. See https://docs.dig.net/docs/protocol/peer-network#range.', - required: ['offset', 'length', 'bytes', 'complete'], + "One frame of a streamed dig.fetchRange response. Data is delivered as an ordered stream of these frames over a logical stream of the multiplexed peer transport — the caller reads incrementally with backpressure and reassembles by `offset`. Frame metadata divides in two by whether it scales with the resource: the FIXED-SIZE identity set (root, total_length, chunk_count, plus chunk_index when the window is chunk-aligned) rides EVERY frame, so a wrong-generation holder is detected as the first bytes arrive; the RESOURCE-SCALING layout set (chunk_lens, inclusion_proof) rides the first frame or a paged prologue ONCE per range stream, each page located by chunk_lens_offset, and is omitted entirely when the caller sets skip_layout. See https://docs.dig.net/docs/protocol/peer-network#range.", + required: ["offset", "length", "bytes", "complete"], properties: { - offset: { type: 'integer', minimum: 0, description: 'This frame\'s start offset within the requested range (bytes into the resource ciphertext).' }, - length: { type: 'integer', minimum: 0, description: 'This frame\'s byte length (= decoded `bytes` length). At most MAX_RANGE_FRAME_PAYLOAD (32768) — the cap a serve path splits its stream on.' }, - bytes: { type: 'string', contentEncoding: 'base64', description: 'This frame\'s ciphertext bytes, standard base64.' }, - complete: { type: 'boolean', description: 'true when this is the final frame of the requested range.' }, - total_length: { type: 'integer', minimum: 0, description: 'EVERY FRAME (identity set): the full resource ciphertext length — plan ranges against it, and with `root` it detects a holder serving a different generation. A chunk_lens array whose entries do not sum to this value is rejected.' }, - chunk_count: { type: 'integer', minimum: 0, description: 'EVERY FRAME (identity set): the number of chunks in the WHOLE resource — the entry count a reassembled chunk_lens must reach.' }, - chunk_index: { type: 'integer', minimum: 0, description: 'EVERY FRAME (identity set) when the frame\'s window is chunk-aligned: index into chunk_lens of this frame\'s first chunk.' }, - root: { ...hex64, description: 'EVERY FRAME (identity set): the generation root this range was served from. Advisory echo — verify against the CALLER\'s chain-anchored root, never this field.' }, + offset: { + type: "integer", + minimum: 0, + description: + "This frame's start offset within the requested range (bytes into the resource ciphertext).", + }, + length: { + type: "integer", + minimum: 0, + description: + "This frame's byte length (= decoded `bytes` length). At most MAX_RANGE_FRAME_PAYLOAD (32768) — the cap a serve path splits its stream on.", + }, + bytes: { + type: "string", + contentEncoding: "base64", + description: "This frame's ciphertext bytes, standard base64.", + }, + complete: { + type: "boolean", + description: "true when this is the final frame of the requested range.", + }, + total_length: { + type: "integer", + minimum: 0, + description: + "EVERY FRAME (identity set): the full resource ciphertext length — plan ranges against it, and with `root` it detects a holder serving a different generation. A chunk_lens array whose entries do not sum to this value is rejected.", + }, + chunk_count: { + type: "integer", + minimum: 0, + description: + "EVERY FRAME (identity set): the number of chunks in the WHOLE resource — the entry count a reassembled chunk_lens must reach.", + }, + chunk_index: { + type: "integer", + minimum: 0, + description: + "EVERY FRAME (identity set) when the frame's window is chunk-aligned: index into chunk_lens of this frame's first chunk.", + }, + root: { + ...hex64, + description: + "EVERY FRAME (identity set): the generation root this range was served from. Advisory echo — verify against the CALLER's chain-anchored root, never this field.", + }, chunk_lens: { - type: 'array', - items: { type: 'integer', minimum: 0 }, - description: 'ONCE PER RANGE STREAM (layout set): per-chunk ciphertext lengths of the WHOLE resource, in order (identical to the dig RPC chunk_lens). A DECRYPT input, not a verify input — per-chunk AES-256-GCM-SIV needs the entire array and a reader rejects one whose entries do not sum to total_length, so it is never sliced to a frame\'s own chunks. It rides the first frame when it fits (at most MAX_FIRST_FRAME_CHUNK_LENS = 2486 entries), otherwise a paged prologue of at most MAX_CHUNK_LENS_PER_FRAME = 2048 entries per page, each page located by chunk_lens_offset. Omitted when the caller sets skip_layout.', + type: "array", + items: { type: "integer", minimum: 0 }, + description: + "ONCE PER RANGE STREAM (layout set): per-chunk ciphertext lengths of the WHOLE resource, in order (identical to the dig RPC chunk_lens). A DECRYPT input, not a verify input — per-chunk AES-256-GCM-SIV needs the entire array and a reader rejects one whose entries do not sum to total_length, so it is never sliced to a frame's own chunks. It rides the first frame when it fits (at most MAX_FIRST_FRAME_CHUNK_LENS = 2486 entries), otherwise a paged prologue of at most MAX_CHUNK_LENS_PER_FRAME = 2048 entries per page, each page located by chunk_lens_offset. Omitted when the caller sets skip_layout.", }, chunk_lens_offset: { - type: 'integer', + type: "integer", minimum: 0, - description: 'ONCE PER RANGE STREAM (layout set): the index into the resource\'s chunk_lens at which this frame\'s prologue page begins, so pages are placed without depending on arrival order.', + description: + "ONCE PER RANGE STREAM (layout set): the index into the resource's chunk_lens at which this frame's prologue page begins, so pages are placed without depending on arrival order.", }, inclusion_proof: { - type: ['string', 'null'], - contentEncoding: 'base64', - description: 'ONCE PER RANGE STREAM (layout set): base64 merkle inclusion proof of the WHOLE resource against the generation `root`, relayed verbatim, at most MAX_INCLUSION_PROOF_B64 (4096) bytes. Verify the range against the CALLER-supplied chain-anchored root — the node is never the trust anchor. null for capsule fetches (capsule: true), which self-verify on install. A resource whose proof exceeds the cap has no conforming range stream and the holder answers RANGE_METADATA_UNREPRESENTABLE (-32009) instead of streaming frames. Omitted when the caller sets skip_layout.', + type: ["string", "null"], + contentEncoding: "base64", + description: + "ONCE PER RANGE STREAM (layout set): base64 merkle inclusion proof of the WHOLE resource against the generation `root`, relayed verbatim, at most MAX_INCLUSION_PROOF_B64 (4096) bytes. Verify the range against the CALLER-supplied chain-anchored root — the node is never the trust anchor. null for capsule fetches (capsule: true), which self-verify on install. A resource whose proof exceeds the cap has no conforming range stream and the holder answers RANGE_METADATA_UNREPRESENTABLE (-32009) instead of streaming frames. Omitted when the caller sets skip_layout.", }, range_proof: { - type: 'array', - items: { type: 'string', contentEncoding: 'base64' }, + type: "array", + items: { type: "string", contentEncoding: "base64" }, description: - 'RESERVED: a server MUST NOT emit this field and a client MUST NOT require it. Range verification is the whole-resource inclusion_proof against the caller\'s chain-anchored root; merkle leaves commit per RESOURCE, so a proof for a single chunk is not derivable.', + "RESERVED: a server MUST NOT emit this field and a client MUST NOT require it. Range verification is the whole-resource inclusion_proof against the caller's chain-anchored root; merkle leaves commit per RESOURCE, so a proof for a single chunk is not derivable.", }, first_chunk_index: { - type: 'integer', + type: "integer", minimum: 0, description: - 'RESERVED: a server MUST NOT emit this field and a client MUST NOT require it. Use chunk_index (identity set) for the frame\'s first chunk position.', + "RESERVED: a server MUST NOT emit this field and a client MUST NOT require it. Use chunk_index (identity set) for the frame's first chunk position.", }, }, }, @@ -237,184 +345,225 @@ export const schemas = { * The dig RPC method set (OpenRPC `methods`) * ------------------------------------------------------------------ */ -const PROOF_STATUS = ['succeeded', 'running', 'queued', 'not_found', 'request_via_control_plane']; +const PROOF_STATUS = ["succeeded", "running", "queued", "not_found", "request_via_control_plane"]; export const methods = [ { - name: 'dig.getContent', - summary: 'Stream one resource’s ciphertext by retrieval key.', + name: "dig.getContent", + summary: "Stream one resource’s ciphertext by retrieval key.", description: - 'The method behind every content view and public store link. Returns a chunk object; reassemble until complete, verify the inclusion proof against `root`, then decrypt with the URN-derived key.', - paramStructure: 'by-name', + "The method behind every content view and public store link. Returns a chunk object; reassemble until complete, verify the inclusion proof against `root`, then decrypt with the URN-derived key.", + paramStructure: "by-name", params: [ - { name: 'store_id', required: true, schema: { $ref: '#/components/schemas/StoreId' } }, - { name: 'retrieval_key', required: true, schema: { $ref: '#/components/schemas/RetrievalKey' } }, - { name: 'root', required: false, schema: { $ref: '#/components/schemas/Root' } }, - { name: 'offset', required: false, schema: { type: 'integer', minimum: 0, default: 0 } }, - { name: 'length', required: false, schema: { type: 'integer', minimum: 1 }, description: 'Clamped to the node’s max chunk (3 MiB).' }, + { name: "store_id", required: true, schema: { $ref: "#/components/schemas/StoreId" } }, + { + name: "retrieval_key", + required: true, + schema: { $ref: "#/components/schemas/RetrievalKey" }, + }, + { name: "root", required: false, schema: { $ref: "#/components/schemas/Root" } }, + { name: "offset", required: false, schema: { type: "integer", minimum: 0, default: 0 } }, + { + name: "length", + required: false, + schema: { type: "integer", minimum: 1 }, + description: "Clamped to the node’s max chunk (3 MiB).", + }, ], - result: { name: 'chunk', schema: { $ref: '#/components/schemas/ChunkObject' } }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR', 'RESOURCE_UNAVAILABLE'], + result: { name: "chunk", schema: { $ref: "#/components/schemas/ChunkObject" } }, + errors: ["INVALID_PARAMS", "INTERNAL_ERROR", "RESOURCE_UNAVAILABLE"], }, { - name: 'dig.getProof', - summary: 'Return the inclusion proof (sync) and the execution-proof status for a resource.', + name: "dig.getProof", + summary: "Return the inclusion proof (sync) and the execution-proof status for a resource.", description: - 'Read-only. The merkle inclusion proof is REAL and synchronous; the ZK execution proof is produced asynchronously by the prover and only returned when you pass a `proof_id` you already requested via the gated control plane.', - paramStructure: 'by-name', + "Read-only. The merkle inclusion proof is REAL and synchronous; the ZK execution proof is produced asynchronously by the prover and only returned when you pass a `proof_id` you already requested via the gated control plane.", + paramStructure: "by-name", params: [ - { name: 'store_id', required: true, schema: { $ref: '#/components/schemas/StoreId' } }, - { name: 'retrieval_key', required: true, schema: { $ref: '#/components/schemas/RetrievalKey' } }, - { name: 'root', required: false, schema: { $ref: '#/components/schemas/Root' } }, - { name: 'proof_id', required: false, schema: { type: 'string' }, description: 'An execution-proof job id to return the real receipt for.' }, + { name: "store_id", required: true, schema: { $ref: "#/components/schemas/StoreId" } }, + { + name: "retrieval_key", + required: true, + schema: { $ref: "#/components/schemas/RetrievalKey" }, + }, + { name: "root", required: false, schema: { $ref: "#/components/schemas/Root" } }, + { + name: "proof_id", + required: false, + schema: { type: "string" }, + description: "An execution-proof job id to return the real receipt for.", + }, ], result: { - name: 'proof', + name: "proof", schema: { - type: 'object', + type: "object", properties: { - inclusion_proof: { type: 'string', contentEncoding: 'base64', description: 'REAL synchronous merkle inclusion proof for the whole resource.' }, - program_hash: { $ref: '#/components/schemas/StoreId' }, - root: { $ref: '#/components/schemas/StoreId' }, - execution_proof: { type: ['string', 'null'], description: 'risc0 receipt or null (read-only / job-based; never a mock receipt on the wire).' }, - execution_proof_status: { type: 'string', enum: PROOF_STATUS }, - node_pubkey: { type: 'string' }, - block_header: { type: 'string' }, + inclusion_proof: { + type: "string", + contentEncoding: "base64", + description: "REAL synchronous merkle inclusion proof for the whole resource.", + }, + program_hash: { $ref: "#/components/schemas/StoreId" }, + root: { $ref: "#/components/schemas/StoreId" }, + execution_proof: { + type: ["string", "null"], + description: + "risc0 receipt or null (read-only / job-based; never a mock receipt on the wire).", + }, + execution_proof_status: { type: "string", enum: PROOF_STATUS }, + node_pubkey: { type: "string" }, + block_header: { type: "string" }, }, }, }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR', 'RESOURCE_UNAVAILABLE'], + errors: ["INVALID_PARAMS", "INTERNAL_ERROR", "RESOURCE_UNAVAILABLE"], }, { - name: 'dig.getProofStatus', - summary: 'Poll a REAL execution-proof job by id.', - description: 'Returns the job status and, when terminal, the real risc0 receipt. Never a mock receipt.', - paramStructure: 'by-name', + name: "dig.getProofStatus", + summary: "Poll a REAL execution-proof job by id.", + description: + "Returns the job status and, when terminal, the real risc0 receipt. Never a mock receipt.", + paramStructure: "by-name", params: [ - { name: 'store_id', required: true, schema: { $ref: '#/components/schemas/StoreId' } }, - { name: 'proof_id', required: true, schema: { type: 'string' } }, + { name: "store_id", required: true, schema: { $ref: "#/components/schemas/StoreId" } }, + { name: "proof_id", required: true, schema: { type: "string" } }, ], result: { - name: 'status', + name: "status", schema: { - type: 'object', + type: "object", properties: { - proof_id: { type: 'string' }, - status: { type: 'string', enum: ['queued', 'running', 'succeeded', 'failed'] }, - receipt: { type: ['string', 'null'] }, - node_pubkey: { type: 'string' }, - block_header: { type: 'string' }, - root: { $ref: '#/components/schemas/StoreId' }, + proof_id: { type: "string" }, + status: { type: "string", enum: ["queued", "running", "succeeded", "failed"] }, + receipt: { type: ["string", "null"] }, + node_pubkey: { type: "string" }, + block_header: { type: "string" }, + root: { $ref: "#/components/schemas/StoreId" }, }, }, }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR'], + errors: ["INVALID_PARAMS", "INTERNAL_ERROR"], }, { - name: 'dig.getCapsule', - summary: 'Stream an entire compiled capsule (the whole .dig module) for one generation.', + name: "dig.getCapsule", + summary: "Stream an entire compiled capsule (the whole .dig module) for one generation.", description: - 'Address a capsule by its (store_id, root) pair. The capsule self-verifies on install (store id + signed root + on-chain root), so `inclusion_proof` is null here. The alias dig.getModule is accepted for identical behavior.', - paramStructure: 'by-name', + "Address a capsule by its (store_id, root) pair. The capsule self-verifies on install (store id + signed root + on-chain root), so `inclusion_proof` is null here. The alias dig.getModule is accepted for identical behavior.", + paramStructure: "by-name", params: [ - { name: 'store_id', required: true, schema: { $ref: '#/components/schemas/StoreId' } }, - { name: 'root', required: false, schema: { $ref: '#/components/schemas/Root' } }, - { name: 'offset', required: false, schema: { type: 'integer', minimum: 0, default: 0 } }, - { name: 'length', required: false, schema: { type: 'integer', minimum: 1 } }, + { name: "store_id", required: true, schema: { $ref: "#/components/schemas/StoreId" } }, + { name: "root", required: false, schema: { $ref: "#/components/schemas/Root" } }, + { name: "offset", required: false, schema: { type: "integer", minimum: 0, default: 0 } }, + { name: "length", required: false, schema: { type: "integer", minimum: 1 } }, ], - result: { name: 'chunk', schema: { $ref: '#/components/schemas/ChunkObject' } }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR', 'RESOURCE_UNAVAILABLE'], + result: { name: "chunk", schema: { $ref: "#/components/schemas/ChunkObject" } }, + errors: ["INVALID_PARAMS", "INTERNAL_ERROR", "RESOURCE_UNAVAILABLE"], }, { - name: 'dig.getManifest', + name: "dig.getManifest", summary: "Convenience over dig.getContent for the store's public discovery manifest.", - description: 'The node derives the canonical retrieval key for .well-known/dig/manifest.json itself.', - paramStructure: 'by-name', + description: + "The node derives the canonical retrieval key for .well-known/dig/manifest.json itself.", + paramStructure: "by-name", params: [ - { name: 'store_id', required: true, schema: { $ref: '#/components/schemas/StoreId' } }, - { name: 'root', required: false, schema: { $ref: '#/components/schemas/Root' } }, - { name: 'offset', required: false, schema: { type: 'integer', minimum: 0, default: 0 } }, - { name: 'length', required: false, schema: { type: 'integer', minimum: 1 } }, + { name: "store_id", required: true, schema: { $ref: "#/components/schemas/StoreId" } }, + { name: "root", required: false, schema: { $ref: "#/components/schemas/Root" } }, + { name: "offset", required: false, schema: { type: "integer", minimum: 0, default: 0 } }, + { name: "length", required: false, schema: { type: "integer", minimum: 1 } }, ], result: { - name: 'chunk', + name: "chunk", schema: { allOf: [ - { $ref: '#/components/schemas/ChunkObject' }, - { type: 'object', properties: { retrieval_key: { $ref: '#/components/schemas/RetrievalKey' } } }, + { $ref: "#/components/schemas/ChunkObject" }, + { + type: "object", + properties: { retrieval_key: { $ref: "#/components/schemas/RetrievalKey" } }, + }, ], }, }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR', 'RESOURCE_UNAVAILABLE'], + errors: ["INVALID_PARAMS", "INTERNAL_ERROR", "RESOURCE_UNAVAILABLE"], }, { - name: 'dig.getMetadata', + name: "dig.getMetadata", summary: "Read the store's plaintext metadata manifest from the .dig.", description: - 'Metadata is plaintext, ungated public discovery info embedded in the module (dig-store §8.4) — not a content resource, no inclusion proof, never encrypted. Its on-chain binding is the module program_hash.', - paramStructure: 'by-name', + "Metadata is plaintext, ungated public discovery info embedded in the module (dig-store §8.4) — not a content resource, no inclusion proof, never encrypted. Its on-chain binding is the module program_hash.", + paramStructure: "by-name", params: [ - { name: 'store_id', required: true, schema: { $ref: '#/components/schemas/StoreId' } }, - { name: 'root', required: false, schema: { $ref: '#/components/schemas/Root' } }, + { name: "store_id", required: true, schema: { $ref: "#/components/schemas/StoreId" } }, + { name: "root", required: false, schema: { $ref: "#/components/schemas/Root" } }, ], result: { - name: 'metadata', + name: "metadata", schema: { - type: 'object', + type: "object", properties: { - manifest: { oneOf: [{ $ref: '#/components/schemas/MetadataManifest' }, { type: 'null' }] }, - program_hash: { $ref: '#/components/schemas/StoreId' }, - root: { $ref: '#/components/schemas/StoreId' }, + manifest: { + oneOf: [{ $ref: "#/components/schemas/MetadataManifest" }, { type: "null" }], + }, + program_hash: { $ref: "#/components/schemas/StoreId" }, + root: { $ref: "#/components/schemas/StoreId" }, }, }, }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR', 'RESOURCE_UNAVAILABLE'], + errors: ["INVALID_PARAMS", "INTERNAL_ERROR", "RESOURCE_UNAVAILABLE"], }, { - name: 'dig.listCapsules', + name: "dig.listCapsules", summary: "Return the store's confirmed capsules — one entry per anchored generation.", - description: 'Discovery metadata, not content: only the public on-chain generation list.', - paramStructure: 'by-name', - params: [{ name: 'store_id', required: true, schema: { $ref: '#/components/schemas/StoreId' } }], + description: "Discovery metadata, not content: only the public on-chain generation list.", + paramStructure: "by-name", + params: [ + { name: "store_id", required: true, schema: { $ref: "#/components/schemas/StoreId" } }, + ], result: { - name: 'capsules', + name: "capsules", schema: { - type: 'object', + type: "object", properties: { - store_id: { $ref: '#/components/schemas/StoreId' }, - capsules: { type: 'array', items: { $ref: '#/components/schemas/CapsuleEntry' } }, + store_id: { $ref: "#/components/schemas/StoreId" }, + capsules: { type: "array", items: { $ref: "#/components/schemas/CapsuleEntry" } }, }, }, }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR'], + errors: ["INVALID_PARAMS", "INTERNAL_ERROR"], }, { - name: 'dig.health', - summary: 'Service discovery: liveness + the implemented method list.', - description: 'Takes no parameters.', - paramStructure: 'by-name', + name: "dig.health", + summary: "Service discovery: liveness + the implemented method list.", + description: "Takes no parameters.", + paramStructure: "by-name", params: [], result: { - name: 'health', + name: "health", schema: { - type: 'object', + type: "object", properties: { - ok: { type: 'boolean' }, - service: { type: 'string' }, - methods: { type: 'array', items: { type: 'string' } }, + ok: { type: "boolean" }, + service: { type: "string" }, + methods: { type: "array", items: { type: "string" } }, }, }, }, errors: [], }, { - name: 'dig.methods', - summary: 'Service discovery: the method catalogue this node implements.', + name: "dig.methods", + summary: "Service discovery: the method catalogue this node implements.", description: - 'Takes no parameters. Use it to confirm a third-party node implements the methods you need before relying on it.', - paramStructure: 'by-name', + "Takes no parameters. Use it to confirm a third-party node implements the methods you need before relying on it.", + paramStructure: "by-name", params: [], - result: { name: 'methods', schema: { type: 'object', properties: { methods: { type: 'array', items: { type: 'string' } } } } }, + result: { + name: "methods", + schema: { + type: "object", + properties: { methods: { type: "array", items: { type: "string" } } }, + }, + }, errors: [], }, ]; @@ -432,196 +581,331 @@ export const methods = [ export const nodeMethods = [ { - name: 'dig.getContent', - summary: 'Stream one resource’s ciphertext (local-first, then proxy).', + name: "dig.getContent", + summary: "Stream one resource’s ciphertext (local-first, then proxy).", description: 'Identical wire contract to the network profile, but served LOCAL-FIRST: from a cached compiled .dig (serve_blind over /modules//.module), else an authenticated §21.9 whole-store sync, else the raw JSON-RPC body is proxied upstream to rpc.dig.net. The in-process node ADDITIVELY tags each chunk with `source` ("local"|"remote").', - paramStructure: 'by-name', + paramStructure: "by-name", params: [ - { name: 'store_id', required: true, schema: { $ref: '#/components/schemas/StoreId' } }, - { name: 'retrieval_key', required: true, schema: { $ref: '#/components/schemas/RetrievalKey' } }, - { name: 'root', required: false, schema: { $ref: '#/components/schemas/Root' } }, - { name: 'offset', required: false, schema: { type: 'integer', minimum: 0, default: 0 } }, - { name: 'length', required: false, schema: { type: 'integer', minimum: 1 }, description: 'Clamped to the node window (3 MiB).' }, + { name: "store_id", required: true, schema: { $ref: "#/components/schemas/StoreId" } }, + { + name: "retrieval_key", + required: true, + schema: { $ref: "#/components/schemas/RetrievalKey" }, + }, + { name: "root", required: false, schema: { $ref: "#/components/schemas/Root" } }, + { name: "offset", required: false, schema: { type: "integer", minimum: 0, default: 0 } }, + { + name: "length", + required: false, + schema: { type: "integer", minimum: 1 }, + description: "Clamped to the node window (3 MiB).", + }, ], - result: { name: 'chunk', schema: { $ref: '#/components/schemas/ChunkObject' } }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR', 'RESOURCE_UNAVAILABLE'], + result: { name: "chunk", schema: { $ref: "#/components/schemas/ChunkObject" } }, + errors: ["INVALID_PARAMS", "INTERNAL_ERROR", "RESOURCE_UNAVAILABLE"], }, { - name: 'dig.getAnchoredRoot', - summary: 'Resolve the store’s CHIP-0035 on-chain head root (the trusted root).', + name: "dig.getAnchoredRoot", + summary: "Resolve the store’s CHIP-0035 on-chain head root (the trusted root).", description: 'Walks the CHIP-0035 DataStore singleton lineage on coinset.org (digstore_chain::singleton::sync_datastore) and returns metadata.root_hash — the on-chain-anchored tip. This is the TRUSTED root a client pins a rootless chia:// URN against; verification must never trust the rpc-served "latest". Coinset host defaults to api.coinset.org (override DIG_NODE_COINSET). NODE-PROFILE ONLY: absent from the network profile (dig-node lib.rs:721-743).', - paramStructure: 'by-name', - params: [{ name: 'store_id', required: true, schema: { $ref: '#/components/schemas/StoreId' } }], + paramStructure: "by-name", + params: [ + { name: "store_id", required: true, schema: { $ref: "#/components/schemas/StoreId" } }, + ], result: { - name: 'anchored', + name: "anchored", schema: { - type: 'object', + type: "object", properties: { - store_id: { $ref: '#/components/schemas/StoreId' }, - root: { $ref: '#/components/schemas/StoreId' }, + store_id: { $ref: "#/components/schemas/StoreId" }, + root: { $ref: "#/components/schemas/StoreId" }, }, }, }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR'], + errors: ["INVALID_PARAMS", "INTERNAL_ERROR"], }, { - name: 'dig.stage', - summary: 'Compile a local folder into a capsule .dig, in-process.', + name: "dig.stage", + summary: "Compile a local folder into a capsule .dig, in-process.", description: - 'Compiles a local directory into a capsule (.dig) in-process for preview/publish. NODE-PROFILE ONLY (dig-node lib.rs:768-904).', - paramStructure: 'by-name', - params: [{ name: 'path', required: true, schema: { type: 'string' } }], - result: { name: 'staged', schema: { type: 'object', properties: { store_id: { $ref: '#/components/schemas/StoreId' }, root: { $ref: '#/components/schemas/StoreId' } } } }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR'], + "Compiles a local directory into a capsule (.dig) in-process for preview/publish. NODE-PROFILE ONLY (dig-node lib.rs:768-904).", + paramStructure: "by-name", + params: [{ name: "path", required: true, schema: { type: "string" } }], + result: { + name: "staged", + schema: { + type: "object", + properties: { + store_id: { $ref: "#/components/schemas/StoreId" }, + root: { $ref: "#/components/schemas/StoreId" }, + }, + }, + }, + errors: ["INVALID_PARAMS", "INTERNAL_ERROR"], }, { - name: 'cache.getConfig', - summary: 'Read the local node cache configuration.', - description: 'Part of the node-only cache.* control surface (getConfig/setCapBytes/clear/listCached/removeCached/fetchAndCache). NODE-PROFILE ONLY (dig-node lib.rs:1143-1231).', - paramStructure: 'by-name', + name: "cache.getConfig", + summary: "Read the local node cache configuration.", + description: + "Part of the node-only cache.* control surface (getConfig/setCapBytes/clear/listCached/removeCached/fetchAndCache). NODE-PROFILE ONLY (dig-node lib.rs:1143-1231).", + paramStructure: "by-name", params: [], - result: { name: 'config', schema: { type: 'object' } }, - errors: ['INTERNAL_ERROR'], + result: { name: "config", schema: { type: "object" } }, + errors: ["INTERNAL_ERROR"], }, { - name: 'dig.getPeers', - summary: 'Return the peers this node knows, with peer_id + candidate addresses.', + name: "dig.getPeers", + summary: "Return the peers this node knows, with peer_id + candidate addresses.", description: - 'Peer-exchange over RPC: the peers in this node\'s address book, each with its peer_id (SHA-256 of the peer\'s TLS SPKI DER) and candidate addresses. Mirrors the gossip RequestPeers/RespondPeers exchange so an agent can drive discovery without speaking the binary peer protocol. NODE-PROFILE ONLY. See https://docs.dig.net/docs/protocol/peer-network#peer-rpc.', - paramStructure: 'by-name', + "Peer-exchange over RPC: the peers in this node's address book, each with its peer_id (SHA-256 of the peer's TLS SPKI DER) and candidate addresses. Mirrors the gossip RequestPeers/RespondPeers exchange so an agent can drive discovery without speaking the binary peer protocol. NODE-PROFILE ONLY. See https://docs.dig.net/docs/protocol/peer-network#peer-rpc.", + paramStructure: "by-name", params: [ - { name: 'network_id', required: false, schema: { type: 'string' }, description: 'Filter to one network (e.g. DIG_MAINNET).' }, - { name: 'limit', required: false, schema: { type: 'integer', minimum: 1 }, description: 'Cap the number of peers returned.' }, + { + name: "network_id", + required: false, + schema: { type: "string" }, + description: "Filter to one network (e.g. DIG_MAINNET).", + }, + { + name: "limit", + required: false, + schema: { type: "integer", minimum: 1 }, + description: "Cap the number of peers returned.", + }, ], result: { - name: 'peers', + name: "peers", schema: { - type: 'object', - required: ['peers'], - properties: { peers: { type: 'array', items: { $ref: '#/components/schemas/PeerRecord' } } }, + type: "object", + required: ["peers"], + properties: { + peers: { type: "array", items: { $ref: "#/components/schemas/PeerRecord" } }, + }, }, }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR', 'PEER_UNREACHABLE'], + errors: ["INVALID_PARAMS", "INTERNAL_ERROR", "PEER_UNREACHABLE"], }, { - name: 'dig.announce', - summary: 'Advertise this node (peer_id + addresses) to a peer or the introducer.', + name: "dig.announce", + summary: "Advertise this node (peer_id + addresses) to a peer or the introducer.", description: - 'Announce this node\'s peer_id + candidate addresses so a target peer (or, if `target` is omitted, the relay introducer) learns to reach it. The RPC face of the introducer/announce path. NODE-PROFILE ONLY. See https://docs.dig.net/docs/protocol/peer-network#peer-rpc.', - paramStructure: 'by-name', + "Announce this node's peer_id + candidate addresses so a target peer (or, if `target` is omitted, the relay introducer) learns to reach it. The RPC face of the introducer/announce path. NODE-PROFILE ONLY. See https://docs.dig.net/docs/protocol/peer-network#peer-rpc.", + paramStructure: "by-name", params: [ - { name: 'peer_id', required: true, schema: { $ref: '#/components/schemas/PeerId' }, description: 'The announcing node\'s peer_id.' }, - { name: 'addresses', required: true, schema: { type: 'array', items: { $ref: '#/components/schemas/CandidateAddress' } }, description: 'The announcing node\'s candidate addresses.' }, - { name: 'network_id', required: false, schema: { type: 'string' } }, - { name: 'target', required: false, schema: { $ref: '#/components/schemas/PeerId' }, description: 'A specific peer to announce to; omit to register with the relay introducer.' }, + { + name: "peer_id", + required: true, + schema: { $ref: "#/components/schemas/PeerId" }, + description: "The announcing node's peer_id.", + }, + { + name: "addresses", + required: true, + schema: { type: "array", items: { $ref: "#/components/schemas/CandidateAddress" } }, + description: "The announcing node's candidate addresses.", + }, + { name: "network_id", required: false, schema: { type: "string" } }, + { + name: "target", + required: false, + schema: { $ref: "#/components/schemas/PeerId" }, + description: "A specific peer to announce to; omit to register with the relay introducer.", + }, ], result: { - name: 'announced', + name: "announced", schema: { - type: 'object', - required: ['accepted'], + type: "object", + required: ["accepted"], properties: { - accepted: { type: 'boolean', description: 'Whether the announcement was accepted.' }, - known_peers: { type: 'integer', minimum: 0, description: 'The recipient\'s resulting peer-view size.' }, + accepted: { type: "boolean", description: "Whether the announcement was accepted." }, + known_peers: { + type: "integer", + minimum: 0, + description: "The recipient's resulting peer-view size.", + }, }, }, }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR', 'PEER_UNREACHABLE'], + errors: ["INVALID_PARAMS", "INTERNAL_ERROR", "PEER_UNREACHABLE"], }, { - name: 'dig.getNetworkInfo', - summary: 'Report this node\'s identity, reachability, candidate addresses + relay state.', + name: "dig.getNetworkInfo", + summary: "Report this node's identity, reachability, candidate addresses + relay state.", description: - 'The node\'s own network posture: its peer_id, listen + STUN-reflexive addresses, candidate addresses, direct-vs-relayed reachability, and relay-reservation state. The self-describe surface for discovery + NAT traversal. NODE-PROFILE ONLY. See https://docs.dig.net/docs/protocol/peer-network#peer-rpc.', - paramStructure: 'by-name', + "The node's own network posture: its peer_id, listen + STUN-reflexive addresses, candidate addresses, direct-vs-relayed reachability, and relay-reservation state. The self-describe surface for discovery + NAT traversal. NODE-PROFILE ONLY. See https://docs.dig.net/docs/protocol/peer-network#peer-rpc.", + paramStructure: "by-name", params: [], result: { - name: 'network_info', + name: "network_info", schema: { - type: 'object', - required: ['peer_id', 'reachability'], + type: "object", + required: ["peer_id", "reachability"], properties: { - peer_id: { $ref: '#/components/schemas/PeerId' }, - network_id: { type: 'string' }, - listen_addr: { type: 'string', description: 'The node\'s configured listen address (host:port).' }, - reflexive_addr: { type: ['string', 'null'], description: 'The STUN-discovered public IP:port, or null if not yet learned.' }, - candidate_addresses: { type: 'array', items: { $ref: '#/components/schemas/CandidateAddress' } }, - reachability: { type: 'string', enum: ['direct', 'relayed'], description: 'direct = a direct inbound path exists; relayed = only reachable through the relay.' }, + peer_id: { $ref: "#/components/schemas/PeerId" }, + network_id: { type: "string" }, + listen_addr: { + type: "string", + description: "The node's configured listen address (host:port).", + }, + reflexive_addr: { + type: ["string", "null"], + description: "The STUN-discovered public IP:port, or null if not yet learned.", + }, + candidate_addresses: { + type: "array", + items: { $ref: "#/components/schemas/CandidateAddress" }, + }, + reachability: { + type: "string", + enum: ["direct", "relayed"], + description: + "direct = a direct inbound path exists; relayed = only reachable through the relay.", + }, relay: { - type: 'object', - description: 'The relay reservation state.', + type: "object", + description: "The relay reservation state.", properties: { - url: { type: 'string', description: 'The relay endpoint (default wss://relay.dig.net:9450).' }, - reserved: { type: 'boolean', description: 'Whether a relay reservation (RLY-001) is currently held.' }, - connected_peers: { type: 'integer', minimum: 0 }, + url: { + type: "string", + description: "The relay endpoint (default wss://relay.dig.net:9450).", + }, + reserved: { + type: "boolean", + description: "Whether a relay reservation (RLY-001) is currently held.", + }, + connected_peers: { type: "integer", minimum: 0 }, }, }, }, }, }, - errors: ['INTERNAL_ERROR'], + errors: ["INTERNAL_ERROR"], }, { - name: 'dig.getAvailability', - summary: 'Batch-ask whether this peer holds given stores / roots / capsules (before fetching).', + name: "dig.getAvailability", + summary: "Batch-ask whether this peer holds given stores / roots / capsules (before fetching).", description: - 'The pre-fetch availability check. Batch per-item query at three granularities inferred from each item\'s fields — store_id only (has_store), + root (has_root / capsule), + retrieval_key (has_resource). Returns one answer per item (positionally aligned): available plus, where cheap, planning metadata (roots for a store; total_length + chunk_count + complete for a root/resource) so a downloader filters to holders and plans ranges WITHOUT a probe fetch. A small control RPC (message-style, not streamed). Absence is available:false, NOT an error. NODE-PROFILE ONLY. See https://docs.dig.net/docs/protocol/peer-network#availability.', - paramStructure: 'by-name', + "The pre-fetch availability check. Batch per-item query at three granularities inferred from each item's fields — store_id only (has_store), + root (has_root / capsule), + retrieval_key (has_resource). Returns one answer per item (positionally aligned): available plus, where cheap, planning metadata (roots for a store; total_length + chunk_count + complete for a root/resource) so a downloader filters to holders and plans ranges WITHOUT a probe fetch. A small control RPC (message-style, not streamed). Absence is available:false, NOT an error. NODE-PROFILE ONLY. See https://docs.dig.net/docs/protocol/peer-network#availability.", + paramStructure: "by-name", params: [ - { name: 'items', required: true, schema: { type: 'array', items: { $ref: '#/components/schemas/AvailabilityItem' } }, description: 'The stores / roots / capsules to check in one call.' }, + { + name: "items", + required: true, + schema: { type: "array", items: { $ref: "#/components/schemas/AvailabilityItem" } }, + description: "The stores / roots / capsules to check in one call.", + }, ], result: { - name: 'availability', + name: "availability", schema: { - type: 'object', - required: ['items'], - properties: { items: { type: 'array', items: { $ref: '#/components/schemas/AvailabilityAnswer' }, description: 'One answer per queried item, in the same order.' } }, + type: "object", + required: ["items"], + properties: { + items: { + type: "array", + items: { $ref: "#/components/schemas/AvailabilityAnswer" }, + description: "One answer per queried item, in the same order.", + }, + }, }, }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR'], + errors: ["INVALID_PARAMS", "INTERNAL_ERROR"], }, { - name: 'dig.listInventory', - summary: 'Enumerate what this peer serves — its stores, or the roots it holds for a store.', + name: "dig.listInventory", + summary: "Enumerate what this peer serves — its stores, or the roots it holds for a store.", description: - 'Discovery variant of availability: omit store_id to list the stores the peer serves; supply it to list the roots the peer holds for that store. Best-effort — a peer MAY cap or omit enumeration (privacy/size); dig.getAvailability is the authoritative per-item check. NODE-PROFILE ONLY. See https://docs.dig.net/docs/protocol/peer-network#availability.', - paramStructure: 'by-name', + "Discovery variant of availability: omit store_id to list the stores the peer serves; supply it to list the roots the peer holds for that store. Best-effort — a peer MAY cap or omit enumeration (privacy/size); dig.getAvailability is the authoritative per-item check. NODE-PROFILE ONLY. See https://docs.dig.net/docs/protocol/peer-network#availability.", + paramStructure: "by-name", params: [ - { name: 'store_id', required: false, schema: { $ref: '#/components/schemas/StoreId' }, description: 'Omit to list stores; supply to list that store\'s roots.' }, - { name: 'limit', required: false, schema: { type: 'integer', minimum: 1 } }, + { + name: "store_id", + required: false, + schema: { $ref: "#/components/schemas/StoreId" }, + description: "Omit to list stores; supply to list that store's roots.", + }, + { name: "limit", required: false, schema: { type: "integer", minimum: 1 } }, ], result: { - name: 'inventory', + name: "inventory", schema: { - type: 'object', + type: "object", properties: { - store_id: { ...hex64, description: 'Echoed when roots are listed for a store.' }, - stores: { type: 'array', items: { ...hex64 }, description: 'The stores this peer serves (when store_id is omitted).' }, - roots: { type: 'array', items: { ...hex64 }, description: 'The roots this peer holds for the store (when store_id is given).' }, + store_id: { ...hex64, description: "Echoed when roots are listed for a store." }, + stores: { + type: "array", + items: { ...hex64 }, + description: "The stores this peer serves (when store_id is omitted).", + }, + roots: { + type: "array", + items: { ...hex64 }, + description: "The roots this peer holds for the store (when store_id is given).", + }, }, }, }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR'], + errors: ["INVALID_PARAMS", "INTERNAL_ERROR"], }, { - name: 'dig.fetchRange', - summary: 'Stream a byte range [offset, offset+length) of a resource or capsule (multi-source).', + name: "dig.fetchRange", + summary: "Stream a byte range [offset, offset+length) of a resource or capsule (multi-source).", description: - 'STREAMING + BYTE-RANGE fetch: return only the requested byte range of a content resource (store_id + retrieval_key) or a whole capsule/.dig (capsule: true, identified by store_id[:root]), delivered as an ordered STREAM of RangeFrame chunks over a logical stream of the multiplexed peer transport (read incrementally with backpressure; reassemble by offset). EVERY frame carries the fixed-size identity set (root + total_length + chunk_count, plus chunk_index when chunk-aligned), and the resource-scaling layout (chunk_lens + inclusion_proof) rides the first frame or a paged prologue once per stream, so the range is INDEPENDENTLY verifiable against the capsule\'s chain-anchored merkle root — a downloader fans different ranges out to different peers concurrently, verifies each, retries a bad range from another source, and resumes per-range. The range is widened to whole-chunk boundaries so each returned chunk is a complete verifiable unit; length is clamped to the node window (3 MiB). NODE-PROFILE ONLY. See https://docs.dig.net/docs/protocol/peer-network#range.', - paramStructure: 'by-name', + "STREAMING + BYTE-RANGE fetch: return only the requested byte range of a content resource (store_id + retrieval_key) or a whole capsule/.dig (capsule: true, identified by store_id[:root]), delivered as an ordered STREAM of RangeFrame chunks over a logical stream of the multiplexed peer transport (read incrementally with backpressure; reassemble by offset). EVERY frame carries the fixed-size identity set (root + total_length + chunk_count, plus chunk_index when chunk-aligned), and the resource-scaling layout (chunk_lens + inclusion_proof) rides the first frame or a paged prologue once per stream, so the range is INDEPENDENTLY verifiable against the capsule's chain-anchored merkle root — a downloader fans different ranges out to different peers concurrently, verifies each, retries a bad range from another source, and resumes per-range. The range is widened to whole-chunk boundaries so each returned chunk is a complete verifiable unit; length is clamped to the node window (3 MiB). NODE-PROFILE ONLY. See https://docs.dig.net/docs/protocol/peer-network#range.", + paramStructure: "by-name", params: [ - { name: 'store_id', required: true, schema: { $ref: '#/components/schemas/StoreId' } }, - { name: 'retrieval_key', required: false, schema: { $ref: '#/components/schemas/RetrievalKey' }, description: 'The content resource to fetch; omit when capsule=true.' }, - { name: 'root', required: false, schema: { $ref: '#/components/schemas/Root' }, description: 'The generation; defaults to the chain-anchored tip.' }, - { name: 'capsule', required: false, schema: { type: 'boolean', default: false }, description: 'true to fetch the whole capsule/.dig (identified by store_id[:root]) instead of a resource.' }, - { name: 'offset', required: false, schema: { type: 'integer', minimum: 0, default: 0 }, description: 'Byte offset into the resource ciphertext.' }, - { name: 'length', required: true, schema: { type: 'integer', minimum: 1 }, description: 'Bytes to return; clamped to the node window (3 MiB) and widened to whole-chunk boundaries.' }, - { name: 'skip_layout', required: false, schema: { type: 'boolean' }, description: 'true when the caller already holds the resource layout + commitment; the peer then omits the layout metadata (chunk_lens, inclusion_proof) and streams data frames only.' }, + { name: "store_id", required: true, schema: { $ref: "#/components/schemas/StoreId" } }, + { + name: "retrieval_key", + required: false, + schema: { $ref: "#/components/schemas/RetrievalKey" }, + description: "The content resource to fetch; omit when capsule=true.", + }, + { + name: "root", + required: false, + schema: { $ref: "#/components/schemas/Root" }, + description: "The generation; defaults to the chain-anchored tip.", + }, + { + name: "capsule", + required: false, + schema: { type: "boolean", default: false }, + description: + "true to fetch the whole capsule/.dig (identified by store_id[:root]) instead of a resource.", + }, + { + name: "offset", + required: false, + schema: { type: "integer", minimum: 0, default: 0 }, + description: "Byte offset into the resource ciphertext.", + }, + { + name: "length", + required: true, + schema: { type: "integer", minimum: 1 }, + description: + "Bytes to return; clamped to the node window (3 MiB) and widened to whole-chunk boundaries.", + }, + { + name: "skip_layout", + required: false, + schema: { type: "boolean" }, + description: + "true when the caller already holds the resource layout + commitment; the peer then omits the layout metadata (chunk_lens, inclusion_proof) and streams data frames only.", + }, + ], + result: { name: "frame", schema: { $ref: "#/components/schemas/RangeFrame" } }, + errors: [ + "INVALID_PARAMS", + "INTERNAL_ERROR", + "RESOURCE_UNAVAILABLE", + "RANGE_NOT_SATISFIABLE", + "RANGE_METADATA_UNREPRESENTABLE", ], - result: { name: 'frame', schema: { $ref: '#/components/schemas/RangeFrame' } }, - errors: ['INVALID_PARAMS', 'INTERNAL_ERROR', 'RESOURCE_UNAVAILABLE', 'RANGE_NOT_SATISFIABLE', 'RANGE_METADATA_UNREPRESENTABLE'], }, ]; @@ -631,28 +915,130 @@ export const nodeMethods = [ * ------------------------------------------------------------------ */ export const rpcErrors = { - PARSE_ERROR: { code: -32700, message: 'Parse error', meaning: 'The request body isn’t valid JSON (id is null).' }, - INVALID_REQUEST: { code: -32600, message: 'Invalid request', meaning: 'Not a request object/array, an empty batch, or a missing method.' }, - METHOD_NOT_FOUND: { code: -32601, message: 'Method not found', meaning: 'This node doesn’t implement the method.' }, - INVALID_PARAMS: { code: -32602, message: 'Invalid params', meaning: 'Missing/malformed store_id, root, or retrieval_key; or "latest" on a store with no confirmed generation.' }, - INTERNAL_ERROR: { code: -32603, message: 'Internal error', meaning: 'The node failed to satisfy a well-formed call.' }, - RESOURCE_UNAVAILABLE: { code: -32004, message: 'Resource not available at the requested root', meaning: 'A genuine infrastructure miss (no host seed, module absent in both buckets, bad magic, oversize, a wasmtime trap, or an undecodable envelope) — distinct from a content miss, which is an indistinguishable decoy with no error.' }, - ROOT_NOT_ANCHORED: { code: -32005, message: 'Root not chain-anchored', meaning: 'The requested or served generation is not the store’s current on-chain root. A content read is pinned to the CHIP-0035 singleton’s on-chain root (resolved live from the chain, never trusted from the serving node): a requested root that is not the on-chain root, an unreachable chain, or a store with no confirmed generation fails closed with this code rather than serving an unverified generation. Omit root to take the chain tip.' }, - PEER_UNREACHABLE: { code: -32006, message: 'Peer unreachable', meaning: 'No connection to the named peer could be established — every NAT-traversal strategy (direct, UPnP/NAT-PMP/PCP mapping, relay-coordinated hole-punch, and relayed fallback) failed, or the peer is not registered on this network. Returned by the node-profile peer methods (dig.getPeers / dig.announce / dig.getNetworkInfo).' }, - RANGE_NOT_SATISFIABLE: { code: -32007, message: 'Range not satisfiable', meaning: 'The requested byte range lies outside the resource (offset >= total_length) or is otherwise unsatisfiable. Returned by the node-profile dig.fetchRange when the offset/length cannot be served.' }, - RANGE_METADATA_UNREPRESENTABLE: { code: -32009, message: 'Range metadata unrepresentable', meaning: 'The range metadata ALONE cannot fit a frame — an inclusion_proof whose base64 exceeds MAX_INCLUSION_PROOF_B64 (4096) — so this holder has no conforming range stream for the resource and streams no frames. Returned by the node-profile dig.fetchRange. HOLDER-FATAL: a client skips this holder, does not re-request the range from it, and MUST NOT count it as a transport failure (counted as transport it retries a peer that can never succeed).' }, - CONTENT_REDIRECT: { code: -32008, message: 'Content held elsewhere — redirect', meaning: 'This node does not hold the requested content, but it located peers that do (via the DHT) — a redirect, not a not-found. error.data.redirect names the holder(s) (providers[] = peer_id + candidate addresses), the requested content, and the bounded redirect budget (redirect_depth, max_redirects). Returned by the node-profile content methods (dig.getContent / dig.fetchRange) on a local miss when a provider exists; re-request against a named provider, echoing redirect_depth.' }, - UPSTREAM_ERROR: { code: -32010, message: 'Upstream error', meaning: 'A thin-shell node relayed a method it does not resolve locally to its upstream DIG RPC, and the upstream was unreachable or returned a non-JSON response.' }, - STAGE_DIR_UNREADABLE: { code: -32011, message: 'Stage: source directory unreadable', meaning: 'dig.stage (local control) could not read the source directory.' }, - STAGE_NO_FILES: { code: -32012, message: 'Stage: no files to stage', meaning: 'dig.stage found no files in the source directory.' }, - STAGE_OVER_CAP: { code: -32013, message: 'Stage: content exceeds capsule size cap', meaning: 'dig.stage content exceeds the capsule size cap.' }, - STAGE_COMPILE_IO: { code: -32014, message: 'Stage: compile/IO failure', meaning: 'dig.stage hit a compile or IO failure while building the module.' }, - ONION_CIRCUIT_UNAVAILABLE: { code: -32020, message: 'Onion circuit unavailable', meaning: 'A mode:"privacy" content read could not be served privately (no circuit could be built or a circuit died mid-fetch). The node MUST NOT downgrade to a direct fetch — that would deanonymize the very reader who asked for privacy — so it fails closed with this code. Retry, or the client may explicitly choose a fast (mode:"speed") fetch instead. See https://docs.dig.net/docs/protocol/onion-routing.' }, - PRIVACY_REQUIRES_LOCAL_NODE: { code: -32021, message: 'Privacy requires a local node', meaning: 'mode:"privacy" was requested but the caller is not the node\'s own trusted local originator (loopback / dig.local). A node will not fetch privately on a remote/anonymous caller\'s behalf (that would hand it the caller\'s identity + query), so privacy mode requires a local DIG node. See https://docs.dig.net/docs/protocol/onion-routing#toggle-placement.' }, - ONION_HOPS_OUT_OF_RANGE: { code: -32022, message: 'Onion hop count out of range', meaning: 'The requested privacy.hops (circuit length) is outside the allowed range [2, 5] (default 3). Request a hop count within range. See https://docs.dig.net/docs/protocol/onion-routing#modes.' }, - CONTROL_UNAUTHORIZED: { code: -32030, message: 'Unauthorized (control)', meaning: 'A control.* method was called without a valid local control token. Control methods are loopback-only. Control codes are -32030+ so they never collide with the onion codes -32020/21/22.' }, - CONTROL_NOT_SUPPORTED: { code: -32031, message: 'Not supported (control)', meaning: 'A control operation this build/pin cannot perform (e.g. §21 whole-store sync with no loaded identity).' }, - CONTROL_ERROR: { code: -32032, message: 'Control error', meaning: 'A control operation failed at runtime — distinct from bad input or an absent capability.' }, + PARSE_ERROR: { + code: -32700, + message: "Parse error", + meaning: "The request body isn’t valid JSON (id is null).", + }, + INVALID_REQUEST: { + code: -32600, + message: "Invalid request", + meaning: "Not a request object/array, an empty batch, or a missing method.", + }, + METHOD_NOT_FOUND: { + code: -32601, + message: "Method not found", + meaning: "This node doesn’t implement the method.", + }, + INVALID_PARAMS: { + code: -32602, + message: "Invalid params", + meaning: + 'Missing/malformed store_id, root, or retrieval_key; or "latest" on a store with no confirmed generation.', + }, + INTERNAL_ERROR: { + code: -32603, + message: "Internal error", + meaning: "The node failed to satisfy a well-formed call.", + }, + RESOURCE_UNAVAILABLE: { + code: -32004, + message: "Resource not available at the requested root", + meaning: + "A genuine infrastructure miss (no host seed, module absent in both buckets, bad magic, oversize, a wasmtime trap, or an undecodable envelope) — distinct from a content miss, which is an indistinguishable decoy with no error.", + }, + ROOT_NOT_ANCHORED: { + code: -32005, + message: "Root not chain-anchored", + meaning: + "The requested or served generation is not the store’s current on-chain root. A content read is pinned to the CHIP-0035 singleton’s on-chain root (resolved live from the chain, never trusted from the serving node): a requested root that is not the on-chain root, an unreachable chain, or a store with no confirmed generation fails closed with this code rather than serving an unverified generation. Omit root to take the chain tip.", + }, + PEER_UNREACHABLE: { + code: -32006, + message: "Peer unreachable", + meaning: + "No connection to the named peer could be established — every NAT-traversal strategy (direct, UPnP/NAT-PMP/PCP mapping, relay-coordinated hole-punch, and relayed fallback) failed, or the peer is not registered on this network. Returned by the node-profile peer methods (dig.getPeers / dig.announce / dig.getNetworkInfo).", + }, + RANGE_NOT_SATISFIABLE: { + code: -32007, + message: "Range not satisfiable", + meaning: + "The requested byte range lies outside the resource (offset >= total_length) or is otherwise unsatisfiable. Returned by the node-profile dig.fetchRange when the offset/length cannot be served.", + }, + RANGE_METADATA_UNREPRESENTABLE: { + code: -32009, + message: "Range metadata unrepresentable", + meaning: + "The range metadata ALONE cannot fit a frame — an inclusion_proof whose base64 exceeds MAX_INCLUSION_PROOF_B64 (4096) — so this holder has no conforming range stream for the resource and streams no frames. Returned by the node-profile dig.fetchRange. HOLDER-FATAL: a client skips this holder, does not re-request the range from it, and MUST NOT count it as a transport failure (counted as transport it retries a peer that can never succeed).", + }, + CONTENT_REDIRECT: { + code: -32008, + message: "Content held elsewhere — redirect", + meaning: + "This node does not hold the requested content, but it located peers that do (via the DHT) — a redirect, not a not-found. error.data.redirect names the holder(s) (providers[] = peer_id + candidate addresses), the requested content, and the bounded redirect budget (redirect_depth, max_redirects). Returned by the node-profile content methods (dig.getContent / dig.fetchRange) on a local miss when a provider exists; re-request against a named provider, echoing redirect_depth.", + }, + UPSTREAM_ERROR: { + code: -32010, + message: "Upstream error", + meaning: + "A thin-shell node relayed a method it does not resolve locally to its upstream DIG RPC, and the upstream was unreachable or returned a non-JSON response.", + }, + STAGE_DIR_UNREADABLE: { + code: -32011, + message: "Stage: source directory unreadable", + meaning: "dig.stage (local control) could not read the source directory.", + }, + STAGE_NO_FILES: { + code: -32012, + message: "Stage: no files to stage", + meaning: "dig.stage found no files in the source directory.", + }, + STAGE_OVER_CAP: { + code: -32013, + message: "Stage: content exceeds capsule size cap", + meaning: "dig.stage content exceeds the capsule size cap.", + }, + STAGE_COMPILE_IO: { + code: -32014, + message: "Stage: compile/IO failure", + meaning: "dig.stage hit a compile or IO failure while building the module.", + }, + ONION_CIRCUIT_UNAVAILABLE: { + code: -32020, + message: "Onion circuit unavailable", + meaning: + 'A mode:"privacy" content read could not be served privately (no circuit could be built or a circuit died mid-fetch). The node MUST NOT downgrade to a direct fetch — that would deanonymize the very reader who asked for privacy — so it fails closed with this code. Retry, or the client may explicitly choose a fast (mode:"speed") fetch instead. See https://docs.dig.net/docs/protocol/onion-routing.', + }, + PRIVACY_REQUIRES_LOCAL_NODE: { + code: -32021, + message: "Privacy requires a local node", + meaning: + "mode:\"privacy\" was requested but the caller is not the node's own trusted local originator (loopback / dig.local). A node will not fetch privately on a remote/anonymous caller's behalf (that would hand it the caller's identity + query), so privacy mode requires a local DIG node. See https://docs.dig.net/docs/protocol/onion-routing#toggle-placement.", + }, + ONION_HOPS_OUT_OF_RANGE: { + code: -32022, + message: "Onion hop count out of range", + meaning: + "The requested privacy.hops (circuit length) is outside the allowed range [2, 5] (default 3). Request a hop count within range. See https://docs.dig.net/docs/protocol/onion-routing#modes.", + }, + CONTROL_UNAUTHORIZED: { + code: -32030, + message: "Unauthorized (control)", + meaning: + "A control.* method was called without a valid local control token. Control methods are loopback-only. Control codes are -32030+ so they never collide with the onion codes -32020/21/22.", + }, + CONTROL_NOT_SUPPORTED: { + code: -32031, + message: "Not supported (control)", + meaning: + "A control operation this build/pin cannot perform (e.g. §21 whole-store sync with no loaded identity).", + }, + CONTROL_ERROR: { + code: -32032, + message: "Control error", + meaning: + "A control operation failed at runtime — distinct from bad input or an absent capability.", + }, }; /* ------------------------------------------------------------------ * @@ -664,7 +1050,7 @@ export const rpcErrors = { /** dig RPC (JSON-RPC). `http_or_exit` is the transport status (always 200; the * error rides in the JSON envelope). */ export const rpcCatalog = Object.entries(rpcErrors).map(([name, e]) => ({ - surface: 'dig-rpc', + surface: "dig-rpc", code: e.code, name, http_or_exit: 200, @@ -674,54 +1060,164 @@ export const rpcCatalog = Object.entries(rpcErrors).map(([name, e]) => ({ /** dig-store CLI process exit codes (0..16). Mirrors CliError::exit_code in * dig-store crates/digstore-cli/src/error.rs. */ export const cliCatalog = [ - { exit: 0, code: 'success', description: 'The command completed.' }, - { exit: 1, code: 'other', description: 'An unclassified error. Re-run with --verbose.' }, - { exit: 2, code: 'invalid-argument', description: 'A flag or argument was invalid.' }, - { exit: 3, code: 'no-store', description: 'No store found here. Run `digs init`.' }, - { exit: 4, code: 'not-found', description: "A resource/URN/root wasn't found." }, - { exit: 5, code: 'verification-failed', description: 'Content failed cryptographic verification (wrong salt/key or tampered data).' }, - { exit: 6, code: 'network', description: 'A network/transport failure.' }, - { exit: 7, code: 'non-fast-forward', description: 'The remote root has advanced past yours. Pull first.' }, - { exit: 8, code: 'unauthorized', description: 'Not authorized for this action.' }, - { exit: 9, code: 'no-seed', description: 'No wallet seed is set up.' }, - { exit: 10, code: 'bad-passphrase', description: 'Wrong passphrase for the seed.' }, - { exit: 11, code: 'invalid-mnemonic', description: 'The mnemonic is invalid (check word list / count).' }, - { exit: 12, code: 'insufficient-funds', description: 'Not enough XCH or DIG to cover the spend (the uniform capsule price in $DIG + XCH fee per capsule).' }, - { exit: 13, code: 'chain', description: 'A Chia chain/coinset error.' }, - { exit: 14, code: 'confirm-timeout', description: 'The on-chain confirmation timed out.' }, - { exit: 15, code: 'mint-failed', description: 'Minting the store singleton failed.' }, - { exit: 16, code: 'update-failed', description: 'Anchoring the new root failed.' }, -].map((e) => ({ surface: 'digstore-cli', code: e.code, exit: e.exit, http_or_exit: e.exit, description: e.description })); + { exit: 0, code: "success", description: "The command completed." }, + { exit: 1, code: "other", description: "An unclassified error. Re-run with --verbose." }, + { exit: 2, code: "invalid-argument", description: "A flag or argument was invalid." }, + { exit: 3, code: "no-store", description: "No store found here. Run `digs init`." }, + { exit: 4, code: "not-found", description: "A resource/URN/root wasn't found." }, + { + exit: 5, + code: "verification-failed", + description: "Content failed cryptographic verification (wrong salt/key or tampered data).", + }, + { exit: 6, code: "network", description: "A network/transport failure." }, + { + exit: 7, + code: "non-fast-forward", + description: "The remote root has advanced past yours. Pull first.", + }, + { exit: 8, code: "unauthorized", description: "Not authorized for this action." }, + { exit: 9, code: "no-seed", description: "No wallet seed is set up." }, + { exit: 10, code: "bad-passphrase", description: "Wrong passphrase for the seed." }, + { + exit: 11, + code: "invalid-mnemonic", + description: "The mnemonic is invalid (check word list / count).", + }, + { + exit: 12, + code: "insufficient-funds", + description: + "Not enough XCH or DIG to cover the spend (the uniform capsule price in $DIG + XCH fee per capsule).", + }, + { exit: 13, code: "chain", description: "A Chia chain/coinset error." }, + { exit: 14, code: "confirm-timeout", description: "The on-chain confirmation timed out." }, + { exit: 15, code: "mint-failed", description: "Minting the store singleton failed." }, + { exit: 16, code: "update-failed", description: "Anchoring the new root failed." }, +].map((e) => ({ + surface: "digstore-cli", + code: e.code, + exit: e.exit, + http_or_exit: e.exit, + description: e.description, +})); /** DIGHUb user-facing codes (the web app shows these + a plain-language message). * Mirrors docs/support/error-codes.md; the underlying wire enum is hub * packages/dighub-core/src/error.rs (ErrorCode, with http_status). `wire` is the * closest snake_case wire code + its HTTP status where one exists. */ export const dighubCatalog = [ - { code: 'WALLET_DECLINED', http: null, wire: null, description: 'You declined the signature in your wallet. Nothing was signed or broadcast.' }, - { code: 'DIG_INSUFFICIENT', http: 429, wire: 'over_quota', description: "Not enough $DIG in your wallet to cover this capsule's price." }, - { code: 'COIN_CONFLICT', http: 409, wire: 'coin_reserved', description: 'The coin was just spent elsewhere (double-spend / mempool conflict).' }, - { code: 'REG_PENDING', http: null, wire: null, description: 'Your spend is on chain and will appear shortly. Do not sign or pay again.' }, - { code: 'WALLET_SESSION', http: 401, wire: 'unauthorized', description: "Your wallet session can't sign (expired, watch-only, or missing method)." }, - { code: 'NET_OFFLINE', http: null, wire: null, description: 'You appear to be offline.' }, - { code: 'NET_TIMEOUT', http: null, wire: null, description: "The request timed out or couldn't reach the network." }, - { code: 'SLUG_TAKEN', http: 409, wire: 'slug_taken', description: 'That store name is already taken.' }, - { code: 'OVER_QUOTA', http: 429, wire: 'over_quota', description: "You've reached the store limit for this account." }, - { code: 'COIN_RESERVED', http: 409, wire: 'coin_reserved', description: 'A coin is busy finishing another transaction.' }, - { code: 'UNAUTHORIZED', http: 401, wire: 'unauthorized', description: "Your session isn't authorized for this." }, - { code: 'FORBIDDEN', http: 403, wire: 'forbidden', description: "You don't have permission to do this." }, - { code: 'NOT_FOUND', http: 404, wire: 'not_found', description: 'The thing could not be found — it may have expired.' }, - { code: 'INVALID_REQUEST', http: 400, wire: 'invalid_request', description: "Something about the request wasn't valid." }, - { code: 'UNEXPECTED', http: 500, wire: null, description: 'An unclassified error.' }, -].map((e) => ({ surface: 'dighub', code: e.code, http: e.http, wire: e.wire, http_or_exit: e.http, description: e.description })); + { + code: "WALLET_DECLINED", + http: null, + wire: null, + description: "You declined the signature in your wallet. Nothing was signed or broadcast.", + }, + { + code: "DIG_INSUFFICIENT", + http: 429, + wire: "over_quota", + description: "Not enough $DIG in your wallet to cover this capsule's price.", + }, + { + code: "COIN_CONFLICT", + http: 409, + wire: "coin_reserved", + description: "The coin was just spent elsewhere (double-spend / mempool conflict).", + }, + { + code: "REG_PENDING", + http: null, + wire: null, + description: "Your spend is on chain and will appear shortly. Do not sign or pay again.", + }, + { + code: "WALLET_SESSION", + http: 401, + wire: "unauthorized", + description: "Your wallet session can't sign (expired, watch-only, or missing method).", + }, + { code: "NET_OFFLINE", http: null, wire: null, description: "You appear to be offline." }, + { + code: "NET_TIMEOUT", + http: null, + wire: null, + description: "The request timed out or couldn't reach the network.", + }, + { + code: "SLUG_TAKEN", + http: 409, + wire: "slug_taken", + description: "That store name is already taken.", + }, + { + code: "OVER_QUOTA", + http: 429, + wire: "over_quota", + description: "You've reached the store limit for this account.", + }, + { + code: "COIN_RESERVED", + http: 409, + wire: "coin_reserved", + description: "A coin is busy finishing another transaction.", + }, + { + code: "UNAUTHORIZED", + http: 401, + wire: "unauthorized", + description: "Your session isn't authorized for this.", + }, + { + code: "FORBIDDEN", + http: 403, + wire: "forbidden", + description: "You don't have permission to do this.", + }, + { + code: "NOT_FOUND", + http: 404, + wire: "not_found", + description: "The thing could not be found — it may have expired.", + }, + { + code: "INVALID_REQUEST", + http: 400, + wire: "invalid_request", + description: "Something about the request wasn't valid.", + }, + { code: "UNEXPECTED", http: 500, wire: null, description: "An unclassified error." }, +].map((e) => ({ + surface: "dighub", + code: e.code, + http: e.http, + wire: e.wire, + http_or_exit: e.http, + description: e.description, +})); /** chia:// content loader codes (the loader in the DIG Browser / extension). * The fail-closed loader's catalogued failure taxonomy, surfaced on the error * page so an agent can branch on the cause. Per the ecosystem agent-friendly * plan for dig-browser / dig-chrome-extension. */ export const loaderCatalog = [ - { code: 'DIG_ERR_PROOF_MISMATCH', description: 'The served ciphertext did not verify against the on-chain generation root (tamper / wrong root).' }, - { code: 'DIG_ERR_DECRYPT_TAG', description: 'AES-256-GCM-SIV authentication tag failed — wrong key/salt or corrupted bytes.' }, - { code: 'DIG_ERR_NOT_FOUND', description: 'A blind miss (decoy) — no resource at this retrieval key under this generation.' }, - { code: 'DIG_ERR_NETWORK', description: 'The node/CDN was unreachable or the transport failed.' }, -].map((e) => ({ surface: 'dig-loader', code: e.code, http_or_exit: null, description: e.description })); + { + code: "DIG_ERR_PROOF_MISMATCH", + description: + "The served ciphertext did not verify against the on-chain generation root (tamper / wrong root).", + }, + { + code: "DIG_ERR_DECRYPT_TAG", + description: "AES-256-GCM-SIV authentication tag failed — wrong key/salt or corrupted bytes.", + }, + { + code: "DIG_ERR_NOT_FOUND", + description: "A blind miss (decoy) — no resource at this retrieval key under this generation.", + }, + { code: "DIG_ERR_NETWORK", description: "The node/CDN was unreachable or the transport failed." }, +].map((e) => ({ + surface: "dig-loader", + code: e.code, + http_or_exit: null, + description: e.description, +})); diff --git a/scripts/gen-hreflang-sitemaps.mjs b/scripts/gen-hreflang-sitemaps.mjs index 46fa69b..bcf8e63 100644 --- a/scripts/gen-hreflang-sitemaps.mjs +++ b/scripts/gen-hreflang-sitemaps.mjs @@ -105,9 +105,15 @@ function annotateSitemap(sitemapPath, locale) { hreflang: code, href: buildAlternateUrl(logicalPath, code), })); - alternates.push({ hreflang: "x-default", href: buildAlternateUrl(logicalPath, DEFAULT_LOCALE) }); + alternates.push({ + hreflang: "x-default", + href: buildAlternateUrl(logicalPath, DEFAULT_LOCALE), + }); const links = alternates - .map((a) => ``) + .map( + (a) => + ``, + ) .join(""); return `${ws}${loc}${links}`; }); @@ -118,7 +124,9 @@ function annotateSitemap(sitemapPath, locale) { function main() { if (!fs.existsSync(distDir)) { - console.error(`gen-hreflang-sitemaps: dist/ not found at ${distDir} — run "docusaurus build" first.`); + console.error( + `gen-hreflang-sitemaps: dist/ not found at ${distDir} — run "docusaurus build" first.`, + ); process.exit(1); } @@ -137,11 +145,15 @@ function main() { const count = annotateSitemap(sitemapPath, locale); totalFiles += 1; totalUrls += count; - console.log(`gen-hreflang-sitemaps: ${path.relative(distDir, sitemapPath)} — ${count} url(s) annotated with ${LOCALES.length + 1} alternates`); + console.log( + `gen-hreflang-sitemaps: ${path.relative(distDir, sitemapPath)} — ${count} url(s) annotated with ${LOCALES.length + 1} alternates`, + ); } if (totalFiles === 0) { - console.error("gen-hreflang-sitemaps: no sitemap.xml found under dist/ for any configured locale."); + console.error( + "gen-hreflang-sitemaps: no sitemap.xml found under dist/ for any configured locale.", + ); process.exit(1); } diff --git a/scripts/gen-knowledge-graph.mjs b/scripts/gen-knowledge-graph.mjs index fb6b4c7..c35393e 100644 --- a/scripts/gen-knowledge-graph.mjs +++ b/scripts/gen-knowledge-graph.mjs @@ -13,76 +13,76 @@ * node: { id, type: "concept" | "doc", title, url, [tags] } * edge: { from, to, type: "defines"|"part-of"|"relates-to"|"requires"|"see-also" } */ -import fs from 'node:fs'; -import path from 'node:path'; -import {fileURLToPath, pathToFileURL} from 'node:url'; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const ROOT = path.resolve(__dirname, '..'); -const DOCS = path.join(ROOT, 'docs'); -const SITE = 'https://docs.dig.net'; +const ROOT = path.resolve(__dirname, ".."); +const DOCS = path.join(ROOT, "docs"); +const SITE = "https://docs.dig.net"; // The controlled vocabulary: each tag (graph concept node) → the doc that is // its canonical definition. The concepts glossary `defines` every term. const CONCEPT_TITLES = { - capsule: 'Capsule', - store: 'Store', - generation: 'Generation', - urn: 'URN', - 'retrieval-key': 'Retrieval key', - encryption: 'Encryption', - 'merkle-proof': 'Merkle proof', - anchoring: 'On-chain anchoring', - 'dig-payment': 'DIG payment', - 'digstore-cli': 'dig-store CLI', - 'dig-toml': 'dig.toml', - 'create-dig-app': 'create-dig-app', - 'deploy-action': 'The GitHub deploy Action', - 'dig-sdk': 'DIG SDK', - 'dig-rpc': 'The dig RPC', - streaming: 'Streaming', - 'chia-protocol': 'The chia:// protocol', - 'window-chia': 'window.chia', - 'provider-spec': 'window.chia provider spec', - browser: 'DIG Browser', - 'chip-0002': 'CHIP-0002', - 'chip-0035': 'CHIP-0035', - dighub: 'DIGHUb', + capsule: "Capsule", + store: "Store", + generation: "Generation", + urn: "URN", + "retrieval-key": "Retrieval key", + encryption: "Encryption", + "merkle-proof": "Merkle proof", + anchoring: "On-chain anchoring", + "dig-payment": "DIG payment", + "digstore-cli": "dig-store CLI", + "dig-toml": "dig.toml", + "create-dig-app": "create-dig-app", + "deploy-action": "The GitHub deploy Action", + "dig-sdk": "DIG SDK", + "dig-rpc": "The dig RPC", + streaming: "Streaming", + "chia-protocol": "The chia:// protocol", + "window-chia": "window.chia", + "provider-spec": "window.chia provider spec", + browser: "DIG Browser", + "chip-0002": "CHIP-0002", + "chip-0035": "CHIP-0035", + dighub: "DIGHUb", }; /** Recursively collect .md files under docs/. */ function walk(dir) { const out = []; - for (const entry of fs.readdirSync(dir, {withFileTypes: true})) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { const p = path.join(dir, entry.name); if (entry.isDirectory()) out.push(...walk(p)); - else if (entry.name.endsWith('.md')) out.push(p); + else if (entry.name.endsWith(".md")) out.push(p); } return out; } /** docs/rpc/methods.md -> /docs/rpc/methods ; docs/intro.md -> /docs (slug: /) */ export function docUrlPath(absFile, frontMatter) { - if (frontMatter.slug === '/') return '/docs'; - let rel = path.relative(DOCS, absFile).replace(/\\/g, '/').replace(/\.md$/, ''); - rel = rel.replace(/\/index$/, ''); // run-a-node/index -> run-a-node + if (frontMatter.slug === "/") return "/docs"; + let rel = path.relative(DOCS, absFile).replace(/\\/g, "/").replace(/\.md$/, ""); + rel = rel.replace(/\/index$/, ""); // run-a-node/index -> run-a-node return `/docs/${rel}`; } /** Minimal frontmatter parser: title, slug, and the tags list. */ export function parseFrontMatter(src) { const m = src.match(/^---\n([\s\S]*?)\n---/); - const fm = {tags: []}; + const fm = { tags: [] }; if (!m) return fm; const body = m[1]; const title = body.match(/^title:\s*"?(.+?)"?\s*$/m); - if (title) fm.title = title[1].replace(/^"|"$/g, ''); + if (title) fm.title = title[1].replace(/^"|"$/g, ""); const slug = body.match(/^slug:\s*(.+?)\s*$/m); if (slug) fm.slug = slug[1].trim(); const tagsBlock = body.match(/^tags:\n((?:\s*-\s*.+\n?)+)/m); if (tagsBlock) { fm.tags = tagsBlock[1] - .split('\n') + .split("\n") .map((l) => l.match(/^\s*-\s*(.+?)\s*$/)) .filter(Boolean) .map((mm) => mm[1]); @@ -92,13 +92,13 @@ export function parseFrontMatter(src) { /** Resolve a relative .md link from a source file into a /docs URL path. */ export function resolveRelLink(fromFile, href) { - const clean = href.split('#')[0]; - if (!clean.endsWith('.md')) return null; + const clean = href.split("#")[0]; + if (!clean.endsWith(".md")) return null; const abs = path.resolve(path.dirname(fromFile), clean); if (!abs.startsWith(DOCS)) return null; - let rel = path.relative(DOCS, abs).replace(/\\/g, '/').replace(/\.md$/, ''); - rel = rel.replace(/\/index$/, ''); - if (rel === 'intro') return '/docs'; + let rel = path.relative(DOCS, abs).replace(/\\/g, "/").replace(/\.md$/, ""); + rel = rel.replace(/\/index$/, ""); + if (rel === "intro") return "/docs"; return `/docs/${rel}`; } @@ -115,13 +115,13 @@ export function parseRelated(src, fromFile) { // genuine prerequisites (`requires`); every other related link is `see-also`. // An explicit table rather than a guess keeps the edge types deterministic. const REQUIRES = new Set([ - '/docs/digstore/cli/quickstart|->|/docs/digstore/cli/install', - '/docs/digstore/cli/quickstart|->|/docs/digstore/cli/onchain-anchoring', - '/docs/digstore/cli/onchain-anchoring|->|/docs/digstore/cli/quickstart', - '/docs/digstore/cli/sharing|->|/docs/digstore/cli/onchain-anchoring', + "/docs/digstore/cli/quickstart|->|/docs/digstore/cli/install", + "/docs/digstore/cli/quickstart|->|/docs/digstore/cli/onchain-anchoring", + "/docs/digstore/cli/onchain-anchoring|->|/docs/digstore/cli/quickstart", + "/docs/digstore/cli/sharing|->|/docs/digstore/cli/onchain-anchoring", ]); -const CONCEPTS_DOC = '/docs/concepts'; +const CONCEPTS_DOC = "/docs/concepts"; /** Build the full knowledge graph from the docs tree. */ function buildGraph() { @@ -131,41 +131,41 @@ function buildGraph() { const edges = []; for (const file of files) { - const src = fs.readFileSync(file, 'utf8'); + const src = fs.readFileSync(file, "utf8"); const fm = parseFrontMatter(src); const url = docUrlPath(file, fm); - const title = fm.title || path.basename(file, '.md'); - docNodes.set(url, {id: url, type: 'doc', title, url: SITE + url, tags: fm.tags}); - docFront.set(url, {tags: fm.tags, related: parseRelated(src, file)}); + const title = fm.title || path.basename(file, ".md"); + docNodes.set(url, { id: url, type: "doc", title, url: SITE + url, tags: fm.tags }); + docFront.set(url, { tags: fm.tags, related: parseRelated(src, file) }); } // Concept nodes (one per controlled-vocabulary tag), keyed by their tag page. const conceptNodes = []; for (const [tag, title] of Object.entries(CONCEPT_TITLES)) { const url = `/docs/tags/${tag}`; - conceptNodes.push({id: `concept:${tag}`, type: 'concept', title, url: SITE + url, tag}); + conceptNodes.push({ id: `concept:${tag}`, type: "concept", title, url: SITE + url, tag }); } // Edge: concepts glossary `defines` every concept. for (const tag of Object.keys(CONCEPT_TITLES)) { - edges.push({from: CONCEPTS_DOC, to: `concept:${tag}`, type: 'defines'}); + edges.push({ from: CONCEPTS_DOC, to: `concept:${tag}`, type: "defines" }); } // Edge: each doc is `part-of` every concept it is tagged with (doc -> concept). - for (const [url, {tags}] of docFront) { + for (const [url, { tags }] of docFront) { for (const tag of tags) { if (CONCEPT_TITLES[tag]) { - edges.push({from: url, to: `concept:${tag}`, type: 'part-of'}); + edges.push({ from: url, to: `concept:${tag}`, type: "part-of" }); } } } // Edge: "## Related" links become typed doc->doc edges. - for (const [url, {related}] of docFront) { + for (const [url, { related }] of docFront) { for (const to of related) { if (to === url) continue; const key = `${url}|->|${to}`; - edges.push({from: url, to, type: REQUIRES.has(key) ? 'requires' : 'see-also'}); + edges.push({ from: url, to, type: REQUIRES.has(key) ? "requires" : "see-also" }); } } @@ -179,8 +179,8 @@ function buildGraph() { function main() { const graph = buildGraph(); - const outPath = path.join(ROOT, 'static', 'knowledge-graph.json'); - fs.writeFileSync(outPath, JSON.stringify(graph, null, 2) + '\n'); + const outPath = path.join(ROOT, "static", "knowledge-graph.json"); + fs.writeFileSync(outPath, JSON.stringify(graph, null, 2) + "\n"); console.log( `knowledge-graph.json: ${graph.nodes.length} nodes, ${graph.edges.length} edges -> ${path.relative(ROOT, outPath)}`, ); diff --git a/scripts/gen-machine-specs.mjs b/scripts/gen-machine-specs.mjs index aeaa1ab..7bf77f9 100644 --- a/scripts/gen-machine-specs.mjs +++ b/scripts/gen-machine-specs.mjs @@ -23,9 +23,9 @@ * * node scripts/gen-machine-specs.mjs */ -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; import { SITE, RPC_ENDPOINT, @@ -37,15 +37,15 @@ import { cliCatalog, dighubCatalog, loaderCatalog, -} from './dig-spec.mjs'; +} from "./dig-spec.mjs"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const ROOT = path.resolve(__dirname, '..'); -const STATIC = path.join(ROOT, 'static'); -const DOCS = path.join(ROOT, 'docs'); +const ROOT = path.resolve(__dirname, ".."); +const STATIC = path.join(ROOT, "static"); +const DOCS = path.join(ROOT, "docs"); // Read the docs version so the artifacts carry a real, comparable version. -const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8')); +const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, "package.json"), "utf8")); /* ------------------------------------------------------------------ * * OpenRPC document @@ -55,7 +55,7 @@ function buildOpenRpc({ title, methodSet, info, servers }) { // A named, reusable JSON-RPC error component per catalogued code. const errorComponents = {}; for (const [name, e] of Object.entries(rpcErrors)) { - errorComponents[name] = { code: e.code, message: e.message, data: { type: 'object' } }; + errorComponents[name] = { code: e.code, message: e.message, data: { type: "object" } }; } const openrpcMethods = methodSet.map((m) => ({ @@ -74,17 +74,17 @@ function buildOpenRpc({ title, methodSet, info, servers }) { })); return { - openrpc: '1.2.6', + openrpc: "1.2.6", info: { title, - version: pkg.version && pkg.version !== '0.0.0' ? pkg.version : '1.0.0', + version: pkg.version && pkg.version !== "0.0.0" ? pkg.version : "1.0.0", description: info, - license: { name: 'GPL-2.0', url: 'https://github.com/DIG-Network/digs/blob/main/LICENSE' }, + license: { name: "GPL-2.0", url: "https://github.com/DIG-Network/digs/blob/main/LICENSE" }, }, servers, methods: openrpcMethods, components: { schemas, errors: errorComponents }, - externalDocs: { description: 'dig RPC methods (prose)', url: `${SITE}/docs/protocol/dig-rpc` }, + externalDocs: { description: "dig RPC methods (prose)", url: `${SITE}/docs/protocol/dig-rpc` }, }; } @@ -97,15 +97,19 @@ function buildErrorCatalog() { const bySurface = {}; for (const row of all) (bySurface[row.surface] ??= []).push(row); return { - $schema: 'https://json-schema.org/draft/2020-12/schema', - generatedFrom: 'scripts/dig-spec.mjs (mirrors docs/support/error-codes.md + the source enums)', + $schema: "https://json-schema.org/draft/2020-12/schema", + generatedFrom: "scripts/dig-spec.mjs (mirrors docs/support/error-codes.md + the source enums)", site: SITE, version: 1, surfaces: { - 'dig-rpc': 'JSON-RPC 2.0 codes for the dig RPC (rpc.dig.net). http_or_exit is the transport status (always 200; the error rides in the JSON envelope).', - 'digstore-cli': 'dig-store CLI process exit codes (0..16). http_or_exit is the process exit code.', - dighub: 'DIGHUb (hub.dig.net) user-facing codes. http_or_exit is the HTTP status where one exists.', - 'dig-loader': 'chia:// content-loader failure codes (DIG Browser / extension). http_or_exit is null (surfaced on the error page).', + "dig-rpc": + "JSON-RPC 2.0 codes for the dig RPC (rpc.dig.net). http_or_exit is the transport status (always 200; the error rides in the JSON envelope).", + "digstore-cli": + "dig-store CLI process exit codes (0..16). http_or_exit is the process exit code.", + dighub: + "DIGHUb (hub.dig.net) user-facing codes. http_or_exit is the HTTP status where one exists.", + "dig-loader": + "chia:// content-loader failure codes (DIG Browser / extension). http_or_exit is null (surfaced on the error page).", }, errors: all, bySurface, @@ -123,16 +127,16 @@ function tableCodes(md, sectionHeading) { const start = md.indexOf(`## ${sectionHeading}`); if (start < 0) throw new Error(`error-codes.md: section "## ${sectionHeading}" not found`); const rest = md.slice(start + 3); - const end = rest.indexOf('\n## '); + const end = rest.indexOf("\n## "); const body = end < 0 ? rest : rest.slice(0, end); const codes = new Set(); - for (const line of body.split('\n')) { - const cells = line.split('|').map((c) => c.trim()); + for (const line of body.split("\n")) { + const cells = line.split("|").map((c) => c.trim()); // A data row looks like: | `code` | … | (leading empty cell from the pipe). // Skip header/separator rows. if (cells.length < 3) continue; const first = cells[1]; - if (!first || first === 'Code' || first === 'Exit' || /^-+$/.test(first)) continue; + if (!first || first === "Code" || first === "Exit" || /^-+$/.test(first)) continue; const m = first.match(/`([^`]+)`/); if (m) codes.add(m[1]); } @@ -146,60 +150,67 @@ function assertSetEqual(label, fromJson, fromMd) { const onlyMd = [...b].filter((x) => !a.has(x)); if (onlyJson.length || onlyMd.length) { const parts = []; - if (onlyJson.length) parts.push(`only in error-codes.json: ${onlyJson.join(', ')}`); - if (onlyMd.length) parts.push(`only in error-codes.md: ${onlyMd.join(', ')}`); - throw new Error(`error-codes drift for ${label} — ${parts.join('; ')}`); + if (onlyJson.length) parts.push(`only in error-codes.json: ${onlyJson.join(", ")}`); + if (onlyMd.length) parts.push(`only in error-codes.md: ${onlyMd.join(", ")}`); + throw new Error(`error-codes drift for ${label} — ${parts.join("; ")}`); } } function driftGate(catalog) { - const md = fs.readFileSync(path.join(DOCS, 'support', 'error-codes.md'), 'utf8'); + const md = fs.readFileSync(path.join(DOCS, "support", "error-codes.md"), "utf8"); // dig RPC: prose lists the numeric -32xxx codes (as `-327..`). assertSetEqual( - 'dig-rpc', - catalog.bySurface['dig-rpc'].map((e) => String(e.code)), - tableCodes(md, 'dig RPC (JSON-RPC)'), + "dig-rpc", + catalog.bySurface["dig-rpc"].map((e) => String(e.code)), + tableCodes(md, "dig RPC (JSON-RPC)"), ); // dig-store CLI: prose first column is the Exit number. assertSetEqual( - 'digstore-cli', - catalog.bySurface['digstore-cli'].map((e) => String(e.exit)), - tableCodes(md, 'dig-store CLI (exit codes)'), + "digstore-cli", + catalog.bySurface["digstore-cli"].map((e) => String(e.exit)), + tableCodes(md, "dig-store CLI (exit codes)"), ); // DIGHUb: prose first column is the UPPER_SNAKE code. assertSetEqual( - 'dighub', - catalog.bySurface['dighub'].map((e) => e.code), - tableCodes(md, 'DIGHUb (web app)'), + "dighub", + catalog.bySurface["dighub"].map((e) => e.code), + tableCodes(md, "DIGHUb (web app)"), ); // chia:// loader: prose first column is the DIG_ERR_* code. assertSetEqual( - 'dig-loader', - catalog.bySurface['dig-loader'].map((e) => e.code), - tableCodes(md, 'chia:// content loader'), + "dig-loader", + catalog.bySurface["dig-loader"].map((e) => e.code), + tableCodes(md, "chia:// content loader"), ); } /* ------------------------------------------------------------------ */ const openrpc = buildOpenRpc({ - title: 'dig RPC — DIG Network Content Interface (network profile)', + title: "dig RPC — DIG Network Content Interface (network profile)", methodSet: methods, - servers: [{ name: 'rpc.dig.net', url: RPC_ENDPOINT, summary: 'The public DIG Network read endpoint.' }], - info: - 'The network-wide read interface for DIG content over JSON-RPC 2.0 — the NETWORK PROFILE served by the canonical node at rpc.dig.net. Blind by construction (the node holds no URN and no key), verifiable without trust (merkle inclusion proofs against the chain-anchored root), and streamable at any size. There is no `decoy` field on the wire and no CDN. See https://docs.dig.net/docs/protocol/dig-rpc.', + servers: [ + { name: "rpc.dig.net", url: RPC_ENDPOINT, summary: "The public DIG Network read endpoint." }, + ], + info: "The network-wide read interface for DIG content over JSON-RPC 2.0 — the NETWORK PROFILE served by the canonical node at rpc.dig.net. Blind by construction (the node holds no URN and no key), verifiable without trust (merkle inclusion proofs against the chain-anchored root), and streamable at any size. There is no `decoy` field on the wire and no CDN. See https://docs.dig.net/docs/protocol/dig-rpc.", }); const openrpcNode = buildOpenRpc({ - title: 'dig RPC — node profile (local dig-node / in-process DIG Browser)', + title: "dig RPC — node profile (local dig-node / in-process DIG Browser)", methodSet: nodeMethods, - servers: [{ name: 'local dig-node', url: 'http://127.0.0.1:9778', summary: 'The local node the DIG Browser runs in-process (FFI) and dig-node serves on 127.0.0.1:9778.' }], - info: - 'The NODE PROFILE: a distinct, smaller surface than the network profile. Of the byte methods it implements ONLY dig.getContent (local-first, else proxy); everything else proxies upstream or returns -32601. It ADDS node-only methods the security model depends on — chiefly dig.getAnchoredRoot (the CHIP-0035 on-chain head, the trusted root for mandatory root-pinning), dig.stage, and cache.*. Gate on dig.methods rather than assuming one uniform surface. See https://docs.dig.net/docs/protocol/dig-rpc#node-profile.', + servers: [ + { + name: "local dig-node", + url: "http://127.0.0.1:9778", + summary: + "The local node the DIG Browser runs in-process (FFI) and dig-node serves on 127.0.0.1:9778.", + }, + ], + info: "The NODE PROFILE: a distinct, smaller surface than the network profile. Of the byte methods it implements ONLY dig.getContent (local-first, else proxy); everything else proxies upstream or returns -32601. It ADDS node-only methods the security model depends on — chiefly dig.getAnchoredRoot (the CHIP-0035 on-chain head, the trusted root for mandatory root-pinning), dig.stage, and cache.*. Gate on dig.methods rather than assuming one uniform surface. See https://docs.dig.net/docs/protocol/dig-rpc#node-profile.", }); const catalog = buildErrorCatalog(); @@ -207,9 +218,12 @@ const catalog = buildErrorCatalog(); driftGate(catalog); fs.mkdirSync(STATIC, { recursive: true }); -fs.writeFileSync(path.join(STATIC, 'openrpc.json'), JSON.stringify(openrpc, null, 2) + '\n'); -fs.writeFileSync(path.join(STATIC, 'openrpc-node.json'), JSON.stringify(openrpcNode, null, 2) + '\n'); -fs.writeFileSync(path.join(STATIC, 'error-codes.json'), JSON.stringify(catalog, null, 2) + '\n'); +fs.writeFileSync(path.join(STATIC, "openrpc.json"), JSON.stringify(openrpc, null, 2) + "\n"); +fs.writeFileSync( + path.join(STATIC, "openrpc-node.json"), + JSON.stringify(openrpcNode, null, 2) + "\n", +); +fs.writeFileSync(path.join(STATIC, "error-codes.json"), JSON.stringify(catalog, null, 2) + "\n"); console.log( `openrpc.json: ${openrpc.methods.length} methods (network); openrpc-node.json: ${openrpcNode.methods.length} methods (node); ` + diff --git a/sidebars.ts b/sidebars.ts index cac3abe..66388d0 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -1,4 +1,4 @@ -import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; +import type { SidebarsConfig } from "@docusaurus/plugin-content-docs"; /** * Audience-first IA (roadmap #113), two top-level sections: @@ -22,130 +22,125 @@ import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; const sidebars: SidebarsConfig = { tutorialSidebar: [ { - type: 'category', - label: 'Start here', + type: "category", + label: "Start here", collapsible: true, collapsed: false, - items: ['intro', 'quickstart', 'concepts'], + items: ["intro", "quickstart", "concepts"], }, { - type: 'category', - label: 'Using DIG', + type: "category", + label: "Using DIG", collapsible: true, collapsed: false, - link: {type: 'doc', id: 'audiences/app-developers'}, + link: { type: "doc", id: "audiences/app-developers" }, items: [ { - type: 'category', - label: 'App developers', + type: "category", + label: "App developers", collapsible: true, collapsed: true, - link: {type: 'doc', id: 'audiences/app-developers'}, + link: { type: "doc", id: "audiences/app-developers" }, items: [ - 'journeys/hub-user', - 'journeys/connect-your-wallet', - 'journeys/digstore-cli', - 'quickstart', - 'digstore/cli/install', - 'digstore/cli/quickstart', - 'digstore/cli/project-workflow', - 'build-a-dapp/scaffold', - 'digstore/cli/configuration', - 'digstore/cli/onchain-anchoring', - 'digstore/cli/sharing', - 'build-a-dapp/tip-your-store', - 'build-a-dapp/submit-to-the-store', - 'digstore/cli/deploy-from-github-actions', - 'automation/deploy-keys', - 'automation/webhooks', + "journeys/hub-user", + "journeys/connect-your-wallet", + "journeys/digstore-cli", + "quickstart", + "digstore/cli/install", + "digstore/cli/quickstart", + "digstore/cli/project-workflow", + "build-a-dapp/scaffold", + "digstore/cli/configuration", + "digstore/cli/onchain-anchoring", + "digstore/cli/sharing", + "build-a-dapp/tip-your-store", + "build-a-dapp/submit-to-the-store", + "digstore/cli/deploy-from-github-actions", + "automation/deploy-keys", + "automation/webhooks", ], }, { - type: 'category', - label: 'NFT developers', + type: "category", + label: "NFT developers", collapsible: true, collapsed: true, - link: {type: 'doc', id: 'audiences/nft-developers'}, - items: [ - 'build-a-dapp/scaffold', - 'digstore/cli/command-reference', - 'spends', - 'sdk', - ], + link: { type: "doc", id: "audiences/nft-developers" }, + items: ["build-a-dapp/scaffold", "digstore/cli/command-reference", "spends", "sdk"], }, { - type: 'category', - label: 'Integration developers', + type: "category", + label: "Integration developers", collapsible: true, collapsed: true, - link: {type: 'doc', id: 'audiences/integration-developers'}, + link: { type: "doc", id: "audiences/integration-developers" }, items: [ - 'journeys/integrate', - 'build-a-dapp/tutorial', - 'build-a-dapp/example-gallery', - 'sdk', - 'browser/using-window-chia', - 'browser/window-chia-reference', - 'browser/wallet-security', - 'rpc/what-is-the-dig-rpc', - 'rpc/methods', - 'rpc/streaming', - 'rpc/public-network-rpc', - 'digstore/cli/streaming-and-keys', - 'spends', - 'machine-surfaces', - 'digstore/cli/deploy-from-github-actions', - 'automation/deploy-keys', - 'automation/webhooks', - 'support/error-codes', + "journeys/integrate", + "build-a-dapp/tutorial", + "build-a-dapp/example-gallery", + "sdk", + "browser/using-window-chia", + "browser/window-chia-reference", + "browser/wallet-security", + "rpc/what-is-the-dig-rpc", + "rpc/methods", + "rpc/streaming", + "rpc/public-network-rpc", + "digstore/cli/streaming-and-keys", + "spends", + "machine-surfaces", + "digstore/cli/deploy-from-github-actions", + "automation/deploy-keys", + "automation/webhooks", + "support/error-codes", ], }, { - type: 'category', - label: 'Node operators', + type: "category", + label: "Node operators", collapsible: true, collapsed: true, - link: {type: 'doc', id: 'run-a-node/index'}, + link: { type: "doc", id: "run-a-node/index" }, items: [ - 'run-a-node/index', - 'run-a-node/apt', - 'run-a-node/universal-installer', - 'run-a-node/dns', - 'run-a-node/point-a-consumer', - 'run-a-node/configure', - 'run-a-node/run-a-relay', - 'rpc/dig-remote', - 'rpc/conformance', - 'run-a-node/manage', - 'run-a-node/control-panel', + "run-a-node/index", + "run-a-node/apt", + "run-a-node/universal-installer", + "run-a-node/dns", + "run-a-node/point-a-consumer", + "run-a-node/configure", + "run-a-node/run-a-relay", + "rpc/dig-remote", + "rpc/conformance", + "run-a-node/manage", + "run-a-node/control-panel", ], }, { - type: 'category', - label: 'Content consumers', + type: "category", + label: "Content consumers", collapsible: true, collapsed: true, - link: {type: 'doc', id: 'audiences/content-consumers'}, + link: { type: "doc", id: "audiences/content-consumers" }, items: [ - 'browser/chia-protocol', - 'browser/using-window-chia', - 'browser/wallet-security', - 'run-a-node/point-a-consumer', + "browser/chia-protocol", + "browser/using-window-chia", + "browser/wallet-security", + "run-a-node/point-a-consumer", ], }, { - type: 'category', - label: 'Troubleshooting', + type: "category", + label: "Troubleshooting", collapsible: true, collapsed: true, - link: {type: 'doc', id: 'audiences/troubleshooting'}, + link: { type: "doc", id: "audiences/troubleshooting" }, items: [ - 'support/troubleshooting', - 'support/error-codes', - 'support/faq', - 'support/get-help', - 'support/status', - 'support/changelog', + "support/troubleshooting", + "support/error-codes", + "support/faq", + "support/get-help", + "support/status", + "support/changelog", ], }, ], @@ -157,51 +152,51 @@ const sidebars: SidebarsConfig = { // machine interface and live here too. // The task-oriented format/RPC/CLI pages are kept under "Reference & guides" // so the dense cross-link web between docs stays intact. - type: 'category', - label: 'Protocol', + type: "category", + label: "Protocol", collapsible: true, collapsed: true, - link: {type: 'doc', id: 'protocol-deep-dive'}, + link: { type: "doc", id: "protocol-deep-dive" }, items: [ - 'protocol-deep-dive', - 'protocol/identity-and-naming', - 'protocol/urn-and-addressing', - 'protocol/cryptography', - 'protocol/merkle-proofs', - 'protocol/bls-signatures', - 'protocol/capsule-format', - 'protocol/self-defending-module', - 'protocol/on-chain-anchoring', - 'protocol/dig-cat-payment', - 'protocol/dig-rpc', - 'protocol/transport-and-push', - 'protocol/peer-network', - 'protocol/onion-routing', - 'protocol/verification-and-provenance', - 'protocol/blind-host-model', - 'protocol/conformance-and-parity', + "protocol-deep-dive", + "protocol/identity-and-naming", + "protocol/urn-and-addressing", + "protocol/cryptography", + "protocol/merkle-proofs", + "protocol/bls-signatures", + "protocol/capsule-format", + "protocol/self-defending-module", + "protocol/on-chain-anchoring", + "protocol/dig-cat-payment", + "protocol/dig-rpc", + "protocol/transport-and-push", + "protocol/peer-network", + "protocol/onion-routing", + "protocol/verification-and-provenance", + "protocol/blind-host-model", + "protocol/conformance-and-parity", { - type: 'category', - label: 'Reference & guides', + type: "category", + label: "Reference & guides", collapsible: true, collapsed: true, items: [ - 'concepts', - 'digstore/format/store-structure', - 'digstore/what-is-digstore', - 'digstore/format/overview', - 'digstore/format/urns-and-encryption', - 'digstore/format/proofs-and-security', - 'digstore/cli/onchain-anchoring', - 'rpc/what-is-the-dig-rpc', - 'rpc/methods', - 'rpc/conformance', - 'rpc/streaming', - 'rpc/public-network-rpc', - 'inclusion-vs-execution-proofs', - 'rpc/dig-remote', - 'protocol/window-chia-provider', - 'chip-0035-spends-and-delegation', + "concepts", + "digstore/format/store-structure", + "digstore/what-is-digstore", + "digstore/format/overview", + "digstore/format/urns-and-encryption", + "digstore/format/proofs-and-security", + "digstore/cli/onchain-anchoring", + "rpc/what-is-the-dig-rpc", + "rpc/methods", + "rpc/conformance", + "rpc/streaming", + "rpc/public-network-rpc", + "inclusion-vs-execution-proofs", + "rpc/dig-remote", + "protocol/window-chia-provider", + "chip-0035-spends-and-delegation", ], }, ], diff --git a/src/css/custom.css b/src/css/custom.css index 2844cf3..7801d5f 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -7,10 +7,34 @@ /* ---------- Brand fonts ---------- */ @import url("https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&display=swap"); -@font-face { font-family: "Space Grotesk"; font-weight: 400; font-style: normal; font-display: swap; src: url("/fonts/space-grotesk-400.ttf") format("truetype"); } -@font-face { font-family: "Space Grotesk"; font-weight: 500; font-style: normal; font-display: swap; src: url("/fonts/space-grotesk-500.ttf") format("truetype"); } -@font-face { font-family: "Space Grotesk"; font-weight: 600; font-style: normal; font-display: swap; src: url("/fonts/space-grotesk-600.ttf") format("truetype"); } -@font-face { font-family: "Space Grotesk"; font-weight: 700; font-style: normal; font-display: swap; src: url("/fonts/space-grotesk-700.ttf") format("truetype"); } +@font-face { + font-family: "Space Grotesk"; + font-weight: 400; + font-style: normal; + font-display: swap; + src: url("/fonts/space-grotesk-400.ttf") format("truetype"); +} +@font-face { + font-family: "Space Grotesk"; + font-weight: 500; + font-style: normal; + font-display: swap; + src: url("/fonts/space-grotesk-500.ttf") format("truetype"); +} +@font-face { + font-family: "Space Grotesk"; + font-weight: 600; + font-style: normal; + font-display: swap; + src: url("/fonts/space-grotesk-600.ttf") format("truetype"); +} +@font-face { + font-family: "Space Grotesk"; + font-weight: 700; + font-style: normal; + font-display: swap; + src: url("/fonts/space-grotesk-700.ttf") format("truetype"); +} :root { /* ---- brand core ---- */ @@ -68,8 +92,8 @@ --ifm-color-primary-lighter: #d6bdff; --ifm-color-primary-lightest: #ecdcff; - --ifm-background-color: #0a0a20; /* deepest canvas */ - --ifm-background-surface-color: #101132; /* brand navy */ + --ifm-background-color: #0a0a20; /* deepest canvas */ + --ifm-background-surface-color: #101132; /* brand navy */ --ifm-color-content: #e9e7f6; --ifm-color-content-secondary: #c5c1e0; --ifm-link-color: #d6bdff; @@ -122,7 +146,9 @@ html[data-theme="dark"] { padding-left: 0.85rem; border-left: 1px solid var(--ifm-toc-border-color); } -.navbar__logo { height: 1.7rem; } +.navbar__logo { + height: 1.7rem; +} /* Pre-release "alpha" pill in the navbar — mirrors hub's badge (lowercase, muted, low-prominence) so the ecosystem reads as one product. */ @@ -200,7 +226,9 @@ html[data-theme="dark"] { font-family: "Space Grotesk", system-ui, sans-serif; letter-spacing: -0.02em; } -.markdown h1:first-child { font-size: 2.6rem; } +.markdown h1:first-child { + font-size: 2.6rem; +} /* divider rule under H2 */ .markdown h2 { @@ -214,8 +242,13 @@ html[data-theme="dark"] { background-image: var(--grad-brand); border: 0; color: #fff; - box-shadow: 0 10px 30px rgba(120, 0, 200, 0.35), 0 0 22px rgba(255, 0, 222, 0.22); - transition: transform 0.16s ease, filter 0.16s ease, box-shadow 0.22s ease; + box-shadow: + 0 10px 30px rgba(120, 0, 200, 0.35), + 0 0 22px rgba(255, 0, 222, 0.22); + transition: + transform 0.16s ease, + filter 0.16s ease, + box-shadow 0.22s ease; } .button--brand:hover, .button.button--primary:hover { diff --git a/src/pages/index.module.css b/src/pages/index.module.css index 2e7b498..702030c 100644 --- a/src/pages/index.module.css +++ b/src/pages/index.module.css @@ -17,8 +17,7 @@ } .sectionAlt { background: - radial-gradient(800px 500px at 50% 0%, rgba(88, 0, 214, 0.16), transparent 60%), - #08081c; + radial-gradient(800px 500px at 50% 0%, rgba(88, 0, 214, 0.16), transparent 60%), #08081c; } .head { @@ -53,8 +52,7 @@ padding: 7rem 0 6rem; background: radial-gradient(1100px 620px at 22% -10%, rgba(88, 0, 214, 0.42), transparent 60%), - radial-gradient(900px 560px at 100% 10%, rgba(255, 0, 222, 0.22), transparent 58%), - #06061a; + radial-gradient(900px 560px at 100% 10%, rgba(255, 0, 222, 0.22), transparent 58%), #06061a; border-bottom: 1px solid rgba(255, 255, 255, 0.06); } .heroBg { @@ -95,7 +93,9 @@ gap: 3rem; align-items: center; } -.heroCopy { max-width: 620px; } +.heroCopy { + max-width: 620px; +} .pill { display: inline-flex; @@ -133,7 +133,9 @@ line-height: 1.6; color: #c5c1e0; } -.lead strong { color: #fff; } +.lead strong { + color: #fff; +} .ctaRow { display: flex; @@ -182,7 +184,15 @@ filter: drop-shadow(0 0 26px rgba(255, 61, 245, 0.55)); animation: float 6s ease-in-out infinite; } -@keyframes float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-7px); } } +@keyframes float { + 0%, + 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-7px); + } +} .terminal { width: 100%; @@ -190,7 +200,9 @@ border-radius: 16px; background: rgba(10, 8, 30, 0.82); border: 1px solid rgba(138, 110, 255, 0.25); - box-shadow: 0 30px 70px rgba(0, 0, 0, 0.55), 0 0 40px rgba(88, 0, 214, 0.22); + box-shadow: + 0 30px 70px rgba(0, 0, 0, 0.55), + 0 0 40px rgba(88, 0, 214, 0.22); overflow: hidden; backdrop-filter: blur(6px); } @@ -202,8 +214,15 @@ background: rgba(255, 255, 255, 0.03); border-bottom: 1px solid rgba(255, 255, 255, 0.07); } -.tdot { width: 11px; height: 11px; border-radius: 50%; background: rgba(255,255,255,0.18); } -.tdot:first-child { background: linear-gradient(135deg, #5800d6, #ff00de); } +.tdot { + width: 11px; + height: 11px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.18); +} +.tdot:first-child { + background: linear-gradient(135deg, #5800d6, #ff00de); +} .termBar em { margin-left: auto; font-family: "Space Grotesk", system-ui, sans-serif; @@ -222,8 +241,14 @@ white-space: pre; overflow-x: auto; } -.termBody code { background: none; border: 0; color: inherit; } -.ok { color: #38e1b0; } +.termBody code { + background: none; + border: 0; + color: inherit; +} +.ok { + color: #38e1b0; +} /* ---------------- PRIMITIVES ---------------- */ .featureCard { @@ -231,12 +256,13 @@ margin: 0 auto; border-radius: 22px; padding: 2.4rem; - background: - linear-gradient(180deg, rgba(25, 18, 72, 0.6), rgba(16, 17, 50, 0.6)); + background: linear-gradient(180deg, rgba(25, 18, 72, 0.6), rgba(16, 17, 50, 0.6)); border: 1px solid rgba(138, 110, 255, 0.22); box-shadow: 0 30px 80px rgba(0, 0, 0, 0.4); } -.featureCardHead { margin-bottom: 1.8rem; } +.featureCardHead { + margin-bottom: 1.8rem; +} .badge { display: inline-block; font-family: "Space Grotesk", system-ui, sans-serif; @@ -266,7 +292,10 @@ font-weight: 600; color: #d6bdff; } -.textLink:hover { color: #ff6bee; text-decoration: none; } +.textLink:hover { + color: #ff6bee; + text-decoration: none; +} .pillarGrid { display: grid; @@ -278,7 +307,10 @@ border-radius: 16px; background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); - transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.22s ease; + transition: + transform 0.18s ease, + border-color 0.18s ease, + box-shadow 0.22s ease; } .pillar:hover { transform: translateY(-3px); @@ -297,15 +329,26 @@ color: #e4c7ff; margin-bottom: 14px; } -.pillarIcon svg { width: 23px; height: 23px; } +.pillarIcon svg { + width: 23px; + height: 23px; +} .pillar h4 { font-family: "Space Grotesk", system-ui, sans-serif; font-size: 1.15rem; margin: 0 0 6px; color: #fff; } -.pillar p { color: #b8b3d8; font-size: 0.95rem; line-height: 1.55; margin: 0; } -.pillar code { background: rgba(255,255,255,0.08); border: 0; } +.pillar p { + color: #b8b3d8; + font-size: 0.95rem; + line-height: 1.55; + margin: 0; +} +.pillar code { + background: rgba(255, 255, 255, 0.08); + border: 0; +} /* ---------------- HOW IT WORKS ---------------- */ .steps { @@ -337,7 +380,11 @@ margin: 8px 0 6px; color: #fff; } -.step p { color: #b8b3d8; margin: 0 0 14px; font-size: 0.96rem; } +.step p { + color: #b8b3d8; + margin: 0 0 14px; + font-size: 0.96rem; +} .stepCode { display: block; font-family: "Space Mono", ui-monospace, monospace; @@ -357,11 +404,13 @@ overflow: hidden; padding: 6rem 0; background: - radial-gradient(700px 400px at 50% 120%, rgba(255, 0, 222, 0.2), transparent 60%), - #0a0a20; + radial-gradient(700px 400px at 50% 120%, rgba(255, 0, 222, 0.2), transparent 60%), #0a0a20; border-top: 1px solid rgba(255, 255, 255, 0.06); } -.ctaInner { position: relative; text-align: center; } +.ctaInner { + position: relative; + text-align: center; +} .ctaGlow { position: absolute; inset: auto 0 -60% 0; @@ -384,13 +433,27 @@ font-size: 1.1rem; margin: 0.8rem 0 0; } -.ctaInner .ctaRow { justify-content: center; } +.ctaInner .ctaRow { + justify-content: center; +} /* ---------------- responsive ---------------- */ @media screen and (max-width: 996px) { - .hero { padding: 4.5rem 0 3.5rem; } - .heroInner { grid-template-columns: 1fr; gap: 2.5rem; } - .heroArt { order: 2; } - .pillarGrid, .steps { grid-template-columns: 1fr; } - .section { padding: 4rem 0; } + .hero { + padding: 4.5rem 0 3.5rem; + } + .heroInner { + grid-template-columns: 1fr; + gap: 2.5rem; + } + .heroArt { + order: 2; + } + .pillarGrid, + .steps { + grid-template-columns: 1fr; + } + .section { + padding: 4rem 0; + } } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 214d905..6988c49 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,11 +1,11 @@ -import clsx from 'clsx'; -import Link from '@docusaurus/Link'; -import Layout from '@theme/Layout'; -import Heading from '@theme/Heading'; -import Translate, { translate } from '@docusaurus/Translate'; -import React from 'react'; +import clsx from "clsx"; +import Link from "@docusaurus/Link"; +import Layout from "@theme/Layout"; +import Heading from "@theme/Heading"; +import Translate, { translate } from "@docusaurus/Translate"; +import React from "react"; -import styles from './index.module.css'; +import styles from "./index.module.css"; function Hero() { return ( @@ -32,50 +32,81 @@ function Hero() {
-
+
- {' '} + {" "} Proof-of-Stake Layer 2 on Chia decentralized }}> - {'Build on the {decentralized} data layer'} + values={{ + decentralized: ( + + decentralized + + ), + }} + > + {"Build on the {decentralized} data layer"}

dig-store }}> - {'DIG Network is a developer platform for publishing and serving content where the host never sees what it carries. Start with {digstore} — encrypted, content-addressable storage that compiles to a single self-defending WebAssembly module.'} + values={{ + digstore: ( + + dig-store + + ), + }} + > + { + "DIG Network is a developer platform for publishing and serving content where the host never sees what it carries. Start with {digstore} — encrypted, content-addressable storage that compiles to a single self-defending WebAssembly module." + }

Explore the docs → - + Quick start + className={clsx("button button--lg", styles.ghost)} + to="https://github.com/DIG-Network/DIG_Browser/releases" + > Get the DIG Browser ↗
- Open source · GPL-2.0 + + Open source · GPL-2.0 + - macOS · Linux · Windows + + macOS · Linux · Windows + - Single-binary CLI + + Single-binary CLI +
- +
@@ -84,13 +115,19 @@ function Hero() { dig-store
-              {`$ digs init site --dir dist
-`}✓ Initialized store 'site'{`
+              
+                {`$ digs init site --dir dist
+`}
+                ✓ Initialized store 'site'
+                {`
 $ digs add -A
   47.2 MB staged · 80.8 MB free
 $ digs commit -m "v1"
-`}✓ generation 1a2b3c… → site.wasm{`
-$ digs cat urn:dig:chia:…/index.html`}
+`}
+                ✓ generation 1a2b3c… → site.wasm
+                {`
+$ digs cat urn:dig:chia:…/index.html`}
+              
             
@@ -106,7 +143,14 @@ function usePillars(): Pillar[] { { title: Encrypted at rest, icon: ( - + @@ -114,37 +158,57 @@ function usePillars(): Pillar[] { ), body: ( - {'The URN is the key — it both locates and decrypts (AES-256-GCM-SIV). No password, no key stored anywhere.'} + { + "The URN is the key — it both locates and decrypts (AES-256-GCM-SIV). No password, no key stored anywhere." + } ), }, { title: Provider-blind, icon: ( - + ), body: ( - {"Hosts hold only ciphertext keyed by hashes. Downloads are verified against the store id and the publisher's signed root."} + { + "Hosts hold only ciphertext keyed by hashes. Downloads are verified against the store id and the publisher's signed root." + } ), }, { - title: One self-defending file, + title: ( + One self-defending file + ), icon: ( - + ), body: ( - .wasm }}> - {'Data, server, and Merkle proofs compile into one {wasm}, padded to a uniform size that reveals nothing.'} + .wasm }}> + { + "Data, server, and Merkle proofs compile into one {wasm}, padded to a uniform size that reveals nothing." + } ), }, @@ -181,12 +245,14 @@ function Primitives() {

- A Git-shaped, encrypted, content-addressable store. Point it at a - build directory, commit generations, and address everything by URN. + A Git-shaped, encrypted, content-addressable store. Point it at a build directory, + commit generations, and address everything by URN.

- Read the dig-store docs → + + Read the dig-store docs → +
@@ -208,22 +274,24 @@ function Primitives() { function useSteps() { return [ { - n: '01', + n: "01", t: Capture, - d: Point a store at your build output., - c: 'digs init site --dir dist', + d: ( + Point a store at your build output. + ), + c: "digs init site --dir dist", }, { - n: '02', + n: "02", t: Commit, d: Seal a generation into one .wasm., c: 'digs add -A && digs commit -m "v1"', }, { - n: '03', + n: "03", t: Share, d: Push to a remote; hand out a URN., - c: 'digs push origin', + c: "digs push origin", }, ]; } @@ -264,7 +332,7 @@ function HowItWorks() { function CTABand() { return (
-
+
@@ -289,13 +360,13 @@ function CTABand() { export default function Home(): JSX.Element { const title = translate({ - id: 'homepage.meta.title', - message: 'DIG Network — the decentralized data layer', + id: "homepage.meta.title", + message: "DIG Network — the decentralized data layer", }); const description = translate({ - id: 'homepage.meta.description', + id: "homepage.meta.description", message: - 'DIG Network is a Proof-of-Stake Layer 2 on Chia. Developer docs for the network and its primitives, including dig-store — encrypted content-addressable storage.', + "DIG Network is a Proof-of-Stake Layer 2 on Chia. Developer docs for the network and its primitives, including dig-store — encrypted content-addressable storage.", }); return ( diff --git a/src/theme/DocItem/Footer/index.tsx b/src/theme/DocItem/Footer/index.tsx index 9534ef2..f6b051a 100644 --- a/src/theme/DocItem/Footer/index.tsx +++ b/src/theme/DocItem/Footer/index.tsx @@ -14,17 +14,17 @@ * typed `about`/`keywords` edges that link the page to the controlled DIG * vocabulary so a scraper can reconstruct the concept graph. */ -import React, {type ReactNode} from 'react'; -import Head from '@docusaurus/Head'; -import {useDoc} from '@docusaurus/plugin-content-docs/client'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import Footer from '@theme-original/DocItem/Footer'; +import React, { type ReactNode } from "react"; +import Head from "@docusaurus/Head"; +import { useDoc } from "@docusaurus/plugin-content-docs/client"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import Footer from "@theme-original/DocItem/Footer"; // The docs site is the single `isPartOf` parent every page belongs to. const DOCS_SITE = { - '@type': 'TechArticle', - name: 'DIG Network Documentation', - url: 'https://docs.dig.net/', + "@type": "TechArticle", + name: "DIG Network Documentation", + url: "https://docs.dig.net/", } as const; function toAbsoluteUrl(siteUrl: string, permalink: string): string { @@ -33,8 +33,8 @@ function toAbsoluteUrl(siteUrl: string, permalink: string): string { } export default function DocItemFooterWrapper(props: Record): ReactNode { - const {metadata, frontMatter} = useDoc(); - const {siteConfig} = useDocusaurusContext(); + const { metadata, frontMatter } = useDoc(); + const { siteConfig } = useDocusaurusContext(); const keywords = ([] as string[]) .concat((frontMatter.keywords as string[] | undefined) ?? []) @@ -46,40 +46,39 @@ export default function DocItemFooterWrapper(props: Record): Re // `schema_type` is a custom frontmatter field (not in DocFrontMatter), so // read it through an index cast. "DefinedTerm" marks the glossary/concept // pages; everything else is a TechArticle. - const isDefinedTerm = - (frontMatter as Record).schema_type === 'DefinedTerm'; + const isDefinedTerm = (frontMatter as Record).schema_type === "DefinedTerm"; // `about` turns each tag into a typed edge to a DefinedTerm node, keyed by // its /docs/tags page — the canonical graph node for that concept. const about = (metadata.tags ?? []).map((t) => ({ - '@type': 'DefinedTerm', + "@type": "DefinedTerm", name: t.label, url: toAbsoluteUrl(siteConfig.url, t.permalink), - inDefinedTermSet: toAbsoluteUrl(siteConfig.url, '/docs/concepts'), + inDefinedTermSet: toAbsoluteUrl(siteConfig.url, "/docs/concepts"), })); const jsonLd: Record = { - '@context': 'https://schema.org', - '@type': isDefinedTerm ? 'DefinedTerm' : 'TechArticle', + "@context": "https://schema.org", + "@type": isDefinedTerm ? "DefinedTerm" : "TechArticle", name: metadata.title, headline: metadata.title, description: metadata.description, url, - '@id': url, - inLanguage: 'en', + "@id": url, + inLanguage: "en", keywords: uniqueKeywords, isPartOf: DOCS_SITE, publisher: { - '@type': 'Organization', - name: 'DIG Network', - url: 'https://dig.net', + "@type": "Organization", + name: "DIG Network", + url: "https://dig.net", }, }; if (about.length > 0) { jsonLd.about = about; } if (isDefinedTerm) { - jsonLd.inDefinedTermSet = toAbsoluteUrl(siteConfig.url, '/docs/concepts'); + jsonLd.inDefinedTermSet = toAbsoluteUrl(siteConfig.url, "/docs/concepts"); } return ( diff --git a/src/theme/NavbarItem/HtmlNavbarItem.tsx b/src/theme/NavbarItem/HtmlNavbarItem.tsx index 74139d5..f3f68d3 100644 --- a/src/theme/NavbarItem/HtmlNavbarItem.tsx +++ b/src/theme/NavbarItem/HtmlNavbarItem.tsx @@ -1,7 +1,7 @@ -import React from 'react'; -import clsx from 'clsx'; +import React from "react"; +import clsx from "clsx"; -import type {Props} from '@theme/NavbarItem/HtmlNavbarItem'; +import type { Props } from "@theme/NavbarItem/HtmlNavbarItem"; // Swizzled from @docusaurus/theme-classic (eject) to fix a real WCAG 2.2 AA // violation (axe rule `list`, "Ensure that lists are structured correctly"): @@ -20,17 +20,17 @@ export default function HtmlNavbarItem({ mobile = false, isDropdownItem = false, }: Props): JSX.Element { - const Comp = mobile || isDropdownItem ? 'li' : 'div'; + const Comp = mobile || isDropdownItem ? "li" : "div"; return ( ); } diff --git a/src/theme/NotFound/index.tsx b/src/theme/NotFound/index.tsx index c37dc27..1f9c7b3 100644 --- a/src/theme/NotFound/index.tsx +++ b/src/theme/NotFound/index.tsx @@ -14,9 +14,9 @@ * `follow` is kept so links elsewhere on the page (e.g. nav) still get * crawled — only THIS page is excluded from the index. */ -import React, {type ReactNode} from 'react'; -import Head from '@docusaurus/Head'; -import NotFound from '@theme-original/NotFound'; +import React, { type ReactNode } from "react"; +import Head from "@docusaurus/Head"; +import NotFound from "@theme-original/NotFound"; export default function NotFoundWrapper(props: Record): ReactNode { return ( diff --git a/tailwind.config.js b/tailwind.config.js index f593b31..dafdc50 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,4 +1,4 @@ /** @type {import('tailwindcss').Config} */ export default { - content: ['./src/**/*.{js,jsx,ts,tsx}'], -}; \ No newline at end of file + content: ["./src/**/*.{js,jsx,ts,tsx}"], +}; diff --git a/tests/e2e/aria-snapshot.spec.ts b/tests/e2e/aria-snapshot.spec.ts index 1bd207f..0096515 100644 --- a/tests/e2e/aria-snapshot.spec.ts +++ b/tests/e2e/aria-snapshot.spec.ts @@ -42,7 +42,9 @@ test("locale dropdown exposes an accessible name and expands via ARIA state", as // Docusaurus's LocaleDropdown renders a